diff options
Diffstat (limited to 'ansible_collections/cisco/ios/plugins')
281 files changed, 70886 insertions, 0 deletions
diff --git a/ansible_collections/cisco/ios/plugins/action/__init__.py b/ansible_collections/cisco/ios/plugins/action/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/action/acl_interfaces.py b/ansible_collections/cisco/ios/plugins/action/acl_interfaces.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/acl_interfaces.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/acls.py b/ansible_collections/cisco/ios/plugins/action/acls.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/acls.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/banner.py b/ansible_collections/cisco/ios/plugins/action/banner.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/banner.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/bgp.py b/ansible_collections/cisco/ios/plugins/action/bgp.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/bgp.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/bgp_address_family.py b/ansible_collections/cisco/ios/plugins/action/bgp_address_family.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/bgp_address_family.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/bgp_global.py b/ansible_collections/cisco/ios/plugins/action/bgp_global.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/bgp_global.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/command.py b/ansible_collections/cisco/ios/plugins/action/command.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/command.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/config.py b/ansible_collections/cisco/ios/plugins/action/config.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/config.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/facts.py b/ansible_collections/cisco/ios/plugins/action/facts.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/facts.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/hostname.py b/ansible_collections/cisco/ios/plugins/action/hostname.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/hostname.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/interfaces.py b/ansible_collections/cisco/ios/plugins/action/interfaces.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/interfaces.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/ios.py b/ansible_collections/cisco/ios/plugins/action/ios.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/ios.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/l2_interfaces.py b/ansible_collections/cisco/ios/plugins/action/l2_interfaces.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/l2_interfaces.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/l3_interfaces.py b/ansible_collections/cisco/ios/plugins/action/l3_interfaces.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/l3_interfaces.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/lacp.py b/ansible_collections/cisco/ios/plugins/action/lacp.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/lacp.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/lacp_interfaces.py b/ansible_collections/cisco/ios/plugins/action/lacp_interfaces.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/lacp_interfaces.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/lag_interfaces.py b/ansible_collections/cisco/ios/plugins/action/lag_interfaces.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/lag_interfaces.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/linkagg.py b/ansible_collections/cisco/ios/plugins/action/linkagg.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/linkagg.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/lldp.py b/ansible_collections/cisco/ios/plugins/action/lldp.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/lldp.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/lldp_global.py b/ansible_collections/cisco/ios/plugins/action/lldp_global.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/lldp_global.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/lldp_interfaces.py b/ansible_collections/cisco/ios/plugins/action/lldp_interfaces.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/lldp_interfaces.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/logging.py b/ansible_collections/cisco/ios/plugins/action/logging.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/logging.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/logging_global.py b/ansible_collections/cisco/ios/plugins/action/logging_global.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/logging_global.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/ntp.py b/ansible_collections/cisco/ios/plugins/action/ntp.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/ntp.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/ntp_global.py b/ansible_collections/cisco/ios/plugins/action/ntp_global.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/ntp_global.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/ospf_interfaces.py b/ansible_collections/cisco/ios/plugins/action/ospf_interfaces.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/ospf_interfaces.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/ospfv2.py b/ansible_collections/cisco/ios/plugins/action/ospfv2.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/ospfv2.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/ospfv3.py b/ansible_collections/cisco/ios/plugins/action/ospfv3.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/ospfv3.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/ping.py b/ansible_collections/cisco/ios/plugins/action/ping.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/ping.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/prefix_lists.py b/ansible_collections/cisco/ios/plugins/action/prefix_lists.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/prefix_lists.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/route_maps.py b/ansible_collections/cisco/ios/plugins/action/route_maps.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/route_maps.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/service.py b/ansible_collections/cisco/ios/plugins/action/service.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/service.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/snmp_server.py b/ansible_collections/cisco/ios/plugins/action/snmp_server.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/snmp_server.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/static_routes.py b/ansible_collections/cisco/ios/plugins/action/static_routes.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/static_routes.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/system.py b/ansible_collections/cisco/ios/plugins/action/system.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/system.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/user.py b/ansible_collections/cisco/ios/plugins/action/user.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/user.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/vlans.py b/ansible_collections/cisco/ios/plugins/action/vlans.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/vlans.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/action/vrf.py b/ansible_collections/cisco/ios/plugins/action/vrf.py new file mode 100644 index 000000000..38c0ed229 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/action/vrf.py @@ -0,0 +1,56 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.action.network import ( + ActionModule as ActionNetworkModule, +) + + +display = Display() + + +class ActionModule(ActionNetworkModule): + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + module_name = self._task.action.split(".")[-1] + self._config_module = True if module_name in ["ios_config", "config"] else False + persistent_connection = self._play_context.connection.split(".")[-1] + warnings = [] + + if persistent_connection != "network_cli": + return { + "failed": True, + "msg": "Connection type %s is not valid for this module" + % self._play_context.connection, + } + + result = super(ActionModule, self).run(task_vars=task_vars) + if warnings: + if "warnings" in result: + result["warnings"].extend(warnings) + else: + result["warnings"] = warnings + return result diff --git a/ansible_collections/cisco/ios/plugins/cache/__init__.py b/ansible_collections/cisco/ios/plugins/cache/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/cache/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/cliconf/__init__.py b/ansible_collections/cisco/ios/plugins/cliconf/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/cliconf/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/cliconf/ios.py b/ansible_collections/cisco/ios/plugins/cliconf/ios.py new file mode 100644 index 000000000..be62c3724 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/cliconf/ios.py @@ -0,0 +1,601 @@ +# +# (c) 2017 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +author: +- Ansible Networking Team (@ansible-network) +name: ios +short_description: Use ios cliconf to run command on Cisco IOS platform +description: +- This ios plugin provides low level abstraction apis for sending and receiving CLI + commands from Cisco IOS network devices. +version_added: 1.0.0 +options: + commit_confirm_immediate: + type: boolean + default: false + description: + - Enable or disable commit confirm mode. + - Confirms the configuration pushed after a custom/ default timeout.(default 1 minute). + - For custom timeout configuration set commit_confirm_timeout value. + - On commit_confirm_immediate default value for commit_confirm_timeout is considered 1 minute + when variable in not explicitly declared. + env: + - name: ANSIBLE_IOS_COMMIT_CONFIRM_IMMEDIATE + vars: + - name: ansible_ios_commit_confirm_immediate + commit_confirm_timeout: + type: int + description: + - Commits the configuration on a trial basis for the time + specified in minutes. + - Using commit_confirm_timeout without specifying commit_confirm_immediate would + need an explicit C(configure confirm) using the ios_command module + to confirm/commit the changes made. + - Refer to example for a use case demonstration. + env: + - name: ANSIBLE_IOS_COMMIT_CONFIRM_TIMEOUT + vars: + - name: ansible_ios_commit_confirm_timeout + config_commands: + description: + - Specifies a list of commands that can make configuration changes + to the target device. + - When `ansible_network_single_user_mode` is enabled, if a command sent + to the device is present in this list, the existing cache is invalidated. + version_added: 2.0.0 + type: list + elements: str + default: [] + vars: + - name: ansible_ios_config_commands +""" + +EXAMPLES = """ +# NOTE - IOS waits for a `configure confirm` when the configure terminal +# command executed is `configure terminal revert timer <timeout>` within the timeout +# period for the configuration to commit successfully, else a rollback +# happens. + +# Use commit confirm with timeout and confirm the commit explicitly + +- name: Example commit confirmed + vars: + ansible_ios_commit_confirm_timeout: 1 + tasks: + - name: "Commit confirmed with timeout" + cisco.ios.ios_hostname: + state: merged + config: + hostname: R1 + + - name: "Confirm the Commit" + cisco.ios.ios_command: + commands: + - configure confirm + +# Commands fired +# - configure terminal revert timer 1 (cliconf specific) +# - hostname R1 (from hostname resource module) +# - configure confirm (from ios_command module) + +# Use commit confirm with timeout and confirm the commit via cliconf + +- name: Example commit confirmed + vars: + ansible_ios_commit_confirm_immediate: True + ansible_ios_commit_confirm_timeout: 3 + tasks: + - name: "Commit confirmed with timeout" + cisco.ios.ios_hostname: + state: merged + config: + hostname: R1 + +# Commands fired +# - configure terminal revert timer 3 (cliconf specific) +# - hostname R1 (from hostname resource module) +# - configure confirm (cliconf specific) + +# Use commit confirm via cliconf using default timeout + +- name: Example commit confirmed + vars: + ansible_ios_commit_confirm_immediate: True + tasks: + - name: "Commit confirmed with timeout" + cisco.ios.ios_hostname: + state: merged + config: + hostname: R1 + +# Commands fired +# - configure terminal revert timer 1 (cliconf specific with default timeout) +# - hostname R1 (from hostname resource module) +# - configure confirm (cliconf specific) + +""" + +import json +import re +import time + +from ansible.errors import AnsibleConnectionFailure +from ansible.module_utils._text import to_text +from ansible.module_utils.common._collections_compat import Mapping +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( + NetworkConfig, + dumps, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list +from ansible_collections.ansible.netcommon.plugins.plugin_utils.cliconf_base import ( + CliconfBase, + enable_mode, +) + + +class Cliconf(CliconfBase): + def __init__(self, *args, **kwargs): + self._device_info = {} + super(Cliconf, self).__init__(*args, **kwargs) + + @enable_mode + def get_config(self, source="running", flags=None, format=None): + if source not in ("running", "startup"): + raise ValueError("fetching configuration from %s is not supported" % source) + + if format: + raise ValueError("'format' value %s is not supported for get_config" % format) + + if not flags: + flags = [] + if source == "running": + cmd = "show running-config " + else: + cmd = "show startup-config " + + cmd += " ".join(to_list(flags)) + cmd = cmd.strip() + + return self.send_command(cmd) + + def get_diff( + self, + candidate=None, + running=None, + diff_match="line", + diff_ignore_lines=None, + path=None, + diff_replace="line", + ): + """ + Generate diff between candidate and running configuration. If the + remote host supports onbox diff capabilities ie. supports_onbox_diff in that case + candidate and running configurations are not required to be passed as argument. + In case if onbox diff capability is not supported candidate argument is mandatory + and running argument is optional. + :param candidate: The configuration which is expected to be present on remote host. + :param running: The base configuration which is used to generate diff. + :param diff_match: Instructs how to match the candidate configuration with current device configuration + Valid values are 'line', 'strict', 'exact', 'none'. + 'line' - commands are matched line by line + 'strict' - command lines are matched with respect to position + 'exact' - command lines must be an equal match + 'none' - will not compare the candidate configuration with the running configuration + :param diff_ignore_lines: Use this argument to specify one or more lines that should be + ignored during the diff. This is used for lines in the configuration + that are automatically updated by the system. This argument takes + a list of regular expressions or exact line matches. + :param path: The ordered set of parents that uniquely identify the section or hierarchy + the commands should be checked against. If the parents argument + is omitted, the commands are checked against the set of top + level or global commands. + :param diff_replace: Instructs on the way to perform the configuration on the device. + If the replace argument is set to I(line) then the modified lines are + pushed to the device in configuration mode. If the replace argument is + set to I(block) then the entire command block is pushed to the device in + configuration mode if any line is not correct. + :return: Configuration diff in json format. + { + 'config_diff': '', + 'banner_diff': {} + } + """ + diff = {} + device_operations = self.get_device_operations() + option_values = self.get_option_values() + + if candidate is None and device_operations["supports_generate_diff"]: + raise ValueError("candidate configuration is required to generate diff") + + if diff_match not in option_values["diff_match"]: + raise ValueError( + "'match' value %s in invalid, valid values are %s" + % (diff_match, ", ".join(option_values["diff_match"])), + ) + + if diff_replace not in option_values["diff_replace"]: + raise ValueError( + "'replace' value %s in invalid, valid values are %s" + % (diff_replace, ", ".join(option_values["diff_replace"])), + ) + + # prepare candidate configuration + candidate_obj = NetworkConfig(indent=1) + want_src, want_banners = self._extract_banners(candidate) + candidate_obj.load(want_src) + + if running and diff_match != "none": + # running configuration + have_src, have_banners = self._extract_banners(running) + running_obj = NetworkConfig(indent=1, contents=have_src, ignore_lines=diff_ignore_lines) + configdiffobjs = candidate_obj.difference( + running_obj, + path=path, + match=diff_match, + replace=diff_replace, + ) + + else: + configdiffobjs = candidate_obj.items + have_banners = {} + + diff["config_diff"] = dumps(configdiffobjs, "commands") if configdiffobjs else "" + banners = self._diff_banners(want_banners, have_banners) + diff["banner_diff"] = banners if banners else {} + return diff + + @enable_mode + def configure(self): + """ + Enter global configuration mode based on the + status of commit_confirm + :return: None + """ + if self.get_option("commit_confirm_timeout") or self.get_option("commit_confirm_immediate"): + commit_timeout = ( + self.get_option("commit_confirm_timeout") + if self.get_option("commit_confirm_timeout") + else 1 + ) # add default timeout not default: 1 to support above or operation + + persistent_command_timeout = self._connection.get_option("persistent_command_timeout") + # check archive state + archive_state = self.send_command("show archive") + rollback_state = self.send_command("show archive config rollback timer") + + if persistent_command_timeout > commit_timeout * 60: + raise ValueError( + "ansible_command_timeout can't be greater than commit_confirm_timeout " + "Please adjust and try again", + ) + + if re.search(r"Archive.*not.enabled", archive_state): + raise ValueError( + "commit_confirm_immediate option set, but archiving " + "not enabled on device. " + "Please set up archiving and try again", + ) + + if not re.search(r"%No Rollback Confirmed Change pending", rollback_state): + raise ValueError( + "Existing rollback change already pending. " + "Please resolve by issuing 'configure confirm' " + "or 'configure revert now'", + ) + + self.send_command(f"configure terminal revert timer {commit_timeout}") + else: + self.send_command("configure terminal") + + @enable_mode + def edit_config(self, candidate=None, commit=True, replace=None, comment=None): + resp = {} + operations = self.get_device_operations() + self.check_edit_config_capability(operations, candidate, commit, replace, comment) + + results = [] + requests = [] + # commit confirm specific attributes + commit_confirm = self.get_option("commit_confirm_immediate") + if commit: + self.configure() + for line in to_list(candidate): + if not isinstance(line, Mapping): + line = {"command": line} + + cmd = line["command"] + if cmd != "end" and cmd[0] != "!": + results.append(self.send_command(**line)) + requests.append(cmd) + + self.send_command("end") + if commit_confirm: + self.send_command("configure confirm") + + else: + raise ValueError("check mode is not supported") + + resp["request"] = requests + resp["response"] = results + return resp + + def edit_macro(self, candidate=None, commit=True, replace=None, comment=None): + """ + ios_config: + lines: "{{ macro_lines }}" + parents: "macro name {{ macro_name }}" + after: '@' + match: line + replace: block + """ + resp = {} + operations = self.get_device_operations() + self.check_edit_config_capability(operations, candidate, commit, replace, comment) + + results = [] + requests = [] + if commit: + commands = "" + self.send_command("config terminal") + time.sleep(0.1) + # first item: macro command + commands += candidate.pop(0) + "\n" + multiline_delimiter = candidate.pop(-1) + for line in candidate: + commands += " " + line + "\n" + commands += multiline_delimiter + "\n" + obj = {"command": commands, "sendonly": True} + results.append(self.send_command(**obj)) + requests.append(commands) + + time.sleep(0.1) + self.send_command("end", sendonly=True) + time.sleep(0.1) + results.append(self.send_command("\n")) + requests.append("\n") + + resp["request"] = requests + resp["response"] = results + return resp + + def get( + self, + command=None, + prompt=None, + answer=None, + sendonly=False, + newline=True, + output=None, + check_all=False, + ): + if not command: + raise ValueError("must provide value of command to execute") + if output: + raise ValueError("'output' value %s is not supported for get" % output) + + return self.send_command( + command=command, + prompt=prompt, + answer=answer, + sendonly=sendonly, + newline=newline, + check_all=check_all, + ) + + def check_device_type(self): + device_type = "L2" + try: + self.get(command="show vlan") + except Exception: + device_type = "L3" + return device_type + + def get_device_info(self): + if not self._device_info: + device_info = {} + + device_info["network_os"] = "ios" + # Ensure we are not in config mode + self._update_cli_prompt_context(config_context=")#", exit_command="end") + reply = self.get(command="show version") + data = to_text(reply, errors="surrogate_or_strict").strip() + match = re.search(r"Version (\S+)", data) + if match: + device_info["network_os_version"] = match.group(1).strip(",") + + model_search_strs = [ + r"^[Cc]isco (.+) \(revision", + r"^[Cc]isco (\S+).+bytes of .*memory", + ] + for item in model_search_strs: + match = re.search(item, data, re.M) + if match: + version = match.group(1).split(" ") + device_info["network_os_model"] = version[0] + break + + match = re.search(r"^(.+) uptime", data, re.M) + if match: + device_info["network_os_hostname"] = match.group(1) + + match = re.search(r'image file is "(.+)"', data) + if match: + device_info["network_os_image"] = match.group(1) + device_info["network_os_type"] = self.check_device_type() + self._device_info = device_info + + return self._device_info + + def get_device_operations(self): + return { + "supports_diff_replace": True, + "supports_commit": False, + "supports_rollback": False, + "supports_defaults": True, + "supports_onbox_diff": False, + "supports_commit_comment": False, + "supports_multiline_delimiter": True, + "supports_diff_match": True, + "supports_diff_ignore_lines": True, + "supports_generate_diff": True, + "supports_replace": False, + } + + def get_option_values(self): + return { + "format": ["text"], + "diff_match": ["line", "strict", "exact", "none"], + "diff_replace": ["line", "block"], + "output": [], + } + + def get_capabilities(self): + result = super(Cliconf, self).get_capabilities() + result["rpc"] += ["edit_banner", "get_diff", "run_commands", "get_defaults_flag"] + result["device_operations"] = self.get_device_operations() + result.update(self.get_option_values()) + return json.dumps(result) + + def edit_banner(self, candidate=None, multiline_delimiter="@", commit=True): + """ + Edit banner on remote device + :param banners: Banners to be loaded in json format + :param multiline_delimiter: Line delimiter for banner + :param commit: Boolean value that indicates if the device candidate + configuration should be pushed in the running configuration or discarded. + :param diff: Boolean flag to indicate if configuration that is applied on remote host should + generated and returned in response or not + :return: Returns response of executing the configuration command received + from remote host + """ + resp = {} + banners_obj = json.loads(candidate) + results = [] + requests = [] + if commit: + for key, value in iteritems(banners_obj): + key += " %s" % multiline_delimiter + self.send_command("config terminal", sendonly=True) + for cmd in [key, value, multiline_delimiter]: + obj = {"command": cmd, "sendonly": True} + results.append(self.send_command(**obj)) + requests.append(cmd) + + self.send_command("end", sendonly=True) + time.sleep(0.1) + results.append(self.send_command("\n")) + requests.append("\n") + + resp["request"] = requests + resp["response"] = results + + return resp + + def run_commands(self, commands=None, check_rc=True): + if commands is None: + raise ValueError("'commands' value is required") + + responses = list() + for cmd in to_list(commands): + if not isinstance(cmd, Mapping): + cmd = {"command": cmd} + + output = cmd.pop("output", None) + if output: + raise ValueError("'output' value %s is not supported for run_commands" % output) + + try: + out = self.send_command(**cmd) + except AnsibleConnectionFailure as e: + if check_rc: + raise + out = getattr(e, "err", to_text(e)) + + responses.append(out) + + return responses + + def get_defaults_flag(self): + """ + The method identifies the filter that should be used to fetch running-configuration + with defaults. + :return: valid default filter + """ + out = self.get("show running-config ?") + out = to_text(out, errors="surrogate_then_replace") + + commands = set() + for line in out.splitlines(): + if line.strip(): + commands.add(line.strip().split()[0]) + + if "all" in commands: + return "all" + else: + return "full" + + def set_cli_prompt_context(self): + """ + Make sure we are in the operational cli mode + :return: None + """ + if self._connection.connected: + out = self._connection.get_prompt() + + if out is None: + raise AnsibleConnectionFailure( + message="cli prompt is not identified from the last received" + " response window: %s" % self._connection._last_recv_window, + ) + + if re.search(r"config.*\)#", to_text(out, errors="surrogate_then_replace").strip()): + self._connection.queue_message("vvvv", "wrong context, sending end to device") + self._connection.send_command("end") + + def _extract_banners(self, config): + banners = {} + banner_cmds = re.findall(r"^banner (\w+)", config, re.M) + for cmd in banner_cmds: + regex = r"banner %s \^C(.+?)(?=\^C)" % cmd + match = re.search(regex, config, re.S) + if match: + key = "banner %s" % cmd + banners[key] = match.group(1).strip() + + for cmd in banner_cmds: + regex = r"banner %s \^C(.+?)(?=\^C)" % cmd + match = re.search(regex, config, re.S) + if match: + config = config.replace(str(match.group(1)), "") + + config = re.sub(r"banner \w+ \^C\^C", "!! banner removed", config) + return config, banners + + def _diff_banners(self, want, have): + candidate = {} + for key, value in iteritems(want): + if value != have.get(key): + candidate[key] = value + return candidate diff --git a/ansible_collections/cisco/ios/plugins/doc_fragments/__init__.py b/ansible_collections/cisco/ios/plugins/doc_fragments/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/doc_fragments/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/doc_fragments/ios.py b/ansible_collections/cisco/ios/plugins/doc_fragments/ios.py new file mode 100644 index 000000000..ab9a5b155 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/doc_fragments/ios.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +# Copyright: (c) 2015, Peter Sprygada <psprygada@ansible.com> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +class ModuleDocFragment(object): + # Standard files documentation fragment + DOCUMENTATION = r"""options: {} +notes: +- For more information on using Ansible to manage network devices see the :ref:`Ansible + Network Guide <network_guide>` +- For more information on using Ansible to manage Cisco devices see the `Cisco integration + page <https://www.ansible.com/integrations/networks/cisco>`_. +""" diff --git a/ansible_collections/cisco/ios/plugins/filter/__init__.py b/ansible_collections/cisco/ios/plugins/filter/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/filter/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/inventory/__init__.py b/ansible_collections/cisco/ios/plugins/inventory/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/inventory/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/lookup/__init__.py b/ansible_collections/cisco/ios/plugins/lookup/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/lookup/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acl_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acl_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acl_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acl_interfaces/acl_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acl_interfaces/acl_interfaces.py new file mode 100644 index 000000000..d3037ed9b --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acl_interfaces/acl_interfaces.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the ios_acl_interfaces module +""" + + +class Acl_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_acl_interfaces module""" + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "access_groups": { + "type": "list", + "elements": "dict", + "options": { + "afi": {"type": "str", "required": True, "choices": ["ipv4", "ipv6"]}, + "acls": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "direction": { + "type": "str", + "required": True, + "choices": ["in", "out"], + }, + }, + }, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "type": "str", + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acls/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acls/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acls/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acls/acls.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acls/acls.py new file mode 100644 index 000000000..a3ca057c5 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/acls/acls.py @@ -0,0 +1,300 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the ios_acls module +""" + + +class AclsArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_acls module""" + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "acls": { + "elements": "dict", + "options": { + "aces": { + "elements": "dict", + "options": { + "destination": { + "options": { + "address": {"type": "str"}, + "any": {"type": "bool"}, + "host": {"type": "str"}, + "object_group": {"type": "str"}, + "port_protocol": { + "options": { + "eq": {"type": "str"}, + "gt": {"type": "str"}, + "lt": {"type": "str"}, + "neq": {"type": "str"}, + "range": { + "options": { + "end": {"type": "int"}, + "start": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "wildcard_bits": {"type": "str"}, + }, + "type": "dict", + }, + "dscp": {"type": "str"}, + "evaluate": {"type": "str"}, + "fragments": {"type": "str"}, + "enable_fragments": {"type": "bool"}, + "grant": {"choices": ["permit", "deny"], "type": "str"}, + "log": { + "options": { + "set": {"type": "bool"}, + "user_cookie": {"type": "str"}, + }, + "type": "dict", + }, + "log_input": { + "options": { + "set": {"type": "bool"}, + "user_cookie": {"type": "str"}, + }, + "type": "dict", + }, + "option": { + "options": { + "add_ext": {"type": "bool"}, + "any_options": {"type": "bool"}, + "com_security": {"type": "bool"}, + "dps": {"type": "bool"}, + "encode": {"type": "bool"}, + "eool": {"type": "bool"}, + "ext_ip": {"type": "bool"}, + "ext_security": {"type": "bool"}, + "finn": {"type": "bool"}, + "imitd": {"type": "bool"}, + "lsr": {"type": "bool"}, + "mtup": {"type": "bool"}, + "mtur": {"type": "bool"}, + "no_op": {"type": "bool"}, + "nsapa": {"type": "bool"}, + "record_route": {"type": "bool"}, + "router_alert": {"type": "bool"}, + "sdb": {"type": "bool"}, + "security": {"type": "bool"}, + "ssr": {"type": "bool"}, + "stream_id": {"type": "bool"}, + "timestamp": {"type": "bool"}, + "traceroute": {"type": "bool"}, + "ump": {"type": "bool"}, + "visa": {"type": "bool"}, + "zsu": {"type": "bool"}, + }, + "type": "dict", + }, + "precedence": {"type": "str"}, + "protocol": {"type": "str"}, + "protocol_options": { + "options": { + "ahp": {"type": "bool"}, + "eigrp": {"type": "bool"}, + "esp": {"type": "bool"}, + "gre": {"type": "bool"}, + "hbh": {"type": "bool"}, + "icmp": { + "options": { + "administratively_prohibited": {"type": "bool"}, + "alternate_address": {"type": "bool"}, + "conversion_error": {"type": "bool"}, + "dod_host_prohibited": {"type": "bool"}, + "dod_net_prohibited": {"type": "bool"}, + "echo": {"type": "bool"}, + "echo_reply": {"type": "bool"}, + "general_parameter_problem": {"type": "bool"}, + "host_isolated": {"type": "bool"}, + "host_precedence_unreachable": {"type": "bool"}, + "host_redirect": {"type": "bool"}, + "host_tos_redirect": {"type": "bool"}, + "host_tos_unreachable": {"type": "bool"}, + "host_unknown": {"type": "bool"}, + "host_unreachable": {"type": "bool"}, + "information_reply": {"type": "bool"}, + "information_request": {"type": "bool"}, + "mask_reply": {"type": "bool"}, + "mask_request": {"type": "bool"}, + "mobile_redirect": {"type": "bool"}, + "net_redirect": {"type": "bool"}, + "net_tos_redirect": {"type": "bool"}, + "net_tos_unreachable": {"type": "bool"}, + "net_unreachable": {"type": "bool"}, + "network_unknown": {"type": "bool"}, + "no_room_for_option": {"type": "bool"}, + "option_missing": {"type": "bool"}, + "packet_too_big": {"type": "bool"}, + "parameter_problem": {"type": "bool"}, + "port_unreachable": {"type": "bool"}, + "precedence_unreachable": {"type": "bool"}, + "protocol_unreachable": {"type": "bool"}, + "reassembly_timeout": {"type": "bool"}, + "redirect": {"type": "bool"}, + "router_advertisement": {"type": "bool"}, + "router_solicitation": {"type": "bool"}, + "source_quench": {"type": "bool"}, + "source_route_failed": {"type": "bool"}, + "time_exceeded": {"type": "bool"}, + "timestamp_reply": {"type": "bool"}, + "timestamp_request": {"type": "bool"}, + "traceroute": {"type": "bool"}, + "ttl_exceeded": {"type": "bool"}, + "unreachable": {"type": "bool"}, + }, + "type": "dict", + }, + "igmp": { + "options": { + "dvmrp": {"type": "bool"}, + "host_query": {"type": "bool"}, + "mtrace_resp": {"type": "bool"}, + "mtrace_route": {"type": "bool"}, + "pim": {"type": "bool"}, + "trace": {"type": "bool"}, + "v1host_report": {"type": "bool"}, + "v2host_report": {"type": "bool"}, + "v2leave_group": {"type": "bool"}, + "v3host_report": {"type": "bool"}, + }, + "type": "dict", + }, + "ip": {"type": "bool"}, + "ipinip": {"type": "bool"}, + "ipv6": {"type": "bool"}, + "nos": {"type": "bool"}, + "ospf": {"type": "bool"}, + "pcp": {"type": "bool"}, + "pim": {"type": "bool"}, + "protocol_number": {"type": "int"}, + "sctp": {"type": "bool"}, + "tcp": { + "options": { + "ack": {"type": "bool"}, + "established": {"type": "bool"}, + "fin": {"type": "bool"}, + "psh": {"type": "bool"}, + "rst": {"type": "bool"}, + "syn": {"type": "bool"}, + "urg": {"type": "bool"}, + }, + "type": "dict", + }, + "udp": {"type": "bool"}, + }, + "type": "dict", + }, + "remarks": {"elements": "str", "type": "list"}, + "sequence": {"type": "int"}, + "source": { + "options": { + "address": {"type": "str"}, + "any": {"type": "bool"}, + "host": {"type": "str"}, + "object_group": {"type": "str"}, + "port_protocol": { + "options": { + "eq": {"type": "str"}, + "gt": {"type": "str"}, + "lt": {"type": "str"}, + "neq": {"type": "str"}, + "range": { + "options": { + "end": {"type": "int"}, + "start": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "wildcard_bits": {"type": "str"}, + }, + "type": "dict", + }, + "time_range": {"type": "str"}, + "tos": { + "options": { + "max_reliability": {"type": "bool"}, + "max_throughput": {"type": "bool"}, + "min_delay": {"type": "bool"}, + "min_monetary_cost": {"type": "bool"}, + "normal": {"type": "bool"}, + "service_value": {"type": "int"}, + }, + "type": "dict", + }, + "ttl": { + "options": { + "eq": {"type": "int"}, + "gt": {"type": "int"}, + "lt": {"type": "int"}, + "neq": {"type": "int"}, + "range": { + "options": { + "end": {"type": "int"}, + "start": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + }, + "type": "list", + }, + "acl_type": {"choices": ["extended", "standard"], "type": "str"}, + "name": {"required": True, "type": "str"}, + }, + "type": "list", + }, + "afi": {"choices": ["ipv4", "ipv6"], "required": True, "type": "str"}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_address_family/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_address_family/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_address_family/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_address_family/bgp_address_family.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_address_family/bgp_address_family.py new file mode 100644 index 000000000..a52e3787c --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_address_family/bgp_address_family.py @@ -0,0 +1,835 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the ios_bgp_address_family module +""" + + +class Bgp_address_familyArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_bgp_address_family module""" + + argument_spec = { + "config": { + "type": "dict", + "options": { + "as_number": {"type": "str"}, + "address_family": { + "type": "list", + "elements": "dict", + "options": { + "afi": { + "type": "str", + "choices": [ + "ipv4", + "ipv6", + "l2vpn", + "nsap", + "rtfilter", + "vpnv4", + "vpnv6", + ], + }, + "safi": { + "type": "str", + "choices": ["flowspec", "mdt", "multicast", "mvpn", "evpn", "unicast"], + }, + "vrf": {"type": "str"}, + "aggregate_addresses": { + "type": "list", + "elements": "dict", + "aliases": ["aggregate_address"], + "options": { + "address": {"type": "str"}, + "netmask": {"type": "str"}, + "advertise_map": {"type": "str"}, + "as_confed_set": {"type": "bool"}, + "as_set": {"type": "bool"}, + "attribute_map": {"type": "str"}, + "summary_only": {"type": "bool"}, + "suppress_map": {"type": "str"}, + }, + }, + "auto_summary": {"type": "bool"}, + "bgp": { + "type": "dict", + "options": { + "additional_paths": { + "type": "dict", + "options": { + "receive": {"type": "bool"}, + "select": { + "type": "dict", + "options": { + "all": {"type": "bool"}, + "backup": {"type": "bool"}, + "best": {"type": "int"}, + "best_external": {"type": "bool"}, + "group_best": {"type": "bool"}, + }, + }, + "send": {"type": "bool"}, + "install": {"type": "bool"}, + }, + }, + "aggregate_timer": {"type": "int"}, + "dampening": { + "type": "dict", + "options": { + "penalty_half_time": {"type": "int"}, + "reuse_route_val": {"type": "int"}, + "suppress_route_val": {"type": "int"}, + "max_suppress": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "dmzlink_bw": {"type": "bool"}, + "nexthop": { + "type": "dict", + "options": { + "route_map": {"type": "str"}, + "trigger": { + "type": "dict", + "options": { + "delay": {"type": "int"}, + "enable": {"type": "bool"}, + }, + }, + }, + }, + "redistribute_internal": {"type": "bool"}, + "route_map": {"type": "bool"}, + "scan_time": {"type": "int"}, + "slow_peer": { + "type": "list", + "elements": "dict", + "options": { + "detection": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "threshold": {"type": "int"}, + }, + }, + "split_update_group": { + "type": "dict", + "options": { + "dynamic": {"type": "bool"}, + "permanent": {"type": "bool"}, + }, + }, + }, + }, + "slow_peer_options": { + "type": "dict", + "options": { + "detection": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "threshold": {"type": "int"}, + }, + }, + "split_update_group": { + "type": "dict", + "options": { + "dynamic": {"type": "bool"}, + "permanent": {"type": "bool"}, + }, + }, + }, + }, + "soft_reconfig_backup": {"type": "bool"}, + "update_group": {"type": "bool"}, + }, + }, + "default": {"type": "bool"}, + "default_information": {"type": "bool"}, + "default_metric": {"type": "int"}, + "distance": { + "type": "dict", + "options": { + "external": {"type": "int"}, + "internal": {"type": "int"}, + "local": {"type": "int"}, + }, + }, + "neighbors": { + "type": "list", + "elements": "dict", + "aliases": ["neighbor"], + "options": { + "neighbor_address": {"type": "str"}, + "address": {"type": "str"}, + "tag": {"type": "str"}, + "ipv6_address": {"type": "str", "aliases": ["ipv6_adddress"]}, + "activate": {"type": "bool"}, + "additional_paths": { + "type": "dict", + "options": { + "disable": {"type": "bool"}, + "receive": {"type": "bool"}, + "send": {"type": "bool"}, + }, + }, + "advertise": { + "type": "dict", + "options": { + "all": {"type": "bool"}, + "best": {"type": "int"}, + "group_best": {"type": "bool"}, + }, + }, + "advertises": { + "type": "dict", + "options": { + "additional_paths": { + "type": "dict", + "options": { + "all": {"type": "bool"}, + "best": {"type": "int"}, + "group_best": {"type": "bool"}, + }, + }, + "best_external": {"type": "bool"}, + "diverse_path": { + "type": "dict", + "options": { + "backup": {"type": "bool"}, + "mpath": {"type": "bool"}, + }, + }, + }, + }, + "advertise_map": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "exist_map": {"type": "str"}, + "non_exist_map": {"type": "str"}, + }, + }, + "advertisement_interval": {"type": "int"}, + "aigp": { + "type": "dict", + "options": { + "enable": {"type": "str"}, + "send": { + "type": "dict", + "options": { + "cost_community": { + "type": "dict", + "options": { + "id": {"type": "int"}, + "poi": { + "type": "dict", + "options": { + "igp_cost": {"type": "bool"}, + "pre_bestpath": {"type": "bool"}, + "transitive": {"type": "bool"}, + }, + }, + }, + }, + "med": {"type": "bool"}, + }, + }, + }, + }, + "allow_policy": {"type": "bool"}, + "allowas_in": {"type": "int"}, + "as_override": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "split_horizon": {"type": "bool"}, + }, + }, + "bmp_activate": { + "type": "dict", + "options": {"all": {"type": "bool"}, "server": {"type": "int"}}, + }, + "capability": { + "type": "dict", + "options": { + "both": {"type": "bool"}, + "receive": {"type": "bool"}, + "send": {"type": "bool"}, + }, + }, + "cluster_id": {"type": "str"}, + "default_originate": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "route_map": {"type": "str"}, + }, + }, + "description": {"type": "str"}, + "disable_connected_check": {"type": "bool"}, + "distribute_list": { + "type": "dict", + "options": { + "acl": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + }, + }, + "dmzlink_bw": {"type": "bool"}, + "ebgp_multihop": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "hop_count": {"type": "int"}, + }, + }, + "fall_over": { + "type": "dict", + "options": { + "bfd": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "multi_hop": {"type": "bool"}, + "single_hop": {"type": "bool"}, + }, + }, + "route_map": {"type": "str"}, + }, + }, + "filter_list": { + "type": "dict", + "options": { + "as_path_acl": {"type": "int"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + }, + }, + "ha_mode": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + "inherit": {"type": "str"}, + "internal_vpn_client": {"type": "bool"}, + "local_as": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "number": {"type": "int"}, + "dual_as": {"type": "bool"}, + "no_prepend": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "replace_as": {"type": "bool"}, + }, + }, + }, + }, + "log_neighbor_changes": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + "maximum_prefix": { + "type": "dict", + "options": { + "number": {"type": "int"}, + "threshold_value": {"type": "int"}, + "restart": {"type": "int"}, + "warning_only": {"type": "bool"}, + }, + }, + "next_hop_self": {"type": "bool"}, + "nexthop_self": { + "type": "dict", + "options": {"set": {"type": "bool"}, "all": {"type": "bool"}}, + }, + "next_hop_unchanged": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "allpaths": {"type": "bool"}, + }, + }, + "password": {"type": "str", "no_log": True}, + "password_options": { + "type": "dict", + "no_log": False, + "options": { + "encryption": {"type": "int"}, + "pass_key": {"type": "str", "no_log": True}, + }, + }, + "path_attribute": { + "type": "dict", + "options": { + "discard": { + "type": "dict", + "options": { + "type": {"type": "int"}, + "range": { + "type": "dict", + "options": { + "start": {"type": "int"}, + "end": {"type": "int"}, + }, + }, + "in": {"type": "bool"}, + }, + }, + "treat_as_withdraw": { + "type": "dict", + "options": { + "type": {"type": "int"}, + "range": { + "type": "dict", + "options": { + "start": {"type": "int"}, + "end": {"type": "int"}, + }, + }, + "in": {"type": "bool"}, + }, + }, + }, + }, + "peer_group": {"type": "bool"}, + "peer_group_name": {"type": "str"}, + "prefix_list": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + }, + }, + "prefix_lists": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + }, + }, + "remote_as": {"type": "int"}, + "remove_private_as": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "all": {"type": "bool"}, + "replace_as": {"type": "bool"}, + }, + }, + "route_map": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + }, + }, + "route_maps": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + }, + }, + "route_reflector_client": {"type": "bool"}, + "route_server_client": {"type": "bool"}, + "send_community": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "both": {"type": "bool"}, + "extended": {"type": "bool"}, + "standard": {"type": "bool"}, + }, + }, + "shutdown": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "graceful": {"type": "int"}, + }, + }, + "slow_peer": { + "type": "list", + "elements": "dict", + "options": { + "detection": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "disable": {"type": "bool"}, + "threshold": {"type": "int"}, + }, + }, + "split_update_group": { + "type": "dict", + "options": { + "dynamic": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "disable": {"type": "bool"}, + "permanent": {"type": "bool"}, + }, + }, + "static": {"type": "bool"}, + }, + }, + }, + }, + "slow_peer_options": { + "type": "dict", + "options": { + "detection": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "disable": {"type": "bool"}, + "threshold": {"type": "int"}, + }, + }, + "split_update_group": { + "type": "dict", + "options": { + "dynamic": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "disable": {"type": "bool"}, + "permanent": {"type": "bool"}, + }, + }, + "static": {"type": "bool"}, + }, + }, + }, + }, + "soft_reconfiguration": {"type": "bool"}, + "soo": {"type": "str"}, + "timers": { + "type": "dict", + "options": { + "interval": {"type": "int"}, + "holdtime": {"type": "int"}, + "min_holdtime": {"type": "int"}, + }, + }, + "transport": { + "type": "dict", + "options": { + "connection_mode": { + "type": "dict", + "options": { + "active": {"type": "bool"}, + "passive": {"type": "bool"}, + }, + }, + "multi_session": {"type": "bool"}, + "path_mtu_discovery": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + }, + }, + "ttl_security": {"type": "int"}, + "unsuppress_map": {"type": "str"}, + "version": {"type": "int"}, + "weight": {"type": "int"}, + }, + }, + "networks": { + "type": "list", + "elements": "dict", + "aliases": ["network"], + "options": { + "address": {"type": "str"}, + "mask": {"type": "str"}, + "backdoor": {"type": "bool"}, + "evpn": {"type": "bool"}, + "route_map": {"type": "str"}, + }, + }, + "redistribute": { + "type": "list", + "elements": "dict", + "options": { + "application": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "bgp": { + "type": "dict", + "options": { + "as_number": {"type": "str"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "connected": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "eigrp": { + "type": "dict", + "options": { + "as_number": {"type": "str"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "isis": { + "type": "dict", + "options": { + "area_tag": {"type": "str"}, + "clns": {"type": "bool"}, + "ip": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "iso_igrp": { + "type": "dict", + "options": { + "area_tag": {"type": "str"}, + "route_map": {"type": "str"}, + }, + }, + "lisp": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "mobile": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "odr": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "ospf": { + "type": "dict", + "options": { + "process_id": {"type": "int"}, + "include_connected": {"type": "bool"}, + "match": { + "type": "dict", + "options": { + "external": {"type": "bool"}, + "internal": {"type": "bool"}, + "externals": { + "type": "dict", + "options": { + "type_1": {"type": "bool"}, + "type_2": {"type": "bool"}, + }, + }, + "nssa_external": {"type": "bool"}, + "nssa_externals": { + "type": "dict", + "options": { + "type_1": {"type": "bool"}, + "type_2": {"type": "bool"}, + }, + }, + "type_1": {"type": "bool"}, + "type_2": {"type": "bool"}, + }, + }, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + "vrf": {"type": "str"}, + }, + }, + "ospfv3": { + "type": "dict", + "options": { + "process_id": {"type": "int"}, + "match": { + "type": "dict", + "options": { + "external": {"type": "bool"}, + "internal": {"type": "bool"}, + "externals": { + "type": "dict", + "options": { + "type_1": {"type": "bool"}, + "type_2": {"type": "bool"}, + }, + }, + "nssa_external": {"type": "bool"}, + "nssa_externals": { + "type": "dict", + "options": { + "type_1": {"type": "bool"}, + "type_2": {"type": "bool"}, + }, + }, + "type_1": {"type": "bool"}, + "type_2": {"type": "bool"}, + }, + }, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "rip": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "static": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "clns": {"type": "bool"}, + "ip": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "vrf": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "global": {"type": "bool"}, + }, + }, + }, + }, + "snmp": { + "type": "dict", + "options": { + "context": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "community": { + "type": "dict", + "options": { + "snmp_community": {"type": "str"}, + "acl": {"type": "str"}, + "ipv6": {"type": "str"}, + "ro": {"type": "bool"}, + "rw": {"type": "bool"}, + }, + }, + "user": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "access": { + "type": "dict", + "options": { + "acl": {"type": "str"}, + "ipv6": {"type": "str"}, + }, + }, + "auth": { + "type": "dict", + "options": { + "md5": {"type": "str"}, + "sha": {"type": "str"}, + }, + }, + "priv": { + "type": "dict", + "options": { + "des": {"type": "str"}, + "des56": {"type": "str"}, + "aes128": {"type": "str"}, + "aes192": {"type": "str"}, + "aes256": {"type": "str"}, + }, + }, + "credential": {"type": "bool"}, + "encrypted": {"type": "bool"}, + }, + }, + }, + }, + }, + }, + "table_map": { + "type": "dict", + "options": {"name": {"type": "str"}, "filter": {"type": "bool"}}, + }, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py new file mode 100644 index 000000000..38c4e1ab3 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py @@ -0,0 +1,952 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the ios_bgp_global module +""" + + +class Bgp_globalArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_bgp_global module""" + + argument_spec = { + "config": { + "type": "dict", + "options": { + "as_number": {"type": "str"}, + "aggregate_address": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "netmask": {"type": "str"}, + "advertise_map": {"type": "str"}, + "as_confed_set": {"type": "bool"}, + "as_set": {"type": "bool"}, + "attribute_map": {"type": "str"}, + "summary_only": {"type": "bool"}, + "suppress_map": {"type": "str"}, + }, + }, + "aggregate_addresses": { + "type": "list", + "elements": "dict", + "options": { + "address": {"type": "str"}, + "netmask": {"type": "str"}, + "advertise_map": {"type": "str"}, + "as_confed_set": {"type": "bool"}, + "as_set": {"type": "bool"}, + "attribute_map": {"type": "str"}, + "summary_only": {"type": "bool"}, + "suppress_map": {"type": "str"}, + }, + }, + "auto_summary": {"type": "bool"}, + "bgp": { + "type": "dict", + "options": { + "additional_paths": { + "type": "dict", + "options": { + "install": {"type": "bool"}, + "receive": {"type": "bool"}, + "select": { + "type": "dict", + "options": { + "all": {"type": "bool"}, + "best": {"type": "int"}, + "best_external": {"type": "bool"}, + "group_best": {"type": "bool"}, + }, + }, + "send": {"type": "bool"}, + }, + }, + "advertise_best_external": {"type": "bool"}, + "aggregate_timer": {"type": "int"}, + "always_compare_med": {"type": "bool"}, + "asnotation": {"type": "bool"}, + "bestpath": { + "type": "list", + "elements": "dict", + "options": { + "aigp": {"type": "bool"}, + "compare_routerid": {"type": "bool"}, + "cost_community": {"type": "bool"}, + "igp_metric": {"type": "bool"}, + "med": { + "type": "dict", + "options": { + "confed": {"type": "bool"}, + "missing_as_worst": {"type": "bool"}, + }, + }, + }, + }, + "bestpath_options": { + "type": "dict", + "options": { + "aigp": {"type": "bool"}, + "compare_routerid": {"type": "bool"}, + "cost_community": {"type": "bool"}, + "igp_metric": {"type": "bool"}, + "med": { + "type": "dict", + "options": { + "confed": {"type": "bool"}, + "missing_as_worst": {"type": "bool"}, + }, + }, + }, + }, + "client_to_client": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "all": {"type": "bool"}, + "intra_cluster": {"type": "str"}, + }, + }, + "cluster_id": {"type": "str"}, + "confederation": { + "type": "dict", + "options": {"identifier": {"type": "str"}, "peers": {"type": "str"}}, + }, + "consistency_checker": { + "type": "dict", + "options": { + "auto_repair": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "interval": {"type": "int"}, + }, + }, + "error_message": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "interval": {"type": "int"}, + }, + }, + }, + }, + "dampening": { + "type": "dict", + "options": { + "penalty_half_time": {"type": "int"}, + "reuse_route_val": {"type": "int"}, + "suppress_route_val": {"type": "int"}, + "max_suppress": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "deterministic_med": {"type": "bool"}, + "dmzlink_bw": {"type": "bool"}, + "enforce_first_as": {"type": "bool"}, + "enhanced_error": {"type": "bool"}, + "fast_external_fallover": {"type": "bool"}, + "graceful_restart": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "extended": {"type": "bool"}, + "restart_time": {"type": "int"}, + "stalepath_time": {"type": "int"}, + }, + }, + "graceful_shutdown": { + "type": "dict", + "options": { + "neighbors": { + "type": "dict", + "options": { + "time": {"type": "int"}, + "activate": {"type": "bool"}, + }, + }, + "vrfs": { + "type": "dict", + "options": { + "time": {"type": "int"}, + "activate": {"type": "bool"}, + }, + }, + "community": {"type": "str"}, + "local_preference": {"type": "int"}, + }, + }, + "inject_map": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "exist_map_name": {"type": "str"}, + "copy_attributes": {"type": "bool"}, + }, + }, + "inject_maps": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"}, + "exist_map_name": {"type": "str"}, + "copy_attributes": {"type": "bool"}, + }, + }, + "listen": { + "type": "dict", + "options": { + "limit": {"type": "int"}, + "range": { + "type": "dict", + "options": { + "ipv4_with_subnet": {"type": "str"}, + "ipv6_with_subnet": {"type": "str"}, + "host_with_subnet": {"type": "str"}, + "peer_group": {"type": "str"}, + }, + }, + }, + }, + "log_neighbor_changes": {"type": "bool"}, + "maxas_limit": {"type": "int"}, + "maxcommunity_limit": {"type": "int"}, + "maxextcommunity_limit": {"type": "int"}, + "nexthop": { + "type": "dict", + "options": { + "route_map": {"type": "str"}, + "trigger": { + "type": "dict", + "options": { + "delay": {"type": "int"}, + "enable": {"type": "bool"}, + }, + }, + }, + }, + "nopeerup_delay": { + "type": "list", + "elements": "dict", + "options": { + "cold_boot": {"type": "int"}, + "nsf_switchover": {"type": "int"}, + "post_boot": {"type": "int"}, + "user_initiated": {"type": "int"}, + }, + }, + "nopeerup_delay_options": { + "type": "dict", + "options": { + "cold_boot": {"type": "int"}, + "nsf_switchover": {"type": "int"}, + "post_boot": {"type": "int"}, + "user_initiated": {"type": "int"}, + }, + }, + "recursion": {"type": "bool"}, + "redistribute_internal": {"type": "bool"}, + "refresh": { + "type": "dict", + "options": { + "max_eor_time": {"type": "int"}, + "stalepath_time": {"type": "int"}, + }, + }, + "regexp": {"type": "bool"}, + "route_map": {"type": "bool"}, + "router_id": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "interface": {"type": "str"}, + "vrf": {"type": "bool"}, + }, + }, + "scan_time": {"type": "int"}, + "slow_peer": { + "type": "dict", + "options": { + "detection": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "threshold": {"type": "int"}, + }, + }, + "split_update_group": { + "type": "dict", + "options": { + "dynamic": {"type": "bool"}, + "permanent": {"type": "bool"}, + }, + }, + }, + }, + "snmp": {"type": "bool"}, + "sso": {"type": "bool"}, + "soft_reconfig_backup": {"type": "bool"}, + "suppress_inactive": {"type": "bool"}, + "transport": {"type": "bool"}, + "update_delay": {"type": "int"}, + "update_group": {"type": "bool"}, + "upgrade_cli": { + "type": "dict", + "options": {"set": {"type": "bool"}, "af_mode": {"type": "bool"}}, + }, + }, + }, + "bmp": { + "type": "dict", + "options": { + "buffer_size": {"type": "int"}, + "initial_refresh": { + "type": "dict", + "options": {"delay": {"type": "int"}, "skip": {"type": "bool"}}, + }, + "server": {"type": "int"}, + "server_options": { + "type": "dict", + "options": { + "activate": {"type": "bool"}, + "address": { + "type": "dict", + "options": {"host": {"type": "str"}, "port": {"type": "int"}}, + }, + }, + }, + }, + }, + "default_information": {"type": "bool"}, + "default_metric": {"type": "int"}, + "distance": { + "type": "dict", + "options": { + "admin": { + "type": "dict", + "options": { + "distance": {"type": "int"}, + "address": {"type": "str"}, + "wildcard_bit": {"type": "str"}, + "acl": {"type": "str"}, + }, + }, + "bgp": { + "type": "dict", + "options": { + "routes_external": {"type": "int"}, + "routes_internal": {"type": "int"}, + "routes_local": {"type": "int"}, + }, + }, + "mbgp": { + "type": "dict", + "options": { + "routes_external": {"type": "int"}, + "routes_internal": {"type": "int"}, + "routes_local": {"type": "int"}, + }, + }, + }, + }, + "distributes": { + "type": "list", + "elements": "dict", + "options": { + "prefix": {"type": "str"}, + "gateway": {"type": "str"}, + "acl": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + "interface": {"type": "str"}, + }, + }, + "distribute_list": { + "type": "dict", + "options": { + "acl": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + "interface": {"type": "str"}, + }, + }, + "maximum_paths": { + "type": "dict", + "options": { + "paths": {"type": "int"}, + "eibgp": {"type": "int"}, + "ibgp": {"type": "int"}, + }, + }, + "maximum_secondary_paths": { + "type": "dict", + "options": { + "paths": {"type": "int"}, + "eibgp": {"type": "int"}, + "ibgp": {"type": "int"}, + }, + }, + "neighbors": { + "type": "list", + "elements": "dict", + "aliases": ["neighbor"], + "options": { + "neighbor_address": {"type": "str"}, + "address": {"type": "str"}, + "tag": {"type": "str"}, + "ipv6_adddress": {"type": "str"}, + "activate": {"type": "bool"}, + "additional_paths": { + "type": "dict", + "options": { + "disable": {"type": "bool"}, + "receive": {"type": "bool"}, + "send": {"type": "bool"}, + }, + }, + "advertise": { + "type": "dict", + "options": { + "additional_paths": { + "type": "dict", + "options": { + "all": {"type": "bool"}, + "best": {"type": "int"}, + "group_best": {"type": "bool"}, + }, + }, + "best_external": {"type": "bool"}, + "diverse_path": { + "type": "dict", + "options": { + "backup": {"type": "bool"}, + "mpath": {"type": "bool"}, + }, + }, + }, + }, + "advertise_map": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "exist_map": {"type": "str"}, + "non_exist_map": {"type": "str"}, + }, + }, + "advertisement_interval": {"type": "int"}, + "aigp": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "send": { + "type": "dict", + "options": { + "cost_community": { + "type": "dict", + "options": { + "id": {"type": "int"}, + "poi": { + "type": "dict", + "options": { + "igp_cost": {"type": "bool"}, + "pre_bestpath": {"type": "bool"}, + "transitive": {"type": "bool"}, + }, + }, + }, + }, + "med": {"type": "bool"}, + }, + }, + }, + }, + "allow_policy": {"type": "bool"}, + "allowas_in": {"type": "int"}, + "as_override": {"type": "bool"}, + "bmp_activate": { + "type": "dict", + "options": {"all": {"type": "bool"}, "server": {"type": "int"}}, + }, + "capability": { + "type": "dict", + "options": { + "both": {"type": "bool"}, + "receive": {"type": "bool"}, + "send": {"type": "bool"}, + }, + }, + "cluster_id": {"type": "str"}, + "default_originate": { + "type": "dict", + "options": {"set": {"type": "bool"}, "route_map": {"type": "str"}}, + }, + "description": {"type": "str"}, + "disable_connected_check": {"type": "bool"}, + "distribute_list": { + "type": "dict", + "options": { + "acl": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + }, + }, + "dmzlink_bw": {"type": "bool"}, + "ebgp_multihop": { + "type": "dict", + "options": {"enable": {"type": "bool"}, "hop_count": {"type": "int"}}, + }, + "fall_over": { + "type": "dict", + "options": { + "bfd": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "multi_hop": {"type": "bool"}, + "single_hop": {"type": "bool"}, + }, + }, + "route_map": {"type": "str"}, + }, + }, + "filter_list": { + "type": "dict", + "options": { + "path_acl": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + }, + }, + "ha_mode": { + "type": "dict", + "options": {"set": {"type": "bool"}, "disable": {"type": "bool"}}, + }, + "inherit": {"type": "str"}, + "local_as": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "number": {"type": "int"}, + "dual_as": {"type": "bool"}, + "no_prepend": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "replace_as": {"type": "bool"}, + }, + }, + }, + }, + "log_neighbor_changes": { + "type": "dict", + "options": {"set": {"type": "bool"}, "disable": {"type": "bool"}}, + }, + "maximum_prefix": { + "type": "dict", + "options": { + "max_no": {"type": "int"}, + "threshold_val": {"type": "int"}, + "restart": {"type": "int"}, + "warning_only": {"type": "bool"}, + }, + }, + "next_hop_self": { + "type": "dict", + "options": {"set": {"type": "bool"}, "all": {"type": "bool"}}, + }, + "next_hop_unchanged": { + "type": "dict", + "options": {"set": {"type": "bool"}, "allpaths": {"type": "bool"}}, + }, + "password": {"type": "str", "no_log": True}, + "password_options": { + "type": "dict", + "no_log": False, + "options": { + "encryption": {"type": "int"}, + "pass_key": {"type": "str", "no_log": True}, + }, + }, + "path_attribute": { + "type": "dict", + "options": { + "discard": { + "type": "dict", + "options": { + "type": {"type": "int"}, + "range": { + "type": "dict", + "options": { + "start": {"type": "int"}, + "end": {"type": "int"}, + }, + }, + "in": {"type": "bool"}, + }, + }, + "treat_as_withdraw": { + "type": "dict", + "options": { + "type": {"type": "int"}, + "range": { + "type": "dict", + "options": { + "start": {"type": "int"}, + "end": {"type": "int"}, + }, + }, + "in": {"type": "bool"}, + }, + }, + }, + }, + "peer_group": {"type": "str"}, + "remote_as": {"type": "str"}, + "remove_private_as": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "all": {"type": "bool"}, + "replace_as": {"type": "bool"}, + }, + }, + "route_map": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + }, + }, + "route_maps": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"}, + "in": {"type": "bool"}, + "out": {"type": "bool"}, + }, + }, + "route_reflector_client": {"type": "bool"}, + "route_server_client": { + "type": "dict", + "options": {"set": {"type": "bool"}, "context": {"type": "str"}}, + }, + "send_community": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "both": {"type": "bool"}, + "extended": {"type": "bool"}, + "standard": {"type": "bool"}, + }, + }, + "send_label": { + "type": "dict", + "options": {"set": {"type": "bool"}, "explicit_null": {"type": "bool"}}, + }, + "shutdown": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "graceful": {"type": "int"}, + "community": {"type": "int"}, + "local_preference": {"type": "bool"}, + }, + }, + "slow_peer": { + "type": "dict", + "options": { + "detection": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "disable": {"type": "bool"}, + "threshold": {"type": "int"}, + }, + }, + "split_update_group": { + "type": "dict", + "options": { + "dynamic": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "disable": {"type": "bool"}, + "permanent": {"type": "bool"}, + }, + }, + "static": {"type": "bool"}, + }, + }, + }, + }, + "soft_reconfiguration": {"type": "bool"}, + "timers": { + "type": "dict", + "options": { + "interval": {"type": "int"}, + "holdtime": {"type": "int"}, + "min_holdtime": {"type": "int"}, + }, + }, + "translate_update": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "nlri": { + "type": "dict", + "options": { + "multicast": {"type": "bool"}, + "unicast": {"type": "bool"}, + }, + }, + }, + }, + "transport": { + "type": "dict", + "options": { + "connection_mode": { + "type": "dict", + "options": { + "active": {"type": "bool"}, + "passive": {"type": "bool"}, + }, + }, + "multi_session": {"type": "bool"}, + "path_mtu_discovery": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + }, + }, + "ttl_security": {"type": "int"}, + "unsuppress_map": {"type": "str"}, + "update_source": {"type": "str"}, + "version": {"type": "int"}, + "weight": {"type": "int"}, + }, + }, + "networks": { + "type": "list", + "elements": "dict", + "options": { + "address": {"type": "str"}, + "netmask": {"type": "str"}, + "route_map": {"type": "str"}, + "backdoor": {"type": "bool"}, + }, + }, + "redistribute": { + "type": "list", + "elements": "dict", + "options": { + "application": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "bgp": { + "type": "dict", + "options": { + "as_number": {"type": "str"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "connected": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "eigrp": { + "type": "dict", + "options": { + "as_number": {"type": "str"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "isis": { + "type": "dict", + "options": { + "area_tag": {"type": "str"}, + "clns": {"type": "bool"}, + "ip": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "iso_igrp": { + "type": "dict", + "options": {"area_tag": {"type": "str"}, "route_map": {"type": "str"}}, + }, + "lisp": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "mobile": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "odr": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "ospf": { + "type": "dict", + "options": { + "process_id": {"type": "int"}, + "match": { + "type": "dict", + "options": { + "external": {"type": "bool"}, + "internal": {"type": "bool"}, + "nssa_external": {"type": "bool"}, + "type_1": {"type": "bool"}, + "type_2": {"type": "bool"}, + }, + }, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + "vrf": {"type": "str"}, + }, + }, + "ospfv3": { + "type": "dict", + "options": { + "process_id": {"type": "int"}, + "match": { + "type": "dict", + "options": { + "external": {"type": "bool"}, + "internal": {"type": "bool"}, + "nssa_external": {"type": "bool"}, + "type_1": {"type": "bool"}, + "type_2": {"type": "bool"}, + }, + }, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "rip": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "static": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "clns": {"type": "bool"}, + "ip": {"type": "bool"}, + "metric": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "vrf": { + "type": "dict", + "options": {"name": {"type": "str"}, "global": {"type": "bool"}}, + }, + }, + }, + "route_server_context": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "address_family": { + "type": "dict", + "options": { + "afi": {"type": "str", "choices": ["ipv4", "ipv6"]}, + "modifier": {"type": "str", "choices": ["multicast", "unicast"]}, + "import_map": {"type": "str"}, + }, + }, + "description": {"type": "str"}, + }, + }, + "scope": { + "type": "dict", + "options": {"global": {"type": "bool"}, "vrf": {"type": "str"}}, + }, + "synchronization": {"type": "bool"}, + "table_map": { + "type": "dict", + "options": {"name": {"type": "str"}, "filter": {"type": "bool"}}, + }, + "template": { + "type": "dict", + "options": {"peer_policy": {"type": "str"}, "peer_session": {"type": "str"}}, + }, + "timers": { + "type": "dict", + "options": { + "keepalive": {"type": "int"}, + "holdtime": {"type": "int"}, + "min_holdtime": {"type": "int"}, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "purged", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/facts/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/facts/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/facts/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/facts/facts.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/facts/facts.py new file mode 100644 index 000000000..18c33971b --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/facts/facts.py @@ -0,0 +1,26 @@ +# +# -*- 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) +""" +The arg spec for the ios facts module. +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class FactsArgs(object): + """The arg spec for the ios facts module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "gather_subset": dict(default=["min"], type="list", elements="str"), + "gather_network_resources": dict(type="list", elements="str"), + "available_network_resources": {"type": "bool", "default": False}, + } diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/hostname/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/hostname/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/hostname/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/hostname/hostname.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/hostname/hostname.py new file mode 100644 index 000000000..f3cdca182 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/hostname/hostname.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the ios_hostname module +""" + + +class HostnameArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_hostname module""" + + argument_spec = { + "config": {"type": "dict", "options": {"hostname": {"type": "str"}}}, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "gathered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/interfaces/interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/interfaces/interfaces.py new file mode 100644 index 000000000..ce5cbb65f --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/interfaces/interfaces.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the ios_interfaces module +""" + + +class InterfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_interfaces module""" + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "description": {"type": "str"}, + "enabled": {"type": "bool", "default": True}, + "speed": {"type": "str"}, + "mtu": {"type": "int"}, + "mode": {"choices": ["layer2", "layer3"], "type": "str"}, + "duplex": {"type": "str", "choices": ["full", "half", "auto"]}, + "template": {"type": "str"}, + }, + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "gathered", + "purged", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l2_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l2_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l2_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l2_interfaces/l2_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l2_interfaces/l2_interfaces.py new file mode 100644 index 000000000..e7a65dd1c --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l2_interfaces/l2_interfaces.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the ios_l2_interfaces module +""" + + +class L2_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_l2_interfaces module""" + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "access": { + "type": "dict", + "options": {"vlan": {"type": "int"}, "vlan_name": {"type": "str"}}, + }, + "voice": { + "type": "dict", + "options": { + "vlan": {"type": "int"}, + "vlan_tag": {"choices": ["dot1p", "none", "untagged"], "type": "str"}, + "vlan_name": {"type": "str"}, + }, + }, + "trunk": { + "type": "dict", + "options": { + "allowed_vlans": {"type": "list", "elements": "str"}, + "native_vlan": {"type": "int"}, + "encapsulation": {"choices": ["dot1q", "isl", "negotiate"], "type": "str"}, + "pruning_vlans": {"type": "list", "elements": "str"}, + }, + }, + "mode": { + "type": "str", + "choices": [ + "access", + "trunk", + "dot1q_tunnel", + "dynamic", + "dynamic_auto", + "dynamic_desirable", + "private_vlan_host", + "private_vlan_promiscuous", + "private_vlan_trunk", + ], + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "gathered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/l3_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/l3_interfaces.py new file mode 100644 index 000000000..455261409 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/l3_interfaces.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the ios_l3_interfaces module +""" + + +class L3_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_l3_interfaces module""" + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "ipv4": { + "type": "list", + "elements": "dict", + "options": { + "address": {"type": "str"}, + "secondary": {"type": "bool"}, + "dhcp_client": {"type": "str"}, + "dhcp_hostname": {"type": "str"}, + "dhcp": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "client_id": {"type": "str"}, + "hostname": {"type": "str"}, + }, + }, + "pool": {"type": "str"}, + }, + }, + "ipv6": { + "type": "list", + "elements": "dict", + "options": { + "address": {"type": "str"}, + "autoconfig": { + "type": "dict", + "options": {"enable": {"type": "bool"}, "default": {"type": "bool"}}, + }, + "dhcp": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "rapid_commit": {"type": "bool"}, + }, + }, + "anycast": {"type": "bool"}, + "cga": {"type": "bool"}, + "eui": {"type": "bool"}, + "link_local": {"type": "bool"}, + "segment_routing": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "default": {"type": "bool"}, + "ipv6_sr": {"type": "bool"}, + }, + }, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "gathered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp/lacp.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp/lacp.py new file mode 100644 index 000000000..3e346db12 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp/lacp.py @@ -0,0 +1,57 @@ +# +# -*- 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 arg spec for the ios_lacp module +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class LacpArgs(object): + """The arg spec for the ios_lacp module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "system": { + "options": {"priority": {"required": True, "type": "int"}}, + "type": "dict", + }, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": ["merged", "replaced", "deleted", "rendered", "parsed", "gathered"], + "default": "merged", + "type": "str", + }, + } diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp_interfaces/lacp_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 000000000..594cfa122 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lacp_interfaces/lacp_interfaces.py @@ -0,0 +1,66 @@ +# +# -*- 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 arg spec for the ios_lacp_interfaces module +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Lacp_InterfacesArgs(object): + """The arg spec for the ios_lacp_interfaces module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "name": {"required": True, "type": "str"}, + "port_priority": {"type": "int"}, + "fast_switchover": {"type": "bool"}, + "max_bundle": {"type": "int"}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "parsed", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lag_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lag_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lag_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lag_interfaces/lag_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lag_interfaces/lag_interfaces.py new file mode 100644 index 000000000..29332f73e --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lag_interfaces/lag_interfaces.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the ios_lag_interfaces module +""" + + +class Lag_InterfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_lag_interfaces module""" + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "members": { + "type": "list", + "elements": "dict", + "options": { + "member": {"type": "str"}, + "mode": { + "type": "str", + "choices": ["auto", "desirable", "on", "active", "passive"], + }, + "link": {"type": "int"}, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "type": "str", + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "parsed", + "gathered", + ], + "default": "merged", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_global/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_global/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_global/lldp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_global/lldp_global.py new file mode 100644 index 000000000..841e34bed --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_global/lldp_global.py @@ -0,0 +1,68 @@ +# +# -*- 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 arg spec for the ios_lldp_global module +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Lldp_globalArgs(object): + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "holdtime": {"type": "int"}, + "reinit": {"type": "int"}, + "enabled": {"type": "bool"}, + "timer": {"type": "int"}, + "tlv_select": { + "options": { + "four_wire_power_management": {"type": "bool"}, + "mac_phy_cfg": {"type": "bool"}, + "management_address": {"type": "bool"}, + "port_description": {"type": "bool"}, + "port_vlan": {"type": "bool"}, + "power_management": {"type": "bool"}, + "system_capabilities": {"type": "bool"}, + "system_description": {"type": "bool"}, + "system_name": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": ["merged", "replaced", "deleted", "rendered", "parsed", "gathered"], + "default": "merged", + "type": "str", + }, + } diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_interfaces/lldp_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_interfaces/lldp_interfaces.py new file mode 100644 index 000000000..080d3fe97 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/lldp_interfaces/lldp_interfaces.py @@ -0,0 +1,70 @@ +# +# -*- 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 arg spec for the ios_lldp_interfaces module +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Lldp_InterfacesArgs(object): + """The arg spec for the ios_lldp_interfaces module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "name": {"required": True, "type": "str"}, + "transmit": {"type": "bool"}, + "receive": {"type": "bool"}, + "med_tlv_select": { + "options": {"inventory_management": {"type": "bool"}}, + "type": "dict", + }, + "tlv_select": {"options": {"power_management": {"type": "bool"}}, "type": "dict"}, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "parsed", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/logging_global/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/logging_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/logging_global/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/logging_global/logging_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/logging_global/logging_global.py new file mode 100644 index 000000000..44dd19352 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/logging_global/logging_global.py @@ -0,0 +1,374 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the ios_logging_global module +""" + + +class Logging_globalArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_logging_global module""" + + argument_spec = { + "config": { + "type": "dict", + "options": { + "buffered": { + "type": "dict", + "options": { + "size": {"type": "int"}, + "severity": { + "type": "str", + "choices": [ + "alerts", + "critical", + "debugging", + "emergencies", + "errors", + "informational", + "notifications", + "warnings", + ], + }, + "discriminator": {"type": "str"}, + "filtered": {"type": "bool"}, + "xml": {"type": "bool"}, + }, + }, + "buginf": {"type": "bool"}, + "cns_events": { + "type": "str", + "choices": [ + "alerts", + "critical", + "debugging", + "emergencies", + "errors", + "informational", + "notifications", + "warnings", + ], + }, + "console": { + "type": "dict", + "options": { + "severity": { + "type": "str", + "choices": [ + "alerts", + "critical", + "debugging", + "emergencies", + "errors", + "informational", + "notifications", + "warnings", + "guaranteed", + ], + }, + "discriminator": {"type": "str"}, + "filtered": {"type": "bool"}, + "xml": {"type": "bool"}, + }, + }, + "count": {"type": "bool"}, + "delimiter": {"type": "dict", "options": {"tcp": {"type": "bool"}}}, + "discriminator": {"type": "list", "elements": "str"}, + "dmvpn": {"type": "dict", "options": {"rate_limit": {"type": "int"}}}, + "esm": {"type": "dict", "options": {"config": {"type": "bool"}}}, + "exception": {"type": "int"}, + "facility": { + "type": "str", + "choices": [ + "auth", + "cron", + "daemon", + "kern", + "local0", + "local1", + "local2", + "local3", + "local4", + "local5", + "local6", + "local7", + "lpr", + "mail", + "news", + "sys10", + "sys11", + "sys12", + "sys13", + "sys14", + "sys9", + "syslog", + "user", + "uucp", + ], + }, + "filter": { + "type": "list", + "elements": "dict", + "options": { + "url": {"type": "str"}, + "order": {"type": "int"}, + "args": {"type": "str"}, + }, + }, + "history": { + "type": "dict", + "options": { + "size": {"type": "int"}, + "severity": { + "type": "str", + "choices": [ + "alerts", + "critical", + "debugging", + "emergencies", + "errors", + "informational", + "notifications", + "warnings", + ], + }, + }, + }, + "hosts": { + "type": "list", + "elements": "dict", + "options": { + "discriminator": {"type": "str"}, + "filtered": {"type": "bool"}, + "sequence_num_session": {"type": "bool"}, + "session_id": { + "type": "dict", + "options": { + "tag": {"type": "str", "choices": ["hostname", "ipv4", "ipv6"]}, + "text": {"type": "str"}, + }, + }, + "stream": {"type": "int"}, + "transport": { + "type": "dict", + "options": { + "tcp": { + "type": "dict", + "options": { + "audit": {"type": "bool"}, + "discriminator": {"type": "str"}, + "stream": {"type": "int"}, + "filtered": {"type": "bool"}, + "port": {"type": "int"}, + "sequence_num_session": {"type": "bool"}, + "session_id": { + "type": "dict", + "options": { + "tag": { + "type": "str", + "choices": ["hostname", "ipv4", "ipv6"], + }, + "text": {"type": "str"}, + }, + }, + "xml": {"type": "bool"}, + }, + }, + "udp": { + "type": "dict", + "options": { + "discriminator": {"type": "str"}, + "stream": {"type": "int"}, + "filtered": {"type": "bool"}, + "port": {"type": "int"}, + "sequence_num_session": {"type": "bool"}, + "session_id": { + "type": "dict", + "options": { + "tag": { + "type": "str", + "choices": ["hostname", "ipv4", "ipv6"], + }, + "text": {"type": "str"}, + }, + }, + "xml": {"type": "bool"}, + }, + }, + }, + }, + "vrf": {"type": "str"}, + "xml": {"type": "bool"}, + "ipv6": {"type": "str"}, + "host": {"type": "str", "aliases": ["hostname"]}, + }, + }, + "message_counter": { + "type": "list", + "elements": "str", + "choices": ["log", "debug", "syslog"], + }, + "monitor": { + "type": "dict", + "options": { + "severity": { + "type": "str", + "choices": [ + "alerts", + "critical", + "debugging", + "emergencies", + "errors", + "informational", + "notifications", + "warnings", + ], + }, + "discriminator": {"type": "str"}, + "filtered": {"type": "bool"}, + "xml": {"type": "bool"}, + }, + }, + "logging_on": {"type": "str", "choices": ["enable", "disable"]}, + "origin_id": { + "type": "dict", + "options": { + "tag": {"type": "str", "choices": ["hostname", "ip", "ipv6"]}, + "text": {"type": "str"}, + }, + }, + "persistent": { + "type": "dict", + "options": { + "batch": {"type": "int"}, + "filesize": {"type": "int"}, + "immediate": {"type": "bool"}, + "notify": {"type": "bool"}, + "protected": {"type": "bool"}, + "size": {"type": "int"}, + "threshold": {"type": "int"}, + "url": {"type": "str"}, + }, + }, + "policy_firewall": {"type": "dict", "options": {"rate_limit": {"type": "int"}}}, + "queue_limit": { + "type": "dict", + "options": { + "size": {"type": "int"}, + "esm": {"type": "int"}, + "trap": {"type": "int"}, + }, + }, + "rate_limit": { + "type": "dict", + "options": { + "size": {"type": "int", "required": True}, + "all": {"type": "bool"}, + "console": {"type": "bool"}, + "except_severity": { + "type": "str", + "choices": [ + "alerts", + "critical", + "debugging", + "emergencies", + "errors", + "informational", + "notifications", + "warnings", + ], + }, + }, + }, + "reload": { + "type": "dict", + "options": { + "severity": { + "type": "str", + "choices": [ + "alerts", + "critical", + "debugging", + "emergencies", + "errors", + "informational", + "notifications", + "warnings", + ], + }, + "message_limit": {"type": "int"}, + }, + }, + "server_arp": {"type": "bool"}, + "snmp_trap": { + "type": "list", + "elements": "str", + "choices": [ + "alerts", + "critical", + "debugging", + "emergencies", + "errors", + "informational", + "notifications", + "warnings", + ], + }, + "source_interface": { + "type": "list", + "elements": "dict", + "options": {"interface": {"type": "str"}, "vrf": {"type": "str"}}, + }, + "trap": { + "type": "str", + "choices": [ + "alerts", + "critical", + "debugging", + "emergencies", + "errors", + "informational", + "notifications", + "warnings", + ], + }, + "userinfo": {"type": "bool"}, + }, + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ntp_global/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ntp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ntp_global/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ntp_global/ntp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ntp_global/ntp_global.py new file mode 100644 index 000000000..dd36984c1 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ntp_global/ntp_global.py @@ -0,0 +1,177 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the ios_ntp_global module +""" + + +class Ntp_globalArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_ntp_global module""" + + argument_spec = { + "config": { + "type": "dict", + "options": { + "access_group": { + "type": "dict", + "options": { + "peer": { + "type": "list", + "elements": "dict", + "options": { + "access_list": {"type": "str"}, + "kod": {"type": "bool"}, + "ipv4": {"type": "bool"}, + "ipv6": {"type": "bool"}, + }, + }, + "query_only": { + "type": "list", + "elements": "dict", + "options": { + "access_list": {"type": "str"}, + "kod": {"type": "bool"}, + "ipv4": {"type": "bool"}, + "ipv6": {"type": "bool"}, + }, + }, + "serve": { + "type": "list", + "elements": "dict", + "options": { + "access_list": {"type": "str"}, + "kod": {"type": "bool"}, + "ipv4": {"type": "bool"}, + "ipv6": {"type": "bool"}, + }, + }, + "serve_only": { + "type": "list", + "elements": "dict", + "options": { + "access_list": {"type": "str"}, + "kod": {"type": "bool"}, + "ipv4": {"type": "bool"}, + "ipv6": {"type": "bool"}, + }, + }, + }, + }, + "allow": { + "type": "dict", + "options": { + "control": {"type": "dict", "options": {"rate_limit": {"type": "int"}}}, + "private": {"type": "bool"}, + }, + }, + "authenticate": {"type": "bool"}, + "authentication_keys": { + "type": "list", + "elements": "dict", + "no_log": False, + "options": { + "id": {"type": "int"}, + "algorithm": {"type": "str"}, + "key": {"type": "str", "no_log": True}, + "encryption": {"type": "int"}, + }, + }, + "broadcast_delay": {"type": "int"}, + "clock_period": {"type": "int"}, + "logging": {"type": "bool"}, + "master": { + "type": "dict", + "options": {"enabled": {"type": "bool"}, "stratum": {"type": "int"}}, + }, + "max_associations": {"type": "int"}, + "max_distance": {"type": "int"}, + "min_distance": {"type": "int"}, + "orphan": {"type": "int"}, + "panic_update": {"type": "bool"}, + "passive": {"type": "bool"}, + "peers": { + "type": "list", + "elements": "dict", + "options": { + "peer": {"type": "str"}, + "use_ipv4": {"type": "bool"}, + "use_ipv6": {"type": "bool"}, + "vrf": {"type": "str"}, + "burst": {"type": "bool"}, + "iburst": {"type": "bool"}, + "key_id": {"type": "int", "aliases": ["key"]}, + "maxpoll": {"type": "int"}, + "minpoll": {"type": "int"}, + "normal_sync": {"type": "bool"}, + "prefer": {"type": "bool"}, + "source": {"type": "str"}, + "version": {"type": "int"}, + }, + }, + "servers": { + "type": "list", + "elements": "dict", + "options": { + "server": {"type": "str"}, + "use_ipv4": {"type": "bool"}, + "use_ipv6": {"type": "bool"}, + "vrf": {"type": "str"}, + "burst": {"type": "bool"}, + "iburst": {"type": "bool"}, + "key_id": {"type": "int", "aliases": ["key"]}, + "maxpoll": {"type": "int"}, + "minpoll": {"type": "int"}, + "normal_sync": {"type": "bool"}, + "prefer": {"type": "bool"}, + "source": {"type": "str"}, + "version": {"type": "int"}, + }, + }, + "source": {"type": "str"}, + "trusted_keys": { + "type": "list", + "elements": "dict", + "no_log": False, + "options": {"range_start": {"type": "int"}, "range_end": {"type": "int"}}, + }, + "update_calendar": {"type": "bool"}, + }, + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "gathered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospf_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospf_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospf_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospf_interfaces/ospf_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 000000000..fafaeb97f --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospf_interfaces/ospf_interfaces.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +# Copyright 2023 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# ansible.content_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the documentation in the module file and re-run +# ansible.content_builder commenting out +# the path to external 'docstring' in build.yaml. +# +############################################## + +""" +The arg spec for the ios_ospf_interfaces module +""" + + +class Ospf_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_ospf_interfaces module""" + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "address_family": { + "type": "list", + "elements": "dict", + "options": { + "afi": { + "type": "str", + "choices": ["ipv4", "ipv6"], + "required": True, + }, + "process": { + "type": "dict", + "options": { + "id": {"type": "int"}, + "area_id": {"type": "str"}, + "secondaries": {"type": "bool"}, + "instance_id": {"type": "int"}, + }, + }, + "adjacency": {"type": "bool"}, + "authentication": { + "type": "dict", + "options": { + "key_chain": {"type": "str", "no_log": True}, + "message_digest": {"type": "bool"}, + "null": {"type": "bool"}, + }, + }, + "bfd": {"type": "bool"}, + "cost": { + "type": "dict", + "options": { + "interface_cost": {"type": "int"}, + "dynamic_cost": { + "type": "dict", + "options": { + "default": {"type": "int"}, + "hysteresis": { + "type": "dict", + "options": { + "percent": {"type": "int"}, + "threshold": {"type": "int"}, + }, + }, + "weight": { + "type": "dict", + "options": { + "l2_factor": {"type": "int"}, + "latency": {"type": "int"}, + "oc": {"type": "bool"}, + "resources": {"type": "int"}, + "throughput": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "database_filter": {"type": "bool"}, + "dead_interval": { + "type": "dict", + "options": { + "time": {"type": "int"}, + "minimal": {"type": "int"}, + }, + }, + "demand_circuit": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "ignore": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + "flood_reduction": {"type": "bool"}, + "hello_interval": {"type": "int"}, + "lls": {"type": "bool"}, + "manet": { + "type": "dict", + "options": { + "cost": { + "type": "dict", + "options": { + "percent": {"type": "int"}, + "threshold": {"type": "int"}, + }, + }, + "link_metrics": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "cost_threshold": {"type": "int"}, + }, + }, + }, + }, + "mtu_ignore": {"type": "bool"}, + "multi_area": { + "type": "dict", + "options": { + "id": {"type": "int"}, + "cost": {"type": "int"}, + }, + }, + "neighbor": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "cost": {"type": "int"}, + "database_filter": {"type": "bool"}, + "poll_interval": {"type": "int"}, + "priority": {"type": "int"}, + }, + }, + "network": { + "type": "dict", + "options": { + "broadcast": {"type": "bool"}, + "manet": {"type": "bool"}, + "non_broadcast": {"type": "bool"}, + "point_to_multipoint": {"type": "bool"}, + "point_to_point": {"type": "bool"}, + }, + }, + "prefix_suppression": {"type": "bool"}, + "priority": {"type": "int"}, + "resync_timeout": {"type": "int"}, + "retransmit_interval": {"type": "int"}, + "shutdown": {"type": "bool"}, + "transmit_delay": {"type": "int"}, + "ttl_security": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "hops": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "type": "str", + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv2/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv2/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv2/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv2/ospfv2.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv2/ospfv2.py new file mode 100644 index 000000000..e9e890c6d --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv2/ospfv2.py @@ -0,0 +1,524 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 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 arg spec for the ios_ospfv2 module +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class Ospfv2Args(object): + """The arg spec for the ios_ospfv2 module""" + + argument_spec = { + "config": { + "options": { + "processes": { + "mutually_exclusive": [("passive_interface", "passive_interfaces")], + "elements": "dict", + "options": { + "address_family": { + "options": { + "default": {"type": "bool"}, + "snmp_context": {"type": "str"}, + "topology": { + "options": { + "base": {"type": "bool"}, + "name": {"type": "str"}, + "tid": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "adjacency": { + "options": { + "max_adjacency": {"type": "int"}, + "min_adjacency": {"type": "int"}, + "none": {"type": "bool"}, + }, + "type": "dict", + }, + "areas": { + "elements": "dict", + "options": { + "area_id": {"type": "str"}, + "authentication": { + "options": { + "enable": {"type": "bool"}, + "message_digest": {"type": "bool"}, + }, + "type": "dict", + }, + "capability": {"type": "bool"}, + "default_cost": {"type": "int"}, + "filter_list": { + "elements": "dict", + "options": { + "direction": { + "choices": ["in", "out"], + "required": True, + "type": "str", + }, + "name": {"type": "str"}, + }, + "type": "list", + }, + "nssa": { + "options": { + "default_information_originate": { + "options": { + "metric": {"type": "int"}, + "metric_type": {"choices": [1, 2], "type": "int"}, + "nssa_only": {"type": "bool"}, + }, + "type": "dict", + }, + "no_ext_capability": {"type": "bool"}, + "no_redistribution": {"type": "bool"}, + "no_summary": {"type": "bool"}, + "set": {"type": "bool"}, + "translate": { + "choices": ["always", "suppress-fa"], + "type": "str", + }, + }, + "type": "dict", + }, + "ranges": { + "options": { + "address": {"type": "str"}, + "advertise": {"type": "bool"}, + "cost": {"type": "int"}, + "netmask": {"type": "str"}, + "not_advertise": {"type": "bool"}, + }, + "type": "list", + "elements": "dict", + }, + "sham_link": { + "options": { + "cost": {"type": "int"}, + "destination": {"type": "str"}, + "source": {"type": "str"}, + "ttl_security": {"type": "int"}, + }, + "type": "dict", + }, + "stub": { + "options": { + "no_ext_capability": {"type": "bool"}, + "no_summary": {"type": "bool"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "list", + }, + "auto_cost": { + "options": { + "reference_bandwidth": {"type": "int"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "bfd": {"type": "bool"}, + "capability": { + "options": { + "lls": {"type": "bool"}, + "opaque": {"type": "bool"}, + "transit": {"type": "bool"}, + "vrf_lite": {"type": "bool"}, + }, + "type": "dict", + }, + "compatible": { + "options": { + "rfc1583": {"type": "bool"}, + "rfc1587": {"type": "bool"}, + "rfc5243": {"type": "bool"}, + }, + "type": "dict", + }, + "default_information": { + "options": { + "always": {"type": "bool"}, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "originate": {"type": "bool"}, + "route_map": {"type": "str"}, + }, + "type": "dict", + }, + "default_metric": {"type": "int"}, + "discard_route": { + "options": { + "external": {"type": "int"}, + "internal": {"type": "int"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "distance": { + "options": { + "admin_distance": { + "options": { + "acl": {"type": "str"}, + "address": {"type": "str"}, + "distance": {"type": "int"}, + "wildcard_bits": {"type": "str"}, + }, + "type": "dict", + }, + "ospf": { + "options": { + "external": {"type": "int"}, + "inter_area": {"type": "int"}, + "intra_area": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "distribute_list": { + "options": { + "acls": { + "elements": "dict", + "options": { + "direction": { + "choices": ["in", "out"], + "required": True, + "type": "str", + }, + "interface": {"type": "str"}, + "name": {"required": True, "type": "str"}, + "protocol": {"type": "str"}, + }, + "type": "list", + }, + "prefix": { + "options": { + "direction": { + "choices": ["in", "out"], + "required": True, + "type": "str", + }, + "gateway_name": {"type": "str"}, + "interface": {"type": "str"}, + "name": {"required": True, "type": "str"}, + "protocol": {"type": "str"}, + }, + "type": "dict", + }, + "route_map": { + "options": {"name": {"required": True, "type": "str"}}, + "type": "dict", + }, + }, + "type": "dict", + }, + "domain_id": { + "options": { + "ip_address": { + "options": { + "address": {"type": "str"}, + "secondary": {"type": "bool"}, + }, + "type": "dict", + }, + "null": {"type": "bool"}, + }, + "type": "dict", + }, + "domain_tag": {"type": "int"}, + "event_log": { + "options": { + "enable": {"type": "bool"}, + "one_shot": {"type": "bool"}, + "pause": {"type": "bool"}, + "size": {"type": "int"}, + }, + "type": "dict", + }, + "help": {"type": "bool"}, + "ignore": {"type": "bool"}, + "interface_id": {"type": "bool"}, + "ispf": {"type": "bool"}, + "limit": { + "options": { + "dc": { + "options": { + "disable": {"type": "bool"}, + "number": {"type": "int"}, + }, + "type": "dict", + }, + "non_dc": { + "options": { + "disable": {"type": "bool"}, + "number": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "local_rib_criteria": { + "options": { + "enable": {"type": "bool"}, + "forwarding_address": {"type": "bool"}, + "inter_area_summary": {"type": "bool"}, + "nssa_translation": {"type": "bool"}, + }, + "type": "dict", + }, + "log_adjacency_changes": { + "options": {"detail": {"type": "bool"}, "set": {"type": "bool"}}, + "type": "dict", + }, + "max_lsa": { + "options": { + "ignore_count": {"type": "int"}, + "ignore_time": {"type": "int"}, + "number": {"type": "int"}, + "reset_time": {"type": "int"}, + "threshold_value": {"type": "int"}, + "warning_only": {"type": "bool"}, + }, + "type": "dict", + }, + "max_metric": { + "options": { + "external_lsa": {"type": "int"}, + "include_stub": {"type": "bool"}, + "on_startup": { + "options": { + "time": {"type": "int"}, + "wait_for_bgp": {"type": "bool"}, + }, + "type": "dict", + }, + "router_lsa": {"required": True, "type": "bool"}, + "summary_lsa": {"type": "int"}, + }, + "type": "dict", + }, + "maximum_paths": {"type": "int"}, + "mpls": { + "options": { + "ldp": { + "options": { + "autoconfig": { + "options": { + "area": {"type": "str"}, + "set": {"type": "bool"}, + }, + "type": "dict", + }, + "sync": {"type": "bool"}, + }, + "type": "dict", + }, + "traffic_eng": { + "options": { + "area": {"type": "str"}, + "autoroute_exclude": {"type": "str"}, + "interface": { + "options": { + "area": {"type": "int"}, + "interface_type": {"type": "str"}, + }, + "type": "dict", + }, + "mesh_group": { + "options": { + "area": {"type": "str"}, + "id": {"type": "int"}, + "interface": {"type": "str"}, + }, + "type": "dict", + }, + "multicast_intact": {"type": "bool"}, + "router_id_interface": {"type": "str"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "neighbor": { + "options": { + "address": {"type": "str"}, + "cost": {"type": "int"}, + "database_filter": {"type": "bool"}, + "poll_interval": {"type": "int"}, + "priority": {"type": "int"}, + }, + "type": "dict", + }, + "network": { + "options": { + "address": {"type": "str"}, + "area": {"type": "str"}, + "wildcard_bits": {"type": "str"}, + }, + "type": "list", + "elements": "dict", + }, + "nsf": { + "options": { + "cisco": { + "options": { + "disable": {"type": "bool"}, + "helper": {"type": "bool"}, + }, + "type": "dict", + }, + "ietf": { + "options": { + "disable": {"type": "bool"}, + "helper": {"type": "bool"}, + "strict_lsa_checking": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "passive_interface": {"type": "str"}, + "passive_interfaces": { + "options": { + "default": {"type": "bool"}, + "interface": { + "options": { + "set_interface": {"type": "bool"}, + "name": {"type": "list", "elements": "str"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "prefix_suppression": {"type": "bool"}, + "priority": {"type": "int"}, + "process_id": {"required": True, "type": "int"}, + "queue_depth": { + "options": { + "hello": { + "options": { + "max_packets": {"type": "int"}, + "unlimited": {"type": "bool"}, + }, + "type": "dict", + }, + "update": { + "options": { + "max_packets": {"type": "int"}, + "unlimited": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "router_id": {"type": "str"}, + "shutdown": {"type": "bool"}, + "summary_address": { + "options": { + "address": {"type": "str"}, + "mask": {"type": "str"}, + "not_advertise": {"type": "bool"}, + "nssa_only": {"type": "bool"}, + "tag": {"type": "int"}, + }, + "type": "dict", + }, + "timers": { + "options": { + "lsa": {"type": "int"}, + "pacing": { + "options": { + "flood": {"type": "int"}, + "lsa_group": {"type": "int"}, + "retransmission": {"type": "int"}, + }, + "type": "dict", + }, + "throttle": { + "options": { + "lsa": { + "options": { + "first_delay": {"type": "int"}, + "max_delay": {"type": "int"}, + "min_delay": {"type": "int"}, + }, + "type": "dict", + }, + "spf": { + "options": { + "between_delay": {"type": "int"}, + "max_delay": {"type": "int"}, + "receive_delay": {"type": "int"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "traffic_share": {"type": "bool"}, + "ttl_security": { + "options": {"hops": {"type": "int"}, "set": {"type": "bool"}}, + "type": "dict", + }, + "vrf": {"type": "str"}, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv3/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv3/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv3/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv3/ospfv3.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv3/ospfv3.py new file mode 100644 index 000000000..3a22e9a15 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ospfv3/ospfv3.py @@ -0,0 +1,725 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# 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 arg spec for the cisco.ios_ospfv3 module +""" + + +class Ospfv3Args(object): # pylint: disable=R0903 + """The arg spec for the cisco.ios_ospfv3 module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "type": "dict", + "options": { + "processes": { + "type": "list", + "elements": "dict", + "options": { + "process_id": {"required": True, "type": "int"}, + "address_family": { + "type": "list", + "elements": "dict", + "options": { + "afi": {"type": "str", "choices": ["ipv4", "ipv6"]}, + "unicast": {"type": "bool"}, + "vrf": {"type": "str"}, + "adjacency": { + "type": "dict", + "options": { + "min_adjacency": {"type": "int"}, + "none": {"type": "bool"}, + "max_adjacency": {"type": "int"}, + "disable": {"type": "bool"}, + }, + }, + "areas": { + "type": "list", + "elements": "dict", + "options": { + "area_id": {"type": "str"}, + "authentication": { + "type": "dict", + "options": { + "key_chain": {"type": "str", "no_log": True}, + "null": {"type": "bool"}, + }, + }, + "default_cost": {"type": "int"}, + "filter_list": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"}, + "direction": { + "type": "str", + "choices": ["in", "out"], + "required": True, + }, + }, + }, + "normal": {"type": "bool"}, + "nssa": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "default_information_originate": { + "type": "dict", + "options": { + "metric": {"type": "int"}, + "metric_type": { + "type": "int", + "choices": [1, 2], + }, + "nssa_only": {"type": "bool"}, + }, + }, + "no_redistribution": {"type": "bool"}, + "no_summary": {"type": "bool"}, + "translate": { + "type": "str", + "choices": ["always", "suppress-fa"], + }, + }, + }, + "ranges": { + "type": "list", + "elements": "dict", + "options": { + "address": {"type": "str"}, + "netmask": {"type": "str"}, + "advertise": {"type": "bool"}, + "cost": {"type": "int"}, + "not_advertise": {"type": "bool"}, + }, + }, + "sham_link": { + "type": "dict", + "options": { + "source": {"type": "str"}, + "destination": {"type": "str"}, + "authentication": { + "type": "dict", + "options": { + "key_chain": { + "type": "str", + "no_log": True, + }, + "null": {"type": "bool"}, + }, + }, + "cost": {"type": "int"}, + "ttl_security": {"type": "int"}, + }, + }, + "stub": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "no_summary": {"type": "bool"}, + }, + }, + }, + }, + "authentication": { + "type": "dict", + "options": { + "deployment": {"type": "bool"}, + "normal": {"type": "bool"}, + }, + }, + "auto_cost": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "reference_bandwidth": {"type": "int"}, + }, + }, + "bfd": { + "type": "dict", + "options": { + "all_interfaces": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + "capability": {"type": "bool"}, + "compatible": { + "type": "dict", + "options": { + "rfc1583": {"type": "bool"}, + "rfc1587": {"type": "bool"}, + "rfc5243": {"type": "bool"}, + }, + }, + "default_information": { + "type": "dict", + "options": { + "originate": {"type": "bool"}, + "always": {"type": "bool"}, + "metric": {"type": "int"}, + "metric_type": {"type": "int"}, + "route_map": {"type": "str"}, + }, + }, + "default_metric": {"type": "int"}, + "discard_route": { + "type": "dict", + "options": { + "sham_link": {"type": "bool"}, + "external": {"type": "bool"}, + "internal": {"type": "bool"}, + }, + }, + "distance": {"type": "int"}, + "distribute_list": { + "type": "dict", + "options": { + "acls": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "direction": { + "type": "str", + "required": True, + "choices": ["in", "out"], + }, + "interface": {"type": "str"}, + "protocol": {"type": "str"}, + }, + }, + "prefix": { + "type": "dict", + "options": { + "name": {"type": "str", "required": True}, + "gateway_name": {"type": "str"}, + "direction": { + "type": "str", + "required": True, + "choices": ["in", "out"], + }, + "interface": {"type": "str"}, + "protocol": {"type": "str"}, + }, + }, + "route_map": { + "type": "dict", + "options": {"name": {"type": "str", "required": True}}, + }, + }, + }, + "event_log": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "one_shot": {"type": "bool"}, + "pause": {"type": "bool"}, + "size": {"type": "int"}, + }, + }, + "graceful_restart": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "disable": {"type": "bool"}, + "strict_lsa_checking": {"type": "bool"}, + }, + }, + "interface_id": { + "type": "dict", + "options": { + "ios_if_index": {"type": "bool"}, + "snmp_if_index": {"type": "bool"}, + }, + }, + "limit": { + "type": "dict", + "options": { + "dc": { + "type": "dict", + "options": { + "number": {"type": "int"}, + "disable": {"type": "bool"}, + }, + }, + "non_dc": { + "type": "dict", + "options": { + "number": {"type": "int"}, + "disable": {"type": "bool"}, + }, + }, + }, + }, + "local_rib_criteria": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "forwarding_address": {"type": "bool"}, + "inter_area_summary": {"type": "bool"}, + "nssa_translation": {"type": "bool"}, + }, + }, + "log_adjacency_changes": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "detail": {"type": "bool"}, + }, + }, + "manet": { + "type": "dict", + "options": { + "cache": { + "type": "dict", + "options": { + "acknowledgement": {"type": "int"}, + "update": {"type": "int"}, + }, + }, + "hello": { + "type": "dict", + "options": { + "multicast": {"type": "bool"}, + "unicast": {"type": "bool"}, + }, + }, + "peering": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "disable": {"type": "bool"}, + "per_interface": {"type": "bool"}, + "redundancy": {"type": "int"}, + }, + }, + "willingness": {"type": "int"}, + }, + }, + "max_lsa": { + "type": "dict", + "options": { + "number": {"type": "int"}, + "threshold_value": {"type": "int"}, + "ignore_count": {"type": "int"}, + "ignore_time": {"type": "int"}, + "reset_time": {"type": "int"}, + "warning_only": {"type": "bool"}, + }, + }, + "max_metric": { + "type": "dict", + "options": { + "disable": {"type": "bool"}, + "external_lsa": {"type": "int"}, + "inter_area_lsas": {"type": "int"}, + "on_startup": { + "type": "dict", + "options": { + "time": {"type": "int"}, + "wait_for_bgp": {"type": "bool"}, + }, + }, + "stub_prefix_lsa": {"type": "bool"}, + }, + }, + "maximum_paths": {"type": "int"}, + "passive_interface": {"type": "str"}, + "prefix_suppression": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + "queue_depth": { + "type": "dict", + "options": { + "hello": { + "type": "dict", + "options": { + "max_packets": {"type": "int"}, + "unlimited": {"type": "bool"}, + }, + }, + "update": { + "type": "dict", + "options": { + "max_packets": {"type": "int"}, + "unlimited": {"type": "bool"}, + }, + }, + }, + }, + "router_id": {"type": "str"}, + "shutdown": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "disable": {"type": "bool"}, + }, + }, + "summary_prefix": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "mask": {"type": "str"}, + "not_advertise": {"type": "bool"}, + "nssa_only": {"type": "bool"}, + "tag": {"type": "int"}, + }, + }, + "timers": { + "type": "dict", + "options": { + "lsa": {"type": "int"}, + "manet": { + "type": "dict", + "options": { + "cache": { + "type": "dict", + "options": { + "acknowledgement": {"type": "int"}, + "redundancy": {"type": "int"}, + }, + }, + "hello": {"type": "bool"}, + "peering": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "per_interface": {"type": "bool"}, + "redundancy": {"type": "int"}, + }, + }, + "willingness": {"type": "int"}, + }, + }, + "pacing": { + "type": "dict", + "options": { + "flood": {"type": "int"}, + "lsa_group": {"type": "int"}, + "retransmission": {"type": "int"}, + }, + }, + "throttle": { + "type": "dict", + "options": { + "lsa": { + "type": "dict", + "options": { + "first_delay": {"type": "int"}, + "min_delay": {"type": "int"}, + "max_delay": {"type": "int"}, + }, + }, + "spf": { + "type": "dict", + "options": { + "receive_delay": {"type": "int"}, + "between_delay": {"type": "int"}, + "max_delay": {"type": "int"}, + }, + }, + }, + }, + }, + }, + }, + }, + "adjacency": { + "type": "dict", + "options": { + "min_adjacency": {"type": "int"}, + "max_adjacency": {"type": "int"}, + "none": {"type": "bool"}, + }, + }, + "areas": { + "type": "list", + "elements": "dict", + "options": { + "area_id": {"type": "str"}, + "authentication": { + "type": "dict", + "options": { + "key_chain": {"type": "str", "no_log": True}, + "ipsec": { + "type": "dict", + "options": { + "spi": {"type": "int"}, + "md5": {"type": "int"}, + "sha1": {"type": "int"}, + "hex_string": {"type": "str"}, + }, + }, + }, + }, + "default_cost": {"type": "int"}, + "nssa": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "default_information_originate": { + "type": "dict", + "options": { + "metric": {"type": "int"}, + "metric_type": {"type": "int", "choices": [1, 2]}, + "nssa_only": {"type": "bool"}, + }, + }, + "no_redistribution": {"type": "bool"}, + "no_summary": {"type": "bool"}, + "translate": { + "type": "str", + "choices": ["always", "suppress-fa"], + }, + }, + }, + "stub": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "no_summary": {"type": "bool"}, + }, + }, + }, + }, + "authentication": {"type": "bool"}, + "auto_cost": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "reference_bandwidth": {"type": "int"}, + }, + }, + "bfd": {"type": "bool"}, + "compatible": { + "type": "dict", + "options": { + "rfc1583": {"type": "bool"}, + "rfc1587": {"type": "bool"}, + "rfc5243": {"type": "bool"}, + }, + }, + "event_log": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "one_shot": {"type": "bool"}, + "pause": {"type": "bool"}, + "size": {"type": "int"}, + }, + }, + "graceful_restart": { + "type": "dict", + "options": { + "disable": {"type": "bool"}, + "strict_lsa_checking": {"type": "bool"}, + }, + }, + "help": {"type": "bool"}, + "interface_id": {"type": "bool"}, + "limit": { + "type": "dict", + "options": { + "dc": { + "type": "dict", + "options": { + "number": {"type": "int"}, + "disable": {"type": "bool"}, + }, + }, + "non_dc": { + "type": "dict", + "options": { + "number": {"type": "int"}, + "disable": {"type": "bool"}, + }, + }, + }, + }, + "local_rib_criteria": { + "type": "dict", + "options": { + "enable": {"type": "bool"}, + "forwarding_address": {"type": "bool"}, + "inter_area_summary": {"type": "bool"}, + "nssa_translation": {"type": "bool"}, + }, + }, + "log_adjacency_changes": { + "type": "dict", + "options": {"set": {"type": "bool"}, "detail": {"type": "bool"}}, + }, + "manet": { + "type": "dict", + "options": { + "cache": { + "type": "dict", + "options": { + "acknowledgement": {"type": "int"}, + "redundancy": {"type": "int"}, + }, + }, + "hello": {"type": "bool"}, + "peering": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "per_interface": {"type": "bool"}, + "redundancy": {"type": "int"}, + }, + }, + "willingness": {"type": "int"}, + }, + }, + "max_lsa": { + "type": "dict", + "options": { + "number": {"type": "int"}, + "threshold_value": {"type": "int"}, + "ignore_count": {"type": "int"}, + "ignore_time": {"type": "int"}, + "reset_time": {"type": "int"}, + "warning_only": {"type": "bool"}, + }, + }, + "max_metric": { + "type": "dict", + "options": { + "router_lsa": {"type": "bool", "required": True}, + "external_lsa": {"type": "int"}, + "include_stub": {"type": "bool"}, + "on_startup": { + "type": "dict", + "options": { + "time": {"type": "int"}, + "wait_for_bgp": {"type": "bool"}, + }, + }, + "summary_lsa": {"type": "int"}, + }, + }, + "passive_interface": {"type": "str"}, + "prefix_suppression": {"type": "bool"}, + "queue_depth": { + "type": "dict", + "options": { + "hello": { + "type": "dict", + "options": { + "max_packets": {"type": "int"}, + "unlimited": {"type": "bool"}, + }, + }, + }, + }, + "router_id": {"type": "str"}, + "shutdown": {"type": "bool"}, + "timers": { + "type": "dict", + "options": { + "lsa": {"type": "int"}, + "manet": { + "type": "dict", + "options": { + "cache": { + "type": "dict", + "options": { + "acknowledgement": {"type": "int"}, + "redundancy": {"type": "int"}, + }, + }, + "hello": {"type": "bool"}, + "peering": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "per_interface": {"type": "bool"}, + "redundancy": {"type": "int"}, + }, + }, + "willingness": {"type": "int"}, + }, + }, + "pacing": { + "type": "dict", + "options": { + "flood": {"type": "int"}, + "lsa_group": {"type": "int"}, + "retransmission": {"type": "int"}, + }, + }, + "throttle": { + "type": "dict", + "options": { + "lsa": { + "type": "dict", + "options": { + "first_delay": {"type": "int"}, + "min_delay": {"type": "int"}, + "max_delay": {"type": "int"}, + }, + }, + "spf": { + "type": "dict", + "options": { + "receive_delay": {"type": "int"}, + "between_delay": {"type": "int"}, + "max_delay": {"type": "int"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "type": "str", + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ping/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ping/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ping/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ping/ping.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ping/ping.py new file mode 100644 index 000000000..f89779d06 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/ping/ping.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the ios_ping module +""" + + +class PingArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_ping module""" + + argument_spec = { + "count": {"type": "int"}, + "afi": {"choices": ["ip", "ipv6"], "default": "ip", "type": "str"}, + "dest": {"required": True, "type": "str"}, + "df_bit": {"default": False, "type": "bool"}, + "source": {"type": "str"}, + "ingress": {"type": "str"}, + "egress": {"type": "str"}, + "timeout": {"type": "int"}, + "state": {"choices": ["absent", "present"], "default": "present", "type": "str"}, + "vrf": {"type": "str"}, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/prefix_lists/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/prefix_lists/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/prefix_lists/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/prefix_lists/prefix_lists.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/prefix_lists/prefix_lists.py new file mode 100644 index 000000000..8a3f8ed7f --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/prefix_lists/prefix_lists.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the cisco.ios_prefix_lists module +""" + + +class Prefix_listsArgs(object): + """The arg spec for the cisco.ios_prefix_lists module""" + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "afi": {"type": "str", "choices": ["ipv4", "ipv6"]}, + "prefix_lists": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str"}, + "description": {"type": "str"}, + "entries": { + "type": "list", + "elements": "dict", + "options": { + "action": {"type": "str", "choices": ["deny", "permit"]}, + "sequence": {"type": "int"}, + "description": {"type": "str"}, + "prefix": {"type": "str"}, + "ge": {"type": "int"}, + "le": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "type": "str", + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + }, + } diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/route_maps/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/route_maps/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/route_maps/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/route_maps/route_maps.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/route_maps/route_maps.py new file mode 100644 index 000000000..fdc127e0e --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/route_maps/route_maps.py @@ -0,0 +1,567 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the cisco.ios_route_maps module +""" + + +class Route_mapsArgs(object): # pylint: disable=R0903 + """The arg spec for the cisco.ios_route_maps module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "route_map": {"type": "str"}, + "entries": { + "type": "list", + "elements": "dict", + "options": { + "sequence": {"type": "int"}, + "action": {"type": "str", "choices": ["deny", "permit"]}, + "continue_entry": { + "type": "dict", + "options": {"set": {"type": "bool"}, "entry_sequence": {"type": "int"}}, + }, + "description": {"type": "str"}, + "match": { + "type": "dict", + "options": { + "additional_paths": { + "type": "dict", + "options": { + "all": {"type": "bool"}, + "best": {"type": "int"}, + "best_range": { + "type": "dict", + "options": { + "lower_limit": {"type": "int"}, + "upper_limit": {"type": "int"}, + }, + }, + "group_best": {"type": "bool"}, + }, + }, + "as_path": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "acls": {"type": "list", "elements": "int"}, + }, + }, + "clns": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "next_hop": {"type": "str"}, + "route_source": {"type": "str"}, + }, + }, + "community": { + "type": "dict", + "options": { + "name": {"type": "list", "elements": "str"}, + "exact_match": {"type": "bool"}, + }, + }, + "extcommunity": {"type": "list", "elements": "str"}, + "interfaces": {"type": "list", "elements": "str"}, + "ip": { + "type": "dict", + "options": { + "address": { + "type": "dict", + "options": { + "acls": {"type": "list", "elements": "str"}, + "prefix_lists": {"type": "list", "elements": "str"}, + }, + }, + "flowspec": { + "type": "dict", + "options": { + "dest_pfx": {"type": "bool"}, + "src_pfx": {"type": "bool"}, + "acls": {"type": "list", "elements": "str"}, + "prefix_lists": {"type": "list", "elements": "str"}, + }, + }, + "next_hop": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "acls": {"type": "list", "elements": "str"}, + "prefix_lists": {"type": "list", "elements": "str"}, + }, + }, + "redistribution_source": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "acls": {"type": "list", "elements": "str"}, + "prefix_lists": {"type": "list", "elements": "str"}, + }, + }, + "route_source": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "redistribution_source": {"type": "bool"}, + "acls": {"type": "list", "elements": "str"}, + "prefix_lists": {"type": "list", "elements": "str"}, + }, + }, + }, + }, + "ipv6": { + "type": "dict", + "options": { + "address": { + "type": "dict", + "options": { + "acl": {"type": "str"}, + "prefix_list": {"type": "str"}, + }, + }, + "flowspec": { + "type": "dict", + "options": { + "dest_pfx": {"type": "bool"}, + "src_pfx": {"type": "bool"}, + "acl": {"type": "str"}, + "prefix_list": {"type": "str"}, + }, + }, + "next_hop": { + "type": "dict", + "options": { + "acl": {"type": "str"}, + "prefix_list": {"type": "str"}, + }, + }, + "route_source": { + "type": "dict", + "options": { + "acl": {"type": "str"}, + "prefix_list": {"type": "str"}, + }, + }, + }, + }, + "length": { + "type": "dict", + "options": { + "minimum": {"type": "int"}, + "maximum": {"type": "int"}, + }, + }, + "local_preference": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "value": {"type": "list", "elements": "str"}, + }, + }, + "mdt_group": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "acls": {"type": "list", "elements": "str"}, + }, + }, + "metric": { + "type": "dict", + "options": { + "value": {"type": "int"}, + "external": {"type": "bool"}, + "deviation": {"type": "bool"}, + "deviation_value": {"type": "int"}, + }, + }, + "mpls_label": {"type": "bool"}, + "policy_lists": {"type": "list", "elements": "str"}, + "route_type": { + "type": "dict", + "options": { + "external": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "type_1": {"type": "bool"}, + "type_2": {"type": "bool"}, + }, + }, + "internal": {"type": "bool"}, + "level_1": {"type": "bool"}, + "level_2": {"type": "bool"}, + "local": {"type": "bool"}, + "nssa_external": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "type_1": {"type": "bool"}, + "type_2": {"type": "bool"}, + }, + }, + }, + }, + "rpki": { + "type": "dict", + "options": { + "invalid": {"type": "bool"}, + "not_found": {"type": "bool"}, + "valid": {"type": "bool"}, + }, + }, + "security_group": { + "type": "dict", + "options": { + "source": {"type": "list", "elements": "int"}, + "destination": {"type": "list", "elements": "int"}, + }, + }, + "source_protocol": { + "type": "dict", + "options": { + "bgp": {"type": "str"}, + "connected": {"type": "bool"}, + "eigrp": {"type": "int"}, + "isis": {"type": "bool"}, + "lisp": {"type": "bool"}, + "mobile": {"type": "bool"}, + "ospf": {"type": "int"}, + "ospfv3": {"type": "int"}, + "rip": {"type": "bool"}, + "static": {"type": "bool"}, + }, + }, + "tag": { + "type": "dict", + "options": { + "value": {"type": "list", "elements": "str"}, + "tag_list": {"type": "list", "elements": "str"}, + }, + }, + "track": {"type": "int"}, + }, + }, + "set": { + "type": "dict", + "options": { + "aigp_metric": { + "type": "dict", + "options": { + "value": {"type": "int"}, + "igp_metric": {"type": "bool"}, + }, + }, + "as_path": { + "type": "dict", + "options": { + "prepend": { + "type": "dict", + "options": { + "as_number": {"type": "list", "elements": "str"}, + "last_as": {"type": "int"}, + }, + }, + "tag": {"type": "bool"}, + }, + }, + "automatic_tag": {"type": "bool"}, + "clns": {"type": "str"}, + "comm_list": {"type": "str"}, + "community": { + "type": "dict", + "options": { + "number": {"type": "str"}, + "additive": {"type": "bool"}, + "gshut": {"type": "bool"}, + "internet": {"type": "bool"}, + "local_as": {"type": "bool"}, + "no_advertise": {"type": "bool"}, + "no_export": {"type": "bool"}, + "none": {"type": "bool"}, + }, + }, + "dampening": { + "type": "dict", + "options": { + "penalty_half_time": {"type": "int"}, + "reuse_route_val": {"type": "int"}, + "suppress_route_val": {"type": "int"}, + "max_suppress": {"type": "int"}, + }, + }, + "default": {"type": "str"}, + "extcomm_list": {"type": "str"}, + "extcommunity": { + "type": "dict", + "options": { + "cost": { + "type": "dict", + "options": { + "id": {"type": "str"}, + "cost_value": {"type": "int"}, + "igp": {"type": "bool"}, + "pre_bestpath": {"type": "bool"}, + }, + }, + "rt": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "range": { + "type": "dict", + "options": { + "lower_limit": {"type": "str"}, + "upper_limit": {"type": "str"}, + }, + }, + "additive": {"type": "bool"}, + }, + }, + "soo": {"type": "str"}, + "vpn_distinguisher": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "range": { + "type": "dict", + "options": { + "lower_limit": {"type": "str"}, + "upper_limit": {"type": "str"}, + }, + }, + "additive": {"type": "bool"}, + }, + }, + }, + }, + "global_route": {"type": "bool"}, + "interfaces": {"type": "list", "elements": "str"}, + "ip": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "df": {"choices": [0, 1], "type": "int"}, + "global_route": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "verify_availability": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "sequence": {"type": "int"}, + "track": {"type": "int"}, + }, + }, + }, + }, + "next_hop": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "dynamic": {"type": "bool"}, + "encapsulate": {"type": "str"}, + "peer_address": {"type": "bool"}, + "recursive": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "global_route": {"type": "bool"}, + "vrf": {"type": "str"}, + }, + }, + "self": {"type": "bool"}, + "verify_availability": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "address": {}, + "sequence": {"type": "int"}, + "track": {"type": "int"}, + }, + }, + }, + }, + "precedence": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "critical": {"type": "bool"}, + "flash": {"type": "bool"}, + "flash_override": {"type": "bool"}, + "immediate": {"type": "bool"}, + "internet": {"type": "bool"}, + "network": {"type": "bool"}, + "priority": {"type": "bool"}, + "routine": {"type": "bool"}, + }, + }, + "qos_group": {"type": "int"}, + "tos": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "max_reliability": {"type": "bool"}, + "max_throughput": {"type": "bool"}, + "min_delay": {"type": "bool"}, + "min_monetary_cost": {"type": "bool"}, + "normal": {"type": "bool"}, + }, + }, + "vrf": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "address": {"type": "str"}, + "verify_availability": { + "type": "dict", + "options": { + "set": {"type": "bool"}, + "address": {}, + "sequence": {"type": "int"}, + "track": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "ipv6": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "default": {"type": "bool"}, + "global_route": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "verify_availability": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "sequence": {"type": "int"}, + "track": {"type": "int"}, + }, + }, + }, + }, + "next_hop": { + "type": "dict", + "options": { + "address": {"type": "str"}, + "encapsulate": {"type": "str"}, + "peer_address": {"type": "bool"}, + "recursive": {"type": "str"}, + }, + }, + "precedence": {"type": "int"}, + "vrf": { + "type": "dict", + "options": { + "name": {"type": "str"}, + "verify_availability": { + "type": "dict", + "options": { + "address": {}, + "sequence": {"type": "int"}, + "track": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "level": { + "type": "dict", + "options": { + "level_1": {"type": "bool"}, + "level_1_2": {"type": "bool"}, + "level_2": {"type": "bool"}, + "nssa_only": {"type": "bool"}, + }, + }, + "lisp": {"type": "str"}, + "local_preference": {"type": "int"}, + "metric": { + "type": "dict", + "options": { + "deviation": {"choices": ["plus", "minus"], "type": "str"}, + "metric_value": {"type": "int"}, + "eigrp_delay": {"type": "int"}, + "metric_reliability": {"type": "int"}, + "metric_bandwidth": {"type": "int"}, + "mtu": {"type": "int"}, + }, + }, + "metric_type": { + "type": "dict", + "options": { + "external": {"type": "bool"}, + "internal": {"type": "bool"}, + "type_1": {"type": "bool"}, + "type_2": {"type": "bool"}, + }, + }, + "mpls_label": {"type": "bool"}, + "origin": { + "type": "dict", + "options": { + "igp": {"type": "bool"}, + "incomplete": {"type": "bool"}, + }, + }, + "tag": {"type": "str"}, + "traffic_index": {"type": "int"}, + "vrf": {"type": "str"}, + "weight": {"type": "int"}, + }, + }, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "type": "str", + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "parsed", + "rendered", + ], + "default": "merged", + }, + } diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/service/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/service/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/service/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/service/service.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/service/service.py new file mode 100644 index 000000000..76d2a3e34 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/service/service.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +# Copyright 2023 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# ansible.content_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the documentation in the module file and re-run +# ansible.content_builder commenting out +# the path to external 'docstring' in build.yaml. +# +############################################## + +""" +The arg spec for the ios_service module +""" + + +class ServiceArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_service module""" + + argument_spec = { + "config": { + "options": { + "call_home": {"type": "bool"}, + "compress_config": {"type": "bool"}, + "config": {"type": "bool"}, + "counters": {"type": "int", "default": 0}, + "dhcp": {"type": "bool", "default": True}, + "disable_ip_fast_frag": {"type": "bool"}, + "exec_callback": {"type": "bool"}, + "exec_wait": {"type": "bool"}, + "hide_telnet_addresses": {"type": "bool"}, + "internal": {"type": "bool"}, + "linenumber": {"type": "bool"}, + "log": {"type": "bool"}, + "log_hidden": {"type": "bool"}, + "nagle": {"type": "bool"}, + "old_slip_prompts": {"type": "bool"}, + "pad": {"type": "bool"}, + "pad_cmns": {"type": "bool"}, + "pad_from_xot": {"type": "bool"}, + "pad_to_xot": {"type": "bool"}, + "password_encryption": {"type": "bool"}, + "password_recovery": {"type": "bool", "default": True}, + "prompt": {"type": "bool", "default": True}, + "private_config_encryption": {"type": "bool"}, + "pt_vty_logging": {"type": "bool"}, + "scripting": {"type": "bool"}, + "sequence_numbers": {"type": "bool"}, + "slave_coredump": {"type": "bool"}, + "slave_log": {"type": "bool", "default": True}, + "tcp_keepalives_in": {"type": "bool"}, + "tcp_keepalives_out": {"type": "bool"}, + "tcp_small_servers": { + "options": { + "enable": {"type": "bool"}, + "max_servers": {"type": "str"}, + }, + "type": "dict", + }, + "telnet_zeroidle": {"type": "bool"}, + "timestamps": { + "elements": "dict", + "options": { + "msg": {"choices": ["debug", "log"], "type": "str"}, + "enable": {"type": "bool"}, + "timestamp": { + "choices": ["datetime", "uptime"], + "type": "str", + }, + "datetime_options": { + "options": { + "localtime": {"type": "bool"}, + "msec": {"type": "bool"}, + "show_timezone": {"type": "bool"}, + "year": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "list", + }, + "udp_small_servers": { + "options": { + "enable": {"type": "bool"}, + "max_servers": {"type": "str"}, + }, + "type": "dict", + }, + "unsupported_transceiver": {"type": "bool"}, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/snmp_server/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/snmp_server/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/snmp_server/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/snmp_server/snmp_server.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/snmp_server/snmp_server.py new file mode 100644 index 000000000..d0473cc97 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/snmp_server/snmp_server.py @@ -0,0 +1,484 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# cli_rm_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the module docstring and re-run +# cli_rm_builder. +# +############################################# + +""" +The arg spec for the ios_snmp_server module +""" + + +class Snmp_serverArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_snmp_server module""" + + argument_spec = { + "config": { + "options": { + "accounting": {"options": {"command": {"type": "str"}}, "type": "dict"}, + "cache": {"type": "int"}, + "chassis_id": {"type": "str"}, + "communities": { + "elements": "dict", + "options": { + "acl_v4": {"type": "str"}, + "acl_v6": {"type": "str"}, + "name": {"type": "str"}, + "ro": {"type": "bool"}, + "rw": {"type": "bool"}, + "view": {"type": "str"}, + }, + "type": "list", + }, + "contact": {"type": "str"}, + "context": {"elements": "str", "type": "list"}, + "drop": { + "options": {"unknown_user": {"type": "bool"}, "vrf_traffic": {"type": "bool"}}, + "type": "dict", + }, + "engine_id": { + "elements": "dict", + "options": { + "id": {"type": "str"}, + "local": {"type": "bool"}, + "remote": { + "options": { + "host": {"type": "str"}, + "udp_port": {"type": "int"}, + "vrf": {"type": "str"}, + }, + "type": "dict", + }, + }, + "type": "list", + }, + "file_transfer": { + "options": { + "access_group": {"type": "str"}, + "protocol": {"type": "list", "elements": "str"}, + }, + "type": "dict", + }, + "groups": { + "elements": "dict", + "options": { + "context": {"type": "str"}, + "version_option": {"choices": ["auth", "noauth", "priv"], "type": "str"}, + "group": {"type": "str"}, + "notify": {"type": "str"}, + "read": {"type": "str"}, + "version": {"choices": ["v1", "v3", "v2c"], "type": "str"}, + "write": {"type": "str"}, + "acl_v4": {"type": "str"}, + "acl_v6": {"type": "str"}, + }, + "type": "list", + }, + "hosts": { + "elements": "dict", + "options": { + "host": {"type": "str"}, + "informs": {"type": "bool"}, + "community_string": {"type": "str"}, + "traps": {"type": "list", "elements": "str"}, + "version": {"choices": ["1", "2c", "3"], "type": "str"}, + "version_option": {"choices": ["auth", "noauth", "priv"], "type": "str"}, + "vrf": {"type": "str"}, + }, + "type": "list", + }, + "if_index": {"type": "bool"}, + "inform": { + "options": { + "pending": {"type": "int"}, + "retries": {"type": "int"}, + "timeout": {"type": "int"}, + }, + "type": "dict", + }, + "ip": { + "options": {"dscp": {"type": "int"}, "precedence": {"type": "int"}}, + "type": "dict", + }, + "location": {"type": "str"}, + "manager": {"type": "int"}, + "packet_size": {"type": "int"}, + "password_policy": { + "elements": "dict", + "no_log": False, + "options": { + "change": {"type": "int"}, + "digits": {"type": "int"}, + "lower_case": {"type": "int"}, + "max_len": {"type": "int"}, + "min_len": {"type": "int"}, + "policy_name": {"type": "str"}, + "special_char": {"type": "int"}, + "upper_case": {"type": "int"}, + "username": {"type": "str"}, + }, + "type": "list", + }, + "queue_length": {"type": "int"}, + "source_interface": {"type": "str"}, + "system_shutdown": {"type": "bool"}, + "trap_source": {"type": "str"}, + "trap_timeout": {"type": "int"}, + "traps": { + "options": { + "auth_framework": { + "options": { + "sec_violation": {"type": "bool"}, + "enable": {"type": "bool"}, + }, + "type": "dict", + }, + "bfd": { + "options": { + "enable": {"type": "bool"}, + "session_down": {"type": "bool"}, + "session_up": {"type": "bool"}, + }, + "type": "dict", + }, + "bgp": { + "options": { + "cbgp2": {"type": "bool"}, + "enable": {"type": "bool"}, + "state_changes": { + "options": { + "all": {"type": "bool"}, + "backward_trans": {"type": "bool"}, + "limited": {"type": "bool"}, + "enable": {"type": "bool"}, + }, + "type": "dict", + }, + "threshold": { + "options": {"prefix": {"type": "bool"}}, + "type": "dict", + }, + }, + "type": "dict", + }, + "bridge": { + "options": { + "newroot": {"type": "bool"}, + "enable": {"type": "bool"}, + "topologychange": {"type": "bool"}, + }, + "type": "dict", + }, + "casa": {"type": "bool"}, + "cef": { + "options": { + "inconsistency": {"type": "bool"}, + "peer_fib_state_change": {"type": "bool"}, + "peer_state_change": {"type": "bool"}, + "resource_failure": {"type": "bool"}, + "enable": {"type": "bool"}, + }, + "type": "dict", + }, + "cnpd": {"type": "bool"}, + "config": {"type": "bool"}, + "config_copy": {"type": "bool"}, + "config_ctid": {"type": "bool"}, + "cpu": { + "options": {"enable": {"type": "bool"}, "threshold": {"type": "bool"}}, + "type": "dict", + }, + "dhcp": {"type": "bool"}, + "dlsw": { + "options": { + "circuit": {"type": "bool"}, + "enable": {"type": "bool"}, + "tconn": {"type": "bool"}, + }, + "type": "dict", + }, + "eigrp": {"type": "bool"}, + "entity": {"type": "bool"}, + "energywise": {"type": "bool"}, + "envmon": { + "options": { + "fan": { + "options": { + "shutdown": {"type": "bool"}, + "enable": {"type": "bool"}, + "status": {"type": "bool"}, + "supply": {"type": "bool"}, + "temperature": {"type": "bool"}, + }, + "type": "dict", + }, + "shutdown": {"type": "bool"}, + "status": {"type": "bool"}, + "supply": {"type": "bool"}, + "temperature": {"type": "bool"}, + }, + "type": "dict", + }, + "ethernet": { + "options": { + "cfm": { + "options": { + "alarm": {"type": "bool"}, + "cc": { + "type": "dict", + "options": { + "config": {"type": "bool"}, + "cross_connect": {"type": "bool"}, + "loop": {"type": "bool"}, + "mep_down": {"type": "bool"}, + "mep_up": {"type": "bool"}, + }, + }, + "crosscheck": { + "type": "dict", + "options": { + "mep_missing": {"type": "bool"}, + "mep_unknown": {"type": "bool"}, + "service_up": {"type": "bool"}, + }, + }, + }, + "type": "dict", + }, + "evc": { + "options": { + "create": {"type": "bool"}, + "delete": {"type": "bool"}, + "status": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "event_manager": {"type": "bool"}, + "flowmon": {"type": "bool"}, + "firewall": { + "options": { + "enable": {"type": "bool"}, + "serverstatus": {"type": "bool"}, + }, + "type": "dict", + }, + "frame_relay": { + "options": { + "enable": {"type": "bool"}, + "subif": { + "options": { + "count": {"type": "int"}, + "interval": {"type": "int"}, + "enable": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "fru_ctrl": {"type": "bool"}, + "hsrp": {"type": "bool"}, + "ike": { + "options": { + "policy": { + "options": { + "add": {"type": "bool"}, + "delete": {"type": "bool"}, + }, + "type": "dict", + }, + "tunnel": { + "options": { + "start": {"type": "bool"}, + "stop": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "ipmulticast": {"type": "bool"}, + "ipsec": { + "options": { + "cryptomap": { + "options": { + "add": {"type": "bool"}, + "attach": {"type": "bool"}, + "delete": {"type": "bool"}, + "detach": {"type": "bool"}, + }, + "type": "dict", + }, + "too_many_sas": {"type": "bool"}, + "tunnel": { + "options": { + "start": {"type": "bool"}, + "stop": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "ipsla": {"type": "bool"}, + "l2tun": { + "options": { + "pseudowire_status": {"type": "bool"}, + "session": {"type": "bool"}, + }, + "type": "dict", + }, + "msdp": {"type": "bool"}, + "mvpn": {"type": "bool"}, + "mpls_vpn": {"type": "bool"}, + "ospf": { + "options": { + "cisco_specific": { + "options": { + "error": {"type": "bool"}, + "lsa": {"type": "bool"}, + "retransmit": {"type": "bool"}, + "state_change": { + "options": { + "nssa_trans_change": {"type": "bool"}, + "shamlink": { + "options": { + "interface": {"type": "bool"}, + "neighbor": {"type": "bool"}, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + }, + "type": "dict", + }, + "error": {"type": "bool"}, + "retransmit": {"type": "bool"}, + "lsa": {"type": "bool"}, + "state_change": {"type": "bool"}, + }, + "type": "dict", + }, + "pim": { + "options": { + "invalid_pim_message": {"type": "bool"}, + "neighbor_change": {"type": "bool"}, + "rp_mapping_change": {"type": "bool"}, + "enable": {"type": "bool"}, + }, + "type": "dict", + }, + "vrfmib": { + "options": { + "vrf_up": {"type": "bool"}, + "vrf_down": {"type": "bool"}, + "vnet_trunk_up": {"type": "bool"}, + "vnet_trunk_down": {"type": "bool"}, + }, + "type": "dict", + }, + "pki": {"type": "bool"}, + "rsvp": {"type": "bool"}, + "isis": {"type": "bool"}, + "pw_vc": {"type": "bool"}, + "snmp": { + "options": { + "authentication": {"type": "bool"}, + "coldstart": {"type": "bool"}, + "linkdown": {"type": "bool"}, + "linkup": {"type": "bool"}, + "warmstart": {"type": "bool"}, + }, + "type": "dict", + }, + "syslog": {"type": "bool"}, + "transceiver_all": {"type": "bool"}, + "tty": {"type": "bool"}, + "vrrp": {"type": "bool"}, + }, + "type": "dict", + }, + "users": { + "elements": "dict", + "options": { + "acl_v6": {"type": "str"}, + "acl_v4": {"type": "str"}, + "authentication": { + "no_log": False, + "type": "dict", + "options": { + "algorithm": {"type": "str", "choices": ["md5", "sha"]}, + "password": {"type": "str", "no_log": True}, + }, + }, + "encryption": { + "no_log": False, + "type": "dict", + "options": { + "priv": {"type": "str", "choices": ["3des", "aes", "des"]}, + "priv_option": {"type": "str"}, + "password": {"type": "str", "no_log": True}, + }, + }, + "group": {"type": "str"}, + "remote": {"type": "str"}, + "udp_port": {"type": "int"}, + "username": {"type": "str"}, + "version": {"choices": ["v1", "v2c", "v3"], "type": "str"}, + "version_option": {"choices": ["encrypted"], "type": "str"}, + "vrf": {"type": "str"}, + }, + "type": "list", + }, + "views": { + "elements": "dict", + "options": { + "excluded": {"type": "bool"}, + "family_name": {"type": "str"}, + "included": {"type": "bool"}, + "name": {"type": "str"}, + }, + "type": "list", + }, + }, + "type": "dict", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "parsed", + "gathered", + "rendered", + ], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/static_routes/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/static_routes/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/static_routes/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/static_routes/static_routes.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/static_routes/static_routes.py new file mode 100644 index 000000000..3d08590fb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/static_routes/static_routes.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +# Copyright 2023 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the +# ansible.content_builder. +# +# Manually editing this file is not advised. +# +# To update the argspec make the desired changes +# in the documentation in the module file and re-run +# ansible.content_builder commenting out +# the path to external 'docstring' in build.yaml. +# +############################################## + +""" +The arg spec for the ios_static_routes module +""" + + +class Static_routesArgs(object): # pylint: disable=R0903 + """The arg spec for the ios_static_routes module""" + + argument_spec = { + "config": { + "type": "list", + "elements": "dict", + "options": { + "vrf": {"type": "str"}, + "address_families": { + "elements": "dict", + "type": "list", + "options": { + "afi": { + "required": True, + "type": "str", + "choices": ["ipv4", "ipv6"], + }, + "routes": { + "type": "list", + "elements": "dict", + "options": { + "dest": {"type": "str", "required": True}, + "topology": {"type": "str"}, + "next_hops": { + "type": "list", + "elements": "dict", + "options": { + "forward_router_address": {"type": "str"}, + "interface": {"type": "str"}, + "dhcp": {"type": "bool"}, + "distance_metric": {"type": "int"}, + "global": {"type": "bool"}, + "name": {"type": "str"}, + "multicast": {"type": "bool"}, + "unicast": {"type": "bool"}, + "permanent": {"type": "bool"}, + "tag": {"type": "int"}, + "track": {"type": "int"}, + }, + }, + }, + }, + }, + }, + }, + }, + "running_config": {"type": "str"}, + "state": { + "type": "str", + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "gathered", + "rendered", + "parsed", + ], + "default": "merged", + }, + } # pylint: disable=C0301 diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/vlans.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/vlans.py new file mode 100644 index 000000000..a3b0b6acd --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/vlans.py @@ -0,0 +1,75 @@ +# +# -*- 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 arg spec for the ios_vlans module +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +class VlansArgs(object): + """The arg spec for the ios_vlans module""" + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "name": {"type": "str"}, + "vlan_id": {"required": True, "type": "int"}, + "mtu": {"type": "int"}, + "remote_span": {"type": "bool"}, + "state": {"type": "str", "choices": ["active", "suspend"]}, + "shutdown": {"type": "str", "choices": ["enabled", "disabled"]}, + "private_vlan": { + "type": "dict", + "options": { + "type": {"type": "str", "choices": ["primary", "community", "isolated"]}, + "associated": {"type": "list", "elements": "int"}, + }, + }, + }, + "type": "list", + }, + "running_config": {"type": "str"}, + "state": { + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "parsed", + "gathered", + ], + "default": "merged", + "type": "str", + }, + } diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acl_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acl_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acl_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acl_interfaces/acl_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acl_interfaces/acl_interfaces.py new file mode 100644 index 000000000..e8d64f18c --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acl_interfaces/acl_interfaces.py @@ -0,0 +1,124 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios_acl_interfaces class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from ansible.module_utils._text import to_text +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.acl_interfaces import ( + Acl_interfacesTemplate, +) + + +class Acl_interfaces(ResourceModule): + """ + The ios_acl_interfaces class + """ + + def __init__(self, module): + super(Acl_interfaces, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="acl_interfaces", + tmplt=Acl_interfacesTemplate(), + ) + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Select the appropriate function based on the state provided + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + # convert list of dicts to dicts of dicts + wantd = {entry["name"]: entry for entry in self.want} + haved = {entry["name"]: entry for entry in self.have} + + # turn all lists of dicts into dicts prior to merge + for entry in wantd, haved: + self._list_to_dict(entry) + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, empty out wantd and set haved to wantd + if self.state == "deleted": + haved = {k: v for k, v in haved.items() if k in wantd or not wantd} + wantd = {} + + # remove superfluous config + if self.state in ["overridden", "deleted"]: + for k, have in haved.items(): + if k not in wantd: + self._compare(want={}, have=have) + + for k, want in wantd.items(): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + begin = len(self.commands) + self._compare_lists(want=want, have=have) + if len(self.commands) != begin: + self.commands.insert(begin, self._tmplt.render(want or have, "interface", False)) + + def _compare_lists(self, want, have): + wdict = want.get("access_groups", {}) + hdict = have.get("access_groups", {}) + + for afi in ("ipv4", "ipv6"): + wacls = wdict.pop(afi, {}).pop("acls", {}) + hacls = hdict.pop(afi, {}).pop("acls", {}) + + for key, entry in wacls.items(): + if entry != hacls.pop(key, {}): + entry["afi"] = afi + self.addcmd(entry, "access_groups", False) + # remove remaining items in have for replaced + for entry in hacls.values(): + entry["afi"] = afi + self.addcmd(entry, "access_groups", True) + + def _list_to_dict(self, entry): + for item in entry.values(): + for ag in item.get("access_groups", []): + ag["acls"] = { + subentry["direction"]: { + "name": to_text(subentry["name"]), + "direction": subentry["direction"], + } + for subentry in ag.get("acls", []) + } + item["access_groups"] = { + subentry["afi"]: subentry for subentry in item.get("access_groups", []) + } diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/acls.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/acls.py new file mode 100644 index 000000000..9d29555cc --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/acls.py @@ -0,0 +1,315 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios_acls class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.acls import ( + AclsTemplate, +) + + +class Acls(ResourceModule): + """ + The ios_acls config class + """ + + def __init__(self, module): + super(Acls, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="acls", + tmplt=AclsTemplate(), + ) + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + haved, wantd = dict(), dict() + + if self.have: + haved = self.list_to_dict(self.have) + if self.want: + wantd = self.list_to_dict(self.want) + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, empty out wantd and set haved to want + if self.state == "deleted": + haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} + if wantd.get("ipv4") and not haved.get("ipv4"): + haved["ipv4"] = {} + if wantd.get("ipv6") and not haved.get("ipv6"): + haved["ipv6"] = {} + for key, hvalue in iteritems(haved): + wvalue = wantd.pop(key, {}) + if wvalue: + wplists = wvalue.get("acls", {}) + hplists = hvalue.get("acls", {}) + hvalue["acls"] = { + k: v for k, v in iteritems(hplists) if k in wplists or not wplists + } + + # remove superfluous config for overridden and deleted + if self.state in ["overridden", "deleted"]: + for k, have in iteritems(haved): + if k not in wantd: + self._compare(want={}, have=have, afi=k) + + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {}), afi=k) + + def _compare(self, want, have, afi): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the acls network resource. + """ + + def rearrange_cmds(aces): + non_negates = [] + negates = [] + for ace in aces: + if ace.startswith("no"): + negates.append(ace) + else: + non_negates.append(ace) + if non_negates or negates: + negates.extend(non_negates) + return negates + + wplists = want.get("acls", {}) + hplists = have.get("acls", {}) + for wname, wentry in iteritems(wplists): + hentry = hplists.pop(wname, {}) + acl_type = wentry["acl_type"] if wentry.get("acl_type") else hentry.get("acl_type") + begin = len(self.commands) # to determine the index for acl command + self._compare_aces( + wentry.pop("aces", {}), + hentry.pop("aces", {}), + afi, + wname, + ) # handle aces + + end = len(self.commands) + self.commands[begin:end] = rearrange_cmds(self.commands[begin:]) + + if len(self.commands) != begin or (not have and want): + _cmd = self.acl_name_cmd(wname, afi, acl_type) + self.commands.insert(begin, _cmd) + + if self.state in ["overridden", "deleted"]: + # remove remaining acls lists + for hname, hval in iteritems(hplists): + _cmd = self.acl_name_cmd(hname, afi, hval.get("acl_type", "")) + self.commands.append("no " + _cmd) + + def _compare_aces(self, want, have, afi, name): + """compares all aces""" + + def add_afi(entry, afi): + """adds afi needed for + setval processing""" + if entry: + entry["afi"] = afi + return entry + + for wseq, wentry in iteritems(want): + hentry = have.pop(wseq, {}) + if hentry: + hentry = self.sanitize_protocol_options(wentry, hentry) + if hentry != wentry: + if hentry: + if self.state == "merged": + self._module.fail_json( + msg="Cannot update existing sequence {0} of ACLs {1} with state merged." + " Please use state replaced or overridden.".format( + hentry.get("sequence", ""), + name, + ), + ) + else: # other action states + if hentry.get("remarks"): # remove remark if not in want + for rems in hentry.get("remarks"): + if rems not in wentry.get("remarks", {}): + self.addcmd({"remarks": rems}, "remarks", negate=True) + else: # remove ace if not in want + self.addcmd(add_afi(hentry, afi), "aces", negate=True) + if wentry.get("remarks"): # add remark if not in have + for rems in wentry.get("remarks"): + if rems not in hentry.get("remarks", {}): + self.addcmd({"remarks": rems}, "remarks") + else: # add ace if not in have + self.addcmd(add_afi(wentry, afi), "aces") + + # remove remaining entries from have aces list + for hseq in have.values(): + if hseq.get("remarks"): # remove remarks that are extra in have + for rems in hseq.get("remarks"): + self.addcmd({"remarks": rems}, "remarks", negate=True) + else: # remove extra aces + self.addcmd(add_afi(hseq, afi), "aces", negate=True) + + def sanitize_protocol_options(self, wace, hace): + """handles protocol and protocol options as optional attribute""" + if wace.get("protocol_options"): + if not wace.get("protocol") and ( + list(wace.get("protocol_options"))[0] == hace.get("protocol") + ): + hace.pop("protocol") + return hace + + def acl_name_cmd(self, name, afi, acl_type): + """generate parent acl command""" + + if afi == "ipv4": + if not acl_type: + try: + acl_id = int(name) + if not acl_type: + if acl_id >= 1 and acl_id <= 99: + acl_type = "standard" + if acl_id >= 100 and acl_id <= 199: + acl_type = "extended" + except ValueError: + acl_type = "extended" + command = "ip access-list {0} {1}".format(acl_type, name) + elif afi == "ipv6": + command = "ipv6 access-list {0}".format(name) + return command + + def list_to_dict(self, param): + """converts list attributes to dict""" + + temp, count = dict(), 0 + if param: + for each in param: # ipv4 and ipv6 acl + temp_acls = {} + if each.get("acls"): + for acl in each.get("acls"): # check each acl for aces + temp_aces = {} + if acl.get("aces"): + temp_rem = [] # remarks if defined in an ace + for ace in acl.get("aces"): # each ace turned to dict + if ace.get("destination") and ace.get("destination", {}).get( + "port_protocol", + {}, + ): + for k, v in ( + ace.get("destination", {}).get("port_protocol", {}).items() + ): + ace["destination"]["port_protocol"][ + k + ] = self.port_protocl_no_to_protocol(v) + if acl.get("acl_type") == "standard": + for ks in list(ace.keys()): + if ks not in [ + "sequence", + "grant", + "source", + "remarks", + "log", + ]: # failing for mutually exclusive standard acl key + self._module.fail_json( + "Unsupported attribute for standard ACL - {0}.".format( + ks, + ), + ) + + if ace.get("remarks"): + en_name = str(acl.get("name")) + "remark" + temp_rem.extend(ace.pop("remarks")) + + if ace.get("sequence"): + temp_aces.update({ace.get("sequence"): ace}) + elif ace: + count += 1 + temp_aces.update({"_" + str(count): ace}) + + if temp_rem: # add remarks to the temp ace + temp_aces.update({en_name: {"remarks": temp_rem}}) + + if acl.get("acl_type"): # update acl dict with req info + temp_acls.update( + {acl.get("name"): {"aces": temp_aces, "acl_type": acl["acl_type"]}}, + ) + else: # if no acl type then here eg: ipv6 + temp_acls.update({acl.get("name"): {"aces": temp_aces}}) + each["acls"] = temp_acls + temp.update({each["afi"]: {"acls": temp_acls}}) + return temp + + def port_protocl_no_to_protocol(self, num): + map_protocol = { + "179": "bgp", + "19": "chargen", + "514": "cmd", + "13": "daytime", + "9": "discard", + "53": "domain", + "7": "echo", + "512": "exec", + "79": "finger", + "21": "ftp", + "20": "ftp-data", + "70": "gopher", + "101": "hostname", + "113": "ident", + "194": "irc", + "543": "klogin", + "544": "kshell", + "513": "login", + "515": "lpd", + "135": "msrpc", + "119": "nntp", + "5001": "onep-plain", + "5002": "onep-tls", + "496": "pim-auto-rp", + "109": "pop2", + "110": "pop3", + "25": "smtp", + "111": "sunrpc", + "49": "tacacs", + "517": "talk", + "23": "telnet", + "37": "time", + "540": "uucp", + "43": "whois", + "80": "www", + } # NOTE - "514": "syslog" duplicate value device renders "cmd" + return map_protocol.get(num, num) diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_address_family/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_address_family/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_address_family/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_address_family/bgp_address_family.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_address_family/bgp_address_family.py new file mode 100644 index 000000000..36ea3e963 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_address_family/bgp_address_family.py @@ -0,0 +1,428 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The cisco.ios_bgp_address_family config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.bgp_address_family import ( + Bgp_address_familyTemplate, +) + + +class Bgp_address_family(ResourceModule): + """ + The cisco.ios_bgp_address_family config class + """ + + parsers = [ + "as_number", # generic + "aggregate_addresses", + "auto_summary", + "default", + "default_information", + "default_metric", + "distance", + "table_map", + "bgp.additional_paths.select", # bgp + "bgp.additional_paths.install", + "bgp.additional_paths.receive", + "bgp.additional_paths.send", + "bgp.aggregate_timer", + "bgp.dmzlink_bw", + "bgp.nexthop.route_map", + "bgp.nexthop.trigger.delay", + "bgp.nexthop.trigger.enable", + "bgp.redistribute_internal", + "bgp.route_map", + "bgp.scan_time", + "bgp.soft_reconfig_backup", + "bgp.update_group", + "bgp.dampening", + "bgp.slow_peer_options.detection.enable", + "bgp.slow_peer_options.detection.threshold", + "bgp.slow_peer_options.split_update_group.dynamic", + "bgp.slow_peer.split_update_group.permanent", + "snmp.context.user", # snmp + "snmp.context.community", + "redistribute.application", # redistribute + "redistribute.bgp", + "redistribute.connected", + "redistribute.eigrp", + "redistribute.isis", + "redistribute.iso_igrp", + "redistribute.lisp", + "redistribute.mobile", + "redistribute.odr", + "redistribute.ospf", + "redistribute.ospfv3", + "redistribute.rip", + "redistribute.static", + "redistribute.vrf", # redistribute + ] + + def __init__(self, module): + super(Bgp_address_family, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="bgp_address_family", + tmplt=Bgp_address_familyTemplate(), + ) + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + if self.state in ["merged", "replaced", "overridden"]: + if self.have.get("as_number") and self.want.get("as_number") != self.have.get( + "as_number", + ): + self._module.fail_json( + msg="BGP is already running. Only one BGP instance is allowed per device.", + ) + + for each in self.want, self.have: + each["address_family"] = self._bgp_add_fam_list_to_dict(each.get("address_family", [])) + + wantd = self.want + haved = self.have + + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + if self.state == "deleted": + for k, have in haved.get("address_family", {}).items(): + if wantd.get("address_family"): + if wantd["address_family"].get(k): + self.commands.append( + self._tmplt.render(wantd["address_family"].get(k, {}), "afi", True), + ) + else: # to clear off all afs + self.commands.append(self._tmplt.render(have, "afi", True)) + + # remove superfluous config + if self.state in ["overridden"]: + for k, have in haved.get("address_family").items(): + if k not in wantd.get("address_family", {}): + self._compare(want={}, have=have) + + if self.state != "deleted": # not deleted state + for k, want in wantd.get("address_family", {}).items(): + self._compare(want=want, have=haved["address_family"].pop(k, {})) + + # adds router bgp AS_NUMB command + if len(self.commands) > 0: + if self.want.get("as_number"): + as_number = self.want + else: + as_number = self.have + self.commands.insert(0, self._tmplt.render(as_number, "as_number", False)) + + def _compare(self, want, have): + begin = len(self.commands) + # for everything else + self.compare(parsers=self.parsers, want=want, have=have) + # for neighbors + self._compare_neighbor_lists(want.get("neighbors", {}), have.get("neighbors", {})) + # for networks + self._compare_network_lists(want.get("networks", {}), have.get("networks", {})) + # for aggregate_addresses + self._compare_agg_add_lists( + want.get("aggregate_addresses", {}), + have.get("aggregate_addresses", {}), + ) + # for distribution of ospfv2 and ospfv3 routes + for ospf_version in ["ospf", "ospfv3"]: + self._compare_redist_ospf( + ospf_version, + want.get("redistribute", {}).get(ospf_version, {}), + have.get("redistribute", {}).get(ospf_version, {}), + ) + # add af command + if len(self.commands) != begin: + self.commands.insert(begin, self._tmplt.render(want or have, "afi", False)) + + def _compare_redist_ospf(self, _parser, w_attr, h_attr): + """ + Adds and/or removes commands related to + ospf and ospv3 redistribution + :param _parser: ospf or ospfv3 + :param w_attr: content of want['redistribute']['ospf'] + :param h_attr:content of have['redistribute']['ospf'] + :return: None + """ + for wkey, wentry in w_attr.items(): + if wentry != h_attr.pop(wkey, {}): + # always negate the command in the + # appropriate states before applying + if self.state in ["overridden", "replaced"]: + self.addcmd(wentry, f"redistribute.{_parser}", True) + self.addcmd(wentry, f"redistribute.{_parser}", False) + + # remove remaining items in have for replaced state + for hkey, hentry in h_attr.items(): + self.addcmd(hentry, "redistribute.ospf", True) + + def _compare_neighbor_lists(self, want, have): + """Compare neighbor list of dict""" + neig_parses = [ + "peer_group", + "peer_group_name", + "local_as", + "remote_as", + "activate", + "additional_paths", + "advertises.additional_paths", + "advertises.best_external", + "advertises.diverse_path", + "advertise_map", + "advertisement_interval", + "aigp", + "aigp.send.cost_community", + "aigp.send.med", + "allow_policy", + "allowas_in", + "as_override", + "bmp_activate", + "capability", + "cluster_id", + "default_originate", + "default_originate.route_map", + "description", + "disable_connected_check", + "ebgp_multihop", + "distribute_list", + "dmzlink_bw", + "filter_list", + "fall_over.bfd", + "fall_over.route_map", + "ha_mode", + "inherit", + "internal_vpn_client", + "log_neighbor_changes", + "maximum_prefix", + "nexthop_self.set", + "nexthop_self.all", + "next_hop_unchanged.set", + "next_hop_unchanged.allpaths", + "password_options", + "path_attribute.discard", + "path_attribute.treat_as_withdraw", + "route_maps", + "remove_private_as.set", + "remove_private_as.all", + "remove_private_as.replace_as", + "route_reflector_client", + "route_server_client", + "send_community.set", + "send_community.both", + "send_community.extended", + "send_community.standard", + "shutdown", + "slow_peer_options.detection", + "slow_peer_options.split_update_group", + "soft_reconfiguration", + "soo", + "timers", + "transport.connection_mode", + "transport.multi_session", + "transport.path_mtu_discovery", + "ttl_security", + "unsuppress_map", + "version", + "weight", + ] + + for name, w_neighbor in want.items(): + have_nbr = have.pop(name, {}) + self.compare(parsers=neig_parses, want=w_neighbor, have=have_nbr) + for i in ["route_maps", "prefix_lists"]: # handles route_maps, prefix_lists + want_route_or_prefix = w_neighbor.pop(i, {}) + have_route_or_prefix = have_nbr.pop(i, {}) + if want_route_or_prefix: + for k_rmps, w_rmps in want_route_or_prefix.items(): + have_rmps = have_route_or_prefix.pop(k_rmps, {}) + w_rmps["neighbor_address"] = w_neighbor.get("neighbor_address") + if have_rmps: + have_rmps["neighbor_address"] = have_nbr.get("neighbor_address") + have_rmps = {i: have_rmps} + self.compare(parsers=[i], want={i: w_rmps}, have=have_rmps) + for name, h_neighbor in have.items(): + self.compare(parsers="neighbor_address", want={}, have=h_neighbor) + + def _compare_network_lists(self, w_attr, h_attr): + """Handling of network list options.""" + for wkey, wentry in w_attr.items(): + if wentry != h_attr.pop(wkey, {}): + self.addcmd(wentry, "networks", False) + + # remove remaining items in have for replaced state + for hkey, hentry in h_attr.items(): + self.addcmd(hentry, "networks", True) + + def _compare_agg_add_lists(self, w_attr, h_attr): + """Handling of agg_add list options.""" + for wkey, wentry in w_attr.items(): + if wentry != h_attr.pop(wkey, {}): + self.addcmd(wentry, "aggregate_addresses", False) + # remove remaining items in have for replaced state + for hkey, hentry in h_attr.items(): + self.addcmd(hentry, "aggregate_addresses", True) + + def _bgp_add_fam_list_to_dict(self, tmp_data): + """Convert all list of dicts to dicts of dicts, also deals with deprecated attributes""" + p_key = { + "aggregate_address": "address", + "aggregate_addresses": "address", + "neighbor": "neighbor_address", + "neighbors": "neighbor_address", + "route_maps": "name", + "prefix_lists": "name", + "networks": "address", + "network": "address", + "ospf": "process_id", + "ospfv3": "process_id", + } + + af_data = {} + for af in tmp_data: + _af = {} + for k, tval in af.items(): + val = deepcopy(tval) + if k == "neighbor" or k == "neighbors": + tmp = {} + for neib in val: + # address/ tag/ ipv6_address to neighbor_address + if neib.get("address"): + neib["neighbor_address"] = neib.pop("address") + if neib.get("tag"): + neib["neighbor_address"] = neib.pop("tag") + if neib.get("ipv6_address"): + neib["neighbor_address"] = neib.pop("ipv6_address") + if neib.get("ipv6_adddress"): + neib["neighbor_address"] = neib.pop("ipv6_adddress") + # next_hop_self deprecated added nexthop_self + if neib.get("next_hop_self"): + neib["nexthop_self"] = {"set": neib.pop("next_hop_self")} + # prefix_list and prefix_lists + if neib.get("prefix_list"): # deprecated made list + neib["prefix_lists"] = [neib.pop("prefix_list")] + if neib.get("prefix_lists"): + neib["prefix_lists"] = { + str(i[p_key["prefix_lists"]]): i for i in neib.get("prefix_lists") + } + # route_map and route_maps + if neib.get("route_map"): # deprecated made list + neib["route_maps"] = [neib.pop("route_map")] + if neib.get("route_maps"): + neib["route_maps"] = { + str(i[p_key["route_maps"]]): i for i in neib.get("route_maps") + } + # slow_peer to slow_peer_options + if neib.get("slow_peer"): # only one slow_peer is allowed + neib["slow_peer_options"] = neib.pop("slow_peer")[0] + # make dict neighbors dict + tmp[neib[p_key[k]]] = neib + _af["neighbors"] = tmp + # make dict networks dict + elif k == "network" or k == "networks": + _af["networks"] = {str(i[p_key[k]]): i for i in tval} + # make dict aggregate_addresses dict + elif k == "aggregate_address" or k == "aggregate_addresses": + _af["aggregate_addresses"] = {str(i[p_key[k]]): i for i in tval} + # slow_peer to slow_peer_options + elif k == "bgp": + _af["bgp"] = val + if val.get("slow_peer"): # only one slow_peer is allowed + _af["bgp"]["slow_peer_options"] = _af["bgp"].pop("slow_peer")[0] + # keep single dict to compare redistribute + elif k == "redistribute": + _redist = {} + for i in tval: + # establish elif sections for future protocols + # if necessary + if any(x in ["ospf", "ospfv3"] for x in i): + for ospf_version in ["ospf", "ospfv3"]: + if i.get(ospf_version): + _i = i[ospf_version] + + # Start handle deprecates + if _i.get("match"): + for depr in [ + "external", + "nssa_external", + "type_1", + "type_2", + ]: + if depr in _i["match"].keys(): + val = _i["match"].pop(depr, False) + if depr.startswith("type"): + # map deprecated nssa_external type to new option + if "nssa_externals" in _i["match"].keys(): + _i["match"]["nssa_externals"][depr] = val + else: + _i["match"]["nssa_externals"] = { + depr: val, + } + elif depr in ["external", "nssa_external"]: + # deprecated external and nssa_external are boolean + # so both types mapped to true + _i["match"][depr + "s"] = { + "type_1": True, + "type_2": True, + } + # End handle deprecates + + if ospf_version not in _redist: + _redist[ospf_version] = {} + + _redist[ospf_version].update( + { + str(_i[p_key[ospf_version]]): dict(_i.items()), + }, + ) + break + else: + _redist.update(i) + _af["redistribute"] = _redist + else: + _af[k] = tval + # make distinct address family entires + af_data[af.get("afi", "") + "_" + af.get("safi", "") + "_" + af.get("vrf", "")] = _af + + return af_data diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py new file mode 100644 index 000000000..59881d622 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py @@ -0,0 +1,473 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The cisco.ios_bgp_global config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.bgp_global import ( + Bgp_globalTemplate, +) + + +class Bgp_global(ResourceModule): + """ + The cisco.ios_bgp_global config class + """ + + parsers = [ + "auto_summary", + "bmp.buffer_size", + "bmp.initial_refresh.delay", + "bmp.initial_refresh.skip", + "bmp.server", + "bmp.server_options.activate", + "bmp.server_options.address", + "default_information", + "default_metric", + "distance.admin", + "distance.bgp", + "distance.mbgp", + "maximum_paths.paths", + "maximum_paths.eibgp", + "maximum_paths.ibgp", + "maximum_secondary_paths.paths", + "maximum_secondary_paths.eibgp", + "maximum_secondary_paths.ibgp", + "route_server_context.name", + "route_server_context.address_family", + "route_server_context.description", + "synchronization", + "table_map", + "timers", + "bgp.additional_paths", + "bgp.advertise_best_external", + "bgp.aggregate_timer", + "bgp.always_compare_med", + "bgp.asnotation", + "bgp.bestpath_options.aigp", + "bgp.bestpath_options.compare_routerid", + "bgp.bestpath_options.cost_community", + "bgp.bestpath_options.igp_metric", + "bgp.bestpath_options.med", + "bgp.client_to_client", + "bgp.cluster_id", + "bgp.confederation.peer", + "bgp.confederation.identifier", + "bgp.consistency_checker.auto_repair", + "bgp.consistency_checker.error_message", + "bgp.dampening", + "bgp.deterministic_med", + "bgp.dmzlink_bw", + "bgp.enforce_first_as", + "bgp.enhanced_error", + "bgp.fast_external_fallover", + "bgp.graceful_restart.set", + "bgp.graceful_restart.extended", + "bgp.graceful_restart.restart_time", + "bgp.graceful_restart.stalepath_time", + "bgp.graceful_shutdown.neighbors", + "bgp.graceful_shutdown.vrfs", + "bgp.listen.limit", + "bgp.listen.range", + "bgp.log_neighbor_changes", + "bgp.maxas_limit", + "bgp.maxcommunity_limit", + "bgp.maxextcommunity_limit", + "bgp.nexthop.route_map", + "bgp.nexthop.trigger.delay", + "bgp.nexthop.trigger.enable", + "bgp.nopeerup_delay_options.cold_boot", + "bgp.nopeerup_delay_options.post_boot", + "bgp.nopeerup_delay_options.nsf_switchover", + "bgp.nopeerup_delay_options.user_initiated", + "bgp.recursion", + "bgp.redistribute_internal", + "bgp.refresh.max_eor_time", + "bgp.refresh.stalepath_time", + "bgp.regexp", + "bgp.router_id", + "bgp.scan_time", + "bgp.slow_peer.detection.set", + "bgp.slow_peer.detection.threshold", + "bgp.slow_peer.split_update_group", + "bgp.snmp", + "bgp.sso", + "bgp.soft_reconfig_backup", + "bgp.suppress_inactive", + "bgp.transport", + "bgp.update_delay", + "bgp.update_group", + "bgp.upgrade_cli.set", + "bgp.upgrade_cli.af_mode", + ] + + def __init__(self, module): + super(Bgp_global, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="bgp_global", + tmplt=Bgp_globalTemplate(), + ) + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + if self.state in ["merged", "replaced"]: + w_asn = self.want.get("as_number") + h_asn = self.have.get("as_number") + + if h_asn and w_asn != h_asn: + self._module.fail_json( + msg="BGP is already configured with ASN {0}. " + "Please remove it with state: purged before " + "configuring new ASN".format(h_asn), + ) + + if self.want: + self.handle_deprecates(self.want) + + for each in self.want, self.have: + self._bgp_global_list_to_dict(each) + + if self.state == "deleted": + # deleted, clean up global params + if not self.want or (self.have.get("as_number") == self.want.get("as_number")): + self._compare(want={}, have=self.have) + + elif self.state == "purged": + # delete as_number takes down whole bgp config + if not self.want or (self.have.get("as_number") == self.want.get("as_number")): + self.addcmd(self.have or {}, "as_number", True) + + else: + wantd = self.want + # if state is merged, merge want with have and then compare + if self.state == "merged": + wantd = dict_merge(self.have, self.want) + + self._compare(want=wantd, have=self.have) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Bgp_global network resource. + """ + self.generic_list_parsers = ["distributes", "aggregate_addresses", "networks"] + if self._has_bgp_inject_maps(want): + self.generic_list_parsers.insert(0, "inject_maps") + + cmd_len = len(self.commands) # holds command length to add as_number + # for dict type attributes + self.compare(parsers=self.parsers, want=want, have=have) + + # for list type attributes + for _parse in self.generic_list_parsers: + if _parse == "inject_maps": + self._compare_generic_lists( + want.get("bgp", {}).get(_parse, {}), + have.get("bgp", {}).get(_parse, {}), + _parse, + ) + else: + self._compare_generic_lists(want.get(_parse, {}), have.get(_parse, {}), _parse) + + # for neighbors + self._compare_neighbor_lists(want.get("neighbors", {}), have.get("neighbors", {})) + + # for redistribute + self._compare_redistribute_lists(want.get("redistribute", {}), have.get("redistribute", {})) + + # add as_number in the begining fo command set if commands generated + if len(self.commands) != cmd_len or (not have and want): + self.commands.insert(0, self._tmplt.render(want or have, "as_number", False)) + + def _has_bgp_inject_maps(self, want): + if want.get("bgp", {}).get("inject_maps", {}): + return True + else: + return False + + def _compare_redistribute_lists(self, want, have): + """Compare redistribute list of dict""" + redist_parses = [ + "application", + "bgp", + "connected", + "eigrp", + "isis", + "iso_igrp", + "lisp", + "mobile", + "odr", + "ospf", + "ospfv3", + "rip", + "static", + "vrf", + ] + for name, w_redist in want.items(): + have_nbr = have.pop(name, {}) + self.compare(parsers=redist_parses, want=w_redist, have=have_nbr) + + # remove remaining items in have for replaced state + for name, h_redist in have.items(): + self.compare(parsers=redist_parses, want={}, have=h_redist) + + def _compare_neighbor_lists(self, want, have): + """Compare neighbor list of dict""" + neig_parses = [ + "remote_as", + "peer_group", + "bmp_activate", + "cluster_id", + "description", + "disable_connected_check", + "ebgp_multihop", + "fall_over.bfd", + "fall_over.route_map", + "ha_mode", + "inherit", + "local_as", + "log_neighbor_changes", + "password_options", + "path_attribute.discard", + "path_attribute.treat_as_withdraw", + "shutdown", + "soft_reconfiguration", + "ntimers", + "transport.connection_mode", + "transport.multi_session", + "transport.path_mtu_discovery", + "ttl_security", + "unsuppress_map", + "update_source", + "version", + "weight", + "activate", + "additional_paths", + "advertise.additional_paths", + "advertise.best_external", + "advertise.diverse_path", + "advertise_map", + "advertisement_interval", + "aigp", + "aigp.send.cost_community", + "aigp.send.med", + "allow_policy", + "allowas_in", + "as_override", + "bmp_activate", + "capability", + "default_originate", + "default_originate.route_map", + "distribute_list", + "dmzlink_bw", + "filter_list", + "maximum_prefix", + "next_hop_self.set", + "next_hop_self.all", + "next_hop_unchanged.set", + "next_hop_unchanged.allpaths", + "remove_private_as.set", + "remove_private_as.all", + "remove_private_as.replace_as", + "route_reflector_client", + "route_server_client.set", + "route_server_client.context", + "send_community.set", + "send_community.both", + "send_community.extended", + "send_community.standard", + "send_label.set", + "send_label.explicit_null", + "slow_peer.detection", + "slow_peer.split_update_group", + "translate_update.set", + "translate_update.nlri", + ] + + for name, w_neighbor in want.items(): + have_nbr = have.pop(name, {}) + want_route = w_neighbor.pop("route_maps", {}) + have_route = have_nbr.pop("route_maps", {}) + self.compare(parsers=neig_parses, want=w_neighbor, have=have_nbr) + if want_route: + for k_rmps, w_rmps in want_route.items(): + have_rmps = have_route.pop(k_rmps, {}) + w_rmps["neighbor_address"] = w_neighbor.get("neighbor_address") + if have_rmps: + have_rmps["neighbor_address"] = have_nbr.get("neighbor_address") + have_rmps = {"route_maps": have_rmps} + self.compare( + parsers=["route_maps"], + want={"route_maps": w_rmps}, + have=have_rmps, + ) + for name, h_neighbor in have.items(): + self.compare(parsers="neighbor_address", want={}, have=h_neighbor) + + def _compare_generic_lists(self, w_attr, h_attr, parser): + """Handling of gereric list options.""" + for wkey, wentry in iteritems(w_attr): + if wentry != h_attr.pop(wkey, {}): + self.addcmd(wentry, parser, False) + + # remove remaining items in have for replaced state + for hkey, hentry in iteritems(h_attr): + self.addcmd(hentry, parser, True) + + def _bgp_global_list_to_dict(self, tmp_data): + """Convert all list of dicts to dicts of dicts""" + p_key = { + "aggregate_addresses": "address", + "inject_maps": "name", + "distributes": ["acl", "gateway", "prefix"], + "neighbors": "neighbor_address", + "route_maps": "name", + "networks": "address", + "bgp": None, + "redistribute": [ + "application", + "bgp", + "eigrp", + "isis", + "iso_igrp", + "ospf", + "ospfv3", + "vrf", + ], + } + for k, _v in p_key.items(): + if tmp_data.get(k) and k not in ["distributes", "redistribute", "bgp"]: + if k == "neighbors": + for neb in tmp_data.get("neighbors"): + neb = self._bgp_global_list_to_dict(neb) + _ntimer = neb.pop("timers", {}) + if _ntimer: + neb["ntimers"] = _ntimer + tmp_data[k] = {str(i[p_key[k]]): i for i in tmp_data[k]} + elif tmp_data.get("distributes") and k == "distributes": + tmp_data[k] = {str("".join([i.get(j, "") for j in _v])): i for i in tmp_data[k]} + elif tmp_data.get("redistribute") and k == "redistribute": + tmp_data[k] = { + str( + [ + ky + vl.get("name", "") + vl.get("process_id", "") if ky in _v else ky + for ky, vl in i.items() + ][0], + ): i + for i in tmp_data[k] + } + elif tmp_data.get("bgp") and k == "bgp": + tmp_data[k] = self._bgp_global_list_to_dict(tmp_data.get(k)) + return tmp_data + + def handle_deprecates(self, want, is_nbr=False): + """ + Handles deprecated values post rewrite + aggregate_address [dict] - aggregate_addresses [list:dict] + bgp.bestpath [list:dict] - bgp.bestpath_options [dict] + bgp.inject_map [dict] - bgp.inject_map [list:dict] + bgp.listen.(ipv4/v6_with_subnet) [multiple] - bgp.listen.host_with_subnet + bgp.nopeerup_delay [list:dict] - bgp.nopeerup_delay_option [dict] + distributed_list [dict] - distributes [list:dict] + neighbor.address.(tag/ipv4/v6_address) [multiple] - neighbor.address.neighbor_address + neighbor.password [str] - neighbor.password [dict] + neighbor.route_map [dict] - neighbor.route_maps [list:dict] + + Args: + want (_type_): Handle want attributes for deprecated values + is_nbr (bool, optional): activates neighbor part on recursion. Defaults to False. + """ + if not is_nbr: + if want.get("aggregate_address"): + if want.get("aggregate_addresses"): + want["aggregate_addresses"].append(want.pop("aggregate_address")) + else: + want["aggregate_addresses"] = [want.pop("aggregate_address")] + if want.get("bgp"): + _want_bgp = want.get("bgp", {}) + if _want_bgp.get("bestpath"): + bpath = {} + for i in _want_bgp.pop("bestpath"): + bpath = dict_merge(bpath, i) + _want_bgp["bestpath_options"] = bpath + if _want_bgp.get("nopeerup_delay"): + npdelay = {} + for i in _want_bgp.pop("nopeerup_delay"): + npdelay = dict_merge(npdelay, i) + _want_bgp["nopeerup_delay_options"] = npdelay + if _want_bgp.get("inject_map"): + if _want_bgp.get("inject_maps"): + _want_bgp["inject_maps"].append(_want_bgp.pop("inject_map")) + else: + _want_bgp["inject_maps"] = [_want_bgp.pop("inject_map")] + if _want_bgp.get("listen", {}).get("range"): + if _want_bgp.get("listen").get("range").get("ipv4_with_subnet"): + _want_bgp["listen"]["range"]["host_with_subnet"] = _want_bgp["listen"][ + "range" + ].pop("ipv4_with_subnet") + elif _want_bgp.get("listen").get("range").get("ipv6_with_subnet"): + _want_bgp["listen"]["range"]["host_with_subnet"] = _want_bgp["listen"][ + "range" + ].pop("ipv6_with_subnet") + if want.get("distribute_list"): + if want.get("distributes"): + want["distributes"].append(want.pop("distribute_list")) + else: + want["distributes"] = [want.pop("distribute_list")] + if want.get("neighbors"): + _want_nbrs = want.get("neighbors", {}) + for nbr in _want_nbrs: + nbr = self.handle_deprecates(nbr, is_nbr=True) + else: + if want.get("address"): + want["neighbor_address"] = want.pop("address") + if want.get("tag"): + want["neighbor_address"] = want.pop("tag") + if want.get("ipv6_adddress"): + want["neighbor_address"] = want.pop("ipv6_adddress") + if want.get("route_map"): + if want.get("route_maps"): + want["route_maps"].append(want.pop("route_map")) + else: + want["route_maps"] = [want.pop("route_map")] + if want.get("password"): + want["password_options"] = {"pass_key": want.pop("password")} diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/hostname/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/hostname/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/hostname/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/hostname/hostname.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/hostname/hostname.py new file mode 100644 index 000000000..88d83fe94 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/hostname/hostname.py @@ -0,0 +1,75 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios_hostname config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.hostname import ( + HostnameTemplate, +) + + +class Hostname(ResourceModule): + """ + The ios_hostname config class + """ + + def __init__(self, module): + super(Hostname, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="hostname", + tmplt=HostnameTemplate(), + ) + self.parsers = ["hostname"] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + wantd = self.want + haved = self.have + + if self.state == "deleted": + wantd = {} + + self._compare(want=wantd, have=haved) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Hostname network resource. + """ + self.compare(parsers=self.parsers, want=want, have=have) diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/interfaces/interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/interfaces/interfaces.py new file mode 100644 index 000000000..2f9e34221 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/interfaces/interfaces.py @@ -0,0 +1,134 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios_interfaces config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.interfaces import ( + InterfacesTemplate, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + normalize_interface, +) + + +class Interfaces(ResourceModule): + """ + The ios_interfaces config class + """ + + def __init__(self, module): + super(Interfaces, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="interfaces", + tmplt=InterfacesTemplate(), + ) + self.parsers = ["description", "speed", "mtu", "duplex", "template"] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + wantd = {entry["name"]: entry for entry in self.want} + haved = {entry["name"]: entry for entry in self.have} + + for each in wantd, haved: + self.normalize_interface_names(each) + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, empty out wantd and set haved to wantd + if self.state in ["deleted", "purged"]: + haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} + wantd = {} + + # remove superfluous config for overridden and deleted + if self.state in ["overridden", "deleted"]: + for k, have in iteritems(haved): + if k not in wantd: + self._compare(want={}, have=have) + + if self.state == "purged": + for k, have in iteritems(haved): + self.purge(have) + else: + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Interfaces network resource. + """ + begin = len(self.commands) + self.compare(parsers=self.parsers, want=want, have=have) + if want.get("enabled") != have.get("enabled"): + if want.get("enabled"): + self.addcmd(want, "enabled", True) + else: + if want: + self.addcmd(want, "enabled", False) + elif have.get("enabled"): + # handles deleted as want be blank and only + # negates if no shutdown + self.addcmd(have, "enabled", False) + if want.get("mode") != have.get("mode"): + if want.get("mode") == "layer3": + self.addcmd(want, "mode", True) + else: + if want: + self.addcmd(want, "mode", False) + elif have.get("mode"): # can oly have layer2 as switchport no show cli + # handles deleted as want be blank and only + self.addcmd(have, "mode", False) + if len(self.commands) != begin: + self.commands.insert(begin, self._tmplt.render(want or have, "interface", False)) + + def purge(self, have): + """Handle operation for purged state""" + self.commands.append(self._tmplt.render(have, "interface", True)) + + def normalize_interface_names(self, param): + if param: + for _k, val in iteritems(param): + val["name"] = normalize_interface(val["name"]) + return param diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l2_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l2_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l2_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l2_interfaces/l2_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l2_interfaces/l2_interfaces.py new file mode 100644 index 000000000..653b837eb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l2_interfaces/l2_interfaces.py @@ -0,0 +1,155 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios_l2_interfaces config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.l2_interfaces import ( + L2_interfacesTemplate, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + normalize_interface, + vlan_list_to_range, + vlan_range_to_list, +) + + +class L2_interfaces(ResourceModule): + """ + The ios_l2_interfaces config class + """ + + def __init__(self, module): + super(L2_interfaces, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="l2_interfaces", + tmplt=L2_interfacesTemplate(), + ) + self.parsers = [ + "access.vlan", + "access.vlan_name", + "voice.vlan", + "voice.vlan_tag", + "voice.vlan_name", + "mode", + "trunk.encapsulation", + "trunk.native_vlan", + ] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + wantd = {entry["name"]: entry for entry in self.want} + haved = {entry["name"]: entry for entry in self.have} + + for each in wantd, haved: + self.process_list_attrs(each) + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, empty out wantd and set haved to wantd + if self.state == "deleted": + haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} + wantd = {} + + # remove superfluous config for overridden and deleted + if self.state in ["overridden", "deleted"]: + for k, have in iteritems(haved): + if k not in wantd: + self._compare(want={}, have=have) + + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the L2_interfaces network resource. + """ + begin = len(self.commands) + self.compare(parsers=self.parsers, want=want, have=have) + self.compare_list(want, have) + if len(self.commands) != begin: + self.commands.insert(begin, self._tmplt.render(want or have, "name", False)) + + def compare_list(self, want, have): + for vlan in ["allowed_vlans", "pruning_vlans"]: + cmd_always = list( + set(want.get("trunk", {}).get(vlan, [])) - set(have.get("trunk", {}).get(vlan, [])), + ) # find vlans to create wrt have + if self.state != "merged": + rem_vlan = [] + for vl_no in have.get("trunk", {}).get(vlan, []): + if vl_no not in cmd_always and vl_no not in want.get("trunk", {}).get(vlan, []): + rem_vlan.append(vl_no) + if ( + not want.get("trunk", {}).get(vlan, []) and rem_vlan + ): # remove vlan all as want blank + self.commands.append( + "no switchport trunk {0} vlan".format(vlan.split("_", maxsplit=1)[0]), + ) + elif rem_vlan: # remove excess vlans for replaced overridden with vlan entries + self.commands.append( + "switchport trunk {0} vlan remove {1}".format( + vlan.split("_", maxsplit=1)[0], + vlan_list_to_range(sorted(rem_vlan)), + ), + ) + if self.state != "deleted" and cmd_always: # add configuration needed + add = "add " if have.get("trunk", {}).get(vlan, []) else "" + self.commands.append( + "switchport trunk {0} vlan {1}{2}".format( + vlan.split("_", maxsplit=1)[0], + add, + vlan_list_to_range(sorted(cmd_always)), + ), + ) + + def process_list_attrs(self, param): + if param: + for _k, val in iteritems(param): + val["name"] = normalize_interface(val["name"]) + if val.get("trunk"): + for vlan in ["allowed_vlans", "pruning_vlans"]: + if val.get("trunk").get(vlan): + val["trunk"][vlan] = vlan_range_to_list(val.get("trunk").get(vlan)) diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/l3_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/l3_interfaces.py new file mode 100644 index 000000000..b4b874d6e --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/l3_interfaces.py @@ -0,0 +1,215 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios_l3_interfaces class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.l3_interfaces import ( + L3_interfacesTemplate, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + normalize_interface, + validate_ipv6, + validate_n_expand_ipv4, +) + + +class L3_interfaces(ResourceModule): + """ + The ios_l3_interfaces class + """ + + def __init__(self, module): + super(L3_interfaces, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="l3_interfaces", + tmplt=L3_interfacesTemplate(), + ) + self.parsers = [ + "ipv4.address", + "ipv4.pool", + "ipv4.dhcp", + "ipv6.address", + "ipv6.autoconfig", + "ipv6.dhcp", + ] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + if self.want: + wantd = {} + for each in self.want: + wantd.update({each["name"]: each}) + else: + wantd = {} + if self.have: + haved = {} + for each in self.have: + haved.update({each["name"]: each}) + else: + haved = {} + + for each in wantd, haved: + self.list_to_dict(each) + + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + if self.state == "deleted": + haved = {k: v for k, v in haved.items() if k in wantd or not wantd} + wantd = {} + + # remove superfluous config + if self.state in ["overridden", "deleted"]: + for k, have in haved.items(): + if k not in wantd: + self._compare(want={}, have=have) + + for k, want in wantd.items(): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + begin = len(self.commands) + self._compare_lists(want=want, have=have) + if len(self.commands) != begin: + self.commands.insert(begin, self._tmplt.render(want or have, "name", False)) + + def _compare_lists(self, want, have): + for afi in ("ipv4", "ipv6"): + wacls = want.pop(afi, {}) + hacls = have.pop(afi, {}) + + for key, entry in wacls.items(): + if entry.get("secondary", False) is True: + continue + # entry is set as primary + hacl = hacls.get(key, {}) + if hacl.get("secondary", False) is True: + hacl = {} + self.validate_ips(afi, want=entry, have=hacl) + + if hacl: + hacls.pop(key, {}) + + self.compare( + parsers=self.parsers, + want={afi: entry}, + have={afi: hacl}, + ) + + for key, entry in wacls.items(): + if entry.get("secondary", False) is False: + continue + # entry is set as secondary + hacl = hacls.get(key, {}) + if hacl.get("secondary", False) is False: + # hacl is set as primary, if wacls has no other primary entry we must keep + # this entry as primary (so we'll compare entry to hacl and not + # generate commands) + if list(filter(lambda w: w.get("secondary", False) is False, wacls.values())): + # another primary is in wacls + hacl = {} + self.validate_ips(afi, want=entry, have=hacl) + + if hacl: + hacls.pop(key, {}) + + self.compare( + parsers=self.parsers, + want={afi: entry}, + have={afi: hacl}, + ) + + # remove remaining items in have for replaced + # these can be subnets that are no longer used + # or secondaries that have moved to primary + # or primary that has moved to secondary + for key, entry in hacls.items(): + self.validate_ips(afi, have=entry) + self.compare(parsers=self.parsers, want={}, have={afi: entry}) + + def validate_ips(self, afi, want=None, have=None): + if afi == "ipv4" and want: + v4_addr = validate_n_expand_ipv4(self._module, want) if want.get("address") else {} + if v4_addr: + want["address"] = v4_addr + elif afi == "ipv6" and want: + if want.get("address"): + validate_ipv6(want["address"], self._module) + + if afi == "ipv4" and have: + v4_addr_h = validate_n_expand_ipv4(self._module, have) if have.get("address") else {} + if v4_addr_h: + have["address"] = v4_addr_h + elif afi == "ipv6" and have: + if have.get("address"): + validate_ipv6(have["address"], self._module) + + def list_to_dict(self, param): + if param: + for _k, val in iteritems(param): + val["name"] = normalize_interface(val["name"]) + if "ipv4" in val: + temp = {} + for each in val["ipv4"]: + if each.get("address") and each.get("address") != "dhcp": + temp.update({each["address"]: each}) + elif each.get("address") == "dhcp": + # deprecated attribute + temp.update( + { + "dhcp": { + "dhcp": { + "client_id": each.get("dhcp_client"), + "hostname": each.get("dhcp_hostname"), + }, + }, + }, + ) + if not each.get("address"): + temp.update({list(each.keys())[0]: each}) + val["ipv4"] = temp + if "ipv6" in val: + temp = {} + for each in val["ipv6"]: + if each.get("address"): + each["address"] = each["address"].lower() + temp.update({each["address"]: each}) + if not each.get("address"): + temp.update({list(each.keys())[0]: each}) + val["ipv6"] = temp diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp/lacp.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp/lacp.py new file mode 100644 index 000000000..0420c347b --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp/lacp.py @@ -0,0 +1,215 @@ +# +# -*- 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) +""" +The ios_lacp class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import dict_to_set + + +class Lacp(ConfigBase): + """ + The ios_lacp class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["lacp"] + + def __init__(self, module): + super(Lacp, self).__init__(module) + + def get_lacp_facts(self, data=None): + """Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, + self.gather_network_resources, + data=data, + ) + lacp_facts = facts["ansible_network_resources"].get("lacp") + if not lacp_facts: + return dict() + + return lacp_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + commands = list() + warnings = list() + + if self.state in self.ACTION_STATES: + existing_lacp_facts = self.get_lacp_facts() + else: + existing_lacp_facts = dict() + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_lacp_facts)) + if commands and self.state in self.ACTION_STATES: + if not self._module.check_mode: + self._connection.edit_config(commands) + result["changed"] = True + if self.state in self.ACTION_STATES: + result["commands"] = commands + + if self.state in self.ACTION_STATES or self.state == "gathered": + changed_lacp_facts = self.get_lacp_facts() + elif self.state == "rendered": + result["rendered"] = commands + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed", + ) + result["parsed"] = self.get_lacp_facts(data=running_config) + else: + changed_lacp_facts = dict() + + if self.state in self.ACTION_STATES: + result["before"] = existing_lacp_facts + if result["changed"]: + result["after"] = changed_lacp_facts + elif self.state == "gathered": + result["gathered"] = changed_lacp_facts + result["warnings"] = warnings + + return result + + def set_config(self, existing_lacp_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_lacp_facts + resp = self.set_state(want, have) + + return to_list(resp) + + def set_state(self, want, have): + """Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + if self.state in ("merged", "replaced", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format(self.state), + ) + + if self.state == "deleted": + commands = self._state_deleted(want, have) + elif self.state in ("merged", "rendered"): + commands = self._state_merged(want, have) + elif self.state == "replaced": + commands = self._state_replaced(want, have) + + return commands + + def _state_replaced(self, want, have): + """The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + commands.extend(self._set_config(want, have)) + + return commands + + def _state_merged(self, want, have): + """The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + commands.extend(self._set_config(want, have)) + + return commands + + def _state_deleted(self, want, have): + """The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + + if want: + commands.extend(self._clear_config(have)) + else: + commands.extend(self._clear_config(have)) + + return commands + + def _remove_command_from_config_list(self, cmd, commands): + commands.append("no %s" % cmd) + return commands + + def _add_command_to_config_list(self, cmd, commands): + if cmd not in commands: + commands.append(cmd) + + def _set_config(self, want, have): + # Set the interface config based on the want and have config + commands = [] + + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + diff = want_dict - have_dict + + if diff: + cmd = "lacp system-priority {0}".format(want.get("system").get("priority")) + self._add_command_to_config_list(cmd, commands) + + return commands + + def _clear_config(self, have): + # Delete the interface config based on the want and have config + commands = [] + + if have.get("system").get("priority") and have.get("system").get("priority") != 32768: + cmd = "lacp system-priority" + self._remove_command_from_config_list(cmd, commands) + + return commands diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp_interfaces/lacp_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 000000000..09427b3d6 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lacp_interfaces/lacp_interfaces.py @@ -0,0 +1,297 @@ +# +# -*- 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) +""" +The ios_lacp_interfaces class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + add_command_to_config_list, + dict_to_set, + filter_dict_having_none_value, + normalize_interface, + remove_command_from_config_list, + remove_duplicate_interface, +) + + +class Lacp_Interfaces(ConfigBase): + """ + The ios_lacp_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["lacp_interfaces"] + + def __init__(self, module): + super(Lacp_Interfaces, self).__init__(module) + + def get_lacp_interfaces_facts(self, data=None): + """Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, + self.gather_network_resources, + data=data, + ) + lacp_interfaces_facts = facts["ansible_network_resources"].get("lacp_interfaces") + + if not lacp_interfaces_facts: + return [] + return lacp_interfaces_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + commands = list() + warnings = list() + + if self.state in self.ACTION_STATES: + existing_lacp_interfaces_facts = self.get_lacp_interfaces_facts() + else: + existing_lacp_interfaces_facts = [] + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_lacp_interfaces_facts)) + if commands and self.state in self.ACTION_STATES: + if not self._module.check_mode: + self._connection.edit_config(commands) + result["changed"] = True + + if self.state in self.ACTION_STATES: + result["commands"] = commands + + if self.state in self.ACTION_STATES or self.state == "gathered": + changed_lacp_interfaces_facts = self.get_lacp_interfaces_facts() + elif self.state == "rendered": + result["rendered"] = commands + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed", + ) + result["parsed"] = self.get_lacp_interfaces_facts(data=running_config) + else: + changed_lacp_interfaces_facts = [] + + if self.state in self.ACTION_STATES: + result["before"] = existing_lacp_interfaces_facts + if result["changed"]: + result["after"] = changed_lacp_interfaces_facts + elif self.state == "gathered": + result["gathered"] = changed_lacp_interfaces_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_lacp_interfaces_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + config = self._module.params.get("config") + want = [] + if config: + for each in config: + each.update({"name": normalize_interface(each["name"])}) + want.append(each) + have = existing_lacp_interfaces_facts + resp = self.set_state(want, have) + + return to_list(resp) + + def set_state(self, want, have): + """Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + if self.state in ("overridden", "merged", "replaced", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format(self.state), + ) + + if self.state == "overridden": + commands = self._state_overridden(want, have) + elif self.state == "deleted": + commands = self._state_deleted(want, have) + elif self.state in ("merged", "rendered"): + commands = self._state_merged(want, have) + elif self.state == "replaced": + commands = self._state_replaced(want, have) + + return commands + + def _state_replaced(self, want, have): + """The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_overridden(self, want, have): + """The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + for each in have: + for interface in want: + if each["name"] == interface["name"]: + break + else: + # We didn't find a matching desired state, which means we can + # pretend we received an empty desired state. + interface = dict(name=each["name"]) + commands.extend(self._clear_config(interface, each)) + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_merged(self, want, have): + """The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + for interface in want: + for each in have: + if interface["name"] == each["name"]: + break + else: + commands.extend(self._set_config(interface, dict())) + continue + commands.extend(self._set_config(interface, each)) + + return commands + + def _state_deleted(self, want, have): + """The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + + if want: + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + continue + interface = dict(name=interface["name"]) + commands.extend(self._clear_config(interface, each)) + else: + for each in have: + commands.extend(self._clear_config(dict(), each)) + + return commands + + def _set_config(self, want, have): + # Set the interface config based on the want and have config + commands = [] + interface = "interface " + want["name"] + + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + diff = want_dict - have_dict + + if diff: + port_priotity = dict(diff).get("port_priority") + max_bundle = dict(diff).get("max_bundle") + fast_switchover = dict(diff).get("fast_switchover") + if port_priotity: + cmd = "lacp port-priority {0}".format(port_priotity) + add_command_to_config_list(interface, cmd, commands) + if max_bundle: + cmd = "lacp max-bundle {0}".format(max_bundle) + add_command_to_config_list(interface, cmd, commands) + if fast_switchover: + cmd = "lacp fast-switchover" + add_command_to_config_list(interface, cmd, commands) + + return commands + + def _clear_config(self, want, have): + # Delete the interface config based on the want and have config + commands = [] + if want.get("name"): + interface = "interface " + want["name"] + else: + interface = "interface " + have["name"] + + if have.get("port_priority") and have.get("port_priority") != want.get("port_priority"): + cmd = "lacp port-priority" + remove_command_from_config_list(interface, cmd, commands) + if have.get("max_bundle") and have.get("max_bundle") != want.get("max_bundle"): + cmd = "lacp max-bundle" + remove_command_from_config_list(interface, cmd, commands) + if have.get("fast_switchover"): + cmd = "lacp fast-switchover" + remove_command_from_config_list(interface, cmd, commands) + + return commands diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lag_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lag_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lag_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lag_interfaces/lag_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lag_interfaces/lag_interfaces.py new file mode 100644 index 000000000..10c7cbf96 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lag_interfaces/lag_interfaces.py @@ -0,0 +1,125 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios_lag_interfaces config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.lag_interfaces import ( + Lag_interfacesTemplate, +) + + +class Lag_interfaces(ResourceModule): + """ + The ios_lag_interfaces config class + """ + + def __init__(self, module): + super(Lag_interfaces, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="lag_interfaces", + tmplt=Lag_interfacesTemplate(), + ) + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + wantd = self.list_to_dict(self.want) + haved = self.list_to_dict(self.have) + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, empty out wantd and set haved to wantd + if self.state == "deleted": + haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} + wantd = {} + + # remove superfluous config for overridden and deleted + if self.state in ["overridden", "deleted"]: + for k, have in iteritems(haved): + if k not in wantd: + self._compare(wants={}, haveing=have) + + for k, want in iteritems(wantd): + self._compare(wants=want, haveing=haved.pop(k, {})) + + def _compare(self, wants, haveing): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Lag_interfaces network resource. + """ + + for key, entry in wants.items(): + begin = len(self.commands) + if entry != haveing.pop(key, {}): + self.addcmd(entry, "channel", False) + if len(self.commands) != begin: + self.commands.insert(begin, self._tmplt.render(entry, "member", False)) + + # remove remaining items in have for replaced + for key, entry in haveing.items(): + if key: + begin = len(self.commands) + self.addcmd(entry, "channel", True) + if len(self.commands) != begin: + self.commands.insert(begin, self._tmplt.render(entry, "member", False)) + + def extract_channel_num(self, channel): + try: + return channel.lower().split("port-channel")[1], False + except IndexError: + return channel.lower().split("port-channel")[0], True + + def list_to_dict(self, params): + channels = {} + for ethChannels in params: + tmp = {} + for member in ethChannels.get("members", {}): + member["channel"] = self.extract_channel_num(ethChannels.get("name"))[0] + tmp[member.get("member")] = member + update = self.extract_channel_num(ethChannels.get("name"))[1] + if update: + ethChannels["name"] = "Port-channel" + ethChannels.get("name") + channels[ethChannels.get("name")] = tmp + return channels diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_global/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_global/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_global/lldp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_global/lldp_global.py new file mode 100644 index 000000000..c575bc7b0 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_global/lldp_global.py @@ -0,0 +1,269 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios_lldp_global class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + dict_to_set, + filter_dict_having_none_value, +) + + +class Lldp_global(ConfigBase): + """ + The ios_lldp_global class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["lldp_global"] + + tlv_select_params = { + "four_wire_power_management": "4-wire-power-management", + "mac_phy_cfg": "mac-phy-cfg", + "management_address": "management-address", + "port_description": "port-description", + "port_vlan": "port-vlan", + "power_management": "power-management", + "system_capabilities": "system-capabilities", + "system_description": "system-description", + "system_name": "system-name", + } + + def __init__(self, module): + super(Lldp_global, self).__init__(module) + + def get_lldp_global_facts(self, data=None): + """Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, + self.gather_network_resources, + data=data, + ) + lldp_global_facts = facts["ansible_network_resources"].get("lldp_global") + if not lldp_global_facts: + return {} + + return lldp_global_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + commands = list() + warnings = list() + + if self.state in self.ACTION_STATES: + existing_lldp_global_facts = self.get_lldp_global_facts() + else: + existing_lldp_global_facts = dict() + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_lldp_global_facts)) + if commands and self.state in self.ACTION_STATES: + if not self._module.check_mode: + self._connection.edit_config(commands) + result["changed"] = True + if self.state in self.ACTION_STATES: + result["commands"] = commands + + if self.state in self.ACTION_STATES or self.state == "gathered": + changed_lldp_global_facts = self.get_lldp_global_facts() + elif self.state == "rendered": + result["rendered"] = commands + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed", + ) + result["parsed"] = self.get_lldp_global_facts(data=running_config) + else: + changed_lldp_global_facts = dict() + + if self.state in self.ACTION_STATES: + result["before"] = existing_lldp_global_facts + if result["changed"]: + result["after"] = changed_lldp_global_facts + elif self.state == "gathered": + result["gathered"] = changed_lldp_global_facts + result["warnings"] = warnings + + return result + + def set_config(self, existing_lldp_global_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_lldp_global_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + if self.state in ("merged", "replaced", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format(self.state), + ) + + if self.state == "overridden": + commands = self._state_overridden(want, have) + elif self.state == "deleted": + commands = self._state_deleted(want, have) + elif self.state in ("merged", "rendered"): + commands = self._state_merged(want, have) + elif self.state == "replaced": + commands = self._state_replaced(want, have) + + return commands + + def _state_replaced(self, want, have): + """The command generator when state is replaced + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + have_dict = filter_dict_having_none_value(want, have) + commands.extend(self._clear_config(have_dict)) + commands.extend(self._set_config(want, have)) + + return commands + + def _state_merged(self, want, have): + """The command generator when state is merged + + :param want: the additive configuration as a dictionary + :param obj_in_have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + commands.extend(self._set_config(want, have)) + + return commands + + def _state_deleted(self, want, have): + """The command generator when state is deleted + + :param want: the objects from which the configuration should be removed + :param obj_in_have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + + commands.extend(self._clear_config(have)) + + return commands + + def _remove_command_from_config_list(self, cmd, commands): + if cmd not in commands: + commands.append("no %s" % cmd) + + def add_command_to_config_list(self, cmd, commands): + if cmd not in commands: + commands.append(cmd) + + def _set_config(self, want, have): + # Set the lldp global config based on the want and have config + commands = [] + + # Get the diff b/w want and have + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + diff = want_dict - have_dict + + if diff: + diff = dict(diff) + holdtime = diff.get("holdtime") + enabled = diff.get("enabled") + timer = diff.get("timer") + reinit = diff.get("reinit") + tlv_select = diff.get("tlv_select") + + if holdtime: + cmd = "lldp holdtime {0}".format(holdtime) + self.add_command_to_config_list(cmd, commands) + if enabled: + cmd = "lldp run" + self.add_command_to_config_list(cmd, commands) + if timer: + cmd = "lldp timer {0}".format(timer) + self.add_command_to_config_list(cmd, commands) + if reinit: + cmd = "lldp reinit {0}".format(reinit) + self.add_command_to_config_list(cmd, commands) + if tlv_select: + tlv_selec_dict = dict(tlv_select) + for k, v in iteritems(self.tlv_select_params): + if k in tlv_selec_dict and tlv_selec_dict[k]: + cmd = "lldp tlv-select {0}".format(v) + self.add_command_to_config_list(cmd, commands) + + return commands + + def _clear_config(self, have): + # Delete the lldp global config based on the want and have config + commands = [] + + if have.get("holdtime"): + cmd = "lldp holdtime" + self._remove_command_from_config_list(cmd, commands) + if have.get("enabled"): + cmd = "lldp run" + self._remove_command_from_config_list(cmd, commands) + if have.get("timer"): + cmd = "lldp timer" + self._remove_command_from_config_list(cmd, commands) + if have.get("reinit"): + cmd = "lldp reinit" + self._remove_command_from_config_list(cmd, commands) + + return commands diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_interfaces/lldp_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_interfaces/lldp_interfaces.py new file mode 100644 index 000000000..20d1f01b8 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/lldp_interfaces/lldp_interfaces.py @@ -0,0 +1,315 @@ +# +# -*- 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) +""" +The ios_lldp_interfaces class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + add_command_to_config_list, + dict_to_set, + filter_dict_having_none_value, + normalize_interface, + remove_command_from_config_list, + remove_duplicate_interface, +) + + +class Lldp_Interfaces(ConfigBase): + """ + The ios_lldp_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["lldp_interfaces"] + + def __init__(self, module): + super(Lldp_Interfaces, self).__init__(module) + + def get_lldp_interfaces_facts(self, data=None): + """Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, + self.gather_network_resources, + data=data, + ) + lldp_interfaces_facts = facts["ansible_network_resources"].get("lldp_interfaces") + if not lldp_interfaces_facts: + return [] + return lldp_interfaces_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + commands = list() + warnings = list() + + if self.state in self.ACTION_STATES: + existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts() + else: + existing_lldp_interfaces_facts = [] + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_lldp_interfaces_facts)) + if commands and self.state in self.ACTION_STATES: + if not self._module.check_mode: + self._connection.edit_config(commands) + result["changed"] = True + if self.state in self.ACTION_STATES: + result["commands"] = commands + + if self.state in self.ACTION_STATES or self.state == "gathered": + changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts() + elif self.state == "rendered": + result["rendered"] = commands + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed", + ) + result["parsed"] = self.get_lldp_interfaces_facts(data=running_config) + else: + changed_lldp_interfaces_facts = [] + + if self.state in self.ACTION_STATES: + result["before"] = existing_lldp_interfaces_facts + if result["changed"]: + result["after"] = changed_lldp_interfaces_facts + elif self.state == "gathered": + result["gathered"] = changed_lldp_interfaces_facts + + result["warnings"] = warnings + + return result + + def set_config(self, existing_lldp_interfaces_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + config = self._module.params.get("config") + want = [] + if config: + for each in config: + each.update({"name": normalize_interface(each["name"])}) + want.append(each) + have = existing_lldp_interfaces_facts + resp = self.set_state(want, have) + + return to_list(resp) + + def set_state(self, want, have): + """Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + + if self.state in ("overridden", "merged", "replaced", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format(self.state), + ) + + if self.state == "overridden": + commands = self._state_overridden(want, have) + elif self.state == "deleted": + commands = self._state_deleted(want, have) + elif self.state in ("merged", "rendered"): + commands = self._state_merged(want, have) + elif self.state == "replaced": + commands = self._state_replaced(want, have) + + return commands + + def _state_replaced(self, want, have): + """The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_overridden(self, want, have): + """The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + for each in have: + for interface in want: + if each["name"] == interface["name"]: + break + else: + # We didn't find a matching desired state, which means we can + # pretend we received an empty desired state. + interface = dict(name=each["name"]) + commands.extend(self._clear_config(interface, each)) + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_merged(self, want, have): + """The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + for interface in want: + for each in have: + if interface["name"] == each["name"]: + break + else: + if self.state == "rendered": + commands.extend(self._set_config(interface, dict())) + continue + commands.extend(self._set_config(interface, each)) + + return commands + + def _state_deleted(self, want, have): + """The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + + if want: + for interface in want: + for each in have: + if each["name"] == interface["name"]: + break + else: + continue + interface = dict(name=interface["name"]) + commands.extend(self._clear_config(interface, each)) + else: + for each in have: + commands.extend(self._clear_config(dict(), each)) + + return commands + + def _set_config(self, want, have): + # Set the interface config based on the want and have config + commands = [] + + interface = "interface " + want["name"] + # Get the diff b/w want and have + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + diff = want_dict - have_dict + + if diff: + diff = dict(diff) + receive = diff.get("receive") + transmit = diff.get("transmit") + med_tlv_select = diff.get("med_tlv_select") + tlv_select = diff.get("tlv_select") + if receive: + cmd = "lldp receive" + add_command_to_config_list(interface, cmd, commands) + elif receive is False: + cmd = "no lldp receive" + add_command_to_config_list(interface, cmd, commands) + if transmit: + cmd = "lldp transmit" + add_command_to_config_list(interface, cmd, commands) + elif transmit is False: + cmd = "no lldp transmit" + add_command_to_config_list(interface, cmd, commands) + + if med_tlv_select: + med_tlv_select = dict(med_tlv_select) + if med_tlv_select.get("inventory_management"): + add_command_to_config_list( + interface, + "lldp med-tlv-select inventory-management", + commands, + ) + if tlv_select: + tlv_select = dict(tlv_select) + if tlv_select.get("power_management"): + add_command_to_config_list( + interface, + "lldp tlv-select power-management", + commands, + ) + + return commands + + def _clear_config(self, want, have): + # Delete the interface config based on the want and have config + commands = [] + if want.get("name"): + interface = "interface " + want["name"] + else: + interface = "interface " + have["name"] + + if have.get("receive") and have.get("receive") != want.get("receive"): + cmd = "lldp receive" + remove_command_from_config_list(interface, cmd, commands) + if have.get("transmit") and have.get("transmit") != want.get("transmit"): + cmd = "lldp transmit" + remove_command_from_config_list(interface, cmd, commands) + + return commands diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/logging_global/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/logging_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/logging_global/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/logging_global/logging_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/logging_global/logging_global.py new file mode 100644 index 000000000..4eb442a0c --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/logging_global/logging_global.py @@ -0,0 +1,170 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios_logging_global config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" +from copy import deepcopy + +from ansible.module_utils._text import to_text +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.logging_global import ( + Logging_globalTemplate, +) + + +class Logging_global(ResourceModule): + """ + The ios_logging_global config class + """ + + def __init__(self, module): + super(Logging_global, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="logging_global", + tmplt=Logging_globalTemplate(), + ) + self.parsers = [ + "buffered", + "buginf", + "cns_events", + "console", + "count", + "delimiter", + "dmvpn", + "esm", + "exception", + "facility", + "history.size", + "history.severity", + "monitor", + "logging_on", + "origin_id", + "persistent", + "policy_firewall", + "queue_limit", + "rate_limit", + "reload", + "server_arp", + "trap", + "userinfo", + ] + self.list_parsers = ["hosts", "filter", "source_interface"] + self.complex_parsers = ["message_counter", "discriminator", "snmp_trap"] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + + wantd = self.list_to_dict(self.want) + haved = self.list_to_dict(self.have) + + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + if self.state == "deleted": + wantd = {} + + self._compare(want=wantd, have=haved) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Logging_global network resource. + """ + self.compare(parsers=self.parsers, want=want, have=have) + self._compare_lists_attrs(want, have) + self._compare_complex_attrs(want, have) + + def _compare_lists_attrs(self, want, have): + """Compare list of dict""" + for _par in self.list_parsers: + i_want = want.get(_par, {}) + i_have = have.get(_par, {}) + for key, wanting in iteritems(i_want): + _parser = _par + if wanting.get("transport") and _parser == "hosts": + _parser = "hosts.transport" + haveing = i_have.pop(key, {}) + if wanting != haveing: + if haveing and self.state in ["overridden", "replaced"]: + self.addcmd(haveing, _parser, negate=True) + self.addcmd(wanting, _parser) + for key, haveing in iteritems(i_have): + _parser = _par + if haveing.get("transport") and _parser == "hosts": + _parser = "hosts.transport" + self.addcmd(haveing, _parser, negate=True) + + def _compare_complex_attrs(self, want, have): + """Compare dict of list""" + for _par in self.complex_parsers: + i_want = want.get(_par, {}) + i_have = have.get(_par, {}) + for key, wanting in iteritems(i_want): + haveing = i_have.pop(key, {}) + if wanting != haveing: + if haveing and self.state in ["overridden", "replaced"]: + self.addcmd(haveing, _par, negate=True) + self.addcmd(wanting, _par) + for key, haveing in iteritems(i_have): + self.addcmd(haveing, _par, negate=True) + + def list_to_dict(self, data): + """Convert all list of dicts to dicts of dicts""" + p_key = {"filter": "url", "hosts": "host", "source_interface": "interface"} + if data.get("hosts"): # handle aliased hostname as host + for v in data.get("hosts"): + if v.get("hostname"): + v["host"] = v.pop("hostname") + list_el = ["message_counter", "discriminator", "snmp_trap"] + tmp_data = deepcopy(data) + for k, _v in p_key.items(): + if tmp_data.get(k): + tmp_data[k] = { + str(i[p_key[k]]) if i.get(p_key[k]) else str(i.get("ipv6")): i + for i in tmp_data[k] + } + for el in list_el: + if tmp_data.get(el): + tmp_data[el] = {self.trim_whitespace(el + i): {el: i} for i in tmp_data[el]} + return tmp_data + + def trim_whitespace(self, word): + return to_text(word).strip() diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ntp_global/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ntp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ntp_global/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ntp_global/ntp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ntp_global/ntp_global.py new file mode 100644 index 000000000..ace378e08 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ntp_global/ntp_global.py @@ -0,0 +1,171 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios_ntp_global config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from copy import deepcopy + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ntp_global import ( + Ntp_globalTemplate, +) + + +class Ntp_global(ResourceModule): + """ + The ios_ntp_global config class + """ + + def __init__(self, module): + super(Ntp_global, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="ntp_global", + tmplt=Ntp_globalTemplate(), + ) + self.parsers = [ + "allow.control.rate_limit", + "allow.private", + "authenticate", + "broadcast_delay", + "clock_period", + "logging", + "master.enabled", + "master.stratum", + "max_associations", + "max_distance", + "min_distance", + "orphan", + "panic_update", + "passive", + "source", + "update_calendar", + ] + self.complex_parser = [ + "peer", + "query_only", + "serve", + "serve_only", + "authentication_keys", + "peers", + "servers", + "trusted_keys", + ] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + wantd = self._ntp_list_to_dict(self.want) + haved = self._ntp_list_to_dict(self.have) + + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + if self.state == "deleted": + wantd = {} + + self._compare(want=wantd, have=haved) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Ntp_global network resource. + """ + self.compare(parsers=self.parsers, want=want, have=have) + self._compare_access_groups(want, have) + self._compare_lists_attrs(want, have) + + def _compare_lists_attrs(self, want, have): + """Compare list of dict""" + for _parser in self.complex_parser[4:8]: # other list attrs + i_want = want.get(_parser, {}) + i_have = have.get(_parser, {}) + for key, wanting in iteritems(i_want): + haveing = i_have.pop(key, {}) + if wanting != haveing: + if ( + _parser != "authentication_keys" + and haveing + and self.state in ["overridden", "replaced"] + ): + self.addcmd(haveing, _parser, negate=True) + self.addcmd(wanting, _parser) + for key, haveing in iteritems(i_have): + self.addcmd(haveing, _parser, negate=True) + + def _compare_access_groups(self, want, have): + w = want.get("access_group", {}) + h = have.get("access_group", {}) + for _parser in self.complex_parser[0:4]: # access_group + i_want = w.get(_parser, {}) + i_have = h.get(_parser, {}) + + for key, wanting in iteritems(i_want): + haveing = i_have.pop(key, {}) + if wanting != haveing: + self.addcmd(wanting, _parser) + + for key, haveing in iteritems(i_have): + self.addcmd(haveing, _parser, negate=True) + + def _ntp_list_to_dict(self, data): + """Convert all list of dicts to dicts of dicts""" + p_key = { + "servers": "server", + "peers": "peer", + "authentication_keys": "id", + "peer": "access_list", + "query_only": "access_list", + "serve": "access_list", + "serve_only": "access_list", + "trusted_keys": "range_start", + "access_group": True, + } + tmp_data = deepcopy(data) + for k, _v in p_key.items(): + if k in tmp_data and k != "access_group": + if k in ["servers", "peers"]: + for i in tmp_data[k]: + if i.get("key"): + del i["key"] + tmp_data[k] = {str(i[p_key[k]]): i for i in tmp_data[k]} + elif tmp_data.get("access_group") and k == "access_group": + tmp_data[k] = self._ntp_list_to_dict(tmp_data.get("access_group")) + return tmp_data diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospf_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospf_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospf_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospf_interfaces/ospf_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 000000000..f10c6d871 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospf_interfaces/ospf_interfaces.py @@ -0,0 +1,150 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The cisco.ios_ospf_interfaces config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ospf_interfaces import ( + Ospf_interfacesTemplate, +) + + +class Ospf_interfaces(ResourceModule): + """ + The cisco.ios_ospf_interfaces config class + """ + + def __init__(self, module): + super(Ospf_interfaces, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="ospf_interfaces", + tmplt=Ospf_interfacesTemplate(), + ) + self.parsers = [] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + + # turn all lists of dicts into dicts prior to merge + wantd = self._list_to_dict(self.want, "want") + haved = self._list_to_dict(self.have) + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, empty out wantd and set haved to wantd + if self.state == "deleted": + haved = {k: v for k, v in haved.items() if k in wantd or not wantd} + wantd = {} + + # remove superfluous config for overridden and deleted + if self.state in ["overridden", "deleted"]: + for k, have in haved.items(): + if k not in wantd: + self._compare(want={}, have=have, interface=k) + + for k, want in wantd.items(): + self._compare(want=want, have=haved.pop(k, {}), interface=k) + + def _compare(self, want, have, interface): + begin = len(self.commands) + self._compare_afis(want=want, have=have) + if len(self.commands) != begin: + self.commands.insert(begin, self._tmplt.render({"name": interface}, "name", False)) + + def _compare_afis(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Ospf_interfaces network resource. + """ + + parsers = [ + "name", + "process", + "adjacency", + "authentication", + "bfd", + "cost", + "database_filter", + "dead_interval", + "demand_circuit", + "flood_reduction", + "hello_interval", + "lls", + "manet", + "mtu_ignore", + "multi_area", + "neighbor", + "network", + "prefix_suppression", + "priority", + "resync_timeout", + "retransmit_interval", + "shutdown", + "transmit_delay", + "ttl_security", + ] + + for afi in ("ipv4", "ipv6"): + wacls = want.pop(afi, {}) + hacls = have.pop(afi, {}) + + self.compare(parsers=parsers, want=wacls, have=hacls) + + def _list_to_dict(self, entry, attr_type=None): + if self.state == "deleted" and attr_type == "want": + del_list = {} + for intf in entry: + del_list[intf.get("name")] = {} + return del_list + + list_to_dict = {} + for intf in entry: + if intf.get("address_family"): + list_to_dict[intf.get("name")] = self.process_list_attr(intf) + return list_to_dict + + def process_list_attr(self, add_fam): + item = {} + for ag in add_fam.get("address_family", []): + item[ag.get("afi")] = ag + return item diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv2/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv2/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv2/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv2/ospfv2.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv2/ospfv2.py new file mode 100644 index 000000000..2cbc53e17 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv2/ospfv2.py @@ -0,0 +1,284 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios_ospfv2 class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ospfv2 import ( + Ospfv2Template, +) + + +class Ospfv2(ResourceModule): + """ + The ios_ospfv2 class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["ospfv2"] + + def __init__(self, module): + super(Ospfv2, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="ospfv2", + tmplt=Ospfv2Template(), + ) + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + self.gen_config() + self.run_commands() + return self.result + + def gen_config(self): + """Select the appropriate function based on the state provided + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + if self.want: + wantd = {} + for entry in self.want.get("processes", []): + wantd.update({(entry["process_id"], entry.get("vrf")): entry}) + else: + wantd = {} + if self.have: + haved = {} + for entry in self.have.get("processes", []): + haved.update({(entry["process_id"], entry.get("vrf")): entry}) + else: + haved = {} + + # turn all lists of dicts into dicts prior to merge + for each in wantd, haved: + self.list_to_dict(each) + # if state is merged, merge want onto have + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, limit the have to anything in want + # set want to nothing + if self.state == "deleted": + temp = {} + for k, v in iteritems(haved): + if k in wantd or not wantd: + temp.update({k: v}) + haved = temp + wantd = {} + + # delete processes first so we do run into "more than one" errors + if self.state in ["overridden", "deleted"]: + for k, have in iteritems(haved): + if k not in wantd: + self.addcmd(have, "pid", True) + + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + parsers = [ + "adjacency", + "address_family", + "auto_cost", + "bfd", + "capability", + "compatible", + "default_information", + "default_metric", + "discard_route", + "distance.admin_distance", + "distance.ospf", + "distribute_list.acls", + "distribute_list.prefix", + "distribute_list.route_map", + "domain_id", + "domain_tag", + "event_log", + "help", + "ignore", + "interface_id", + "ispf", + "limit", + "local_rib_criteria", + "log_adjacency_changes", + "max_lsa", + "max_metric", + "maximum_paths", + "mpls.ldp", + "mpls.traffic_eng", + "neighbor", + "network", + "nsf.cisco", + "nsf.ietf", + "passive_interface", + "prefix_suppression", + "priority", + "queue_depth.hello", + "queue_depth.update", + "router_id", + "shutdown", + "summary_address", + "timers.throttle.lsa", + "timers.throttle.spf", + "traffic_share", + "ttl_security", + ] + + if want != have: + self.addcmd(want or have, "pid", False) + self.compare(parsers, want, have) + self._areas_compare(want, have) + if want.get("passive_interfaces"): + self._passive_interfaces_compare(want, have) + + def _areas_compare(self, want, have): + wareas = want.get("areas", {}) + hareas = have.get("areas", {}) + for name, entry in iteritems(wareas): + self._area_compare(want=entry, have=hareas.pop(name, {})) + for name, entry in iteritems(hareas): + self._area_compare(want={}, have=entry) + + def _area_compare(self, want, have): + parsers = [ + "area.authentication", + "area.capability", + "area.default_cost", + "area.nssa", + "area.nssa.translate", + "area.ranges", + "area.sham_link", + "area.stub", + ] + self.compare(parsers=parsers, want=want, have=have) + self._area_compare_filters(want, have) + + def _area_compare_filters(self, wantd, haved): + for name, entry in iteritems(wantd): + h_item = haved.pop(name, {}) + if entry != h_item and name == "filter_list": + filter_list_entry = {} + filter_list_entry["area_id"] = wantd["area_id"] + if h_item: + li_diff = [ + item for item in entry + h_item if item not in entry or item not in h_item + ] + else: + li_diff = entry + filter_list_entry["filter_list"] = li_diff + self.addcmd(filter_list_entry, "area.filter_list", False) + for name, entry in iteritems(haved): + if name == "filter_list": + self.addcmd(entry, "area.filter_list", True) + + def _passive_interfaces_compare(self, want, have): + parsers = ["passive_interfaces"] + h_pi = None + for k, v in iteritems(want["passive_interfaces"]): + h_pi = have.get("passive_interfaces", []) + if h_pi and h_pi.get(k) and h_pi.get(k) != v: + for each in v["name"]: + h_interface_name = h_pi[k].get("name", []) + if each not in h_interface_name: + temp = {"interface": {each: each}, "set_interface": v["set_interface"]} + self.compare( + parsers=parsers, + want={"passive_interfaces": temp}, + have=dict(), + ) + else: + h_interface_name.pop(each) + elif not h_pi: + if k == "interface": + for each in v["name"]: + temp = {"interface": {each: each}, "set_interface": v["set_interface"]} + self.compare( + parsers=parsers, + want={"passive_interfaces": temp}, + have=dict(), + ) + elif k == "default": + self.compare( + parsers=parsers, + want={"passive_interfaces": {"default": True}}, + have=dict(), + ) + else: + h_pi.pop(k) + if (self.state == "replaced" or self.state == "overridden") and h_pi: + if h_pi.get("default") or h_pi.get("interface"): + for k, v in iteritems(h_pi): + if k == "interface": + for each in v["name"]: + temp = { + "interface": {each: each}, + "set_interface": not (v["set_interface"]), + } + self.compare( + parsers=parsers, + want={"passive_interface": temp}, + have=dict(), + ) + elif k == "default": + self.compare( + parsers=parsers, + want=dict(), + have={"passive_interface": {"default": True}}, + ) + + def list_to_dict(self, param): + if param: + for _pid, proc in iteritems(param): + for area in proc.get("areas", []): + ranges = {} + for entry in area.get("ranges", []): + ranges.update({entry["address"]: entry}) + if bool(ranges): + area["ranges"] = ranges + filter_list = {} + for entry in area.get("filter_list", []): + filter_list.update({entry["direction"]: entry}) + if bool(filter_list): + area["filter_list"] = filter_list + temp = {} + for entry in proc.get("areas", []): + temp.update({entry["area_id"]: entry}) + proc["areas"] = temp + if proc.get("distribute_list"): + if "acls" in proc.get("distribute_list"): + temp = {} + for entry in proc["distribute_list"].get("acls", []): + temp.update({entry["name"]: entry}) + proc["distribute_list"]["acls"] = temp + if proc.get("passive_interfaces") and proc["passive_interfaces"].get("interface"): + temp = {} + for entry in proc["passive_interfaces"]["interface"].get("name", []): + temp.update({entry: entry}) + proc["passive_interfaces"]["interface"]["name"] = temp diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv3/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv3/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv3/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv3/ospfv3.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv3/ospfv3.py new file mode 100644 index 000000000..7b2464260 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ospfv3/ospfv3.py @@ -0,0 +1,298 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios_ospfv3 class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ospfv3 import ( + Ospfv3Template, +) + + +class Ospfv3(ResourceModule): + """ + The ios_ospfv3 class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["ospfv3"] + + def __init__(self, module): + super(Ospfv3, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="ospfv3", + tmplt=Ospfv3Template(), + ) + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + self.gen_config() + self.run_commands() + return self.result + + def gen_config(self): + """Select the appropriate function based on the state provided + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + if self.want: + wantd = {} + for entry in self.want.get("processes", []): + wantd.update({(entry["process_id"], entry.get("vrf")): entry}) + else: + wantd = {} + if self.have: + haved = {} + for entry in self.have.get("processes", []): + haved.update({(entry["process_id"], entry.get("vrf")): entry}) + else: + haved = {} + + # turn all lists of dicts into dicts prior to merge + for thing in wantd, haved: + for _pid, proc in iteritems(thing): + for area in proc.get("areas", []): + ranges = {} + for entry in area.get("ranges", []): + ranges.update({entry["address"]: entry}) + if bool(ranges): + area["ranges"] = ranges + filter_list = {} + for entry in area.get("filter_list", []): + filter_list.update({entry["direction"]: entry}) + if bool(filter_list): + area["filter_list"] = filter_list + temp = {} + for entry in proc.get("areas", []): + temp.update({entry["area_id"]: entry}) + proc["areas"] = temp + if proc.get("distribute_list"): + if "acls" in proc.get("distribute_list"): + temp = {} + for entry in proc["distribute_list"].get("acls", []): + temp.update({entry["name"]: entry}) + proc["distribute_list"]["acls"] = temp + + # if state is merged, merge want onto have + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, limit the have to anything in want + # set want to nothing + if self.state == "deleted": + temp = {} + for k, v in iteritems(haved): + if k in wantd or not wantd: + temp.update({k: v}) + haved = temp + wantd = {} + + # delete processes first so we do run into "more than one" errors + if self.state in ["overridden", "deleted"]: + for k, have in iteritems(haved): + if k not in wantd: + self.addcmd(have, "pid", True) + + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + parsers = [ + "adjacency", + "auto_cost", + "bfd", + "compatible", + "event_log", + "help", + "interface_id", + "limit", + "local_rib_criteria", + "log_adjacency_changes", + "manet", + "max_lsa", + "max_metric", + "passive_interface", + "prefix_suppression", + "queue_depth.hello", + "queue_depth.update", + "router_id", + "shutdown", + "timers.throttle.lsa", + "timers.throttle.spf", + ] + + if want != have: + self.addcmd(want or have, "pid", False) + self.compare(parsers, want, have) + self._areas_compare(want, have) + self._address_family_compare(want, have) + + if len(self.commands) == 1 and "router" in self.commands[0]: + del self.commands[0] + + def _areas_compare(self, want, have): + wareas = want.get("areas", {}) + hareas = have.get("areas", {}) + for name, entry in iteritems(wareas): + self._area_compare(want=entry, have=hareas.pop(name, {})) + for name, entry in iteritems(hareas): + self._area_compare(want={}, have=entry) + + def _area_compare(self, want, have): + parsers = [ + "area.authentication", + "area.capability", + "area.default_cost", + "area.nssa", + "area.nssa.translate", + "area.ranges", + "area.sham_link", + "area.stub", + ] + self.compare(parsers=parsers, want=want, have=have) + self._area_compare_filters(want, have) + + def _area_compare_filters(self, wantd, haved): + for name, entry in iteritems(wantd): + h_item = haved.pop(name, {}) + if entry != h_item and name == "filter_list": + filter_list_entry = {} + filter_list_entry["area_id"] = wantd["area_id"] + if h_item: + li_diff = [ + item for item in entry + h_item if item not in entry or item not in h_item + ] + else: + li_diff = entry + filter_list_entry["filter_list"] = li_diff + self.addcmd(filter_list_entry, "area.filter_list", False) + for name, entry in iteritems(haved): + if name == "filter_list": + self.addcmd(entry, "area.filter_list", True) + + def _address_family_compare(self, want, have): + if want["process_id"] == have.get("process_id") or want["process_id"]: + af_parsers = [ + "address_family.adjacency", + "address_family.auto_cost", + "address_family.bfd", + "address_family.capability", + "address_family.compatible", + "address_family.default_information", + "address_family.default_metric", + "address_family.distance.admin_distance", + "address_family.distance.ospf", + "address_family.distribute_list.acls", + "address_family.distribute_list.prefix", + "address_family.distribute_list.route_map", + "address_family.event_log", + "address_family.graceful_restart", + "address_family.interface_id", + "address_family.limit", + "address_family.local_rib_criteria", + "address_family.log_adjacency_changes", + "address_family.manet", + "address_family.max_lsa", + "address_family.max_metric", + "address_family.maximum_paths", + "address_family.passive_interface", + "address_family.prefix_suppression", + "address_family.queue_depth.hello", + "address_family.queue_depth.update", + "address_family.router_id", + "address_family.shutdown", + "address_family.summary_prefix", + "address_family.timers.throttle.lsa", + "address_family.timers.throttle.spf", + ] + delete_exit_family = False + for each_want_af in want["address_family"]: + if have.get("address_family"): + for each_have_af in have["address_family"]: + if each_have_af.get("vrf") == each_want_af.get("vrf") and each_have_af.get( + "afi", + ) == each_want_af.get("afi"): + self.compare( + parsers=["address_family"], + want={"address_family": each_want_af}, + have={"address_family": each_have_af}, + ) + self.compare(parsers=af_parsers, want=each_want_af, have=each_have_af) + elif each_have_af.get("afi") == each_want_af.get("afi"): + self.compare( + parsers=["address_family"], + want={"address_family": each_want_af}, + have={"address_family": each_have_af}, + ) + self.compare( + parsers=af_parsers, + want={"address_family": each_want_af}, + have={"address_family": each_have_af}, + ) + if each_want_af.get("areas"): + af_want_areas = {} + af_have_areas = {} + for each_area in each_want_af["areas"]: + af_want_areas.update({each_area["area_id"]: each_area}) + if each_have_af.get("areas"): + for each_area in each_have_af["areas"]: + af_have_areas.update({each_area["area_id"]: each_area}) + + if "exit-address-family" in self.commands: + del self.commands[self.commands.index("exit-address-family")] + delete_exit_family = True + + if af_have_areas: + self._areas_compare( + {"areas": af_want_areas}, + {"areas": af_have_areas}, + ) + else: + self._areas_compare({"areas": af_want_areas}, dict()) + if delete_exit_family: + self.commands.append("exit-address-family") + else: + temp_cmd_before = self.commands + self.commands = [] + self.compare( + parsers=["address_family"], + want={"address_family": each_want_af}, + have=dict(), + ) + self.compare(parsers=af_parsers, want=each_want_af, have=dict()) + if each_want_af.get("areas"): + af_areas = {} + for each_area in each_want_af["areas"]: + af_areas.update({each_area["area_id"]: each_area}) + self._areas_compare({"areas": af_areas}, dict()) + del self.commands[self.commands.index("exit-address-family")] + self.commands.append("exit-address-family") + self.commands[0:0] = temp_cmd_before diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ping/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ping/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ping/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ping/ping.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ping/ping.py new file mode 100644 index 000000000..f2417f596 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/ping/ping.py @@ -0,0 +1,96 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios_ping config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import run_commands +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ping import ( + PingTemplate, +) + + +class Ping: + """ + The ios_ping config class + """ + + def __init__(self, module): + self.module = module + self.result = {} + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + self.generate_command() + res = self.run_command() + return self.process_result(res) + + def build_ping(self, params): + tmplt = PingTemplate() + params = utils.remove_empties(params) + cmd = tmplt.render(params, "rate", False) + return cmd + + def validate_results(self, module, loss, results): + """ + This function is used to validate whether the ping results were unexpected per "state" param. + """ + state = module.params["state"] + if state == "present" and loss == 100: + module.fail_json(msg="Ping failed unexpectedly", **results) + elif state == "absent" and loss < 100: + module.fail_json(msg="Ping succeeded unexpectedly", **results) + + def generate_command(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + warnings = list() + if warnings: + self.result["warnings"] = warnings + self.result["commands"] = self.build_ping(self.module.params) + + def run_command(self): + ping_results = run_commands(self.module, commands=self.result["commands"]) + return ping_results + + def process_result(self, ping_results): + """ + Function used to parse the statistical information from the ping response. + Example: "Success rate is 100 percent (5/5), round-trip min/avg/max = 1/2/8 ms" + Returns the percent of packet loss, received packets, transmitted packets, and RTT data. + """ + + if type(ping_results) == list: + ping_results = ping_results[0] + + ping_data = PingTemplate(lines=ping_results.splitlines()) + obj = list(ping_data.parse().values()) + + self.result["packet_loss"] = obj[0].get("loss_percentage") + self.result["packets_rx"] = obj[0].get("rx") + self.result["packets_tx"] = obj[0].get("tx") + self.result["rtt"] = obj[0].get("rtt") + loss = obj[0].get("loss") + self.validate_results(self.module, loss, self.result) + return self.result diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/prefix_lists/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/prefix_lists/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/prefix_lists/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/prefix_lists/prefix_lists.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/prefix_lists/prefix_lists.py new file mode 100644 index 000000000..7c4ebe5ec --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/prefix_lists/prefix_lists.py @@ -0,0 +1,250 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The cisco.ios_prefix_lists config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.prefix_lists import ( + Prefix_listsTemplate, +) + + +class Prefix_lists(ResourceModule): + """ + The cisco.ios_prefix_lists config class + """ + + def __init__(self, module): + super(Prefix_lists, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="prefix_lists", + tmplt=Prefix_listsTemplate(), + ) + self.parsers = ["prefix_list"] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + wantd = {entry["afi"]: entry for entry in self.want} + + haved = {entry["afi"]: entry for entry in self.have} + + # Convert each of config list to dict + for each in wantd, haved: + self.list_to_dict(each) + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, empty out wantd and set haved to wantd + if self.state == "deleted": + temp = None + for k, v in iteritems(haved): + if k in wantd: + if wantd[k].get("prefix_lists"): + want_afi_name = wantd[k].get("prefix_lists", {}) + haved[k]["prefix_lists"] = { + key: val + for key, val in iteritems(v.get("prefix_lists")) + if key in want_afi_name + } + elif wantd: + temp = k + if temp: + haved.pop(k) + wantd = {} + for k, have in iteritems(haved): + for key, val in iteritems(have["prefix_lists"]): + if k == "ipv4": + k = "ip" + self.commands.append("no {0} prefix-list {1}".format(k, key)) + + # remove superfluous config for overridden and deleted + if self.state in ["overridden", "deleted"]: + for k, have in iteritems(haved): + want_afi = wantd.get(k, {}) + for key, val in iteritems(have["prefix_lists"]): + if k == "ipv4": + k = "ip" + if want_afi and key not in want_afi.get("prefix_lists"): + self.commands.append("no {0} prefix-list {1}".format(k, key)) + + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + # alligning cmd with negate cmd 1st followed by config cmd + if self.state in ["overridden", "replaced"]: + self.commands = [each for each in self.commands if "no" in each] + [ + each for each in self.commands if "no" not in each + ] + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Prefix_lists network resource. + """ + if want != have and self.state != "deleted": + for k, v in iteritems(want["prefix_lists"]): + if have.get("prefix_lists") and have["prefix_lists"].get(k): + have_prefix = have["prefix_lists"].pop(k, {}) + for key, val in iteritems(v.get("entries")): + if have_prefix.get("entries"): + have_prefix_param = have_prefix["entries"].pop(key, {}) + else: + have_prefix_param = None + if have_prefix.get("description"): + self.compare( + parsers=self.parsers, + want={ + "afi": want["afi"], + "name": k, + "prefix_list": {"description": v["description"]}, + }, + have={ + "afi": want["afi"], + "name": k, + "prefix_list": {"description": have_prefix.pop("description")}, + }, + ) + if have_prefix_param and val != have_prefix_param: + if key == "description": + # Code snippet should be removed when Description param is removed from + # entries level as this supports deprecated level of Description + self.compare( + parsers=self.parsers, + want={"afi": want["afi"], "name": k, "prefix_list": {key: val}}, + have={ + "afi": have["afi"], + "name": k, + "prefix_list": {key: have_prefix_param}, + }, + ) + else: + if self.state == "merged" and have_prefix_param.get( + "sequence", + ) == val.get("sequence"): + self._module.fail_json( + "Cannot update existing sequence {0} of Prefix Lists {1} with state merged.".format( + val.get("sequence"), + k, + ) + + " Please use state replaced or overridden.", + ) + self.compare( + parsers=self.parsers, + want=dict(), + have={ + "afi": have["afi"], + "name": k, + "prefix_list": have_prefix_param, + }, + ) + self.compare( + parsers=self.parsers, + want={"afi": want["afi"], "name": k, "prefix_list": val}, + have={ + "afi": have["afi"], + "name": k, + "prefix_list": have_prefix_param, + }, + ) + elif val and val != have_prefix_param: + self.compare( + parsers=self.parsers, + want={"afi": want["afi"], "name": k, "prefix_list": val}, + have=dict(), + ) + if have_prefix and (self.state == "replaced" or self.state == "overridden"): + if have_prefix.get("description"): + # Code snippet should be removed when Description param is removed from + # entries level as this supports deprecated level of Description + self.compare( + parsers=self.parsers, + want=dict(), + have={ + "afi": want["afi"], + "name": k, + "prefix_list": {"description": have_prefix["description"]}, + }, + ) + for key, val in iteritems(have_prefix.get("entries")): + self.compare( + parsers=self.parsers, + want=dict(), + have={"afi": have["afi"], "name": k, "prefix_list": val}, + ) + elif v: + if v.get("description"): + self.compare( + parsers=self.parsers, + want={ + "afi": want["afi"], + "name": k, + "prefix_list": {"description": v["description"]}, + }, + have=dict(), + ) + for key, val in iteritems(v.get("entries")): + self.compare( + parsers=self.parsers, + want={"afi": want["afi"], "name": k, "prefix_list": val}, + have=dict(), + ) + + def list_to_dict(self, param): + if param: + for key, val in iteritems(param): + if val.get("prefix_lists"): + temp_prefix_list = {} + for each in val["prefix_lists"]: + temp_entries = dict() + if each.get("entries"): + for every in each["entries"]: + temp_entries.update({str(every["sequence"]): every}) + temp_prefix_list.update( + { + each["name"]: { + "description": each.get("description"), + "entries": temp_entries, + }, + }, + ) + val["prefix_lists"] = temp_prefix_list diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/route_maps/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/route_maps/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/route_maps/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/route_maps/route_maps.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/route_maps/route_maps.py new file mode 100644 index 000000000..d8a1bec9f --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/route_maps/route_maps.py @@ -0,0 +1,369 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The cisco.ios_route_maps config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.route_maps import ( + Route_mapsTemplate, +) + + +class Route_maps(ResourceModule): + """ + The cisco.ios_route_maps config class + """ + + parsers = ["continue_entry", "description"] + + def __init__(self, module): + super(Route_maps, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="route_maps", + tmplt=Route_mapsTemplate(), + ) + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + + if self.want: + wantd = {(entry["route_map"]): entry for entry in self.want} + else: + wantd = {} + + if self.have: + haved = {(entry["route_map"]): entry for entry in self.have} + else: + haved = {} + + # Convert each of config list to dict + for each in wantd, haved: + self.list_to_dict(each) + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, empty out wantd and set haved to wantd + if self.state == "deleted": + haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} + wantd = {} + + # remove superfluous config for overridden and deleted + if self.state in ["overridden", "deleted"]: + for k, have in iteritems(haved): + if k not in wantd: + route_map_cmd = "no route-map {route_map}".format(**have) + self.commands.append(route_map_cmd) + + for k, want in iteritems(wantd): + self._compare(want=want, have=haved.pop(k, {})) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Route_maps network resource. + """ + if want != have and self.state != "deleted": + self.entries_compare(want, have) + + def entries_compare(self, want, have): + if want.get("entries"): + cmd_len = len(self.commands) + if have.get("entries"): + for k, v in iteritems(want["entries"]): + have_entry = have["entries"].pop(k, {}) + if want["entries"][k] != have_entry: + # description gets merged with existing description, so explicit delete is required + # replaced and overridden state + if ( + (self.state == "replaced" or self.state == "overridden") + and have_entry.get("description") + and have_entry.get("description") + != want["entries"][k].get("description") + ): + self.compare(parsers=["description"], want=dict(), have=have_entry) + self.compare(parsers=self.parsers, want=want["entries"][k], have=have_entry) + have_match = have_entry.get("match") + want_match = v.get("match") + if have_match and want_match: + self.list_type_compare("match", want=want_match, have=have_match) + elif not have_match and want_match: + self.list_type_compare("match", want=want_match, have=dict()) + have_set = have_entry.get("set") + want_set = v.get("set") + + if have_set and want_set: + self.list_type_compare("set", want=want_set, have=have_set) + elif not have_set and want_set: + self.list_type_compare("set", want=want_set, have=dict()) + + if cmd_len != len(self.commands): + route_map_cmd = "route-map {route_map}".format(**want) + if want["entries"][k].get("action"): + route_map_cmd += " {action}".format(**want["entries"][k]) + if want["entries"][k].get("sequence"): + route_map_cmd += " {sequence}".format(**want["entries"][k]) + self.commands.insert(cmd_len, route_map_cmd) + cmd_len = len(self.commands) + else: + for k, v in iteritems(want["entries"]): + self.compare(parsers=self.parsers, want=want["entries"][k], have=dict()) + want_match = v.get("match") + if want_match: + self.list_type_compare("match", want=want_match, have=dict()) + want_set = v.get("set") + if want_set: + self.list_type_compare("set", want=want_set, have=dict()) + if cmd_len != len(self.commands): + route_map_cmd = "route-map {route_map}".format(**want) + if want["entries"][k].get("action"): + route_map_cmd += " {action}".format(**want["entries"][k]) + if want["entries"][k].get("sequence"): + route_map_cmd += " {sequence}".format(**want["entries"][k]) + self.commands.insert(cmd_len, route_map_cmd) + cmd_len = len(self.commands) + + if (self.state == "replaced" or self.state == "overridden") and have.get("entries"): + cmd_len = len(self.commands) + for k, v in iteritems(have["entries"]): + route_map_cmd = "no route-map {route_map}".format(**have) + if have["entries"][k].get("action"): + route_map_cmd += " {action}".format(**have["entries"][k]) + if have["entries"][k].get("sequence"): + route_map_cmd += " {sequence}".format(**have["entries"][k]) + self.commands.insert(cmd_len, route_map_cmd) + + def list_type_compare(self, compare_type, want, have): + parsers = [ + "{0}".format(compare_type), + "{0}.ip".format(compare_type), + "{0}.ipv6".format(compare_type), + ] + for k, v in iteritems(want): + have_v = have.pop(k, {}) + if v != have_v and k not in ["ip", "ipv6", "action", "sequence", "community"]: + if have_v: + self.compare( + parsers=parsers, + want={compare_type: {k: v}}, + have={compare_type: {k: have_v}}, + ) + else: + self.compare(parsers=parsers, want={compare_type: {k: v}}, have=dict()) + + if k in ["community"]: + if have_v: + if have_v != v: + if self.state == "overridden" or self.state == "replaced": + self.compare(parsers=parsers, want={}, have={compare_type: {k: have_v}}) + elif self.state == "merged": + for _key, _val in have_v.items(): + if isinstance(_val, list): + v[_key].extend(_val) + v[_key] = list(set(v[_key])) + v[_key].sort() + + self.compare( + parsers=parsers, + want={compare_type: {k: v}}, + have={compare_type: {k: have_v}}, + ) + else: + self.compare(parsers=parsers, want={compare_type: {k: v}}, have=dict()) + + if k in ["ip", "ipv6"]: + for key, val in iteritems(v): + have_val = have_v.pop(key, {}) + if val != have_val: + if have_val: + if self.state == "overridden" or self.state == "replaced": + self.compare( + parsers=parsers, + want=dict(), + have={compare_type: {k: {key: have_val}}}, + ) + self.compare( + parsers=parsers, + want={compare_type: {k: {key: val}}}, + have={compare_type: {k: {key: have_val}}}, + ) + else: + self.compare( + parsers=parsers, + want={compare_type: {k: {key: val}}}, + have=dict(), + ) + if (self.state == "overridden" or self.state == "replaced") and have_v: + for key, val in iteritems(have_v): + self.compare( + parsers=parsers, + want=dict(), + have={compare_type: {k: {key: val}}}, + ) + + if have and (self.state == "replaced" or self.state == "overridden"): + for k, v in iteritems(have): + if k in ["ip", "ipv6"]: + for key, val in iteritems(v): + if key and val: + self.compare( + parsers=parsers, + want=dict(), + have={compare_type: {k: {key: val}}}, + ) + else: + self.compare(parsers=parsers, want=dict(), have={compare_type: {k: v}}) + + def list_to_dict(self, param): + if param: + + def convert_to_dict(inner_match, key): + temp = dict() + for each in inner_match: + temp.update({key + "_" + str(each): each}) + return dict(sorted(temp.items(), key=lambda x: x[1])) + + for key, val in iteritems(param): + temp_entries = dict() + if val.get("entries"): + for every in val["entries"]: + match = every.get("match") + if match: + if match.get("as_path") and match.get("as_path").get("acls"): + match["as_path"]["acls"] = convert_to_dict( + match["as_path"]["acls"], + "acl", + ) + if match.get("community") and match.get("community").get("name"): + match["community"]["name"] = convert_to_dict( + match["community"]["name"], + "name", + ) + if match.get("extcommunity"): + match["extcommunity"] = convert_to_dict( + match["extcommunity"], + "num", + ) + if match.get("interfaces"): + match["interfaces"] = convert_to_dict( + match["interfaces"], + "interface", + ) + if match.get("ip"): + for each_ip_param in [ + "address", + "flowspec", + "next_hop", + "redistribution_source", + "route_source", + ]: + if match["ip"].get(each_ip_param): + if match["ip"][each_ip_param].get("acls"): + match["ip"][each_ip_param]["acls"] = convert_to_dict( + match["ip"][each_ip_param]["acls"], + "acl", + ) + elif match["ip"][each_ip_param].get("prefix_lists"): + match["ip"][each_ip_param][ + "prefix_lists" + ] = convert_to_dict( + match["ip"][each_ip_param]["prefix_lists"], + "prefix_list", + ) + if match.get("local_preference") and match.get("local_preference").get( + "value", + ): + match["local_preference"]["value"] = convert_to_dict( + match["local_preference"]["value"], + "value", + ) + if match.get("mdt_group") and match.get("mdt_group").get("acls"): + match["mdt_group"]["acls"] = convert_to_dict( + match["mdt_group"]["acls"], + "acl", + ) + if match.get("policy_lists"): + match["policy_lists"] = convert_to_dict( + match["policy_lists"], + "policy", + ) + if match.get("security_group"): + for each_sg_param in ["source", "destination"]: + if match.get("security_group").get(each_sg_param): + match["security_group"][each_sg_param] = convert_to_dict( + match["security_group"][each_sg_param], + each_sg_param, + ) + set = every.get("set") + if set: + if set.get("interfaces"): + set["interfaces"] = convert_to_dict(set["interfaces"], "interface") + if set.get("as_path"): + _k = set.get("as_path").get("prepend") + if _k: + if _k.get("as_number"): + _k["as_number"] = " ".join(_k["as_number"]) + + if set.get("community"): + _k = set.get("community") + if _k and _k.get("number"): + # asplain helper func + def to_asplain(new_format): + _int, _remainder = (int(i) for i in new_format.split(":")) + return str(_int * 65536 + _remainder) + + # convert to asplain for correct sorting + if ":" in _k["number"]: + _k["number"] = list( + map(to_asplain, _k["number"].split(" ")), + ) + else: + _k["number"] = _k["number"].split(" ") + + # sort the list to ensure idempotency + _k["number"].sort() + + action = every.get("action") + sequence = every.get("sequence") + temp_entries.update({action + "_" + str(sequence): every}) + val["entries"] = temp_entries diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/service/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/service/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/service/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/service/service.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/service/service.py new file mode 100644 index 000000000..23e3329be --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/service/service.py @@ -0,0 +1,152 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios_service class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from copy import deepcopy + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.service import ( + ServiceTemplate, +) + + +class Service(ResourceModule): + """ + The ios_service class + """ + + def __init__(self, module): + super(Service, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="service", + tmplt=ServiceTemplate(), + ) + self.parsers = [ + "call_home", + "compress_config", + "config", + "counters", + "dhcp", + "disable_ip_fast_frag", + "exec_callback", + "exec_wait", + "hide_telnet_addresses", + "internal", + "linenumber", + "log", + "log_hidden", + "nagle", + "old_slip_prompts", + "pad", + "pad_cmns", + "pad_from_xot", + "pad_to_xot", + "password_encryption", + "password_recovery", + "private_config_encryption", + "prompt", + "pt_vty_logging", + "scripting", + "sequence_numbers", + "slave_coredump", + "slave_log", + "tcp_keepalives_in", + "tcp_keepalives_out", + "telnet_zeroidle", + "unsupported_transceiver", + ] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state + """ + wantd = self._service_list_to_dict(self.want) + haved = self._service_list_to_dict(self.have) + + service_default = { + "counters": 0, + "dhcp": True, + "prompt": True, + "slave_log": True, + "password_recovery": True, + "private_config_encryption": True, + } + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, empty out wantd and set haved to wantd + elif self.state == "deleted": + wantd = self._service_list_to_dict(service_default) + + # if state is replaced + elif self.state == "replaced": + wantd = dict_merge(self._service_list_to_dict(service_default), wantd) + + self._compare(want=wantd, have=haved) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Service network resource. + """ + self.compare(parsers=self.parsers, want=want, have=have) + self._compare_lists_attrs(want, have) + + def _compare_lists_attrs(self, want, have): + """Compare list of dict""" + i_want = want.get("timestamps", {}) + i_have = have.get("timestamps", {}) + for key, wanting in iteritems(i_want): + haveing = i_have.pop(key, {}) + if wanting != haveing: + self.addcmd(wanting, "timestamps") + for key, haveing in iteritems(i_have): + self.addcmd(haveing, "timestamps", negate=True) + + def _service_list_to_dict(self, data): + """Convert all list of dicts to dicts of dicts""" + p_key = { + "timestamps": "msg", + } + tmp_data = deepcopy(data) + for k, _v in p_key.items(): + if k in tmp_data: + tmp_data[k] = {str(i[p_key.get(k)]): i for i in tmp_data[k]} + return tmp_data diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/snmp_server/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/snmp_server/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/snmp_server/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/snmp_server/snmp_server.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/snmp_server/snmp_server.py new file mode 100644 index 000000000..ff45f7b5f --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/snmp_server/snmp_server.py @@ -0,0 +1,253 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios_snmp_server config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from copy import deepcopy + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.snmp_server import ( + Snmp_serverTemplate, +) + + +class Snmp_server(ResourceModule): + """ + The ios_snmp_server config class + """ + + def __init__(self, module): + super(Snmp_server, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="snmp_server", + tmplt=Snmp_serverTemplate(), + ) + self.parsers = [ + "accounting", + "cache", + "chassis_id", + "contact", + "drop", + "file_transfer", + "if_index", + "inform", + "ip", + "location", + "manager", + "packet_size", + "queue_length", + "trap_timeout", + "source_interface", + "trap_source", + "system_shutdown", + ] + self.list_parsers = [ + "hosts", + "groups", + "engine_id", + "communities", + "context", + "password_policy", + "users", + "views", + ] + self.complex_parsers = [ + "traps.auth_framework", + "traps.bfd", + "traps.bgp", + "traps.bridge", + "traps.casa", + "traps.cnpd", + "traps.config", + "traps.config_copy", + "traps.config_ctid", + "traps.dhcp", + "traps.eigrp", + "traps.entity", + "traps.energywise", + "traps.event_manager", + "traps.flowmon", + "traps.fru_ctrl", + "traps.hsrp", + "traps.ipsla", + "traps.isis", + "traps.msdp", + "traps.mvpn", + "traps.mpls_vpn", + "traps.pki", + "traps.pw_vc", + "traps.rsvp", + "traps.syslog", + "traps.transceiver_all", + "traps.tty", + "traps.vrrp", + "traps.vrfmib", + "traps.ipmulticast", + "traps.ike.policy.add", + "traps.ike.policy.delete", + "traps.ike.tunnel.start", + "traps.ike.tunnel.stop", + "traps.ipsec.cryptomap.add", + "traps.ipsec.cryptomap.delete", + "traps.ipsec.cryptomap.attach", + "traps.ipsec.cryptomap.detach", + "traps.ipsec.tunnel.start", + "traps.ipsec.tunnel.stop", + "traps.ipsec.too_many_sas", + "traps.ospf.cisco_specific.error", + "traps.ospf.cisco_specific.retransmit", + "traps.ospf.cisco_specific.lsa", + "traps.ospf.cisco_specific.state_change.nssa_trans_change", + "traps.ospf.cisco_specific.state_change.shamlink.interface", + "traps.ospf.cisco_specific.state_change.shamlink.neighbor", + "traps.ospf.error", + "traps.ospf.retransmit", + "traps.ospf.lsa", + "traps.ospf.state_change", + "traps.l2tun.pseudowire_status", + "traps.l2tun.session", + "traps.cpu", + "traps.firewall", + "traps.pim", + "traps.snmp", + "traps.frame_relay", + "traps.frame_relay.subif", + "traps.cef", + "traps.dlsw", + "traps.ethernet.evc", + "traps.ethernet.cfm.alarm", + "traps.ethernet.cfm.cc", + "traps.ethernet.cfm.crosscheck", + ] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + wantd = self._snmp_list_to_dict(self.want) + haved = self._snmp_list_to_dict(self.have) + + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + # if state is deleted, empty out wantd and set haved to wantd + if self.state == "deleted": + wantd = {} + + self._compare(want=wantd, have=haved) + + def _compare(self, want, have): + """Leverages the base class `compare()` method and + populates the list of commands to be run by comparing + the `want` and `have` data with the `parsers` defined + for the Snmp_server network resource. + """ + self.compare(parsers=self.parsers, want=want, have=have) + self.compare(parsers=self.complex_parsers, want=want, have=have) + self._compare_lists_attrs(want, have) + + def _compare_lists_attrs(self, want, have): + """Compare list of dict""" + for _parser in self.list_parsers: + i_want = want.get(_parser, {}) + i_have = have.get(_parser, {}) + for key, wanting in iteritems(i_want): + haveing = i_have.pop(key, {}) + if wanting != haveing: + if haveing and self.state in ["overridden", "replaced"]: + self.addcmd(haveing, _parser, negate=True) + self.addcmd(wanting, _parser) + for key, haveing in iteritems(i_have): + self.addcmd(haveing, _parser, negate=True) + + def _snmp_list_to_dict(self, data): + """Convert all list of dicts to dicts of dicts""" + p_key = { + "hosts": "host", + "groups": "group", + "engine_id": "id", + "communities": "name", + "context": True, + "password_policy": "policy_name", + "file_transfer": True, + "users": "username", + "views": "name", + } + tmp_data = deepcopy(data) + for k, _v in p_key.items(): + if k in tmp_data: + if k == "hosts": + tmp_host = dict() + for i in tmp_data[k]: + tmp = dict() + if i.get("traps"): + for t in i.get("traps"): + tmp.update({t: t}) + i["traps"] = tmp + tmp_host.update( + { + str( + i[p_key.get(k)] + + i.get("version", "") + + i.get("community_string", ""), + ): i, + }, + ) + tmp_data[k] = tmp_host + elif k == "context": + tmp_data[k] = {i: {"context": i} for i in tmp_data[k]} + elif k == "file_transfer": + if tmp_data.get(k): + if tmp_data[k].get("protocol"): + tmp = dict() + for t in tmp_data[k].get("protocol"): + tmp.update({t: t}) + tmp_data[k]["protocol"] = tmp + elif k == "groups": + tmp_data[k] = { + str(i[p_key.get(k)] + i.get("version_option", "")): i for i in tmp_data[k] + } + elif k == "views": + tmp_data[k] = { + str(i[p_key.get(k)] + i.get("family_name", "")): i for i in tmp_data[k] + } + else: + tmp_data[k] = {str(i[p_key.get(k)]): i for i in tmp_data[k]} + return tmp_data diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/static_routes/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/static_routes/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/static_routes/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/static_routes/static_routes.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/static_routes/static_routes.py new file mode 100644 index 000000000..79b63c8fe --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/static_routes/static_routes.py @@ -0,0 +1,188 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2023 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios_static_routes config file. +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to its desired end-state is +created. +""" + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( + ResourceModule, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.static_routes import ( + Static_routesTemplate, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + validate_n_expand_ipv4, +) + + +class Static_routes(ResourceModule): + """ + The ios_static_routes config class + """ + + def __init__(self, module): + super(Static_routes, self).__init__( + empty_fact_val={}, + facts_module=Facts(module), + module=module, + resource="static_routes", + tmplt=Static_routesTemplate(), + ) + self.parsers = [] + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + if self.state not in ["parsed", "gathered"]: + self.generate_commands() + self.run_commands() + return self.result + + def generate_commands(self): + """Generate configuration commands to send based on + want, have and desired state. + """ + wantd, delete_spcl = self.list_to_dict(self.want, "want") + haved, n_req = self.list_to_dict(self.have, "have") + + if delete_spcl and haved and self.state == "deleted": + for pk, to_rem in delete_spcl.items(): + if pk in ["ipv4", "ipv6"]: + _afis = haved.get("_afis_") + for k, v in _afis.get(pk, {}).items(): + for each_dest in to_rem: + if k.split("_")[0] == each_dest: + self.addcmd({pk: v}, pk, True) + else: + _vrfs = haved.get(pk) + for ak, v in _vrfs.items(): + for k, srts in v.items(): + for each_dest in to_rem.get(ak): + if k.split("_")[0] == each_dest: + self.addcmd({ak: srts}, ak, True) + + else: + # if state is merged, merge want onto have and then compare + if self.state == "merged": + wantd = dict_merge(haved, wantd) + + for k, want in iteritems(wantd): + self._compare_top_level_keys(want=want, have=haved.pop(k, {})) + + # if self.state in ["overridden", "deleted"]: + if (self.state == "deleted" and not wantd) or self.state == "overridden": + for k, have in iteritems(haved): + self._compare_top_level_keys(want={}, have=have) + + def _compare_top_level_keys(self, want, have): + # if state is deleted, empty out wantd and set haved to wantd + if self.state == "deleted" and have: + _have = {} + for addf in ["ipv4", "ipv6"]: + _temp_sr = {} + for k, ha in iteritems(have.get(addf, {})): + if k in want.get(addf, {}): # or not want.get(addf) + _temp_sr[k] = ha + if _temp_sr: + _have[addf] = _temp_sr + if _have: + have = _have + want = {} + + if self.state != "deleted": + for _afi, routes in want.items(): + self._compare(s_want=routes, s_have=have.pop(_afi, {}), afi=_afi) + + if self.state in ["overridden", "deleted"]: + for _afi, routes in have.items(): + self._compare(s_want={}, s_have=routes, afi=_afi) + + def _compare(self, s_want, s_have, afi): + for name, w_srs in s_want.items(): + have_srs = s_have.pop(name, {}) + self.compare(parsers=afi, want={afi: w_srs}, have={afi: have_srs}) + + # remove remaining items in have for replaced state + for name, h_srs in s_have.items(): + self.compare(parsers=afi, want={}, have={afi: h_srs}) + + def list_to_dict(self, param, operation): + _static_rts = {} + _delete_spc = {} + if param: + for srs in param: + _vrf = srs.get("vrf") + _srts = {} + for adfs in srs.get("address_families", []): + _afi = adfs.get("afi") + _routes = {} + for rts in adfs.get("routes", []): + _dest = rts.get("dest", "") + _sdest = rts.get("dest", "") + _topo = rts.get("topology", "") + # below if specific to special deletes + if ( + self.state == "deleted" + and operation == "want" + and not rts.get("next_hops") + ): + if _vrf: + if not _delete_spc.get(_vrf): + _delete_spc[_vrf] = {} + if not _delete_spc[_vrf].get(_afi): + _delete_spc[_vrf][_afi] = [] + _delete_spc[_vrf][_afi].append(_dest) + else: + if not _delete_spc.get(_afi): + _delete_spc[_afi] = [] + _delete_spc[_afi].append(_dest) + + for nxh in rts.get("next_hops", []): + _forw_rtr_add = nxh.get("forward_router_address", "").upper() + _intf = nxh.get("interface", "") + _key = _sdest + "_" + _topo + _forw_rtr_add + _intf + + if _afi == "ipv4": + _dest = validate_n_expand_ipv4(self._module, {"address": _dest}) + dummy_sr = { + "afi": _afi, + "dest": _dest, + } + + if _vrf: + dummy_sr["vrf"] = _vrf + if _topo: + dummy_sr["topology"] = _topo + if _intf: + dummy_sr["interface"] = _intf + if _forw_rtr_add: + dummy_sr["forward_router_address"] = _forw_rtr_add + dummy_sr.update(nxh) + + _routes[_key] = dummy_sr + _srts[_afi] = _routes + _static_rts[_vrf if _vrf else "_afis_"] = _srts + return _static_rts, _delete_spc diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/vlans.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/vlans.py new file mode 100644 index 000000000..9d206f509 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/vlans.py @@ -0,0 +1,371 @@ +# +# -*- 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) +""" +The ios_vlans class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( + ConfigBase, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import dict_to_set + + +class Vlans(ConfigBase): + """ + The ios_vlans class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["vlans"] + + def __init__(self, module): + super(Vlans, self).__init__(module) + + def get_vlans_facts(self, data=None): + """Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, + self.gather_network_resources, + data=data, + ) + vlans_facts = facts["ansible_network_resources"].get("vlans") + if not vlans_facts: + return [] + return vlans_facts + + def execute_module(self): + """Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + commands = list() + warnings = list() + + if self.state in self.ACTION_STATES: + existing_vlans_facts = self.get_vlans_facts() + else: + existing_vlans_facts = [] + + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_vlans_facts)) + if commands and self.state in self.ACTION_STATES: + if not self._module.check_mode: + self._connection.edit_config(commands) + result["changed"] = True + if self.state in self.ACTION_STATES: + result["commands"] = commands + + if self.state in self.ACTION_STATES or self.state == "gathered": + changed_vlans_facts = self.get_vlans_facts() + elif self.state == "rendered": + result["rendered"] = commands + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed", + ) + result["parsed"] = self.get_vlans_facts(data=running_config) + else: + changed_vlans_facts = [] + + if self.state in self.ACTION_STATES: + result["before"] = existing_vlans_facts + if result["changed"]: + result["after"] = changed_vlans_facts + elif self.state == "gathered": + result["gathered"] = changed_vlans_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_vlans_facts): + """Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_vlans_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + + if self.state in ("overridden", "merged", "replaced", "rendered") and not want: + self._module.fail_json( + msg="value of config parameter must not be empty for state {0}".format(self.state), + ) + + if self.state == "overridden": + commands = self._state_overridden(want, have) + elif self.state == "deleted": + commands = self._state_deleted(want, have) + elif self.state in ("merged", "rendered"): + commands = self._state_merged(want, have) + elif self.state == "replaced": + commands = self._state_replaced(want, have) + return commands + + def _state_replaced(self, want, have): + """The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + check = False + for each in want: + for every in have: + if every["vlan_id"] == each["vlan_id"]: + check = True + break + continue + if check: + commands.extend(self._set_config(each, every)) + else: + commands.extend(self._set_config(each, dict())) + + return commands + + def _state_overridden(self, want, have): + """The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + want_local = want + for each in have: + count = 0 + for every in want_local: + if each["vlan_id"] == every["vlan_id"]: + break + count += 1 + else: + # We didn't find a matching desired state, which means we can + # pretend we received an empty desired state. + commands.extend(self._clear_config(every, each)) + continue + commands.extend(self._set_config(every, each)) + # as the pre-existing VLAN are now configured by + # above set_config call, deleting the respective + # VLAN entry from the want_local list + del want_local[count] + + # Iterating through want_local list which now only have new VLANs to be + # configured + for each in want_local: + commands.extend(self._set_config(each, dict())) + + return commands + + def _state_merged(self, want, have): + """The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + check = False + for each in want: + for every in have: + if each.get("vlan_id") == every.get("vlan_id"): + check = True + break + continue + if check: + commands.extend(self._set_config(each, every)) + else: + commands.extend(self._set_config(each, dict())) + + return commands + + def _state_deleted(self, want, have): + """The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + + if want: + check = False + for each in want: + for every in have: + if each.get("vlan_id") == every.get("vlan_id"): + check = True + break + check = False + continue + if check: + commands.extend(self._clear_config(each, every)) + else: + for each in have: + commands.extend(self._clear_config(dict(), each)) + + return commands + + def remove_command_from_config_list(self, vlan_id, cmd, commands): + if vlan_id not in commands and cmd != "vlan": + commands.insert(0, vlan_id) + elif cmd == "vlan": + commands.append("no %s" % vlan_id) + return commands + commands.append("no %s" % cmd) + return commands + + def add_command_to_config_list(self, vlan_id, cmd, commands): + if vlan_id not in commands: + commands.insert(0, vlan_id) + if cmd not in commands: + commands.append(cmd) + + def _set_config(self, want, have): + # Set the interface config based on the want and have config + commands = [] + vlan = "vlan {0}".format(want.get("vlan_id")) + + def negate_have_config(want_diff, have_diff, vlan, commands): + name = dict(have_diff).get("name") + if name and not dict(want_diff).get("name"): + self.remove_command_from_config_list(vlan, "name {0}".format(name), commands) + state = dict(have_diff).get("state") + if state and not dict(want_diff).get("state"): + self.remove_command_from_config_list(vlan, "state {0}".format(state), commands) + shutdown = dict(have_diff).get("shutdown") + if shutdown and not dict(want_diff).get("shutdown"): + self.remove_command_from_config_list(vlan, "shutdown", commands) + mtu = dict(have_diff).get("mtu") + if mtu and not dict(want_diff).get("mtu"): + self.remove_command_from_config_list(vlan, "mtu {0}".format(mtu), commands) + remote_span = dict(have_diff).get("remote_span") + if remote_span and not dict(want_diff).get("remote_span"): + self.remove_command_from_config_list(vlan, "remote-span", commands) + private_vlan = dict(have_diff).get("private_vlan") + if private_vlan and not dict(want_diff).get("private_vlan"): + private_vlan_type = dict(private_vlan).get("type") + self.remove_command_from_config_list( + vlan, + "private-vlan {0}".format(private_vlan_type), + commands, + ) + if private_vlan_type == "primary" and dict(private_vlan).get("associated"): + self.remove_command_from_config_list(vlan, "private-vlan association", commands) + + # Get the diff b/w want n have + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + diff = want_dict - have_dict + have_diff = have_dict - want_dict + + if diff: + if have_diff and (self.state == "replaced" or self.state == "overridden"): + negate_have_config(diff, have_diff, vlan, commands) + + name = dict(diff).get("name") + state = dict(diff).get("state") + shutdown = dict(diff).get("shutdown") + mtu = dict(diff).get("mtu") + remote_span = dict(diff).get("remote_span") + private_vlan = dict(diff).get("private_vlan") + + if name: + self.add_command_to_config_list(vlan, "name {0}".format(name), commands) + if state: + self.add_command_to_config_list(vlan, "state {0}".format(state), commands) + if mtu: + self.add_command_to_config_list(vlan, "mtu {0}".format(mtu), commands) + if remote_span: + self.add_command_to_config_list(vlan, "remote-span", commands) + + if private_vlan: + private_vlan_type = dict(private_vlan).get("type") + private_vlan_associated = dict(private_vlan).get("associated") + if private_vlan_type: + self.add_command_to_config_list( + vlan, + "private-vlan {0}".format(private_vlan_type), + commands, + ) + if private_vlan_associated: + associated_list = ",".join( + str(e) for e in private_vlan_associated + ) # Convert python list to string with elements separated by a comma + self.add_command_to_config_list( + vlan, + "private-vlan association {0}".format(associated_list), + commands, + ) + + if shutdown == "enabled": + self.add_command_to_config_list(vlan, "shutdown", commands) + elif shutdown == "disabled": + self.add_command_to_config_list(vlan, "no shutdown", commands) + elif have_diff and (self.state == "replaced" or self.state == "overridden"): + negate_have_config(diff, have_diff, vlan, commands) + + return commands + + def _clear_config(self, want, have): + # Delete the interface config based on the want and have config + commands = [] + vlan = "vlan {0}".format(have.get("vlan_id")) + + if ( + have.get("vlan_id") + and "default" not in have.get("name") + and (have.get("vlan_id") != want.get("vlan_id") or self.state == "deleted") + ): + self.remove_command_from_config_list(vlan, "vlan", commands) + elif "default" not in have.get("name"): + if have.get("mtu") != want.get("mtu"): + self.remove_command_from_config_list(vlan, "mtu", commands) + if have.get("remote_span") != want.get("remote_span") and want.get("remote_span"): + self.remove_command_from_config_list(vlan, "remote-span", commands) + if have.get("shutdown") != want.get("shutdown") and want.get("shutdown"): + self.remove_command_from_config_list(vlan, "shutdown", commands) + if have.get("state") != want.get("state") and want.get("state"): + self.remove_command_from_config_list(vlan, "state", commands) + + return commands diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acl_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acl_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acl_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acl_interfaces/acl_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acl_interfaces/acl_interfaces.py new file mode 100644 index 000000000..a3bf09957 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acl_interfaces/acl_interfaces.py @@ -0,0 +1,69 @@ +# +# -*- 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) +""" +The ios_acl_interfaces fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.acl_interfaces.acl_interfaces import ( + Acl_interfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.acl_interfaces import ( + Acl_interfacesTemplate, +) + + +class Acl_interfacesFacts(object): + """The ios_acl_interfaces fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Acl_interfacesArgs.argument_spec + + def get_acl_interfaces_data(self, connection): + return connection.get( + "show running-config | include ^interface|ip access-group|ipv6 traffic-filter", + ) + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not data: + data = self.get_acl_interfaces_data(connection) + + config_parser = Acl_interfacesTemplate(lines=data.splitlines()) + entry = sorted(list(config_parser.parse().values()), key=lambda k, sk="name": k[sk]) + if entry: + for item in entry: + item["access_groups"] = sorted( + list(item["access_groups"].values()), + key=lambda k, sk="afi": k[sk], + ) + + ansible_facts["ansible_network_resources"].pop("acl_interfaces", None) + facts = {"acl_interfaces": []} + for cfg in entry: + utils.remove_empties(cfg) + if cfg.get("access_groups"): + facts["acl_interfaces"].append(cfg) + utils.validate_config(self.argument_spec, {"config": facts.get("acl_interfaces")}) + + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/acls.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/acls.py new file mode 100644 index 000000000..6037d99e1 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/acls.py @@ -0,0 +1,168 @@ +# pylint: skip-file +# -*- 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) +""" +The ios_acls fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import re + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.acls.acls import ( + AclsArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.acls import ( + AclsTemplate, +) + + +class AclsFacts(object): + """The ios_acls fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = AclsArgs.argument_spec + + def get_acl_data(self, connection): + # Get the access-lists from the ios router + # Get the remarks on access-lists from the ios router + # alternate command 'sh run partition access-list' but has a lot of ordering issues + # and incomplete ACLs are not viewed correctly + _acl_data = connection.get("show access-list") + _remarks_data = connection.get("show running-config | include ip(v6)* access-list|remark") + if _remarks_data: + _acl_data += "\n" + _remarks_data + return _acl_data + + def sanitize_data(self, data): + """removes matches or extra config info that is added on acl match""" + re_data = "" + for da in data.split("\n"): + if "match" in da: + mod_da = re.sub(r"\([^()]*\)", "", da) + re_data += mod_da[:-1] + "\n" + else: + re_data += da + "\n" + return re_data + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for acls + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + + if not data: + data = self.get_acl_data(connection) + + if data: + data = self.sanitize_data(data) + + rmmod = NetworkTemplate(lines=data.splitlines(), tmplt=AclsTemplate()) + current = rmmod.parse() + + temp_v4 = [] + temp_v6 = [] + + if current.get("acls"): + for k, v in iteritems(current.get("acls")): + if v.get("afi") == "ipv4" and v.get("acl_type") in ["standard", "extended"]: + del v["afi"] + temp_v4.append(v) + elif v.get("afi") == "ipv6": + del v["afi"] + temp_v6.append(v) + + temp_v4 = sorted(temp_v4, key=lambda i: str(i["name"])) + temp_v6 = sorted(temp_v6, key=lambda i: str(i["name"])) + + def factor_source_dest(ace, typ): + temp = ace.get(typ, {}) + if temp.get("address"): + _temp_addr = temp.get("address", "") + ace[typ]["address"] = _temp_addr.split(" ")[0] + ace[typ]["wildcard_bits"] = _temp_addr.split(" ")[1] + + def process_protocol_options(each): + for each_ace in each.get("aces"): + if each.get("acl_type") == "standard": + if len(each_ace.get("source", {})) == 1 and each_ace.get("source", {}).get( + "address", + ): + each_ace["source"]["host"] = each_ace["source"].pop("address") + if each_ace.get("source", {}).get("address"): + addr = each_ace.get("source", {}).get("address") + if addr[-1] == ",": + each_ace["source"]["address"] = addr[:-1] + else: # for extended acl + if each_ace.get("source", {}): + factor_source_dest(each_ace, "source") + if each_ace.get("destination", {}): + factor_source_dest(each_ace, "destination") + + if each_ace.get("icmp_igmp_tcp_protocol"): + each_ace["protocol_options"] = { + each_ace["protocol"]: { + each_ace.pop("icmp_igmp_tcp_protocol").replace("-", "_"): True, + }, + } + if each_ace.get("protocol_number"): + each_ace["protocol_options"] = { + "protocol_number": each_ace.pop("protocol_number"), + } + + def collect_remarks(aces): + """makes remarks list per ace""" + ace_entry = [] + rem = [] + for i in aces: + if i.get("remarks"): + rem.append(i.pop("remarks")) + else: + ace_entry.append(i) + if rem: + ace_entry.append({"remarks": rem}) + return ace_entry + + for each in temp_v4: + if each.get("aces"): + each["aces"] = collect_remarks(each.get("aces")) + process_protocol_options(each) + + for each in temp_v6: + if each.get("aces"): + each["aces"] = collect_remarks(each.get("aces")) + process_protocol_options(each) + + objs = [] + if temp_v4: + objs.append({"afi": "ipv4", "acls": temp_v4}) + if temp_v6: + objs.append({"afi": "ipv6", "acls": temp_v6}) + + facts = {} + if objs: + facts["acls"] = [] + params = utils.validate_config(self.argument_spec, {"config": objs}) + for cfg in params["config"]: + facts["acls"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_address_family/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_address_family/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_address_family/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_address_family/bgp_address_family.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_address_family/bgp_address_family.py new file mode 100644 index 000000000..29631eec1 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_address_family/bgp_address_family.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The cisco.ios bgp_address_family fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.bgp_address_family.bgp_address_family import ( + Bgp_address_familyArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.bgp_address_family import ( + Bgp_address_familyTemplate, +) + + +class Bgp_address_familyFacts(object): + """The cisco.ios_bgp_address_family facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Bgp_address_familyArgs.argument_spec + + def get_bgp_address_family_data(self, connection): + return connection.get("show running-config | section ^router bgp") + + def _process_facts(self, objs): + """makes data as per the facts after data obtained from parsers""" + addr_fam_facts = {} + temp_af = [] + + addr_fam_facts["as_number"] = objs["as_number"] + if objs.get("address_family"): + for kaf, afs in (objs["address_family"]).items(): # remove unique value from add_fam + af = {} + for af_key, afs_val in afs.items(): + if af_key == "neighbors": + temp_neighbor = [] + for tag, neighbor in afs_val.items(): # remove unique value from neighbor + if not neighbor.get("neighbor_address"): + neighbor["neighbor_address"] = tag + temp_neighbor.append(neighbor) + af[af_key] = temp_neighbor + else: + af[af_key] = afs_val + temp_af.append(af) + + if temp_af: + addr_fam_facts["address_family"] = temp_af + return addr_fam_facts + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Bgp_address_family network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = {} + + if not data: + data = self.get_bgp_address_family_data(connection) + + # parse native config using the Bgp_address_family template + bgp_af_parser = Bgp_address_familyTemplate(lines=data.splitlines(), module=self._module) + objs = bgp_af_parser.parse() + if objs: + objs = self._process_facts(utils.remove_empties(objs)) + ansible_facts["ansible_network_resources"].pop("bgp_address_family", None) + params = utils.remove_empties( + bgp_af_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), + ) + facts["bgp_address_family"] = params.get("config", {}) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/bgp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/bgp_global.py new file mode 100644 index 000000000..68ff93636 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/bgp_global.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The cisco.ios bgp_global fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.bgp_global.bgp_global import ( + Bgp_globalArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.bgp_global import ( + Bgp_globalTemplate, +) + + +class Bgp_globalFacts(object): + """The cisco.ios bgp_global facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Bgp_globalArgs.argument_spec + + def get_bgp_global_data(self, connection): + return connection.get("show running-config | section ^router bgp") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Bgp_global network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + + if not data: + data = self.get_bgp_global_data(connection) + + # parse native config using the Bgp_global template + bgp_global_parser = Bgp_globalTemplate(lines=data.splitlines(), module=self._module) + objs = bgp_global_parser.parse() + neighbor_list = objs.get("neighbors", {}) + if neighbor_list: + objs["neighbors"] = sorted( + list(neighbor_list.values()), + key=lambda k, pk="neighbor_address": k[pk], + ) + + obj = utils.remove_empties(objs) + + ansible_facts["ansible_network_resources"].pop("bgp_global", None) + params = utils.remove_empties( + bgp_global_parser.validate_config(self.argument_spec, {"config": obj}, redact=True), + ) + + facts["bgp_global"] = params.get("config", {}) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/facts.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/facts.py new file mode 100644 index 000000000..6a28043bf --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/facts.py @@ -0,0 +1,151 @@ +# +# -*- 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) +""" +The facts class for ios +this file validates each subset of facts and selectively +calls the appropriate facts gathering function +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts import ( + FactsBase, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.acl_interfaces.acl_interfaces import ( + Acl_interfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.acls.acls import AclsFacts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.bgp_address_family.bgp_address_family import ( + Bgp_address_familyFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.bgp_global.bgp_global import ( + Bgp_globalFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.hostname.hostname import ( + HostnameFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.interfaces.interfaces import ( + InterfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.l2_interfaces.l2_interfaces import ( + L2_interfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.l3_interfaces.l3_interfaces import ( + L3_InterfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.lacp.lacp import LacpFacts +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.lacp_interfaces.lacp_interfaces import ( + Lacp_InterfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.lag_interfaces.lag_interfaces import ( + Lag_interfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.legacy.base import ( + Config, + Default, + Hardware, + Interfaces, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.lldp_global.lldp_global import ( + Lldp_globalFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.lldp_interfaces.lldp_interfaces import ( + Lldp_InterfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.logging_global.logging_global import ( + Logging_globalFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.ntp_global.ntp_global import ( + Ntp_globalFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.ospf_interfaces.ospf_interfaces import ( + Ospf_interfacesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.ospfv2.ospfv2 import ( + Ospfv2Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.ospfv3.ospfv3 import ( + Ospfv3Facts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.prefix_lists.prefix_lists import ( + Prefix_listsFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.route_maps.route_maps import ( + Route_mapsFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.service.service import ( + ServiceFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.snmp_server.snmp_server import ( + Snmp_serverFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.static_routes.static_routes import ( + Static_routesFacts, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vlans.vlans import ( + VlansFacts, +) + + +FACT_LEGACY_SUBSETS = dict(default=Default, hardware=Hardware, interfaces=Interfaces, config=Config) + +FACT_RESOURCE_SUBSETS = dict( + interfaces=InterfacesFacts, + l2_interfaces=L2_interfacesFacts, + vlans=VlansFacts, + lag_interfaces=Lag_interfacesFacts, + lacp=LacpFacts, + lacp_interfaces=Lacp_InterfacesFacts, + lldp_global=Lldp_globalFacts, + lldp_interfaces=Lldp_InterfacesFacts, + l3_interfaces=L3_InterfacesFacts, + acl_interfaces=Acl_interfacesFacts, + static_routes=Static_routesFacts, + acls=AclsFacts, + ospfv2=Ospfv2Facts, + ospfv3=Ospfv3Facts, + ospf_interfaces=Ospf_interfacesFacts, + bgp_global=Bgp_globalFacts, + bgp_address_family=Bgp_address_familyFacts, + logging_global=Logging_globalFacts, + route_maps=Route_mapsFacts, + prefix_lists=Prefix_listsFacts, + ntp_global=Ntp_globalFacts, + service=ServiceFacts, + snmp_server=Snmp_serverFacts, + hostname=HostnameFacts, +) + + +class Facts(FactsBase): + """The fact class for ios""" + + VALID_LEGACY_GATHER_SUBSETS = frozenset(FACT_LEGACY_SUBSETS.keys()) + VALID_RESOURCE_SUBSETS = frozenset(FACT_RESOURCE_SUBSETS.keys()) + + def __init__(self, module): + super(Facts, self).__init__(module) + + def get_facts(self, legacy_facts_type=None, resource_facts_type=None, data=None): + """Collect the facts for ios + :param legacy_facts_type: List of legacy facts types + :param resource_facts_type: List of resource fact types + :param data: previously collected conf + :rtype: dict + :return: the facts gathered + """ + if self.VALID_RESOURCE_SUBSETS: + self.get_network_resources_facts(FACT_RESOURCE_SUBSETS, resource_facts_type, data) + + if self.VALID_LEGACY_GATHER_SUBSETS: + self.get_network_legacy_facts(FACT_LEGACY_SUBSETS, legacy_facts_type) + + return self.ansible_facts, self._warnings diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/hostname/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/hostname/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/hostname/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/hostname/hostname.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/hostname/hostname.py new file mode 100644 index 000000000..20fa1e20f --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/hostname/hostname.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios hostname fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.hostname.hostname import ( + HostnameArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.hostname import ( + HostnameTemplate, +) + + +class HostnameFacts(object): + """The ios hostname facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = HostnameArgs.argument_spec + + def get_hostname_data(self, connection): + return connection.get("show running-config | section ^hostname") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Hostname network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + + if not data: + data = self.get_hostname_data(connection) + + # parse native config using the Hostname template + hostname_parser = HostnameTemplate(lines=data.splitlines(), module=self._module) + objs = hostname_parser.parse() + + ansible_facts["ansible_network_resources"].pop("hostname", None) + + params = utils.remove_empties( + hostname_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), + ) + + facts["hostname"] = params.get("config", {}) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/interfaces/interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/interfaces/interfaces.py new file mode 100644 index 000000000..8c7e562b7 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/interfaces/interfaces.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios interfaces fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.interfaces.interfaces import ( + InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.interfaces import ( + InterfacesTemplate, +) + + +class InterfacesFacts(object): + """The ios interfaces facts class""" + + def __init__(self, module): + self._module = module + self.argument_spec = InterfacesArgs.argument_spec + + def get_interfaces_data(self, connection): + return connection.get("show running-config | section ^interface") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Interfaces network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + if not data: + data = self.get_interfaces_data(connection) + + # parse native config using the Interfaces template + interfaces_parser = InterfacesTemplate(lines=data.splitlines(), module=self._module) + objs = sorted(list(interfaces_parser.parse().values()), key=lambda k, sk="name": k[sk]) + + ansible_facts["ansible_network_resources"].pop("interfaces", None) + facts = {"interfaces": []} + params = utils.remove_empties( + interfaces_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), + ) + + facts["interfaces"] = params["config"] + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l2_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l2_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l2_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l2_interfaces/l2_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l2_interfaces/l2_interfaces.py new file mode 100644 index 000000000..b1e6e0659 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l2_interfaces/l2_interfaces.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios l2_interfaces fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.l2_interfaces.l2_interfaces import ( + L2_interfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.l2_interfaces import ( + L2_interfacesTemplate, +) + + +class L2_interfacesFacts(object): + """The ios l2_interfaces facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = L2_interfacesArgs.argument_spec + + def get_l2_interfaces_data(self, connection): + return connection.get("show running-config | section ^interface") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for L2_interfaces network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + + if not data: + data = self.get_l2_interfaces_data(connection) + + # parse native config using the L2_interfaces template + l2_interfaces_parser = L2_interfacesTemplate(lines=data.splitlines(), module=self._module) + objs = list(l2_interfaces_parser.parse().values()) + + def process_mode(obj): + mode = "" + if obj == "dot1q-tunnel": + mode = "dot1q_tunnel" + elif obj == "dynamic auto": + mode = "dynamic_auto" + elif obj == "dynamic desirable": + mode = "dynamic_desirable" + elif obj == "private-vlan host": + mode = "private_vlan_host" + elif obj == "private-vlan promiscuous": + mode = "private_vlan_promiscuous" + elif obj == "private-vlan trunk secondary": + mode = "private_vlan_trunk" + return mode + + def process_vlans(obj, vlan_type): + vlans = [] + _vlans = obj.get("trunk") + if _vlans.get(vlan_type): + vlans.extend(_vlans.get(vlan_type)) + if _vlans.get(vlan_type + "_add"): + for vlan_grp in _vlans.get(vlan_type + "_add"): + vlans.extend(vlan_grp) + del _vlans[vlan_type + "_add"] + return vlans + + if objs: + for obj in objs: + if obj.get("mode") and obj.get("mode") not in ["trunk", "access", "dynamic"]: + obj["mode"] = process_mode(obj.get("mode")) + if obj.get("trunk"): + for _vlan in ["allowed_vlans", "pruning_vlans"]: + if obj.get("trunk", {}).get(_vlan): + obj["trunk"][_vlan] = process_vlans(obj, _vlan) + + ansible_facts["ansible_network_resources"].pop("l2_interfaces", None) + + params = utils.remove_empties( + l2_interfaces_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), + ) + + facts["l2_interfaces"] = params.get("config", []) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/l3_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/l3_interfaces.py new file mode 100644 index 000000000..bea49159b --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/l3_interfaces.py @@ -0,0 +1,83 @@ +# +# -*- 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) +""" +The ios_l3_interfaces fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.l3_interfaces.l3_interfaces import ( + L3_interfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.l3_interfaces import ( + L3_interfacesTemplate, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + netmask_to_cidr, +) + + +class L3_InterfacesFacts(object): + """The ios l3 interfaces fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = L3_interfacesArgs.argument_spec + + def get_l3_interfaces_data(self, connection): + return connection.get("show running-config | section ^interface") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for l3 interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + objs = [] + + if not data: + data = self.get_l3_interfaces_data(connection) + + # parse native config using the l3_interfaces template + l3_interfaces_parser = L3_interfacesTemplate(lines=data.splitlines()) + objs = l3_interfaces_parser.parse() + + objs = utils.remove_empties(objs) + temp = [] + for k, v in iteritems(objs): + if v.get("ipv4"): + for each in v["ipv4"]: + if each.get("netmask"): + cidr_val = netmask_to_cidr(each["netmask"]) + each["address"] = each["address"].strip(" ") + "/" + cidr_val + del each["netmask"] + temp.append(v) + # sorting the dict by interface name + temp = sorted(temp, key=lambda k, sk="name": k[sk]) + + objs = temp + facts = {} + if objs: + facts["l3_interfaces"] = [] + params = utils.validate_config(self.argument_spec, {"config": objs}) + for cfg in params["config"]: + facts["l3_interfaces"].append(utils.remove_empties(cfg)) + facts["l3_interfaces"] = sorted(facts["l3_interfaces"], key=lambda k, sk="name": k[sk]) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp/lacp.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp/lacp.py new file mode 100644 index 000000000..ec664c2e6 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp/lacp.py @@ -0,0 +1,89 @@ +# +# -*- 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) +""" +The ios lacp fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lacp.lacp import ( + LacpArgs, +) + + +class LacpFacts(object): + """The ios lacp fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = LacpArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_lacp_data(self, connection): + return connection.get("show lacp sys-id") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for lacp + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + + if not data: + data = self.get_lacp_data(connection) + + obj = {} + if data: + lacp_obj = self.render_config(self.generated_spec, data) + if lacp_obj: + obj = lacp_obj + + ansible_facts["ansible_network_resources"].pop("lacp", None) + facts = {} + + params = utils.validate_config(self.argument_spec, {"config": obj}) + facts["lacp"] = utils.remove_empties(params["config"]) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + + config["system"]["priority"] = int(conf.split(",")[0]) + + return utils.remove_empties(config) diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp_interfaces/lacp_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 000000000..08ae028ae --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lacp_interfaces/lacp_interfaces.py @@ -0,0 +1,112 @@ +# +# -*- 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) +""" +The ios_lacp_interfaces fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +import re + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lacp_interfaces.lacp_interfaces import ( + Lacp_InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + get_interface_type, + normalize_interface, +) + + +class Lacp_InterfacesFacts(object): + """The ios_lacp_interfaces fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Lacp_InterfacesArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_lacp_interface_data(self, connection): + return connection.get("show running-config | section ^interface") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for lacp_interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + + objs = [] + if not data: + data = self.get_lacp_interface_data(connection) + + # operate on a collection of resource x + config = ("\n" + data).split("\ninterface ") + + for conf in config: + if conf: + obj = self.render_config(self.generated_spec, conf) + if obj: + objs.append(obj) + facts = {} + + if objs: + facts["lacp_interfaces"] = [] + params = utils.validate_config(self.argument_spec, {"config": objs}) + for cfg in params["config"]: + facts["lacp_interfaces"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + match = re.search(r"^(\S+)", conf) + intf = match.group(1) + if get_interface_type(intf) == "unknown": + return {} + + config["name"] = normalize_interface(intf) + port_priority = utils.parse_conf_arg(conf, "lacp port-priority") + max_bundle = utils.parse_conf_arg(conf, "lacp max-bundle") + if port_priority: + config["port_priority"] = int(port_priority) + if "lacp fast-switchover" in conf: + config["fast_switchover"] = True + if max_bundle: + config["max_bundle"] = int(max_bundle) + + return utils.remove_empties(config) diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lag_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lag_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lag_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lag_interfaces/lag_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lag_interfaces/lag_interfaces.py new file mode 100644 index 000000000..a8e54bd9f --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lag_interfaces/lag_interfaces.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios lag_interfaces fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from itertools import groupby + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lag_interfaces.lag_interfaces import ( + Lag_InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.lag_interfaces import ( + Lag_interfacesTemplate, +) + + +class Lag_interfacesFacts(object): + """The ios lag_interfaces facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Lag_InterfacesArgs.argument_spec + + def get_lag_interfaces_data(self, connection): + return connection.get("show running-config | section ^interface") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Lag_interfaces network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + + if not data: + data = self.get_lag_interfaces_data(connection) + + # parse native config using the Lag_interfaces template + lag_interfaces_parser = Lag_interfacesTemplate(lines=data.splitlines(), module=self._module) + objs = self.process_facts(list(lag_interfaces_parser.parse().values())) + ansible_facts["ansible_network_resources"].pop("lag_interfaces", None) + + params = utils.remove_empties( + lag_interfaces_parser.validate_config( + self.argument_spec, + {"config": objs}, + redact=True, + ), + ) + + facts["lag_interfaces"] = params.get("config", []) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def process_facts(self, all_objs): + lag_facts = [] + objs = [] + temp_channels = [] + + def key_channel(k): + return k.get("channel") + + for intrf in all_objs: + if intrf.get("channel"): + if intrf.get("channel") in temp_channels: + temp_channels.remove(intrf.get("channel")) + objs.append(intrf) + if "Port-channel" in intrf.get("member"): + temp_channels.append(intrf.get("member")) + + objs = sorted(objs, key=key_channel) + + for empty_channel in temp_channels: + objs.append({"channel": empty_channel}) + + for key, value in groupby(objs, key_channel): + if key: + _v = list(value) + for v in _v: + v.pop("channel") + lag_facts.append({"name": key, "members": _v}) + + return lag_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/legacy/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/legacy/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/legacy/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/legacy/base.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/legacy/base.py new file mode 100644 index 000000000..de92f1ed5 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/legacy/base.py @@ -0,0 +1,426 @@ +# +# -*- 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) + +""" +The ios legacy fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +import platform +import re + +from ansible.module_utils.six import iteritems +from ansible.module_utils.six.moves import zip + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_capabilities, + normalize_interface, + run_commands, +) + + +class FactsBase(object): + COMMANDS = list() + + def __init__(self, module): + self.module = module + self.facts = dict() + self.warnings = list() + self.responses = None + + def populate(self): + self.responses = run_commands(self.module, commands=self.COMMANDS, check_rc=False) + + def run(self, cmd): + return run_commands(self.module, commands=cmd, check_rc=False) + + +class Default(FactsBase): + COMMANDS = ["show version", "show switch virtual", "show inventory"] + + def populate(self): + super(Default, self).populate() + self.facts.update(self.platform_facts()) + data = self.responses[0] + if data: + self.facts["iostype"] = self.parse_iostype(data) + self.facts["operatingmode"] = self.parse_operatingmode(data, self.facts["iostype"]) + self.facts["serialnum"] = self.parse_serialnum(data) + self.parse_stacks(data) + data = self.responses[1] + self.responses[2] + vss_errs = ["Invalid input", "Switch Mode : Standalone"] + if data and not any(err in data for err in vss_errs): + self.parse_virtual_switch(data) + + def parse_iostype(self, data): + match = re.search(r"\sIOS-XE\s", data) + if match: + return "IOS-XE" + else: + return "IOS" + + def parse_operatingmode(self, data, iostype): + # for older ios versions default being autonomous where operating mode classification not present + match = re.search(r"Router\soperating\smode: (\S+)", data) + if (match and "autonomous" in match.group(1).lower()) or iostype == "IOS": + return "autonomous" + else: + return "controller" + + def parse_serialnum(self, data): + match = re.search(r"board ID (\S+)", data) + if match: + return match.group(1) + + def parse_stacks(self, data): + match = re.findall(r"^Model [Nn]umber\s+: (\S+)", data, re.M) + if match: + self.facts["stacked_models"] = match + + match = re.findall(r"^System [Ss]erial [Nn]umber\s+: (\S+)", data, re.M) + if match: + self.facts["stacked_serialnums"] = match + + if "stacked_models" in self.facts: + self.facts["virtual_switch"] = "STACK" + + def parse_virtual_switch(self, data): + match = re.search(r"^Virtual switch domain number : ([0-9]+)", data, re.M) + if match: + self.facts["virtual_switch"] = "VSS" + self.facts["virtual_switch_domain"] = match.group(1) + + match = re.findall(r"System\".*?SN:\s*([^\s]+)", data, re.S) + if match: + self.facts["virtual_switch_serialnums"] = match + + def platform_facts(self): + platform_facts = {} + + resp = get_capabilities(self.module) + device_info = resp["device_info"] + + platform_facts["system"] = device_info["network_os"] + + for item in ("model", "image", "version", "platform", "hostname"): + val = device_info.get("network_os_%s" % item) + if val: + platform_facts[item] = val + + platform_facts["api"] = resp["network_api"] + platform_facts["python_version"] = platform.python_version() + + return platform_facts + + +class Hardware(FactsBase): + COMMANDS = ["dir", "show memory statistics"] + + def populate(self): + warnings = list() + super(Hardware, self).populate() + data = self.responses[0] + if data: + self.facts["filesystems"] = self.parse_filesystems(data) + self.facts["filesystems_info"] = self.parse_filesystems_info(data) + + data = self.responses[1] + if data: + if "Invalid input detected" in data: + warnings.append("Unable to gather memory statistics") + else: + processor_line = [line for line in data.splitlines() if "Processor" in line].pop() + match = re.findall(r"\s(\d+)\s", processor_line) + if match: + self.facts["memtotal_mb"] = int(match[0]) / 1048576 + self.facts["memfree_mb"] = int(match[2]) / 1048576 + + def parse_filesystems(self, data): + return re.findall(r"^Directory of (\S+)/", data, re.M) + + def parse_filesystems_info(self, data): + facts = dict() + fs = "" + for line in data.split("\n"): + match = re.match(r"^Directory of (\S+)/", line) + if match: + fs = match.group(1) + facts[fs] = dict() + continue + match = re.match(r"^(\d+) bytes total \((\d+) bytes free\)", line) + if match: + facts[fs]["spacetotal_kb"] = int(match.group(1)) / 1024 + facts[fs]["spacefree_kb"] = int(match.group(2)) / 1024 + return facts + + +class Config(FactsBase): + COMMANDS = ["show running-config"] + + def populate(self): + super(Config, self).populate() + data = self.responses[0] + if data: + data = re.sub( + r"""^Building configuration + ...\s+Current configuration : \d+ bytes\n""", + "", + data, + flags=re.MULTILINE, + ) + self.facts["config"] = data + + +class Interfaces(FactsBase): + COMMANDS = [ + "show interfaces", + "show ip interface", + "show ipv6 interface", + "show lldp", + "show cdp", + ] + + def populate(self): + super(Interfaces, self).populate() + + self.facts["all_ipv4_addresses"] = list() + self.facts["all_ipv6_addresses"] = list() + self.facts["neighbors"] = {} + + data = self.responses[0] + if data: + interfaces = self.parse_interfaces(data) + self.facts["interfaces"] = self.populate_interfaces(interfaces) + + data = self.responses[1] + if data: + data = self.parse_interfaces(data) + self.populate_ipv4_interfaces(data) + + data = self.responses[2] + if data: + data = self.parse_interfaces(data) + self.populate_ipv6_interfaces(data) + + data = self.responses[3] + lldp_errs = ["Invalid input", "LLDP is not enabled"] + + if data and not any(err in data for err in lldp_errs): + neighbors = self.run(["show lldp neighbors detail"]) + if neighbors: + self.facts["neighbors"].update(self.parse_neighbors(neighbors[0])) + + data = self.responses[4] + cdp_errs = ["CDP is not enabled"] + + if data and not any(err in data for err in cdp_errs): + cdp_neighbors = self.run(["show cdp neighbors detail"]) + if cdp_neighbors: + self.facts["neighbors"].update(self.parse_cdp_neighbors(cdp_neighbors[0])) + + def populate_interfaces(self, interfaces): + facts = dict() + for key, value in iteritems(interfaces): + intf = dict() + intf["description"] = self.parse_description(value) + intf["macaddress"] = self.parse_macaddress(value) + + intf["mtu"] = self.parse_mtu(value) + intf["bandwidth"] = self.parse_bandwidth(value) + intf["mediatype"] = self.parse_mediatype(value) + intf["duplex"] = self.parse_duplex(value) + intf["lineprotocol"] = self.parse_lineprotocol(value) + intf["operstatus"] = self.parse_operstatus(value) + intf["type"] = self.parse_type(value) + + facts[key] = intf + return facts + + def populate_ipv4_interfaces(self, data): + for key, value in data.items(): + self.facts["interfaces"][key]["ipv4"] = list() + primary_address = addresses = [] + primary_address = re.findall(r"Internet address is (.+)$", value, re.M) + addresses = re.findall(r"Secondary address (.+)$", value, re.M) + if len(primary_address) == 0: + continue + addresses.append(primary_address[0]) + for address in addresses: + addr, subnet = address.split("/") + ipv4 = dict(address=addr.strip(), subnet=subnet.strip()) + self.add_ip_address(addr.strip(), "ipv4") + self.facts["interfaces"][key]["ipv4"].append(ipv4) + + def populate_ipv6_interfaces(self, data): + for key, value in iteritems(data): + try: + self.facts["interfaces"][key]["ipv6"] = list() + except KeyError: + self.facts["interfaces"][key] = dict() + self.facts["interfaces"][key]["ipv6"] = list() + addresses = re.findall(r"\s+(.+), subnet", value, re.M) + subnets = re.findall(r", subnet is (.+)$", value, re.M) + for addr, subnet in zip(addresses, subnets): + ipv6 = dict(address=addr.strip(), subnet=subnet.strip()) + self.add_ip_address(addr.strip(), "ipv6") + self.facts["interfaces"][key]["ipv6"].append(ipv6) + + def add_ip_address(self, address, family): + if family == "ipv4": + self.facts["all_ipv4_addresses"].append(address) + else: + self.facts["all_ipv6_addresses"].append(address) + + def parse_neighbors(self, neighbors): + facts = dict() + for entry in neighbors.split("------------------------------------------------"): + if entry == "": + continue + intf = self.parse_lldp_intf(entry) + if intf is None: + return facts + intf = normalize_interface(intf) + if intf not in facts: + facts[intf] = list() + fact = dict() + fact["host"] = self.parse_lldp_host(entry) + fact["port"] = self.parse_lldp_port(entry) + fact["ip"] = self.parse_lldp_ip(entry) + facts[intf].append(fact) + return facts + + def parse_cdp_neighbors(self, neighbors): + facts = dict() + for entry in neighbors.split("-------------------------"): + if entry == "": + continue + intf_port = self.parse_cdp_intf_port(entry) + if intf_port is None: + return facts + intf, port = intf_port + if intf not in facts: + facts[intf] = list() + fact = dict() + fact["host"] = self.parse_cdp_host(entry) + fact["platform"] = self.parse_cdp_platform(entry) + fact["port"] = port + fact["ip"] = self.parse_cdp_ip(entry) + facts[intf].append(fact) + return facts + + def parse_interfaces(self, data): + parsed = dict() + key = "" + for line in data.split("\n"): + if len(line) == 0: + continue + if line[0] == " ": + parsed[key] += "\n%s" % line + else: + match = re.match(r"^(\S+)", line) + if match: + key = match.group(1) + parsed[key] = line + return parsed + + def parse_description(self, data): + match = re.search(r"Description: (.+)$", data, re.M) + if match: + return match.group(1) + + def parse_macaddress(self, data): + match = re.search(r"Hardware is (?:.*), address is (\S+)", data) + if match: + return match.group(1) + + def parse_ipv4(self, data): + match = re.search(r"Internet address is (\S+)", data) + if match: + addr, masklen = match.group(1).split("/") + return dict(address=addr, masklen=int(masklen)) + + def parse_mtu(self, data): + match = re.search(r"MTU (\d+)", data) + if match: + return int(match.group(1)) + + def parse_bandwidth(self, data): + match = re.search(r"BW (\d+)", data) + if match: + return int(match.group(1)) + + def parse_duplex(self, data): + match = re.search(r"(\w+) Duplex", data, re.M) + if match: + return match.group(1) + + def parse_mediatype(self, data): + match = re.search(r"media type is (.+)$", data, re.M) + if match: + return match.group(1) + + def parse_type(self, data): + match = re.search(r"Hardware is (.+),", data, re.M) + if match: + return match.group(1) + + def parse_lineprotocol(self, data): + match = re.search(r"line protocol is (up|down)(.+)?$", data, re.M) + if match: + return match.group(1) + + def parse_operstatus(self, data): + match = re.search(r"^(?:.+) is (.+),", data, re.M) + if match: + return (match.group(1)).lstrip() + + def parse_lldp_intf(self, data): + match = re.search(r"^Local Intf: (.+)$", data, re.M) + if match: + return match.group(1) + + def parse_lldp_host(self, data): + match = re.search(r"System Name: (.+)$", data, re.M) + if match: + return match.group(1) + + def parse_lldp_port(self, data): + match = re.search(r"Port id: (.+)$", data, re.M) + if match: + return match.group(1) + + def parse_lldp_ip(self, data): + match = re.search(r"^ IP: (.+)$", data, re.M) + if match: + return match.group(1) + + def parse_cdp_intf_port(self, data): + match = re.search(r"^Interface: (.+), Port ID \(outgoing port\): (.+)$", data, re.M) + if match: + return match.group(1), match.group(2) + + def parse_cdp_host(self, data): + match = re.search(r"^Device ID: (.+)$", data, re.M) + if match: + return match.group(1) + + def parse_cdp_platform(self, data): + match = re.search(r"^Platform: (.+),", data, re.M) + if match: + return match.group(1) + + def parse_cdp_ip(self, data): + match = re.search(r"^ IP address: (.+)$", data, re.M) + if match: + return match.group(1) diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_global/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_global/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_global/lldp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_global/lldp_global.py new file mode 100644 index 000000000..2b8e4fe04 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_global/lldp_global.py @@ -0,0 +1,101 @@ +# +# -*- 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) +""" +The ios lldp_global fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lldp_global.lldp_global import ( + Lldp_globalArgs, +) + + +class Lldp_globalFacts(object): + """The ios lldp_global fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Lldp_globalArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_lldp_global_data(self, connection): + return connection.get("show running-config | section ^lldp") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for lldp_global + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + objs = dict() + if not data: + data = self.get_lldp_global_data(connection) + # operate on a collection of resource x + config = data.split("\n") + for conf in config: + if conf: + obj = self.render_config(self.generated_spec, conf) + if obj: + objs.update(obj) + facts = {} + + if objs: + params = utils.validate_config( + self.argument_spec, + {"config": utils.remove_empties(objs)}, + ) + facts["lldp_global"] = utils.remove_empties(params["config"]) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + + holdtime = utils.parse_conf_arg(conf, "lldp holdtime") + timer = utils.parse_conf_arg(conf, "lldp timer") + reinit = utils.parse_conf_arg(conf, "lldp reinit") + if holdtime: + config["holdtime"] = int(holdtime) + if "lldp run" in conf: + config["enabled"] = True + if timer: + config["timer"] = int(timer) + if reinit: + config["reinit"] = int(reinit) + + return utils.remove_empties(config) diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_interfaces/lldp_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_interfaces/lldp_interfaces.py new file mode 100644 index 000000000..906be1874 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/lldp_interfaces/lldp_interfaces.py @@ -0,0 +1,116 @@ +# +# -*- 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) +""" +The ios_lldp_interfaces fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +import re + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lldp_interfaces.lldp_interfaces import ( + Lldp_InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + get_interface_type, + normalize_interface, +) + + +class Lldp_InterfacesFacts(object): + """The ios_lldp_interfaces fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Lldp_InterfacesArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_lldp_interfaces_data(self, connection): + return connection.get("show lldp interface") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for lldp_interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + + objs = [] + if not data: + data = self.get_lldp_interfaces_data(connection) + # operate on a collection of resource x + config = data.split("\n\n") + for conf in config: + if conf: + obj = self.render_config(self.generated_spec, conf) + if obj: + objs.append(obj) + facts = {} + + if objs: + facts["lldp_interfaces"] = [] + params = utils.validate_config(self.argument_spec, {"config": objs}) + for cfg in params["config"]: + facts["lldp_interfaces"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def render_config(self, spec, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + match = re.search(r"^(\S+)(:)", conf) + intf = "" + if match: + intf = match.group(1) + + if get_interface_type(intf) == "unknown": + return {} + if intf.lower().startswith("gi"): + config["name"] = normalize_interface(intf) + receive = utils.parse_conf_arg(conf, "Rx:") + transmit = utils.parse_conf_arg(conf, "Tx:") + + if receive == "enabled": + config["receive"] = True + elif receive == "disabled": + config["receive"] = False + if transmit == "enabled": + config["transmit"] = True + elif transmit == "disabled": + config["transmit"] = False + + return utils.remove_empties(config) diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/logging_global/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/logging_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/logging_global/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/logging_global/logging_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/logging_global/logging_global.py new file mode 100644 index 000000000..639d43dba --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/logging_global/logging_global.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios logging_global fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.logging_global.logging_global import ( + Logging_globalArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.logging_global import ( + Logging_globalTemplate, +) + + +class Logging_globalFacts(object): + """The ios logging_global facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Logging_globalArgs.argument_spec + + def get_logging_data(self, connection): + return connection.get("show running-config | include logging") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Logging_global network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objFinal = [] + + if not data: + data = self.get_logging_data(connection) + + # parse native config using the Logging_global template + logging_global_parser = Logging_globalTemplate(lines=data.splitlines(), module=self._module) + objFinal = logging_global_parser.parse() + + if objFinal: + for k, v in iteritems(objFinal): + if type(v) == list and k not in ["hosts", "source_interface", "filter"]: + v.sort() + objFinal[k] = v + elif type(v) == list and k == "hosts": + objFinal[k] = sorted( + objFinal[k], + key=lambda item: item["host"] if item.get("host") else item.get("ipv6"), + ) + elif type(v) == list and k == "source_interface": + objFinal[k] = sorted(objFinal[k], key=lambda item: item["interface"]) + elif type(v) == list and k == "filter": + objFinal[k] = sorted(objFinal[k], key=lambda item: item["url"]) + ansible_facts["ansible_network_resources"].pop("logging_global", None) + + params = utils.remove_empties( + logging_global_parser.validate_config( + self.argument_spec, + {"config": objFinal}, + redact=True, + ), + ) + + facts["logging_global"] = params.get("config", {}) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ntp_global/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ntp_global/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ntp_global/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ntp_global/ntp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ntp_global/ntp_global.py new file mode 100644 index 000000000..f762ec0f1 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ntp_global/ntp_global.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios ntp_global fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ntp_global.ntp_global import ( + Ntp_globalArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ntp_global import ( + Ntp_globalTemplate, +) + + +class Ntp_globalFacts(object): + """The ios ntp_global facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Ntp_globalArgs.argument_spec + + def sort_dicts(self, objs): + p_key = { + "servers": "server", + "peers": "peer", + "authentication_keys": "id", + "peer": "access_list", + "query_only": "access_list", + "serve": "access_list", + "serve_only": "access_list", + "trusted_keys": "range_start", + "access_group": True, + } + for k, _v in p_key.items(): + if k in objs and k != "access_group": + objs[k] = sorted(objs[k], key=lambda _k: str(_k[p_key[k]])) + elif objs.get("access_group") and k == "access_group": + objs[k] = self.sort_dicts(objs.get("access_group")) + return objs + + def get_ntp_data(self, connection): + return connection.get("show running-config | section ^ntp") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Ntp_global network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + + if not data: + data = self.get_ntp_data(connection) + + # parse native config using the Ntp_global template + ntp_global_parser = Ntp_globalTemplate(lines=data.splitlines(), module=self._module) + objs = ntp_global_parser.parse() + + if objs: + objs = self.sort_dicts(objs) + + ansible_facts["ansible_network_resources"].pop("ntp_global", None) + + params = utils.remove_empties( + ntp_global_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), + ) + + facts["ntp_global"] = params.get("config", {}) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospf_interfaces/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospf_interfaces/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospf_interfaces/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospf_interfaces/ospf_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospf_interfaces/ospf_interfaces.py new file mode 100644 index 000000000..eab4d8263 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospf_interfaces/ospf_interfaces.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The cisco.ios ospf_interfaces fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ospf_interfaces.ospf_interfaces import ( + Ospf_interfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ospf_interfaces import ( + Ospf_interfacesTemplate, +) + + +class Ospf_interfacesFacts(object): + """The cisco.ios ospf_interfaces facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Ospf_interfacesArgs.argument_spec + + def get_ospf_interfaces_data(self, connection): + return connection.get("show running-config | section ^interface") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Ospf_interfaces network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + + if not data: + data = self.get_ospf_interfaces_data(connection) + + # parse native config using the Ospf_interfaces template + ospf_interfaces_parser = Ospf_interfacesTemplate( + lines=data.splitlines(), + module=self._module, + ) + + objs = ospf_interfaces_parser.parse() + final_objs = [] + + for key, value in objs.items(): + temp_af = [] + if value["address_family"].get("ip"): + temp_af.append(value["address_family"].get("ip")) + if value["address_family"].get("ipv6"): + temp_af.append(value["address_family"].get("ipv6")) + if temp_af: + value["address_family"] = temp_af + if value: + value = utils.remove_empties(value) + final_objs.append(value) + + ansible_facts["ansible_network_resources"].pop("ospf_interfaces", None) + + params = utils.remove_empties( + ospf_interfaces_parser.validate_config( + self.argument_spec, + {"config": final_objs}, + redact=True, + ), + ) + + facts["ospf_interfaces"] = params.get("config", []) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv2/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv2/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv2/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv2/ospfv2.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv2/ospfv2.py new file mode 100644 index 000000000..48a77de99 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv2/ospfv2.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios ospfv2 fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ospfv2.ospfv2 import ( + Ospfv2Args, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ospfv2 import ( + Ospfv2Template, +) + + +class Ospfv2Facts(object): + """The ios ospfv2 fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Ospfv2Args.argument_spec + + def get_ospfv2_data(self, connection): + return connection.get("show running-config | section ^router ospf") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for ospfv2 + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not data: + data = self.get_ospfv2_data(connection) + + ipv4 = {"processes": []} + rmmod = NetworkTemplate(lines=data.splitlines(), tmplt=Ospfv2Template()) + current = rmmod.parse() + + # convert some of the dicts to lists + for key, sortv in [("processes", "process_id")]: + if key in current and current[key]: + current[key] = current[key].values() + current[key] = sorted(current[key], key=lambda k, sk=sortv: k[sk]) + + for process in current.get("processes", []): + if "passive_interfaces" in process and process["passive_interfaces"].get("default"): + if process["passive_interfaces"].get("interface"): + temp = [] + for each in process["passive_interfaces"]["interface"]["name"]: + if each: + temp.append(each) + process["passive_interfaces"]["interface"]["name"] = temp + if "areas" in process: + process["areas"] = list(process["areas"].values()) + process["areas"] = sorted(process["areas"], key=lambda k, sk="area_id": k[sk]) + for area in process["areas"]: + if "filters" in area: + area["filters"].sort() + ipv4["processes"].append(process) + + ansible_facts["ansible_network_resources"].pop("ospfv2", None) + facts = {} + if current: + params = utils.validate_config(self.argument_spec, {"config": ipv4}) + params = utils.remove_empties(params) + + facts["ospfv2"] = params["config"] + + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv3/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv3/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv3/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv3/ospfv3.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv3/ospfv3.py new file mode 100644 index 000000000..d67a02bc6 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/ospfv3/ospfv3.py @@ -0,0 +1,170 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios ospfv3 fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import re + +from copy import deepcopy + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ospfv3.ospfv3 import ( + Ospfv3Args, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.ospfv3 import ( + Ospfv3Template, +) + + +class Ospfv3Facts(object): + """The ios ospfv3 fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Ospfv3Args.argument_spec + + def get_ospfv3_data(self, connection): + return connection.get("show running-config | section ^router ospfv3") + + def parse(self, net_template_obj): + """Overrided network template parse""" + result = {} + shared = {} + temp_pid = None + for line in net_template_obj._lines: + for parser in net_template_obj._tmplt.PARSERS: + cap = re.match(parser["getval"], line) + if cap: + capdict = cap.groupdict() + temp = {} + for k, v in iteritems(capdict): + if v is not None: + temp.update({k: v}) + capdict = temp + if "address-family" in line: + capdict.update({"id": temp_pid}) + if "manet" in line and "pid" not in shared and shared.get("unicast"): + del shared["unicast"] + + if "router ospfv3" in line: + temp_pid = None + if parser.get("shared"): + shared = capdict + if not temp_pid and (shared.get("pid") or shared.get("id")): + temp_pid = shared.get("pid") or shared.get("id") + vals = utils.dict_merge(capdict, shared) + try: + res = net_template_obj._deepformat(deepcopy(parser["result"]), vals) + except Exception: + continue + result = utils.dict_merge(result, res) + break + return result + + def parse_for_address_family(self, current): + """Parsing and Fishing out address family contents""" + pid_addr_family_dict = {} + temp_dict = {} + temp_pid = None + temp = [] + if current.get("address_family"): + for each in current.pop("address_family"): + each = utils.remove_empties(each) + if each.get("exit"): + if temp_pid == each.get("exit")["pid"]: + temp.append(temp_dict) + pid_addr_family_dict[temp_pid] = temp + temp_dict = dict() + else: + temp_pid = each.get("exit")["pid"] + pid_addr_family_dict[temp_pid] = [temp_dict] + temp = [] + temp.append(temp_dict) + temp_dict = dict() + elif each.get("manet") and temp_dict.get("manet"): + for k, v in iteritems(each.get("manet")): + if k in temp_dict.get("manet"): + temp_dict.get("manet")[k].update(v) + else: + temp_dict["manet"].update(each.get("manet")) + elif each.get("manet") and not temp_dict.get("manet"): + temp_dict["manet"] = each.get("manet") + else: + temp_dict.update(each) + return pid_addr_family_dict + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for ospfv3 + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not data: + data = self.get_ospfv3_data(connection) + + ipv4 = {"processes": []} + ospfv3_parser = NetworkTemplate( + lines=data.splitlines(), + tmplt=Ospfv3Template(), + module=self._module, + ) + current = self.parse(ospfv3_parser) + address_family = self.parse_for_address_family(current) + if address_family: + for k, v in iteritems(current["processes"]): + temp = address_family.pop(k) + v.update({"address_family": temp}) + # convert some of the dicts to lists + for key, sortv in [("processes", "process_id")]: + if key in current and current[key]: + current[key] = current[key].values() + current[key] = sorted(current[key], key=lambda k, sk=sortv: k[sk]) + + for process in current.get("processes", []): + if "areas" in process: + process["areas"] = list(process["areas"].values()) + process["areas"] = sorted(process["areas"], key=lambda k, sk="area_id": k[sk]) + for area in process["areas"]: + if "filters" in area: + area["filters"].sort() + if "address_family" in process: + for each in process["address_family"]: + if "areas" in each: + each["areas"] = list(each["areas"].values()) + each["areas"] = sorted(each["areas"], key=lambda k, sk="area_id": k[sk]) + for area in each["areas"]: + if "filters" in area: + area["filters"].sort() + ipv4["processes"].append(process) + + ansible_facts["ansible_network_resources"].pop("ospfv3", None) + facts = {} + if current: + params = ospfv3_parser.validate_config( + self.argument_spec, + {"config": ipv4}, + redact=True, + ) + params = utils.remove_empties(params) + + facts["ospfv3"] = params["config"] + + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/prefix_lists/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/prefix_lists/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/prefix_lists/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/prefix_lists/prefix_lists.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/prefix_lists/prefix_lists.py new file mode 100644 index 000000000..167f68c73 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/prefix_lists/prefix_lists.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The cisco.ios prefix_lists fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from copy import copy + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.prefix_lists.prefix_lists import ( + Prefix_listsArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.prefix_lists import ( + Prefix_listsTemplate, +) + + +class Prefix_listsFacts(object): + """The cisco.ios prefix_lists facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Prefix_listsArgs.argument_spec + + def get_prefix_list_data(self, connection): + return connection.get("show running-config | section ^ip prefix-list|^ipv6 prefix-list") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Prefix_lists network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + + if not data: + data = self.get_prefix_list_data(connection) + + # parse native config using the Prefix_lists template + prefix_lists_parser = Prefix_listsTemplate(lines=data.splitlines()) + objs = prefix_lists_parser.parse() + + final_objs = [] + temp = {} + temp["afi"] = None + temp["prefix_lists"] = [] + if objs: + for k, v in iteritems(objs): + temp_prefix_list = {} + temp_prefix_list["entries"] = [] + if not temp["afi"] or v["afi"] != temp["afi"]: + if temp and temp["afi"]: + temp["prefix_lists"] = sorted( + temp["prefix_lists"], + key=lambda k, sk="name": str(k[sk]), + ) + # additional check for py3.5 + if len(final_objs) == 2: + for each in final_objs: + if v["afi"] == each["afi"]: + each["prefix_lists"].extend(temp["prefix_lists"]) + else: + final_objs.append(copy(temp)) + temp["prefix_lists"] = [] + temp["afi"] = v["afi"] + for each in v["prefix_lists"]: + if not temp_prefix_list.get("name"): + temp_prefix_list["name"] = each["name"] + if not temp_prefix_list.get("description") and each.get("description"): + temp_prefix_list["description"] = each["description"] + if each["entries"] and not each["entries"].get("description"): + temp_prefix_list["entries"].append(each["entries"]) + temp["prefix_lists"].append(temp_prefix_list) + if temp and temp["afi"]: + temp["prefix_lists"] = sorted( + temp["prefix_lists"], + key=lambda k, sk="name": str(k[sk]), + ) + # additional check for py3.5 + if len(final_objs) == 2: + for each in final_objs: + if v["afi"] == each["afi"]: + each["prefix_lists"].extend(temp["prefix_lists"]) + else: + final_objs.append(copy(temp)) + + final_objs = sorted(final_objs, key=lambda k, sk="afi": k[sk]) + + ansible_facts["ansible_network_resources"].pop("prefix_lists", None) + + params = utils.remove_empties( + utils.validate_config(self.argument_spec, {"config": final_objs}), + ) + + facts["prefix_lists"] = params.get("config", []) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/route_maps/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/route_maps/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/route_maps/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/route_maps/route_maps.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/route_maps/route_maps.py new file mode 100644 index 000000000..030076279 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/route_maps/route_maps.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The cisco.ios route_maps fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from copy import deepcopy + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.route_maps.route_maps import ( + Route_mapsArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.route_maps import ( + Route_mapsTemplate, +) + + +class Route_mapsFacts(object): + """The cisco.ios route_maps facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Route_mapsArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_route_maps_data(self, connection): + return connection.get("show running-config | section ^route-map") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Route_maps network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + + if not data: + data = self.get_route_maps_data(connection) + # parse native config using the Route_maps template + route_maps_parser = Route_mapsTemplate(lines=data.splitlines()) + objs = route_maps_parser.parse() + + final_objs = [] + if objs: + for k, v in iteritems(objs): + temp_dict = {} + temp_dict["entries"] = [] + for key, val in iteritems(v): + if key == "route_map": + temp_dict.update({"route_map": val}) + continue + if val.get("entries"): + if val["entries"].get("match"): + if val["entries"]["match"].get("ip"): + for k_ip, v_ip in iteritems(val["entries"]["match"]["ip"]): + if v_ip.get("acls"): + if "src-pfx" in v_ip["acls"]: + v_ip["acls"].pop(v_ip["acls"].index("src-pfx")) + elif "dest-pfx" in v_ip["acls"]: + v_ip["acls"].pop(v_ip["acls"].index("dest-pfx")) + temp_dict["entries"].append(val["entries"]) + temp_dict["entries"] = sorted( + temp_dict["entries"], + key=lambda k, sk="sequence": k[sk], + ) + final_objs.append(temp_dict) + final_objs = sorted(final_objs, key=lambda k, sk="route_map": k[sk]) + + ansible_facts["ansible_network_resources"].pop("route_maps", None) + + params = utils.remove_empties( + utils.validate_config(self.argument_spec, {"config": final_objs}), + ) + + facts["route_maps"] = params["config"] + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/service/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/service/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/service/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/service/service.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/service/service.py new file mode 100644 index 000000000..df2dcf993 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/service/service.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios_service fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.service.service import ( + ServiceArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.service import ( + ServiceTemplate, +) + + +class ServiceFacts(object): + """The ios service facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = ServiceArgs.argument_spec + + def get_service_data(self, connection): + return connection.get("show running-config all | section ^service ") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Service network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + params = {} + + if not data: + data = self.get_service_data(connection) + + # parse native config using the ServiceTemplate + service_parser = ServiceTemplate(lines=data.splitlines(), module=self._module) + objs = service_parser.parse() + + ansible_facts["ansible_network_resources"].pop("service", None) + + params = utils.remove_empties( + service_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), + ) + + facts["service"] = params.get("config", {}) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/snmp_server/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/snmp_server/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/snmp_server/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/snmp_server/snmp_server.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/snmp_server/snmp_server.py new file mode 100644 index 000000000..2fc1042e7 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/snmp_server/snmp_server.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios snmp_server fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.snmp_server.snmp_server import ( + Snmp_serverArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.snmp_server import ( + Snmp_serverTemplate, +) + + +class Snmp_serverFacts(object): + """The ios snmp_server facts class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Snmp_serverArgs.argument_spec + + def get_snmp_data(self, connection): + _get_snmp_data = connection.get("show running-config | section ^snmp-server") + return _get_snmp_data + + def get_snmpv3_user_data(self, connection): + """get snmpv3 user data from the device + + :param connection: the device connection + + :rtype: string + :returns: snmpv3 user data + + Note: The seperate method is needed because the snmpv3 user data is not returned within the snmp-server config + """ + _get_snmpv3_user = connection.get("show snmp user") + return _get_snmpv3_user + + def sort_list_dicts(self, objs): + p_key = { + "hosts": "host", + "groups": "group", + "engine_id": "id", + "communities": "name", + "password_policy": "policy_name", + "users": "username", + "views": "name", + } + for k, _v in p_key.items(): + if k in objs: + objs[k] = sorted(objs[k], key=lambda _k: str(_k[p_key[k]])) + return objs + + def host_traps_string_to_list(self, hosts): + if hosts: + for element in hosts: + if element.get("traps", {}): + element["traps"] = list(element.get("traps").split()) + return hosts + + def get_snmpv3_user_facts(self, snmpv3_user): + """Parse the snmpv3_user data and return a list of users + example data- + User name: TESTU25 + Engine ID: 000000090200000000000A0B + storage-type: nonvolatile active access-list: 22 + Authentication Protocol: MD5 + Privacy Protocol: None + Group-name: TESTG + :param snmpv3_user: the snmpv3_user data which is a string + + :rtype: list + :returns: list of users + """ + user_sets = snmpv3_user.split("User ") + user_list = [] + for user_set in user_sets: + one_set = {} + lines = user_set.splitlines() + for line in lines: + if line.startswith("name"): + one_set["username"] = line.split(": ")[1] + if line.startswith("Group-name:"): + one_set["group"] = line.split(": ")[1] + if "IPv6 access-list:" in line: + one_set["acl_v6"] = line.split(": ")[-1] + if "active\taccess-list:" in line: + one_set["acl_v4"] = line.split(": ")[-1] + one_set["version"] = "v3" # defaults to version 3 data + if len(one_set): + user_list.append(one_set) + return user_list + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Snmp_server network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + params = {} + snmpv3_user = "" + + if not data: + data = self.get_snmp_data(connection) + snmpv3_user = self.get_snmpv3_user_data(connection) # gathers v3 user data + + # parse native config using the Snmp_server template + snmp_server_parser = Snmp_serverTemplate(lines=data.splitlines(), module=self._module) + # parse snmpv3_user data using the get_snmpv3_user_facts method + snmp_user_data = self.get_snmpv3_user_facts(snmpv3_user) + objs = snmp_server_parser.parse() + + # add snmpv3_user data to the objs dictionary + if snmp_user_data: + if objs.get("users") is None: + objs["users"] = snmp_user_data + else: + objs["users"] = objs["users"] + snmp_user_data + if objs: + self.host_traps_string_to_list(objs.get("hosts")) + self.sort_list_dicts(objs) + + ansible_facts["ansible_network_resources"].pop("snmp_server", None) + + params = utils.remove_empties( + snmp_server_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), + ) + + facts["snmp_server"] = params.get("config", {}) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/static_routes/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/static_routes/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/static_routes/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/static_routes/static_routes.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/static_routes/static_routes.py new file mode 100644 index 000000000..cdd6aaf45 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/static_routes/static_routes.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- +# Copyright 2023 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The ios static_routes fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.static_routes.static_routes import ( + Static_routesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.static_routes import ( + Static_routesTemplate, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import ( + netmask_to_cidr, +) + + +class Static_routesFacts(object): + """The ios static_routes facts class""" + + def __init__(self, module): + self._module = module + self.argument_spec = Static_routesArgs.argument_spec + + def get_static_routes_data(self, connection): + return connection.get("show running-config | include ^ip route .+ |^ipv6 route .+") + + def process_static_routes(self, objs): + def update_netmask_to_cidr(address, netmask): + dest = address + "/" + netmask_to_cidr(netmask) + return dest + + strout = {} + for k, obj in objs.items(): + _routes = {"next_hops": []} + _nx_hop = [] + is_vrf = False + + for routes in obj: + _vrf = routes.pop("_vrf", None) + if _vrf: + is_vrf = True + _afi = routes.pop("_afi") + _dest = routes.pop("_dest") + _topology = routes.pop("_topology", None) + _netmask = routes.pop("_netmask", None) + _routes["dest"] = ( + update_netmask_to_cidr(_dest, _netmask) if _afi == "ipv4" else _dest + ) + if _topology: + _routes["topology"] = _topology + _nx_hop.append(routes) + + _routes["next_hops"].extend(_nx_hop) + + if is_vrf: + if strout.get(_vrf) and strout[_vrf].get(_afi): + strout[_vrf][_afi].append(_routes) + else: + if strout.get(_vrf): + _tma = {_afi: [_routes]} + strout[_vrf].update(_tma) + else: + _tm = {_vrf: {_afi: [_routes]}} + strout.update(_tm) + else: + if strout.get(_afi): + strout[_afi].append(_routes) + else: + _tma = {_afi: [_routes]} + strout.update(_tma) + return strout + + def structure_static_routes(self, strout): + _static_route_facts = [] + afi_v4 = strout.pop("ipv4", None) + afi_v6 = strout.pop("ipv6", None) + + if afi_v4 or afi_v6: + _triv_static_route = {"address_families": []} + + if afi_v4: + _triv_static_route["address_families"].append({"afi": "ipv4", "routes": afi_v4}) + if afi_v6: + _triv_static_route["address_families"].append({"afi": "ipv6", "routes": afi_v6}) + + _static_route_facts.append(_triv_static_route) + + for k, v in strout.items(): + afi_v4 = v.pop("ipv4", None) + afi_v6 = v.pop("ipv6", None) + + _vrf_static_route = { + "vrf": k, + "address_families": [], + } + + if afi_v4: + _vrf_static_route["address_families"].append({"afi": "ipv4", "routes": afi_v4}) + if afi_v6: + _vrf_static_route["address_families"].append({"afi": "ipv6", "routes": afi_v6}) + + _static_route_facts.append(_vrf_static_route) + return _static_route_facts + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for Static_routes network resource + + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + + :rtype: dictionary + :returns: facts + """ + facts = {} + objs = [] + + if not data: + data = self.get_static_routes_data(connection) + + # parse native config using the Static_routes template + static_routes_parser = Static_routesTemplate(lines=data.splitlines(), module=self._module) + objs = static_routes_parser.parse() + + strout = self.process_static_routes(objs) + objs = self.structure_static_routes(strout) + + ansible_facts["ansible_network_resources"].pop("static_routes", None) + + params = utils.remove_empties( + static_routes_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), + ) + + facts["static_routes"] = params.get("config") + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/vlans.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/vlans.py new file mode 100644 index 000000000..73f73a5a2 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/vlans.py @@ -0,0 +1,239 @@ +# +# -*- 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) +""" +The ios vlans fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +from copy import deepcopy + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vlans.vlans import ( + VlansArgs, +) + + +class VlansFacts(object): + """The ios vlans fact class""" + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = VlansArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def get_vlans_data(self, connection): + """Checks device is L2/L3 and returns + facts gracefully. Does not fail module. + """ + check_os_type = connection.get_device_info() + if check_os_type.get("network_os_type") == "L3": + return "" + return connection.get("show vlan") + + def populate_facts(self, connection, ansible_facts, data=None): + """Populate the facts for vlans + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + + objs = [] + mtu_objs = [] + remote_objs = [] + final_objs = [] + pvlan_objs = [] + if not data: + data = self.get_vlans_data(connection) + # operate on a collection of resource x + config = data.split("\n") + # Get individual vlan configs separately + vlan_info = "" + temp = "" + vlan_name = True + for conf in config: + if len(list(filter(None, conf.split(" ")))) <= 2 and vlan_name: + temp = temp + conf + if len(list(filter(None, temp.split(" ")))) <= 2: + continue + if "VLAN Name" in conf: + vlan_info = "Name" + elif "VLAN Type" in conf: + vlan_info = "Type" + vlan_name = False + elif "Remote SPAN" in conf: + vlan_info = "Remote" + vlan_name = False + elif "VLAN AREHops" in conf or "STEHops" in conf: + vlan_info = "Hops" + vlan_name = False + elif "Primary Secondary" in conf: + vlan_info = "Private" + vlan_name = False + if temp: + conf = temp + temp = "" + if conf and " " not in filter(None, conf.split("-")) and not conf.split(" ")[0] == "": + obj = self.render_config(self.generated_spec, conf, vlan_info) + if "mtu" in obj: + mtu_objs.append(obj) + elif "remote_span" in obj: + remote_objs = obj + elif "tmp_pvlans" in obj: + pvlan_objs.append(obj) + elif obj: + objs.append(obj) + # Appending MTU value to the retrieved dictionary + for o, m in zip(objs, mtu_objs): + o.update(m) + final_objs.append(o) + + # Appending Remote Span value to related VLAN + if remote_objs: + if remote_objs.get("remote_span"): + for each in remote_objs.get("remote_span"): + for every in final_objs: + if each == every.get("vlan_id"): + every.update({"remote_span": True}) + break + + # Appending private vlan information to related VLAN + if pvlan_objs: + pvlan_final = {} + if len(pvlan_objs) > 0: + # Sanitize and structure everything + for data in pvlan_objs: + pvdata = data.get("tmp_pvlans") + privlan = pvdata.get("primary") + secvlan = pvdata.get("secondary") + sectype = pvdata.get("sec_type") + + # Define the secondary VLAN's type + if secvlan and (isinstance(secvlan, int) or secvlan.isnumeric()): + secvlan = int(secvlan) + pvlan_final[secvlan] = {"private_vlan": {"type": sectype}} + + # Assemble and merge data for primary private VLANs + if privlan and (isinstance(privlan, int) or privlan.isnumeric()): + privlan = int(privlan) + if privlan not in pvlan_final.keys(): + pvlan_final[privlan] = { + "private_vlan": {"type": "primary", "associated": []}, + } + if secvlan and (isinstance(secvlan, int) or secvlan.isnumeric()): + pvlan_final[privlan]["private_vlan"]["associated"].append(int(secvlan)) + + # Associate with the proper VLAN in final_objs + for vlan_id, data in pvlan_final.items(): + for every in final_objs: + if vlan_id == every.get("vlan_id"): + every.update(data) + + facts = {} + if final_objs: + facts["vlans"] = [] + params = utils.validate_config(self.argument_spec, {"config": objs}) + + for cfg in params["config"]: + facts["vlans"].append(utils.remove_empties(cfg)) + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def render_config(self, spec, conf, vlan_info): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + config = deepcopy(spec) + + if vlan_info == "Name" and "VLAN Name" not in conf: + conf = list(filter(None, conf.split(" "))) + config["vlan_id"] = int(conf[0]) + config["name"] = conf[1] + state_idx = 2 + for i in range(2, len(conf)): # check for index where state starts + if conf[i] in ["suspended", "active"]: + state_idx = i + break + elif conf[i].split("/")[0] in ["sus", "act"]: + state_idx = i + break + config["name"] += " " + conf[i] + try: + if len(conf[state_idx].split("/")) > 1: + _state = conf[state_idx].split("/")[0] + if _state == "sus": + config["state"] = "suspend" + elif _state == "act": + config["state"] = "active" + config["shutdown"] = "enabled" + else: + if conf[state_idx] == "suspended": + config["state"] = "suspend" + elif conf[state_idx] == "active": + config["state"] = "active" + config["shutdown"] = "disabled" + except IndexError: + pass + elif vlan_info == "Type" and "VLAN Type" not in conf: + conf = list(filter(None, conf.split(" "))) + config["mtu"] = int(conf[3]) + elif vlan_info == "Remote": + if len(conf.split(",")) > 1 or conf.isdigit(): + remote_span_vlan = [] + if len(conf.split(",")) > 1: + remote_span_vlan = conf.split(",") + else: + remote_span_vlan.append(conf) + remote_span = [] + for each in remote_span_vlan: + split_sp_list = each.split("-") + if len(split_sp_list) > 1: # break range + for r_sp in range(int(split_sp_list[0]), int(split_sp_list[1]) + 1): + remote_span.append(r_sp) + else: + remote_span.append(int(each)) + config["remote_span"] = remote_span + + elif vlan_info == "Private" and "Primary Secondary" not in conf: + conf = list(filter(None, conf.split(" "))) + + pri_idx = 0 + sec_idx = 1 + priv_type_idx = 2 + + config["tmp_pvlans"] = { + "primary": conf[pri_idx], + "secondary": conf[sec_idx], + "sec_type": conf[priv_type_idx], + } + + return utils.remove_empties(config) diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/ios.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/ios.py new file mode 100644 index 000000000..9ed7a1f27 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/ios.py @@ -0,0 +1,174 @@ +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. +# +# (c) 2016 Red Hat Inc. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type +import json + +from ansible.module_utils._text import to_text +from ansible.module_utils.connection import Connection, ConnectionError +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + + +_DEVICE_CONFIGS = {} + + +def get_connection(module): + if hasattr(module, "_ios_connection"): + return module._ios_connection + + capabilities = get_capabilities(module) + network_api = capabilities.get("network_api") + if network_api == "cliconf": + module._ios_connection = Connection(module._socket_path) + else: + module.fail_json(msg="Invalid connection type %s" % network_api) + + return module._ios_connection + + +def get_capabilities(module): + if hasattr(module, "_ios_capabilities"): + return module._ios_capabilities + try: + capabilities = Connection(module._socket_path).get_capabilities() + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + module._ios_capabilities = json.loads(capabilities) + return module._ios_capabilities + + +def get_defaults_flag(module): + connection = get_connection(module) + try: + out = connection.get_defaults_flag() + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + return to_text(out, errors="surrogate_then_replace").strip() + + +def get_config(module, flags=None): + flags = to_list(flags) + + section_filter = False + if flags and "section" in flags[-1]: + section_filter = True + + flag_str = " ".join(flags) + + try: + return _DEVICE_CONFIGS[flag_str] + except KeyError: + connection = get_connection(module) + try: + out = connection.get_config(flags=flags) + except ConnectionError as exc: + if section_filter: + # Some ios devices don't understand `| section foo` + out = get_config(module, flags=flags[:-1]) + else: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + cfg = to_text(out, errors="surrogate_then_replace").strip() + _DEVICE_CONFIGS[flag_str] = cfg + return cfg + + +def run_commands(module, commands, check_rc=True): + connection = get_connection(module) + try: + return connection.run_commands(commands=commands, check_rc=check_rc) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc)) + + +def load_config(module, commands): + connection = get_connection(module) + + try: + resp = connection.edit_config(commands) + return resp.get("response") + except ConnectionError as exc: + module.fail_json(msg=to_text(exc)) + + +def normalize_interface(name): + """Return the normalized interface name""" + if not name: + return + + def _get_number(name): + digits = "" + for char in name: + if char.isdigit() or char in "/.": + digits += char + return digits + + if name.lower().startswith("gi"): + if_type = "GigabitEthernet" + elif name.lower().startswith("twe"): + if_type = "TwentyFiveGigE" + elif name.lower().startswith("tw"): + if_type = "TwoGigabitEthernet" + elif name.lower().startswith("te"): + if_type = "TenGigabitEthernet" + elif name.lower().startswith("fa"): + if_type = "FastEthernet" + elif name.lower().startswith("fo"): + if_type = "FortyGigabitEthernet" + elif name.lower().startswith("fi"): + if_type = "FiveGigabitEthernet" + elif name.lower().startswith("et"): + if_type = "Ethernet" + elif name.lower().startswith("vl"): + if_type = "Vlan" + elif name.lower().startswith("lo"): + if_type = "loopback" + elif name.lower().startswith("po"): + if_type = "port-channel" + elif name.lower().startswith("nv"): + if_type = "nve" + elif name.lower().startswith("hu"): + if_type = "HundredGigE" + elif name.lower().startswith("se"): + if_type = "Serial" + else: + if_type = None + + number_list = name.split(" ") + if len(number_list) == 2: + if_number = number_list[-1].strip() + else: + if_number = _get_number(name) + + if if_type: + proper_interface = if_type + if_number + else: + proper_interface = name + + return proper_interface diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/base.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/base.py new file mode 100644 index 000000000..f1b9cdb90 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/base.py @@ -0,0 +1,82 @@ +# +# (c) 2019, Ansible by Red Hat, inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( + NetworkConfig, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + + +class ConfigBase(object): + argument_spec = {} + + mutually_exclusive = [] + + identifier = () + + def __init__(self, **kwargs): + self.values = {} + self._rendered_configuration = {} + self.active_configuration = None + + for item in self.identifier: + self.values[item] = kwargs.pop(item) + + for key, value in iteritems(kwargs): + if key in self.argument_spec: + setattr(self, key, value) + + for key, value in iteritems(self.argument_spec): + if value.get("default"): + if not getattr(self, key, None): + setattr(self, key, value.get("default")) + + def __getattr__(self, key): + if key in self.argument_spec: + return self.values.get(key) + + def __setattr__(self, key, value): + if key in self.argument_spec: + if key in self.identifier: + raise TypeError("cannot set value") + elif value is not None: + self.values[key] = value + else: + super(ConfigBase, self).__setattr__(key, value) + + def context_config(self, cmd): + if "context" not in self._rendered_configuration: + self._rendered_configuration["context"] = list() + self._rendered_configuration["context"].extend(to_list(cmd)) + + def global_config(self, cmd): + if "global" not in self._rendered_configuration: + self._rendered_configuration["global"] = list() + self._rendered_configuration["global"].extend(to_list(cmd)) + + def get_rendered_configuration(self): + config = list() + for section in ("context", "global"): + config.extend(self._rendered_configuration.get(section, [])) + return config + + def set_active_configuration(self, config): + self.active_configuration = config + + def render(self, config=None): + raise NotImplementedError + + def get_section(self, config, section): + if config is not None: + netcfg = NetworkConfig(indent=1, contents=config) + try: + config = netcfg.get_block_config(to_list(section)) + except ValueError: + config = None + return config diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/address_family.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/address_family.py new file mode 100644 index 000000000..e4c2bd803 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/address_family.py @@ -0,0 +1,147 @@ +# +# (c) 2019, Ansible by Red Hat, inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type +import re + +from ansible.module_utils.common.network import to_netmask +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.cli.config.bgp.neighbors import ( + AFNeighbors, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.providers import ( + CliProvider, +) + + +class AddressFamily(CliProvider): + def render(self, config=None): + commands = list() + safe_list = list() + + router_context = "router bgp %s" % self.get_value("config.bgp_as") + context_config = None + + for item in self.get_value("config.address_family"): + context = "address-family %s" % item["afi"] + if item["safi"] != "unicast": + context += " %s" % item["safi"] + context_commands = list() + + if config: + context_path = [router_context, context] + context_config = self.get_config_context(config, context_path, indent=1) + + for key, value in iteritems(item): + if value is not None: + meth = getattr(self, "_render_%s" % key, None) + if meth: + resp = meth(item, context_config) + if resp: + context_commands.extend(to_list(resp)) + + if context_commands: + commands.append(context) + commands.extend(context_commands) + commands.append("exit-address-family") + + safe_list.append(context) + + if self.params["operation"] == "replace": + if config: + resp = self._negate_config(config, safe_list) + commands.extend(resp) + + return commands + + def _negate_config(self, config, safe_list=None): + commands = list() + matches = re.findall(r"(address-family .+)$", config, re.M) + for item in set(matches).difference(safe_list): + commands.append("no %s" % item) + return commands + + def _render_auto_summary(self, item, config=None): + cmd = "auto-summary" + if item["auto_summary"] is False: + cmd = "no %s" % cmd + if not config or cmd not in config: + return cmd + + def _render_synchronization(self, item, config=None): + cmd = "synchronization" + if item["synchronization"] is False: + cmd = "no %s" % cmd + if not config or cmd not in config: + return cmd + + def _render_networks(self, item, config=None): + commands = list() + safe_list = list() + + for entry in item["networks"]: + network = entry["prefix"] + cmd = "network %s" % network + if entry["masklen"]: + cmd += " mask %s" % to_netmask(entry["masklen"]) + network += " mask %s" % to_netmask(entry["masklen"]) + if entry["route_map"]: + cmd += " route-map %s" % entry["route_map"] + network += " route-map %s" % entry["route_map"] + + safe_list.append(network) + + if not config or cmd not in config: + commands.append(cmd) + + if self.params["operation"] == "replace": + if config: + matches = re.findall(r"network (.*)", config, re.M) + for entry in set(matches).difference(safe_list): + commands.append("no network %s" % entry) + + return commands + + def _render_redistribute(self, item, config=None): + commands = list() + safe_list = list() + + for entry in item["redistribute"]: + option = entry["protocol"] + + cmd = "redistribute %s" % entry["protocol"] + + if entry["id"] and entry["protocol"] in ("ospf", "ospfv3", "eigrp"): + cmd += " %s" % entry["id"] + option += " %s" % entry["id"] + + if entry["metric"]: + cmd += " metric %s" % entry["metric"] + + if entry["route_map"]: + cmd += " route-map %s" % entry["route_map"] + + if not config or cmd not in config: + commands.append(cmd) + + safe_list.append(option) + + if self.params["operation"] == "replace": + if config: + matches = re.findall(r"redistribute (\S+)(?:\s*)(\d*)", config, re.M) + for i in range(0, len(matches)): + matches[i] = " ".join(matches[i]).strip() + for entry in set(matches).difference(safe_list): + commands.append("no redistribute %s" % entry) + + return commands + + def _render_neighbors(self, item, config): + """generate bgp neighbor configuration""" + return AFNeighbors(self.params).render(config, nbr_list=item["neighbors"]) diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/neighbors.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/neighbors.py new file mode 100644 index 000000000..4ee337b00 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/neighbors.py @@ -0,0 +1,203 @@ +# +# (c) 2019, Ansible by Red Hat, inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type +import re + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.providers import ( + CliProvider, +) + + +class Neighbors(CliProvider): + def render(self, config=None, nbr_list=None): + commands = list() + safe_list = list() + if not nbr_list: + nbr_list = self.get_value("config.neighbors") + + for item in nbr_list: + neighbor_commands = list() + context = "neighbor %s" % item["neighbor"] + cmd = "%s remote-as %s" % (context, item["remote_as"]) + + if not config or cmd not in config: + neighbor_commands.append(cmd) + + for key, value in iteritems(item): + if value is not None: + meth = getattr(self, "_render_%s" % key, None) + if meth: + resp = meth(item, config) + if resp: + neighbor_commands.extend(to_list(resp)) + + commands.extend(neighbor_commands) + safe_list.append(context) + + if self.params["operation"] == "replace": + if config and safe_list: + commands.extend(self._negate_config(config, safe_list)) + + return commands + + def _negate_config(self, config, safe_list=None): + commands = list() + matches = re.findall(r"(neighbor \S+)", config, re.M) + for item in set(matches).difference(safe_list): + commands.append("no %s" % item) + return commands + + def _render_local_as(self, item, config=None): + cmd = "neighbor %s local-as %s" % (item["neighbor"], item["local_as"]) + if not config or cmd not in config: + return cmd + + def _render_port(self, item, config=None): + cmd = "neighbor %s port %s" % (item["neighbor"], item["port"]) + if not config or cmd not in config: + return cmd + + def _render_description(self, item, config=None): + cmd = "neighbor %s description %s" % (item["neighbor"], item["description"]) + if not config or cmd not in config: + return cmd + + def _render_enabled(self, item, config=None): + cmd = "neighbor %s shutdown" % item["neighbor"] + if item["enabled"] is True: + if not config or cmd in config: + cmd = "no %s" % cmd + return cmd + elif not config or cmd not in config: + return cmd + + def _render_update_source(self, item, config=None): + cmd = "neighbor %s update-source %s" % (item["neighbor"], item["update_source"]) + if not config or cmd not in config: + return cmd + + def _render_password(self, item, config=None): + cmd = "neighbor %s password %s" % (item["neighbor"], item["password"]) + if not config or cmd not in config: + return cmd + + def _render_ebgp_multihop(self, item, config=None): + cmd = "neighbor %s ebgp-multihop %s" % (item["neighbor"], item["ebgp_multihop"]) + if not config or cmd not in config: + return cmd + + def _render_peer_group(self, item, config=None): + cmd = "neighbor %s peer-group %s" % (item["neighbor"], item["peer_group"]) + if not config or cmd not in config: + return cmd + + def _render_timers(self, item, config): + """generate bgp timer related configuration""" + keepalive = item["timers"]["keepalive"] + holdtime = item["timers"]["holdtime"] + min_neighbor_holdtime = item["timers"]["min_neighbor_holdtime"] + neighbor = item["neighbor"] + + if keepalive and holdtime: + cmd = "neighbor %s timers %s %s" % (neighbor, keepalive, holdtime) + if min_neighbor_holdtime: + cmd += " %s" % min_neighbor_holdtime + if not config or cmd not in config: + return cmd + + +class AFNeighbors(CliProvider): + def render(self, config=None, nbr_list=None): + commands = list() + if not nbr_list: + return + + for item in nbr_list: + neighbor_commands = list() + for key, value in iteritems(item): + if value is not None: + meth = getattr(self, "_render_%s" % key, None) + if meth: + resp = meth(item, config) + if resp: + neighbor_commands.extend(to_list(resp)) + + commands.extend(neighbor_commands) + + return commands + + def _render_advertisement_interval(self, item, config=None): + cmd = "neighbor %s advertisement-interval %s" % ( + item["neighbor"], + item["advertisement_interval"], + ) + if not config or cmd not in config: + return cmd + + def _render_route_reflector_client(self, item, config=None): + cmd = "neighbor %s route-reflector-client" % item["neighbor"] + if item["route_reflector_client"] is False: + if not config or cmd in config: + cmd = "no %s" % cmd + return cmd + elif not config or cmd not in config: + return cmd + + def _render_route_server_client(self, item, config=None): + cmd = "neighbor %s route-server-client" % item["neighbor"] + if item["route_server_client"] is False: + if not config or cmd in config: + cmd = "no %s" % cmd + return cmd + elif not config or cmd not in config: + return cmd + + def _render_remove_private_as(self, item, config=None): + cmd = "neighbor %s remove-private-as" % item["neighbor"] + if item["remove_private_as"] is False: + if not config or cmd in config: + cmd = "no %s" % cmd + return cmd + elif not config or cmd not in config: + return cmd + + def _render_next_hop_self(self, item, config=None): + cmd = "neighbor %s next-hop-self" % item["neighbor"] + if item["next_hop_self"] is False: + if not config or cmd in config: + cmd = "no %s" % cmd + return cmd + elif not config or cmd not in config: + return cmd + + def _render_activate(self, item, config=None): + cmd = "neighbor %s activate" % item["neighbor"] + if item["activate"] is False: + if not config or cmd in config: + cmd = "no %s" % cmd + return cmd + elif not config or cmd not in config: + return cmd + + def _render_maximum_prefix(self, item, config=None): + cmd = "neighbor %s maximum-prefix %s" % (item["neighbor"], item["maximum_prefix"]) + if not config or cmd not in config: + return cmd + + def _render_prefix_list_in(self, item, config=None): + cmd = "neighbor %s prefix-list %s in" % (item["neighbor"], item["prefix_list_in"]) + if not config or cmd not in config: + return cmd + + def _render_prefix_list_out(self, item, config=None): + cmd = "neighbor %s prefix-list %s out" % (item["neighbor"], item["prefix_list_out"]) + if not config or cmd not in config: + return cmd diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/process.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/process.py new file mode 100644 index 000000000..2b54daa35 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/process.py @@ -0,0 +1,163 @@ +# +# (c) 2019, Ansible by Red Hat, inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type +import re + +from ansible.module_utils.common.network import to_netmask +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.cli.config.bgp.address_family import ( + AddressFamily, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.cli.config.bgp.neighbors import ( + Neighbors, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.providers import ( + CliProvider, + register_provider, +) + + +REDISTRIBUTE_PROTOCOLS = [ + "ospf", + "ospfv3", + "eigrp", + "isis", + "static", + "connected", + "odr", + "lisp", + "mobile", + "rip", +] + + +@register_provider("ios", "ios_bgp") +class Provider(CliProvider): + def render(self, config=None): + commands = list() + + existing_as = None + if config: + match = re.search(r"router bgp (\d+)", config, re.M) + if match: + existing_as = match.group(1) + + operation = self.params["operation"] + + context = None + if self.params["config"]: + context = "router bgp %s" % self.get_value("config.bgp_as") + + if operation == "delete": + if existing_as: + commands.append("no router bgp %s" % existing_as) + elif context: + commands.append("no %s" % context) + + else: + self._validate_input(config) + if operation == "replace": + if existing_as and int(existing_as) != self.get_value("config.bgp_as"): + commands.append("no router bgp %s" % existing_as) + config = None + + elif operation == "override": + if existing_as: + commands.append("no router bgp %s" % existing_as) + config = None + + context_commands = list() + + for key, value in iteritems(self.get_value("config")): + if value is not None: + meth = getattr(self, "_render_%s" % key, None) + if meth: + resp = meth(config) + if resp: + context_commands.extend(to_list(resp)) + + if context and context_commands: + commands.append(context) + commands.extend(context_commands) + commands.append("exit") + return commands + + def _render_router_id(self, config=None): + cmd = "bgp router-id %s" % self.get_value("config.router_id") + if not config or cmd not in config: + return cmd + + def _render_log_neighbor_changes(self, config=None): + cmd = "bgp log-neighbor-changes" + log_neighbor_changes = self.get_value("config.log_neighbor_changes") + if log_neighbor_changes is True: + if not config or cmd not in config: + return cmd + elif log_neighbor_changes is False: + if config and cmd in config: + return "no %s" % cmd + + def _render_networks(self, config=None): + commands = list() + safe_list = list() + + for entry in self.get_value("config.networks"): + network = entry["prefix"] + cmd = "network %s" % network + if entry["masklen"] and entry["masklen"] not in (24, 16, 8): + cmd += " mask %s" % to_netmask(entry["masklen"]) + network += " mask %s" % to_netmask(entry["masklen"]) + + if entry["route_map"]: + cmd += " route-map %s" % entry["route_map"] + network += " route-map %s" % entry["route_map"] + + safe_list.append(network) + + if not config or cmd not in config: + commands.append(cmd) + + if self.params["operation"] == "replace": + if config: + matches = re.findall(r"network (.*)", config, re.M) + for entry in set(matches).difference(safe_list): + commands.append("no network %s" % entry) + + return commands + + def _render_neighbors(self, config): + """generate bgp neighbor configuration""" + return Neighbors(self.params).render(config) + + def _render_address_family(self, config): + """generate address-family configuration""" + return AddressFamily(self.params).render(config) + + def _validate_input(self, config=None): + def device_has_AF(config): + return re.search(r"address-family (?:.*)", config) + + address_family = self.get_value("config.address_family") + root_networks = self.get_value("config.networks") + operation = self.params["operation"] + + if operation == "replace": + if address_family and root_networks: + for item in address_family: + if item["networks"]: + raise ValueError( + "operation is replace but provided both root level network(s) and network(s) under %s %s address family" + % (item["afi"], item["safi"]), + ) + + if root_networks and config and device_has_AF(config): + raise ValueError( + "operation is replace and device has one or more address family activated but root level network(s) provided", + ) diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/module.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/module.py new file mode 100644 index 000000000..4de464795 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/module.py @@ -0,0 +1,66 @@ +# +# (c) 2019, Ansible by Red Hat, inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import Connection + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers import providers + + +class NetworkModule(AnsibleModule): + fail_on_missing_provider = True + + def __init__(self, connection=None, *args, **kwargs): + super(NetworkModule, self).__init__(*args, **kwargs) + + if connection is None: + connection = Connection(self._socket_path) + + self.connection = connection + + @property + def provider(self): + if not hasattr(self, "_provider"): + capabilities = self.from_json(self.connection.get_capabilities()) + + network_os = capabilities["device_info"]["network_os"] + network_api = capabilities["network_api"] + + if network_api == "cliconf": + connection_type = "network_cli" + + cls = providers.get(network_os, self._name.split(".")[-1], connection_type) + + if not cls: + msg = "unable to find suitable provider for network os %s" % network_os + if self.fail_on_missing_provider: + self.fail_json(msg=msg) + else: + self.warn(msg) + + obj = cls(self.params, self.connection, self.check_mode) + + setattr(self, "_provider", obj) + + return getattr(self, "_provider") + + def get_facts(self, subset=None): + try: + self.provider.get_facts(subset) + except Exception as exc: + self.fail_json(msg=to_text(exc)) + + def edit_config(self, config_filter=None): + current_config = self.connection.get_config(flags=config_filter) + try: + commands = self.provider.edit_config(current_config) + changed = bool(commands) + return {"commands": commands, "changed": changed} + except Exception as exc: + self.fail_json(msg=to_text(exc)) diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/providers.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/providers.py new file mode 100644 index 000000000..b422f745f --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/providers.py @@ -0,0 +1,125 @@ +# +# (c) 2019, Ansible by Red Hat, inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type +import json + +from threading import RLock + +from ansible.module_utils.six import itervalues +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( + NetworkConfig, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list + + +_registered_providers = {} +_provider_lock = RLock() + + +def register_provider(network_os, module_name): + def wrapper(cls): + _provider_lock.acquire() + try: + if network_os not in _registered_providers: + _registered_providers[network_os] = {} + for ct in cls.supported_connections: + if ct not in _registered_providers[network_os]: + _registered_providers[network_os][ct] = {} + for item in to_list(module_name): + for entry in itervalues(_registered_providers[network_os]): + entry[item] = cls + finally: + _provider_lock.release() + return cls + + return wrapper + + +def get(network_os, module_name, connection_type): + network_os_providers = _registered_providers.get(network_os) + if network_os_providers is None: + raise ValueError("unable to find a suitable provider for this module") + if connection_type not in network_os_providers: + raise ValueError("provider does not support this connection type") + elif module_name not in network_os_providers[connection_type]: + raise ValueError("could not find a suitable provider for this module") + return network_os_providers[connection_type][module_name] + + +class ProviderBase(object): + supported_connections = () + + def __init__(self, params, connection=None, check_mode=False): + self.params = params + self.connection = connection + self.check_mode = check_mode + + @property + def capabilities(self): + if not hasattr(self, "_capabilities"): + resp = self.from_json(self.connection.get_capabilities()) + setattr(self, "_capabilities", resp) + return getattr(self, "_capabilities") + + def get_value(self, path): + params = self.params.copy() + for key in path.split("."): + params = params[key] + return params + + def get_facts(self, subset=None): + raise NotImplementedError(self.__class__.__name__) + + def edit_config(self): + raise NotImplementedError(self.__class__.__name__) + + +class CliProvider(ProviderBase): + supported_connections = ("network_cli",) + + @property + def capabilities(self): + if not hasattr(self, "_capabilities"): + resp = self.from_json(self.connection.get_capabilities()) + setattr(self, "_capabilities", resp) + return getattr(self, "_capabilities") + + def get_config_context(self, config, path, indent=1): + if config is not None: + netcfg = NetworkConfig(indent=indent, contents=config) + try: + config = netcfg.get_block_config(to_list(path)) + except ValueError: + config = None + return config + + def render(self, config=None): + raise NotImplementedError(self.__class__.__name__) + + def cli(self, command): + try: + if not hasattr(self, "_command_output"): + setattr(self, "_command_output", {}) + return self._command_output[command] + except KeyError: + out = self.connection.get(command) + try: + out = json.loads(out) + except ValueError: + pass + self._command_output[command] = out + return out + + def get_facts(self, subset=None): + return self.populate() + + def edit_config(self, config=None): + commands = self.render(config) + if commands and self.check_mode is False: + self.connection.edit_config(commands) + return commands diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/acl_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/acl_interfaces.py new file mode 100644 index 000000000..9ae0dbe50 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/acl_interfaces.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Acl_interfaces parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +class Acl_interfacesTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Acl_interfacesTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + # fmt: off + PARSERS = [ + { + 'name': 'interface', + 'getval': re.compile( + r''' + ^interface\s + (?P<name>\S+)$''', re.VERBOSE, + ), + 'setval': 'interface {{ name }}', + 'result': { + '{{ name }}': { + 'name': '{{ name }}', + 'access_groups': {}, + }, + }, + 'shared': True, + }, + { + "name": "access_groups", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6) + (\saccess-group\s(?P<acl_name>\S+))? + (\straffic-filter\s(?P<acl_name_traffic>\S+))? + \s(?P<direction>\S+)$ + """, + re.VERBOSE, + ), + "setval": "{{ 'ip access-group' if afi == 'ipv4' else 'ipv6 traffic-filter' }} {{ name|string }} {{ direction }}", + "result": { + "{{ name }}": { + "access_groups": { + "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "acls": [ + { + "name": "{{ acl_name|string if acl_name is defined else acl_name_traffic }}", + "direction": "{{ direction }}", + }, + ], + }, + }, + }, + }, + }, + ] + # fmt: on diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/acls.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/acls.py new file mode 100644 index 000000000..164c93caf --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/acls.py @@ -0,0 +1,372 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The acls parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +def _tmplt_access_list_entries(aces): + def source_destination_common_config(config_data, command, attr): + if config_data[attr].get("address"): + command += " {address}".format(**config_data[attr]) + if config_data[attr].get("wildcard_bits"): + command += " {wildcard_bits}".format(**config_data[attr]) + elif config_data[attr].get("any"): + command += " any".format(**config_data[attr]) + elif config_data[attr].get("host"): + command += " host {host}".format(**config_data[attr]) + elif config_data[attr].get("object_group"): + command += " object-group {object_group}".format(**config_data[attr]) + if config_data[attr].get("port_protocol"): + if config_data[attr].get("port_protocol").get("range"): + command += " range {0} {1}".format( + config_data[attr]["port_protocol"]["range"].get("start"), + config_data[attr]["port_protocol"]["range"].get("end"), + ) + else: + port_proto_type = list(config_data[attr]["port_protocol"].keys())[0] + command += " {0} {1}".format( + port_proto_type, + config_data[attr]["port_protocol"][port_proto_type], + ) + return command + + command = "" + proto_option = None + if aces: + if aces.get("sequence") and aces.get("afi") == "ipv4": + command += "{sequence}".format(**aces) + if aces.get("grant") and aces.get("sequence") and aces.get("afi") == "ipv4": + command += " {grant}".format(**aces) + elif aces.get("grant") and aces.get("sequence") and aces.get("afi") == "ipv6": + command += "{grant}".format(**aces) + elif aces.get("grant"): + command += "{grant}".format(**aces) + if aces.get("protocol_options"): + if "protocol_number" in aces["protocol_options"]: + command += " {protocol_number}".format(**aces["protocol_options"]) + else: + command += " {0}".format(list(aces["protocol_options"])[0]) + proto_option = aces["protocol_options"].get(list(aces["protocol_options"])[0]) + elif aces.get("protocol"): + command += " {protocol}".format(**aces) + if aces.get("source"): + command = source_destination_common_config(aces, command, "source") + if aces.get("destination"): + command = source_destination_common_config(aces, command, "destination") + if isinstance(proto_option, dict): + command += " {0}".format(list(proto_option.keys())[0].replace("_", "-")) + if aces.get("dscp"): + command += " dscp {dscp}".format(**aces) + if aces.get("sequence") and aces.get("afi") == "ipv6": + command += " sequence {sequence}".format(**aces) + if aces.get("enable_fragments") or aces.get("fragments"): + command += " fragments" + if aces.get("log"): + command += " log" + if aces["log"].get("user_cookie"): + command += " {user_cookie}".format(**aces["log"]) + if aces.get("log_input"): + command += " log-input" + if aces["log_input"].get("user_cookie"): + command += " {user_cookie}".format(**aces["log_input"]) + if aces.get("option"): + option_val = list(aces.get("option").keys())[0] + command += " option {0}".format(option_val) + if aces.get("precedence"): + command += " precedence {precedence}".format(**aces) + if aces.get("time_range"): + command += " time-range {time_range}".format(**aces) + if aces.get("tos"): + command += " tos" + if aces["tos"].get("service_value"): + command += " {service_value}".format(**aces["tos"]) + elif aces["tos"].get("max_reliability"): + command += " max-reliability" + elif aces["tos"].get("max_throughput"): + command += " max-throughput" + elif aces["tos"].get("min_delay"): + command += " min-delay" + elif aces["tos"].get("min_monetary_cost"): + command += " min-monetary-cost" + elif aces["tos"].get("normal"): + command += " normal" + if aces.get("ttl"): + command += " ttl {0}".format(list(aces["ttl"])[0]) + proto_option = aces["ttl"].get(list(aces["ttl"])[0]) + command += " {0}".format(proto_option) + return command + + +class AclsTemplate(NetworkTemplate): + def __init__(self, lines=None): + super(AclsTemplate, self).__init__(lines=lines, tmplt=self) + + PARSERS = [ + { + "name": "acls_name", + "getval": re.compile( + r"""^(?P<acl_type>Standard|Extended|Reflexive)* + \s*(?P<afi>IP|IPv6)* + \s*access* + \s*list* + \s*(?P<acl_name>.+)* + $""", + re.VERBOSE, + ), + "compval": "name", + "setval": "name", + "result": { + "acls": { + "{{ acl_name|d() }}": { + "name": "{{ acl_name }}", + "acl_type": "{{ acl_type.lower() if acl_type is defined }}", + "afi": "{{ 'ipv4' if afi == 'IP' else 'ipv6' }}", + }, + }, + }, + "shared": True, + }, + { + "name": "_acls_name", + "getval": re.compile( + r"""^(ip|ipv6) + (\s(access-list)) + (\s(standard|extended)) + (\s(?P<acl_name_r>\S+))? + $""", + re.VERBOSE, + ), + "compval": "name", + "setval": "ip access-list", + "result": {}, + "shared": True, + }, + { + "name": "_mac_acls_name", # + "getval": re.compile( + r"""^(?P<acl_type>Standard|Extended|Reflexive)* + \s*(?P<afi>MAC)* + \s*access + \s*list* + \s*(?P<acl_name>.+)* + $""", + re.VERBOSE, + ), + "compval": "name", + "setval": "", + "result": { + "acls": { + "{{ acl_name|d() }}": { + "name": "{{ acl_name }}", + "acl_type": "{{ acl_type.lower() if acl_type is defined }}", + "afi": "{{ afi }}", + }, + }, + }, + "shared": True, + }, + { + "name": "remarks", + "getval": re.compile( + r"""\s+remark + (\s(?P<remarks>.+))? + $""", + re.VERBOSE, + ), + "setval": "remark {{ remarks }}", + "result": { + "acls": { + "{{ acl_name_r|d() }}": { + "name": "{{ acl_name_r }}", + "aces": [{"remarks": "{{ remarks }}"}], + }, + }, + }, + }, + { + "name": "remarks_type_linear", + "getval": re.compile( + r"""^(access-list) + (\s(?P<acl_name_linear>\S+))? + (\sremark\s(?P<remarks>.+))? + $""", + re.VERBOSE, + ), + "setval": "remark {{ remarks }}", + "result": { + "acls": { + "{{ acl_name_linear|d() }}": { + "name": "{{ acl_name_linear }}", + "aces": [{"remarks": "{{ remarks }}"}], + }, + }, + }, + }, + { + "name": "aces_ipv4_standard", + "getval": re.compile( + r"""\s*(?P<sequence>\d+)* + \s(?P<grant>deny|permit)? + (\s+(?P<address>(?!ahp|eigrp|esp|gre|icmp|igmp|ipv6|ipinip|ip|nos|object-group|ospf|pcp|pim|sctp|tcp|udp)\S+|\S+,))? + (\s*(?P<any>any))? + (\swildcard\sbits\s(?P<wildcard>\S+))? + (\shost\s(?P<host>\S+))? + (\s(?P<log>log))? + $""", + re.VERBOSE, + ), + "compval": "aces", + "result": { + "acls": { + "{{ acl_name|d() }}": { + "name": "{{ acl_name }}", + "aces": [ + { + "sequence": "{{ sequence }}", + "grant": "{{ grant }}", + "source": { + "address": "{{ address }}", + "wildcard_bits": "{{ wildcard }}", + "any": "{{ not not any }}", + "host": "{{ host }}", + }, + "log": {"set": "{{ not not log }}"}, + }, + ], + }, + }, + }, + }, + { + "name": "aces", + "getval": re.compile( + r"""\s*((?P<sequence>\d+))? + (\s*(?P<grant>deny|permit)) + (\sevaluate\s(?P<evaluate>\S+))? + (\s(?P<protocol_num>\d+))? + (\s(?P<protocol>ahp|eigrp|esp|gre|icmp|igmp|ipv6|ipinip|ip|nos|ospf|pcp|pim|sctp|tcp|udp))? + ((\s(?P<source_any>any))| + (\sobject-group\s(?P<source_obj_grp>\S+))| + (\shost\s(?P<source_host>\S+))| + (\s(?P<source_address>(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s\S+)))? + (\s(?P<source_port_protocol>(eq|gts|gt|lt|neq)\s(\S+|\d+)))? + (\srange\s(?P<srange_start>\d+)\s(?P<srange_end>\d+))? + (\s(?P<dest_any>any))? + (\sobject-group\s(?P<dest_obj_grp>\S+))? + (\shost\s(?P<dest_host>\S+))? + (\s(?P<dest_address>(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s\S+))? + (\s(?P<dest_port_protocol>(eq|gts|lt|neq)\s(\S+|\d+)))? + (\srange\s(?P<drange_start>\d+)\s(?P<drange_end>\d+))? + (\s(?P<icmp_igmp_tcp_protocol>administratively-prohibited|alternate-address|conversion-error|dod-host-prohibited|dod-net-prohibited|echo-reply|echo|general-parameter-problem|host-isolated|host-precedence-unreachable|host-redirect|host-tos-redirect|host-tos-unreachable|host-unknown|host-unreachable|information-reply|information-request|mask-reply|mask-request|mobile-redirect|net-redirect|net-tos-redirect|net-tos-unreachable|net-unreachable|network-unknown|no-room-for-option|option-missing|packet-too-big|parameter-problem|port-unreachable|precedence-unreachable|protocol-unreachable|reassembly-timeout|redirect|router-advertisement|router-solicitation|source-quench|source-route-failed|time-exceeded|timestamp-reply|timestamp-request|traceroute|ttl-exceeded|unreachable|dvmrp|host-query|mtrace-resp|mtrace-route|pim|trace|v1host-report|v2host-report|v2leave-group|v3host-report|ack|established|fin|psh|rst|syn|urg))? + (\sdscp\s(?P<dscp>\S+))? + (\s(?P<enable_fragments>fragments))? + (\s(?P<log_input>log-input\s\(tag\s=\s\S+\)|log-input))? + (\s(?P<log>log\s\(tag\s=\s\S+\)|log))? + (\soption\s(?P<option>\S+|\d+))? + (\sprecedence\s(?P<precedence>\S+))? + (\stime-range\s(?P<time_range>\S+))? + (\stos\s(?P<tos>\S+|\d+))? + (\sttl\seq\s(?P<ttl_eq>\d+))? + (\sttl\sgt\s(?P<ttl_gt>\d+))? + (\sttl\slt\s(?P<ttl_lt>\d+))? + (\sttl\sneg\s(?P<ttl_neg>\d+))? + (\ssequence\s(?P<sequence_ipv6>\d+))? + """, + re.VERBOSE, + ), + "setval": _tmplt_access_list_entries, + "compval": "aces", + "result": { + "acls": { + "{{ acl_name|d() }}": { + "name": "{{ acl_name }}", + "aces": [ + { + "sequence": "{% if sequence is defined %}{{ sequence \ + }}{% elif sequence_ipv6 is defined %}{{ sequence_ipv6 }}{% endif %}", + "grant": "{{ grant }}", + "evaluate": "{{ evaluate }}", + "protocol": "{{ protocol }}", + "protocol_number": "{{ protocol_num }}", + "icmp_igmp_tcp_protocol": "{{ icmp_igmp_tcp_protocol }}", + "source": { + "address": "{{ source_address }}", + "any": "{{ not not source_any }}", + "host": "{{ source_host }}", + "object_group": "{{ source_obj_grp }}", + "port_protocol": { + "{{ source_port_protocol.split(' ')[0] if source_port_protocol is defined else None }}": "{{\ + source_port_protocol.split(' ')[1] if source_port_protocol is defined else None }}", + "range": { + "start": "{{ srange_start if srange_start is defined else None }}", + "end": "{{ srange_end if srange_end is defined else None }}", + }, + }, + }, + "destination": { + "address": "{{ dest_address }}", + "any": "{{ not not dest_any }}", + "host": "{{ dest_host }}", + "object_group": "{{ dest_obj_grp }}", + "port_protocol": { + "{{ dest_port_protocol.split(' ')[0] if dest_port_protocol is defined else None }}": "{{\ + dest_port_protocol.split(' ')[1] if dest_port_protocol is defined else None }}", + "range": { + "start": "{{ drange_start if drange_start is defined else None }}", + "end": "{{ drange_end if drange_end is defined else None }}", + }, + }, + }, + "dscp": "{{ dscp }}", + "enable_fragments": "{{ True if enable_fragments is defined else None }}", + "log": { + "set": "{{ True if log is defined and 'tag' not in log else '' }}", + "user_cookie": "{{ log.split(' ')[-1].split(')')[0] if log is defined and 'tag' in log else '' }}", + }, + "log_input": { + "set": "{{ True if log_input is defined and 'tag' not in log_input else '' }}", + "user_cookie": "{{ log_input.split(' ')[-1].split(')')[0] if log_input is defined and 'tag' in log_input }}", + }, + "option": { + "{{ option if option is defined else None }}": "{{ True if option is defined else None }}", + }, + "precedence": "{{ precedence }}", + "time_range": "{{ time_range }}", + "tos": { + "max_reliability": "{{ True if tos is defined and 'max-reliability' in tos else '' }}", + "max_throughput": "{{ True if tos is defined and 'max-throughput' in tos else '' }}", + "min_delay": "{{ True if tos is defined and 'min-delay' in tos else '' }}", + "min_monetary_cost": "{{ True if tos is defined and 'min-monetary-cost' in tos else '' }}", + "normal": "{{ True if tos is defined and 'normal' in tos else '' }}", + "service_value": "{{ tos if tos is defined else None }}", + }, + "ttl": { + "eq": "{{ ttl_eq }}", + "gt": "{{ ttl_gt }}", + "lt": "{{ ttl_lt }}", + "neq": "{{ ttl_neq }}", + }, + }, + ], + }, + }, + }, + }, + ] diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_address_family.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_address_family.py new file mode 100644 index 000000000..b9654956c --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_address_family.py @@ -0,0 +1,2673 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Bgp_address_family parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +UNIQUE_AFI = "{{ afi|d() + '_' + safi|d() + '_' + vrf|d() }}" +UNIQUE_NEIB_ADD = "{{ neighbor_address }}" + + +class Bgp_address_familyTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Bgp_address_familyTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + PARSERS = [ + { + "name": "as_number", + "getval": re.compile( + r""" + ^router\sbgp + (\s(?P<as_number>\S+))? + $""", + re.VERBOSE, + ), + "setval": "router bgp {{ as_number }}", + "result": {"as_number": "{{ as_number }}"}, + "shared": True, + }, + { + "name": "afi", + "getval": re.compile( + r""" + \saddress-family + (\s(?P<afi>ipv4|ipv6|l2vpn|nsap|rtfilter|vpnv4|vpnv6))? + (\s(?P<safi>flowspec|mdt|multicast|mvpn|unicast|evpn|vpls))? + (\svrf\s(?P<vrf>\S+))? + $""", + re.VERBOSE, + ), + "setval": "address-family" + "{{ (' ' + afi) if afi is defined else '' }}" + "{{ (' ' + safi) if safi is defined else '' }}" + "{{ (' vrf ' + vrf) if vrf is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: {"afi": "{{ afi }}", "safi": "{{ safi }}", "vrf": "{{ vrf }}"}, + }, + }, + "shared": True, + }, + { + "name": "aggregate_addresses", + "getval": re.compile( + r""" + \s\saggregate-address + (\s(?P<address>\S+))? + (\s(?P<netmask>\S+))? + (\s(?P<as_set>as-set))? + (\s(?P<summary_only>summary-only))? + (\s(?P<as_confed_set>as-confed-set))? + (\sadvertise-map\s(?P<advertise_map>\S+))? + (\sattribute-map\s(?P<attribute_map>\S+))? + (\ssuppress-map\s(?P<suppress_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "aggregate-address " + "{{ address }} {{ netmask }}" + "{{ ' as-set' if as_set|d(False) else ''}}" + "{{ ' summary-only' if summary_only|d(False) else ''}}" + "{{ ' as-confed-set' if as_confed_set|d(False) else ''}}" + "{{ (' advertise-map ' + advertise_map) if advertise_map is defined else '' }}" + "{{ (' attribute-map ' + attribute_map) if attribute_map is defined else '' }}" + "{{ (' suppress-map ' + suppress_map) if suppress_map is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "aggregate_addresses": [ + { + "address": "{{ address }}", + "netmask": "{{ netmask }}", + "advertise_map": "{{ advertise_map }}", + "as_confed_set": "{{ not not as_confed_set }}", + "as_set": "{{ not not as_set }}", + "attribute_map": "{{ attribute_map }}", + "suppress_map": "{{ suppress_map }}", + "summary_only": "{{ not not summary_only }}", + }, + ], + }, + }, + }, + }, + { + "name": "auto_summary", + "getval": re.compile( + r""" + ((\s\sauto-summary))? + $""", + re.VERBOSE, + ), + "setval": "auto-summary", + "result": {"address_family": {UNIQUE_AFI: {"auto_summary": True}}}, + }, + { + "name": "table_map", + "getval": re.compile( + r""" + \s\stable-map + (\s(?P<name>\S+))? + (\s(?P<filter>filter))? + $""", + re.VERBOSE, + ), + "setval": "table-map" + "{{ (' ' + table_map.name) if table_map.name is defined else '' }}" + "{{ (' filter' ) if table_map.filter|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "table_map": {"name": "{{ name }}", "filter": "{{ not not filter }}"}, + }, + }, + }, + }, + { + "name": "default", + "getval": re.compile(r"""\s\sdefault$""", re.VERBOSE), + "setval": "default", + "result": {"address_family": {UNIQUE_AFI: {"default": True}}}, + }, + { + "name": "default_information", + "getval": re.compile(r"""\s\sdefault-information\soriginate$""", re.VERBOSE), + "setval": "default-information originate", + "result": {"address_family": {UNIQUE_AFI: {"default_information": True}}}, + }, + { + "name": "default_metric", + "getval": re.compile( + r"""\s\sdefault-metric + (\s(?P<default_metric>\d+)) + $""", + re.VERBOSE, + ), + "setval": "default-metric {{ default_metric|string }}", + "result": {"address_family": {UNIQUE_AFI: {"default_metric": "{{ default_metric }}"}}}, + }, + { + "name": "distance", + "getval": re.compile( + r"""\s\sdistance\sbgp + (\s(?P<external>\d+)) + (\s(?P<internal>\d+)) + (\s(?P<local>\d+)) + $""", + re.VERBOSE, + ), + "setval": "distance bgp {{ distance.external|string }} {{ distance.internal|string }} {{ distance.local|string }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "distance": { + "external": "{{ external }}", + "internal": "{{ internal }}", + "local": "{{ local }}", + }, + }, + }, + }, + }, + # bgp starts + { + "name": "bgp.additional_paths.select", + "getval": re.compile( + r""" + \s\sbgp\sadditional-paths\sselect + (\s(?P<select_all>all))? + (\s(?P<select_backup>backup))? + (\s(?P<select_best_ext>best-external))? + (\s(?P<select_group_best>group-best))? + (\sbest\s(?P<select_best>\d))? + $""", + re.VERBOSE, + ), + "setval": "bgp additional-paths select" + "{{ (' all' ) if bgp.additional_paths.select.all|d(False) else '' }}" + "{{ (' backup' ) if bgp.additional_paths.select.backup|d(False) else '' }}" + "{{ (' best-external' ) if bgp.additional_paths.select.best_external|d(False) else '' }}" + "{{ (' group-best' ) if bgp.additional_paths.select.group_best|d(False) else '' }}" + "{{ (' best ' + bgp.additional_paths.select.best|string ) if bgp.additional_paths.select.best is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "bgp": { + "additional_paths": { + "select": { + "all": "{{ not not select_all }}", + "backup": "{{ not not select_backup }}", + "best": "{{ select_best }}", + "group_best": "{{ not not select_group_best }}", + "best_external": "{{ not not select_best_ext }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "bgp.additional_paths.install", + "getval": re.compile( + r""" + \s\sbgp\sadditional-paths\sinstall$""", + re.VERBOSE, + ), + "setval": "bgp additional-paths select install", + "result": { + "address_family": {UNIQUE_AFI: {"bgp": {"additional_paths": {"install": True}}}}, + }, + }, + { + "name": "bgp.additional_paths.receive", + "getval": re.compile( + r""" + \s\sbgp\sadditional-paths\sreceive$""", + re.VERBOSE, + ), + "setval": "bgp additional-paths select receive", + "result": { + "address_family": {UNIQUE_AFI: {"bgp": {"additional_paths": {"receive": True}}}}, + }, + }, + { + "name": "bgp.additional_paths.send", + "getval": re.compile( + r""" + \s\sbgp\sadditional-paths\ssend$""", + re.VERBOSE, + ), + "setval": "bgp additional-paths select send", + "result": { + "address_family": {UNIQUE_AFI: {"bgp": {"additional_paths": {"send": True}}}}, + }, + }, + { + "name": "bgp.aggregate_timer", + "getval": re.compile( + r""" + \s\sbgp\saggregate-timer + (\s(?P<aggregate_timer>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp aggregate-timer {{ bgp.aggregate_timer }}", + "result": { + "address_family": { + UNIQUE_AFI: {"bgp": {"aggregate_timer": "{{ aggregate_timer }}"}}, + }, + }, + }, + { + "name": "bgp.dmzlink_bw", + "getval": re.compile( + r""" + \s\sbgp\sdmzlink-bw + $""", + re.VERBOSE, + ), + "setval": "bgp dmzlink-bw", + "result": {"address_family": {UNIQUE_AFI: {"bgp": {"dmzlink_bw": True}}}}, + }, + { + "name": "bgp.nexthop.route_map", + "getval": re.compile( + r""" + \s\sbgp\snexthop\sroute-map + (\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "bgp nexthop route-map {{ bgp.nexthop.route_map }}", + "result": { + "address_family": { + UNIQUE_AFI: {"bgp": {"nexthop": {"route_map": "{{ route_map }}"}}}, + }, + }, + }, + { + "name": "bgp.nexthop.trigger.delay", + "getval": re.compile( + r""" + \s\sbgp\snexthop\strigger\sdelay + (\s(?P<delay>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp nexthop trigger delay {{ bgp.nexthop.trigger.delay|string }}", + "result": { + "address_family": { + UNIQUE_AFI: {"bgp": {"nexthop": {"trigger": {"delay": "{{ delay }}"}}}}, + }, + }, + }, + { + "name": "bgp.nexthop.trigger.enable", + "getval": re.compile( + r""" + \s\sbgp\snexthop\strigger\sdelay\senable + $""", + re.VERBOSE, + ), + "setval": "bgp nexthop trigger delay enable", + "result": { + "address_family": {UNIQUE_AFI: {"bgp": {"nexthop": {"trigger": {"enable": True}}}}}, + }, + }, + { + "name": "bgp.redistribute_internal", + "getval": re.compile( + r""" + \s\sbgp\sredistribute-internal + $""", + re.VERBOSE, + ), + "setval": "bgp redistribute-internal", + "result": {"address_family": {UNIQUE_AFI: {"bgp": {"redistribute_internal": True}}}}, + }, + { + "name": "bgp.route_map", + "getval": re.compile( + r""" + \s\sbgp\sroute-map\spriority + $""", + re.VERBOSE, + ), + "setval": "bgp route-map priority", + "result": {"address_family": {UNIQUE_AFI: {"bgp": {"route_map": True}}}}, + }, + { + "name": "bgp.scan_time", + "getval": re.compile( + r""" + \s\sbgp\sscan-time + (\s(?P<scan_time>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp scan-time {{ bgp.scan_time }}", + "result": {"address_family": {UNIQUE_AFI: {"bgp": {"scan_time": "{{ scan_time }}"}}}}, + }, + { + "name": "bgp.soft_reconfig_backup", + "getval": re.compile( + r""" + \s\sbgp\ssoft-reconfig-backup + $""", + re.VERBOSE, + ), + "setval": "bgp soft-reconfig-backup", + "result": {"address_family": {UNIQUE_AFI: {"bgp": {"soft_reconfig_backup": True}}}}, + }, + { + "name": "bgp.update_group", + "getval": re.compile( + r""" + \s\sbgp\supdate-group\ssplit\sas-override + $""", + re.VERBOSE, + ), + "setval": "bgp update-group split as-override", + "result": {"address_family": {UNIQUE_AFI: {"bgp": {"update_group": True}}}}, + }, + { + "name": "bgp.dampening", + "getval": re.compile( + r"""\s\sbgp\sdampening + (\s(?P<penalty_half_time>\d+))? + (\s(?P<reuse_route_val>\d+))? + (\s(?P<suppress_route_val>\d+))? + (\s(?P<max_suppress>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "bgp dampening" + "{{ (' ' + bgp.dampening.penalty_half_time|string ) if bgp.dampening.penalty_half_time is defined else '' }}" + "{{ (' ' + bgp.dampening.reuse_route_val|string ) if bgp.dampening.reuse_route_val is defined else '' }}" + "{{ (' ' + bgp.dampening.suppress_route_val|string ) if bgp.dampening.suppress_route_val is defined else '' }}" + "{{ (' ' + bgp.dampening.max_suppress|string ) if bgp.dampening.max_suppress is defined else '' }}" + "{{ (' route-map ' + bgp.dampening.route_map ) if bgp.dampening.route_map is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "bgp": { + "dampening": { + "penalty_half_time": "{{ penalty_half_time }}", + "reuse_route_val": "{{ reuse_route_val }}", + "suppress_route_val": "{{ suppress_route_val }}", + "max_suppress": "{{ max_suppress }}", + "route_map": "{{ route_map }}", + }, + }, + }, + }, + }, + }, + { + "name": "bgp.slow_peer_options.detection.enable", + "getval": re.compile(r"""\s\sbgp\sslow-peer\sdetection$""", re.VERBOSE), + "setval": "bgp slow-peer detection", + "result": { + "address_family": { + UNIQUE_AFI: {"bgp": {"slow_peer_options": {"detection": {"enable": True}}}}, + }, + }, + }, + { + "name": "bgp.slow_peer_options.detection.threshold", + "getval": re.compile( + r""" + \s\sbgp\sslow-peer\sdetection\sthreshold + (\s(?P<threshold>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp slow-peer detection threshold {{ bgp.slow_peer_options.detection.threshold }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "bgp": { + "slow_peer_options": {"detection": {"threshold": "{{ threshold }}"}}, + }, + }, + }, + }, + }, + { + "name": "bgp.slow_peer_options.split_update_group.dynamic", + "getval": re.compile( + r""" + \s\sbgp\sslow-peer\ssplit-update-group\sdynamic$""", + re.VERBOSE, + ), + "setval": "bgp slow-peer split-update-group dynamic", + "result": { + "address_family": { + UNIQUE_AFI: { + "bgp": {"slow_peer_options": {"split_update_group": {"dynamic": True}}}, + }, + }, + }, + }, + { + "name": "bgp.slow_peer.split_update_group.permanent", + "getval": re.compile( + r""" + \s\sbgp\sslow-peer\ssplit-update-group\sdynamic\spermanent$""", + re.VERBOSE, + ), + "setval": "bgp slow-peer split-update-group dynamic permanent", + "result": { + "address_family": { + UNIQUE_AFI: { + "bgp": {"slow_peer_options": {"split_update_group": {"permanent": True}}}, + }, + }, + }, + }, + # bgp ends + # neighbor starts + { + "name": "peer_group_name", + "getval": re.compile( + r"""\s\sneighbor\s(?P<neighbor_address>\S+) + \speer-group\s(?P<peer_group_name>\S+) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' peer-group ' + peer_group_name) if peer_group_name|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "peer_group_name": "{{ peer_group_name }}", + "neighbor_address": UNIQUE_NEIB_ADD, + }, + }, + }, + }, + }, + }, + { + "name": "peer_group", + "getval": re.compile( + r"""\s\sneighbor\s(?P<neighbor_address>\S+)\speer-group$""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} peer-group", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "peer_group": True, + "neighbor_address": UNIQUE_NEIB_ADD, + }, + }, + }, + }, + }, + }, + { + "name": "remote_as", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\s(?P<remote_as>remote-as) + (\s(?P<number>\S+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} remote-as" + "{{ (' ' + remote_as|string) if remote_as is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"remote_as": "{{ number }}"}}}, + }, + }, + }, + { + "name": "local_as", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\s(?P<local_as>local-as) + (\s(?P<number>\S+))? + (\s(?P<dual_as>dual-as))? + (\s(?P<no_prepend>no-prepend))? + (\s(?P<replace_as>replace-as))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} local-as" + "{{ (' ' + local_as.number|string) if local_as.number is defined else '' }}" + "{{ (' dual-as') if local_as.dual_as is defined else '' }}" + "{{ (' no-prepend') if local_as.no_prepend.set is defined else '' }}" + "{{ (' replace-as') if local_as.no_prepend.replace_as is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "local_as": { + "set": "{{ not not local_as }}", + "number": "{{ number }}", + "dual_as": "{{ not not dual_as }}", + "no_prepend": { + "set": "{{ not not no_prepend }}", + "replace_as": "{{ not not replace_as }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "neighbor_address", + "getval": re.compile( + r""" + \sneighbordelDummy(?P<neighbor_address>\S+) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}", + "result": {"dummy_neighbor": True}, + }, + { + "name": "activate", + "getval": re.compile( + r"""\s\sneighbor\s(?P<neighbor_address>\S+)\sactivate$""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} activate", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "neighbor_address": UNIQUE_NEIB_ADD, + "activate": True, + }, + }, + }, + }, + }, + }, + { + "name": "additional_paths", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sadditional-paths + (\s(?P<disable>disable))? + (\s(?P<receive>receive))? + (\s(?P<send>send))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} additional-paths" + "{{ (' disable') if additional_paths.disable|d(False) else '' }}" + "{{ (' receive') if additional_paths.receive|d(False) else '' }}" + "{{ (' send') if additional_paths.send|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "additional_paths": { + "disable": "{{ not not disable }}", + "receive": "{{ not not receive }}", + "send": "{{ not not send }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "advertises.additional_paths", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sadvertise\sadditional-paths + (\s(?P<all>all))? + (\sbest\s(?P<receive>\d+))? + (\s(?P<group_best>group-best))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} advertise additional-paths" + "{{ (' all') if advertise.additional_paths.all|d(False) else '' }}" + "{{ (' best '+ best|string) if advertise.additional_paths.best|d(False) else '' }}" + "{{ (' group-best') if advertise.additional_paths.group_best|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "advertises": { + "additional_paths": { + "all": "{{ not not all }}", + "best": "{{ receive }}", + "group_best": "{{ not not group_best }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "advertises.best_external", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sadvertise\sbest-external + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' advertise best-external') if advertise.best_external|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": {UNIQUE_NEIB_ADD: {"advertises": {"best-external": True}}}, + }, + }, + }, + }, + { + "name": "advertises.diverse_path", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sadvertise\sdiverse-path + (\s(?P<backup>backup))? + (\s(?P<mpath>mpath))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} advertise diverse-path" + "{{ (' backup') if advertise.diverse_path.backup|d(False) else '' }}" + "{{ (' mpath') if advertise.diverse_path.mpath|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "advertises": { + "diverse_path": { + "backup": "{{ not not backup }}", + "mpath": "{{ not not mpath }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "advertise_map", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sadvertise-map + (\s(?P<name>\S+))? + (\sexist-map\s(?P<exist_map>\S+))? + (\snon-exist-map\s(?P<non_exist_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} advertise-map" + "{{ (' ' + name) if advertise_map.name is defined else '' }}" + "{{ (' exist-map ' + exist_map) if advertise_map.exist_map is defined else '' }}" + "{{ (' non-exist-map ' + non_exist_map) if advertise_map.non_exist_map is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "advertise_map": { + "name": "{{ name }}", + "exist_map": "{{ exist_map }}", + "non_exist_map": "{{ non_exist_map }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "advertisement_interval", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sadvertisement-interval + (\s(?P<advertisement_interval>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} advertisement-interval" + "{{ (' ' + advertisement_interval|string) if advertisement_interval is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "advertisement_interval": "{{ advertisement_interval }}", + }, + }, + }, + }, + }, + }, + { + "name": "aigp", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\saigp + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' aigp') if aigp.enable|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"aigp": {"enable": True}}}}, + }, + }, + }, + { + "name": "aigp.send.cost_community", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\saigp\ssend\scost-community + (\s(?P<id>\d+))\spoi + (\s(?P<igp_cost>igp-cost))? + (\s(?P<pre_bestpath>pre-bestpath))? + (\s(?P<transitive>transitive))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} aigp send cost-community" + "{{ (' ' + aigp.send.cost_community.id|string + ' poi') if aigp.send.cost_community.id is defined else '' }}" + "{{ (' igp-cost') if aigp.send.cost_community.poi.igp_cost|d(False) else '' }}" + "{{ (' pre-bestpath') if aigp.send.cost_community.poi.pre_bestpath|d(False) else '' }}" + "{{ (' transitive') if aigp.send.cost_community.poi.transitive|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "aigp": { + "send": { + "cost_community": { + "id": "{{ id }}", + "poi": { + "igp_cost": "{{ not not igp_cost }}", + "pre_bestpath": "{{ not not pre_bestpath }}", + "transitive": "{{ not not transitive }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "aigp.send.med", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\saigp\ssend\smed + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' aigp send med') if aigp.send.med|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"aigp": {"send": {"med": True}}}}}, + }, + }, + }, + { + "name": "allow_policy", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sallow-policy + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' allow-policy') if allow_policy|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"allow_policy": True}}}, + }, + }, + }, + { + "name": "allowas_in", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sallowas-in + (\s(?P<allowas_in>\d+)) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' allowas-in ' + allowas_in|string) if allowas_in is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": {UNIQUE_NEIB_ADD: {"allowas_in": "{{ allowas_in }}"}}, + }, + }, + }, + }, + { + "name": "as_override", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sas-override + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' as-override') if as_override|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"as_override": True}}}, + }, + }, + }, + { + "name": "bmp_activate", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sbmp-activate + (\sserver\s(?P<server>\d+))? + (\s(?P<all>all))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} bmp-activate" + "{{ (' server '+ bmp_activate.server|string) if bmp_activate.server is defined else '' }}" + "{{ (' all') if bmp_activate.all|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "neighbor_address": UNIQUE_NEIB_ADD, + "bmp_activate": { + "server": "{{ server }}", + "all": "{{ not not all }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "capability", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\scapability\sorf\sprefix-list + (\s(?P<both>both))? + (\s(?P<receive>receive))? + (\s(?P<send>send))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} capability orf prefix-list" + "{{ (' both') if capability.both|d(False) else '' }}" + "{{ (' receive') if capability.receive|d(False) else '' }}" + "{{ (' send') if capability.send|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "capability": { + "both": "{{ not not both }}", + "receive": "{{ not not receive }}", + "send": "{{ not not send }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "cluster_id", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\scluster-id(\s(?P<cluster_id>\s\d+))$""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} cluster-id {{ cluster_id }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": {UNIQUE_NEIB_ADD: {"cluster_id": "{{ cluster_id }}"}}, + }, + }, + }, + }, + { + "name": "default_originate", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sdefault-originate$""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' default-originate') if default_originate.set|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": {UNIQUE_NEIB_ADD: {"default_originate": {"set": True}}}, + }, + }, + }, + }, + { + "name": "default_originate.route_map", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sdefault-originate + (\sroute-map\s(?P<route_map>\S+)) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} default-originate" + "{{ (' route-map' + default_originate.route_map) if default_originate.route_map is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "default_originate": {"route_map": "{{ route_map }}"}, + }, + }, + }, + }, + }, + }, + { + "name": "description", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sdescription\s(?P<description>\S.+)$""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} description {{ description }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "neighbor_address": UNIQUE_NEIB_ADD, + "description": "{{ description }}", + }, + }, + }, + }, + }, + }, + { + "name": "disable_connected_check", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+) + \s(?P<disable_connected_check>disable-connected-check) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} disable-connected-check", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "disable_connected_check": "{{ not not disable_connected_check }}", + }, + }, + }, + }, + }, + }, + { + "name": "ebgp_multihop", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+) + \s(?P<enable>ebgp_multihop) + (\s(?P<hop_count>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} ebgp-multihop" + "{{ (' ' + hop_count|string) if hop_count is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "ebgp_multihop": { + "enable": "{{ not not enable }}", + "hop_count": "{{ hop_count }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "distribute_list", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sdistribute-list + (\s(?P<acl>\S+)) + (\s(?P<in>in))? + (\s(?P<out>out))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} distribute-list" + "{{ (' ' + distribute_list.acl) if distribute_list.acl is defined else '' }}" + "{{ (' in') if distribute_list.in|d(False) else '' }}" + "{{ (' out') if distribute_list.out|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "distribute_list": { + "acl": "{{ acl }}", + "in": "{{ not not in }}", + "out": "{{ not not out }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "dmzlink_bw", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sdmzlink-bw + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' dmzlink-bw') if dmzlink_bw|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"dmzlink_bw": True}}}, + }, + }, + }, + { + "name": "filter_list", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sfilter-list + (\s(?P<acl>\S+)) + (\s(?P<in>in))? + (\s(?P<out>out))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} filter-list" + "{{ (' ' + filter_list.as_path_acl) if filter_list.as_path_acl is defined else '' }}" + "{{ (' in') if filter_list.in|d(False) else '' }}" + "{{ (' out') if filter_list.out|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "filter_list": { + "as_path_acl": "{{ acl }}", + "in": "{{ not not in }}", + "out": "{{ not not out }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "fall_over.bfd", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sfall-over + \s(?P<set>bfd) + (\s(?P<multi_hop>multi-hop))? + (\s(?P<single_hop>single-hop))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} fall-over" + "{{ (' bfd') if fall_over.bfd.set is defined else '' }}" + "{{ (' multi-hop') if fall_over.bfd.multi_hop is defined else '' }}" + "{{ (' single-hop') if fall_over.bfd.single_hop is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "fall_over": { + "bfd": { + "set": "{{ not not set }}", + "multi_hop": "{{ not not multi_hop }}", + "single_hop": "{{ not not single_hop }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "fall_over.route_map", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sroute-map + \s(?P<route_map>\S+) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} route-map {{ fall_over.route_map }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "fall_over": {"route_map": "{{ not not route_map }}"}, + }, + }, + }, + }, + }, + }, + { + "name": "ha_mode", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sha-mode + \s(?P<set>graceful-restart) + (\s(?P<disable>disable))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} ha-mode" + "{{ (' graceful-restart') if ha_mode.set is defined else '' }}" + "{{ (' disable') if ha_mode.disable is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "ha_mode": { + "set": "{{ not not set }}", + "disable": "{{ not not disable }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "inherit", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sinherit\speer-session + \s(?P<inherit>\S+) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} inherit peer-session" + "{{ (' ' + inherit) if inherit is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"inherit": "{{ inherit }}"}}}, + }, + }, + }, + { + "name": "internal_vpn_client", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sinternal-vpn-client + \s(?P<inherit>\S+) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} internal-vpn-client", + "result": { + "address_family": { + UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"internal_vpn_client": True}}}, + }, + }, + }, + { + "name": "log_neighbor_changes", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+) + \s(?P<set>log-neighbor-changes) + (\s(?P<disable>disable))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' log-neighbor-changes') if log_neighbor_changes.set is defined else '' }}" + "{{ (' disable') if log_neighbor_changes.disable is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "neighbor_address": UNIQUE_NEIB_ADD, + "log_neighbor_changes": { + "set": "{{ not not set }}", + "disable": "{{ not not disable }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "maximum_prefix", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\smaximum-prefix + (\s(?P<max_no>\d+)) + (\s(?P<threshold_val>\d+))? + (\srestart\s(?P<restart>\d+))? + (\s(?P<warning_only>warning-only))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} maximum-prefix" + "{{ (' ' + maximum_prefix.number|string) if maximum_prefix.number is defined else '' }}" + "{{ (' ' + maximum_prefix.threshold_value|string) if maximum_prefix.threshold_value is defined else '' }}" + "{{ (' restart ' + maximum_prefix.restart|string) if maximum_prefix.restart is defined else '' }}" + "{{ (' warning-only') if maximum_prefix.warning_only|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "maximum_prefix": { + "number": "{{ max_no }}", + "threshold_value": "{{ threshold_val }}", + "restart": "{{ restart }}", + "warning_only": "{{ not not warning_only }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "nexthop_self.set", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\snext-hop-self + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' next-hop-self') if nexthop_self.set|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"nexthop_self": {"set": True}}}}, + }, + }, + }, + { + "name": "nexthop_self.all", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\snext-hop-self\sall + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' next-hop-self all') if nexthop_self.all|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"nexthop_self": {"all": True}}}}, + }, + }, + }, + { + "name": "next_hop_unchanged.set", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\snext-hop-unchanged + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' next-hop-unchanged') if next_hop_unchanged.set|d(False) else ''}}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": {UNIQUE_NEIB_ADD: {"next_hop_unchanged": {"set": True}}}, + }, + }, + }, + }, + { + "name": "next_hop_unchanged.allpaths", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\snext-hop-unchanged\sallpaths + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' next-hop-unchanged allpaths') if next_hop_unchanged.allpaths|d(False) else ''}}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": {UNIQUE_NEIB_ADD: {"next_hop_unchanged": {"allpaths": True}}}, + }, + }, + }, + }, + { + "name": "password_options", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\spassword + \s(?P<encryption>\d+) + (\s(?P<pass_key>.$))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} password" + "{{ (' '+ password_options.encryption|string) if password_options.encryption is defined else '' }}" + "{{ (' '+ password_options.pass_key) if password_options.pass_key is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "neighbor_address": UNIQUE_NEIB_ADD, + "password_options": { + "encryption": "{{ encryption }}", + "pass_key": "{{ pass_key }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "path_attribute.discard", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\spath-attribute\sdiscard + (\s(?P<type>\d+))? + (\srange\s(?P<start>\d+)\s(?P<end>\d+))? + (\s(?P<in>in))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} path-attribute discard" + "{{ (' ' + type) if path_attribute.discard.type is defined else '' }}" + "{{ (' range '+ path_attribute.discard.range.start|string) if spath_attribute.discard.range.start is defined else '' }}" + "{{ (' '+ path_attribute.discard.range.end|string) if spath_attribute.discard.range.end is defined else '' }}" + "{{ (' in') if path_attribute.discard.in|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "path_attribute": { + "discard": { + "type": "{{ type }}", + "range": {"start": "{{ start }}", "end": "{{ end }}"}, + "in": "{{ not not in }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "path_attribute.treat_as_withdraw", + "getval": re.compile( + r"""\s\sneighbor\s(?P<neighbor_address>\S+)\spath-attribute\streat-as-withdraw + (\s(?P<type>\d+))? + (\srange\s(?P<start>\d+)\s(?P<end>\d+))? + (\s(?P<in>in))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} path-attribute treat-as-withdraw" + "{{ (' ' + type) if path_attribute.treat_as_withdraw.type is defined else '' }}" + "{{ (' range '+ path_attribute.treat_as_withdraw.range.start|string) if spath_attribute.treat_as_withdraw.range.start is defined else '' }}" + "{{ (' '+ path_attribute.treat_as_withdraw.range.end|string) if spath_attribute.treat_as_withdraw.range.end is defined else '' }}" + "{{ (' in') if path_attribute.treat_as_withdraw.in|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "path_attribute": { + "treat_as_withdraw": { + "type": "{{ type }}", + "range": {"start": "{{ start }}", "end": "{{ end }}"}, + "in": "{{ not not in }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "route_maps", + "getval": re.compile( + r"""\s\sneighbor\s(?P<neighbor_address>\S+)\sroute-map + (\s(?P<route_map>\S+)) + (\s(?P<in>in))? + (\s(?P<out>out))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ route_maps.neighbor_address }} route-map" + "{{ (' ' + route_maps.name) if route_maps.name is defined else '' }}" + "{{ (' in') if route_maps.in|d(False) else '' }}" + "{{ (' out') if route_maps.out|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "neighbor_address": UNIQUE_NEIB_ADD, + "route_maps": [ + { + "name": "{{ route_map }}", + "in": "{{ not not in }}", + "out": "{{ not not out }}", + }, + ], + }, + }, + }, + }, + }, + }, + { + "name": "prefix_lists", + "getval": re.compile( + r"""\s\sneighbor\s(?P<neighbor_address>\S+)\sprefix-list + (\s(?P<prefix_list>\S+)) + (\s(?P<in>in))? + (\s(?P<out>out))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ prefix_lists.neighbor_address }} prefix-list" + "{{ (' ' + prefix_lists.name) if prefix_lists.name is defined else '' }}" + "{{ (' in') if prefix_lists.in|d(False) else '' }}" + "{{ (' out') if prefix_lists.out|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "neighbor_address": UNIQUE_NEIB_ADD, + "prefix_lists": [ + { + "name": "{{ prefix_list }}", + "in": "{{ not not in }}", + "out": "{{ not not out }}", + }, + ], + }, + }, + }, + }, + }, + }, + { + "name": "remove_private_as.set", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sremove-private-as + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' remove-private-as') if remove_private_as.set|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": {UNIQUE_NEIB_ADD: {"remove_private_as": {"set": True}}}, + }, + }, + }, + }, + { + "name": "remove_private_as.all", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sremove-private-as\sall + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' remove-private-as all') if remove_private_as.all|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": {UNIQUE_NEIB_ADD: {"remove_private_as": {"all": True}}}, + }, + }, + }, + }, + { + "name": "remove_private_as.replace_as", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sremove-private-as\sreplace-as + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' remove-private-as replace-as') if remove_private_as.replace_as|d(False) else ''}}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": {UNIQUE_NEIB_ADD: {"remove_private_as": {"replace_as": True}}}, + }, + }, + }, + }, + { + "name": "route_reflector_client", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sroute-reflector-client + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' route-reflector-client') if route_reflector_client|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "neighbor_address": UNIQUE_NEIB_ADD, + "route_reflector_client": True, + }, + }, + }, + }, + }, + }, + { + "name": "route_server_client", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sroute-server-client + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' route-server-client') if route_server_client|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "neighbor_address": UNIQUE_NEIB_ADD, + "route_server_client": True, + }, + }, + }, + }, + }, + }, + { + "name": "send_community.set", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\ssend-community + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' send-community') if send_community.set|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"send_community": {"set": True}}}}, + }, + }, + }, + { + "name": "send_community.both", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\ssend-community\sboth + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} send-community" + "{{ (' both') if send_community.both|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": {UNIQUE_NEIB_ADD: {"send_community": {"both": True}}}, + }, + }, + }, + }, + { + "name": "send_community.extended", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\ssend-community\sextended + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} send-community" + "{{ (' extended') if send_community.extended|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": {UNIQUE_NEIB_ADD: {"send_community": {"extended": True}}}, + }, + }, + }, + }, + { + "name": "send_community.standard", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\ssend-community\sstandard + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} send-community" + "{{ (' standard') if send_community.standard|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": {UNIQUE_NEIB_ADD: {"send_community": {"standard": True}}}, + }, + }, + }, + }, + { + "name": "shutdown", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sshutdown + (\sgraceful(?P<graceful>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' shutdown') if shutdown.set is defined else '' }}" + "{{ (' graceful '+ shutdown.graceful|string) if shutdown.graceful is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "shutdown": {"set": True, "graceful": "{{ graceful }}"}, + }, + }, + }, + }, + }, + }, + { + "name": "slow_peer_options.detection", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sslow-peer\sdetection + (\s(?P<enable>enable))? + (\s(?P<disable>disable))? + (\sthreshold\s(?P<threshold>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} slow-peer detection" + "{{ (' enable') if slow_peer_options.detection.enable|d(False) else '' }}" + "{{ (' disable') if slow_peer_options.detection.disable|d(False) else '' }}" + "{{ (' threshold ' + slow_peer_options.detection.threshold|string) if slow_peer_options.detection.threshold is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "slow_peer_options": { + "detection": { + "enable": "{{ not not enable }}", + "disable": "{{ not not disable }}", + "threshold": "{{ threshold }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "slow_peer_options.split_update_group", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sslow-peer\ssplit-update-group + (\s(?P<static>static))? + (\s(?P<dynamic>dynamic))? + (\s(?P<disable>disable))? + (\s(?P<permanent>permanent))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} slow-peer split-update-group" + "{{ (' static') if slow_peer_options.split_update_group.static|d(False) else '' }}" + "{{ (' dynamic') if slow_peer_options.split_update_group.dynamic.enable|d(False) else '' }}" + "{{ (' disable') if slow_peer_options.split_update_group.dynamic.disable|d(False) else '' }}" + "{{ (' permanent') if slow_peer_options.split_update_group.dynamic.permanent|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "slow_peer_options": { + "split_update_group": { + "static": "{{ not not static }}", + "dynamic": { + "enable": "{{ not not dynamic }}", + "disable": "{{ not not disable }}", + "permanent": "{{ not not permanent }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "soft_reconfiguration", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\ssoft-reconfiguration\sinbound + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' soft-reconfiguration inbound') if soft_reconfiguration|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"soft_reconfiguration": True}}}, + }, + }, + }, + { + "name": "soo", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\ssoo + (\s(?P<soo>\S+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} soo {{ soo }}", + "result": { + "address_family": { + UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"soo": "{{ soo }}"}}}, + }, + }, + }, + { + "name": "timers", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\stimers + (\s(?P<keepalive>\d+))? + (\s(?P<holdtime>\d+))? + (\s(?P<min_holdtime>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} timers" + "{{ (' ' + timers.interval|string) if timers.interval is defined else '' }}" + "{{ (' ' + timers.holdtime|string) if timers.holdtime is defined else '' }}" + "{{ (' ' + timers.min_holdtime|string) if timers.min_holdtime is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "timers": { + "interval": "{{ keepalive }}", + "holdtime": "{{ holdtime }}", + "min_holdtime": "{{ min_holdtime }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "transport.connection_mode", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\stransport\sconnection-mode + (\s(?P<active>active))? + (\s(?P<passive>passive))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} transport connection-mode" + "{{ (' active') if transport.connection_mode.active|d(False) else '' }}" + "{{ (' passive') if transport.connection_mode.passive|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "transport": { + "connection_mode": { + "active": "{{ not not active }}", + "passive": "{{ not not passive }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "transport.multi_session", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\stransport\smulti-session + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} transport multi-session", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "neighbor_address": UNIQUE_NEIB_ADD, + "transport": {"multi_session": True}, + }, + }, + }, + }, + }, + }, + { + "name": "transport.path_mtu_discovery", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\stransport\spath-mtu-discovery + (\s(?P<disable>disable))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} transport" + "{{ (' path-mtu-discovery') if transport.path_mtu_discovery.set|d(False) else '' }}" + "{{ (' disable') if transport.path_mtu_discovery.disable|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "transport": { + "path_mtu_discovery": { + "set": True, + "disable": "{{ not not disable }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "ttl_security", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sttl-security + (\shops(?P<ttl_security>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} ttl-security" + "{{ (' hops '+ ttl_security|string) if ttl_security is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": {UNIQUE_NEIB_ADD: {"ttl_security": "{{ ttl_security }}"}}, + }, + }, + }, + }, + { + "name": "unsuppress_map", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sunsuppress-map + (\s(?P<unsuppress_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} unsuppress-map" + "{{ (' ' + unsuppress_map) if unsuppress_map is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": {UNIQUE_NEIB_ADD: {"unsuppress_map": "{{ unsuppress_map }}"}}, + }, + }, + }, + }, + { + "name": "version", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sversion + (\s(?P<version>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} version" + "{{ (' ' + version|string) if version is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "neighbors": { + UNIQUE_NEIB_ADD: { + "neighbor_address": UNIQUE_NEIB_ADD, + "version": "{{ version }}", + }, + }, + }, + }, + }, + }, + { + "name": "weight", + "getval": re.compile( + r""" + \s\sneighbor\s(?P<neighbor_address>\S+)\sweight + (\s(?P<weight>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} weight" + "{{ (' ' + weight|string) if weight is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"weight": "{{ weight }}"}}}, + }, + }, + }, + # neighbors end + { + "name": "networks", + "getval": re.compile( + r""" + \s\snetwork + (\s(?P<address>\S+))? + (\smask\s(?P<netmask>\S+))? + (\sroute-map\s(?P<route_map>\S+))? + (\s(?P<backdoor>backdoor))? + (\s(?P<evpn>evpn))? + $""", + re.VERBOSE, + ), + "setval": "network" + "{{ (' ' + address) if address is defined else '' }}" + "{{ (' mask ' + mask) if mask is defined else '' }}" + "{{ (' route-map ' + route_map) if route_map is defined else '' }}" + "{{ (' backdoor' ) if backdoor|d(False) else '' }}" + "{{ (' evpn' ) if evpn|d(False) else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "networks": [ + { + "address": "{{ address }}", + "mask": "{{ netmask }}", + "route_map": "{{ route_map }}", + "evpn": "{{ not not evpn }}", + "backdoor": "{{ not not backdoor }}", + }, + ], + }, + }, + }, + }, + { + "name": "snmp.context.user", + "getval": re.compile( + r"""\s\ssnmp + (\scontext\s(?P<context>\S+)) + (\suser\s(?P<user>\S+)) + (\s(?P<credential>credential))? + (\s(?P<encrypted>encrypted))? + (\sauth\smd5\s(?P<md5>\S+))? + (\sauth\ssha\s(?P<sha>\S+))? + (\spriv\saes\s128\s(?P<a>\S+))? + (\spriv\saes\s192\s(?P<b>\S+))? + (\spriv\saes\s256\s(?P<c>\S+))? + (\spriv\sdes\s(?P<des>\S+))? + (\spriv\sdes56\s(?P<des56>\S+))? + (\saccess\sipv6\s(?P<aclv6>\S+))? + (\saccess\s(?P<acl>\S+))? + $""", + re.VERBOSE, + ), + "setval": "snmp context {{ snmp.context.name }} user" + "{{ (' ' + snmp.context.user.name) if snmp.context.user.name is defined else '' }}" + "{{ (' credential' ) if snmp.context.user.credential|d(False) else '' }}" + "{{ (' encrypted' ) if snmp.context.user.encrypted|d(False) else '' }}" + "{{ (' auth md5 ' + snmp.context.user.auth.md5 ) if snmp.context.user.auth is defined and snmp.context.user.auth.acl is defined else '' }}" + "{{ (' auth sha ' + snmp.context.user.auth.sha ) if snmp.context.user.auth is defined and snmp.context.user.auth.sha is defined else '' }}" + "{{ (' priv md5 ' + snmp.context.user.priv.aes128 ) if snmp.context.user.priv is defined and snmp.context.user.priv.aes128 is defined else '' }}" + "{{ (' priv sha ' + snmp.context.user.priv.aes192 ) if snmp.context.user.priv is defined and snmp.context.user.priv.aes192 is defined else '' }}" + "{{ (' priv sha ' + snmp.context.user.priv.aes256 ) if snmp.context.user.priv is defined and snmp.context.user.priv.aes256 is defined else '' }}" + "{{ (' priv sha ' + snmp.context.user.priv.des56 ) if snmp.context.user.priv is defined and snmp.context.user.priv.des56 is defined else '' }}" + "{{ (' priv sha ' + snmp.context.user.priv.des ) if snmp.context.user.priv is defined and snmp.context.user.priv.des is defined else '' }}" + "{{ (' access ' + snmp.context.user.access.acl|string ) if snmp.context.user.access is defined" + " and snmp.context.user.access.acl is defined else '' }}" + "{{ (' access ipv6 ' + snmp.context.user.access.ipv6|string ) if snmp.context.user.access is defined" + " and snmp.context.user.access.ipv6 is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "snmp": { + "context": { + "name": "{{ context }}", + "user": { + "name": "{{ user }}", + "access": {"acl": "{{ acl }}", "ipv6": "{{ aclv6 }}"}, + "auth": {"md5": "{{ md5 }}", "sha": "{{ sha }}"}, + "priv": { + "des56": "{{ des56 }}", + "aes128": "{{ a }}", + "aes192": "{{ b }}", + "aes256": "{{ c }}", + "des": "{{ des }}", + }, + "credential": "{{ not not credential }}", + "encrypted": "{{ not not encrypted }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "snmp.context.community", + "getval": re.compile( + r"""\s\ssnmp + (\scontext\s(?P<context>\S+)) + (\scommunity\s(?P<community>\S+)) + (\s(?P<ro>RO))? + (\s(?P<rw>RW))? + (\sipv6\s(?P<ip6acl>\S+))? + (\s(?P<acl>\S+))? + $""", + re.VERBOSE, + ), + "setval": "snmp context {{ snmp.context.name }} community" + "{{ (' ' + snmp.context.community.snmp_community) if snmp.context.community.snmp_community is defined else '' }}" + "{{ (' ro' ) if snmp.context.community.ro|d(False) else '' }}" + "{{ (' rw' ) if snmp.context.community.rw|d(False) else '' }}" + "{{ (' ' + snmp.context.community.acl) if snmp.context.community.acl is defined else '' }}" + "{{ (' ipv6 ' + snmp.context.community.ipv6) if snmp.context.community.ipv6 is defined else '' }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "snmp": { + "context": { + "name": "{{ context }}", + "community": { + "snmp_community": "{{ community }}", + "acl": "{{ acl }}", + "ro": "{{ not not ro }}", + "rw": "{{ not not rw }}", + "ipv6": "{{ ip6acl }}", + }, + }, + }, + }, + }, + }, + }, + # redistribute starts + { + "name": "redistribute.application", + "getval": re.compile( + r""" + \s\sredistribute\sapplication\s(?P<name>\S+) + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute application {{ redistribute.application.name }}" + "{{ (' metric ' + redistribute.application.metric|string) if redistribute.application.metric is defined else '' }}" + "{{ (' route-map ' + redistribute.application.route_map) if redistribute.application.route_map is defined else '' }}", + "remval": "redistribute application {{ redistribute.application.name }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "redistribute": [ + { + "application": { + "name": "{{ name }}", + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + }, + }, + { + "name": "redistribute.bgp", + "getval": re.compile( + r""" + \s\sredistribute\sbgp\s(?P<name>\S+) + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute bgp {{ redistribute.bgp.as_number }}" + "{{ (' metric ' + redistribute.bgp.metric|string) if redistribute.bgp.metric is defined else '' }}" + "{{ (' route-map ' + redistribute.bgp.route_map) if redistribute.bgp.route_map is defined else '' }}", + "remval": "redistribute bgp {{ redistribute.bgp.as_number }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "redistribute": [ + { + "bgp": { + "as_number": "{{ name }}", + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + }, + }, + { + "name": "redistribute.connected", + "getval": re.compile( + r""" + \s\sredistribute\sconnected + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute connected" + "{{ (' metric ' + redistribute.connected.metric|string) if redistribute.connected.metric is defined else '' }}" + "{{ (' route-map ' + redistribute.connected.route_map) if redistribute.connected.route_map is defined else '' }}", + "remval": "redistribute connected", + "result": { + "address_family": { + UNIQUE_AFI: { + "redistribute": [ + { + "connected": { + "set": True, + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + }, + }, + { + "name": "redistribute.eigrp", + "getval": re.compile( + r""" + \s\sredistribute\seigrp\s(?P<name>\S+) + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute eigrp {{ redistribute.eigrp.name|string }}" + "{{ (' metric ' + redistribute.eigrp.metric|string) if redistribute.eigrp.metric is defined else '' }}" + "{{ (' route-map ' + redistribute.eigrp.route_map) if redistribute.eigrp.route_map is defined else '' }}", + "remval": "redistribute eigrp {{ redistribute.eigrp.name|string }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "redistribute": [ + { + "eigrp": { + "as_number": "{{ name }}", + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + }, + }, + { + "name": "redistribute.isis", + "getval": re.compile( + r""" + \s\sredistribute\sisis\s(?P<name>\S+) + (\s(?P<clns>clns))? + (\s(?P<ip>ip))? + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute isis {{ redistribute.isis.area_tag }}" + "{{ (' clns') if redistribute.isis.clns|d(False) else '' }}" + "{{ (' ip') if redistribute.isis.ip|d(False) else '' }}" + "{{ (' metric ' + redistribute.isis.metric|string) if redistribute.isis.metric is defined else '' }}" + "{{ (' route-map ' + redistribute.isis.route_map) if redistribute.isis.route_map is defined else '' }}", + "remval": "redistribute isis {{ redistribute.isis.area_tag }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "redistribute": [ + { + "isis": { + "area_tag": "{{ name }}", + "clns": "{{ not not clns }}", + "ip": "{{ not not ip }}", + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + }, + }, + { + "name": "redistribute.iso_igrp", + "getval": re.compile( + r""" + \s\sredistribute\siso-igrp\s(?P<name>\S+) + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute iso-igrp {{ redistribute.iso_igrp.area_tag }}" + "{{ (' route-map ' + redistribute.iso_igrp.route_map) if redistribute.iso_igrp.route_map is defined else '' }}", + "remval": "redistribute iso-igrp {{ redistribute.iso_igrp.area_tag }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "redistribute": [ + { + "iso_igrp": { + "area_tag": "{{ name }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + }, + }, + { + "name": "redistribute.lisp", + "getval": re.compile( + r""" + \s\sredistribute\slisp + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute lisp" + "{{ (' metric ' + redistribute.lisp.metric|string) if redistribute.lisp.metric is defined else '' }}" + "{{ (' route-map ' + redistribute.lisp.route_map) if redistribute.lisp.route_map is defined else '' }}", + "remval": "redistribute lisp", + "result": { + "address_family": { + UNIQUE_AFI: { + "redistribute": [ + { + "lisp": { + "set": True, + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + }, + }, + { + "name": "redistribute.mobile", + "getval": re.compile( + r""" + \s\sredistribute\smobile + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute mobile" + "{{ (' metric ' + redistribute.mobile.metric|string) if redistribute.mobile.metric is defined else '' }}" + "{{ (' route-map ' + redistribute.mobile.route_map) if redistribute.mobile.route_map is defined else '' }}", + "remval": "redistribute mobile", + "result": { + "address_family": { + UNIQUE_AFI: { + "redistribute": [ + { + "mobile": { + "set": True, + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + }, + }, + { + "name": "redistribute.odr", + "getval": re.compile( + r""" + \s\sredistribute\sodr + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute odr" + "{{ (' metric ' + redistribute.odr.metric|string) if redistribute.odr.metric is defined else '' }}" + "{{ (' route-map ' + redistribute.odr.route_map) if redistribute.odr.route_map is defined else '' }}", + "remval": "redistribute odr", + "result": { + "address_family": { + UNIQUE_AFI: { + "redistribute": [ + { + "odr": { + "set": True, + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + }, + }, + { + "name": "redistribute.ospf", + "getval": re.compile( + r""" + \s+redistribute\sospf\s(?P<process_id>\S+) + (\svrf(?P<vrf>\s\S+))? + (\smetric\s(?P<metric>\d+))? + (\smatch)? + (\s(?P<internal>internal))? + (\s(?P<ext_type_1>external\s1))? + (\s(?P<ext_type_2>external\s2))? + (\s(?P<nssa_type_1>nssa-external\s1))? + (\s(?P<nssa_type_2>nssa-external\s2))? + (\sroute-map\s(?P<route_map>\S+))? + (\s(?P<include_connected>include-connected))? + $""", + re.VERBOSE, + ), + "setval": "redistribute ospf {{ process_id }}" + "{{ (' metric ' + metric|string) if metric is defined }}" + "{{ (' vrf ' + vrf) if vrf is defined }}" + "{{ (' match') if match is defined }}" + "{{ (' internal') if match is defined and match.internal is defined and match.internal }}" + "{{ (' external 1') if match is defined and match.externals is defined and " + "match.externals.type_1 is defined and match.externals.type_1 }}" + "{{ (' external 2') if match is defined and match.externals is defined and " + "match.externals.type_2 is defined and match.externals.type_2 }}" + "{{ (' nssa-external 1') if match is defined and match.nssa_externals is defined and " + "match.nssa_externals.type_1 is defined and match.nssa_externals.type_1 }}" + "{{ (' nssa-external 2') if match is defined and match.nssa_externals is defined and " + "match.nssa_externals.type_2 is defined and match.nssa_externals.type_2}}" + "{{ (' route-map ' + route_map) if route_map is defined }}" + "{{ (' include-connected') if include_connected is defined and include_connected }}", + "remval": "redistribute ospf {{ process_id }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "redistribute": [ + { + "ospf": { + "process_id": "{{ process_id }}", + "vrf": "{{ vrf }}", + "metric": "{{ metric }}", + "match": { + "internal": "{{ not not internal }}", + "externals": { + "type_1": "{{ not not ext_type_1 }}", + "type_2": "{{ not not ext_type_2 }}", + }, + "nssa_externals": { + "type_1": "{{ not not nssa_type_1 }}", + "type_2": "{{ not not nssa_type_2 }}", + }, + }, + "route_map": "{{ route_map }}", + "include_connected": "{{ not not include_connected }}", + }, + }, + ], + }, + }, + }, + }, + { + "name": "redistribute.ospfv3", + "getval": re.compile( + r""" + \s+redistribute\sospfv3\s(?P<process_id>\S+) + (\smetric\s(?P<metric>\d+))? + (\smatch)? + (\s(?P<internal>internal))? + (\s(?P<ext_type_1>external\s1))? + (\s(?P<ext_type_2>external\s2))? + (\s(?P<nssa_type_1>nssa-external\s1))? + (\s(?P<nssa_type_2>nssa-external\s2))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute ospfv3 {{ process_id }}" + "{{ (' metric ' + metric|string) if metric is defined }}" + "{{ (' match') if match is defined }}" + "{{ (' internal') if match is defined and match.internal is defined and match.internal }}" + "{{ (' external 1') if match is defined and match.externals is defined and " + "match.externals.type_1 is defined and match.externals.type_1 }}" + "{{ (' external 2') if match is defined and match.externals is defined and " + "match.externals.type_2 is defined and match.externals.type_2 }}" + "{{ (' nssa-external 1') if match is defined and match.nssa_externals is defined and " + "match.nssa_externals.type_1 is defined and match.nssa_externals.type_1 }}" + "{{ (' nssa-external 2') if match is defined and match.nssa_externals is defined and " + "match.nssa_externals.type_2 is defined and match.nssa_externals.type_2}}" + "{{ (' route-map ' + route_map) if route_map is defined }}", + "remval": "redistribute ospfv3 {{ process_id }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "redistribute": [ + { + "ospfv3": { + "process_id": "{{ process_id }}", + "metric": "{{ metric }}", + "match": { + "internal": "{{ not not internal }}", + "externals": { + "type_1": "{{ not not ext_type_1 }}", + "type_2": "{{ not not ext_type_2 }}", + }, + "nssa_externals": { + "type_1": "{{ not not nssa_type_1 }}", + "type_2": "{{ not not nssa_type_2 }}", + }, + }, + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + }, + }, + { + "name": "redistribute.rip", + "getval": re.compile( + r""" + \s\sredistribute\srip + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute rip" + "{{ (' metric ' + redistribute.rip.metric|string) if redistribute.rip.metric is defined else '' }}" + "{{ (' route-map ' + redistribute.rip.route_map) if redistribute.rip.route_map is defined else '' }}", + "remval": "redistribute rip", + "result": { + "address_family": { + UNIQUE_AFI: { + "redistribute": [ + { + "rip": { + "set": True, + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + }, + }, + { + "name": "redistribute.static", + "getval": re.compile( + r""" + \s\sredistribute\sstatic + (\s(?P<clns>clns))? + (\s(?P<ip>ip))? + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute static" + "{{ (' clns') if redistribute.static.clns|d(False) else '' }}" + "{{ (' ip') if redistribute.static.ip|d(False) else '' }}" + "{{ (' metric ' + redistribute.static.metric|string) if redistribute.static.metric is defined else '' }}" + "{{ (' route-map ' + redistribute.static.route_map) if redistribute.static.route_map is defined else '' }}", + "remval": "redistribute static", + "result": { + "address_family": { + UNIQUE_AFI: { + "redistribute": [ + { + "static": { + "set": True, + "clns": "{{ not not clns }}", + "ip": "{{ not not ip }}", + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + }, + }, + { + "name": "redistribute.vrf", + "getval": re.compile( + r""" + \s\sredistribute\svrf + (\s(?P<name>\S+))? + (\s(?P<global>global))? + $""", + re.VERBOSE, + ), + "setval": "redistribute vrf {{ redistribute.vrf.name }}" + "{{ (' global') if redistribute.vrf.global|d(False) else '' }}", + "remval": "redistribute vrf {{ redistribute.vrf.name }}", + "result": { + "address_family": { + UNIQUE_AFI: { + "redistribute": [ + {"vrf": {"name": "{{ name }}", "global": "{{ not not global }}"}}, + ], + }, + }, + }, + }, + # redistribute ends + ] diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_global.py new file mode 100644 index 000000000..477cbc679 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_global.py @@ -0,0 +1,3056 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Bgp_global parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +class Bgp_globalTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Bgp_globalTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + PARSERS = [ + { + "name": "as_number", + "getval": re.compile( + r""" + ^router\sbgp + (\s(?P<as_number>\S+))? + $""", + re.VERBOSE, + ), + "setval": "router bgp {{ as_number|string }}", + "result": {"as_number": "{{ as_number }}"}, + }, + { + "name": "aggregate_addresses", + "getval": re.compile( + r""" + \saggregate-address + (\s(?P<address>\S+))? + (\s(?P<netmask>\S+))? + (\s(?P<as_set>as-set))? + (\s(?P<summary_only>summary-only))? + (\s(?P<as_confed_set>as-confed-set))? + (\sadvertise-map\s(?P<advertise_map>\S+))? + (\sattribute-map\s(?P<attribute_map>\S+))? + (\ssuppress-map\s(?P<suppress_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "aggregate-address " + "{{ address }} {{ netmask }}" + "{{ ' as-set' if as_set|d(False) else ''}}" + "{{ ' summary-only' if summary_only|d(False) else ''}}" + "{{ ' as-confed-set' if as_confed_set|d(False) else ''}}" + "{{ (' advertise-map ' + advertise_map) if advertise_map is defined else '' }}" + "{{ (' attribute-map ' + attribute_map) if attribute_map is defined else '' }}" + "{{ (' suppress-map ' + suppress_map) if suppress_map is defined else '' }}", + "result": { + "aggregate_addresses": [ + { + "address": "{{ address }}", + "netmask": "{{ netmask }}", + "advertise_map": "{{ advertise_map }}", + "as_confed_set": "{{ not not as_confed_set }}", + "as_set": "{{ not not as_set }}", + "attribute_map": "{{ attribute_map }}", + "suppress_map": "{{ suppress_map }}", + "summary_only": "{{ not not summary_only }}", + }, + ], + }, + }, + { + "name": "auto_summary", + "getval": re.compile( + r""" + ((\sauto-summary))? + $""", + re.VERBOSE, + ), + "setval": "auto-summary", + "result": {"auto_summary": True}, + }, + { + "name": "bmp.buffer_size", + "getval": re.compile( + r""" + \sbmp\sbuffer-size + (\s(?P<buffer_size>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bmp buffer-size {{ bmp.buffer_size|string }}", + "result": {"bmp": {"buffer_size": "{{ buffer_size }}"}}, + }, + { + "name": "bmp.initial_refresh.delay", + "getval": re.compile( + r""" + \sbmp\sinitial-refresh\sdelay + (\s(?P<delay>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bmp initial-refresh delay {{ bmp.initial_refresh.delay|string }}", + "result": {"bmp": {"initial_refresh": {"delay": "{{ delay }}"}}}, + }, + { + "name": "bmp.initial_refresh.skip", + "getval": re.compile( + r""" + \sbmp\sinitial-refresh\sskip + $""", + re.VERBOSE, + ), + "setval": "bmp initial-refresh skip", + "result": {"bmp": {"initial_refresh": {"skip": True}}}, + }, + { + "name": "bmp.server", + "getval": re.compile( + r""" + \sbmp\sserver + (\s(?P<server>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bmp server {{ bmp.server }}", + "result": {"bmp": {"server": "{{ server|string }}"}}, + }, + { + "name": "bmp.server_options.activate", + "getval": re.compile( + r""" + \sactivate + $""", + re.VERBOSE, + ), + "setval": "activate", + "result": {"bmp": {"server_options": {"activate": True}}}, + }, + { + "name": "bmp.server_options.address", + "getval": re.compile( + r""" + \saddress\s(?P<host>\S+)\s + (\sport-number(?P<port>\d+)) + $""", + re.VERBOSE, + ), + "setval": "address " + "{{ bmp.server_options.address.host }} port-number {{ bmp.server_options.address.port|string }}\nexit-bmp-server-mode", + "result": { + "bmp": { + "server_options": {"address": {"host": "{{ host }}", "port": "{{ port }}"}}, + }, + }, + }, + { + "name": "default_information", + "getval": re.compile( + r""" + (\sdefault-information originate)? + $""", + re.VERBOSE, + ), + "setval": "default-information originate", + "result": {"default_information": True}, + }, + { + "name": "default_metric", + "getval": re.compile( + r""" + ^default-metric\s(?P<default_metric>\d+) + $""", + re.VERBOSE, + ), + "setval": "default-metric {{ default_metric|string }}", + "result": {"default_metric": "{{ default_metric }}"}, + }, + { + "name": "distance.admin", + "getval": re.compile( + r""" + \sdistance + (\s(?P<distance>\d+))? + (\s(?P<address>\S+))? + (\s(?P<wildcard_bit>\S+))? + (\s(?P<acl>\S+))? + $""", + re.VERBOSE, + ), + "setval": "distance" + "{{ (' ' + distance.admin.distance|string) if distance.admin.distance is defined else '' }}" + "{{ (' ' + distance.admin.address) if distance.admin.address is defined else '' }}" + "{{ (' ' + distance.admin.wildcard_bit) if distance.admin.wildcard_bit is defined else '' }}" + "{{ (' ' + distance.admin.acl) if distance.admin.acl is defined else '' }}", + "result": { + "distance": { + "admin": { + "distance": "{{ distance }}", + "address": "{{ address }}", + "wildcard_bit": "{{ wildcard_bit }}", + "acl": "{{ acl }}", + }, + }, + }, + }, + { + "name": "distance.bgp", + "getval": re.compile( + r""" + \sdistance\sbgp + (\s(?P<routes_external>\d+))? + (\s(?P<routes_internal>\d+))? + (\s(?P<routes_local>\d+))? + $""", + re.VERBOSE, + ), + "setval": "distance bgp" + "{{ (' ' + distance.bgp.routes_external|string) if distance.bgp.routes_external is defined else '' }}" + "{{ (' ' + distance.bgp.routes_internal|string) if distance.bgp.routes_internal is defined else '' }}" + "{{ (' ' + distance.bgp.routes_local|string) if distance.bgp.routes_local is defined else '' }}", + "result": { + "distance": { + "bgp": { + "routes_external": "{{ routes_external }}", + "routes_internal": "{{ routes_internal }}", + "routes_local": "{{ routes_local }}", + }, + }, + }, + }, + { + "name": "distance.mbgp", + "getval": re.compile( + r""" + \sdistance\smbgp + (\s(?P<routes_external>\d+))? + (\s(?P<routes_internal>\d+))? + (\s(?P<routes_local>\d+))? + $""", + re.VERBOSE, + ), + "setval": "distance mbgp" + "{{ (' ' + distance.mbgp.routes_external|string) if distance.mbgp.routes_external is defined else '' }}" + "{{ (' ' + distance.mbgp.routes_internal|string) if distance.mbgp.routes_internal is defined else '' }}" + "{{ (' ' + distance.mbgp.routes_local|string) if distance.mbgp.routes_local is defined else '' }}", + "result": { + "distance": { + "mbgp": { + "routes_external": "{{ routes_external }}", + "routes_internal": "{{ routes_internal }}", + "routes_local": "{{ routes_local }}", + }, + }, + }, + }, + { + "name": "distributes", + "getval": re.compile( + r""" + \sdistribute-list + (\s(?P<acl>\S+))? + (\sprefix\s(?P<prefix>\S+))? + (\sgateway\s(?P<gateway>\S+))? + (\s(?P<in>in))? + (\s(?P<out>out))? + (\s(?P<interface>\S+))? + $""", + re.VERBOSE, + ), + "setval": "distribute-list" + "{{ (' prefix ' + prefix) if prefix is defined else '' }}" + "{{ (' gateway ' + gateway) if gateway is defined else '' }}" + "{{ (' ' + acl) if acl is defined else '' }}" + "{{ (' in' ) if in|d(False) else '' }}" + "{{ (' out' ) if out|d(False) else '' }}" + "{{ (' ' + interface) if interface is defined else '' }}", + "result": { + "distributes": [ + { + "prefix": "{{ prefix }}", + "gateway": "{{ gateway }}", + "acl": "{{ acl }}", + "in": "{{ not not in }}", + "out": "{{ not not out }}", + "interface": "{{ interface }}", + }, + ], + }, + }, + { + "name": "maximum_paths.paths", + "getval": re.compile( + r""" + \smaximum-paths + (\s(?P<paths>\d+))? + $""", + re.VERBOSE, + ), + "setval": "maximum-paths {{ maximum_paths.paths|string }}", + "result": {"maximum_paths": {"paths": "{{ paths }}"}}, + }, + { + "name": "maximum_paths.eibgp", + "getval": re.compile( + r""" + \smaximum-paths\seibgp + (\s(?P<eibgp>\d+))? + $""", + re.VERBOSE, + ), + "setval": "maximum-paths eibgp {{ maximum_paths.eibgp|string }}", + "result": {"maximum_paths": {"eibgp": "{{ eibgp }}"}}, + }, + { + "name": "maximum_paths.ibgp", + "getval": re.compile( + r""" + \smaximum-paths\sibgp + (\s(?P<ibgp>\d+))? + $""", + re.VERBOSE, + ), + "setval": "maximum-paths ibgp {{ maximum_paths.ibgp|string }}", + "result": {"maximum_paths": {"ibgp": "{{ ibgp }}"}}, + }, + { + "name": "maximum_secondary_paths.paths", + "getval": re.compile( + r""" + \smaximum-secondary-paths + (\s(?P<paths>\d+))? + $""", + re.VERBOSE, + ), + "setval": "maximum-secondary-paths {{ maximum_secondary_paths.paths|string }}", + "result": {"maximum_secondary_paths": {"paths": "{{ paths }}"}}, + }, + { + "name": "maximum_secondary_paths.eibgp", + "getval": re.compile( + r""" + \smaximum-secondary-paths\seibgp + (\s(?P<eibgp>\d+))? + $""", + re.VERBOSE, + ), + "setval": "maximum-secondary-paths eibgp {{ maximum_secondary_paths.eibgp|string }}", + "result": {"maximum_secondary_paths": {"eibgp": "{{ eibgp }}"}}, + }, + { + "name": "maximum_secondary_paths.ibgp", + "getval": re.compile( + r""" + \smaximum-secondary-paths\sibgp + (\s(?P<ibgp>\d+))? + $""", + re.VERBOSE, + ), + "setval": "maximum-secondary-paths ibgp {{ maximum_secondary_paths.ibgp|string }}", + "result": {"maximum_secondary_paths": {"ibgp": "{{ ibgp }}"}}, + }, + { + "name": "networks", + "getval": re.compile( + r""" + \snetwork + (\s(?P<address>\S+))? + (\smask\s(?P<netmask>\S+))? + (\sroute-map\s(?P<route_map>\S+))? + (\s(?P<backdoor>backdoor))? + $""", + re.VERBOSE, + ), + "setval": "network" + "{{ (' ' + address) if address is defined else '' }}" + "{{ (' mask ' + netmask) if netmask is defined else '' }}" + "{{ (' route-map ' + route_map) if route_map is defined else '' }}" + "{{ (' backdoor' ) if backdoor|d(False) else '' }}", + "result": { + "networks": [ + { + "address": "{{ address }}", + "netmask": "{{ netmask }}", + "route_map": "{{ route_map }}", + "backdoor": "{{ not not backdoor }}", + }, + ], + }, + }, + { + "name": "route_server_context.name", + "getval": re.compile( + r""" + \sroute-server_context + (\s(?P<name>\S+))? + $""", + re.VERBOSE, + ), + "setval": "route-server-context {{ route_server_context.name }}", + "result": {"route_server_context": {"name": "{{ name }}"}}, + }, + { + "name": "route_server_context.address_family", + "getval": re.compile( + r""" + \sroute-server_context\saddress-family + (\s(?P<afi>ipv4|ipv6))? + (\s(?P<modifier>multicast|unicast))? + (\simport-map\s(?P<import_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "address-family" + "{{ (' ' + route_server_context.address_family.afi) if route_server_context.address_family.afi is defined else '' }}" + "{{ (' ' + route_server_context.address_family.modifier) if route_server_context.address_family.modifier is defined else '' }}" + "{{ (' import-map ' + route_server_context.address_family.import_map) if route_server_context.address_family.import_map is defined else '' }}", + "result": { + "route_server_context": { + "address_family": { + "afi": "{{ afi }}", + "modifier": "{{ modifier }}", + "import_map": "{{ import_map }}", + }, + }, + }, + }, + { + "name": "route_server_context.description", + "getval": re.compile( + r""" + \sroute-server_context\sdescription + (\s(?P<description>.+$))? + """, + re.VERBOSE, + ), + "setval": "description {{ route_server_context.description }}", + "result": {"route_server_context": {"description": "{{ description }}"}}, + }, + { + "name": "synchronization", + "getval": re.compile(r"""\s(?P<synchronization>synchronization)""", re.VERBOSE), + "setval": "synchronization", + "result": {"synchronization": "{{ not not synchronization }}"}, + }, + { + "name": "table_map", + "getval": re.compile( + r""" + \stable-map + (\s(?P<name>\S+))? + (\s(?P<filter>filter))? + $""", + re.VERBOSE, + ), + "setval": "table-map" + "{{ (' ' + name) if name is defined else '' }}" + "{{ (' filter' ) if filter|d(False) else '' }}", + "result": {"table_map": {"name": "{{ name }}", "filter": "{{ not not filter }}"}}, + }, + { + "name": "timers", + "getval": re.compile( + r""" + \stimers\sbgp + (\s(?P<keepalive>\d+))? + (\s(?P<holdtime>\d+))? + (\s(?P<min_holdtime>\d+))? + $""", + re.VERBOSE, + ), + "setval": "timers bgp" + "{{ (' ' + timers.keepalive|string) if timers.keepalive is defined else '' }}" + "{{ (' ' + timers.holdtime|string) if timers.holdtime is defined else '' }}" + "{{ (' ' + timers.min_holdtime|string) if timers.min_holdtime is defined else '' }}", + "result": { + "timers": { + "keepalive": "{{ keepalive }}", + "holdtime": "{{ holdtime }}", + "min_holdtime": "{{ min_holdtime }}", + }, + }, + }, + # bgp starts + { + "name": "bgp.additional_paths", + "getval": re.compile( + r""" + \sbgp\sadditional-paths + (\s(?P<install>install))? + (\s(?P<receive>receive))? + (\s(?P<select>select))? + (\s(?P<send>send))? + $""", + re.VERBOSE, + ), + "setval": "bgp additional-paths" + "{{ (' install' ) if bgp.additional_paths.install|d(False) else '' }}" + "{{ (' receive' ) if bgp.additional_paths.receive|d(False) else '' }}" + "{{ (' select' ) if bgp.additional_paths.select|d(False) else '' }}" + "{{ (' send' ) if bgp.additional_paths.send|d(False) else '' }}", + "result": { + "bgp": { + "additional_paths": { + "install": "{{ not not install }}", + "receive": "{{ not not receive }}", + "select": "{{ not not select }}", + "send": "{{ not not send }}", + }, + }, + }, + }, + { + "name": "bgp.advertise_best_external", + "getval": re.compile(r"""\s(bgp\sadvertise-best-external)""", re.VERBOSE), + "setval": "{{ ('bgp advertise-best-external' ) if bgp.advertise_best_external|d(False) else '' }}", + "result": {"bgp": {"advertise_best_external": True}}, + }, + { + "name": "bgp.aggregate_timer", + "getval": re.compile( + r""" + \sbgp\saggregate-timer + (\s(?P<aggregate_timer>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp aggregate-timer {{ bgp.aggregate_timer|string }}", + "result": {"bgp": {"aggregate_timer": "{{ aggregate_timer }}"}}, + }, + { + "name": "bgp.always_compare_med", + "getval": re.compile(r"""\s(bgp\salways-compare-med)""", re.VERBOSE), + "setval": "{{ ('bgp always-compare-med' ) if bgp.always_compare_med|d(False) else '' }}", + "result": {"bgp": {"always_compare_med": True}}, + }, + { + "name": "bgp.asnotation", + "getval": re.compile(r"""\s(bgp\sasnotation\sdot)""", re.VERBOSE), + "setval": "{{ ('bgp asnotation dot' ) if bgp.asnotation|d(False) else '' }}", + "result": {"bgp": {"asnotation": True}}, + }, + { + "name": "bgp.bestpath_options.aigp", + "getval": re.compile(r"""\s(bgp\sbestpath\saigp\signore)""", re.VERBOSE), + "setval": "{{ ('bgp bestpath aigp ignore' ) if bgp.bestpath_options.aigp|d(False) else '' }}", + "result": {"bgp": {"bestpath_options": {"aigp": True}}}, + }, + { + "name": "bgp.bestpath_options.compare_routerid", + "getval": re.compile(r"""\s(bgp\sbestpath\scompare-routerid)""", re.VERBOSE), + "setval": "{{ ('bgp bestpath compare-routerid' ) if bgp.bestpath_options.compare_routerid|d(False) else '' }}", + "result": {"bgp": {"bestpath_options": {"compare_routerid": True}}}, + }, + { + "name": "bgp.bestpath_options.cost_community", + "getval": re.compile(r"""\s(bgp\sbestpath\scost-community\signore)""", re.VERBOSE), + "setval": "{{ ('bgp bestpath cost-community ignore' ) if bgp.bestpath_options.cost_community|d(False) else '' }}", + "result": {"bgp": {"bestpath_options": {"cost_community": True}}}, + }, + { + "name": "bgp.bestpath_options.igp_metric", + "getval": re.compile(r"""\s(bgp\sbestpath\sigp-metric\signore)""", re.VERBOSE), + "setval": "bgp bestpath igp-metric ignore", + "result": {"bgp": {"bestpath_options": {"igp_metric": True}}}, + }, + { + "name": "bgp.bestpath_options.med", + "getval": re.compile( + r""" + \sbgp\sbestpath\smed + (\s(?P<confed>confed))? + (\s(?P<missing_as_worst>missing-as-worst))? + $""", + re.VERBOSE, + ), + "setval": "bgp bestpath med" + "{{ (' confed') if bgp.bestpath_options.med.confed|d(False) else '' }}" + "{{ (' missing-as-worst') if bgp.bestpath_options.med.missing_as_worst|d(False) else '' }}", + "result": { + "bgp": { + "bestpath_options": { + "med": { + "confed": "{{ not not confed }}", + "missing_as_worst": "{{ not not missing_as_worst }}", + }, + }, + }, + }, + }, + { + "name": "bgp.client_to_client", + "getval": re.compile( + r""" + \sbgp\sclient-to-client + (\s(?P<set>reflection))? + (\s(?P<all>all))? + (\sintra-cluster\scluster-id\s(?P<intra_cluster>\S+))? + $""", + re.VERBOSE, + ), + "setval": "bgp client-to-client" + "{{ (' reflection') if bgp.client_to_client.set|d(False) else '' }}" + "{{ (' all') if bgp.client_to_client.all|d(False) else '' }}" + "{{ (' intra-cluster cluster-id '+ bgp.client_to_client.intra_cluster ) if bgp.client_to_client.intra_cluster is defined else '' }}", + "result": { + "bgp": { + "client_to_client": { + "set": "{{ not not set }}", + "all": "{{ not not all }}", + "intra_cluster": "{{ intra_cluster }}", + }, + }, + }, + }, + { + "name": "bgp.cluster_id", + "getval": re.compile( + r""" + \sbgp\scluster-id + (\s(?P<cluster_id>\S+))? + $""", + re.VERBOSE, + ), + "setval": "bgp cluster-id {{ bgp.cluster_id }}", + "result": {"bgp": {"cluster_id": "{{ not not cluster_id }}"}}, + }, + { + "name": "bgp.confederation.peer", + "getval": re.compile( + r""" + \sbgp\sconfederation\speer + (\s(?P<peer>\S+))? + $""", + re.VERBOSE, + ), + "setval": "bgp confederation peers {{ bgp.confederation.peer }}", + "result": {"bgp": {"confederation": {"peers": "{{ peer }}"}}}, + }, + { + "name": "bgp.confederation.identifier", + "getval": re.compile( + r""" + \sbgp\sconfederation\sidentifier + (\s(?P<identifier>\S+))? + $""", + re.VERBOSE, + ), + "setval": "bgp confederation identifier {{ bgp.confederation.identifier }}", + "result": {"bgp": {"confederation": {"identifier": "{{ identifier }}"}}}, + }, + { + "name": "bgp.consistency_checker.auto_repair", + "getval": re.compile( + r""" + \sbgp\sconsistency-checker\sauto-repair + (\sinterval\s(?P<interval>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp consistency-checker auto-repair" + "{{ (' interval '+ bgp.consistency_checker.auto_repair.interval|string) if bgp.consistency_checker.auto_repair.interval is defined else '' }}", + "result": { + "bgp": { + "consistency_checker": { + "auto_repair": {"set": True, "interval": "{{ interval }}"}, + }, + }, + }, + }, + { + "name": "bgp.consistency_checker.error_message", + "getval": re.compile( + r""" + \sbgp\sconsistency-checker\serror-message + (\sinterval\s(?P<interval>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp consistency-checker error-message" + "{{ (' interval '+ bgp.consistency_checker.error_message.interval|string) if bgp.consistency_checker.error_message.interval is defined else '' }}", + "result": { + "bgp": { + "consistency_checker": { + "error_message": {"set": True, "interval": "{{ interval }}"}, + }, + }, + }, + }, + { + "name": "bgp.dampening", + "getval": re.compile( + r""" + \sbgp\sdampening + (\s(?P<penalty_half_time>\d+))? + (\s(?P<reuse_route_val>\d+))? + (\s(?P<suppress_route_val>\d+))? + (\s(?P<max_suppress>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "bgp dampening" + "{{ (' '+ bgp.dampening.penalty_half_time|string) if bgp.dampening.penalty_half_time is defined else '' }}" + "{{ (' '+ bgp.dampening.reuse_route_val|string) if bgp.dampening.reuse_route_val is defined else '' }}" + "{{ (' '+ bgp.dampening.suppress_route_val|string) if bgp.dampening.suppress_route_val is defined else '' }}" + "{{ (' '+ bgp.dampening.max_suppress|string) if bgp.dampening.max_suppress is defined else '' }}" + "{{ (' route-map '+ bgp.dampening.route_map|string) if bgp.dampening.route_map is defined else '' }}", + "result": { + "bgp": { + "dampening": { + "penalty_half_time": "{{ penalty_half_time }}", + "reuse_route_val": "{{ reuse_route_val }}", + "suppress_route_val": "{{ suppress_route_val }}", + "max_suppress": "{{ max_suppress }}", + "route_map": "{{ route_map }}", + }, + }, + }, + }, + { + "name": "bgp.deterministic_med", + "getval": re.compile(r"""\s(bgp\sdeterministic-med)""", re.VERBOSE), + "setval": "bgp deterministic-med", + "result": {"bgp": {"deterministic_med": True}}, + }, + { + "name": "bgp.dmzlink_bw", + "getval": re.compile(r"""\s(bgp\sdmzlink-bw)""", re.VERBOSE), + "setval": "bgp dmzlink-bw", + "result": {"bgp": {"dmzlink_bw": True}}, + }, + { + "name": "bgp.enforce_first_as", + "getval": re.compile(r"""\s(bgp\senforce-first-as)""", re.VERBOSE), + "setval": "bgp enforce-first-as", + "result": {"bgp": {"enforce_first_as": True}}, + }, + { + "name": "bgp.enhanced_error", + "getval": re.compile(r"""\s(bgp\senhanced-error)""", re.VERBOSE), + "setval": "bgp enhanced-error", + "result": {"bgp": {"enhanced_error": True}}, + }, + { + "name": "bgp.fast_external_fallover", + "getval": re.compile(r"""\s(bgp\sfast-external-fallover)""", re.VERBOSE), + "setval": "bgp fast-external-fallover", + "result": {"bgp": {"fast_external_fallover": True}}, + }, + { + "name": "bgp.graceful_restart.set", + "getval": re.compile( + r""" + \sbgp\sgraceful-restart + $""", + re.VERBOSE, + ), + "setval": "bgp graceful-restart", + "result": {"bgp": {"graceful_restart": {"set": True}}}, + }, + { + "name": "bgp.graceful_restart.extended", + "getval": re.compile( + r""" + \sbgp\sgraceful-restart\sextended + $""", + re.VERBOSE, + ), + "setval": "bgp graceful-restart extended", + "result": {"bgp": {"graceful_restart": {"extended": True}}}, + }, + { + "name": "bgp.graceful_restart.restart_time", + "getval": re.compile( + r""" + \sbgp\sgraceful-restart\srestart-time + (\s(?P<restart_time>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp graceful-restart restart-time {{ bgp.graceful_restart.restart_time|string }}", + "result": {"bgp": {"graceful_restart": {"restart_time": "{{ restart_time }}"}}}, + }, + { + "name": "bgp.graceful_restart.stalepath_time", + "getval": re.compile( + r""" + \sbgp\sgraceful-restart\sstalepath-time + (\s(?P<stalepath_time>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp graceful-restart stalepath-time {{ bgp.graceful_restart.stalepath_time|string }}", + "result": {"bgp": {"graceful_restart": {"stalepath_time": "{{ stalepath_time }}"}}}, + }, + { + "name": "bgp.graceful_shutdown.neighbors", + "getval": re.compile( + r""" + \sbgp\sgraceful-shutdown\sall\sneighbors + (\s(?P<time>\d+))? + (\s(?P<activate>activate))? + (\slocal-preference\s(?P<local_preference>\d+))? + (\scommunity\s(?P<community>\S+))? + $""", + re.VERBOSE, + ), + "setval": "bgp graceful-shutdown all neighbors" + "{{ (' ' + bgp.graceful_shutdown.neighbors.time|string) if bgp.graceful_shutdown.neighbors.time is defined else '' }}" + "{{ (' activate') if bgp.graceful_shutdown.neighbors.activate|d(False) else '' }}" + "{{ (' local-preference ' + bgp.graceful_shutdown.local_preference|string) if bgp.graceful_shutdown.local_preference is defined else '' }}" + "{{ (' community ' + bgp.graceful_shutdown.community|string) if bgp.graceful_shutdown.community is defined else '' }}", + "result": { + "bgp": { + "graceful_shutdown": { + "neighbors": {"time": "{{ time }}", "activate": "{{ not not activate }}"}, + "community": "{{ community }}", + "local_preference": "{{ local_preference }}", + }, + }, + }, + }, + { + "name": "bgp.graceful_shutdown.vrfs", + "getval": re.compile( + r""" + \sbgp\sgraceful-shutdown\sall\svrfs + (\s(?P<time>\d+))? + (\s(?P<activate>activate))? + (\slocal-preference\s(?P<local_preference>\d+))? + (\scommunity\s(?P<community>\S+))? + $""", + re.VERBOSE, + ), + "setval": "bgp graceful-shutdown all vrfs" + "{{ (' ' + bgp.graceful_shutdown.vrfs.time|string) if bgp.graceful_shutdown.vrfs.time is defined else '' }}" + "{{ (' activate') if bgp.graceful_shutdown.vrfs.activate|d(False) else '' }}" + "{{ (' local-preference ' + bgp.graceful_shutdown.local_preference|string) if bgp.graceful_shutdown.local_preference is defined else '' }}" + "{{ (' community ' + bgp.graceful_shutdown.community|string) if bgp.graceful_shutdown.community is defined else '' }}", + "result": { + "bgp": { + "graceful_shutdown": { + "vrfs": {"time": "{{ time }}", "activate": "{{ not not activate }}"}, + "community": "{{ community }}", + "local_preference": "{{ local_preference }}", + }, + }, + }, + }, + { + "name": "inject_maps", + "getval": re.compile( + r""" + \sbgp\sinject-map + (\s(?P<name>\S+))? + (\sexist-map\s(?P<exist_map_name>\S+))? + (\s(?P<copy_attributes>copy-attributes))? + $""", + re.VERBOSE, + ), + "setval": "bgp inject-map" + "{{ (' '+ name) if name is defined else '' }}" + "{{ (' exist-map '+ exist_map_name) if exist_map_name is defined else '' }}" + "{{ (' copy-attributes') if copy_attributes|d(False) else '' }}", + "result": { + "bgp": { + "inject_maps": [ + { + "name": "{{ name }}", + "exist_map_name": "{{ exist_map_name }}", + "copy_attributes": "{{ not not copy_attributes }}", + }, + ], + }, + }, + }, + { + "name": "bgp.listen.limit", + "getval": re.compile( + r""" + \sbgp\slisten\slimit + (\s(?P<limit>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp listen limit {{ bgp.listen.limit|string }}", + "result": {"bgp": {"listen": {"limit": "{{ limit }}"}}}, + }, + { + "name": "bgp.listen.range", + "getval": re.compile( + r""" + \sbgp\slisten\srange + (\s(?P<host_with_subnet>\S+))? + (\speer-group\s(?P<peer_group>\S+))? + $""", + re.VERBOSE, + ), + "setval": "bgp listen range" + "{{ (' '+ bgp.listen.range.host_with_subnet) if bgp.listen.range.host_with_subnet is defined else '' }}" + "{{ (' peer-group '+ bgp.listen.range.peer_group) if bgp.listen.range.peer_group is defined else '' }}", + "result": { + "bgp": { + "listen": { + "range": { + "host_with_subnet": "{{ host_with_subnet }}", + "peer_group": "{{ peer_group }}", + }, + }, + }, + }, + }, + { + "name": "bgp.log_neighbor_changes", + "getval": re.compile(r"""\s(bgp\slog-neighbor-changes)""", re.VERBOSE), + "setval": "bgp log-neighbor-changes", + "result": {"bgp": {"log_neighbor_changes": True}}, + }, + { + "name": "bgp.maxas_limit", + "getval": re.compile( + r""" + \sbgp\smaxas-limit + (\s(?P<maxas_limit>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp maxas-limit {{ bgp.maxas_limit|string }}", + "result": {"bgp": {"maxas_limit": "{{ maxas_limit }}"}}, + }, + { + "name": "bgp.maxcommunity_limit", + "getval": re.compile( + r""" + \sbgp\smaxcommunity-limit + (\s(?P<maxcommunity_limit>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp maxcommunity-limit {{ bgp.maxcommunity_limit|string }}", + "result": {"bgp": {"maxcommunity_limit": "{{ maxcommunity_limit }}"}}, + }, + { + "name": "bgp.maxextcommunity_limit", + "getval": re.compile( + r""" + \sbgp\smaxextcommunity-limit + (\s(?P<maxextcommunity_limit>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp maxextcommunity-limit {{ bgp.maxextcommunity_limit|string }}", + "result": {"bgp": {"maxextcommunity_limit": "{{ maxextcommunity_limit }}"}}, + }, + { + "name": "bgp.nexthop.route_map", + "getval": re.compile( + r""" + \sbgp\snexthop\sroute-map + (\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "bgp nexthop route-map {{ bgp.nexthop.route_map }}", + "result": {"bgp": {"nexthop": {"route_map": "{{ route_map }}"}}}, + }, + { + "name": "bgp.nexthop.trigger.delay", + "getval": re.compile( + r""" + \sbgp\snexthop\strigger\sdelay + (\s(?P<delay>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp nexthop trigger delay {{ bgp.nexthop.trigger.delay|string }}", + "result": {"bgp": {"nexthop": {"trigger": {"delay": "{{ delay }}"}}}}, + }, + { + "name": "bgp.nexthop.trigger.enable", + "getval": re.compile( + r""" + \sbgp\snexthop\strigger\senable + $""", + re.VERBOSE, + ), + "setval": "bgp nexthop trigger enable", + "result": {"bgp": {"nexthop": {"trigger": {"enable": True}}}}, + }, + { + "name": "bgp.nopeerup_delay_options.cold_boot", + "getval": re.compile( + r""" + \sbgp\snopeerup-delay\scold-boot + (\s(?P<cold_boot>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp nopeerup-delay cold-boot {{ bgp.nopeerup_delay_options.cold_boot|string }}", + "result": {"bgp": {"nopeerup_delay_options": {"cold_boot": "{{ cold_boot }}"}}}, + }, + { + "name": "bgp.nopeerup_delay_options.post_boot", + "getval": re.compile( + r""" + \sbgp\snopeerup-delay\spost-boot + (\s(?P<post_boot>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp nopeerup-delay post-boot {{ bgp.nopeerup_delay_options.post_boot|string }}", + "result": {"bgp": {"nopeerup_delay_options": {"post_boot": "{{ post_boot }}"}}}, + }, + { + "name": "bgp.nopeerup_delay_options.nsf_switchover", + "getval": re.compile( + r""" + \sbgp\snopeerup-delay\snsf-switchover + (\s(?P<nsf_switchover>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp nopeerup-delay nsf-switchover {{ bgp.nopeerup_delay_options.nsf_switchover|string }}", + "result": { + "bgp": {"nopeerup_delay_options": {"nsf_switchover": "{{ nsf_switchover }}"}}, + }, + }, + { + "name": "bgp.nopeerup_delay_options.user_initiated", + "getval": re.compile( + r""" + \sbgp\snopeerup-delay\suser-initiated + (\s(?P<user_initiated>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp nopeerup-delay user-initiated {{ bgp.nopeerup_delay_options.user_initiated|string }}", + "result": { + "bgp": {"nopeerup_delay_options": {"user_initiated": "{{ user_initiated }}"}}, + }, + }, + { + "name": "bgp.recursion", + "getval": re.compile( + r""" + \sbgp\srecursion\shost + $""", + re.VERBOSE, + ), + "setval": "bgp recursion host", + "result": {"bgp": {"recursion": True}}, + }, + { + "name": "bgp.redistribute_internal", + "getval": re.compile( + r""" + \sbgp\sredistribute-internal + $""", + re.VERBOSE, + ), + "setval": "bgp redistribute-internal", + "result": {"bgp": {"redistribute_internal": True}}, + }, + { + "name": "bgp.refresh.max_eor_time", + "getval": re.compile( + r""" + \sbgp\srefresh\smax-eor-time + (\s(?P<max_eor_time>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp refresh max-eor-time {{ bgp.refresh.max_eor_time|string }}", + "result": {"bgp": {"refresh": {"max_eor_time": "{{ max_eor_time }}"}}}, + }, + { + "name": "bgp.refresh.stalepath_time", + "getval": re.compile( + r""" + \sbgp\srefresh\sstalepath-time + (\s(?P<stalepath_time>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp refresh stalepath-time {{ bgp.refresh.stalepath_time|string }}", + "result": {"bgp": {"refresh": {"stalepath_time": "{{ stalepath_time }}"}}}, + }, + { + "name": "bgp.regexp", + "getval": re.compile( + r""" + \sbgp\sregexp\sdeterministic + $""", + re.VERBOSE, + ), + "setval": "bgp regexp deterministic", + "result": {"bgp": {"regexp": True}}, + }, + { + "name": "bgp.router_id", + "getval": re.compile( + r""" + \sbgp\srouter-id + (\s(?P<address>\S+))? + (\sinterface\s(?P<interface>\S+))? + (\svrf\s(?P<vrf>auto-assign))? + $""", + re.VERBOSE, + ), + "setval": "bgp router-id" + "{{ (' ' + bgp.router_id.address) if bgp.router_id.address is defined else '' }}" + "{{ (' interface ' + bgp.router_id.interface) if bgp.router_id.interface is defined else '' }}" + "{{ (' vrf auto-assign') if bgp.router_id.vrf|d(False) else '' }}", + "result": { + "bgp": { + "router_id": { + "address": "{{ address }}", + "interface": "{{ interface }}", + "vrf": "{{ not not vrf }}", + }, + }, + }, + }, + { + "name": "bgp.scan_time", + "getval": re.compile( + r""" + \sbgp\sscan-time + (\s(?P<scan_time>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp scan-time {{ bgp.scan_time|string }}", + "result": {"bgp": {"scan_time": "{{ scan_time }}"}}, + }, + { + "name": "bgp.slow_peer.detection.set", + "getval": re.compile( + r""" + \sbgp\sslow-peer\sdetection + $""", + re.VERBOSE, + ), + "setval": "bgp slow-peer detection", + "result": {"bgp": {"slow_peer": {"detection": {"set": True}}}}, + }, + { + "name": "bgp.slow_peer.detection.threshold", + "getval": re.compile( + r""" + \sbgp\sslow-peer\sdetection\sthreshold + (\s(?P<threshold>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp slow-peer detection threshold {{ bgp.slow_peer.detection.threshold|string }}", + "result": {"bgp": {"slow_peer": {"detection": {"threshold": "{{ threshold }}"}}}}, + }, + { + "name": "bgp.slow_peer.split_update_group", + "getval": re.compile( + r""" + \sbgp\sslow-peer\ssplit-update-group + (\s(?P<dynamic>dynamic))? + (\s(?P<permanent>permanent))? + $""", + re.VERBOSE, + ), + "setval": "bgp slow-peer split-update-group" + "{{ (' dynamic') if bgp.slow_peer.split_update_group.dynamic|d(False) else '' }}" + "{{ (' permanent') if bgp.slow_peer.split_update_group.permanent|d(False) else '' }}", + "result": { + "bgp": { + "slow_peer": { + "split_update_group": { + "dynamic": "{{ not not dynamic }}", + "permanent": "{{ not not permanent }}", + }, + }, + }, + }, + }, + { + "name": "bgp.snmp", + "getval": re.compile( + r""" + \sbgp\ssnmp\straps\sadd-type + $""", + re.VERBOSE, + ), + "setval": "bgp snmp traps add-type", + "result": {"bgp": {"snmp": True}}, + }, + { + "name": "bgp.sso", + "getval": re.compile( + r""" + \sbgp\ssso\sroute-refresh-enable + $""", + re.VERBOSE, + ), + "setval": "bgp sso route-refresh-enable", + "result": {"bgp": {"sso": True}}, + }, + { + "name": "bgp.soft_reconfig_backup", + "getval": re.compile( + r""" + \sbgp\ssoft-reconfig-backup + $""", + re.VERBOSE, + ), + "setval": "bgp soft-reconfig-backup ", + "result": {"bgp": {"soft_reconfig_backup": True}}, + }, + { + "name": "bgp.suppress_inactive", + "getval": re.compile( + r""" + \sbgp\ssuppress-inactive + $""", + re.VERBOSE, + ), + "setval": "bgp suppress-inactive", + "result": {"bgp": {"suppress_inactive": True}}, + }, + { + "name": "bgp.transport", + "getval": re.compile( + r""" + \sbgp\stransport\spath-mtu-discovery + $""", + re.VERBOSE, + ), + "setval": "bgp transport path-mtu-discovery", + "result": {"bgp": {"transport": True}}, + }, + { + "name": "bgp.update_delay", + "getval": re.compile( + r""" + \sbgp\supdate-delay + (\s(?P<update_delay>\d+))? + $""", + re.VERBOSE, + ), + "setval": "bgp update-delay {{ bgp.update_delay|string }}", + "result": {"bgp": {"update_delay": "{{ update_delay }}"}}, + }, + { + "name": "bgp.update_group", + "getval": re.compile( + r""" + \sbgp\supdate-group\ssplit\sas-override + $""", + re.VERBOSE, + ), + "setval": "bgp update-group split as-override", + "result": {"bgp": {"update_group": True}}, + }, + { + "name": "bgp.upgrade_cli.set", + "getval": re.compile( + r""" + \sbgp\supgrade-cli + $""", + re.VERBOSE, + ), + "setval": "bgp upgrade-cli", + "result": {"bgp": {"update_group": {"set": True}}}, + }, + { + "name": "bgp.upgrade_cli.af_mode", + "getval": re.compile( + r""" + \sbgp\supgrade-cli\saf-mode + $""", + re.VERBOSE, + ), + "setval": "bgp upgrade-cli af-mode", + "result": {"bgp": {"update_group": {"af_mode": True}}}, + }, + # bgp ends + # neighbor remote-as starts + { + "name": "neighbor_address", + "getval": re.compile( + r""" + \sneighbordel(?P<neighbor_address>\S+) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}", + "result": {"del_neighbor": True}, + }, + { + "name": "remote_as", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+) + \sremote-as\s(?P<remote_as>\S+) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' remote-as ' + remote_as|string) if remote_as is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "remote_as": "{{ remote_as }}", + "neighbor_address": "{{ neighbor_address }}", + }, + }, + }, + }, + { + "name": "peer_group", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+) + \speer-group\s(?P<peer_group>\S+) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' peer-group ' + peer_group) if peer_group|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "peer_group": "{{ peer_group }}", + "neighbor_address": "{{ neighbor_address }}", + }, + }, + }, + }, + { + "name": "bmp_activate", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+) + \s(?P<bmp_activate>bmp-activate) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} bmp-activate", + "result": { + "neighbors": { + "{{ neighbor_address }}": {"bmp_activate": "{{ not not bmp_activate }}"}, + }, + }, + }, + { + "name": "cluster_id", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+) + \s(?P<cluster_id>cluster-id) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} cluster-id", + "result": { + "neighbors": {"{{ neighbor_address }}": {"cluster_id": "{{ not not cluster_id }}"}}, + }, + }, + { + "name": "description", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+) + \sdescription\s(?P<description>.+$) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} description" + "{{ (' ' + description) if description is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "neighbor_address": "{{ neighbor_address }}", + "description": "{{ description }}", + }, + }, + }, + }, + { + "name": "disable_connected_check", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+) + \s(?P<disable_connected_check>disable-connected-check) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} disable-connected-check", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "disable_connected_check": "{{ not not disable_connected_check }}", + }, + }, + }, + }, + { + "name": "ebgp_multihop", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+) + \s(?P<enable>ebgp_multihop) + (\s(?P<hop_count>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} ebgp-multihop" + "{{ (' ' + hop_count|string) if hop_count is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "ebgp_multihop": { + "enable": "{{ not not enable }}", + "hop_count": "{{ hop_count }}", + }, + }, + }, + }, + }, + { + "name": "fall_over.bfd", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sfall-over + \s(?P<set>bfd) + (\s(?P<multi_hop>multi-hop))? + (\s(?P<single_hop>single-hop))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} fall-over" + "{{ (' bfd') if fall_over.bfd.set is defined else '' }}" + "{{ (' multi-hop') if fall_over.bfd.multi_hop is defined else '' }}" + "{{ (' single-hop') if fall_over.bfd.single_hop is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "fall_over": { + "bfd": { + "set": "{{ not not set }}", + "multi_hop": "{{ not not multi_hop }}", + "single_hop": "{{ not not single_hop }}", + }, + }, + }, + }, + }, + }, + { + "name": "fall_over.route_map", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sroute-map + \s(?P<route_map>\S+) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} route-map {{ fall_over.route_map }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "fall_over": {"route_map": "{{ not not route_map }}"}, + }, + }, + }, + }, + { + "name": "ha_mode", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sha-mode + \s(?P<set>graceful-restart) + (\s(?P<disable>disable))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} ha-mode" + "{{ (' graceful-restart') if ha_mode.set|d(False) is defined else '' }}" + "{{ (' disable') if ha_mode.disable is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "ha_mode": {"set": "{{ not not set }}", "disable": "{{ not not disable }}"}, + }, + }, + }, + }, + { + "name": "inherit", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sinherit\speer-session + \s(?P<inherit>\S+) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} inherit peer-session" + "{{ (' ' + inherit) if inherit is defined else '' }}", + "result": {"neighbors": {"{{ neighbor_address }}": {"inherit": "{{ inherit }}"}}}, + }, + { + "name": "local_as", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\s(?P<local_as>local-as) + (\s(?P<number>\S+))? + (\s(?P<dual_as>dual-as))? + (\s(?P<no_prepend>no-prepend))? + (\s(?P<replace_as>replace-as))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} local-as" + "{{ (' ' + local_as.number|string) if local_as.number is defined else '' }}" + "{{ (' dual-as') if local_as.dual_as is defined else '' }}" + "{{ (' no-prepend') if local_as.no_prepend.set is defined else '' }}" + "{{ (' replace-as') if local_as.no_prepend.replace_as is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "local_as": { + "set": "{{ not not local_as }}", + "number": "{{ number }}", + "dual_as": "{{ not not dual_as }}", + "no_prepend": { + "set": "{{ not not no_prepend }}", + "replace_as": "{{ not not replace_as }}", + }, + }, + }, + }, + }, + }, + { + "name": "log_neighbor_changes", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+) + \s(?P<set>log-neighbor-changes) + (\s(?P<disable>disable))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' log-neighbor-changes') if log_neighbor_changes.set is defined else '' }}" + "{{ (' disable') if log_neighbor_changes.disable is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "neighbor_address": "{{ neighbor_address }}", + "log_neighbor_changes": { + "set": "{{ not not set }}", + "disable": "{{ not not disable }}", + }, + }, + }, + }, + }, + { + "name": "password_options", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\spassword + \s(?P<encryption>\d+) + (\s(?P<pass_key>.$))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} password" + "{{ (' '+ password_options.encryption|string) if password_options.encryption is defined else '' }}" + "{{ (' '+ password_options.pass_key) if password_options.pass_key is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "neighbor_address": "{{ neighbor_address }}", + "password_options": { + "encryption": "{{ encryption }}", + "pass_key": "{{ pass_key }}", + }, + }, + }, + }, + }, + { + "name": "path_attribute.discard", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\spath-attribute\sdiscard + (\s(?P<type>\d+))? + (\srange\s(?P<start>\d+)\s(?P<end>\d+))? + (\s(?P<in>in))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} path-attribute discard" + "{{ (' ' + type) if path_attribute.discard.type is defined else '' }}" + "{{ (' range '+ path_attribute.discard.range.start|string) if spath_attribute.discard.range.start is defined else '' }}" + "{{ (' '+ path_attribute.discard.range.end|string) if spath_attribute.discard.range.end is defined else '' }}" + "{{ (' in') if path_attribute.discard.in|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "path_attribute": { + "discard": { + "type": "{{ type }}", + "range": {"start": "{{ start }}", "end": "{{ end }}"}, + "in": "{{ not not in }}", + }, + }, + }, + }, + }, + }, + { + "name": "path_attribute.treat_as_withdraw", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\spath-attribute\streat-as-withdraw + (\s(?P<type>\d+))? + (\srange\s(?P<start>\d+)\s(?P<end>\d+))? + (\s(?P<in>in))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} path-attribute treat-as-withdraw" + "{{ (' ' + type) if path_attribute.treat_as_withdraw.type is defined else '' }}" + "{{ (' range '+ path_attribute.treat_as_withdraw.range.start|string) if spath_attribute.treat_as_withdraw.range.start is defined else '' }}" + "{{ (' '+ path_attribute.treat_as_withdraw.range.end|string) if spath_attribute.treat_as_withdraw.range.end is defined else '' }}" + "{{ (' in') if path_attribute.treat_as_withdraw.in|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "path_attribute": { + "treat_as_withdraw": { + "type": "{{ type }}", + "range": {"start": "{{ start }}", "end": "{{ end }}"}, + "in": "{{ not not in }}", + }, + }, + }, + }, + }, + }, + { + "name": "shutdown", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sshutdown + (\sgraceful(?P<graceful>\d+))? + (\scommunity(?P<community>\d+))? + (\s(?P<local_preference>local-preference))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' shutdown') if shutdown.set|d(False) is defined else '' }}" + "{{ (' graceful '+ shutdown.graceful|string) if shutdown.graceful is defined else '' }}" + "{{ (' community '+ shutdown.community|string) if shutdown.community is defined else '' }}" + "{{ (' local-preference') if shutdown.local_preference|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "shutdown": { + "set": True, + "graceful": "{{ graceful }}", + "community": "{{ community }}", + "local_preference": "{{ not not local_preference }}", + }, + }, + }, + }, + }, + { + "name": "soft_reconfiguration", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\ssoft-reconfiguration\sinbound + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' soft-reconfiguration inbound') if soft_reconfiguration|d(False) else '' }}", + "result": {"neighbors": {"{{ neighbor_address }}": {"soft_reconfiguration": True}}}, + }, + { + "name": "ntimers", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\stimers + (\s(?P<interval>\d+))? + (\s(?P<holdtime>\d+))? + (\s(?P<min_holdtime>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} timers" + "{{ (' ' + ntimers.interval|string) if ntimers.interval is defined else '' }}" + "{{ (' ' + ntimers.holdtime|string) if ntimers.holdtime is defined else '' }}" + "{{ (' ' + ntimers.min_holdtime|string) if ntimers.min_holdtime is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "timers": { + "interval": "{{ interval }}", + "holdtime": "{{ holdtime }}", + "min_holdtime": "{{ min_holdtime }}", + }, + }, + }, + }, + }, + { + "name": "transport.connection_mode", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\stransport\sconnection-mode + (\s(?P<active>active))? + (\s(?P<passive>passive))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} transport connection-mode" + "{{ (' active') if transport.connection_mode.active|d(False) else '' }}" + "{{ (' passive') if transport.connection_mode.passive|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "transport": { + "connection_mode": { + "active": "{{ not not active }}", + "passive": "{{ not not passive }}", + }, + }, + }, + }, + }, + }, + { + "name": "transport.multi_session", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\stransport\smulti-session + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} transport multi-session", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "neighbor_address": "{{ neighbor_address }}", + "transport": {"multi_session": True}, + }, + }, + }, + }, + { + "name": "transport.path_mtu_discovery", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\stransport\spath-mtu-discovery + (\s(?P<disable>disable))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} transport" + "{{ (' path-mtu-discovery') if transport.path_mtu_discovery.set|d(False) else '' }}" + "{{ (' disable') if transport.path_mtu_discovery.disable|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "transport": { + "path_mtu_discovery": {"set": True, "disable": "{{ not not disable }}"}, + }, + }, + }, + }, + }, + { + "name": "ttl_security", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sttl-security + (\shops(?P<ttl_security>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} ttl-security" + "{{ (' hops '+ ttl_security|string) if ttl_security is defined else '' }}", + "result": { + "neighbors": {"{{ neighbor_address }}": {"ttl_security": "{{ ttl_security }}"}}, + }, + }, + { + "name": "unsuppress_map", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sunsuppress-map + (\s(?P<unsuppress_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} unsuppress-map" + "{{ (' ' + unsuppress_map) if unsuppress_map is defined else '' }}", + "result": { + "neighbors": {"{{ neighbor_address }}": {"unsuppress_map": "{{ unsuppress_map }}"}}, + }, + }, + { + "name": "update_source", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\supdate-source + (\s(?P<update_source>\S+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} update-source" + "{{ (' ' + update_source) if update_source is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "update_source": "{{ update_source }}", + "neighbor_address": "{{ neighbor_address }}", + }, + }, + }, + }, + { + "name": "version", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sversion + (\s(?P<version>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} version" + "{{ (' ' + version|string) if version is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "neighbor_address": "{{ neighbor_address }}", + "version": "{{ version }}", + }, + }, + }, + }, + { + "name": "weight", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sweight + (\s(?P<weight>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} weight" + "{{ (' ' + weight|string) if weight is defined else '' }}", + "result": {"neighbors": {"{{ neighbor_address }}": {"weight": "{{ weight }}"}}}, + }, + # neighbor remote-as ends + # neighbor peer-group starts + { + "name": "activate", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sactivate + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' activate') if activate|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "activate": True, + "neighbor_address": "{{ neighbor_address }}", + }, + }, + }, + }, + { + "name": "additional_paths", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sadditional-paths + (\s(?P<disable>disable))? + (\s(?P<receive>receive))? + (\s(?P<send>send))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} additional-paths" + "{{ (' disable') if additional_paths.disable|d(False) else '' }}" + "{{ (' receive') if additional_paths.receive|d(False) else '' }}" + "{{ (' send') if additional_paths.send|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "additional_paths": { + "disable": "{{ not not disable }}", + "receive": "{{ not not receive }}", + "send": "{{ not not send }}", + }, + }, + }, + }, + }, + { + "name": "advertise.additional_paths", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sadvertise\sadditional-paths + (\s(?P<all>all))? + (\sbest\s(?P<receive>\d+))? + (\s(?P<group_best>group-best))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} advertise additional-paths" + "{{ (' all') if advertise.additional_paths.all|d(False) else '' }}" + "{{ (' best '+ best|string) if advertise.additional_paths.best|d(False) else '' }}" + "{{ (' group-best') if advertise.additional_paths.group_best|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "advertise": { + "additional_paths": { + "all": "{{ not not all }}", + "best": "{{ receive }}", + "group_best": "{{ not not group_best }}", + }, + }, + }, + }, + }, + }, + { + "name": "advertise.best_external", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sadvertise\sbest-external + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' advertise best-external') if advertise.best_external|d(False) else '' }}", + "result": { + "neighbors": {"{{ neighbor_address }}": {"advertise": {"best-external": True}}}, + }, + }, + { + "name": "advertise.diverse_path", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sadvertise\sdiverse-path + (\s(?P<backup>backup))? + (\s(?P<mpath>mpath))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} advertise diverse-path" + "{{ (' backup') if advertise.diverse_path.backup|d(False) else '' }}" + "{{ (' mpath') if advertise.diverse_path.mpath|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "advertise": { + "diverse_path": { + "backup": "{{ not not backup }}", + "mpath": "{{ not not mpath }}", + }, + }, + }, + }, + }, + }, + { + "name": "advertise_map", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sadvertise-map + (\s(?P<name>\S+))? + (\sexist-map\s(?P<exist_map>\S+))? + (\snon-exist-map\s(?P<non_exist_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} advertise-map" + "{{ (' ' + name) if advertise_map.name is defined else '' }}" + "{{ (' exist-map ' + exist_map) if advertise_map.exist_map is defined else '' }}" + "{{ (' non-exist-map ' + non_exist_map) if advertise_map.non_exist_map is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "advertise_map": { + "name": "{{ name }}", + "exist_map": "{{ exist_map }}", + "non_exist_map": "{{ non_exist_map }}", + }, + }, + }, + }, + }, + { + "name": "advertisement_interval", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sadvertisement-interval + (\s(?P<advertisement_interval>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} advertisement-interval" + "{{ (' ' + advertisement_interval|string) if advertisement_interval is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "advertisement_interval": "{{ advertisement_interval }}", + }, + }, + }, + }, + { + "name": "aigp", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\saigp + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' aigp') if aigp.enable|d(False) else '' }}", + "result": {"neighbors": {"{{ neighbor_address }}": {"aigp": {"enable": True}}}}, + }, + { + "name": "aigp.send.cost_community", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\saigp\ssend\scost-community + (\s(?P<id>\d+))\spoi + (\s(?P<igp_cost>igp-cost))? + (\s(?P<pre_bestpath>pre-bestpath))? + (\s(?P<transitive>transitive))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} aigp send cost-community" + "{{ (' ' + aigp.send.cost_community.id|string + ' poi') if aigp.send.cost_community.id is defined else '' }}" + "{{ (' igp-cost') if aigp.send.cost_community.poi.igp_cost|d(False) else '' }}" + "{{ (' pre-bestpath') if aigp.send.cost_community.poi.pre_bestpath|d(False) else '' }}" + "{{ (' transitive') if aigp.send.cost_community.poi.transitive|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "aigp": { + "send": { + "cost_community": { + "id": "{{ id }}", + "poi": { + "igp_cost": "{{ not not igp_cost }}", + "pre_bestpath": "{{ not not pre_bestpath }}", + "transitive": "{{ not not transitive }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "aigp.send.med", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\saigp\ssend\smed + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' aigp send med') if aigp.send.med|d(False) else '' }}", + "result": {"neighbors": {"{{ neighbor_address }}": {"aigp": {"send": {"med": True}}}}}, + }, + { + "name": "allow_policy", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sallow-policy + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' allow-policy') if allow_policy|d(False) else '' }}", + "result": {"neighbors": {"{{ neighbor_address }}": {"allow_policy": True}}}, + }, + { + "name": "allowas_in", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sallowas-in + (\s(?P<allowas_in>\d+)) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' allowas-in ' + allowas_in|string) if allowas_in is defined else '' }}", + "result": {"neighbors": {"{{ neighbor_address }}": {"allowas_in": "{{ allowas_in }}"}}}, + }, + { + "name": "as_override", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sas-override + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }}" + "{{ (' as-override') if as_override|d(False) else '' }}", + "result": {"neighbors": {"{{ neighbor_address }}": {"as_override": True}}}, + }, + { + "name": "bmp_activate", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sbmp-activate + (\sserver\s(?P<server>\d+))? + (\s(?P<all>all))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} bmp-activate" + "{{ (' server '+ bmp_activate.server|string) if bmp_activate.server is defined else '' }}" + "{{ (' all') if bmp_activate.all|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "neighbor_address": "{{ neighbor_address }}", + "bmp_activate": {"server": "{{ server }}", "all": "{{ not not all }}"}, + }, + }, + }, + }, + { + "name": "capability", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\scapability\sorf\sprefix-list + (\s(?P<both>both))? + (\s(?P<receive>receive))? + (\s(?P<send>send))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} capability orf prefix-list" + "{{ (' both') if capability.both|d(False) else '' }}" + "{{ (' receive') if capability.receive|d(False) else '' }}" + "{{ (' send') if capability.send|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "capability": { + "both": "{{ not not both }}", + "receive": "{{ not not receive }}", + "send": "{{ not not send }}", + }, + }, + }, + }, + }, + { + "name": "default_originate", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sdefault-originate + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' default-originate') if default_originate.set|d(False) else '' }}", + "result": { + "neighbors": {"{{ neighbor_address }}": {"default_originate": {"set": True}}}, + }, + }, + { + "name": "default_originate.route_map", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sdefault-originate + (\sroute-map\s(?P<route_map>\S+)) + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} default-originate" + "{{ (' route-map' + default_originate.route_map) if default_originate.route_map is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "default-originate": {"route_map": "{{ route_map }}"}, + }, + }, + }, + }, + { + "name": "distribute_list", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sdistribute-list + (\s(?P<acl>\S+)) + (\s(?P<in>in))? + (\s(?P<out>out))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} distribute-list" + "{{ (' ' + distribute_list.acl) if distribute_list.acl is defined else '' }}" + "{{ (' in') if distribute_list.in|d(False) else '' }}" + "{{ (' out') if distribute_list.out|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "distribute_list": { + "acl": "{{ acl }}", + "in": "{{ not not in }}", + "out": "{{ not not out }}", + }, + }, + }, + }, + }, + { + "name": "dmzlink_bw", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sdmzlink-bw + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' dmzlink-bw') if dmzlink_bw|d(False) else '' }}", + "result": {"neighbors": {"{{ neighbor_address }}": {"dmzlink_bw": True}}}, + }, + { + "name": "filter_list", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sfilter-list + (\s(?P<acl>\S+)) + (\s(?P<in>in))? + (\s(?P<out>out))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} filter-list" + "{{ (' ' + filter_list.path_acl) if filter_list.path_acl is defined else '' }}" + "{{ (' in') if filter_list.in|d(False) else '' }}" + "{{ (' out') if filter_list.out|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "filter_list": { + "path_acl": "{{ acl }}", + "in": "{{ not not in }}", + "out": "{{ not not out }}", + }, + }, + }, + }, + }, + { + "name": "maximum_prefix", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\smaximum-prefix + (\s(?P<max_no>\d+)) + (\s(?P<threshold_val>\d+))? + (\srestart(?P<restart>\d+))? + (\s(?P<warning_only>warning-only))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} maximum-prefix" + "{{ (' ' + maximum_prefix.max_no|string) if maximum_prefix.max_no is defined else '' }}" + "{{ (' ' + maximum_prefix.threshold_val|string) if maximum_prefix.threshold_val is defined else '' }}" + "{{ (' restart ' + maximum_prefix.restart|string) if maximum_prefix.restart is defined else '' }}" + "{{ (' warning-only') if maximum_prefix.warning_only|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "maximum_prefix": { + "max_no": "{{ max_no }}", + "threshold_val": "{{ threshold_val }}", + "restart": "{{ restart }}", + "warning_only": "{{ not not warning_only }}", + }, + }, + }, + }, + }, + { + "name": "next_hop_self.set", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\snext-hop-self + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' next-hop-self') if next_hop_self.set|d(False) else '' }}", + "result": {"neighbors": {"{{ neighbor_address }}": {"next_hop_self": {"set": True}}}}, + }, + { + "name": "next_hop_self.all", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\snext-hop-self\sall + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' next-hop-self all') if next_hop_self.all|d(False) else '' }}", + "result": {"neighbors": {"{{ neighbor_address }}": {"next_hop_self": {"all": True}}}}, + }, + { + "name": "next_hop_unchanged.set", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\snext-hop-unchanged + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' next-hop-unchanged') if next_hop_unchanged.set|d(False) else ''}}", + "result": { + "neighbors": {"{{ neighbor_address }}": {"next_hop_unchanged": {"set": True}}}, + }, + }, + { + "name": "next_hop_unchanged.allpaths", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\snext-hop-unchanged\sallpaths + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' next-hop-unchanged allpaths') if next_hop_unchanged.allpaths|d(False) else ''}}", + "result": { + "neighbors": {"{{ neighbor_address }}": {"next_hop_unchanged": {"allpaths": True}}}, + }, + }, + { + "name": "remove_private_as.set", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sremove-private-as + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' remove-private-as') if remove_private_as.set|d(False) else '' }}", + "result": { + "neighbors": {"{{ neighbor_address }}": {"remove_private_as": {"set": True}}}, + }, + }, + { + "name": "remove_private_as.all", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sremove-private-as\sall + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' remove-private-as all') if remove_private_as.all|d(False) else '' }}", + "result": { + "neighbors": {"{{ neighbor_address }}": {"remove_private_as": {"all": True}}}, + }, + }, + { + "name": "remove_private_as.replace_as", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sremove-private-as\sreplace-as + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' remove-private-as replace-as') if remove_private_as.replace_as|d(False) else ''}}", + "result": { + "neighbors": { + "{{ neighbor_address }}": {"remove_private_as": {"replace_as": True}}, + }, + }, + }, + { + "name": "route_maps", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sroute-map + (\s(?P<route_maps>\S+)) + (\s(?P<in>in))? + (\s(?P<out>out))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ route_maps.neighbor_address }} route-map" + "{{ (' ' + route_maps.name) if route_maps.name is defined else '' }}" + "{{ (' in') if route_maps.in|d(False) else '' }}" + "{{ (' out') if route_maps.out|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "route_maps": [ + { + "name": "{{ route_maps }}", + "in": "{{ not not in }}", + "out": "{{ not not out }}", + }, + ], + }, + }, + }, + }, + { + "name": "route_reflector_client", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sroute-reflector-client + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' route-reflector-client') if route_reflector_client|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "neighbor_address": "{{ neighbor_address }}", + "route_reflector_client": True, + }, + }, + }, + }, + { + "name": "route_server_client.set", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sroute-server-client + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' route-server-client') if route_server_client.set|d(False) else '' }}", + "result": { + "neighbors": {"{{ neighbor_address }}": {"route_server_client": {"set": True}}}, + }, + }, + { + "name": "route_server_client.context", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sroute-server-client + (\scontext(?P<context>\S+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} route-server-client" + "{{ (' context ' + route_server_client.context) if route_server_client.context is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": {"route_server_client": {"context": "{{ context }}"}}, + }, + }, + }, + { + "name": "send_community.set", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\ssend-community + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' send-community') if send_community.set|d(False) else '' }}", + "result": {"neighbors": {"{{ neighbor_address }}": {"send_community": {"set": True}}}}, + }, + { + "name": "send_community.both", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\ssend-community\sboth + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} send-community" + "{{ (' both') if send_community.both|d(False) else '' }}", + "result": {"neighbors": {"{{ neighbor_address }}": {"send_community": {"both": True}}}}, + }, + { + "name": "send_community.extended", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\ssend-community\sextended + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} send-community" + "{{ (' extended') if send_community.extended|d(False) else '' }}", + "result": { + "neighbors": {"{{ neighbor_address }}": {"send_community": {"extended": True}}}, + }, + }, + { + "name": "send_community.standard", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\ssend-community\sstandard + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} send-community" + "{{ (' standard') if send_community.standard|d(False) else '' }}", + "result": { + "neighbors": {"{{ neighbor_address }}": {"send_community": {"standard": True}}}, + }, + }, + { + "name": "send_label.set", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\ssend-label + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' send-label') if send_label.set|d(False) else '' }}", + "result": {"neighbors": {"{{ neighbor_address }}": {"send_label": {"set": True}}}}, + }, + { + "name": "send_label.explicit_null", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sexplicit-null + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} send-label" + "{{ (' explicit-null') if send_label.explicit_null|d(False) else '' }}", + "result": { + "neighbors": {"{{ neighbor_address }}": {"send_label": {"explicit_null": True}}}, + }, + }, + { + "name": "slow_peer.detection", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sslow-peer\sdetection + (\s(?P<enable>enable))? + (\s(?P<disable>disable))? + (\sthreshold\s(?P<threshold>\d+))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} slow-peer detection" + "{{ (' enable') if slow_peer.detection.enable|d(False) else '' }}" + "{{ (' disable') if slow_peer.detection.disable|d(False) else '' }}" + "{{ (' threshold ' + slow_peer.detection.threshold|string) if slow_peer.detection.threshold is defined else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "slow_peer": { + "detection": { + "enable": "{{ not not enable }}", + "disable": "{{ not not disable }}", + "threshold": "{{ threshold }}", + }, + }, + }, + }, + }, + }, + { + "name": "slow_peer.split_update_group", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\sslow-peer\ssplit-update-group + (\s(?P<static>static))? + (\s(?P<dynamic>dynamic))? + (\s(?P<disable>disable))? + (\s(?P<permanent>permanent))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} slow-peer split-update-group" + "{{ (' static') if slow_peer.split_update_group.static|d(False) else '' }}" + "{{ (' dynamic') if slow_peer.split_update_group.dynamic.enable|d(False) else '' }}" + "{{ (' disable') if slow_peer.split_update_group.dynamic.disable|d(False) else '' }}" + "{{ (' permanent') if slow_peer.split_update_group.dynamic.permanent|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "slow_peer": { + "split_update_group": { + "static": "{{ not not static }}", + "dynamic": { + "enable": "{{ not not dynamic }}", + "disable": "{{ not not disable }}", + "permanent": "{{ not not permanent }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "translate_update.set", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\stranslate-update + $""", + re.VERBOSE, + ), + "setval": "{{ ('neighbor ' + neighbor_address + ' translate_update') if translate_update.set|d(False) else '' }}", + "result": { + "neighbors": {"{{ neighbor_address }}": {"translate_update": {"set": True}}}, + }, + }, + { + "name": "translate_update.nlri", + "getval": re.compile( + r""" + \sneighbor\s(?P<neighbor_address>\S+)\stranslate-update\snlri + (\s(?P<multicast>multicast))? + (\s(?P<unicast>unicast))? + $""", + re.VERBOSE, + ), + "setval": "neighbor {{ neighbor_address }} translate-update nlri" + "{{ (' multicast') if translate_update.nlri.multicast|d(False) else '' }}" + "{{ (' unicast') if translate_update.nlri.unicast|d(False) else '' }}", + "result": { + "neighbors": { + "{{ neighbor_address }}": { + "translate_update": { + "nlri": { + "multicast": "{{ not not multicast }}", + "unicast": "{{ not not unicast }}", + }, + }, + }, + }, + }, + }, + # neighbor peer-group ends + # redistribute starts + { + "name": "application", + "getval": re.compile( + r""" + \sredistribute\sapplication\s(?P<name>\S+) + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute application {{ application.name }}" + "{{ (' metric ' + application.metric|string) if application.metric is defined else '' }}" + "{{ (' route-map ' + application.route_map) if application.route_map is defined else '' }}", + "remval": "redistribute application {{ application.name }}", + "result": { + "redistribute": [ + { + "application": { + "name": "{{ name }}", + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + { + "name": "bgp", + "getval": re.compile( + r""" + \sredistribute\sbgp\s(?P<name>\S+) + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute bgp {{ bgp.name }}" + "{{ (' metric ' + bgp.metric|string) if bgp.metric is defined else '' }}" + "{{ (' route-map ' + bgp.route_map) if bgp.route_map is defined else '' }}", + "remval": "redistribute bgp {{ bgp.name }}", + "result": { + "redistribute": [ + { + "bgp": { + "name": "{{ name }}", + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + { + "name": "connected", + "getval": re.compile( + r""" + \sredistribute\sconnected + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute connected" + "{{ (' metric ' + connected.metric|string) if connected.metric is defined else '' }}" + "{{ (' route-map ' + connected.route_map) if connected.route_map is defined else '' }}", + "remval": "redistribute connected", + "result": { + "redistribute": [ + { + "connected": { + "set": True, + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + { + "name": "eigrp", + "getval": re.compile( + r""" + \sredistribute\seigrp\s(?P<name>\S+) + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute eigrp {{ eigrp.name|string }}" + "{{ (' metric ' + eigrp.metric|string) if eigrp.metric is defined else '' }}" + "{{ (' route-map ' + eigrp.route_map) if eigrp.route_map is defined else '' }}", + "remval": "redistribute eigrp {{ eigrp.name|string }}", + "result": { + "redistribute": [ + { + "eigrp": { + "as_number": "{{ name }}", + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + { + "name": "isis", + "getval": re.compile( + r""" + \sredistribute\sisis\s(?P<name>\S+) + (\s(?P<clns>clns))? + (\s(?P<ip>ip))? + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute isis {{ isis.area_tag }}" + "{{ (' clns') if isis.clns|d(False) else '' }}" + "{{ (' ip') if isis.ip|d(False) else '' }}" + "{{ (' metric ' + isis.metric|string) if isis.metric is defined else '' }}" + "{{ (' route-map ' + isis.route_map) if isis.route_map is defined else '' }}", + "remval": "redistribute isis {{ isis.area_tag }}", + "result": { + "redistribute": [ + { + "isis": { + "area_tag": "{{ name }}", + "clns": "{{ not not clns }}", + "ip": "{{ not not ip }}", + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + { + "name": "iso_igrp", + "getval": re.compile( + r""" + \sredistribute\siso-igrp\s(?P<name>\S+) + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute iso-igrp {{ iso_igrp.area_tag }}" + "{{ (' route-map ' + iso_igrp.route_map) if iso_igrp.route_map is defined else '' }}", + "remval": "redistribute iso-igrp {{ iso_igrp.area_tag }}", + "result": { + "redistribute": [ + {"iso_igrp": {"area_tag": "{{ name }}", "route_map": "{{ route_map }}"}}, + ], + }, + }, + { + "name": "lisp", + "getval": re.compile( + r""" + \sredistribute\slisp + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute lisp" + "{{ (' metric ' + lisp.metric|string) if lisp.metric is defined else '' }}" + "{{ (' route-map ' + lisp.route_map) if lisp.route_map is defined else '' }}", + "remval": "redistribute lisp", + "result": { + "redistribute": [ + { + "lisp": { + "set": True, + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + { + "name": "mobile", + "getval": re.compile( + r""" + \sredistribute\smobile + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute mobile" + "{{ (' metric ' + mobile.metric|string) if mobile.metric is defined else '' }}" + "{{ (' route-map ' + mobile.route_map) if mobile.route_map is defined else '' }}", + "remval": "redistribute mobile", + "result": { + "redistribute": [ + { + "mobile": { + "set": True, + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + { + "name": "odr", + "getval": re.compile( + r""" + \sredistribute\sodr + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute odr" + "{{ (' metric ' + odr.metric|string) if odr.metric is defined else '' }}" + "{{ (' route-map ' + odr.route_map) if odr.route_map is defined else '' }}", + "remval": "redistribute odr", + "result": { + "redistribute": [ + { + "odr": { + "set": True, + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + { + "name": "ospf", + "getval": re.compile( + r""" + \sredistribute\sospf\s(?P<process_id>\S+) + (\s(?P<type_1>1))? + (\s(?P<type_2>2))? + (\s(?P<external>external))? + (\s(?P<internal>internal))? + (\s(?P<nssa_external>nssa-external))? + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + (\svrf\s(?P<vrf>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute ospf {{ ospf.process_id }}" + "{{ (' 1') if ospf.match.type_1|d(False) else '' }}" + "{{ (' 2') if ospf.match.type_2|d(False) else '' }}" + "{{ (' external') if ospf.match.external|d(False) else '' }}" + "{{ (' internal') if ospf.match.internal|d(False) else '' }}" + "{{ (' nssa-external') if ospf.match.nssa_external|d(False) else '' }}" + "{{ (' metric ' + ospf.metric|string) if ospf.metric is defined else '' }}" + "{{ (' route-map ' + ospf.route_map) if ospf.route_map is defined else '' }}" + "{{ (' vrf ' + ospf.vrf) if ospf.vrf is defined else '' }}", + "remval": "redistribute ospf {{ ospf.process_id }}", + "result": { + "redistribute": [ + { + "ospf": { + "process_id": "{{ process_id }}", + "match": { + "type_1": "{{ not not type_1 }}", + "type_2": "{{ not not type_2 }}", + "external": "{{ not not external }}", + "internal": "{{ not not internal }}", + "nssa_external": "{{ not not nssa_external }}", + }, + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + "vrf": "{{ vrf }}", + }, + }, + ], + }, + }, + { + "name": "ospfv3", + "getval": re.compile( + r""" + \sredistribute\sospfv3\s(?P<process_id>\S+) + (\s(?P<type_1>1))? + (\s(?P<type_2>2))? + (\s(?P<external>external))? + (\s(?P<internal>internal))? + (\s(?P<nssa_external>nssa-external))? + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute ospfv3 {{ ospfv3.process_id }}" + "{{ (' 1') if ospfv3.match.type_1|d(False) else '' }}" + "{{ (' 2') if ospfv3.match.type_2|d(False) else '' }}" + "{{ (' external') if ospfv3.match.external|d(False) else '' }}" + "{{ (' internal') if ospfv3.match.internal|d(False) else '' }}" + "{{ (' nssa-external') if ospfv3.match.nssa_external|d(False) else '' }}" + "{{ (' metric ' + ospfv3.metric|string) if ospfv3.metric is defined else '' }}" + "{{ (' route-map ' + ospfv3.route_map) if ospfv3.route_map is defined else '' }}", + "remval": "redistribute ospfv3 {{ ospfv3.process_id }}", + "result": { + "redistribute": [ + { + "ospfv3": { + "process_id": "{{ process_id }}", + "match": { + "type_1": "{{ not not type_2 }}", + "type_2": "{{ not not type_2 }}", + "external": "{{ not not external }}", + "internal": "{{ not not internal }}", + "nssa_external": "{{ not not nssa_external }}", + }, + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + { + "name": "rip", + "getval": re.compile( + r""" + \sredistribute\srip + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute rip" + "{{ (' metric ' + rip.metric|string) if rip.metric is defined else '' }}" + "{{ (' route-map ' + rip.route_map) if rip.route_map is defined else '' }}", + "remval": "redistribute rip", + "result": { + "redistribute": [ + { + "rip": { + "set": True, + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + { + "name": "static", + "getval": re.compile( + r""" + \sredistribute\sstatic + (\s(?P<clns>clns))? + (\s(?P<ip>ip))? + (\smetric\s(?P<metric>\d+))? + (\sroute-map\s(?P<route_map>\S+))? + $""", + re.VERBOSE, + ), + "setval": "redistribute static" + "{{ (' clns') if static.clns|d(False) else '' }}" + "{{ (' ip') if static.ip|d(False) else '' }}" + "{{ (' metric ' + static.metric|string) if static.metric is defined else '' }}" + "{{ (' route-map ' + static.route_map) if static.route_map is defined else '' }}", + "remval": "redistribute static", + "result": { + "redistribute": [ + { + "static": { + "set": True, + "clns": "{{ not not clns }}", + "ip": "{{ not not ip }}", + "metric": "{{ metric }}", + "route_map": "{{ route_map }}", + }, + }, + ], + }, + }, + { + "name": "vrf", + "getval": re.compile( + r""" + \sredistribute\svrf + (\s(?P<name>\S+))? + (\s(?P<global>global))? + $""", + re.VERBOSE, + ), + "setval": "redistribute vrf {{ vrf.name }}" + "{{ (' global') if vrf.global|d(False) else '' }}", + "remval": "redistribute vrf {{ vrf.name }}", + "result": { + "redistribute": [{"vrf": {"name": "{{ name }}", "global": "{{ not not global }}"}}], + }, + }, + # redistribute ends + ] diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/hostname.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/hostname.py new file mode 100644 index 000000000..bf922fb4c --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/hostname.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Hostname parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +class HostnameTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(HostnameTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + # fmt: off + PARSERS = [ + { + "name": "hostname", + "getval": re.compile( + r""" + ^hostname\s(?P<hostname>\S+) + $""", re.VERBOSE, + ), + "setval": "hostname {{ hostname }}", + "result": { + "hostname": "{{ hostname }}", + }, + }, + ] + # fmt: on diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/interfaces.py new file mode 100644 index 000000000..084945143 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/interfaces.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Interfaces parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +class InterfacesTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(InterfacesTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + # fmt: off + PARSERS = [ + { + 'name': 'interface', + 'getval': re.compile( + r''' + ^interface\s + (?P<name>\S+)$''', re.VERBOSE, + ), + 'setval': 'interface {{ name }}', + 'result': { + '{{ name }}': { + 'name': '{{ name }}', + }, + }, + 'shared': True, + }, + { + "name": "description", + "getval": re.compile( + r""" + \s+description\s(?P<description>.+$) + $""", re.VERBOSE, + ), + "setval": "description {{ description }}", + "result": { + '{{ name }}': { + 'description': '{{ description }}', + }, + }, + }, + { + "name": "enabled", + "getval": re.compile( + r""" + (?P<negate>\sno)? + (?P<shutdown>\sshutdown) + $""", re.VERBOSE, + ), + "setval": "shutdown", + "result": { + '{{ name }}': { + 'enabled': "{{ False if shutdown is defined and negate is not defined else True }}", + }, + }, + }, + { # only applicable for switches + "name": "mode", + "getval": re.compile( + r""" + (?P<negate>\sno)? + (?P<switchport>\sswitchport) + $""", re.VERBOSE, + ), + "setval": "switchport", + "result": { + '{{ name }}': { + 'mode': "{{ 'layer2' if switchport is defined and negate is not defined else 'layer3' }}", + }, + }, + }, + { + "name": "speed", + "getval": re.compile( + r""" + \s+speed\s(?P<speed>.+$) + $""", re.VERBOSE, + ), + "setval": "speed {{ speed|string }}", + "result": { + '{{ name }}': { + 'speed': '{{ speed }}', + }, + }, + }, + { + "name": "mtu", + "getval": re.compile( + r""" + \s+mtu\s(?P<mtu>.+$) + $""", re.VERBOSE, + ), + "setval": "mtu {{ mtu|string }}", + "result": { + '{{ name }}': { + 'mtu': '{{ mtu }}', + }, + }, + }, + { + "name": "duplex", + "getval": re.compile( + r""" + \s+duplex\s(?P<duplex>full|half|auto) + $""", re.VERBOSE, + ), + "setval": "duplex {{ duplex }}", + "result": { + '{{ name }}': { + 'duplex': '{{ duplex }}', + }, + }, + }, + { + "name": "template", + "getval": re.compile( + r""" + \s+source\stemplate\s(?P<template>.+$) + """, re.VERBOSE, + ), + "setval": "source template {{ template }}", + "result": { + '{{ name }}': { + 'template': '{{ template }}', + }, + }, + }, + ] + # fmt: on diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/l2_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/l2_interfaces.py new file mode 100644 index 000000000..30ddb405c --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/l2_interfaces.py @@ -0,0 +1,250 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The L2_interfaces parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +class L2_interfacesTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(L2_interfacesTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + # fmt: off + PARSERS = [ + { + "name": "name", + "getval": re.compile( + r"""^interface + (\s(?P<name>\S+)) + $""", + re.VERBOSE, + ), + "compval": "name", + "setval": "interface {{ name }}", + "result": {"{{ name }}": {"name": "{{ name }}"}}, + "shared": True, + }, + { + "name": "access.vlan", + "getval": re.compile( + r""" + \s+switchport\saccess\svlan\s(?P<vlan>\d+) + $""", re.VERBOSE, + ), + "setval": "switchport access vlan {{ access.vlan }}", + "remval": "switchport access vlan", + "result": { + "{{ name }}": { + "access": { + "vlan": "{{ vlan }}", + }, + }, + }, + }, + { + "name": "access.vlan_name", + "getval": re.compile( + r""" + \s+switchport\saccess\svlan\sname\s(?P<vlan_name>\S+) + $""", re.VERBOSE, + ), + "setval": "switchport access vlan name {{ access.vlan_name }}", + "result": { + "{{ name }}": { + "access": { + "vlan_name": "{{ vlan_name }}", + }, + }, + }, + }, + { + "name": "voice.vlan", + "getval": re.compile( + r""" + \s+switchport\svoice\svlan\s(?P<vlan>\d+) + $""", re.VERBOSE, + ), + "setval": "switchport voice vlan {{ voice.vlan|string }}", + "remval": "switchport voice vlan", + "result": { + "{{ name }}": { + "voice": { + "vlan": "{{ vlan }}", + }, + }, + }, + }, + { + "name": "voice.vlan_tag", + "getval": re.compile( + r""" + \s+switchport\svoice\svlan\s(?P<vlan_tag>dot1p|none|untagged) + $""", re.VERBOSE, + ), + "setval": "switchport voice vlan {{ voice.vlan_tag }}", + "remval": "switchport voice vlan", + "result": { + "{{ name }}": { + "voice": { + "vlan_tag": "{{ vlan_tag }}", + }, + }, + }, + }, + { + "name": "voice.vlan_name", + "getval": re.compile( + r""" + \s+switchport\svoice\svlan\sname\s(?P<vlan_name>\S+) + $""", re.VERBOSE, + ), + "setval": "switchport voice vlan name {{ voice.vlan_name }}", + "result": { + "{{ name }}": { + "voice": { + "vlan_name": "{{ vlan_name }}", + }, + }, + }, + }, + { + "name": "mode", + "getval": re.compile( + r""" + \s+switchport\smode\s(?P<mode>access|trunk|dynamic|dynamic\sauto|dynamic\sdesirable|private-vlan\shost|private-vlan\spromiscuous|private-vlan\strunk\ssecondary|dot1q-tunnel) + $""", re.VERBOSE, + ), + "setval": "switchport mode " + "{{ 'trunk' if mode == 'trunk' }}" + "{{ 'access' if mode == 'access' }}" + "{{ 'dynamic' if mode == 'dynamic' }}" + "{{ 'dynamic desirable' if mode == 'dynamic_desirable' }}" + "{{ 'dynamic auto' if mode == 'dynamic_auto' }}" + "{{ 'dot1q-tunnel' if mode == 'dot1q_tunnel' }}" + "{{ 'private-vlan host' if mode == 'private_vlan_host' }}" + "{{ 'private-vlan promiscuous' if mode == 'private_vlan_promiscuous' }}" + "{{ 'private-vlan trunk secondary' if mode == 'private_vlan_trunk' }}", + "remval": "switchport mode", + "result": { + "{{ name }}": { + "mode": "{{ mode }}", + }, + }, + }, + { + "name": "trunk.allowed_vlans", + "getval": re.compile( + r""" + \s+switchport\strunk\sallowed\svlan\s(?P<allowed_vlan>\S+) + $""", re.VERBOSE, + ), + "setval": "", + "result": { + "{{ name }}": { + "trunk": { + "allowed_vlans": "{{ allowed_vlan.split(',') }}", + }, + }, + }, + }, + { + "name": "trunk.allowed_vlans_add", + "getval": re.compile( + r""" + \s+switchport\strunk\sallowed\svlan\sadd\s(?P<allowed_vlans_add>\S+$) + """, re.VERBOSE, + ), + "setval": "", + "result": { + "{{ name }}": { + "trunk": { + "allowed_vlans_add": ["{{ allowed_vlans_add.split(',') }}"], + }, + }, + }, + }, + { + "name": "trunk.encapsulation", + "getval": re.compile( + r""" + \s+switchport\strunk\sencapsulation\s(?P<encapsulation>dot1q|isl|negotiate) + $""", re.VERBOSE, + ), + "setval": "switchport trunk encapsulation {{ trunk.encapsulation }}", + "remval": "switchport trunk encapsulation", + "result": { + "{{ name }}": { + "trunk": { + "encapsulation": "{{ encapsulation }}", + }, + }, + }, + }, + { + "name": "trunk.native_vlan", + "getval": re.compile( + r""" + \s+switchport\strunk\snative\svlan\s(?P<native_vlan>\d+) + $""", re.VERBOSE, + ), + "setval": "switchport trunk native vlan {{ trunk.native_vlan }}", + "remval": "switchport trunk native vlan", + "result": { + "{{ name }}": { + "trunk": { + "native_vlan": "{{ native_vlan }}", + }, + }, + }, + }, + { + "name": "trunk.pruning_vlans", + "getval": re.compile( + r""" + \s+switchport\strunk\spruning\svlan\s(?P<pruning_vlans>\S+) + $""", re.VERBOSE, + ), + "setval": "", + "result": { + "{{ name }}": { + "trunk": { + "pruning_vlans": "{{ pruning_vlans.split(',') }}", + }, + }, + }, + }, + { + "name": "trunk.pruning_vlans_add", + "getval": re.compile( + r""" + \s+switchport\strunk\spruning\svlan\sadd\s(?P<pruning_vlans_add>\S+) + $""", re.VERBOSE, + ), + "setval": "", + "result": { + "{{ name }}": { + "trunk": { + "pruning_vlans_add": ["{{ pruning_vlans_add.split(',') }}"], + }, + }, + }, + }, + ] + # fmt: on diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/l3_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/l3_interfaces.py new file mode 100644 index 000000000..d57a8ded8 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/l3_interfaces.py @@ -0,0 +1,231 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The L3_interfaces parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +def ip_tmplt(config_data): + cmd = "ipv6 address {ip}" + if config_data.get("ipv6"): + config = config_data.get("ipv6") + cmd = cmd.format(ip=config["address"]) + if config.get("segment_routing"): + # if config.get("segment_routing").get("enable"): + cmd += " segment-routing" + if config.get("segment_routing").get("default"): + cmd += " default" + if config.get("segment_routing").get("ipv6_sr"): + cmd += " ipv6-sr" + if config.get("secondary"): + cmd += " secondary" + if config.get("link_local"): + cmd += " link-local" + if config.get("anycast"): + cmd += " anycast" + if config.get("cga"): + cmd += " cga" + if config.get("eui"): + cmd += " eui" + return cmd + + +def ipv4_dhcp(config_data): + _data = config_data.get("ipv4", {}).get("dhcp") + if not _data.get("enable", True): + return "" + cmd = "ip address dhcp" + if _data.get("client_id"): + cmd += " client-id {client_id}".format(**_data) + if _data.get("hostname"): + cmd += " hostname {hostname}".format(**_data) + return cmd + + +class L3_interfacesTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(L3_interfacesTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + # fmt: off + PARSERS = [ + { + "name": "name", + "getval": re.compile( + r"""^interface + (\s(?P<name>\S+)) + $""", + re.VERBOSE, + ), + "compval": "name", + "setval": "interface {{ name }}", + "result": {"{{ name }}": {"name": "{{ name }}"}}, + "shared": True, + }, + { + "name": "ipv4.address", + "getval": re.compile( + r"""\s+ip\saddress + (\s(?P<ipv4>\S+)) + (\s(?P<netmask>\S+)) + (\s(?P<secondary>secondary))? + $""", + re.VERBOSE, + ), + "setval": "ip address {{ ipv4.address }}" + "{{ ' secondary' if ipv4.secondary|d(False) else ''}}", + "result": { + "{{ name }}": { + "ipv4": [ + { + "address": "{{ ipv4 }}", + "netmask": "{{ netmask }}", + "secondary": "{{ True if secondary is defined }}", + }, + ], + }, + }, + }, + { + "name": "ipv4.pool", + "getval": re.compile( + r""" + \s+ip\saddress\spool\s(?P<pool>.+$) + $""", re.VERBOSE, + ), + "setval": "ip address pool {{ ipv4.pool }}", + "result": { + "{{ name }}": { + "ipv4": [ + { + "pool": "{{ pool }}", + }, + ], + }, + }, + }, + { + "name": "ipv4.dhcp", + "getval": re.compile( + r"""\s+ip\saddress\s + ((?P<dhcp>dhcp)) + (\sclient-id\s(?P<client_id>\S+))? + (\shostname\s(?P<hostname>\S+))? + $""", + re.VERBOSE, + ), + "setval": ipv4_dhcp, + "result": { + "{{ name }}": { + "ipv4": [ + { + "dhcp": { + "enable": "{{ True if dhcp is defined }}", + "client_id": "{{ client_id }}", + "hostname": "{{ hostname }}", + }, + }, + ], + }, + }, + }, + { + "name": "ipv6.address", + "getval": re.compile( + r"""\s+ipv6\saddress + (\s(?P<ipv6>\b(?!autoconfig\b)\S+)) + (\s(?P<link_local>link-local))? + (\s(?P<anycast>anycast))? + (\s(?P<cga>cga))? + (\s(?P<eui>eui))? + (\s(?P<enable>segment-routing))? + (\s(?P<default>default))? + (\s(?P<ipv6_sr>ipv6-sr))? + $""", + re.VERBOSE, + ), + "setval": ip_tmplt, + "result": { + "{{ name }}": { + "ipv6": [ + { + "address": "{{ ipv6 }}", + "link_local": "{{ True if link_local is defined }}", + "anycast": "{{ True if anycast is defined }}", + "cga": "{{ True if cga is defined }}", + "eui": "{{ True if eui is defined }}", + "segment_routing": { + "enable": "{{ True if enable is defined }}", + "default": "{{ True if default is defined }}", + "ipv6_sr": "{{ True if ipv6_sr is defined }}", + }, + }, + ], + }, + }, + }, + { + "name": "ipv6.autoconfig", + "getval": re.compile( + r"""\s+ipv6\saddress\s + ((?P<enable>autoconfig)) + (\s(?P<default>default))? + $""", + re.VERBOSE, + ), + "setval": "{{ 'ipv6 address autoconfig' if ipv6.autoconfig.enable|d(False) or ipv6.autoconfig.default|d(False) else ''}}" + "{{ ' default' if ipv6.autoconfig.default|d(False) else ''}}", + "result": { + "{{ name }}": { + "ipv6": [ + { + "autoconfig": { + "enable": "{{ True if enable is defined }}", + "default": "{{ True if default is defined }}", + }, + }, + ], + }, + }, + }, + { + "name": "ipv6.dhcp", + "getval": re.compile( + r"""\s+ipv6\saddress\sdhcp + (\s(?P<rapid_commit>rapid-commit))? + $""", + re.VERBOSE, + ), + "setval": "{{ 'ipv6 address dhcp' if ipv6.dhcp.enable|d(False)|d(False) or ipv6.dhcp.rapid_commit|d(False)|d(False)else ''}}" + "{{ ' rapid-commit' if ipv6.dhcp.rapid_commit|d(False) else ''}}", + "result": { + "{{ name }}": { + "ipv6": [ + { + "dhcp": { + "enable": True, + "rapid_commit": "{{ True if rapid_commit is defined }}", + }, + }, + ], + }, + }, + }, + ] + # fmt: on diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/lag_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/lag_interfaces.py new file mode 100644 index 000000000..8bee47581 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/lag_interfaces.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Lag_interfaces parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +class Lag_interfacesTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Lag_interfacesTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + # fmt: off + PARSERS = [ + { + 'name': 'member', + 'getval': re.compile( + r''' + ^interface\s + (?P<member>\S+)$''', re.VERBOSE, + ), + 'setval': 'interface {{ member }}', + 'result': { + '{{ member }}': { + 'member': '{{ member }}', + }, + }, + 'shared': True, + }, + { + "name": "channel", + "getval": re.compile( + r""" + \s+channel-group + (\s(?P<channel>\d+))? + (\smode\s(?P<mode>active|passive|on|desirable|auto))? + (\slink\s(?P<link>\d+))? + $""", re.VERBOSE, + ), + "setval": "channel-group" + "{{ (' ' + channel|string) if channel is defined else '' }}" + "{{ (' mode ' + mode) if mode is defined else '' }}" + "{{ (' link ' + link|string) if link is defined else '' }}", + "result": { + '{{ member }}': { + 'member': '{{ member }}', + 'mode': '{{ mode }}', + 'channel': 'Port-channel{{ channel }}', + 'link': '{{ link }}', + }, + }, + }, + ] + # fmt: on diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/logging_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/logging_global.py new file mode 100644 index 000000000..f6e43da4e --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/logging_global.py @@ -0,0 +1,782 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Logging_global parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +def tmplt_host(verb): + cmd = "logging host" + changed = True + if verb.get("transport"): + changed = False + if verb: + if verb.get("host"): + cmd += " {hostname}".format(hostname=verb["host"]) + if verb.get("ipv6"): + cmd += " ipv6 {ipv6}".format(ipv6=verb["ipv6"]) + if verb.get("vrf"): + cmd += " vrf {vrf}".format(vrf=verb["vrf"]) + if verb.get("filtered"): + cmd += " {filtered}".format(filtered="filtered") + changed = True + if verb.get("xml"): + cmd += " {xml}".format(xml="xml") + changed = True + if verb.get("session_id"): + session_id = verb.get("session_id") + changed = True + if session_id.get("text"): + cmd += " session-id string {text}".format(text=session_id["text"]) + elif session_id.get("tag"): + cmd += " session-id {tag}".format(tag=session_id["tag"]) + if verb.get("stream"): + cmd += " stream {stream}".format(stream=verb["stream"]) + changed = True + if verb.get("sequence_num_session"): + cmd += " {sequence_num_session}".format(sequence_num_session="sequence-num-session") + changed = True + if verb.get("discriminator"): + cmd += " discriminator {discriminator}".format(discriminator=verb["discriminator"]) + changed = True + if not changed: + cmd = None + return cmd + + +def tmplt_host_transport(verb): + cmd = "logging host" + + if verb.get("host"): + cmd += " {hostname}".format(hostname=verb["host"]) + if verb.get("ipv6"): + cmd += " ipv6 {ipv6}".format(ipv6=verb["ipv6"]) + if verb.get("vrf"): + cmd += " vrf {vrf}".format(vrf=verb["vrf"]) + if verb.get("transport"): + transport_type = verb.get("transport") + prot = None + if transport_type.get("udp"): + cmd += " transport {prot}".format(prot="udp") + prot = "udp" + elif transport_type.get("tcp"): + cmd += " transport {prot}".format(prot="tcp") + prot = "tcp" + + if prot: + verb = transport_type.get(prot) + + if verb.get("port"): + cmd += " port {port}".format(port=verb["port"]) + if verb.get("audit"): + cmd += " {audit}".format(audit="audit") + if verb.get("xml"): + cmd += " {xml}".format(xml="xml") + if verb.get("filtered"): + cmd += " {filtered}".format(filtered="filtered") + if verb.get("discriminator"): + cmd += " discriminator {discriminator}".format(discriminator=verb["discriminator"]) + if verb.get("stream"): + cmd += " stream {stream}".format(stream=verb["stream"]) + if verb.get("session_id"): + session_id = verb.get("session_id") + if session_id.get("text"): + cmd += " session-id string {text}".format(text=session_id["text"]) + elif session_id.get("tag"): + cmd += " session-id {tag}".format(tag=session_id["tag"]) + if verb.get("sequence_num_session"): + cmd += " {sequence_num_session}".format(sequence_num_session="sequence-num-session") + return cmd + + +def tmplt_host_del(verb): + cmd = "logging host" + if verb.get("host"): + cmd += " {hostname}".format(hostname=verb["host"]) + if verb.get("ipv6"): + cmd += " ipv6 {ipv6}".format(ipv6=verb["ipv6"]) + if verb.get("transport"): + cmd = None + return cmd + + +def tmplt_host_transport_del(verb): + cmd = "logging host" + if verb.get("host"): + cmd += " {hostname}".format(hostname=verb["host"]) + if verb.get("ipv6"): + cmd += " ipv6 {ipv6}".format(ipv6=verb["ipv6"]) + return cmd + + +def tmplt_buffered(config_data): + return tmplt_common(config_data.get("buffered"), "logging buffered") + + +def tmplt_console(config_data): + return tmplt_common(config_data.get("console"), "logging console") + + +def tmplt_monitor(config_data): + return tmplt_common(config_data.get("monitor"), "logging monitor") + + +def tmplt_origin_id(config_data): + return tmplt_common(config_data.get("origin_id"), "logging origin-id") + + +def tmplt_logging_on(config_data): + cmd = "logging on" + if config_data.get("logging_on") == "disable": + cmd = "no " + cmd + return cmd + + +def tmplt_queue_limit(config_data): + return tmplt_common(config_data.get("queue_limit"), "logging queue-limit") + + +def tmplt_rate_limit(config_data): + return tmplt_common(config_data.get("rate_limit"), "logging rate-limit") + + +def tmplt_reload(config_data): + return tmplt_common(config_data.get("reload"), "logging reload") + + +def tmplt_message_counter(verb): + cmd = "logging message-counter" + + if verb.get("message_counter"): + cmd += " {message_counter}".format(message_counter=verb["message_counter"]) + return cmd + + +def tmplt_filter(verb): + cmd = "logging filter" + + if verb.get("url"): + cmd += " {url}".format(url=verb["url"]) + if verb.get("order"): + cmd += " {order}".format(order=verb["order"]) + if verb.get("args"): + cmd += " args {args}".format(args=verb["args"]) + return cmd + + +def tmplt_source_interface(verb): + cmd = "logging source-interface" + + if verb.get("interface"): + cmd += " {interface}".format(interface=verb["interface"]) + if verb.get("vrf"): + cmd += " vrf {vrf}".format(vrf=verb["vrf"]) + return cmd + + +def tmplt_common(verb, cmd): + if verb: + if verb.get("all"): + cmd += " {all}".format(all="all") + if verb.get("console"): + cmd += " {console}".format(console="console") + if verb.get("message_limit"): + cmd += " message-limit {message_limit}".format(message_limit=verb["message_limit"]) + if verb.get("discriminator"): + cmd += " discriminator {discriminator}".format(discriminator=verb.get("discriminator")) + if verb.get("filtered"): + cmd += " {filtered}".format(filtered="filtered") + if verb.get("xml"): + cmd += " {xml}".format(xml="xml") + if verb.get("size"): + cmd += " {size}".format(size=verb["size"]) + if verb.get("severity"): + cmd += " {severity}".format(severity=verb["severity"]) + if verb.get("except_severity"): + cmd += " except {exceptSev}".format(exceptSev=verb["except_severity"]) + if verb.get("tag"): + cmd += " {tag}".format(tag=verb["tag"]) + if verb.get("text"): + cmd += " string {tag}".format(tag=verb["text"]) + if verb.get("esm"): + cmd += " esm {tag}".format(tag=verb["esm"]) + if verb.get("trap"): + cmd += " trap {tag}".format(tag=verb["trap"]) + return cmd + + +def tmplt_persistent(config_data): + cmd = "logging persistent" + verb = config_data.get("persistent") + + if verb.get("url"): + cmd += " url {url}".format(url=verb["url"]) + if verb.get("size"): + cmd += " size {size}".format(size=verb["size"]) + if verb.get("filesize"): + cmd += " filesize {filesize}".format(filesize=verb["filesize"]) + if verb.get("batch"): + cmd += " batch {batch}".format(batch=verb["batch"]) + if verb.get("threshold"): + cmd += " threshold {threshold}".format(threshold=verb["threshold"]) + if verb.get("immediate"): + cmd += " {immediate}".format(immediate="immediate") + if verb.get("protected"): + cmd += " {protected}".format(protected="protected") + if verb.get("notify"): + cmd += " {notify}".format(notify="notify") + return cmd + + +class Logging_globalTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Logging_globalTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + # fmt: off + PARSERS = [ + { + "name": "hosts", + "getval": re.compile( + r""" + ^logging\shost + (\s(?P<hostname>\S+))? + (\sipv6\s(?P<ipv6>\S+))? + (\svrf\s(?P<vrf>\S+))? + (\s(?P<filtered>filtered))? + (\s(?P<xml>xml))? + (\sstream\s(?P<stream>\d+))? + (\ssession-id\s(?P<tag>hostname|ipv4|ipv6))? + (\ssession-id\sstring\s(?P<text>\S+))? + (\s(?P<sequence_num_session>sequence-num-session))? + (\sdiscriminator\s(?P<discriminator>.+$))? + $""", re.VERBOSE, + ), + "setval": tmplt_host, + "remval": tmplt_host_del, + "result": { + "hosts": [{ + "host": "{{ hostname }}", + "ipv6": "{{ ipv6 }}", + "discriminator": "{{ discriminator }}", + "vrf": "{{ vrf }}", + "xml": "{{ True if xml is defined }}", + "filtered": "{{ True if filtered is defined }}", + "sequence_num_session": "{{ True if sequence_num_session is defined }}", + "stream": "{{ stream }}", + "session_id": { + "tag": "{{ tag }}", + "text": "{{ text }}", + }, + }], + }, + }, + { + "name": "hosts.transport", + "getval": re.compile( + r""" + ^logging\shost + (\s(?P<hostname>\S+))? + (\sipv6\s(?P<ipv6>\S+))? + (\svrf\s(?P<vrf>\S+))? + (\stransport\s(?P<transport>tcp|udp))? + (\sport\s(?P<port>\d+))? + (\s(?P<audit>audit))? + (\s(?P<xml>xml))? + (\s(?P<filtered>filtered))? + (\sdiscriminator\s(?P<discriminator>.+$))? + (\sstream\s(?P<stream>\d+))? + (\ssession-id\s(?P<tag>hostname|ipv4|ipv6))? + (\ssession-id\sstring\s(?P<text>\S+))? + (\s(?P<sequence_num_session>sequence-num-session))? + $""", re.VERBOSE, + ), + "setval": tmplt_host_transport, + "remval": tmplt_host_transport_del, + "result": { + "hosts": [{ + "host": "{{ hostname }}", + "ipv6": "{{ ipv6 }}", + "vrf": "{{ vrf if transport is defined }}", + "transport": { + "{{ transport }}": { + "audit": "{{ True if audit is defined }}", + "sequence_num_session": "{{ True if sequence_num_session is defined }}", + "xml": "{{ True if xml is defined }}", + "discriminator": "{{ discriminator }}", + "port": "{{ port }}", + "session_id": { + "tag": "{{ tag }}", + "text": "{{ text }}", + }, + "filtered": "{{ True if filtered is defined }}", + "stream": "{{ stream }}", + }, + }, + }], + }, + }, + { + "name": "buffered", + "getval": re.compile( + r""" + ^logging\sbuffered + (\sdiscriminator\s(?P<discriminator>.+))? + (\s(?P<filtered>filtered))? + (\s(?P<xml>xml))? + (\s(?P<size>[1-9][0-9]*))? + (\s(?P<severity>alerts|critical|debugging|emergencies|errors|informational|notifications|warnings))? + $""", re.VERBOSE, + ), + "setval": tmplt_buffered, + "result": { + "buffered": { + "filtered": "{{ True if filtered is defined }}", + "xml": "{{ True if xml is defined }}", + "severity": "{{ severity }}", + "size": "{{ size }}", + "discriminator": "{{ discriminator }}", + }, + }, + }, + { + "name": "buginf", + "getval": re.compile( + r""" + ^logging\s(?P<buginf>buginf) + $""", re.VERBOSE, + ), + "setval": "logging buginf", + "result": { + "buginf": "{{ True if buginf is defined }}", + }, + }, + { + "name": "cns_events", + "getval": re.compile( + r""" + ^logging\scns-events + (\s(?P<severity>alerts|critical|debugging|emergencies|errors|informational|notifications|warnings))? + $""", re.VERBOSE, + ), + "setval": "logging cns-events {{ cns_events }}", + "result": { + "cns_events": "{{ severity }}", + }, + }, + { + "name": "console", + "getval": re.compile( + r""" + ^logging\sconsole + (\s(?P<filtered>filtered))? + (\s(?P<xml>xml))? + (\s(?P<severity>alerts|critical|debugging|emergencies|errors|informational|notifications|warnings|guaranteed$))? + (\sdiscriminator\s(?P<discriminator>.+$))? + $""", re.VERBOSE, + ), + "setval": tmplt_console, + "result": { + "console": { + "severity": "{{ severity }}", + "discriminator": "{{ discriminator }}", + "filtered": "{{ True if filtered is defined }}", + "xml": "{{ True if xml is defined }}", + }, + }, + }, + { + "name": "count", + "getval": re.compile( + r""" + ^logging\s(?P<count>count) + $""", re.VERBOSE, + ), + "setval": "logging count", + "result": { + "count": "{{ True if count is defined }}", + }, + }, + { + "name": "delimiter", + "getval": re.compile( + r""" + ^logging\sdelimiter\s(?P<tcp>tcp) + $""", re.VERBOSE, + ), + "setval": "logging delimiter tcp", + "result": { + "delimiter": { + "tcp": "{{ True if tcp is defined }}", + }, + }, + }, + { + "name": "discriminator", + "getval": re.compile( + r""" + ^logging\sdiscriminator\s(?P<discriminator>.+$) + $""", re.VERBOSE, + ), + "setval": "logging discriminator {{ discriminator }}", + "result": { + "discriminator": ["{{ discriminator }}"], + }, + }, + { + "name": "dmvpn", + "getval": re.compile( + r""" + ^logging\sdmvpn\srate-limit + (\s(?P<rate>\d+))? + $""", re.VERBOSE, + ), + "setval": "logging dmvpn rate-limit {{ dmvpn.rate_limit }}", + "result": { + "dmvpn": { + "rate_limit": "{{ rate }}", + }, + }, + }, + { + "name": "esm", + "getval": re.compile( + r""" + ^logging\sesm\s(?P<config>config) + $""", re.VERBOSE, + ), + "setval": "logging esm config", + "result": { + "esm": { + "config": "{{ True if config is defined }}", + }, + }, + }, + { + "name": "exception", + "getval": re.compile( + r""" + ^logging\sexception + \s(?P<exception>[1-9][0-9]*) + $""", re.VERBOSE, + ), + "setval": "logging exception {{ exception }}", + "result": { + "exception": "{{ exception }}", + }, + }, + { + "name": "facility", + "getval": re.compile( + r""" + ^logging\sfacility + \s(?P<facility>auth|cron|daemon|kern|local0|local1|local2|local3|local4|local5|local6|local7|lpr|mail|news|sys10|sys11|sys12|sys13|sys14|sys9|syslog|user|uucp) + $""", re.VERBOSE, + ), + "setval": "logging facility {{ facility }}", + "result": { + "facility": "{{ facility }}", + }, + }, + { + "name": "filter", + "getval": re.compile( + r""" + ^logging\sfilter + (\s(?P<url>\S+))? + (\s(?P<order>\d+))? + (\sargs\s(?P<args>.+$))? + $""", re.VERBOSE, + ), + "setval": tmplt_filter, + "result": { + "filter": [{ + "url": "{{ url }}", + "order": "{{ order }}", + "args": "{{ args }}", + }], + }, + }, + { + "name": "history.size", + "getval": re.compile( + r""" + ^logging\shistory\ssize + (\s(?P<size>\d+)) + $""", re.VERBOSE, + ), + "setval": "logging history size {{ history.size }}", + "result": { + "history": { + "size": "{{ size }}", + }, + }, + }, + { + "name": "history.severity", + "getval": re.compile( + r""" + ^logging\shistory + (\s(?P<severity>alerts|critical|debugging|emergencies|errors|informational|notifications|warnings)) + $""", re.VERBOSE, + ), + "setval": "logging history {{ history.severity }}", + "result": { + "history": { + "severity": "{{ severity }}", + }, + }, + }, + { + "name": "message_counter", + "getval": re.compile( + r""" + ^logging\smessage-counter + \s(?P<counter>log|debug|syslog) + $""", re.VERBOSE, + ), + "setval": tmplt_message_counter, + "result": { + "message_counter": ["{{ counter }}"], + }, + }, + { + "name": "monitor", + "getval": re.compile( + r""" + ^logging\smonitor + (\s(?P<filtered>filtered))? + (\s(?P<xml>xml))? + (\s(?P<severity>alerts|critical|debugging|emergencies|errors|informational|notifications|warnings))? + (\sdiscriminator\s(?P<discriminator>.+$))? + $""", re.VERBOSE, + ), + "setval": tmplt_monitor, + "result": { + "monitor": { + "filtered": "{{ True if filtered is defined }}", + "xml": "{{ True if xml is defined }}", + "severity": "{{ severity }}", + "discriminator": "{{ discriminator }}", + }, + }, + }, + { + "name": "logging_on", + "getval": re.compile( + r""" + ((?P<negate>no))? + ((\s|^))?logging + (\s(?P<on>on))? + $""", re.VERBOSE, + ), + "setval": tmplt_logging_on, + "remval": "logging on", + "result": { + "logging_on": "{{ 'disable' if negate is defined else 'enable' }}", + }, + }, + { + "name": "origin_id", + "getval": re.compile( + r""" + ^logging\sorigin-id + (\s(?P<tag>hostname|ip|ipv6))? + (\sstring\s(?P<text>.+$))? + $""", re.VERBOSE, + ), + "setval": tmplt_origin_id, + "result": { + "origin_id": { + "tag": "{{ tag }}", + "text": "{{ text }}", + }, + }, + }, + { + "name": "persistent", + "getval": re.compile( + r""" + ^logging\spersistent + (\surl\s(?P<url>\S+))? + (\ssize\s(?P<size>[1-9][0-9]*))? + (\sfilesize\s(?P<filesize>[1-9][0-9]*))? + (\sbatch\s(?P<batch>[1-9][0-9]*))? + (\sthreshold\s(?P<threshold>[1-9][0-9]*))? + (\s(?P<immediate>immediate))? + (\s(?P<protected>protected))? + (\s(?P<notify>notify))? + $""", re.VERBOSE, + ), + "setval": tmplt_persistent, + "result": { + "persistent": { + "batch": "{{ batch }}", + "filesize": "{{ filesize }}", + "immediate": "{{ True if immediate is defined }}", + "notify": "{{ True if notify is defined }}", + "protected": "{{ True if protected is defined }}", + "size": "{{ size }}", + "threshold": "{{ threshold }}", + "url": "{{ url }}", + }, + }, + }, + { + "name": "policy_firewall", + "getval": re.compile( + r""" + ^logging\spolicy-firewall + (\srate-limit\s(?P<rate>[1-9][0-9]*))? + $""", re.VERBOSE, + ), + "setval": "logging policy-firewall rate-limit {{ policy_firewall.rate_limit }}", + "result": { + "policy_firewall": { + "rate_limit": "{{ rate }}", + }, + }, + }, + { + "name": "queue_limit", + "getval": re.compile( + r""" + ^logging\squeue-limit + (\s(?P<size>[1-9][0-9]*))? + (\sesm\s(?P<esm>[1-9][0-9]*))? + (\strap\s(?P<trap>[1-9][0-9]*))? + $""", re.VERBOSE, + ), + "setval": tmplt_queue_limit, + "result": { + "queue_limit": { + "size": "{{ size }}", + "esm": "{{ esm }}", + "trap": "{{ trap }}", + }, + }, + }, + { + "name": "rate_limit", + "getval": re.compile( + r""" + ^logging\srate-limit + (\s(?P<option>all|console))? + (\s(?P<size>[1-9][0-9]*))? + (\sexcept\s(?P<except_severity>alerts|critical|debugging|emergencies|errors|informational|notifications|warnings))? + $""", re.VERBOSE, + ), + "setval": tmplt_rate_limit, + "result": { + "rate_limit": { + "size": "{{ size }}", + "all": "{{ True if option == 'all' }}", + "console": "{{ True if option == 'console' }}", + "except_severity": "{{ except_severity }}", + }, + }, + }, + { + "name": "reload", + "getval": re.compile( + r""" + ^logging + (\s(?P<reload>reload))? + (\smessage-limit\s(?P<message_limit>[1-9][0-9]*))? + (\s(?P<severity>alerts|critical|debugging|emergencies|errors|informational|notifications|warnings))? + $""", re.VERBOSE, + ), + "setval": tmplt_reload, + "result": { + "reload": { + "severity": "{{ severity }}", + "message_limit": "{{ message_limit }}", + }, + }, + }, + { + "name": "server_arp", + "getval": re.compile( + r""" + ^logging + \s(?P<server_arp>server-arp) + $""", re.VERBOSE, + ), + "setval": "logging server-arp", + "result": { + "server_arp": "{{ True if server_arp is defined }}", + }, + }, + { + "name": "snmp_trap", + "getval": re.compile( + r""" + ^logging\ssnmp-trap + (\s(?P<severity>alerts|critical|debugging|emergencies|errors|informational|notifications|warnings))? + $""", re.VERBOSE, + ), + "setval": "logging snmp-trap {{ snmp_trap }}", + "result": { + "snmp_trap": ["{{ severity }}"], + }, + }, + { + "name": "source_interface", + "getval": re.compile( + r""" + ^logging\ssource-interface + (\s(?P<interface>\S+))? + (\svrf\s(?P<vrf>\S+))? + $""", re.VERBOSE, + ), + "setval": tmplt_source_interface, + "result": { + "source_interface": [{ + "interface": "{{ interface }}", + "vrf": "{{ vrf }}", + }], + }, + }, + { + "name": "trap", + "getval": re.compile( + r""" + ^logging\strap + \s(?P<severity>alerts|critical|debugging|emergencies|errors|informational|notifications|warnings) + $""", re.VERBOSE, + ), + "setval": "logging trap {{ trap }}", + "result": { + "trap": "{{ severity }}", + }, + }, + { + "name": "userinfo", + "getval": re.compile( + r""" + ^logging\s(?P<userinfo>userinfo) + $""", re.VERBOSE, + ), + "setval": "logging userinfo", + "result": { + "userinfo": "{{ True if userinfo is defined }}", + }, + }, + ] + # fmt: on diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ntp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ntp_global.py new file mode 100644 index 000000000..7a1a68658 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ntp_global.py @@ -0,0 +1,509 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Ntp_global parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +class Ntp_globalTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Ntp_globalTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + # fmt: off + PARSERS = [ + { + "name": "peer", + "getval": re.compile( + r""" + ^ntp\saccess-group + (\s(?P<ipv4>ipv4))? + (\s(?P<ipv6>ipv6))? + \speer + \s(?P<access_list>\S+) + (\s(?P<kod>kod))? + $""", re.VERBOSE, + ), + "setval": "ntp access-group" + "{{ ' ipv4' if ipv4 is defined else '' }}" + "{{ ' ipv6' if ipv6 is defined else '' }}" + " peer " + "{{ access_list }}" + "{{ ' kod' if kod|d(False) else '' }}", + "result": { + "access_group": { + "peer": [ + { + "access_list": "{{ access_list }}", + "kod": "{{ not not kod }}", + "ipv4": "{{ not not ipv4 }}", + "ipv6": "{{ not not ipv6 }}", + }, + ], + }, + }, + }, + { + "name": "query_only", + "getval": re.compile( + r""" + ^ntp\saccess-group + (\s(?P<ipv4>ipv4))? + (\s(?P<ipv6>ipv6))? + \squery-only + \s(?P<access_list>\S+) + (\s(?P<kod>kod))? + $""", re.VERBOSE, + ), + "setval": "ntp access-group" + "{{ ' ipv4' if ipv4 is defined else '' }}" + "{{ ' ipv6' if ipv6 is defined else '' }}" + " query-only " + "{{ access_list }}" + "{{ ' kod' if kod|d(False) else '' }}", + "result": { + "access_group": { + "query_only": [ + { + "access_list": "{{ access_list }}", + "kod": "{{ not not kod }}", + "ipv4": "{{ not not ipv4 }}", + "ipv6": "{{ not not ipv6 }}", + }, + ], + }, + }, + }, + { + "name": "serve", + "getval": re.compile( + r""" + ^ntp\saccess-group + (\s(?P<ipv4>ipv4))? + (\s(?P<ipv6>ipv6))? + \sserve + \s(?P<access_list>\S+) + (\s(?P<kod>kod))? + $""", re.VERBOSE, + ), + "setval": "ntp access-group" + "{{ ' ipv4' if ipv4 is defined else '' }}" + "{{ ' ipv6' if ipv6 is defined else '' }}" + " serve " + "{{ access_list }}" + "{{ ' kod' if kod|d(False) else '' }}", + "result": { + "access_group": { + "serve": [ + { + "access_list": "{{ access_list }}", + "kod": "{{ not not kod }}", + "ipv4": "{{ not not ipv4 }}", + "ipv6": "{{ not not ipv6 }}", + }, + ], + }, + }, + }, + { + "name": "serve_only", + "getval": re.compile( + r""" + ^ntp\saccess-group + (\s(?P<ipv4>ipv4))? + (\s(?P<ipv6>ipv6))? + \sserve-only + \s(?P<access_list>\S+) + (\s(?P<kod>kod))? + $""", re.VERBOSE, + ), + "setval": "ntp access-group" + "{{ ' ipv4' if ipv4 is defined else '' }}" + "{{ ' ipv6' if ipv6 is defined else '' }}" + " serve-only " + "{{ access_list }}" + "{{ ' kod' if kod|d(False) else '' }}", + "result": { + "access_group": { + "serve_only": [ + { + "access_list": "{{ access_list }}", + "kod": "{{ not not kod }}", + "ipv4": "{{ not not ipv4 }}", + "ipv6": "{{ not not ipv6 }}", + }, + ], + }, + }, + }, + { + "name": "allow.control.rate_limit", + "getval": re.compile( + r""" + ^ntp\sallow\smode\scontrol\s(?P<rate_limit>\d+) + $""", re.VERBOSE, + ), + "setval": "ntp allow mode control {{ allow.control.rate_limit }}", + "result": { + "allow": { + "control": { + "rate_limit": "{{ rate_limit }}", + }, + }, + }, + }, + { + "name": "allow.private", + "getval": re.compile( + r""" + ^ntp\sallow\smode\s(?P<private>private) + $""", re.VERBOSE, + ), + "setval": "ntp allow mode private", + "result": { + "allow": { + "private": "{{ not not private }}", + }, + }, + }, + { + "name": "authenticate", + "getval": re.compile( + r""" + ^ntp\s(?P<authenticate>authenticate) + $""", re.VERBOSE, + ), + "setval": "ntp authenticate", + "result": { + "authenticate": "{{ not not authenticate }}", + }, + }, + { + "name": "authentication_keys", + "getval": re.compile( + r""" + ^ntp\sauthentication-key\s(?P<id>\d+) + \s(?P<algorithm>\S+) + \s(?P<key>\S+) + \s(?P<encryption>\d+) + $""", re.VERBOSE, + ), + "setval": "ntp authentication-key {{ id }} {{ algorithm }} {{ key }} {{ encryption }}", + "result": { + "authentication_keys": [ + { + "id": "{{ id }}", + "algorithm": "{{ algorithm }}", + "key": "{{ key }}", + "encryption": "{{ encryption }}", + }, + ], + }, + }, + { + "name": "broadcast_delay", + "getval": re.compile( + r""" + ^ntp\sbroadcastdelay\s(?P<broadcast_delay>\d+) + $""", re.VERBOSE, + ), + "setval": "ntp broadcastdelay {{ broadcast_delay }}", + "result": { + "broadcast_delay": "{{ broadcast_delay }}", + }, + }, + { + "name": "clock_period", + "getval": re.compile( + r""" + ^ntp\sclock-period\s(?P<clock_period>\d+) + $""", re.VERBOSE, + ), + "setval": "ntp clock-period {{ clock_period }}", + "result": { + "clock_period": "{{ clock_period }}", + }, + }, + { + "name": "logging", + "getval": re.compile( + r""" + ^ntp\s(?P<logging>logging) + $""", re.VERBOSE, + ), + "setval": "ntp logging", + "result": { + "logging": "{{ not not logging }}", + }, + }, + { + "name": "master.enabled", + "getval": re.compile( + r""" + ^ntp\s(?P<master>master) + $""", re.VERBOSE, + ), + "setval": "ntp master", + "result": { + "master": { + "enabled": "{{ not not master }}", + }, + }, + }, + { + "name": "master.stratum", + "getval": re.compile( + r""" + ^ntp\smaster\s(?P<stratum>\d+) + $""", re.VERBOSE, + ), + "setval": "ntp master {{ master.stratum }}", + "result": { + "master": { + "stratum": "{{ stratum }}", + }, + }, + }, + { + "name": "max_associations", + "getval": re.compile( + r""" + ^ntp\smax-associations\s(?P<max_associations>\d+) + $""", re.VERBOSE, + ), + "setval": "ntp max-associations {{ max_associations }}", + "result": { + "max_associations": "{{ max_associations }}", + }, + }, + { + "name": "max_distance", + "getval": re.compile( + r""" + ^ntp\smaxdistance\s(?P<max_distance>\d+) + $""", re.VERBOSE, + ), + "setval": "ntp maxdistance {{ max_distance }}", + "result": { + "max_distance": "{{ max_distance }}", + }, + }, + { + "name": "min_distance", + "getval": re.compile( + r""" + ^ntp\smindistance\s(?P<min_distance>\d+) + $""", re.VERBOSE, + ), + "setval": "ntp mindistance {{ min_distance }}", + "result": { + "min_distance": "{{ min_distance }}", + }, + }, + { + "name": "orphan", + "getval": re.compile( + r""" + ^ntp\sorphan\s(?P<orphan>\d+) + $""", re.VERBOSE, + ), + "setval": "ntp orphan {{ orphan }}", + "result": { + "orphan": "{{ orphan }}", + }, + }, + { + "name": "panic_update", + "getval": re.compile( + r""" + ^ntp\spanic\s(?P<update>update) + $""", re.VERBOSE, + ), + "setval": "ntp panic update", + "result": { + "panic_update": "{{ not not update }}", + }, + }, + { + "name": "passive", + "getval": re.compile( + r""" + ^ntp\s(?P<passive>passive) + $""", re.VERBOSE, + ), + "setval": "ntp passive", + "result": { + "passive": "{{ not not passive }}", + }, + }, + { + "name": "peers", + "getval": re.compile( + r""" + ^ntp\speer + (\svrf\s(?P<vrf>\S+))? + (\s(?P<ipv4>ip))? + (\s(?P<ipv6>ipv6))? + \s(?P<peer>\S+) + (\s(?P<burst>burst))? + (\s(?P<iburst>iburst))? + (\skey\s(?P<key>\d+))? + (\sminpoll\s(?P<minpoll>\d+))? + (\smaxpoll\s(?P<maxpoll>\d+))? + (\s(?P<normal_sync>normal-sync))? + (\s(?P<prefer>prefer))? + (\ssource\s(?P<source>\S+))? + (\sversion\s(?P<version>\d+))? + $""", re.VERBOSE, + ), + "setval": "ntp peer" + "{{ (' vrf ' + vrf) if vrf is defined else '' }}" + "{{ ' ip' if use_ipv4|d(False) else ''}}" + "{{ ' ipv6' if use_ipv6|d(False) else ''}}" + "{{ ( ' ' + peer ) if peer is defined else '' }}" + "{{ ' burst ' if burst|d(False) else ''}}" + "{{ ' iburst ' if iburst|d(False) else ''}}" + "{{ (' key ' + key_id|string) if key_id is defined else '' }}" + "{{ (' minpoll ' + minpoll|string) if minpoll is defined else '' }}" + "{{ (' maxpoll ' + maxpoll|string) if maxpoll is defined else '' }}" + "{{ ' normal-sync ' if normal_sync is defined else ''}}" + "{{ ' prefer' if prefer|d(False) else ''}}" + "{{ (' source ' + source) if source is defined else '' }}" + "{{ (' version ' + version|string) if version is defined else '' }}", + "result": { + "peers": [ + { + "peer": "{{ peer }}", + "use_ipv4": "{{ not not ipv4 }}", + "use_ipv6": "{{ not not ipv6 }}", + "vrf": "{{ vrf }}", + "burst": "{{ not not burst }}", + "iburst": "{{ not not iburst }}", + "key_id": "{{ key }}", + "minpoll": "{{ minpoll }}", + "maxpoll": "{{ maxpoll }}", + "normal_sync": "{{ not not normal_sync }}", + "prefer": "{{ not not prefer }}", + "source": "{{ source }}", + "version": "{{ version }}", + }, + ], + }, + }, + { + "name": "servers", + "getval": re.compile( + r""" + ^ntp\sserver + (\svrf\s(?P<vrf>\S+))? + (\s(?P<ipv4>ip))? + (\s(?P<ipv6>ipv6))? + \s(?P<server>\S+) + (\s(?P<burst>burst))? + (\s(?P<iburst>iburst))? + (\skey\s(?P<key>\d+))? + (\sminpoll\s(?P<minpoll>\d+))? + (\smaxpoll\s(?P<maxpoll>\d+))? + (\s(?P<normal_sync>normal-sync))? + (\s(?P<prefer>prefer))? + (\ssource\s(?P<source>\S+))? + (\sversion\s(?P<version>\d+))? + $""", re.VERBOSE, + ), + "setval": "ntp server" + "{{ (' vrf ' + vrf) if vrf is defined else '' }}" + "{{ ' ip' if use_ipv4|d(False) else ''}}" + "{{ ' ipv6' if use_ipv6|d(False) else ''}}" + "{{ ( ' ' + server ) if server is defined else '' }}" + "{{ ' burst ' if burst|d(False) else ''}}" + "{{ ' iburst ' if iburst|d(False) else ''}}" + "{{ (' key ' + key_id|string) if key_id is defined else '' }}" + "{{ (' minpoll ' + minpoll|string) if minpoll is defined else '' }}" + "{{ (' maxpoll ' + maxpoll|string) if maxpoll is defined else '' }}" + "{{ ' normal-sync ' if normal_sync is defined else ''}}" + "{{ ' prefer' if prefer|d(False) else ''}}" + "{{ (' source ' + source) if source is defined else '' }}" + "{{ (' version ' + version|string) if version is defined else '' }}", + "result": { + "servers": [ + { + "server": "{{ server }}", + "use_ipv4": "{{ not not ipv4 }}", + "use_ipv6": "{{ not not ipv6 }}", + "vrf": "{{ vrf }}", + "burst": "{{ not not burst }}", + "iburst": "{{ not not iburst }}", + "key_id": "{{ key }}", + "minpoll": "{{ minpoll }}", + "maxpoll": "{{ maxpoll }}", + "normal_sync": "{{ not not normal_sync }}", + "prefer": "{{ not not prefer }}", + "source": "{{ source }}", + "version": "{{ version }}", + }, + ], + }, + }, + { + "name": "source", + "getval": re.compile( + r""" + ^ntp\ssource\s(?P<source>\S+) + $""", re.VERBOSE, + ), + "setval": "ntp source {{ source }}", + "result": { + "source": "{{ source }}", + }, + }, + { + "name": "trusted_keys", + "getval": re.compile( + r""" + ^ntp\strusted-key + \s((?P<range_start>\d+)) + (\s\-\s)? + ((?P<range_end>\d+))? + $""", re.VERBOSE, + ), + "setval": "ntp trusted-key {{ range_start }}" + "{{ (' - ' + range_end|string) if range_end is defined else '' }}", + "result": { + "trusted_keys": [ + { + "range_start": "{{ range_start }}", + "range_end": "{{ range_end }}", + }, + ], + }, + }, + { + "name": "update_calendar", + "getval": re.compile( + r""" + ^ntp\s(?P<update_calendar>update-calendar) + $""", re.VERBOSE, + ), + "setval": "ntp update-calendar", + "result": { + "update_calendar": "{{ not not update_calendar }}", + }, + }, + ] + # fmt: on diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospf_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospf_interfaces.py new file mode 100644 index 000000000..2894240b9 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospf_interfaces.py @@ -0,0 +1,643 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Ospf_interfaces parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +class Ospf_interfacesTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Ospf_interfacesTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + PARSERS = [ + { + "name": "name", + "getval": re.compile( + r""" + ^interface\s(?P<name>\S+)$""", + re.VERBOSE, + ), + "setval": "interface {{ name }}", + "result": {"{{ name }}": {"name": "{{ name }}", "address_family": {}}}, + "shared": True, + }, + { + "name": "process", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)\sospf + (\s(?P<id>\d+)) + (\sarea\s(?P<area>\d+))? + (\sarea\s(?P<area_ip>\s+))? + (\s(?P<secondaries>secondaries))? + (\sinstance\s(?P<instance>\d+))? + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} {{ process.id|string }}" + "{{ (' area ' + process.area_id|string ) if process.area_id is defined else '' }}" + "{{ (' ' + secondaries) if process.secondaries is defined else '' }}" + "{{ (' instance ' + process.instance) if process.instance is defined else '' }}", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "process": { + "id": "{{ id }}", + "area_id": "{{ area }}", + "secondaries": "{{ not not secondaries }}", + "instance_id": "{{ instance }}", + }, + }, + }, + }, + }, + }, + { + "name": "adjacency", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)\sospf + \s(?P<adjacency>adjacency\sstagger\sdisable) + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} adjacency stagger disable", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "adjacency": "{{ not not adjacency }}", + }, + }, + }, + }, + }, + { + "name": "authentication", + "getval": re.compile( + r""" + \s+ip\sospf\sauthentication + (\skey-chain\s(?P<key_chain>\S+))? + (\s(?P<message_digest>message-digest))? + (\s(?P<isnull>null))? + $""", + re.VERBOSE, + ), + "setval": "ip ospf authentication" + "{{ (' key-chain ' + authentication.key_chain) if authentication.key_chain is defined else '' }}" + "{{ (' ' + message-digest) if authentication.message_digest is defined else '' }}" + "{{ (' ' + null) if authentication.null is defined else '' }}", + "result": { + "{{ name }}": { + "address_family": { + "ip": { + "afi": "ipv4", + "authentication": { + "key_chain": "{{ key_chain }}", + "message_digest": "{{ not not message_digest }}", + "null": "{{ not not isnull }}", + }, + }, + }, + }, + }, + }, + { + "name": "bfd", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)\sospf\sbfd + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} bfd", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "bfd": True, + }, + }, + }, + }, + }, + { + "name": "cost", + "getval": re.compile( + r""" + \s+ip\sospf + (\scost\s(?P<cost>\d+)) + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} cost" + " {{ cost.interface_cost|string }}", + "result": { + "{{ name }}": { + "address_family": { + "ip": { + "afi": "ipv4", + "cost": {"interface_cost": "{{ cost }}"}, + }, + }, + }, + }, + }, + { + "name": "cost_ipv6_dynamic_cost", + "getval": re.compile( + r""" + \s+ipv6\sospf\scost\s(?P<interface_cost>\d+) + (\sdynamic)? + (\sdefault\s(?P<default>\d+))? + (\shysteresis)? + (\spercent\s(?P<h_params_p>\d+))? + (\sthreshold\s(?P<h_params_t>\d+))? + (\sweight)? + (\sL2-factor\s(?P<l2_factor>\d+))? + (\slatency\s(?P<latency>\d+))? + (\sresources\s(?P<resources>\d+))? + (\sthroughput\s(?P<throughput>\d+))? + (\s(?P<weight_oc>oc))? + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} cost" + " {{ cost.interface_cost|string }}", + "result": { + "{{ name }}": { + "address_family": { + "ipv6": { + "afi": "ipv6", + "cost": { + "interface_cost": "{{ interface_cost }}", + "dynamic_cost": { + "default": "{{ default }}", + "hysteresis": { + "percent": "{{ h_params_p }}", + "threshold": "{{ h_params_t }}", + }, + "weight": { + "l2_factor": "{{ l2_factor }}", + "latency": "{{ latency }}", + "oc": "{{ not not weight_oc }}", + "resources": "{{ resources }}", + "throughput": "{{ throughput }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "database_filter", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)\sospf\sdatabase-filter\sall\sout + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} database-filter all out", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "database_filter": True, + }, + }, + }, + }, + }, + { + "name": "dead_interval", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)\sospf\sdead-interval + (\s(?P<seconds>\d+))? + (\sminimal\shello-multiplier\s(?P<minimal>\d+))? + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} dead-interval {{ dead_interval.time|string }}" + "{{ (' minimal hello-multiplier ' + dead_interval.minimal|string) if dead_interval.minimal is defined else '' }}", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "dead_interval": {"time": "{{ seconds }}", "minimal": "{{ minimal }}"}, + }, + }, + }, + }, + }, + { + "name": "demand_circuit", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)\sospf\sdemand-circuit + (\s(?P<ignore>ignore))? + (\s(?P<disable>disable))? + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} demand-circuit" + "{{ ' ignore' if demand_circuit.ignore is defined else '' }}" + "{{ ' disable' if demand_circuit.disable is defined else '' }}", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "demand_circuit": { + "enable": True, + "ignore": "{{ not not ignore }}", + "disable": "{{ not not disable }}", + }, + }, + }, + }, + }, + }, + { + "name": "flood_reduction", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)\sospf\sflood-reduction + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} flood-reduction", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "flood_reduction": True, + }, + }, + }, + }, + }, + { + "name": "hello_interval", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)\sospf(\shello-interval\s(?P<hello_interval>\d+)) + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} hello-interval {{ hello_interval }}", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "hello_interval": "{{ hello_interval }}", + }, + }, + }, + }, + }, + { + "name": "lls", + "getval": re.compile( + r"""\s+ip\sospf\slls$""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} lls", + "result": { + "{{ name }}": { + "address_family": { + "ip": { + "afi": "ipv4", + "lls": True, + }, + }, + }, + }, + }, + { + "name": "manet", + "getval": re.compile( + r""" + \s+ipv6\sospf\smanet\speering + (\scost\spercent\s(?P<cost_p>\d+))? + (\scost\sthreshold\s(?P<cost_t>\d+))? + (\slink-metrics\s(?P<link_metrics>\d+))? + $""", + re.VERBOSE, + ), + "setval": "ipv6 ospf manet peering" + "{{ ' cost' if manet.cost is defined else '' }}" + "{{ (' percent ' + manet.cost.percent|string ) if manet.cost.percent is defined else '' }}" + "{{ (' threshold ' + manet.cost.threshold|string ) if manet.cost.threshold is defined else '' }}" + "{{ ' link-metrics' if manet.link_metrics is defined else '' }}" + "{{ (' ' + manet.link_metrics.cost_threshold) if manet.link_metrics is defined and manet.link_metrics.cost_threshold is defined else '' }}", + "result": { + "{{ name }}": { + "address_family": { + "ipv6": { + "afi": "ipv6", + "manet": { + "cost": { + "percent": "{{ cost_p }}", + "threshold": "{{ cost_t }}", + }, + "link_metrics": { + "set": "{{ True if link_metrics is not defined and link_metrics is defined }}", + "cost_threshold": "{{ link_metrics }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "mtu_ignore", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)\sospf\smtu-ignore + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} mtu-ignore", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "mtu_ignore": True, + }, + }, + }, + }, + }, + { + "name": "multi_area", + "getval": re.compile( + r""" + \s+ip\sospf\smulti-area\s(?P<multi_area>\d+) + (\scost\s(?P<cost>\d+))? + $""", + re.VERBOSE, + ), + "setval": "ip ospf multi-area {{ multi_area.id|string }}" + "{{ (' cost ' + multi_area.cost|string ) if multi_area.cost is defined else '' }}", + "result": { + "{{ name }}": { + "address_family": { + "ip": { + "afi": "ipv4", + "multi_area": { + "id": "{{ multi_area }}", + "cost": "{{ cost }}", + }, + }, + }, + }, + }, + }, + { + "name": "neighbor", + "getval": re.compile( + r""" + \s+ipv6\sospf\sneighbor\s(?P<address>\S+) + (\scost\s(?P<cost>\d+))? + (\s(?P<database_filter>database-filter\sall\sout))? + (\spoll-interval\s(?P<poll_interval>\d+))? + (\spriority\s(?P<priority>\d+))? + $""", + re.VERBOSE, + ), + "setval": "ipv6 ospf neighbor {{ neighbor.address }}" + "{{ (' cost ' + neighbor.cost|string ) if neighbor.cost is defined else '' }}" + "{{ ' database-filter all out' if neighbor.database_filter is defined else '' }}" + "{{ (' poll-interval ' + neighbor.poll_interval|string ) if neighbor.poll_interval is defined else '' }}" + "{{ (' priority ' + neighbor.priority|string ) if neighbor.priority is defined else '' }}", + "result": { + "{{ name }}": { + "address_family": { + "ipv6": { + "afi": "ipv6", + "neighbor": { + "address": "{{ address }}", + "cost": "{{ cost }}", + "database_filter": "{{ not not database_filter }}", + "poll_interval": "{{ poll_interval }}", + "priority": "{{ priority }}", + }, + }, + }, + }, + }, + }, + { + "name": "network", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)\sospf\snetwork + (\s(?P<broadcast>broadcast))? + (\s(?P<manet>manet))? + (\s(?P<non_broadcast>non-broadcast))? + (\s(?P<point_to_multipoint>point-to-multipoint))? + (\s(?P<point_to_point>point-to-point))? + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} network" + "{{ ' broadcast' if network.broadcast is defined else '' }}" + "{{ ' manet' if network.manet is defined else '' }}" + "{{ ' non-broadcast' if network.non_broadcast is defined else '' }}" + "{{ ' point-to-multipoint' if network.point_to_multipoint is defined else '' }}" + "{{ ' point-to-point' if network.point_to_point is defined else '' }}", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "network": { + "broadcast": "{{ not not broadcast }}", + "manet": "{{ not not manet }}", + "non_broadcast": "{{ not not non_broadcast }}", + "point_to_multipoint": "{{ not not point_to_multipoint }}", + "point_to_point": "{{ not not point_to_point }}", + }, + }, + }, + }, + }, + }, + { + "name": "prefix_suppression", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)\sospf\sprefix-suppression + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} prefix-suppression", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "prefix_suppression": True, + }, + }, + }, + }, + }, + { + "name": "priority", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)\sospf + (\spriority\s(?P<priority>\d+)) + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} priority {{ priority }}", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "priority": "{{ priority }}", + }, + }, + }, + }, + }, + { + "name": "resync_timeout", + "getval": re.compile( + r""" + \s+ip\sospf + (\sresync-timeout\s(?P<resync_timeout>\d+)) + $""", + re.VERBOSE, + ), + "setval": "ip ospf resync-timeout {{ resync_timeout|string }}", + "result": { + "{{ name }}": { + "address_family": { + "ip": { + "afi": "ipv4", + "resync_timeout": "{{ resync_timeout }}", + }, + }, + }, + }, + }, + { + "name": "retransmit_interval", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)\sospf + (\sretransmit-interval\s(?P<retransmit_interval>\d+)) + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} retransmit-interval {{ retransmit_interval|string }}", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "retransmit_interval": "{{ retransmit_interval }}", + }, + }, + }, + }, + }, + { + "name": "shutdown", + "getval": re.compile( + r""" + \s+(?P<afi>ip|ipv6)\sospf\sshutdown + $""", + re.VERBOSE, + ), + "setval": "{{ 'ip ospf' if afi == 'ipv4' else 'ipv6 ospf' }} shutdown", + "result": { + "{{ name }}": { + "address_family": { + "{{ afi }}": { + "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}", + "shutdown": True, + }, + }, + }, + }, + }, + { + "name": "transmit_delay", + "getval": re.compile( + r""" + \s+ipv6\sospf\stransmit-delay\s(?P<transmit_delay>\d+) + $""", + re.VERBOSE, + ), + "setval": "ipv6 ospf transmit-delay {{ transmit_delay|string }}", + "result": { + "{{ name }}": { + "address_family": { + "ipv6": { + "afi": "ipv6", + "transmit_delay": "{{ transmit_delay }}", + }, + }, + }, + }, + }, + { + "name": "ttl_security", + "getval": re.compile( + r""" + \s+ip\sospf\sttl-security\shops\s(?P<hops>\d+) + *$""", + re.VERBOSE, + ), + "setval": "ip ospf ttl-security" + "{{ (' hops ' + ttl_security.hops|string) if ttl_security.hops is defined else '' }}", + "result": { + "{{ name }}": { + "address_family": { + "ip": { + "afi": "ipv4", + "ttl_security": { + "set": True, + "hops": "{{ hops }}", + }, + }, + }, + }, + }, + }, + ] diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv2.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv2.py new file mode 100644 index 000000000..5b746e507 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv2.py @@ -0,0 +1,1830 @@ +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import re + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +def _tmplt_ospf_vrf_cmd(process): + command = "router ospf {process_id}".format(**process) + if "vrf" in process: + command += " vrf {vrf}".format(**process) + return command + + +def _tmplt_ospf_adjacency_cmd(config_data): + if "adjacency" in config_data: + command = "adjacency stagger " + if "none" in config_data["adjacency"]: + command += " none" + else: + command += " {min_adjacency}".format(**config_data["adjacency"]) + if "max_adjacency" in config_data["adjacency"]: + command += " {min_adjacency}".format(**config_data["adjacency"]) + return command + + +def _tmplt_ospf_address_family_cmd(config_data): + if "address_family" in config_data: + command = ["address-family ipv4 multicast", "exit-address-family"] + if config_data["address_family"].get("topology"): + if "base" in config_data["address_family"].get("topology"): + command.insert(1, "topology base") + elif "name" in config_data["address_family"].get("topology"): + cmd = "topology {name}".format(**config_data["address_family"].get("topology")) + if "tid" in config_data["address_family"].get("topology"): + cmd += " tid {tid}".format(**config_data["address_family"].get("topology")) + command.insert(1, cmd) + return command + + +def _tmplt_ospf_area_authentication(config_data): + if "authentication" in config_data: + command = "area {area_id} authentication".format(**config_data) + if config_data["authentication"].get("message_digest"): + command += " message-digest" + return command + + +def _tmplt_ospf_area_filter(config_data): + if "filter_list" in config_data: + command = [] + for key, value in iteritems(config_data.get("filter_list")): + cmd = "area {area_id}".format(**config_data) + if value.get("name") and value.get("direction"): + cmd += " filter-list prefix {name} {direction}".format(**value) + command.append(cmd) + return command + + +def _tmplt_ospf_area_nssa(config_data): + if "nssa" in config_data: + command = "area {area_id} nssa".format(**config_data) + if "default_information_originate" in config_data["nssa"]: + command += " default-information-originate" + if "metric" in config_data["nssa"]["default_information_originate"]: + command += " metric {metric}".format( + **config_data["nssa"]["default_information_originate"] + ) + if "metric_type" in config_data["nssa"]["default_information_originate"]: + command += " metric-type {metric_type}".format( + **config_data["nssa"]["default_information_originate"] + ) + if "nssa_only" in config_data["nssa"]["default_information_originate"]: + command += " nssa-only" + if config_data["nssa"].get("no_ext_capability"): + command += " no-ext-capability" + if config_data["nssa"].get("no_redistribution"): + command += " no-redistribution" + if config_data["nssa"].get("no_summary"): + command += " no-summary" + return command + + +def _tmplt_ospf_area_nssa_translate(config_data): + if "nssa" in config_data: + command = "area {area_id} nssa".format(**config_data) + if "translate" in config_data["nssa"]: + command += " translate type7 {translate}".format(**config_data["nssa"]) + return command + + +def _tmplt_ospf_area_ranges(config_data): + if "ranges" in config_data: + commands = [] + for k, v in iteritems(config_data["ranges"]): + cmd = "area {area_id} range".format(**config_data) + temp_cmd = " {address} {netmask}".format(**v) + if "advertise" in v: + temp_cmd += " advertise" + elif "not_advertise" in v: + temp_cmd += " not-advertise" + if "cost" in v: + temp_cmd += " cost {cost}".format(**v) + cmd += temp_cmd + commands.append(cmd) + return commands + + +def _tmplt_ospf_area_sham_link(config_data): + if "sham_link" in config_data: + command = "area {area_id} sham-link".format(**config_data) + if "source" in config_data["sham_link"]: + command += " {source} {destination}".format(**config_data["sham_link"]) + if "cost" in config_data["sham_link"]: + command += " cost {cost}".format(**config_data["sham_link"]) + if "ttl_security" in config_data["sham_link"]: + command += " ttl-security hops {ttl_security}".format(**config_data["sham_link"]) + return command + + +def _tmplt_ospf_area_stub_link(config_data): + if "stub" in config_data: + command = "area {area_id} stub".format(**config_data) + if "no_ext_capability" in config_data["stub"]: + command += " no-ext-capability" + if "no_summary" in config_data["stub"]: + command += " no-summary" + return command + + +def _tmplt_ospf_auto_cost(config_data): + if "auto_cost" in config_data: + command = "auto-cost" + if "reference_bandwidth" in config_data["auto_cost"]: + command += " reference-bandwidth {reference_bandwidth}".format( + **config_data["auto_cost"] + ) + return command + + +def _tmplt_ospf_capability(config_data): + if "capability" in config_data: + if "lls" in config_data["capability"]: + command = "capability lls" + elif "opaque" in config_data["capability"]: + command = "capability opaque" + elif "transit" in config_data["capability"]: + command = "capability transit" + elif "vrf_lite" in config_data["capability"]: + command = "capability vrf-lite" + return command + + +def _tmplt_ospf_compatible(config_data): + if "compatible" in config_data: + if "rfc1583" in config_data["compatible"]: + command = "compatible rfc1583" + elif "rfc1587" in config_data["compatible"]: + command = "compatible rfc1587" + elif "rfc5243" in config_data["compatible"]: + command = "compatible rfc5243" + return command + + +def _tmplt_ospf_default_information(config_data): + if "default_information" in config_data: + command = "default-information" + if "originate" in config_data["default_information"]: + command += " originate" + if "always" in config_data["default_information"]: + command += " always" + if "metric" in config_data["default_information"]: + command += " metric {metric}".format(**config_data["default_information"]) + if "metric_type" in config_data["default_information"]: + command += " metric-type {metric_type}".format(**config_data["default_information"]) + if "metric" in config_data["default_information"]: + command += " route-map {route_map}".format(**config_data["default_information"]) + return command + + +def _tmplt_ospf_discard_route(config_data): + if "discard_route" in config_data: + command = "discard-route" + if "external" in config_data["discard_route"]: + command += " external {external}".format(**config_data["discard_route"]) + if "internal" in config_data["discard_route"]: + command += " internal {internal}".format(**config_data["discard_route"]) + return command + + +def _tmplt_ospf_distance_admin_distance(config_data): + if "admin_distance" in config_data["distance"]: + command = "distance {distance}".format(**config_data["distance"]["admin_distance"]) + if "address" in config_data["distance"]["admin_distance"]: + command += " {address} {wildcard_bits}".format( + **config_data["distance"]["admin_distance"] + ) + if "acl" in config_data["distance"]["admin_distance"]: + command += " {acl}".format(**config_data["distance"]["admin_distance"]) + return command + + +def _tmplt_ospf_distance_ospf(config_data): + if "ospf" in config_data["distance"]: + command = "distance ospf" + if "inter_area" in config_data["distance"]["ospf"]: + command += " inter-area {inter_area}".format(**config_data["distance"]["ospf"]) + if config_data["distance"].get("ospf").get("intra_area"): + command += " intra-area {intra_area}".format(**config_data["distance"]["ospf"]) + if config_data["distance"].get("ospf").get("external"): + command += " external {external}".format(**config_data["distance"]["ospf"]) + return command + + +def _tmplt_ospf_distribute_list_acls(config_data): + if "acls" in config_data.get("distribute_list"): + command = [] + for k, v in iteritems(config_data["distribute_list"]["acls"]): + cmd = "distribute-list {name} {direction}".format(**v) + if "interface" in v: + cmd += " {interface}".format(**v) + if "protocol" in v: + cmd += " {protocol}".format(**v) + command.append(cmd) + return command + + +def _tmplt_ospf_distribute_list_prefix(config_data): + if "prefix" in config_data.get("distribute_list"): + command = "distribute-list prefix {name}".format(**config_data["distribute_list"]["prefix"]) + if "gateway_name" in config_data["distribute_list"]["prefix"]: + command += " gateway {gateway_name}".format(**config_data["distribute_list"]["prefix"]) + if "direction" in config_data["distribute_list"]["prefix"]: + command += " {direction}".format(**config_data["distribute_list"]["prefix"]) + if "interface" in config_data["distribute_list"]["prefix"]: + command += " {interface}".format(**config_data["distribute_list"]["prefix"]) + if "protocol" in config_data["distribute_list"]["prefix"]: + command += " {protocol}".format(**config_data["distribute_list"]["prefix"]) + return command + + +def _tmplt_ospf_domain_id(config_data): + if "domain_id" in config_data: + command = "domain-id" + if "ip_address" in config_data["domain_id"]: + if "address" in config_data["domain_id"]["ip_address"]: + command += " {address}".format(**config_data["domain_id"]["ip_address"]) + if "secondary" in config_data["domain_id"]["ip_address"]: + command += " {secondary}".format(**config_data["domain_id"]["ip_address"]) + elif "null" in config_data["domain_id"]: + command += " null" + return command + + +def _tmplt_ospf_event_log(config_data): + if "event_log" in config_data: + command = "event-log" + if "one_shot" in config_data["event_log"]: + command += " one-shot" + if "pause" in config_data["event_log"]: + command += " pause" + if "size" in config_data["event_log"]: + command += " size {size}".format(**config_data["event_log"]) + return command + + +def _tmplt_ospf_limit(config_data): + if "limit" in config_data: + command = "limit retransmissions" + if "dc" in config_data["limit"]: + if "number" in config_data["limit"]["dc"]: + command += " dc {number}".format(**config_data["limit"]["dc"]) + if "disable" in config_data["limit"]["dc"]: + command += " dc disable" + if "non_dc" in config_data["limit"]: + if "number" in config_data["limit"]["non_dc"]: + command += " non-dc {number}".format(**config_data["limit"]["non_dc"]) + if "disable" in config_data["limit"]["dc"]: + command += " non-dc disable" + return command + + +def _tmplt_ospf_vrf_local_rib_criteria(config_data): + if "local_rib_criteria" in config_data: + command = "local-rib-criteria" + if "forwarding_address" in config_data["local_rib_criteria"]: + command += " forwarding-address" + if "inter_area_summary" in config_data["local_rib_criteria"]: + command += " inter-area-summary" + if "nssa_translation" in config_data["local_rib_criteria"]: + command += " nssa-translation" + return command + + +def _tmplt_ospf_log_adjacency_changes(config_data): + if "log_adjacency_changes" in config_data: + command = "log-adjacency-changes" + if "detail" in config_data["log_adjacency_changes"]: + command += " detail" + return command + + +def _tmplt_ospf_max_lsa(config_data): + if "max_lsa" in config_data: + command = "max-lsa {number}".format(**config_data["max_lsa"]) + if "threshold_value" in config_data["max_lsa"]: + command += " {threshold_value}".format(**config_data["max_lsa"]) + if "ignore_count" in config_data["max_lsa"]: + command += " ignore-count {ignore_count}".format(**config_data["max_lsa"]) + if "ignore_time" in config_data["max_lsa"]: + command += " ignore-time {ignore_time}".format(**config_data["max_lsa"]) + if "reset_time" in config_data["max_lsa"]: + command += " reset-time {reset_time}".format(**config_data["max_lsa"]) + if "warning_only" in config_data["max_lsa"]: + command += " warning-only" + return command + + +def _tmplt_ospf_max_metric(config_data): + if "max_metric" in config_data: + command = "max-metric" + if "router_lsa" in config_data["max_metric"]: + command += " router-lsa" + if "external_lsa" in config_data["max_metric"]: + command += " external-lsa {external_lsa}".format(**config_data["max_metric"]) + if "include_stub" in config_data["max_metric"]: + command += " include-stub" + if "on_startup" in config_data["max_metric"]: + if "time" in config_data["max_metric"]["on_startup"]: + command += " on-startup {time}".format(**config_data["max_metric"]["on_startup"]) + elif "wait_for_bgp" in config_data["max_metric"]["on_startup"]: + command += " on-startup wait-for-bgp" + if "summary_lsa" in config_data["max_metric"]: + command += " summary-lsa {summary_lsa}".format(**config_data["max_metric"]) + return command + + +def _tmplt_ospf_mpls_ldp(config_data): + if "ldp" in config_data["mpls"]: + command = "mpls ldp" + if "autoconfig" in config_data["mpls"]["ldp"]: + command += " autoconfig" + if "area" in config_data["mpls"]["ldp"]["autoconfig"]: + command += " area {area}".format(**config_data["mpls"]["ldp"]["autoconfig"]) + elif "sync" in config_data["mpls"]["ldp"]: + command += " sync" + return command + + +def _tmplt_ospf_mpls_traffic_eng(config_data): + if "traffic_eng" in config_data["mpls"]: + command = "mpls traffic-eng" + if "area" in config_data["mpls"]["traffic_eng"]: + command += " area {area}".format(**config_data["mpls"]["traffic_eng"]) + elif "autoroute_exclude" in config_data["mpls"]["traffic_eng"]: + command += " autoroute-exclude prefix-list {autoroute_exclude}".format( + **config_data["mpls"]["traffic_eng"] + ) + elif "interface" in config_data["mpls"]["traffic_eng"]: + command += " interface {int_type}".format( + **config_data["mpls"]["traffic_eng"]["interface"] + ) + if "area" in config_data["mpls"]["traffic_eng"]["interface"]: + command += " area {area}".format(**config_data["mpls"]["traffic_eng"]["interface"]) + elif "mesh_group" in config_data["mpls"]["traffic_eng"]: + command += " mesh-group {id} {interface}".format( + **config_data["mpls"]["traffic_eng"]["mesh_group"] + ) + if "area" in config_data["mpls"]["traffic_eng"]["mesh_group"]: + command += " area {area}".format(**config_data["mpls"]["traffic_eng"]["mesh_group"]) + elif "multicast_intact" in config_data["mpls"]["traffic_eng"]: + command += " multicast-intact" + elif "router_id_interface" in config_data["mpls"]["traffic_eng"]: + command += " router-id {router_id_interface}".format( + **config_data["mpls"]["traffic_eng"] + ) + return command + + +def _tmplt_ospf_neighbor(config_data): + if "neighbor" in config_data: + command = "neighbor" + if "address" in config_data["neighbor"]: + command += " {address}".format(**config_data["neighbor"]) + if "cost" in config_data["neighbor"]: + command += " cost {cost}".format(**config_data["neighbor"]) + if "database_filter" in config_data["neighbor"]: + command += " database-filter all out" + if "poll_interval" in config_data["neighbor"]: + command += " poll-interval {poll_interval}".format(**config_data["neighbor"]) + if "priority" in config_data["neighbor"]: + command += " priority {priority}".format(**config_data["neighbor"]) + return command + + +def _tmplt_ospf_network(config_data): + if "network" in config_data: + command = [] + for each in config_data["network"]: + cmd = "network" + if "address" in each: + cmd += " {address} {wildcard_bits}".format(**each) + if "area" in each: + cmd += " area {area}".format(**each) + command.append(cmd) + return command + + +def _tmplt_ospf_nsf_cisco(config_data): + if "cisco" in config_data["nsf"]: + command = "nsf cisco helper" + if "disable" in config_data["nsf"]["cisco"]: + command += " disable" + return command + + +def _tmplt_ospf_nsf_ietf(config_data): + if "ietf" in config_data["nsf"]: + command = "nsf ietf helper" + if "disable" in config_data["nsf"]["ietf"]: + command += " disable" + elif "strict_lsa_checking" in config_data["nsf"]["ietf"]: + command += " strict-lsa-checking" + return command + + +def _tmplt_ospf_queue_depth_hello(config_data): + if "hello" in config_data["queue_depth"]: + command = "queue-depth hello" + if "max_packets" in config_data["queue_depth"]["hello"]: + command += " {max_packets}".format(**config_data["queue_depth"]["hello"]) + elif "unlimited" in config_data["queue_depth"]["hello"]: + command += " unlimited" + return command + + +def _tmplt_ospf_queue_depth_update(config_data): + if "update" in config_data["queue_depth"]: + command = "queue-depth update" + if "max_packets" in config_data["queue_depth"]["update"]: + command += " {max_packets}".format(**config_data["queue_depth"]["update"]) + elif "unlimited" in config_data["queue_depth"]["update"]: + command += " unlimited" + return command + + +def _tmplt_ospf_passive_interfaces(config_data): + if "passive_interfaces" in config_data: + if config_data["passive_interfaces"].get("default"): + cmd = "passive-interface default" + if config_data["passive_interfaces"].get("interface"): + if config_data["passive_interfaces"].get("set_interface"): + for each in config_data["passive_interfaces"]["interface"]: + cmd = "passive-interface {0}".format(each) + elif not config_data["passive_interfaces"].get("set_interface"): + for each in config_data["passive_interfaces"]["interface"]: + cmd = "no passive-interface {0}".format(each) + return cmd + + +def _tmplt_ospf_summary_address(config_data): + if "summary_address" in config_data: + command = "summary-address {address} {mask}".format(**config_data["summary_address"]) + if "not_advertise" in config_data["summary_address"]: + command += " not-advertise" + elif "nssa_only" in config_data["summary_address"]: + command += " nssa-only" + if "tag" in config_data["summary_address"]: + command += " tag {tag}".format(**config_data["summary_address"]) + return command + + +def _tmplt_ospf_timers_pacing(config_data): + if "pacing" in config_data["timers"]: + command = "timers pacing" + if "flood" in config_data["timers"]["pacing"]: + command += " flood {flood}".format(**config_data["timers"]["pacing"]) + elif "lsa_group" in config_data["timers"]["pacing"]: + command += " lsa-group {lsa_group}".format(**config_data["timers"]["pacing"]) + elif "retransmission" in config_data["timers"]["pacing"]: + command += " retransmission {retransmission}".format(**config_data["timers"]["pacing"]) + return command + + +def _tmplt_ospf_ttl_security(config_data): + if "ttl_security" in config_data: + command = "ttl-security all-interfaces" + if "hops" in config_data["ttl_security"]: + command += " hops {hops}".format(**config_data["ttl_security"]) + return command + + +class Ospfv2Template(NetworkTemplate): + def __init__(self, lines=None): + super(Ospfv2Template, self).__init__(lines=lines, tmplt=self) + + PARSERS = [ + { + "name": "pid", + "getval": re.compile( + r""" + ^router\s + ospf* + \s(?P<pid>\S+) + \svrf + \s(?P<vrf>\S+) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_vrf_cmd, + "result": { + "processes": {"{{ pid }}": {"process_id": "{{ pid|int }}", "vrf": "{{ vrf }}"}}, + }, + "shared": True, + }, + { + "name": "pid", + "getval": re.compile( + r""" + ^router\s + ospf* + \s(?P<pid>\S+) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_vrf_cmd, + "result": {"processes": {"{{ pid }}": {"process_id": "{{ pid|int }}"}}}, + "shared": True, + }, + { + "name": "adjacency", + "getval": re.compile( + r"""\s+adjacency + \sstagger* + \s*((?P<min>\d+)|(?P<none_adj>none))* + \s*(?P<max>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_adjacency_cmd, + "result": { + "processes": { + "{{ pid }}": { + "adjacency": { + "min_adjacency": "{{ min|int }}", + "max_adjacency": "{{ max|int }}", + "none": "{{ True if none_adj is defined else None }}", + }, + }, + }, + }, + }, + { + "name": "address_family", + "getval": re.compile( + r"""\s+topology + \s(?P<base>base)* + \s*(?P<name>\S+)* + \s*(?P<tid>tid\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_address_family_cmd, + "result": { + "processes": { + "{{ pid }}": { + "address_family": { + "topology": { + "base": "{{ True if base is defined }}", + "name": "{{ name }}", + "tid": "{{ tid.split(" ")[1] }}", + }, + }, + }, + }, + }, + }, + { + "name": "area.authentication", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \s*(?P<auth>authentication)* + \s*(?P<md>message-digest) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_authentication, + "compval": "authentication", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "authentication": { + "enable": "{{ True if auth is defined and md is undefined }}", + "message_digest": "{{ not not md }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "area.capability", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \s*(?P<capability>capability)* + \s*(?P<df>default-exclusion) + *$""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} capability default-exclusion", + "compval": "capability", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "capability": "{{ not not capability }}", + }, + }, + }, + }, + }, + }, + { + "name": "area.default_cost", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \sdefault-cost* + \s*(?P<default_cost>\S+) + *$""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} default-cost {{ default_cost }}", + "compval": "default_cost", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "default_cost": "{{ default_cost|int }}", + }, + }, + }, + }, + }, + }, + { + "name": "area.filter_list", + "getval": re.compile( + r"""\s+area + \s*(?P<area_id>\S+)* + \s*filter-list\sprefix* + \s*(?P<name>\S+)* + \s*(?P<dir>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_filter, + "compval": "filter_list", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "filter_list": [{"name": "{{ name }}", "direction": "{{ dir }}"}], + }, + }, + }, + }, + }, + }, + { + "name": "area.nssa", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \s(?P<nssa>nssa)* + \s*(?P<no_redis>no-redistribution)* + \s*(?P<def_origin>default-information-originate)* + \s*(?P<metric>metric\s\d+)* + \s*(?P<metric_type>metric-type\s\d+)* + \s*(?P<no_summary>no-summary)* + \s*(?P<no_ext>no-ext-capability)*$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa_translate, + "compval": "nssa", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "nssa": { + "set": "{{ True if nssa is defined and def_origin is undefined and " + "no_ext is undefined and no_redis is undefined and nssa_only is undefined }}", + "default_information_originate": { + "set": "{{ True if def_origin is defined and metric is undefined and " + "metric_type is undefined and nssa_only is undefined }}", + "metric": "{{ metric.split(" ")[1]|int }}", + "metric_type": "{{ metric_type.split(" ")[1]|int }}", + "nssa_only": "{{ True if nssa_only is defined }}", + }, + "no_ext_capability": "{{ True if no_ext is defined }}", + "no_redistribution": "{{ True if no_redis is defined }}", + "no_summary": "{{ True if no_summary is defined }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "area.nssa.translate", + "getval": re.compile( + r"""\s+area* + \s*(?P<area_id>\S+)* + \s*(?P<nssa>nssa)* + \stranslate\stype7* + \s(?P<translate_always>always)* + \s* (?P<translate_supress>suppress-fa) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa, + "compval": "nssa.translate", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "nssa": { + "translate": "{{ translate_always if translate_always is defined else translate_supress if translate_supress is defined }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "area.ranges", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \srange + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + \s(?P<netmask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*((?P<advertise>advertise)|(?P<not_advertise>not-advertise))* + \s*(?P<cost>cost\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_ranges, + "compval": "ranges", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "ranges": [ + { + "address": "{{ address }}", + "netmask": "{{ netmask }}", + "advertise": "{{ True if advertise is defined }}", + "cost": "{{ cost.split(" ")[1]|int }}", + "not_advertise": "{{ True if not_advertise is defined }}", + }, + ], + }, + }, + }, + }, + }, + }, + { + "name": "area.sham_link", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \ssham-link + \s(?P<source>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + \s(?P<destination>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<cost>cost\s\d+)* + \s*(?P<ttl_security>ttl-security\shops\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_sham_link, + "compval": "sham_link", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "sham_link": { + "source": "{{ source }}", + "destination": "{{ destination }}", + "cost": "{{ cost.split(" ")[1]|int }}", + "ttl_security": '{{ ttl_security.split("hops ")[1] }}', + }, + }, + }, + }, + }, + }, + }, + { + "name": "area.stub", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \s(?P<stub>stub)* + \s*(?P<no_ext>no-ext-capability)* + \s*(?P<no_sum>no-summary) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_stub_link, + "compval": "stub", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "stub": { + "set": "{{ True if stub is defined and no_ext is undefined and no_sum is undefined }}", + "no_ext_capability": "{{ True if no_ext is defined }}", + "no_summary": "{{ True if no_sum is defined }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "auto_cost", + "getval": re.compile( + r"""\s+(?P<auto_cost>auto-cost)* + \s*(?P<ref_band>reference-bandwidth\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_auto_cost, + "result": { + "processes": { + "{{ pid }}": { + "auto_cost": { + "set": "{{ True if auto_cost is defined and ref_band is undefined }}", + "reference_bandwidth": '{{ ref_band.split(" ")[1] }}', + }, + }, + }, + }, + }, + { + "name": "bfd", + "getval": re.compile( + r"""\s+bfd* + \s*(?P<bfd>all-interfaces) + *$""", + re.VERBOSE, + ), + "setval": "bfd all-interfaces", + "result": {"processes": {"{{ pid }}": {"bfd": "{{ True if bfd is defined }}"}}}, + }, + { + "name": "capability", + "getval": re.compile( + r"""\s+capability* + \s*((?P<lls>lls)|(?P<opaque>opaque)|(?P<transit>transit)|(?P<vrf_lite>vrf-lite)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_capability, + "result": { + "processes": { + "{{ pid }}": { + "capability": { + "lls": "{{ True if lls is defined }}", + "opaque": "{{ True if opaque is defined }}", + "transit": "{{ True if transit is defined }}", + "vrf_lite": "{{ True if vrf_lite is defined }}", + }, + }, + }, + }, + }, + { + "name": "compatible", + "getval": re.compile( + r"""\s+compatible* + \s*((?P<rfc1583>rfc1583)|(?P<rfc1587>rfc1587)|(?P<rfc5243>rfc5243)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_compatible, + "result": { + "processes": { + "{{ pid }}": { + "compatible": { + "rfc1583": "{{ True if rfc1583 is defined }}", + "rfc1587": "{{ True if rfc1587 is defined }}", + "rfc5243": "{{ True if rfc5243 is defined }}", + }, + }, + }, + }, + }, + { + "name": "default_information", + "getval": re.compile( + r"""\s+default-information* + \s*(?P<originate>originate)* + \s*(?P<always>always)* + \s*(?P<metric>metric\s\d+)* + \s*(?P<metric_type>metric-type\s\d+)* + \s*(?P<route_map>route-map\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_default_information, + "result": { + "processes": { + "{{ pid }}": { + "default_information": { + "originate": "{{ True if originate is defined }}", + "always": "{{ True if always is defined }}", + "metric": "{{ metric.split(" ")[1]|int }}", + "metric_type": "{{ metric_type.split(" ")[1]|int }}", + "route_map": "{{ route_map.split(" ")[1] }}", + }, + }, + }, + }, + }, + { + "name": "default_metric", + "getval": re.compile( + r"""\s+default-metric(?P<default_metric>\s\d+) + *$""", + re.VERBOSE, + ), + "setval": "default-metric {{ default_metric }}", + "result": {"processes": {"{{ pid }}": {"default_metric": "{{ default_metric| int}}"}}}, + }, + { + "name": "discard_route", + "getval": re.compile( + r"""\s+(?P<discard_route>discard-route)* + \s*(?P<external>external\s\d+)* + \s*(?P<internal>internal\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_discard_route, + "result": { + "processes": { + "{{ pid }}": { + "discard_route": { + "set": "{{ True if discard_route is defined and external is undefined and internal is undefined }}", + "external": "{{ external.split(" ")[1]|int }}", + "internal": "{{ internal.split(" ")[1]|int }}", + }, + }, + }, + }, + }, + { + "name": "distance.admin_distance", + "getval": re.compile( + r"""\s+distance + \s(?P<admin_dist>\S+)* + \s*(?P<source>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<wildcard>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<acl>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_admin_distance, + "compval": "admin_distance", + "result": { + "processes": { + "{{ pid }}": { + "distance": { + "admin_distance": { + "distance": "{{ admin_dist }}", + "address": "{{ source }}", + "wildcard_bits": "{{ wildcard }}", + "acl": "{{ acl }}", + }, + }, + }, + }, + }, + }, + { + "name": "distance.ospf", + "getval": re.compile( + r"""\s+distance + \sospf* + \s*(?P<intra>intra-area\s\d+)* + \s*(?P<inter>inter-area\s\d+)* + \s*(?P<external>external\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_ospf, + "compval": "ospf", + "result": { + "processes": { + "{{ pid }}": { + "distance": { + "ospf": { + "inter_area": "{{ inter.split(" ")[1]|int }}", + "intra_area": "{{ intra.split(" ")[1]|int }}", + "external": "{{ external.split(" ")[1]|int }}", + }, + }, + }, + }, + }, + }, + { + "name": "distribute_list.acls", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<name>\S+)* + \s*(?P<dir>\S+)* + \s*(?P<int_pro>\S+\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distribute_list_acls, + "compval": "distribute_list.acls", + "result": { + "processes": { + "{{ pid }}": { + "distribute_list": { + "acls": [ + { + "name": "{{ name }}", + "direction": "{{ dir }}", + "interface": '{{ int_pro if dir == "in" }}', + "protocol": '{{ int_pro if dir == "out" }}', + }, + ], + }, + }, + }, + }, + }, + { + "name": "distribute_list.prefix", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<prefix>prefix\s\S+)* + \s*(?P<gateway>gateway\s\S+)* + \s*(?P<dir>\S+)* + \s*(?P<int_pro>\S+\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distribute_list_prefix, + "compval": "distribute_list.prefix", + "result": { + "processes": { + "{{ pid }}": { + "distribute_list": { + "prefix": { + "name": "{{ prefix.split(" ")[1] }}", + "gateway_name": "{{ gateway.split(" ")[1] if prefix is defined }}", + "direction": "{{ dir if gateway is undefined }}", + "interface": '{{ int_pro if dir == "in" }}', + "protocol": '{{ int_pro if dir == "out" }}', + }, + }, + }, + }, + }, + }, + { + "name": "distribute_list.route_map", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<route_map>route-map\s\S+)* + \s*(?P<dir>\S+) + *$""", + re.VERBOSE, + ), + "setval": "distribute-list route-map {{ distribute_list.route_map.name }} in", + "compval": "distribute_list.route_map", + "result": { + "processes": { + "{{ pid }}": { + "distribute_list": {"route_map": {"name": "{{ route_map.split(" ")[1] }}"}}, + }, + }, + }, + }, + { + "name": "domain_id", + "getval": re.compile( + r"""\s+domain-id + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<secondary>secondary)* + \s*(?P<null>null) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_domain_id, + "result": { + "processes": { + "{{ pid }}": { + "domain_id": { + "ip_address": { + "address": "{{ address }}", + "secondary": "{{ True if secondary is defined }}", + }, + "null": "{{ True if null is defined }}", + }, + }, + }, + }, + }, + { + "name": "domain_tag", + "getval": re.compile( + r"""\s+domain-tag + \s(?P<tag>\d+) + *$""", + re.VERBOSE, + ), + "setval": "domain-tag {{ domain_tag }}", + "result": {"processes": {"{{ pid }}": {"domain_tag": "{{ tag|int }}"}}}, + }, + { + "name": "event_log", + "getval": re.compile( + r"""\s+(?P<event_log>event-log)* + \s*(?P<one_shot>one-shot)* + \s*(?P<pause>pause)* + \s*(?P<size>size\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_event_log, + "result": { + "processes": { + "{{ pid }}": { + "event_log": { + "enable": "{{ True if event_log is defined and one_shot is undefined and pause is undefined and size is undefined }}", + "one_shot": "{{ True if one_shot is defined }}", + "pause": "{{ True if pause is defined }}", + "size": "{{ size.split(" ")[1]|int }}", + }, + }, + }, + }, + }, + { + "name": "help", + "getval": re.compile( + r"""\s+(?P<help>help) + *$""", + re.VERBOSE, + ), + "setval": "help", + "result": {"processes": {"{{ pid }}": {"help": "{{ True if help is defined }}"}}}, + }, + { + "name": "ignore", + "getval": re.compile( + r"""\s+(?P<ignore>ignore) + *$""", + re.VERBOSE, + ), + "setval": "ignore lsa mospf", + "result": {"processes": {"{{ pid }}": {"ignore": "{{ True if ignore is defined }}"}}}, + }, + { + "name": "interface_id", + "getval": re.compile( + r"""\s+(?P<interface_id>interface-id\ssnmp-if-index) + *$""", + re.VERBOSE, + ), + "setval": "interface-id snmp-if-index", + "result": { + "processes": { + "{{ pid }}": {"interface_id": "{{ True if interface_id is defined }}"}, + }, + }, + }, + { + "name": "ispf", + "getval": re.compile( + r"""\s+(?P<ispf>ispf) + *$""", + re.VERBOSE, + ), + "setval": "ispf", + "result": {"processes": {"{{ pid }}": {"ispf": "{{ True if ispf is defined }}"}}}, + }, + { + "name": "limit", + "getval": re.compile( + r"""\s+limit\sretransmissions + \s((?P<dc_num>dc\s\d+)|(?P<dc_disable>dc\sdisable))* + \s*((?P<non_dc_num>non-dc\s\d+)|(?P<non_dc_disable>non-dc\sdisable)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_limit, + "result": { + "processes": { + "{{ pid }}": { + "limit": { + "dc": { + "number": "{{ dc_num.split(" ")[1]|int }}", + "disable": "{{ True if dc_disable is defined }}", + }, + "non_dc": { + "number": "{{ non_dc_num.split(" ")[1]|int }}", + "disable": "{{ True if dc_disable is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "local_rib_criteria", + "getval": re.compile( + r"""\s+(?P<local>local-rib-criteria)* + \s*(?P<forward>forwarding-address)* + \s*(?P<inter>inter-area-summary)* + \s*(?P<nssa>nssa-translation) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_vrf_local_rib_criteria, + "result": { + "processes": { + "{{ pid }}": { + "local_rib_criteria": { + "enable": "{{ True if local is defined and forward is undefined and inter is undefined and nssa is undefined }}", + "forwarding_address": "{{ True if forward is defined }}", + "inter_area_summary": "{{ True if inter is defined }}", + "nssa_translation": "{{ True if nssa is defined }}", + }, + }, + }, + }, + }, + { + "name": "log_adjacency_changes", + "getval": re.compile( + r"""\s+(?P<log>log-adjacency-changes)* + \s*(?P<detail>detail) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_log_adjacency_changes, + "result": { + "processes": { + "{{ pid }}": { + "log_adjacency_changes": { + "set": "{{ True if log is defined and detail is undefined }}", + "detail": "{{ True if detail is defined }}", + }, + }, + }, + }, + }, + { + "name": "max_lsa", + "getval": re.compile( + r"""\s+max-lsa + \s(?P<number>\d+)* + \s*(?P<threshold>\d+)* + \s*(?P<ignore_count>ignore-count\s\d+)* + \s*(?P<ignore_time>ignore-time\s\d+)* + \s*(?P<reset_time>reset-time\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_lsa, + "result": { + "processes": { + "{{ pid }}": { + "max_lsa": { + "number": "{{ number }}", + "threshold_value": "{{ threshold }}", + "ignore_count": "{{ ignore_count.split(" ")[1] }}", + "ignore_time": "{{ ignore_time.split(" ")[1] }}", + "reset_time": "{{ reset_time.split(" ")[1] }}", + "warning_only": "{{ True if warning is defined }}", + }, + }, + }, + }, + }, + { + "name": "max_metric", + "getval": re.compile( + r"""\s+max-metric* + \s*(?P<router_lsa>router-lsa)* + \s*(?P<include_stub>include-stub)* + \s*(?P<external_lsa>external-lsa\s\d+)* + \s*(?P<startup_time>on-startup\s\d+)* + \s*(?P<startup_wait>on-startup\s\S+)* + \s*(?P<summary_lsa>summary-lsa\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_metric, + "result": { + "processes": { + "{{ pid }}": { + "max_metric": { + "router_lsa": "{{ True if router_lsa is defined }}", + "external_lsa": "{{ external_lsa.split(" ")[1] }}", + "include_stub": "{{ ignore_count.split(" ")[1] }}", + "on_startup": { + "time": "{{ startup_time.split(" ")[1] }}", + "wait_for_bgp": "{{ True if startup_wait is defined }}", + }, + "summary_lsa": "{{ summary_lsa.split(" ")[1] }}", + }, + }, + }, + }, + }, + { + "name": "maximum_paths", + "getval": re.compile( + r"""\s+maximum-paths* + \s+(?P<paths>\d+) + *$""", + re.VERBOSE, + ), + "setval": "maximum-paths {{ maximum_paths }}", + "result": {"processes": {"{{ pid }}": {"maximum_paths": "{{ paths }}"}}}, + }, + { + "name": "mpls.ldp", + "getval": re.compile( + r"""\s+mpls + \sldp* + \s*(?P<autoconfig>autoconfig*\s*(?P<area>area\s\S+))* + \s*(?P<sync>sync) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_mpls_ldp, + "compval": "ldp", + "result": { + "processes": { + "{{ pid }}": { + "mpls": { + "ldp": { + "autoconfig": { + "set": "{{ True if autoconfig is defined and area is undefined }}", + "area": "{{ area.split(" ")[1] }}", + }, + "sync": "{{ True if sync is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "mpls.traffic_eng", + "getval": re.compile( + r"""\s+mpls + \straffic-eng* + \s*(?P<area>area\s\S+)* + \s*(?P<autoroute>autoroute-exclude\s\S+\s\S+)* + \s*(?P<interface>interface\s(?P<int_type>\S+\s\S+)\s(?P<int_area>area\s\S+))* + \s*(?P<mesh>mesh-group\s\d+\s(?P<mesh_int>\S+\s\S+)\s(?P<mesh_area>area\s\d+))* + \s*(?P<multicast>multicast-intact)* + \s*(?P<router>router-id\s(?P<router_int>\S+\s\S+)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_mpls_traffic_eng, + "compval": "traffic_eng", + "result": { + "processes": { + "{{ pid }}": { + "mpls": { + "traffic_eng": { + "area": "{{ area.split(" ")[1] }}", + "autoroute_exclude": "{{ autoroute.split(" ")[2] }}", + "interface": { + "interface_type": "{{ int_type }}", + "area": "{{ int_area.split(" ")[1] }}", + }, + "mesh_group": { + "id": "{{ mesh.split(" ")[1] }}", + "interface": "{{ mest_int }}", + "area": "{{ mesh_area.split(" ")[1] }}", + }, + "multicast_intact": "{{ True if multicast is defined }}", + "router_id_interface": "{{ router_int }}", + }, + }, + }, + }, + }, + }, + { + "name": "neighbor", + "getval": re.compile( + r"""\s+neighbor + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<cost>cost\s\d+)* + \s*(?P<db_filter>database-filter\sall\sout)* + \s*(?P<poll>poll-interval\s\d+)* + \s*(?P<priority>priority\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_neighbor, + "result": { + "processes": { + "{{ pid }}": { + "neighbor": { + "address": "{{ address }}", + "cost": "{{ cost.split(" ")[1] }}", + "database_filter": "{{ True if db_filter is defined }}", + "poll_interval": "{{ poll.split(" ")[1] }}", + "priority": "{{ priority.split(" ")[1] }}", + }, + }, + }, + }, + }, + { + "name": "network", + "getval": re.compile( + r"""\s+network + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<wildcard>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<area>area\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_network, + "result": { + "processes": { + "{{ pid }}": { + "network": [ + { + "address": "{{ address }}", + "wildcard_bits": "{{ wildcard }}", + "area": "{{ area.split(" ")[1] }}", + }, + ], + }, + }, + }, + }, + { + "name": "nsf.cisco", + "getval": re.compile( + r"""\s+nsf + \s(?P<cisco>cisco)* + \s*(?P<helper>helper)* + \s*(?P<disable>disable) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_nsf_cisco, + "compval": "cisco", + "result": { + "processes": { + "{{ pid }}": { + "nsf": { + "cisco": { + "helper": "{{ True if helper is defined }}", + "disable": "{{ True if disable is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "nsf.ietf", + "getval": re.compile( + r"""\s+nsf + \s(?P<ietf>ietf)* + \s*(?P<helper>helper)* + \s*(?P<disable>disable)* + \s*(?P<strict>strict-lsa-checking) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_nsf_ietf, + "compval": "ietf", + "result": { + "processes": { + "{{ pid }}": { + "nsf": { + "ietf": { + "helper": "{{ True if helper is defined }}", + "disable": "{{ True if disable is defined }}", + "strict_lsa_checking": "{{ True if strict is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "passive_interfaces", + "getval": re.compile( + r"""\s*(?P<no>no)* + \s*passive-interface* + \s*(?P<interface>\S+\s\S+|\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_passive_interfaces, + "result": { + "processes": { + "{{ pid }}": { + "passive_interfaces": { + "default": "{{ True if 'default' in interface }}", + "interface": { + "set_interface": "{% if no is defined %}{{ False }}{% elif 'default' not in interface %}{{ True }}{% endif %}", + "name": ["{{ interface if 'default' not in interface }}"], + }, + }, + }, + }, + }, + }, + { + "name": "passive_interface", + "getval": re.compile( + r"""\s+passive-interface + \s(?P<interface>\S+\s\S+) + *$""", + re.VERBOSE, + ), + "setval": "passive-interface {{ passive_interface }}", + "result": {"processes": {"{{ pid }}": {"passive_interface": "{{ interface }}"}}}, + }, + { + "name": "prefix_suppression", + "getval": re.compile( + r"""\s+(?P<prefix_sup>prefix-suppression) + *$""", + re.VERBOSE, + ), + "setval": "prefix-suppression", + "result": { + "processes": { + "{{ pid }}": {"prefix_suppression": "{{ True if prefix_sup is defined }}"}, + }, + }, + }, + { + "name": "priority", + "getval": re.compile( + r"""\s+priority + \s(?P<priority>\d+) + *$""", + re.VERBOSE, + ), + "setval": "priority {{ priority }}", + "result": {"processes": {"{{ pid }}": {"priority": "{{ priority }}"}}}, + }, + { + "name": "queue_depth.hello", + "getval": re.compile( + r"""\s+queue-depth + \shello* + \s*(?P<max_packets>\d+)* + \s*(?P<unlimited>unlimited) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_queue_depth_hello, + "compval": "hello", + "result": { + "processes": { + "{{ pid }}": { + "queue_depth": { + "hello": { + "max_packets": "{{ max_packets }}", + "unlimited": "{{ True if unlimited is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "queue_depth.update", + "getval": re.compile( + r"""\s+queue-depth + \supdate* + \s*(?P<max_packets>\d+)* + \s*(?P<unlimited>unlimited) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_queue_depth_update, + "compval": "update", + "result": { + "processes": { + "{{ pid }}": { + "queue_depth": { + "update": { + "max_packets": "{{ max_packets }}", + "unlimited": "{{ True if unlimited is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "router_id", + "getval": re.compile( + r"""\s+router-id + \s(?P<id>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + *$""", + re.VERBOSE, + ), + "setval": "router-id {{ router_id }}", + "result": {"processes": {"{{ pid }}": {"router_id": "{{ id }}"}}}, + }, + { + "name": "shutdown", + "getval": re.compile( + r"""\s+(?P<shutdown>shutdown) + *$""", + re.VERBOSE, + ), + "setval": "shutdown", + "result": { + "processes": {"{{ pid }}": {"shutdown": "{{ True if shutdown is defined }}"}}, + }, + }, + { + "name": "summary_address", + "getval": re.compile( + r"""\s+summary-address + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<mask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<not_adv>not-advertise)* + \s*(?P<nssa>nssa-only)* + \s*(?P<tag>tag\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_summary_address, + "result": { + "processes": { + "{{ pid }}": { + "summary_address": { + "address": "{{ address }}", + "mask": "{{ mask }}", + "not_advertise": "{{ True if not_adv is defined }}", + "nssa_only": "{{ True if nssa is defined }}", + "tag": "{{ tag.split(" ")[1] }}", + }, + }, + }, + }, + }, + { + "name": "timers.lsa", + "getval": re.compile( + r"""\s+timers + \slsa + \sarrival + \s(?P<lsa>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers lsa arrival {{ timers.lsa }}", + "compval": "lsa", + "result": {"processes": {"{{ pid }}": {"timers": {"lsa": "{{ lsa }}"}}}}, + }, + { + "name": "timers.pacing", + "getval": re.compile( + r"""\s+timers + \spacing + \s(?P<flood>flood\s\d+)* + \s*(?P<lsa_group>lsa-group\s\d+)* + \s*(?P<retransmission>retransmission\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_timers_pacing, + "compval": "pacing", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "pacing": { + "flood": "{{ flood.split(" ")[1] }}", + "lsa_group": "{{ lsa_group.split(" ")[1] }}", + "retransmission": "{{ retransmission.split(" ")[1] }}", + }, + }, + }, + }, + }, + }, + { + "name": "timers.throttle.lsa", + "getval": re.compile( + r"""\s+timers + \sthrottle + \s*(?P<lsa>lsa)* + \s*(?P<first_delay>\d+)* + \s*(?P<min_delay>\d+)* + \s*(?P<max_delay>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers throttle lsa {{ throttle.lsa.first_delay }} {{ throttle.lsa.min_delay }} {{ throttle.lsa.max_delay }}", + "compval": "throttle.lsa", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "throttle": { + "lsa": { + "first_delay": "{{ first_delay }}", + "min_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "timers.throttle.spf", + "getval": re.compile( + r"""\s+timers + \sthrottle + \s*(?P<spf>spf)* + \s*(?P<first_delay>\d+)* + \s*(?P<min_delay>\d+)* + \s*(?P<max_delay>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers throttle spf {{ throttle.spf.receive_delay }} {{ throttle.spf.between_delay }} {{ throttle.spf.max_delay }}", + "compval": "throttle.spf", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "throttle": { + "spf": { + "receive_delay": "{{ first_delay }}", + "between_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "traffic_share", + "getval": re.compile( + r"""\s+(?P<traffic>traffic-share\smin\sacross-interfaces) + *$""", + re.VERBOSE, + ), + "setval": "traffic-share min across-interfaces", + "result": { + "processes": {"{{ pid }}": {"traffic_share": "{{ True if traffic is defined }}"}}, + }, + }, + { + "name": "ttl_security", + "getval": re.compile( + r"""\s+ttl-security + \s(?P<interfaces>all-interfaces)* + \s*(?P<hops>hops\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_ttl_security, + "result": { + "processes": { + "{{ pid }}": { + "ttl_security": { + "set": "{{ True if interfaces is defined and hops is undefined }}", + "hops": "{{ hops.split(" ")[1] }}", + }, + }, + }, + }, + }, + ] diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv3.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv3.py new file mode 100644 index 000000000..874eed614 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv3.py @@ -0,0 +1,2935 @@ +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import re + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +def _tmplt_ospfv3_cmd(process): + command = "router ospfv3 {process_id}".format(**process) + if "vrf" in process: + command += " vrf {vrf}".format(**process) + return command + + +def _tmplt_ospf_adjacency_cmd(config_data): + if "adjacency" in config_data: + command = "adjacency stagger" + if "none" in config_data["adjacency"]: + command += " none" + else: + command += " {min_adjacency}".format(**config_data["adjacency"]) + if "max_adjacency" in config_data["adjacency"]: + command += " {min_adjacency}".format(**config_data["adjacency"]) + return command + + +def _tmplt_ospf_address_family_cmd(config_data): + if "address_family" in config_data: + command = [] + # for config_data in config_data["address_family"]: + cmd = "address-family {afi}".format(**config_data["address_family"]) + if config_data["address_family"].get("unicast"): + cmd += " unicast" + if config_data["address_family"].get("vrf"): + cmd += " vrf {vrf}".format(**config_data["address_family"]) + command.append(cmd) + if command: + command.insert(len(command), "exit-address-family") + return command + + +def _tmplt_address_family_graceful_restart(config_data): + if "graceful_restart" in config_data: + command = "graceful_restart {enable}".format(**config_data["graceful_restart"]) + if "disable" in config_data["graceful_restart"]: + command += " disable" + elif "strict_lsa_checking" in config_data["graceful_restart"]: + command += " strict-lsa-checking" + return command + + +def _tmplt_ospf_area_authentication(config_data): + if "authentication" in config_data: + command = "area {area_id} authentication".format(**config_data) + if config_data["authentication"].get("message_digest"): + command += " message-digest" + return command + + +def _tmplt_ospf_area_filter(config_data): + if "filter_list" in config_data: + command = [] + for key, value in iteritems(config_data.get("filter_list")): + cmd = "area {area_id}".format(**config_data) + if value.get("name") and value.get("direction"): + cmd += " filter-list prefix {name} {direction}".format(**value) + command.append(cmd) + return command + + +def _tmplt_ospf_area_nssa(config_data): + if "nssa" in config_data: + command = "area {area_id} nssa".format(**config_data) + if "default_information_originate" in config_data["nssa"]: + command += " default-information-originate" + if "metric" in config_data["nssa"]["default_information_originate"]: + command += " metric {metric}".format( + **config_data["nssa"]["default_information_originate"] + ) + if "metric_type" in config_data["nssa"]["default_information_originate"]: + command += " metric-type {metric_type}".format( + **config_data["nssa"]["default_information_originate"] + ) + if "nssa_only" in config_data["nssa"]["default_information_originate"]: + command += " nssa-only" + if config_data["nssa"].get("no_ext_capability"): + command += " no-ext-capability" + if config_data["nssa"].get("no_redistribution"): + command += " no-redistribution" + if config_data["nssa"].get("no_summary"): + command += " no-summary" + return command + + +def _tmplt_ospf_area_nssa_translate(config_data): + if "nssa" in config_data and "translate" in config_data["nssa"]: + command = "area {area_id} nssa".format(**config_data) + if "translate" in config_data["nssa"]: + command += " translate type7 {translate}".format(**config_data["nssa"]) + return command + + +def _tmplt_ospf_area_ranges(config_data): + if "ranges" in config_data: + commands = [] + for k, v in iteritems(config_data["ranges"]): + cmd = "area {area_id} range".format(**config_data) + temp_cmd = " {address} {netmask}".format(**v) + if "advertise" in v: + temp_cmd += " advertise" + elif "not_advertise" in v: + temp_cmd += " not-advertise" + if "cost" in v: + temp_cmd += " cost {cost}".format(**v) + cmd += temp_cmd + commands.append(cmd) + return commands + + +def _tmplt_ospf_area_sham_link(config_data): + if "sham_link" in config_data: + command = "area {area_id} sham-link".format(**config_data) + if "source" in config_data["sham_link"]: + command += " {source} {destination}".format(**config_data["sham_link"]) + if "cost" in config_data["sham_link"]: + command += " cost {cost}".format(**config_data["sham_link"]) + if "ttl_security" in config_data["sham_link"]: + command += " ttl-security hops {ttl_security}".format(**config_data["sham_link"]) + return command + + +def _tmplt_ospf_area_stub_link(config_data): + if "stub" in config_data: + command = "area {area_id} stub".format(**config_data) + if "no_ext_capability" in config_data["stub"]: + command += " no-ext-capability" + if "no_summary" in config_data["stub"]: + command += " no-summary" + return command + + +def _tmplt_ospf_auto_cost(config_data): + if "auto_cost" in config_data: + command = "auto-cost" + if "reference_bandwidth" in config_data["auto_cost"]: + command += " reference-bandwidth {reference_bandwidth}".format( + **config_data["auto_cost"] + ) + return command + + +def _tmplt_ospf_capability(config_data): + if "capability" in config_data: + if "lls" in config_data["capability"]: + command = "capability lls" + elif "opaque" in config_data["capability"]: + command = "capability opaque" + elif "transit" in config_data["capability"]: + command = "capability transit" + elif "vrf_lite" in config_data["capability"]: + command = "capability vrf-lite" + return command + + +def _tmplt_ospf_compatible(config_data): + if "compatible" in config_data: + if "rfc1583" in config_data["compatible"]: + command = "compatible rfc1583" + elif "rfc1587" in config_data["compatible"]: + command = "compatible rfc1587" + elif "rfc5243" in config_data["compatible"]: + command = "compatible rfc5243" + return command + + +def _tmplt_ospf_default_information(config_data): + if "default_information" in config_data: + command = "default-information" + if "originate" in config_data["default_information"]: + command += " originate" + if "always" in config_data["default_information"]: + command += " always" + if "metric" in config_data["default_information"]: + command += " metric {metric}".format(**config_data["default_information"]) + if "metric_type" in config_data["default_information"]: + command += " metric-type {metric_type}".format(**config_data["default_information"]) + if "metric" in config_data["default_information"]: + command += " route-map {route_map}".format(**config_data["default_information"]) + return command + + +def _tmplt_ospf_discard_route(config_data): + if "discard_route" in config_data: + command = "discard-route" + if "external" in config_data["discard_route"]: + command += " external {external}".format(**config_data["discard_route"]) + if "internal" in config_data["discard_route"]: + command += " internal {internal}".format(**config_data["discard_route"]) + return command + + +def _tmplt_ospf_distance_admin_distance(config_data): + if "admin_distance" in config_data["distance"]: + command = "distance {distance}".format(**config_data["distance"]["admin_distance"]) + if "address" in config_data["distance"]["admin_distance"]: + command += " {address} {wildcard_bits}".format( + **config_data["distance"]["admin_distance"] + ) + if "acl" in config_data["distance"]["admin_distance"]: + command += " {acl}".format(**config_data["distance"]["admin_distance"]) + return command + + +def _tmplt_ospf_distance_ospf(config_data): + if "ospf" in config_data["distance"]: + command = "distance ospf" + if "inter_area" in config_data["distance"]["ospf"]: + command += " inter-area {inter_area}".format(**config_data["distance"]["ospf"]) + if config_data["distance"].get("ospf").get("intra_area"): + command += " intra-area {intra_area}".format(**config_data["distance"]["ospf"]) + if config_data["distance"].get("ospf").get("external"): + command += " external {external}".format(**config_data["distance"]["ospf"]) + return command + + +def _tmplt_ospf_distribute_list_acls(config_data): + if "acls" in config_data.get("distribute_list"): + command = [] + for k, v in iteritems(config_data["distribute_list"]["acls"]): + cmd = "distribute-list {name} {direction}".format(**v) + if "interface" in v: + cmd += " {interface}".format(**v) + if "protocol" in v: + cmd += " {protocol}".format(**v) + command.append(cmd) + return command + + +def _tmplt_ospf_distribute_list_prefix(config_data): + if "prefix" in config_data.get("distribute_list"): + command = "distribute-list prefix {name}".format(**config_data["distribute_list"]["prefix"]) + if "gateway_name" in config_data["distribute_list"]["prefix"]: + command += " gateway {gateway_name}".format(**config_data["distribute_list"]["prefix"]) + if "direction" in config_data["distribute_list"]["prefix"]: + command += " {direction}".format(**config_data["distribute_list"]["prefix"]) + if "interface" in config_data["distribute_list"]["prefix"]: + command += " {interface}".format(**config_data["distribute_list"]["prefix"]) + if "protocol" in config_data["distribute_list"]["prefix"]: + command += " {protocol}".format(**config_data["distribute_list"]["prefix"]) + return command + + +def _tmplt_ospf_domain_id(config_data): + if "domain_id" in config_data: + command = "domain-id" + if "ip_address" in config_data["domain_id"]: + if "address" in config_data["domain_id"]["ip_address"]: + command += " {address}".format(**config_data["domain_id"]["ip_address"]) + if "secondary" in config_data["domain_id"]["ip_address"]: + command += " {secondary}".format(**config_data["domain_id"]["ip_address"]) + elif "null" in config_data["domain_id"]: + command += " null" + return command + + +def _tmplt_ospf_event_log(config_data): + if "event_log" in config_data: + command = "event-log" + if "one_shot" in config_data["event_log"]: + command += " one-shot" + if "pause" in config_data["event_log"]: + command += " pause" + if "size" in config_data["event_log"]: + command += " size {size}".format(**config_data["event_log"]) + return command + + +def _tmplt_ospf_manet(config_data): + if "manet" in config_data: + command = [] + if "cache" in config_data["manet"]: + cmd = "manet cache" + if "acknowledgement" in config_data["manet"]["cache"]: + cmd += " acknowledgement {acknowledgement}".format(**config_data["manet"]["cache"]) + elif "redundancy" in config_data["manet"]["cache"]: + cmd += " redundancy {redundancy}".format(**config_data["manet"]["cache"]) + command.append(cmd) + if "hello" in config_data["manet"] and config_data["manet"]["hello"]: + command.append("manet hello") + if "peering" in config_data["manet"]: + cmd = "manet peering selective" + if "per_interface" in config_data["manet"]["peering"]: + cmd += " per-interface" + if "redundancy" in config_data["manet"]["peering"]: + cmd += " redundancy {redundancy}".format(**config_data["manet"]["peering"]) + command.append(cmd) + if "willingness" in config_data["manet"]: + command.append("manet willingness".format(**config_data["manet"]["willingness"])) + return command + + +def _tmplt_ospf_limit(config_data): + if "limit" in config_data: + command = "limit retransmissions" + if "dc" in config_data["limit"]: + if "number" in config_data["limit"]["dc"]: + command += " dc {number}".format(**config_data["limit"]["dc"]) + if "disable" in config_data["limit"]["dc"]: + command += " dc disable" + if "non_dc" in config_data["limit"]: + if "number" in config_data["limit"]["non_dc"]: + command += " non-dc {number}".format(**config_data["limit"]["non_dc"]) + if "disable" in config_data["limit"]["dc"]: + command += " non-dc disable" + return command + + +def _tmplt_ospf_vrf_local_rib_criteria(config_data): + if "local_rib_criteria" in config_data: + command = "local-rib-criteria" + if "forwarding_address" in config_data["local_rib_criteria"]: + command += " forwarding-address" + if "inter_area_summary" in config_data["local_rib_criteria"]: + command += " inter-area-summary" + if "nssa_translation" in config_data["local_rib_criteria"]: + command += " nssa-translation" + return command + + +def _tmplt_ospf_log_adjacency_changes(config_data): + if "log_adjacency_changes" in config_data: + command = "log-adjacency-changes" + if "detail" in config_data["log_adjacency_changes"]: + command += " detail" + return command + + +def _tmplt_ospf_max_lsa(config_data): + if "max_lsa" in config_data: + command = "max-lsa {number}".format(**config_data["max_lsa"]) + if "threshold_value" in config_data["max_lsa"]: + command += " {threshold_value}".format(**config_data["max_lsa"]) + if "ignore_count" in config_data["max_lsa"]: + command += " ignore-count {ignore_count}".format(**config_data["max_lsa"]) + if "ignore_time" in config_data["max_lsa"]: + command += " ignore-time {ignore_time}".format(**config_data["max_lsa"]) + if "reset_time" in config_data["max_lsa"]: + command += " reset-time {reset_time}".format(**config_data["max_lsa"]) + if "warning_only" in config_data["max_lsa"]: + command += " warning-only" + return command + + +def _tmplt_ospf_max_metric(config_data): + if "max_metric" in config_data: + command = "max-metric" + if "router_lsa" in config_data["max_metric"]: + command += " router-lsa" + if "external_lsa" in config_data["max_metric"]: + command += " external-lsa {external_lsa}".format(**config_data["max_metric"]) + if "include_stub" in config_data["max_metric"]: + command += " include-stub" + if "on_startup" in config_data["max_metric"]: + if "time" in config_data["max_metric"]["on_startup"]: + command += " on-startup {time}".format(**config_data["max_metric"]["on_startup"]) + elif "wait_for_bgp" in config_data["max_metric"]["on_startup"]: + command += " on-startup wait-for-bgp" + if "summary_lsa" in config_data["max_metric"]: + command += " summary-lsa {summary_lsa}".format(**config_data["max_metric"]) + return command + + +def _tmplt_ospf_mpls_ldp(config_data): + if "ldp" in config_data["mpls"]: + command = "mpls ldp" + if "autoconfig" in config_data["mpls"]["ldp"]: + command += " autoconfig" + if "area" in config_data["mpls"]["ldp"]["autoconfig"]: + command += " area {area}".format(**config_data["mpls"]["ldp"]["autoconfig"]) + elif "sync" in config_data["mpls"]["ldp"]: + command += " sync" + return command + + +def _tmplt_ospf_mpls_traffic_eng(config_data): + if "traffic_eng" in config_data["mpls"]: + command = "mpls traffic-eng" + if "area" in config_data["mpls"]["traffic_eng"]: + command += " area {area}".format(**config_data["mpls"]["traffic_eng"]) + elif "autoroute_exclude" in config_data["mpls"]["traffic_eng"]: + command += " autoroute-exclude prefix-list {autoroute_exclude}".format( + **config_data["mpls"]["traffic_eng"] + ) + elif "interface" in config_data["mpls"]["traffic_eng"]: + command += " interface {int_type}".format( + **config_data["mpls"]["traffic_eng"]["interface"] + ) + if "area" in config_data["mpls"]["traffic_eng"]["interface"]: + command += " area {area}".format(**config_data["mpls"]["traffic_eng"]["interface"]) + elif "mesh_group" in config_data["mpls"]["traffic_eng"]: + command += " mesh-group {id} {interface}".format( + **config_data["mpls"]["traffic_eng"]["mesh_group"] + ) + if "area" in config_data["mpls"]["traffic_eng"]["mesh_group"]: + command += " area {area}".format(**config_data["mpls"]["traffic_eng"]["mesh_group"]) + elif "multicast_intact" in config_data["mpls"]["traffic_eng"]: + command += " multicast-intact" + elif "router_id_interface" in config_data["mpls"]["traffic_eng"]: + command += " router-id {router_id_interface}".format( + **config_data["mpls"]["traffic_eng"] + ) + return command + + +def _tmplt_ospf_neighbor(config_data): + if "neighbor" in config_data: + command = "neighbor" + if "address" in config_data["neighbor"]: + command += " {address}".format(**config_data["neighbor"]) + if "cost" in config_data["neighbor"]: + command += " cost {cost}".format(**config_data["neighbor"]) + if "database_filter" in config_data["neighbor"]: + command += " database-filter all out" + if "poll_interval" in config_data["neighbor"]: + command += " poll-interval {poll_interval}".format(**config_data["neighbor"]) + if "priority" in config_data["neighbor"]: + command += " priority {priority}".format(**config_data["neighbor"]) + return command + + +def _tmplt_ospf_network(config_data): + if "network" in config_data: + command = "network" + if "address" in config_data["network"]: + command += " {address} {wildcard_bits}".format(**config_data["network"]) + if "area" in config_data["network"]: + command += " area {area}".format(**config_data["network"]) + return command + + +def _tmplt_ospf_nsf_cisco(config_data): + if "cisco" in config_data["nsf"]: + command = "nsf cisco helper" + if "disable" in config_data["nsf"]["cisco"]: + command += " disable" + return command + + +def _tmplt_ospf_nsf_ietf(config_data): + if "ietf" in config_data["nsf"]: + command = "nsf ietf helper" + if "disable" in config_data["nsf"]["ietf"]: + command += " disable" + elif "strict_lsa_checking" in config_data["nsf"]["ietf"]: + command += " strict-lsa-checking" + return command + + +def _tmplt_ospf_queue_depth_hello(config_data): + if "hello" in config_data["queue_depth"]: + command = "queue-depth hello" + if "max_packets" in config_data["queue_depth"]["hello"]: + command += " {max_packets}".format(**config_data["queue_depth"]["hello"]) + elif "unlimited" in config_data["queue_depth"]["hello"]: + command += " unlimited" + return command + + +def _tmplt_ospf_queue_depth_update(config_data): + if "update" in config_data["queue_depth"]: + command = "queue-depth update" + if "max_packets" in config_data["queue_depth"]["update"]: + command += " {max_packets}".format(**config_data["queue_depth"]["update"]) + elif "unlimited" in config_data["queue_depth"]["update"]: + command += " unlimited" + return command + + +def _tmplt_ospf_summary_prefix(config_data): + if "summary_prefix" in config_data: + command = "summary-prefix {address} {mask}".format(**config_data["summary_prefix"]) + if "not_advertise" in config_data["summary_prefix"]: + command += " not-advertise" + elif "nssa_only" in config_data["summary_prefix"]: + command += " nssa-only" + if "tag" in config_data["summary_prefix"]: + command += " tag {tag}".format(**config_data["summary_prefix"]) + return command + + +def _tmplt_ospf_timers_pacing(config_data): + if "pacing" in config_data["timers"]: + command = "timers pacing" + if "flood" in config_data["timers"]["pacing"]: + command += " flood {flood}".format(**config_data["timers"]["pacing"]) + elif "lsa_group" in config_data["timers"]["pacing"]: + command += " lsa-group {lsa_group}".format(**config_data["timers"]["pacing"]) + elif "retransmission" in config_data["timers"]["pacing"]: + command += " retransmission {retransmission}".format(**config_data["timers"]["pacing"]) + return command + + +def _tmplt_ospf_ttl_security(config_data): + if "ttl_security" in config_data: + command = "ttl-security all-interfaces" + if "hops" in config_data["ttl_security"]: + command += " hops {hops}".format(**config_data["ttl_security"]) + return command + + +class Ospfv3Template(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Ospfv3Template, self).__init__(lines=lines, tmplt=self, module=module) + + PARSERS = [ + { + "name": "pid", + "getval": re.compile( + r""" + ^router\s + ospfv3* + \s(?P<pid>\S+) + $""", + re.VERBOSE, + ), + "setval": _tmplt_ospfv3_cmd, + "result": {"processes": {"{{ pid }}": {"process_id": "{{ pid|int }}"}}}, + "shared": True, + }, + { + "name": "adjacency", + "getval": re.compile( + r"""\s+adjacency + \sstagger* + \s*((?P<min>\d+)|(?P<none_adj>none))* + \s*(?P<max>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_adjacency_cmd, + "result": { + "processes": { + "{{ pid }}": { + "adjacency": { + "min_adjacency": "{{ min|int }}", + "max_adjacency": "{{ max|int }}", + "none": "{{ True if none_adj is defined else None }}", + }, + }, + }, + }, + }, + { + "name": "area.authentication", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \s*(?P<auth>authentication)* + \s*(?P<md>message-digest) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_authentication, + "compval": "authentication", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "authentication": { + "enable": "{{ True if auth is defined and md is undefined }}", + "message_digest": "{{ not not md }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "area.capability", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \s*(?P<capability>capability)* + \s*(?P<df>default-exclusion) + *$""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} capability default-exclusion", + "compval": "capability", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "capability": "{{ not not capability }}", + }, + }, + }, + }, + }, + }, + { + "name": "area.default_cost", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \sdefault-cost* + \s*(?P<default_cost>\S+) + *$""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} default-cost {{ default_cost }}", + "compval": "default_cost", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "default_cost": "{{ default_cost|int }}", + }, + }, + }, + }, + }, + }, + { + "name": "area.filter_list", + "getval": re.compile( + r"""\s+area + \s*(?P<area_id>\S+)* + \s*filter-list\sprefix* + \s*(?P<name>\S+)* + \s*(?P<dir>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_filter, + "compval": "filter_list", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "filter_list": [{"name": "{{ name }}", "direction": "{{ dir }}"}], + }, + }, + }, + }, + }, + }, + { + "name": "area.nssa", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \s(?P<nssa>nssa)* + \s*(?P<no_redis>no-redistribution)* + \s*(?P<def_origin>default-information-originate)* + \s*(?P<metric>metric\s\d+)* + \s*(?P<metric_type>metric-type\s\d+)* + \s*(?P<no_summary>no-summary)* + \s*(?P<no_ext>no-ext-capability)*$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa, + "compval": "nssa", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "nssa": { + "set": "{{ True if nssa is defined and def_origin is undefined and " + "no_ext is undefined and no_redis is undefined and nssa_only is undefined }}", + "default_information_originate": { + "set": "{{ True if def_origin is defined and metric is undefined and " + "metric_type is undefined and nssa_only is undefined }}", + "metric": "{{ metric.split(" ")[1]|int }}", + "metric_type": "{{ metric_type.split(" ")[1]|int }}", + "nssa_only": "{{ True if nssa_only is defined }}", + }, + "no_ext_capability": "{{ True if no_ext is defined }}", + "no_redistribution": "{{ True if no_redis is defined }}", + "no_summary": "{{ True if no_summary is defined }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "area.nssa.translate", + "getval": re.compile( + r"""\s+area* + \s*(?P<area_id>\S+)* + \s*(?P<nssa>nssa)* + \stranslate\stype7* + \s(?P<translate_always>always)* + \s* (?P<translate_supress>suppress-fa) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa_translate, + "compval": "nssa.translate", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "nssa": { + "translate": "{{ translate_always if translate_always is defined else translate_supress if translate_supress is defined }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "area.ranges", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \srange + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + \s(?P<netmask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*((?P<advertise>advertise)|(?P<not_advertise>not-advertise))* + \s*(?P<cost>cost\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_ranges, + "compval": "ranges", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "ranges": [ + { + "address": "{{ address }}", + "netmask": "{{ netmask }}", + "advertise": "{{ True if advertise is defined }}", + "cost": "{{ cost.split(" ")[1]|int }}", + "not_advertise": "{{ True if not_advertise is defined }}", + }, + ], + }, + }, + }, + }, + }, + }, + { + "name": "area.sham_link", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \ssham-link + \s(?P<source>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + \s(?P<destination>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<cost>cost\s\d+)* + \s*(?P<ttl_security>ttl-security\shops\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_sham_link, + "compval": "sham_link", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "sham_link": { + "source": "{{ source }}", + "destination": "{{ destination }}", + "cost": "{{ cost.split(" ")[1]|int }}", + "ttl_security": '{{ ttl_security.split("hops ")[1] }}', + }, + }, + }, + }, + }, + }, + }, + { + "name": "area.stub", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \s(?P<stub>stub)* + \s*(?P<no_ext>no-ext-capability)* + \s*(?P<no_sum>no-summary) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_stub_link, + "compval": "stub", + "result": { + "processes": { + "{{ pid }}": { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "stub": { + "set": "{{ True if stub is defined and no_ext is undefined and no_sum is undefined }}", + "no_ext_capability": "{{ True if no_ext is defined }}", + "no_summary": "{{ True if no_sum is defined }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "auto_cost", + "getval": re.compile( + r"""\s+(?P<auto_cost>auto-cost)* + \s*(?P<ref_band>reference-bandwidth\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_auto_cost, + "result": { + "processes": { + "{{ pid }}": { + "auto_cost": { + "set": "{{ True if auto_cost is defined and ref_band is undefined }}", + "reference_bandwidth": '{{ ref_band.split(" ")[1] }}', + }, + }, + }, + }, + }, + { + "name": "bfd", + "getval": re.compile( + r"""\s+bfd* + \s*(?P<bfd>all-interfaces) + *$""", + re.VERBOSE, + ), + "setval": "bfd all-interfaces", + "result": {"processes": {"{{ pid }}": {"bfd": "{{ True if bfd is defined }}"}}}, + }, + { + "name": "capability", + "getval": re.compile( + r"""\s+capability* + \s*((?P<lls>lls)|(?P<opaque>opaque)|(?P<transit>transit)|(?P<vrf_lite>vrf-lite)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_capability, + "result": { + "processes": { + "{{ pid }}": { + "capability": { + "lls": "{{ True if lls is defined }}", + "opaque": "{{ True if opaque is defined }}", + "transit": "{{ True if transit is defined }}", + "vrf_lite": "{{ True if vrf_lite is defined }}", + }, + }, + }, + }, + }, + { + "name": "compatible", + "getval": re.compile( + r"""\s+compatible* + \s*((?P<rfc1583>rfc1583)|(?P<rfc1587>rfc1587)|(?P<rfc5243>rfc5243)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_compatible, + "result": { + "processes": { + "{{ pid }}": { + "compatible": { + "rfc1583": "{{ True if rfc1583 is defined }}", + "rfc1587": "{{ True if rfc1587 is defined }}", + "rfc5243": "{{ True if rfc5243 is defined }}", + }, + }, + }, + }, + }, + { + "name": "default_information", + "getval": re.compile( + r"""\s+default-information* + \s*(?P<originate>originate)* + \s*(?P<always>always)* + \s*(?P<metric>metric\s\d+)* + \s*(?P<metric_type>metric-type\s\d+)* + \s*(?P<route_map>route-map\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_default_information, + "result": { + "processes": { + "{{ pid }}": { + "default_information": { + "originate": "{{ True if originate is defined }}", + "always": "{{ True if always is defined }}", + "metric": "{{ metric.split(" ")[1]|int }}", + "metric_type": "{{ metric_type.split(" ")[1]|int }}", + "route_map": "{{ route_map.split(" ")[1] }}", + }, + }, + }, + }, + }, + { + "name": "default_metric", + "getval": re.compile( + r"""\s+default-metric(?P<default_metric>\s\d+) + *$""", + re.VERBOSE, + ), + "setval": "default-metric {{ default_metric }}", + "result": {"processes": {"{{ pid }}": {"default_metric": "{{ default_metric| int}}"}}}, + }, + { + "name": "discard_route", + "getval": re.compile( + r"""\s+(?P<discard_route>discard-route)* + \s*(?P<external>external\s\d+)* + \s*(?P<internal>internal\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_discard_route, + "result": { + "processes": { + "{{ pid }}": { + "discard_route": { + "set": "{{ True if discard_route is defined and external is undefined and internal is undefined }}", + "external": "{{ external.split(" ")[1]|int }}", + "internal": "{{ internal.split(" ")[1]|int }}", + }, + }, + }, + }, + }, + { + "name": "distance.admin_distance", + "getval": re.compile( + r"""\s+distance + \s(?P<admin_dist>\S+)* + \s*(?P<source>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<wildcard>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<acl>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_admin_distance, + "compval": "admin_distance", + "result": { + "processes": { + "{{ pid }}": { + "distance": { + "admin_distance": { + "distance": "{{ admin_dist }}", + "address": "{{ source }}", + "wildcard_bits": "{{ wildcard }}", + "acl": "{{ acl }}", + }, + }, + }, + }, + }, + }, + { + "name": "distance.ospf", + "getval": re.compile( + r"""\s+distance + \sospf* + \s*(?P<intra>intra-area\s\d+)* + \s*(?P<inter>inter-area\s\d+)* + \s*(?P<external>external\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_ospf, + "compval": "ospf", + "result": { + "processes": { + "{{ pid }}": { + "distance": { + "ospf": { + "inter_area": "{{ inter.split(" ")[1]|int }}", + "intra_area": "{{ intra.split(" ")[1]|int }}", + "external": "{{ external.split(" ")[1]|int }}", + }, + }, + }, + }, + }, + }, + { + "name": "distribute_list.acls", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<name>\S+)* + \s*(?P<dir>\S+)* + \s*(?P<int_pro>\S+\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distribute_list_acls, + "compval": "distribute_list.acls", + "result": { + "processes": { + "{{ pid }}": { + "distribute_list": { + "acls": [ + { + "name": "{{ name }}", + "direction": "{{ dir }}", + "interface": '{{ int_pro if dir == "in" }}', + "protocol": '{{ int_pro if dir == "out" }}', + }, + ], + }, + }, + }, + }, + }, + { + "name": "distribute_list.prefix", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<prefix>prefix\s\S+)* + \s*(?P<gateway>gateway\s\S+)* + \s*(?P<dir>\S+)* + \s*(?P<int_pro>\S+\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distribute_list_prefix, + "compval": "distribute_list.prefix", + "result": { + "processes": { + "{{ pid }}": { + "distribute_list": { + "prefix": { + "name": "{{ prefix.split(" ")[1] }}", + "gateway_name": "{{ gateway.split(" ")[1] if prefix is defined }}", + "direction": "{{ dir if gateway is undefined }}", + "interface": '{{ int_pro if dir == "in" }}', + "protocol": '{{ int_pro if dir == "out" }}', + }, + }, + }, + }, + }, + }, + { + "name": "distribute_list.route_map", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<route_map>route-map\s\S+)* + \s*(?P<dir>\S+) + *$""", + re.VERBOSE, + ), + "setval": "distribute-list route-map {{ distribute_list.route_map.name }} in", + "compval": "distribute_list.route_map", + "result": { + "processes": { + "{{ pid }}": { + "distribute_list": {"route_map": {"name": "{{ route_map.split(" ")[1] }}"}}, + }, + }, + }, + }, + { + "name": "domain_id", + "getval": re.compile( + r"""\s+domain-id + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<secondary>secondary)* + \s*(?P<null>null) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_domain_id, + "result": { + "processes": { + "{{ pid }}": { + "domain_id": { + "ip_address": { + "address": "{{ address }}", + "secondary": "{{ True if secondary is defined }}", + }, + "null": "{{ True if null is defined }}", + }, + }, + }, + }, + }, + { + "name": "domain_tag", + "getval": re.compile( + r"""\s+domain-tag + \s(?P<tag>\d+) + *$""", + re.VERBOSE, + ), + "setval": "domain-tag {{ domain_tag }}", + "result": {"processes": {"{{ pid }}": {"domain_tag": "{{ tag|int }}"}}}, + }, + { + "name": "event_log", + "getval": re.compile( + r"""\s+(?P<event_log>event-log)* + \s*(?P<one_shot>one-shot)* + \s*(?P<pause>pause)* + \s*(?P<size>size\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_event_log, + "result": { + "processes": { + "{{ pid }}": { + "event_log": { + "enable": "{{ True if event_log is defined and one_shot is undefined and pause is undefined and size is undefined }}", + "one_shot": "{{ True if one_shot is defined }}", + "pause": "{{ True if pause is defined }}", + "size": "{{ size.split(" ")[1]|int }}", + }, + }, + }, + }, + }, + { + "name": "help", + "getval": re.compile( + r"""\s+(?P<help>help) + *$""", + re.VERBOSE, + ), + "setval": "help", + "result": {"processes": {"{{ pid }}": {"help": "{{ True if help is defined }}"}}}, + }, + { + "name": "ignore", + "getval": re.compile( + r"""\s+(?P<ignore>ignore) + *$""", + re.VERBOSE, + ), + "setval": "ignore lsa mospf", + "result": {"processes": {"{{ pid }}": {"ignore": "{{ True if ignore is defined }}"}}}, + }, + { + "name": "interface_id", + "getval": re.compile( + r"""\s+(?P<interface_id>interface-id\ssnmp-if-index) + *$""", + re.VERBOSE, + ), + "setval": "interface-id snmp-if-index", + "result": { + "processes": { + "{{ pid }}": {"interface_id": "{{ True if interface_id is defined }}"}, + }, + }, + }, + { + "name": "ispf", + "getval": re.compile( + r"""\s+(?P<ispf>ispf) + *$""", + re.VERBOSE, + ), + "setval": "ispf", + "result": {"processes": {"{{ pid }}": {"ispf": "{{ True if ispf is defined }}"}}}, + }, + { + "name": "limit", + "getval": re.compile( + r"""\s+limit\sretransmissions + \s((?P<dc_num>dc\s\d+)|(?P<dc_disable>dc\sdisable))* + \s*((?P<non_dc_num>non-dc\s\d+)|(?P<non_dc_disable>non-dc\sdisable)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_limit, + "result": { + "processes": { + "{{ pid }}": { + "limit": { + "dc": { + "number": "{{ dc_num.split(" ")[1]|int }}", + "disable": "{{ True if dc_disable is defined }}", + }, + "non_dc": { + "number": "{{ non_dc_num.split(" ")[1]|int }}", + "disable": "{{ True if dc_disable is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "local_rib_criteria", + "getval": re.compile( + r"""\s+(?P<local>local-rib-criteria)* + \s*(?P<forward>forwarding-address)* + \s*(?P<inter>inter-area-summary)* + \s*(?P<nssa>nssa-translation) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_vrf_local_rib_criteria, + "result": { + "processes": { + "{{ pid }}": { + "local_rib_criteria": { + "enable": "{{ True if local is defined and forward is undefined and inter is undefined and nssa is undefined }}", + "forwarding_address": "{{ True if forward is defined }}", + "inter_area_summary": "{{ True if inter is defined }}", + "nssa_translation": "{{ True if nssa is defined }}", + }, + }, + }, + }, + }, + { + "name": "log_adjacency_changes", + "getval": re.compile( + r"""\s+(?P<log>log-adjacency-changes)* + \s*(?P<detail>detail) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_log_adjacency_changes, + "result": { + "processes": { + "{{ pid }}": { + "log_adjacency_changes": { + "set": "{{ True if log is defined and detail is undefined }}", + "detail": "{{ True if detail is defined }}", + }, + }, + }, + }, + }, + { + "name": "manet", + "getval": re.compile( + r"""\s+manet* + \s*(?P<cache>cache)* + \s*(?P<acknowledgement>acknowledgement\s\d+)* + \s*(?P<update>update\s\d+)* + \s*(?P<hello>hello)* + \s*(?P<unicast>unicast)* + \s*(?P<multicast>multicast)* + \s*(?P<peering>peering\sselective)* + \s*(?P<disable>disable)* + \s*(?P<per_interface>per-interface)* + \s*(?P<redundancy>redundancy\s\d+)* + \s*(?P<willingness>willingness\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_manet, + "compval": "manet", + "result": { + "processes": { + "{{ pid }}": { + "manet": { + "cache": { + "acknowledgement": "{{ acknowledgement.split(" ")[1] }}", + "update": "{{ update.split(" ")[1] }}", + }, + "hello": { + "unicast": "{{ True if unicast is defined }}", + "multicast": "{{ True if multicast is defined }}", + }, + "peering": { + "set": "{{ True if peering is defined }}", + "disable": "{{ True if disable is defined }}", + "per_interface": "{{ True if per_interface is defined }}", + "redundancy": "{{ redundancy.split(" ")[1] }}", + }, + "willingness": "{{ willingness.split(" ")[1] }}", + }, + }, + }, + }, + }, + { + "name": "max_lsa", + "getval": re.compile( + r"""\s+max-lsa + \s(?P<number>\d+)* + \s*(?P<threshold>\d+)* + \s*(?P<ignore_count>ignore-count\s\d+)* + \s*(?P<ignore_time>ignore-time\s\d+)* + \s*(?P<reset_time>reset-time\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_lsa, + "result": { + "processes": { + "{{ pid }}": { + "max_lsa": { + "number": "{{ number }}", + "threshold_value": "{{ threshold }}", + "ignore_count": "{{ ignore_count.split(" ")[1] }}", + "ignore_time": "{{ ignore_time.split(" ")[1] }}", + "reset_time": "{{ reset_time.split(" ")[1] }}", + "warning_only": "{{ True if warning is defined }}", + }, + }, + }, + }, + }, + { + "name": "max_metric", + "getval": re.compile( + r"""\s+max-metric* + \s*(?P<router_lsa>router-lsa)* + \s*(?P<include_stub>include-stub)* + \s*(?P<external_lsa>external-lsa\s\d+)* + \s*(?P<startup_time>on-startup\s\d+)* + \s*(?P<startup_wait>on-startup\s\S+)* + \s*(?P<summary_lsa>summary-lsa\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_metric, + "result": { + "processes": { + "{{ pid }}": { + "max_metric": { + "router_lsa": "{{ True if router_lsa is defined }}", + "external_lsa": "{{ external_lsa.split(" ")[1] }}", + "include_stub": "{{ ignore_count.split(" ")[1] }}", + "on_startup": { + "time": "{{ startup_time.split(" ")[1] }}", + "wait_for_bgp": "{{ True if startup_wait is defined }}", + }, + "summary_lsa": "{{ summary_lsa.split(" ")[1] }}", + }, + }, + }, + }, + }, + { + "name": "maximum_paths", + "getval": re.compile( + r"""\s+maximum-paths* + \s+(?P<paths>\d+) + *$""", + re.VERBOSE, + ), + "setval": "maximum-paths {{ maximum_paths }}", + "result": {"processes": {"{{ pid }}": {"maximum_paths": "{{ paths }}"}}}, + }, + { + "name": "mpls.ldp", + "getval": re.compile( + r"""\s+mpls + \sldp* + \s*(?P<autoconfig>autoconfig*\s*(?P<area>area\s\S+))* + \s*(?P<sync>sync) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_mpls_ldp, + "compval": "ldp", + "result": { + "processes": { + "{{ pid }}": { + "mpls": { + "ldp": { + "autoconfig": { + "set": "{{ True if autoconfig is defined and area is undefined }}", + "area": "{{ area.split(" ")[1] }}", + }, + "sync": "{{ True if sync is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "mpls.traffic_eng", + "getval": re.compile( + r"""\s+mpls + \straffic-eng* + \s*(?P<area>area\s\S+)* + \s*(?P<autoroute>autoroute-exclude\s\S+\s\S+)* + \s*(?P<interface>interface\s(?P<int_type>\S+\s\S+)\s(?P<int_area>area\s\S+))* + \s*(?P<mesh>mesh-group\s\d+\s(?P<mesh_int>\S+\s\S+)\s(?P<mesh_area>area\s\d+))* + \s*(?P<multicast>multicast-intact)* + \s*(?P<router>router-id\s(?P<router_int>\S+\s\S+)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_mpls_traffic_eng, + "compval": "traffic_eng", + "result": { + "processes": { + "{{ pid }}": { + "mpls": { + "traffic_eng": { + "area": "{{ area.split(" ")[1] }}", + "autoroute_exclude": "{{ autoroute.split(" ")[2] }}", + "interface": { + "interface_type": "{{ int_type }}", + "area": "{{ int_area.split(" ")[1] }}", + }, + "mesh_group": { + "id": "{{ mesh.split(" ")[1] }}", + "interface": "{{ mest_int }}", + "area": "{{ mesh_area.split(" ")[1] }}", + }, + "multicast_intact": "{{ True if multicast is defined }}", + "router_id_interface": "{{ router_int }}", + }, + }, + }, + }, + }, + }, + { + "name": "neighbor", + "getval": re.compile( + r"""\s+neighbor + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<cost>cost\s\d+)* + \s*(?P<db_filter>database-filter\sall\sout)* + \s*(?P<poll>poll-interval\s\d+)* + \s*(?P<priority>priority\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_neighbor, + "result": { + "processes": { + "{{ pid }}": { + "neighbor": { + "address": "{{ address }}", + "cost": "{{ cost.split(" ")[1] }}", + "database_filter": "{{ True if db_filter is defined }}", + "poll_interval": "{{ poll.split(" ")[1] }}", + "priority": "{{ priority.split(" ")[1] }}", + }, + }, + }, + }, + }, + { + "name": "network", + "getval": re.compile( + r"""\s+network + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<wildcard>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<area>area\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_network, + "result": { + "processes": { + "{{ pid }}": { + "network": { + "address": "{{ address }}", + "wildcard_bits": "{{ wildcard }}", + "area": "{{ area.split(" ")[1] }}", + }, + }, + }, + }, + }, + { + "name": "nsf.cisco", + "getval": re.compile( + r"""\s+nsf + \s(?P<cisco>cisco)* + \s*(?P<helper>helper)* + \s*(?P<disable>disable) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_nsf_cisco, + "compval": "cisco", + "result": { + "processes": { + "{{ pid }}": { + "nsf": { + "cisco": { + "helper": "{{ True if helper is defined }}", + "disable": "{{ True if disable is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "nsf.ietf", + "getval": re.compile( + r"""\s+nsf + \s(?P<ietf>ietf)* + \s*(?P<helper>helper)* + \s*(?P<disable>disable)* + \s*(?P<strict>strict-lsa-checking) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_nsf_ietf, + "compval": "ietf", + "result": { + "processes": { + "{{ pid }}": { + "nsf": { + "ietf": { + "helper": "{{ True if helper is defined }}", + "disable": "{{ True if disable is defined }}", + "strict_lsa_checking": "{{ True if strict is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "passive_interface", + "getval": re.compile( + r"""\s+passive-interface + \s(?P<interface>\S+\s\S+) + *$""", + re.VERBOSE, + ), + "setval": "passive-interface {{ passive_interface }}", + "result": {"processes": {"{{ pid }}": {"passive_interface": "{{ interface }}"}}}, + }, + { + "name": "prefix_suppression", + "getval": re.compile( + r"""\s+(?P<prefix_sup>prefix-suppression) + *$""", + re.VERBOSE, + ), + "setval": "prefix-suppression", + "result": { + "processes": { + "{{ pid }}": {"prefix_suppression": "{{ True if prefix_sup is defined }}"}, + }, + }, + }, + { + "name": "priority", + "getval": re.compile( + r"""\s+priority + \s(?P<priority>\d+) + *$""", + re.VERBOSE, + ), + "setval": "priority {{ priority }}", + "result": {"processes": {"{{ pid }}": {"priority": "{{ priority }}"}}}, + }, + { + "name": "queue_depth.hello", + "getval": re.compile( + r"""\s+queue-depth + \shello* + \s*(?P<max_packets>\d+)* + \s*(?P<unlimited>unlimited) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_queue_depth_hello, + "compval": "hello", + "result": { + "processes": { + "{{ pid }}": { + "queue_depth": { + "hello": { + "max_packets": "{{ max_packets }}", + "unlimited": "{{ True if unlimited is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "queue_depth.update", + "getval": re.compile( + r"""\s+queue-depth + \supdate* + \s*(?P<max_packets>\d+)* + \s*(?P<unlimited>unlimited) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_queue_depth_update, + "compval": "update", + "result": { + "processes": { + "{{ pid }}": { + "queue_depth": { + "update": { + "max_packets": "{{ max_packets }}", + "unlimited": "{{ True if unlimited is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "router_id", + "getval": re.compile( + r"""\s+router-id + \s(?P<id>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + *$""", + re.VERBOSE, + ), + "setval": "router-id {{ router_id }}", + "result": {"processes": {"{{ pid }}": {"router_id": "{{ id }}"}}}, + }, + { + "name": "shutdown", + "getval": re.compile( + r"""\s+(?P<shutdown>shutdown) + *$""", + re.VERBOSE, + ), + "setval": "shutdown", + "result": { + "processes": {"{{ pid }}": {"shutdown": "{{ True if shutdown is defined }}"}}, + }, + }, + { + "name": "timers.lsa", + "getval": re.compile( + r"""\s+timers + \slsa + \sarrival + \s(?P<lsa>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers lsa arrival {{ timers.lsa }}", + "compval": "lsa", + "result": {"processes": {"{{ pid }}": {"timers": {"lsa": "{{ lsa }}"}}}}, + }, + { + "name": "timers.pacing", + "getval": re.compile( + r"""\s+timers + \spacing + \s(?P<flood>flood\s\d+)* + \s*(?P<lsa_group>lsa-group\s\d+)* + \s*(?P<retransmission>retransmission\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_timers_pacing, + "compval": "pacing", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "pacing": { + "flood": "{{ flood.split(" ")[1] }}", + "lsa_group": "{{ lsa_group.split(" ")[1] }}", + "retransmission": "{{ retransmission.split(" ")[1] }}", + }, + }, + }, + }, + }, + }, + { + "name": "timers.throttle.lsa", + "getval": re.compile( + r"""\s+timers + \sthrottle + \s*(?P<lsa>lsa)* + \s*(?P<first_delay>\d+)* + \s*(?P<min_delay>\d+)* + \s*(?P<max_delay>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers throttle lsa {{ throttle.lsa.first_delay }} {{ throttle.lsa.min_delay }} {{ throttle.lsa.max_delay }}", + "compval": "throttle.lsa", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "throttle": { + "lsa": { + "first_delay": "{{ first_delay }}", + "min_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "timers.throttle.spf", + "getval": re.compile( + r"""\s+timers + \sthrottle + \s*(?P<spf>spf)* + \s*(?P<first_delay>\d+)* + \s*(?P<min_delay>\d+)* + \s*(?P<max_delay>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers throttle spf {{ throttle.spf.receive_delay }} {{ throttle.spf.between_delay }} {{ throttle.spf.max_delay }}", + "compval": "throttle.spf", + "result": { + "processes": { + "{{ pid }}": { + "timers": { + "throttle": { + "spf": { + "receive_delay": "{{ first_delay }}", + "between_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + }, + }, + }, + }, + }, + }, + }, + { + "name": "traffic_share", + "getval": re.compile( + r"""\s+(?P<traffic>traffic-share\smin\sacross-interfaces) + *$""", + re.VERBOSE, + ), + "setval": "traffic-share min across-interfaces", + "result": { + "processes": {"{{ pid }}": {"traffic_share": "{{ True if traffic is defined }}"}}, + }, + }, + { + "name": "ttl_security", + "getval": re.compile( + r"""\s+ttl-security + \s(?P<interfaces>all-interfaces)* + \s*(?P<hops>hops\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_ttl_security, + "result": { + "processes": { + "{{ pid }}": { + "ttl_security": { + "set": "{{ True if interfaces is defined and hops is undefined }}", + "hops": "{{ hops.split(" ")[1] }}", + }, + }, + }, + }, + }, + { + "name": "address_family", + "getval": re.compile( + r"""\s+address-family* + \s*(?P<afi>\S+)* + \s*(?P<unicast>unicast)* + \s*(?P<vrf>vrf\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_address_family_cmd, + # "compval": "afi", + "result": { + "address_family": [ + { + "afi": "{{ afi }}", + "unicast": "{{ True if unicast is defined }}", + "vrf": "{{ vrf.split(" ")[1] }}", + }, + ], + }, + "shared": True, + }, + { + "name": "address_family.exit", + "getval": re.compile( + r"""\s+exit-address-family + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_address_family_cmd, + "result": { + "address_family": [ + { + "exit": { + "pid": "{{ id }}", + "afi": "{{ afi }}", + "unicast": "{{ True if unicast is defined }}", + "vrf": "{{ vrf.split(" ")[1] }}", + }, + }, + ], + }, + }, + { + "name": "address_family.adjacency", + "getval": re.compile( + r"""\s+adjacency + \sstagger* + \s*((?P<min>\d+)|(?P<none_adj>none))* + \s*(?P<max>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_adjacency_cmd, + "compval": "adjacency", + "result": { + "address_family": [ + { + "adjacency": { + "min_adjacency": "{{ min|int }}", + "max_adjacency": "{{ max|int }}", + "none": "{{ True if none_adj is defined else None }}", + }, + }, + ], + }, + }, + { + "name": "address_family.area.authentication", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \s*(?P<auth>authentication)* + \s*(?P<md>message-digest) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_authentication, + "compval": "authentication", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "authentication": { + "enable": "{{ True if auth is defined and md is undefined }}", + "message_digest": "{{ not not md }}", + }, + }, + }, + }, + ], + }, + }, + { + "name": "address_family.area.capability", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \s*(?P<capability>capability)* + \s*(?P<df>default-exclusion) + *$""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} capability default-exclusion", + "compval": "capability", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "capability": "{{ not not capability }}", + }, + }, + }, + ], + }, + }, + { + "name": "address_family.area.default_cost", + "getval": re.compile( + r"""\s+area + \s(?P<area_id>\S+)* + \sdefault-cost* + \s*(?P<default_cost>\S+) + *$""", + re.VERBOSE, + ), + "setval": "area {{ area_id }} default-cost {{ default_cost }}", + "compval": "default_cost", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "default_cost": "{{ default_cost|int }}", + }, + }, + }, + ], + }, + }, + { + "name": "address_family.area.filter_list", + "getval": re.compile( + r"""\s+area + \s*(?P<area_id>\S+)* + \s*filter-list\sprefix* + \s*(?P<name>\S+)* + \s*(?P<dir>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_filter, + "compval": "filter_list", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "filter_list": [{"name": "{{ name }}", "direction": "{{ dir }}"}], + }, + }, + }, + ], + }, + }, + { + "name": "address_family.area.nssa", + "getval": re.compile( + r"""\s+area* + \s*(?P<area_id>\S+)* + \s*(?P<nssa>nssa)* + \s*(?P<no_redis>no-redistribution)* + \s*(?P<def_origin>default-information-originate)* + \s*(?P<metric>metric\s\d+)* + \s*(?P<metric_type>metric-type\s\d+)* + \s*(?P<nssa_only>nssa-only)* + \s*(?P<no_summary>no-summary) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa, + "compval": "nssa", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "nssa": { + "set": "{{ True if nssa is defined and def_origin is undefined and " + "no_ext is undefined and no_redis is undefined and nssa_only is undefined }}", + "default_information_originate": { + "set": "{{ True if def_origin is defined and metric is undefined and " + "metric_type is undefined and nssa_only is undefined }}", + "metric": "{{ metric.split(" ")[1]|int }}", + "metric_type": "{{ metric_type.split(" ")[1]|int }}", + "nssa_only": "{{ True if nssa_only is defined }}", + }, + "no_ext_capability": "{{ True if no_ext is defined }}", + "no_redistribution": "{{ True if no_redis is defined }}", + "no_summary": "{{ True if no_summary is defined }}", + }, + }, + }, + }, + ], + }, + }, + { + "name": "address_family.area.nssa.translate", + "getval": re.compile( + r"""\s+area* + \s*(?P<area_id>\S+)* + \s*(?P<nssa>nssa)* + \stranslate\stype7* + \s(?P<translate_always>always)* + \s* (?P<translate_supress>suppress-fa) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_nssa_translate, + "compval": "nssa.translate", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "nssa": { + "translate": "{{ translate_always if translate_always is defined else translate_supress if translate_supress is defined }}", + }, + }, + }, + }, + ], + }, + }, + { + "name": "address_family.area.ranges", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \srange + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + \s(?P<netmask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*((?P<advertise>advertise)|(?P<not_advertise>not-advertise))* + \s*(?P<cost>cost\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_ranges, + "compval": "ranges", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "ranges": [ + { + "address": "{{ address }}", + "netmask": "{{ netmask }}", + "advertise": "{{ True if advertise is defined }}", + "cost": "{{ cost.split(" ")[1]|int }}", + "not_advertise": "{{ True if not_advertise is defined }}", + }, + ], + }, + }, + }, + ], + }, + }, + { + "name": "address_family.area.sham_link", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \ssham-link + \s(?P<source>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + \s(?P<destination>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<cost>cost\s\d+)* + \s*(?P<ttl_security>ttl-security\shops\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_sham_link, + "compval": "sham_link", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "sham_link": { + "source": "{{ source }}", + "destination": "{{ destination }}", + "cost": "{{ cost.split(" ")[1]|int }}", + "ttl_security": '{{ ttl_security.split("hops ")[1] }}', + }, + }, + }, + }, + ], + }, + }, + { + "name": "address_family.area.stub", + "getval": re.compile( + r"""\s+area\s(?P<area_id>\S+) + \s(?P<stub>stub)* + \s*(?P<no_ext>no-ext-capability)* + \s*(?P<no_sum>no-summary) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_area_stub_link, + "compval": "stub", + "result": { + "address_family": [ + { + "areas": { + "{{ area_id }}": { + "area_id": "{{ area_id }}", + "stub": { + "set": "{{ True if stub is defined and no_ext is undefined and no_sum is undefined }}", + "no_ext_capability": "{{ True if no_ext is defined }}", + "no_summary": "{{ True if no_sum is defined }}", + }, + }, + }, + }, + ], + }, + }, + { + "name": "address_family.auto_cost", + "getval": re.compile( + r"""\s+(?P<auto_cost>auto-cost)* + \s*(?P<ref_band>reference-bandwidth\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_auto_cost, + "compval": "auto_cost", + "result": { + "address_family": [ + { + "auto_cost": { + "set": "{{ True if auto_cost is defined and ref_band is undefined }}", + "reference_bandwidth": '{{ ref_band.split(" ")[1] }}', + }, + }, + ], + }, + }, + { + "name": "address_family.bfd", + "getval": re.compile( + r"""\s+bfd* + \s*(?P<bfd>all-interfaces) + *$""", + re.VERBOSE, + ), + "setval": "bfd all-interfaces", + "compval": "bfd", + "result": {"address_family": [{"bfd": "{{ True if bfd is defined }}"}]}, + }, + { + "name": "address_family.capability", + "getval": re.compile( + r"""\s+capability* + \s*((?P<lls>lls)|(?P<opaque>opaque)|(?P<transit>transit)|(?P<vrf_lite>vrf-lite)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_capability, + "compval": "capability", + "result": { + "address_family": [ + { + "capability": { + "lls": "{{ True if lls is defined }}", + "opaque": "{{ True if opaque is defined }}", + "transit": "{{ True if transit is defined }}", + "vrf_lite": "{{ True if vrf_lite is defined }}", + }, + }, + ], + }, + }, + { + "name": "address_family.compatible", + "getval": re.compile( + r"""\s+compatible* + \s*((?P<rfc1583>rfc1583)|(?P<rfc1587>rfc1587)|(?P<rfc5243>rfc5243)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_compatible, + "compval": "compatible", + "result": { + "address_family": [ + { + "compatible": { + "rfc1583": "{{ True if rfc1583 is defined }}", + "rfc1587": "{{ True if rfc1587 is defined }}", + "rfc5243": "{{ True if rfc5243 is defined }}", + }, + }, + ], + }, + }, + { + "name": "address_family.default_information", + "getval": re.compile( + r"""\s+default-information* + \s*(?P<originate>originate)* + \s*(?P<always>always)* + \s*(?P<metric>metric\s\d+)* + \s*(?P<metric_type>metric-type\s\d+)* + \s*(?P<route_map>route-map\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_default_information, + "compval": "default_information", + "result": { + "address_family": [ + { + "default_information": { + "originate": "{{ True if originate is defined }}", + "always": "{{ True if always is defined }}", + "metric": "{{ metric.split(" ")[1]|int }}", + "metric_type": "{{ metric_type.split(" ")[1]|int }}", + "route_map": "{{ route_map.split(" ")[1] }}", + }, + }, + ], + }, + }, + { + "name": "address_family.default_metric", + "getval": re.compile( + r"""\s+default-metric(?P<default_metric>\s\d+) + *$""", + re.VERBOSE, + ), + "setval": "default-metric {{ default_metric }}", + "compval": "default_metric", + "result": {"address_family": [{"default_metric": "{{ default_metric| int}}"}]}, + }, + { + "name": "address_family.discard_route", + "getval": re.compile( + r"""\s+(?P<discard_route>discard-route)* + \s*(?P<external>external\s\d+)* + \s*(?P<internal>internal\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_discard_route, + "compval": "discard_route", + "result": { + "address_family": [ + { + "discard_route": { + "set": "{{ True if discard_route is defined and external is undefined and internal is undefined }}", + "external": "{{ external.split(" ")[1]|int }}", + "internal": "{{ internal.split(" ")[1]|int }}", + }, + }, + ], + }, + }, + { + "name": "address_family.distance.admin_distance", + "getval": re.compile( + r"""\s+distance + \s(?P<admin_dist>\S+)* + \s*(?P<source>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<wildcard>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<acl>\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_admin_distance, + "compval": "admin_distance", + "result": { + "address_family": [ + { + "distance": { + "admin_distance": { + "distance": "{{ admin_dist }}", + "address": "{{ source }}", + "wildcard_bits": "{{ wildcard }}", + "acl": "{{ acl }}", + }, + }, + }, + ], + }, + }, + { + "name": "address_family.distance.ospf", + "getval": re.compile( + r"""\s+distance + \sospf* + \s*(?P<intra>intra-area\s\d+)* + \s*(?P<inter>inter-area\s\d+)* + \s*(?P<external>external\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distance_ospf, + "compval": "ospf", + "result": { + "address_family": [ + { + "distance": { + "ospf": { + "inter_area": "{{ inter.split(" ")[1]|int }}", + "intra_area": "{{ intra.split(" ")[1]|int }}", + "external": "{{ external.split(" ")[1]|int }}", + }, + }, + }, + ], + }, + }, + { + "name": "address_family.distribute_list.acls", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<name>\S+)* + \s*(?P<dir>\S+)* + \s*(?P<int_pro>\S+\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distribute_list_acls, + "compval": "distribute_list.acls", + "result": { + "address_family": [ + { + "distribute_list": { + "acls": [ + { + "name": "{{ name }}", + "direction": "{{ dir }}", + "interface": '{{ int_pro if dir == "in" }}', + "protocol": '{{ int_pro if dir == "out" }}', + }, + ], + }, + }, + ], + }, + }, + { + "name": "address_family.distribute_list.prefix", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<prefix>prefix\s\S+)* + \s*(?P<gateway>gateway\s\S+)* + \s*(?P<dir>\S+)* + \s*(?P<int_pro>\S+\s\S+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_distribute_list_prefix, + "compval": "distribute_list.prefix", + "result": { + "address_family": [ + { + "distribute_list": { + "prefix": { + "name": "{{ prefix.split(" ")[1] }}", + "gateway_name": "{{ gateway.split(" ")[1] if prefix is defined }}", + "direction": "{{ dir if gateway is undefined }}", + "interface": '{{ int_pro if dir == "in" }}', + "protocol": '{{ int_pro if dir == "out" }}', + }, + }, + }, + ], + }, + }, + { + "name": "address_family.distribute_list.route_map", + "getval": re.compile( + r"""\s+distribute-list + \s(?P<route_map>route-map\s\S+)* + \s*(?P<dir>\S+) + *$""", + re.VERBOSE, + ), + "setval": "distribute-list route-map {{ distribute_list.route_map.name }} in", + "compval": "distribute_list.route_map", + "result": { + "address_family": [ + {"distribute_list": {"route_map": {"name": "{{ route_map.split(" ")[1] }}"}}}, + ], + }, + }, + { + "name": "address_family.domain_id", + "getval": re.compile( + r"""\s+domain-id + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<secondary>secondary)* + \s*(?P<null>null) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_domain_id, + "compval": "domain_id", + "result": { + "address_family": [ + { + "domain_id": { + "ip_address": { + "address": "{{ address }}", + "secondary": "{{ True if secondary is defined }}", + }, + "null": "{{ True if null is defined }}", + }, + }, + ], + }, + }, + { + "name": "address_family.domain_tag", + "getval": re.compile( + r"""\s+domain-tag + \s(?P<tag>\d+) + *$""", + re.VERBOSE, + ), + "setval": "domain-tag {{ domain_tag }}", + "compval": "domain_tag", + "result": {"address_family": [{"domain_tag": "{{ tag|int }}"}]}, + }, + { + "name": "address_family.graceful_restart", + "getval": re.compile( + r"""\s+graceful-restart* + \s*(?P<enable>helper)* + \s*(?P<disable>disable)* + \s*(?P<lsa>strict-lsa-checking) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_address_family_graceful_restart, + "compval": "graceful_restart", + "result": { + "address_family": [ + { + "event_log": { + "enable": "{{ True if is enable defined }}", + "disable": "{{ True if disable is defined }}", + "strict_lsa_checking": "{{ True if lsa is defined }}", + }, + }, + ], + }, + }, + { + "name": "address_family.event_log", + "getval": re.compile( + r"""\s+(?P<event_log>event-log)* + \s*(?P<one_shot>one-shot)* + \s*(?P<pause>pause)* + \s*(?P<size>size\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_event_log, + "compval": "event_log", + "result": { + "address_family": [ + { + "event_log": { + "enable": "{{ True if event_log is defined and one_shot is undefined and pause is undefined and size is undefined }}", + "one_shot": "{{ True if one_shot is defined }}", + "pause": "{{ True if pause is defined }}", + "size": "{{ size.split(" ")[1]|int }}", + }, + }, + ], + }, + }, + { + "name": "address_family.help", + "getval": re.compile( + r"""\s+(?P<help>help) + *$""", + re.VERBOSE, + ), + "setval": "help", + "compval": "help", + "result": {"address_family": [{"help": "{{ True if help is defined }}"}]}, + }, + { + "name": "address_family.interface_id", + "getval": re.compile( + r"""\s+(?P<interface_id>interface-id\ssnmp-if-index) + *$""", + re.VERBOSE, + ), + "setval": "interface-id snmp-if-index", + "compval": "interface_id", + "result": { + "address_family": [{"interface_id": "{{ True if interface_id is defined }}"}], + }, + }, + { + "name": "address_family.limit", + "getval": re.compile( + r"""\s+limit\sretransmissions + \s((?P<dc_num>dc\s\d+)|(?P<dc_disable>dc\sdisable))* + \s*((?P<non_dc_num>non-dc\s\d+)|(?P<non_dc_disable>non-dc\sdisable)) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_limit, + "compval": "limit", + "result": { + "address_family": [ + { + "limit": { + "dc": { + "number": "{{ dc_num.split(" ")[1]|int }}", + "disable": "{{ True if dc_disable is defined }}", + }, + "non_dc": { + "number": "{{ non_dc_num.split(" ")[1]|int }}", + "disable": "{{ True if dc_disable is defined }}", + }, + }, + }, + ], + }, + }, + { + "name": "address_family.local_rib_criteria", + "getval": re.compile( + r"""\s+(?P<local>local-rib-criteria)* + \s*(?P<forward>forwarding-address)* + \s*(?P<inter>inter-area-summary)* + \s*(?P<nssa>nssa-translation) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_vrf_local_rib_criteria, + "compval": "local_rib_criteria", + "result": { + "address_family": [ + { + "local_rib_criteria": { + "enable": "{{ True if local is defined and forward is undefined and inter is undefined and nssa is undefined }}", + "forwarding_address": "{{ True if forward is defined }}", + "inter_area_summary": "{{ True if inter is defined }}", + "nssa_translation": "{{ True if nssa is defined }}", + }, + }, + ], + }, + }, + { + "name": "address_family.log_adjacency_changes", + "getval": re.compile( + r"""\s+(?P<log>log-adjacency-changes)* + \s*(?P<detail>detail) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_log_adjacency_changes, + "compval": "log_adjacency_changes", + "result": { + "address_family": [ + { + "log_adjacency_changes": { + "set": "{{ True if log is defined and detail is undefined }}", + "detail": "{{ True if detail is defined }}", + }, + }, + ], + }, + }, + { + "name": "address_family.manet", + "getval": re.compile( + r"""\s+manet* + \s*(?P<cache>cache)* + \s*(?P<acknowledgement>acknowledgement\s\d+)* + \s*(?P<update>update\s\d+)* + \s*(?P<hello>hello)* + \s*(?P<unicast>unicast)* + \s*(?P<multicast>multicast)* + \s*(?P<peering>peering\sselective)* + \s*(?P<disable>disable)* + \s*(?P<per_interface>per-interface)* + \s*(?P<redundancy>redundancy\s\d+)* + \s*(?P<willingness>willingness\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_lsa, + "compval": "manet", + "result": { + "address_family": [ + { + "manet": { + "cache": { + "acknowledgement": "{{ acknowledgement.split(" ")[1] }}", + "update": "{{ update.split(" ")[1] }}", + }, + "hello": { + "unicast": "{{ True if unicast is defined }}", + "multicast": "{{ True if multicast is defined }}", + }, + "peering": { + "set": "{{ True if peering is defined }}", + "disable": "{{ True if disable is defined }}", + "per_interface": "{{ True if per_interface is defined }}", + "redundancy": "{{ redundancy.split(" ")[1] }}", + }, + "willingness": "{{ willingness.split(" ")[1] }}", + }, + }, + ], + }, + }, + { + "name": "address_family.max_lsa", + "getval": re.compile( + r"""\s+max-lsa + \s(?P<number>\d+)* + \s*(?P<threshold>\d+)* + \s*(?P<ignore_count>ignore-count\s\d+)* + \s*(?P<ignore_time>ignore-time\s\d+)* + \s*(?P<reset_time>reset-time\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_lsa, + "compval": "max_lsa", + "result": { + "address_family": [ + { + "max_lsa": { + "number": "{{ number }}", + "threshold_value": "{{ threshold }}", + "ignore_count": "{{ ignore_count.split(" ")[1] }}", + "ignore_time": "{{ ignore_time.split(" ")[1] }}", + "reset_time": "{{ reset_time.split(" ")[1] }}", + "warning_only": "{{ True if warning is defined }}", + }, + }, + ], + }, + }, + { + "name": "address_family.max_metric", + "getval": re.compile( + r"""\s+max-metric* + \s*(?P<router_lsa>router-lsa)* + \s*(?P<include_stub>include-stub)* + \s*(?P<external_lsa>external-lsa\s\d+)* + \s*(?P<startup_time>on-startup\s\d+)* + \s*(?P<startup_wait>on-startup\s\S+)* + \s*(?P<summary_lsa>summary-lsa\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_max_metric, + "compval": "max_metric", + "result": { + "address_family": [ + { + "max_metric": { + "router_lsa": "{{ True if router_lsa is defined }}", + "external_lsa": "{{ external_lsa.split(" ")[1] }}", + "include_stub": "{{ ignore_count.split(" ")[1] }}", + "on_startup": { + "time": "{{ startup_time.split(" ")[1] }}", + "wait_for_bgp": "{{ True if startup_wait is defined }}", + }, + "summary_lsa": "{{ summary_lsa.split(" ")[1] }}", + }, + }, + ], + }, + }, + { + "name": "address_family.maximum_paths", + "getval": re.compile( + r"""\s+maximum-paths* + \s+(?P<paths>\d+) + *$""", + re.VERBOSE, + ), + "setval": "maximum-paths {{ maximum_paths }}", + "compval": "maximum_paths", + "result": {"address_family": [{"maximum_paths": "{{ paths }}"}]}, + }, + { + "name": "address_family.passive_interface", + "getval": re.compile( + r"""\s+passive-interface + \s(?P<interface>\S+\s\S+) + *$""", + re.VERBOSE, + ), + "setval": "passive-interface {{ passive_interface }}", + "compval": "passive_interface", + "result": {"address_family": [{"passive_interface": "{{ interface }}"}]}, + }, + { + "name": "address_family.prefix_suppression", + "getval": re.compile( + r"""\s+(?P<prefix_sup>prefix-suppression) + *$""", + re.VERBOSE, + ), + "setval": "prefix-suppression", + "compval": "prefix_suppression", + "result": { + "address_family": [{"prefix_suppression": "{{ True if prefix_sup is defined }}"}], + }, + }, + { + "name": "address_family.queue_depth.hello", + "getval": re.compile( + r"""\s+queue-depth + \shello* + \s*(?P<max_packets>\d+)* + \s*(?P<unlimited>unlimited) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_queue_depth_hello, + "compval": "hello", + "result": { + "address_family": [ + { + "queue_depth": { + "hello": { + "max_packets": "{{ max_packets }}", + "unlimited": "{{ True if unlimited is defined }}", + }, + }, + }, + ], + }, + }, + { + "name": "address_family.queue_depth.update", + "getval": re.compile( + r"""\s+queue-depth + \supdate* + \s*(?P<max_packets>\d+)* + \s*(?P<unlimited>unlimited) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_queue_depth_update, + "compval": "update", + "result": { + "address_family": [ + { + "queue_depth": { + "update": { + "max_packets": "{{ max_packets }}", + "unlimited": "{{ True if unlimited is defined }}", + }, + }, + }, + ], + }, + }, + { + "name": "address_family.router_id", + "getval": re.compile( + r"""\s+router-id + \s(?P<id>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) + *$""", + re.VERBOSE, + ), + "setval": "router-id {{ router_id }}", + "compval": "router_id", + "result": {"address_family": [{"router_id": "{{ id }}"}]}, + }, + { + "name": "address_family.summary_prefix", + "getval": re.compile( + r"""\s+summary-address + \s(?P<address>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<mask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})* + \s*(?P<not_adv>not-advertise)* + \s*(?P<nssa>nssa-only)* + \s*(?P<tag>tag\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_summary_prefix, + "compval": "summary_prefix", + "result": { + "address_family": [ + { + "summary_prefix": { + "address": "{{ address }}", + "mask": "{{ mask }}", + "not_advertise": "{{ True if not_adv is defined }}", + "nssa_only": "{{ True if nssa is defined }}", + "tag": "{{ tag.split(" ")[1] }}", + }, + }, + ], + }, + }, + { + "name": "address_family.shutdown", + "getval": re.compile( + r"""\s+(?P<shutdown>shutdown) + *$""", + re.VERBOSE, + ), + "setval": "shutdown", + "compval": "shutdown", + "result": {"address_family": [{"shutdown": "{{ True if shutdown is defined }}"}]}, + }, + { + "name": "address_family.timers.lsa", + "getval": re.compile( + r"""\s+timers + \slsa + \sarrival + \s(?P<lsa>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers lsa arrival {{ timers.lsa }}", + "compval": "lsa", + "result": {"address_family": [{"timers": {"lsa": "{{ lsa }}"}}]}, + }, + { + "name": "address_family.timers.pacing", + "getval": re.compile( + r"""\s+timers + \spacing + \s(?P<flood>flood\s\d+)* + \s*(?P<lsa_group>lsa-group\s\d+)* + \s*(?P<retransmission>retransmission\s\d+) + *$""", + re.VERBOSE, + ), + "setval": _tmplt_ospf_timers_pacing, + "compval": "pacing", + "result": { + "address_family": [ + { + "timers": { + "pacing": { + "flood": "{{ flood.split(" ")[1] }}", + "lsa_group": "{{ lsa_group.split(" ")[1] }}", + "retransmission": "{{ retransmission.split(" ")[1] }}", + }, + }, + }, + ], + }, + }, + { + "name": "address_family.timers.throttle.lsa", + "getval": re.compile( + r"""\s+timers + \sthrottle + \s*(?P<lsa>lsa)* + \s*(?P<first_delay>\d+)* + \s*(?P<min_delay>\d+)* + \s*(?P<max_delay>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers throttle lsa {{ throttle.lsa.first_delay }} {{ throttle.lsa.min_delay }} {{ throttle.lsa.max_delay }}", + "compval": "throttle.lsa", + "result": { + "address_family": [ + { + "timers": { + "throttle": { + "lsa": { + "first_delay": "{{ first_delay }}", + "min_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + }, + }, + }, + }, + ], + }, + }, + { + "name": "address_family.timers.throttle.spf", + "getval": re.compile( + r"""\s+timers + \sthrottle + \s*(?P<spf>spf)* + \s*(?P<first_delay>\d+)* + \s*(?P<min_delay>\d+)* + \s*(?P<max_delay>\d+) + *$""", + re.VERBOSE, + ), + "setval": "timers throttle spf {{ throttle.spf.receive_delay }} {{ throttle.spf.between_delay }} {{ throttle.spf.max_delay }}", + "compval": "throttle.spf", + "result": { + "address_family": [ + { + "timers": { + "throttle": { + "spf": { + "receive_delay": "{{ first_delay }}", + "between_delay": "{{ min_delay }}", + "max_delay": "{{ max_delay }}", + }, + }, + }, + }, + ], + }, + }, + ] diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ping.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ping.py new file mode 100644 index 000000000..d7f375b71 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ping.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Ping parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +class PingTemplate(NetworkTemplate): + def __init__(self, lines=None): + super(PingTemplate, self).__init__(lines=lines, tmplt=self) + + # fmt: off + PARSERS = [ + { + 'name': 'rate', + 'getval': re.compile( + r''' + ^Success\srate\sis + (\s(?P<pct>\d+))? + (\spercent\s\((?P<rx>\d+)/(?P<tx>\d+)\))? + (,\s+round-trip\smin/avg/max\s=)? + (\s(?P<min>\d+)/(?P<avg>\d+)/(?P<max>\d+))? + (\s+\w+\s*$|.*\s*$)? + ''', re.VERBOSE, + ), + "setval": "ping" + "{{ (' vrf ' + vrf) if vrf is defined else '' }}" + "{{ (' ' + afi|string ) if afi is defined else '' }}" + "{{ (' ' + dest ) if dest is defined else '' }}" + "{{ (' repeat ' + count|string ) if count is defined else '' }}" + "{{ (' df-bit' ) if df_bit|d(False) else '' }}" + "{{ (' timeout ' + timeout|string) if timeout is defined else '' }}" + "{{ (' ingress ' + ingress) if ingress is defined else '' }}" + "{{ (' egress ' + egress) if egress is defined else '' }}" + "{{ (' source ' + source) if source is defined else '' }}", + 'result': { + "ping": { + 'loss_percentage': '{{ 100 - pct|int }}%', + 'loss': '{{ 100 - pct|int }}', + 'rx': '{{ rx|int }}', + 'tx': '{{ tx|int }}', + 'rtt': { + 'min': '{{ min }}', + 'avg': '{{ avg }}', + 'max': '{{ max }}', + }, + }, + }, + }, + ] + # fmt: on diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/prefix_lists.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/prefix_lists.py new file mode 100644 index 000000000..041d926f5 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/prefix_lists.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Prefix_lists parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +def _tmplt_set_prefix_lists(config_data): + if "prefix_list" in config_data: + if config_data.get("afi") == "ipv4": + config_data["afi"] = "ip" + cmd = "{afi} prefix-list {name}".format(**config_data) + if config_data.get("prefix_list"): + if config_data["prefix_list"].get("description"): + cmd += " description {description}".format(**config_data["prefix_list"]) + else: + cmd += " seq {sequence} {action} {prefix}".format(**config_data["prefix_list"]) + if config_data["prefix_list"].get("ge"): + cmd += " ge {ge}".format(**config_data["prefix_list"]) + if config_data["prefix_list"].get("le"): + cmd += " le {le}".format(**config_data["prefix_list"]) + return cmd + + +class Prefix_listsTemplate(NetworkTemplate): + def __init__(self, lines=None): + super(Prefix_listsTemplate, self).__init__(lines=lines, tmplt=self) + + PARSERS = [ + { + "name": "prefix_list", + "getval": re.compile( + r""" + ^(?P<afi>ip|ipv6)* + \s*prefix-list* + \s*(?P<name>\S+)* + \s*(?P<description>description\s\S.*)* + \s*(?P<sequence>seq\s\S+)* + \s*(?P<action>deny|permit)* + \s*(?P<prefix>(?:[0-9]{1,3}\.){3}[0-9]{1,3}/\d+|(([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}/\d+))* + \s*(?P<ge>ge\s\d+)* + \s*(?P<le>le\s\d+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_set_prefix_lists, + "result": { + "{{ afi + '_' + name }}": { + "afi": "{{ 'ipv4' if afi is defined and afi=='ip' else 'ipv6' }}", + "prefix_lists": [ + { + "name": "{{ name if name is defined }}", + "description": "{{ description.split('description ')[1] if description is defined }}", + "entries": { + # Description at this level is deprecated, should be removed when we plan to remove the + # Description from entries level + "description": "{{ description.split('description ')[1] if description is defined }}", + "sequence": "{{ sequence.split(' ')[1] if sequence is defined }}", + "action": "{{ action if action is defined }}", + "prefix": "{{ prefix if prefix is defined }}", + "ge": "{{ ge.split(' ')[1] if ge is defined }}", + "le": "{{ le.split(' ')[1] if le is defined }}", + }, + }, + ], + }, + }, + "shared": True, + }, + ] diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/route_maps.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/route_maps.py new file mode 100644 index 000000000..47b4402b6 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/route_maps.py @@ -0,0 +1,1273 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Route_maps parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +def _tmplt_route_map_match(config_data): + if ( + config_data.get("match") + and not config_data["match"].get("ip") + and not config_data["match"].get("ipv6") + ): + command = [] + match = config_data["match"] + if match and match.get("additional_paths"): + cmd = "match additional-paths advertise-set" + if config_data["match"]["additional_paths"].get("all"): + cmd += " all" + if config_data["match"]["additional_paths"].get("best"): + cmd += " best {best}".format(**config_data["match"]["additional_paths"]) + if config_data["match"]["additional_paths"].get("best_range"): + cmd += " best-range" + if config_data["match"]["additional_paths"]["best_range"].get("lower_limit"): + cmd += " lower-limit {lower_limit}".format( + **config_data["match"]["additional_paths"]["best_range"] + ) + if config_data["match"]["additional_paths"]["best_range"].get("upper_limit"): + cmd += " upper-limit {upper_limit}".format( + **config_data["match"]["additional_paths"]["best_range"] + ) + if config_data["match"]["additional_paths"].get("group_best"): + cmd += " group-best" + command.append(cmd) + if match.get("as_path"): + cmd = "match as-path " + if match["as_path"].get("acls"): + temp = [] + for k, v in iteritems(match["as_path"]["acls"]): + temp.append(str(v)) + cmd += " ".join(sorted(temp)) + command.append(cmd) + if match.get("clns"): + cmd = "match clns" + if match["clns"].get("address"): + cmd += " address {address}".format(**match["clns"]) + elif match["clns"].get("next_hop"): + cmd = " next-hop {next_hop}".format(**match["clns"]) + elif match["clns"].get("route_source"): + cmd = " route-source {route_source}".format(**match["clns"]) + command.append(cmd) + if match.get("community"): + cmd = "match community " + temp = [] + for k, v in iteritems(match["community"]["name"]): + temp.append(v) + cmd += " ".join(sorted(temp)) + if match["community"].get("exact_match"): + cmd += " exact-match" + command.append(cmd) + if match.get("extcommunity"): + cmd = "match extcommunity " + temp = [] + for k, v in iteritems(match["extcommunity"]): + temp.append(v) + cmd += " ".join(sorted(temp)) + command.append(cmd) + if match.get("interfaces"): + cmd = "match interface " + temp = [] + for k, v in iteritems(match["interfaces"]): + temp.append(v) + cmd += " ".join(sorted(temp)) + command.append(cmd) + if match.get("length"): + command.append("match length {minimum} {maximum}".format(**match["length"])) + if match.get("local_preference"): + cmd = "match local-preference " + if match["local_preference"].get("value"): + temp = [] + for k, v in iteritems(match["local_preference"]["value"]): + temp.append(v) + cmd += " ".join(sorted(temp)) + command.append(cmd) + if match.get("mdt_group"): + cmd = "match mdt-group " + if match["mdt_group"].get("acls"): + temp = [] + for k, v in iteritems(match["mdt_group"]["acls"]): + temp.append(v) + cmd += " ".join(sorted(temp)) + command.append(cmd) + if match.get("metric"): + cmd = "match metric" + if match["metric"].get("external"): + cmd += " external" + if match["metric"].get("value"): + cmd += " {value}".format(**match["metric"]) + if match["metric"].get("deviation"): + cmd += " +-" + if match["metric"].get("deviation_value"): + cmd += " {deviation_value}".format(**match["metric"]) + command.append(cmd) + if match.get("mpls_label"): + command.append("match mpls-label") + if match.get("policy_lists"): + cmd = "match policy-list " + temp = [] + for k, v in iteritems(match["policy_lists"]): + temp.append(v) + cmd += " ".join(sorted(temp)) + command.append(cmd) + if match.get("route_type"): + cmd = "match route-type" + if match["route_type"].get("external"): + cmd += " external" + if match["route_type"]["external"].get("type_1"): + cmd += " type-1" + elif match["route_type"]["external"].get("type_2"): + cmd += " type-2" + elif match["route_type"].get("internal"): + cmd += " internal" + elif match["route_type"].get("level_1"): + cmd += " level-1" + elif match["route_type"].get("level_2"): + cmd += " level-2" + elif match["route_type"].get("local"): + cmd += " local" + elif match["route_type"].get("nssa_external"): + cmd += " nssa-external" + if match["route_type"]["nssa_external"].get("type_1"): + cmd += " type-1" + elif match["route_type"]["nssa_external"].get("type_2"): + cmd += " type-2" + command.append(cmd) + if match.get("rpki"): + cmd = "match rpki" + if match["rpki"].get("invalid"): + cmd += " invalid" + if match["rpki"].get("not_found"): + cmd += " not-found" + if match["rpki"].get("valid"): + cmd += " valid" + command.append(cmd) + if match.get("security_group"): + cmd = "match security-group" + if match["security_group"].get("source"): + cmd += " source tag " + temp = [] + for k, v in iteritems(match["security_group"]["source"]): + temp.append(str(v)) + cmd += " ".join(sorted(temp)) + elif match["security_group"].get("destination"): + cmd += " destination tag" + for each in match["destination"]: + cmd += " {0}".format(each) + command.append(cmd) + if match.get("source_protocol"): + cmd = "match source-protocol" + if match["source_protocol"].get("bgp"): + cmd += " bgp {bgp}".format(**match["source_protocol"]) + if match["source_protocol"].get("connected"): + cmd += " connected" + if match["source_protocol"].get("eigrp"): + cmd += " eigrp {eigrp}".format(**match["source_protocol"]) + if match["source_protocol"].get("isis"): + cmd += " isis" + if match["source_protocol"].get("lisp"): + cmd += " lisp" + if match["source_protocol"].get("mobile"): + cmd += " mobile" + if match["source_protocol"].get("ospf"): + cmd += " ospf {ospf}".format(**match["source_protocol"]) + if match["source_protocol"].get("ospfv3"): + cmd += " ospfv3 {ospfv3}".format(**match["source_protocol"]) + if match["source_protocol"].get("rip"): + cmd += " rip" + if match["source_protocol"].get("static"): + cmd += " static" + command.append(cmd) + if match.get("tag"): + cmd = "match tag" + if match["tag"].get("tag_list"): + cmd += " list" + for each in match["tag"]["tag_list"]: + cmd += " {0}".format(each) + elif match["tag"].get("value"): + for each in match["tag"]["value"]: + cmd += " {0}".format(each) + command.append(cmd) + if match.get("track"): + command.append("match track {track}".format(**match)) + return command + + +def _tmplt_route_map_match_ip(config_data): + if config_data.get("match") and config_data["match"].get("ip"): + + def construct_cmd_from_list(cmd, config): + temp = [] + for k, v in iteritems(config): + temp.append(v) + cmd += " " + " ".join(sorted(temp)) + return cmd + + cmd = "match ip" + if config_data["match"]["ip"].get("address"): + cmd += " address" + if config_data["match"]["ip"]["address"].get("prefix_lists"): + cmd += " prefix-list" + cmd = construct_cmd_from_list( + cmd, + config_data["match"]["ip"]["address"]["prefix_lists"], + ) + elif config_data["match"]["ip"]["address"].get("acls"): + cmd = construct_cmd_from_list(cmd, config_data["match"]["ip"]["address"]["acls"]) + if config_data["match"]["ip"].get("flowspec"): + cmd += " flowspec" + if config_data["match"]["ip"]["flowspec"].get("dest_pfx"): + cmd += " dest-pfx" + elif config_data["match"]["ip"]["flowspec"].get("src_pfx"): + cmd += " src-pfx" + if config_data["match"]["ip"]["flowspec"].get("prefix_lists"): + cmd += " prefix-list" + cmd = construct_cmd_from_list( + cmd, + config_data["match"]["ip"]["flowspec"]["prefix_lists"], + ) + elif config_data["match"]["ip"]["flowspec"].get("acls"): + cmd = construct_cmd_from_list(cmd, config_data["match"]["ip"]["flowspec"]["acls"]) + if config_data["match"]["ip"].get("next_hop"): + cmd += " next-hop" + if config_data["match"]["ip"]["next_hop"].get("prefix_lists"): + cmd += " prefix-list" + cmd = construct_cmd_from_list( + cmd, + config_data["match"]["ip"]["next_hop"]["prefix_lists"], + ) + elif config_data["match"]["ip"]["next_hop"].get("acls"): + cmd = construct_cmd_from_list(cmd, config_data["match"]["ip"]["next_hop"]["acls"]) + if config_data["match"]["ip"].get("redistribution_source"): + cmd += " redistribution-source" + if config_data["match"]["ip"]["redistribution_source"].get("prefix_lists"): + cmd += " prefix-list" + cmd = construct_cmd_from_list( + cmd, + config_data["match"]["ip"]["redistribution_source"]["prefix_lists"], + ) + elif config_data["match"]["ip"]["redistribution_source"].get("acls"): + cmd = construct_cmd_from_list( + cmd, + config_data["match"]["ip"]["redistribution_source"]["acls"], + ) + if config_data["match"]["ip"].get("route_source"): + cmd += " route-source" + if config_data["match"]["ip"]["route_source"].get("redistribution_source"): + cmd += " redistribution-source" + if config_data["match"]["ip"]["route_source"].get("prefix_lists"): + cmd += " prefix-list" + cmd = construct_cmd_from_list( + cmd, + config_data["match"]["ip"]["route_source"]["prefix_lists"], + ) + elif config_data["match"]["ip"]["route_source"].get("acls"): + cmd = construct_cmd_from_list( + cmd, + config_data["match"]["ip"]["route_source"]["acls"], + ) + return cmd + + +def _tmplt_route_map_match_ipv6(config_data): + if config_data.get("match") and config_data["match"].get("ipv6"): + cmd = "match ipv6" + if config_data["match"]["ipv6"].get("address"): + cmd += " address" + if config_data["match"]["ipv6"]["address"].get("prefix_list"): + cmd += " prefix-list {prefix_list}".format( + **config_data["match"]["ipv6"]["address"] + ) + elif config_data["match"]["ipv6"]["address"].get("acl"): + cmd += " {acl}".format(**config_data["match"]["ipv6"]["address"]) + if config_data["match"]["ipv6"].get("flowspec"): + cmd += " flowspec" + if config_data["match"]["ipv6"]["flowspec"].get("dest_pfx"): + cmd += " dest-pfx" + elif config_data["match"]["ipv6"]["flowspec"].get("src_pfx"): + cmd += " src-pfx" + if config_data["match"]["ipv6"]["flowspec"].get("prefix_list"): + cmd += " prefix-list {prefix_list}".format( + **config_data["match"]["ipv6"]["flowspec"] + ) + elif config_data["match"]["ipv6"]["flowspec"].get("acl"): + cmd += " {acl}".format(**config_data["match"]["ipv6"]["flowspec"]) + if config_data["match"]["ipv6"].get("next_hop"): + cmd += " next-hop" + if config_data["match"]["ipv6"]["next_hop"].get("prefix_list"): + cmd += " prefix-list {prefix_list}".format( + **config_data["match"]["ipv6"]["next_hop"] + ) + elif config_data["match"]["ipv6"]["next_hop"].get("acl"): + cmd += " {acl}".format(**config_data["match"]["ipv6"]["next_hop"]) + if config_data["match"]["ipv6"].get("route_source"): + cmd += " route-source" + if config_data["match"]["ipv6"]["route_source"].get("prefix_list"): + cmd += " prefix-list {prefix_list}".format( + **config_data["match"]["ipv6"]["route_source"] + ) + elif config_data["match"]["ipv6"]["route_source"].get("acl"): + cmd += " {acl}".format(**config_data["match"]["ipv6"]["route_source"]) + return cmd + + +def _tmplt_route_map_set(config_data): + if config_data.get("set"): + command = [] + set = config_data["set"] + if set.get("aigp_metric"): + cmd = "set aigp-metric" + if set["aigp_metric"].get("value"): + cmd += " {value}".format(**set["aigp_metric"]) + elif set["aigp_metric"].get("igp_metric"): + cmd += " igp-metric" + command.append(cmd) + if set.get("as_path"): + cmd = "set as-path" + if set["as_path"].get("prepend"): + cmd += " prepend" + if set["as_path"]["prepend"].get("as_number"): + cmd += " {0}".format(set["as_path"]["prepend"].get("as_number")) + elif set["as_path"]["prepend"].get("last_as"): + cmd += " last-as {last_as}".format(**set["as_path"]["prepend"]) + if set["as_path"].get("tag"): + cmd += " tag" + command.append(cmd) + if set.get("automatic_tag"): + command.append("set automatic-tag") + if set.get("clns"): + command.append("set clns next-hop {clns}".format(**set)) + if set.get("comm_list"): + command.append("set comm-list {comm_list} delete".format(**set)) + if set.get("community"): + cmd = "set community" + if set["community"].get("number"): + cmd += " " + " ".join(i for i in set["community"]["number"]) + if set["community"].get("gshut"): + cmd += " gshut" + if set["community"].get("internet"): + cmd += " internet" + if set["community"].get("local_as"): + cmd += " local-as" + if set["community"].get("no_advertise"): + cmd += " no-advertise" + if set["community"].get("no_export"): + cmd += " no-export" + if set["community"].get("none"): + cmd += " none" + # additive must be set last last + if set["community"].get("additive"): + cmd += " additive" + command.append(cmd) + if set.get("dampening"): + command.append( + "set dampening {penalty_half_time} {reuse_route_val} {suppress_route_val} {max_suppress}".format( + **set["dampening"] + ), + ) + if set.get("default"): + command.append("set default interface {default}".format(**set["default"])) + if set.get("extcomm_list"): + command.append("set extcomm-list {extcomm_list} delete".format(**set)) + if set.get("extcommunity"): + if set["extcommunity"].get("cost"): + cmd = "set extcommunity cost" + if set["extcommunity"]["cost"].get("igp"): + cmd += " igp" + elif set["extcommunity"]["cost"].get("pre_bestpath"): + cmd += " pre-bestpath" + if set["extcommunity"]["cost"].get("id"): + cmd += " {id}".format(**set["extcommunity"]["cost"]) + if set["extcommunity"]["cost"].get("cost_value"): + cmd += " {cost_value}".format(**set["extcommunity"]["cost"]) + command.append(cmd) + if set["extcommunity"].get("rt"): + cmd = "set extcommunity rt" + if set["extcommunity"]["rt"].get("range"): + cmd += " range {lower_limit} {upper_limit}".format( + **set["extcommunity"]["rt"]["range"] + ) + elif set["extcommunity"]["rt"].get("address"): + cmd += " {address}".format(**set["extcommunity"]["rt"]) + if set["extcommunity"]["rt"].get("additive"): + cmd += " additive" + command.append(cmd) + if set["extcommunity"].get("soo"): + command.append("set extcommunity soo {soo}".format(**set["extcommunity"])) + if set["extcommunity"].get("vpn_distinguisher"): + cmd = "set extcommunity vpn-distinguisher" + if set["extcommunity"]["vpn_distinguisher"].get("range"): + cmd += " range {lower_limit} {upper_limit}".format( + **set["extcommunity"]["vpn_distinguisher"]["range"] + ) + elif set["extcommunity"]["vpn_distinguisher"].get("address"): + cmd += " {address}".format(**set["extcommunity"]["vpn_distinguisher"]) + if set["extcommunity"]["vpn_distinguisher"].get("additive"): + cmd += " additive" + command.append(cmd) + if set.get("global"): + command.append("set global") + if set.get("interfaces"): + cmd = "set interface " + temp = [] + for k, v in iteritems(set["interfaces"]): + temp.append(v) + cmd += " ".join(sorted(temp)) + command.append(cmd) + if set.get("level"): + cmd = "set level" + if set["level"].get("level_1"): + cmd += " level-1" + elif set["level"].get("level_1_2"): + cmd += " level-1-2" + elif set["level"].get("level_2"): + cmd += " level-2" + elif set["level"].get("nssa_only"): + cmd += " nssa-only" + if set.get("lisp"): + command.append("set lisp locator-set {lisp}".format(**set)) + if set.get("local_preference"): + command.append("set local-preference {local_preference}".format(**set)) + if set.get("metric"): + cmd = "set metric" + if set["metric"].get("metric_value"): + cmd += " {metric_value}".format(**set["metric"]) + if set["metric"].get("deviation"): + if set["metric"]["deviation"] == "plus": + cmd += ( + " +{eigrp_delay} {metric_reliability} {metric_bandwidth} {mtu}".format( + **set["metric"] + ) + ) + elif set["metric"]["deviation"] == "minus": + cmd += ( + " -{eigrp_delay} {metric_reliability} {metric_bandwidth} {mtu}".format( + **set["metric"] + ) + ) + if set["metric"].get("deviation") and not set["metric"].get("eigrp_delay"): + if set["metric"]["deviation"] == "plus": + cmd = "set metric +{metric_value}".format(**set["metric"]) + elif set["metric"]["deviation"] == "minus": + cmd = "set metric -{metric_value}".format(**set["metric"]) + command.append(cmd) + if set.get("metric_type"): + cmd = "set metric-type" + if set["metric_type"].get("external"): + cmd += " external" + elif set["metric_type"].get("internal"): + cmd += " internal" + elif set["metric_type"].get("type_1"): + cmd += " type-1" + elif set["metric_type"].get("type_2"): + cmd += " type-2" + command.append(cmd) + if set.get("mpls_label"): + command.append("set mpls-label") + if set.get("origin"): + cmd = "set origin" + if set["origin"].get("igp"): + cmd += " igp" + elif set["origin"].get("incomplete"): + cmd += " incomplete" + if set.get("tag"): + command.append("set tag {tag}".format(**set)) + if set.get("traffic_index"): + command.append("set traffic-index {traffic_index}".format(**set)) + if set.get("vrf"): + command.append("set vrf {vrf}".format(**set)) + if set.get("weight"): + command.append("set weight {weight}".format(**set)) + return command + + +def _tmplt_route_map_set_ip(config_data): + if config_data.get("set") and config_data["set"].get("ip"): + command = [] + set_ip = config_data["set"]["ip"] + cmd = "set ip" + if set_ip.get("address"): + command.append("{0} address prefix-list {address}".format(cmd, **set_ip)) + if set_ip.get("df"): + command.append("{0} df {df}".format(cmd, **set_ip)) + if set_ip.get("global_route"): + cmd += " global next-hop" + if set_ip["global_route"].get("verify_availability"): + cmd += " verify-availability {address} {sequence} track {track}".format( + **set_ip["global_route"]["verify_availability"] + ) + elif set_ip["global_route"].get("address"): + cmd += " {address}".format(**set_ip["global_route"]) + command.append(cmd) + if set_ip.get("next_hop"): + cmd += " next-hop" + if set_ip["next_hop"].get("address"): + command.append("{0} {address}".format(cmd, **set_ip["next_hop"])) + if set_ip["next_hop"].get("dynamic"): + command.append("{0} dynamic dhcp".format(cmd)) + if set_ip["next_hop"].get("encapsulate"): + command.append( + "{0} encapsulate l3vpn {encapsulate}".format(cmd, **set_ip["next_hop"]), + ) + if set_ip["next_hop"].get("peer_address"): + command.append("{0} peer-address".format(cmd)) + if set_ip["next_hop"].get("recursive"): + child_cmd = "{0} recursive".format(cmd) + if set_ip["next_hop"]["recursive"].get("global_route"): + child_cmd += " global" + elif set_ip["next_hop"]["recursive"].get("vrf"): + child_cmd += " vrf {vrf}".format(**set_ip["next_hop"]["recursive"]) + if set_ip["next_hop"]["recursive"].get("address"): + child_cmd += " {address}".format(**set_ip["next_hop"]["recursive"]) + command.append(child_cmd) + if set_ip["next_hop"].get("self"): + command.append("{0} self".format(cmd)) + if set_ip["next_hop"].get("verify_availability"): + command.append( + "{0} verify-availability {address} {sequence} track {track}".format( + cmd, **set_ip["next_hop"]["verify_availability"] + ), + ) + if set_ip.get("precedence"): + cmd += " precedence" + if set_ip["precedence"].get("critical"): + cmd += " critical" + elif set_ip["precedence"].get("flash"): + cmd += " flash" + elif set_ip["precedence"].get("flash_override"): + cmd += " flash-override" + elif set_ip["precedence"].get("immediate"): + cmd += " immediate" + elif set_ip["precedence"].get("internet"): + cmd += " internet" + elif set_ip["precedence"].get("network"): + cmd += " network" + elif set_ip["precedence"].get("priority"): + cmd += " priority" + elif set_ip["precedence"].get("routine"): + cmd += " routine" + command.append(cmd) + if set_ip.get("qos_group"): + command.append("{0} qos-group {qos_group}".format(cmd, **set_ip)) + if set_ip.get("tos"): + cmd += " tos" + if set_ip["tos"].get("max_reliability"): + cmd += " max-reliability" + elif set_ip["tos"].get("max_throughput"): + cmd += " max-throughput" + elif set_ip["tos"].get("min_delay"): + cmd += " min-delay" + elif set_ip["tos"].get("min_monetary_cost"): + cmd += " min-monetary-cost" + elif set_ip["tos"].get("normal"): + cmd += " normal" + command.append(cmd) + if set_ip.get("vrf"): + cmd += " vrf {vrf} next-hop".format(**set_ip) + if set_ip["vrf"].get("verify_availability").get("address"): + cmd += " verify-availability {address} {sequence} track {track}".format( + **set_ip["vrf"]["verify_availability"] + ) + elif set_ip["vrf"].get("address"): + cmd += " {address}".format(**set_ip["vrf"]) + command.append(cmd) + return command + + +def _tmplt_route_map_set_ipv6(config_data): + if config_data.get("set") and config_data["set"].get("ipv6"): + set_ipv6 = config_data["set"]["ipv6"] + cmd = "set ipv6" + if set_ipv6.get("address"): + cmd += " address prefix-list {address}".format(**set_ipv6) + if set_ipv6.get("default"): + cmd += " default" + if set_ipv6.get("global_route"): + cmd += " global next-hop" + if set_ipv6["global_route"].get("verify_availability"): + cmd += " verify-availability {address} {sequence} track {track}".format( + **set_ipv6["global_route"]["verify_availability"] + ) + elif set_ipv6["global_route"].get("address"): + cmd += " {address}".format(**set_ipv6["global_route"]) + if set_ipv6.get("next_hop"): + cmd += " next-hop" + if set_ipv6["next_hop"].get("address"): + cmd += " {address}".format(**set_ipv6["next_hop"]) + if set_ipv6["next_hop"].get("encapsulate"): + cmd += " encapsulate l3vpn {encapsulate}".format(**set_ipv6["next_hop"]) + if set_ipv6["next_hop"].get("peer_address"): + cmd += " peer-address" + if set_ipv6.get("precedence"): + cmd += " precedence {precedence}".format(**set_ipv6) + if set_ipv6.get("vrf"): + cmd += ( + " vrf {vrf} next-hop verify-availability {address} {sequence} track {track}".format( + **set_ipv6["vrf"]["verify_availability"] + ) + ) + return cmd + + +class Route_mapsTemplate(NetworkTemplate): + def __init__(self, lines=None): + super(Route_mapsTemplate, self).__init__(lines=lines, tmplt=self) + + PARSERS = [ + { + "name": "route_map", + "getval": re.compile( + r""" + ^route-map* + \s*(?P<route_map>\S+)* + \s*(?P<action>deny|permit)* + \s*(?P<sequence>\d+)* + (\s|$)""", + re.VERBOSE, + ), + "setval": "", + "result": { + "{{ route_map }}": { + "route_map": "{{ route_map }}", + "{{ action|d() + '_' + sequence|d() }}": { + "entries": {"action": "{{ action }}", "sequence": "{{ sequence }}"}, + }, + }, + }, + "shared": True, + }, + { + "name": "continue_entry", + "getval": re.compile( + r""" + \s+continue* + \s*(?P<entry_sequence>\d+)* + $""", + re.VERBOSE, + ), + "setval": "continue {{ continue_entry.entry_sequence }}", + "result": { + "{{ route_map }}": { + "{{ action|d() + '_' + sequence|d() }}": { + "entries": { + "continue_entry": { + "set": "{{ True if entry_sequence is not defined }}", + "entry_sequence": "{{ entry_sequence }}", + }, + }, + }, + }, + }, + }, + { + "name": "description", + "getval": re.compile( + r""" + \s+description* + \s*(?P<description>\S.*)* + $""", + re.VERBOSE, + ), + "setval": "description {{ description }}", + "result": { + "{{ route_map }}": { + "{{ action|d() + '_' + sequence|d() }}": { + "entries": {"description": "{{ description }}"}, + }, + }, + }, + }, + { + "name": "match", + "getval": re.compile( + r""" + \s+match* + \s*(?P<additional_paths>additional-paths\sadvertise-set\s\S.*)* + \s*(?P<as_path>as-path.*|as-path)* + \s*(?P<clns>clns\s(address\s\S+|next-hop\s\S+|route-source\s\S+))* + \s*(?P<community>community\s\S.*)* + \s*(?P<extcommunity>extcommunity\s\S.*)* + \s*(?P<interfaces>interface\s\S.*)* + \s*(?P<length>length\s\d+\s\d+)* + \s*(?P<local_preference>local-preference\s\d.*|local-preference)* + \s*(?P<mdt_group>mdt-group\s\S.*|mdt-group)* + \s*(?P<metric>metric\sexternal\s\S.*|metric\s\d+\S.*)* + \s*(?P<mpls_label>mpls-label)* + \s*(?P<policy_list>policy-list\s\S.*)* + \s*(?P<route_type>route-type\s(external\s(type-1|type-2)|internal|level-1|level-2|local|nssa-external\s(type-1|type-2)))* + \s*(?P<rpki>rpki\s(invalid|not-found|valid))* + \s*(?P<security_group>security-group\s(destination\stag\s\d.*|source\stag\s\d.*))* + \s*(?P<source_protocol>source-protocol\s\S.*)* + \s*(?P<tag>tag\slist\s\S.*|tag\s\S.*)* + \s*(?P<track>track\s*\d+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_route_map_match, + "result": { + "{{ route_map }}": { + "{{ action|d() + '_' + sequence|d() }}": { + "entries": { + "match": { + "additional_paths": { + "all": "{{ True if additional_paths is defined and 'all' in additional_paths }}", + "best": "{{ additional_paths.split('best ')[1].split(' ')[0]|int if additional_paths is defined and\ + 'best' in additional_paths and 'best-range' not in additional_paths }}", + "best_range": { + "lower_limit": "{{ additional_paths.split('best-range ')[1].split(' ')[0]|int if additional_paths is defined and\ + 'best-range' in additional_paths }}", + "upper_limit": "{{ additional_paths.split('best-range ')[1].split(' ')[1]|int if additional_paths is defined and\ + 'best-range' in additional_paths }}", + }, + "group_best": "{{ True if additional_paths is defined and 'group-best' in additional_paths }}", + }, + "as_path": { + "set": "{{ True if as_path is defined and as_path.split(' ')|length == 1 }}", + "acls": "{{ as_path.split('as-path ')[1].split(' ') if as_path is defined and as_path.split(' ')|length > 1 }}", + }, + "clns": { + "address": "{{ clns.split('clns address ')[1] if clns is defined }}", + "next_hop": "{{ clns.split('clns next-hop ')[1] if clns is defined }}", + "route_source": "{{ clns.split('clns route-source ')[1] if clns is defined }}", + }, + "community": { + "name": "{{ community.split('community ')[1].split(' exact-match')[0].split(' ') if community is defined }}", + "exact_match": "{{ True if community is defined and 'exact-match' in community }}", + }, + "extcommunity": "{{ extcommunity.split('extcommunity ')[1].split(' ') if extcommunity is defined }}", + "interfaces": "{{ interfaces.split('interface ')[1].split(' ') if interfaces is defined }}", + "length": { + "minimum": "{{ length.split(' ')[1] if length is defined }}", + "maximum": "{{ length.split(' ')[2] if length is defined }}", + }, + "local_preference": { + "set": "{{ True if local_preference is defined and local_preference.split(' ')|length == 1 }}", + "value": "{{ local_preference.split('local-preference ')[1].split(' ') if local_preference is defined }}", + }, + "mdt_group": { + "set": "{{ True if mdt_group is defined and mdt_group.split(' ')|length == 1 }}", + "acls": "{{ mdt_group.split('mdt-group ')[1].split(' ') if mdt_group is defined }}", + }, + "metric": { + "external": "{{ True if metric is defined and 'external' in metric.split(' ') }}", + "value": "{% if metric is defined and 'external' not in metric.split(' ') %}{{ metric.split(' ')[1] }}\ + {% elif metric is defined and 'external' in metric.split(' ') %}{{ metric.split(' ')[2] }}\ + {% endif %}", + "deviation": "{{ True if metric is defined and '+-' in metric }}", + "deviation_value": "{% if metric is defined and 'external' in metric and '+-' in metric %}{{ metric.split(' ')[4] }}\ + {% elif metric is defined and 'external' not in metric and '+-' in metric %}{{ metric.split(' ')[3] }}{% endif %}", + }, + "mpls_label": "{{ True if mpls_label is defined }}", + "policy_lists": "{{ policy_list.split('policy-list ')[1].split(' ') if policy_list is defined }}", + "route_type": { + "external": { + "set": "{{ True if route_type is defined and 'type-1' not in route_type and 'type-2' not in route_type}}", + "type_1": "{{ True if route_type is defined and 'type-1' in route_type }}", + "type_2": "{{ True if route_type is defined and 'type-2' in route_type }}", + }, + "internal": "{{ True if route_type is defined and 'internal' in route_type }}", + "level_1": "{{ True if route_type is defined and 'level-1' in route_type }}", + "level_2": "{{ True if route_type is defined and 'level-2' in route_type }}", + "local": "{{ True if route_type is defined and 'local' in route_type }}", + "nssa_external": { + "set": "{{ True if route_type is defined and 'type-1' not in route_type and 'type-2' not in route_type}}", + "type_1": "{{ True if route_type is defined and 'type-1' in route_type }}", + "type_2": "{{ True if route_type is defined and 'type-2' in route_type }}", + }, + }, + "rpki": { + "invalid": "{{ True if rpki is defined and 'invalid' in rpki and 'valid' not in rpki.split(' ') }}", + "not_found": "{{ True if rpki is defined and 'not-found' in rpki }}", + "valid": "{{ True if rpki is defined and 'valid' in rpki and 'invalid' not in rpki.split(' ') }}", + }, + "security_group": { + "destination": "{{ security_group.split('destination tag ')[1].split(' ') if security_group is defined and\ + 'destination' in security_group }}", + "source": "{{ security_group.split('source tag ')[1].split(' ') if security_group is defined and\ + 'source' in security_group }}", + }, + "source_protocol": { + "bgp": "{{ source_protocol.split('bgp ')[1].split(' ')[0] if source_protocol is defined and 'bgp' in source_protocol }}", + "connected": "{{ True if source_protocol is defined and 'connected' in source_protocol }}", + "eigrp": "{{ source_protocol.split('eigrp ')[1].split(' ')[0] if source_protocol is defined and\ + 'eigrp' in source_protocol }}", + "isis": "{{ True if source_protocol is defined and 'isis' in source_protocol }}", + "lisp": "{{ True if source_protocol is defined and 'lisp' in source_protocol }}", + "mobile": "{{ True if source_protocol is defined and 'mobile' in source_protocol }}", + "ospf": "{{ source_protocol.split('ospf ')[1].split(' ')[0] if source_protocol is defined and\ + 'ospf' in source_protocol }}", + "ospfv3": "{{ source_protocol.split('ospfv3 ')[1].split(' ')[0] if source_protocol is defined and\ + 'ospfv3' in source_protocol }}", + "rip": "{{ True if source_protocol is defined and 'rip' in source_protocol }}", + "static": "{{ True if source_protocol is defined and 'static' in source_protocol }}", + }, + "tag": { + "value": "{{ tag.split('tag ')[1].split(' ') if tag is defined and 'list' not in tag }}", + "tag_list": "{{ tag.split('tag list ')[1].split(' ') if tag is defined and 'list' in tag }}", + }, + "track": "{{ track.split('track ')[1] if track is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "match.ip", + "getval": re.compile( + r""" + \s+match* + \s*(?P<ip>ip)* + \s*(?P<address>address\sprefix-list\s\S.*|address\s\S.*)* + \s*(?P<flowspec>flowspec\sdest-pfx\s(prefix-list\s\S.*|\S.*)|flowspec\ssrc-pfx\s(prefix-list\s\S.*|\S.*))* + \s*(?P<next_hop>next-hop\sprefix-list\s\S.*|next-hop\s\S.*|next-hop)* + \s*(?P<redistribution_source>redistribution-source\sprefix-list\s\S.*|redistribution-source\s\S.*|redistribution-source)* + \s*(?P<route_source>route-source\sredistribution-source\sprefix-list\s\S.*|route-source\sredistribution-source\s\S.*|route-source\sprefix-list\s\S.*|route-source\s\S.*|route-source\sredistribution-source|route-source)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_route_map_match_ip, + "compval": "match", + "result": { + "{{ route_map }}": { + "{{ action|d() + '_' + sequence|d() }}": { + "entries": { + "match": { + "ip": { + "address": { + "acls": "{{ address.split('address ')[1].split(' ') if address is defined and\ + 'prefix-list' not in address else none }}", + "prefix_lists": "{{ address.split('address prefix-list ')[1].split(' ') if address is defined and\ + 'prefix-list' in address else None }}", + }, + "flowspec": { + "dest_pfx": "{{ True if flowspec is defined and 'dest-pfx' in flowspec }}", + "src_pfx": "{{ True if flowspec is defined and 'src-pfx' in flowspec }}", + "acls": "{{ flowspec.split('flowspec ')[1].split(' ')|d() if flowspec is defined and\ + 'prefix-list' not in flowspec else '' }}", + "prefix_lists": "{{ flowspec.split('flowspec prefix-list ')[1].split(' ')|d() if flowspec is defined and\ + 'prefix-list' in flowspec else ''}}", + }, + "next_hop": { + "set": "{{ True if next_hop is defined and next_hop.split(' ')|length == 1 }}", + "acls": "{{ next_hop.split('next-hop ')[1].split(' ') if next_hop is defined and\ + 'prefix-list' not in next_hop else '' }}", + "prefix_lists": "{{ next_hop.split('next-hop prefix-list ')[1].split(' ') if next_hop is defined and\ + 'prefix-list' in next_hop and next_hop.split('next-hop prefix-list ')[1] is not none else '' }}", + }, + "redistribution_source": { + "set": "{{ True if redistribution_source is defined and redistribution_source.split(' ')|length == 1 }}", + "acls": "{{ redistribution_source.split('redistribution-source ')[1].split(' ')|d()\ + if redistribution_source is defined and 'prefix-list' not in redistribution_source else '' }}", + "prefix_lists": "{{ redistribution_source.split('redistribution-source prefix-list ')[1].split(' ')|d()\ + if redistribution_source is defined and 'prefix-list' in redistribution_source else '' }}", + }, + "route_source": { + "set": "{{ True if route_source is defined and route_source.split(' ')|length == 1 }}", + "redistribution_source": "{{ True if route_source is defined and 'redistribution-source' in route_source }}", + "acls": "{{ route_source.split('route-source ')[1].split(' ') if route_source is defined and\ + 'prefix-list' not in route_source else '' }}", + "prefix_lists": "{{ route_source.split('route-source prefix-list ')[1].split(' ') if route_source is defined and\ + 'prefix-list' in route_source else '' }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "match.ipv6", + "getval": re.compile( + r""" + \s+match* + \s*(?P<ipv6>ipv6)* + \s*(?P<address>address\sprefix-list\s\S.*|address\s\S.*)* + \s*(?P<flowspec>flowspec\sdest-pfx\s(prefix-list\s\S.*|\S.*)|flowspec\ssrc-pfx\s(prefix-list\s\S.*|\S.*))* + \s*(?P<next_hop>next-hop\sprefix-list\s\S.*|next-hop\s\S.*)* + \s*(?P<route_source>route-source\sprefix-list\s\S.*|route-source\s\S.*)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_route_map_match_ipv6, + "compval": "match", + "result": { + "{{ route_map }}": { + "{{ action|d() + '_' + sequence|d() }}": { + "entries": { + "match": { + "ipv6": { + "address": { + "acl": "{{ address.split('address ')[1] if address is defined and 'prefix-list' not in address }}", + "prefix_list": "{{ address.split('address prefix-list ')[1] if address is defined and 'prefix-list' in address }}", + }, + "flowspec": { + "dest_pfx": "{{ True if flowspec is defined and 'dest-pfx' in flowspec }}", + "src_pfx": "{{ True if flowspec is defined and 'src-pfx' in flowspec }}", + "acl": "{{ flowspec.split('flowspec ')[1] if flowspec is defined and 'prefix-list' not in flowspec }}", + "prefix_list": "{{ flowspec.split('flowspec prefix-list ')[1] if flowspec is defined and 'prefix-list' in flowspec }}", + }, + "next_hop": { + "acl": "{{ next_hop.split('next-hop ')[1] if next_hop is defined and 'prefix-list' not in next_hop }}", + "prefix_list": "{{ next_hop.split('next-hop prefix-list ')[1] if next_hop is defined and 'prefix-list' in next_hop }}", + }, + "route_source": { + "acl": "{{ route_source.split('route-source ')[1] if route_source is defined and 'prefix-list' not in route_source }}", + "prefix_list": "{{ route_source.split('route-source prefix-list ')[1] if route_source is defined and\ + 'prefix-list' in route_source }}", + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "set", + "getval": re.compile( + r""" + \s+set* + \s*(?P<aigp_metric>aigp-metric\sigp-metric|aigp-metric\s\d+)* + \s*(?P<as_path>as-path\s(prepend\s(last-as\s\d+|\d+(?:\s\d+)*)|tag))* + \s*(?P<automatic_tag>automatic-tag)* + \s*(?P<clns>clns\snext-hop\s\S.*)* + \s*(?P<comm_list>comm-list\s\S+\sdelete)* + \s*(?P<community>community\s\S.*)* + \s*(?P<dampening>dampening\s\d+\s\d+\s\d+\s\d+)* + \s*(?P<default>default\sinterface\s\S.*)* + \s*(?P<extcomm_list>extcomm-list\s\S+\sdelete)* + \s*(?P<extcommunity>extcommunity\s\S.*)* + \s*(?P<global>global)* + \s*(?P<interfaces>interface\s\S.*)* + \s*(?P<level>level\s(level-1-2|level-1|level-2|nssa-only))* + \s*(?P<lisp>lisp\slocator-set\s\S+)* + \s*(?P<local_preference>local-preference\s\d+)* + \s*(?P<metric>metric\s\S.*)* + \s*(?P<metric_type>metric-type\s(external|internal|type-1|type-2))* + \s*(?P<mpls_label>mpls-label)* + \s*(?P<origin>origin\s(igp|incomplete))* + \s*(?P<tag>tag\s(([0-9]{1,3}\.?){4}|\d+))* + \s*(?P<traffic_index>traffic-index\s\d+)* + \s*(?P<vrf>vrf\s\S+)* + \s*(?P<weight>weight\s\d+)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_route_map_set, + "result": { + "{{ route_map }}": { + "{{ action|d() + '_' + sequence|d() }}": { + "entries": { + "set": { + "aigp_metric": { + "value": "{{ aigp_metric.split('aigp-metric ')[1] if aigp_metric is defined and\ + 'igp-metric' not in aigp_metric.split(' ') }}", + "igp_metric": "{{ True if aigp_metric is defined and 'igp-metric' in aigp_metric.split(' ') }}", + }, + "as_path": { + "prepend": { + "as_number": "{{ as_path.split('as-path prepend ')[1].split(' ')\ + if as_path is defined and 'prepend' in as_path and 'last-as' not in as_path }}", + "last_as": "{{ as_path.split('as-path prepend last-as ')[1] if as_path is defined and 'prepend' in as_path and\ + 'last-as' in as_path }}", + }, + "tag": "{{ True if as_path is defined and 'tag' in as_path }}", + }, + "automatic_tag": "{{ True if automatic_tag is defined }}", + "clns": "{{ clns.split('clns next-hop ')[1] if clns is defined }}", + "comm_list": "{{ comm_list.split(' ')[1] if comm_list is defined }}", + "community": { + "number": "{{ community.split(' ')[1:]|reject('in',\ + ['additive','gshut','internet','local-AS','no-advertise','no-export','none']\ + )|join(' ')}}", + "additive": "{{ True if community is defined and 'additive' in community }}", + "gshut": "{{ True if community is defined and 'gshut' in community }}", + "internet": "{{ True if community is defined and 'internet' in community }}", + "local_as": "{{ True if community is defined and 'local-AS' in community }}", + "no_advertise": "{{ True if community is defined and 'no-advertise' in community }}", + "no_export": "{{ True if community is defined and 'no-export' in community }}", + "none": "{{ True if community is defined and 'none' in community }}", + }, + "dampening": { + "penalty_half_time": "{{ dampening.split(' ')[1] if dampening is defined }}", + "reuse_route_val": "{{ dampening.split(' ')[2] if dampening is defined }}", + "suppress_route_val": "{{ dampening.split(' ')[3] if dampening is defined }}", + "max_suppress": "{{ dampening.split(' ')[4] if dampening is defined }}", + }, + "default": "{{ default.split('default interface ')[1] if default is defined }}", + "extcomm_list": "{{ extcomm_list.split(' ')[1] if extcomm_list is defined }}", + "extcommunity": { + "cost": { + "id": "{%- if extcommunity is defined and 'cost' in extcommunity and\ + 'igp' not in extcommunity and 'pre-bestpath' not in extcommunity -%} {{ extcommunity.split(' ')[2] }}\ + {%- elif extcommunity is defined and 'cost' in extcommunity and ('igp' in extcommunity or\ + 'pre-bestpath' in extcommunity) -%} {{ extcommunity.split(' ')[3] }} {%- endif -%}", + "cost_value": "{% if extcommunity is defined and 'cost' in extcommunity and 'igp' not in extcommunity and\ + 'pre-bestpath' not in extcommunity %} {{ extcommunity.split(' ')[3] }}\ + {% elif extcommunity is defined and 'cost' in extcommunity and ('igp' in extcommunity or\ + 'pre-bestpath' in extcommunity) %} {{ extcommunity.split(' ')[4] }} {% endif %}", + "igp": "{{ True if extcommunity is defined and 'cost' in extcommunity and 'igp' in extcommunity }}", + "pre_bestpath": "{{ True if extcommunity is defined and 'cost' in extcommunity and 'pre-bestpath' in extcommunity }}", + }, + "rt": { + "address": "{{ extcommunity.split(' ')[2] if extcommunity is defined and 'rt' in extcommunity and\ + 'range' not in extcommunity }}", + "range": { + "lower_limit": "{{ extcommunity.split('range ')[1].split(' ')[0] if extcommunity is defined and\ + 'rt' in extcommunity and 'range' in extcommunity }}", + "upper_limit": "{{ extcommunity.split('range ')[1].split(' ')[1] if extcommunity is defined and\ + 'rt' in extcommunity and 'range' in extcommunity }}", + }, + "additive": "{{ True if extcommunity is defined and 'rt' in extcommunity and 'additive' in extcommunity }}", + }, + "soo": "{{ extcommunity.split(' ')[2] if extcommunity is defined and 'soo' in extcommunity }}", + "vpn_distinguisher": { + "address": "{{ extcommunity.split(' ')[2] if extcommunity is defined and\ + 'vpn-distinguisher' in extcommunity and 'range' not in extcommunity }}", + "range": { + "lower_limit": "{{ extcommunity.split('range ')[1].split(' ')[0] if extcommunity is defined and\ + 'vpn-distinguisher' in extcommunity and 'range' in extcommunity }}", + "upper_limit": "{{ extcommunity.split('range ')[1].split(' ')[1] if extcommunity is defined and\ + 'vpn-distinguisher' in extcommunity and 'range' in extcommunity }}", + }, + "additive": "{{ True if extcommunity is defined and 'vpn-distinguisher' in extcommunity and\ + 'additive' in extcommunity }}", + }, + }, + "global_route": "{{ True if global is defined }}", + "interfaces": "{{ interfaces.split('interface ')[1].split(' ') if interfaces is defined }}", + "level": { + "level_1": "{{ True if level is defined and 'level-1' in level and 'level-1-2' not in level }}", + "level_1_2": "{{ True if level is defined and 'level-1-2' in level }}", + "level_2": "{{ True if level is defined and 'level-2' in level }}", + "nssa_only": "{{ True if level is defined and 'nssa-only' in level }}", + }, + "lisp": "{{ lisp.split('lisp locator-set ')[1] if lisp is defined }}", + "local_preference": "{{ local_preference.split('local-preference ')[1] if local_preference is defined }}", + "metric": { + "deviation": "{%- if metric is defined and '+' in metric -%}{{ 'plus' }}\ + {%- elif metric is defined and '-' in metric -%}{{ 'minus' }}{%- endif -%}", + "metric_value": "{{ metric.split(' ')[1] if metric is defined and\ + (metric.split(' ')[1] != '+' or metric.split(' ')[1] != '-') }}", + "eigrp_delay": "{% if metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\ + {{ metric.split('+')[1].split(' ')[0] }}\ + {% elif metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\ + {{ metric.split('-')[1].split(' ')[0] }}\ + {% endif %}", + "metric_reliability": "{% if metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\ + {{ metric.split('+')[1].split(' ')[1] }}\ + {% elif metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\ + {{ metric.split('-')[1].split(' ')[1] }}\ + {% endif %}", + "metric_bandwidth": "{% if metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\ + {{ metric.split('+')[1].split(' ')[2] }}\ + {% elif metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\ + {{ metric.split('-')[1].split(' ')[2] }}\ + {% endif %}", + "mtu": "{% if metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\ + {{ metric.split('+')[1].split(' ')[3] }}\ + {% elif metric is defined and metric.split(' ')|length > 2 and '+' in metric %}\ + {{ metric.split('-')[1].split(' ')[3] }}\ + {% endif %}", + }, + "metric_type": { + "external": "{{ True if metric_type is defined and 'external' in metric_type }}", + "internal": "{{ True if metric_type is defined and 'internal' in metric_type }}", + "type_1": "{{ True if metric_type is defined and 'type-1' in metric_type }}", + "type_2": "{{ True if metric_type is defined and 'type-2' in metric_type }}", + }, + "mpls_label": "{{ True if mpls_label is defined }}", + "origin": { + "igp": "{{ True if origin is defined and 'igp' in origin }}", + "incomplete": "{{ True if origin is defined and 'incomplete' in origin }}", + }, + "tag": "{{ tag.split('tag ')[1] if tag is defined }}", + "traffic_index": "{{ traffic_index.split('traffic-index ')[1] if traffic_index is defined }}", + "vrf": "{{ vrf.split('vrf ')[1] if vrf is defined }}", + "weight": "{{ weight.split('weight ')[1] if weight is defined }}", + }, + }, + }, + }, + }, + }, + { + "name": "set.ip", + "getval": re.compile( + r""" + \s+set* + \s*(?P<ip>ip)* + \s*(?P<address>address\sprefix-list\s\S+)* + \s*(?P<default>default)* + \s*(?P<df>df\s\d)* + \s*(?P<global>global\snext-hop\s(verify-availability\s([0-9]{1,3}\.?){4}\s\d+\strack\s\d+|(([0-9]{1,3}\.?){4}).*))* + \s*(?P<precedence>precedence\s(critical|flash|flash-override|immediate|internet|network|priority|routine)|precedence)* + \s*(?P<qos_group>qos_group\s\d+)* + \s*(?P<tos>tos\s(max-reliability|max-throughput|min-delay|min-monetary-cost|normal)|tos)* + \s*(?P<vrf>vrf\s\S+\snext-hop\s\S.*)* + \s*(?P<next_hop>next-hop\s\S.*)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_route_map_set_ip, + "compval": "set", + "result": { + "{{ route_map }}": { + "{{ action|d() + '_' + sequence|d() }}": { + "entries": { + "set": { + "ip": { + "address": "{{ address.split('address prefix-list ')[1] if address is defined }}", + "default": "{{ True if default is defined }}", + "df": "{{ df.split('df ')[1] if df is defined }}", + "global_route": { + "address": "{% if global is defined and 'verify-availability' not in global %}{{ global.split('global next-hop ')[1] }}\ + {% elif global is defined and 'verify-availability' in global %}{{ global.split(' ')[3] }}{% endif %}", + "verify_availability": { + "address": "{{ global.split(' ')[3] if global is defined and 'verify-availability' in global }}", + "sequence": "{{ global.split(' ')[4] if global is defined and 'verify-availability' in global }}", + "track": "{{ global.split('track ')[1] if global is defined and 'verify-availability' in global }}", + }, + }, + "next_hop": { + "address": "{{ next_hop.split('next-hop ')[1] if next_hop is defined and 'peer-address' not in next_hop and\ + 'self' not in next_hop and next_hop.split(' ')|length == 2 }}", + "dynamic": "{{ True if next_hop is defined and 'dynamic dhcp' in next_hop }}", + "encapsulate": "{{ next_hop.split('next-hop encapsulate l3vpn ')[1] if next_hop is defined and\ + 'encapsulate' in next_hop }}", + "peer_address": "{{ True if next_hop is defined and 'peer-address' in next_hop }}", + "recursive": { + "global_route": "{{ True if next_hop is defined and 'global' in next_hop.split(' ') }}", + "vrf": "{{ next_hop.split(' ')[3] if next_hop is defined and 'vrf' in next_hop }}", + "address": "{%- if next_hop is defined and 'global' in next_hop.split(' ') -%}\ + {{ next_hop.split('next-hop recursive global ')[1] }}\ + {%- elif next_hop is defined and 'vrf' in next_hop.split(' ') -%}{{ next_hop.split(' ')[4] }}\ + {%- elif next_hop is defined and 'vrf' not in next_hop.split(' ') and 'global' not in next_hop.split(' ') -%}\ + {{ next_hop.split(' ')[2] }} {%- endif -%}", + }, + "self": "{{ True if next_hop is defined and 'self' in next_hop }}", + "verify_availability": { + "set": "{{ True if next_hop is defined and 'verify-availability' in next_hop and 'track' not in next_hop }}", + "address": "{{ next_hop.split(' ')[2] if next_hop is defined and 'verify-availability' in next_hop and\ + 'track' in next_hop }}", + "sequence": "{{ next_hop.split(' ')[3] if next_hop is defined and 'verify-availability' in next_hop and\ + 'track' in next_hop }}", + "track": "{{ next_hop.split('track ')[1] if next_hop is defined and 'verify-availability' in next_hop and\ + 'track' in next_hop }}", + }, + }, + "precedence": { + "set": "{{ True if precedence is defined and precedence.split(' ')|length == 1 }}", + "critical": "{{ True if precedence is defined and 'critical' in precedence }}", + "flash": "{{ True if precedence is defined and 'flash' in precedence }}", + "flash_override": "{{ True if precedence is defined and 'flash-override' in precedence }}", + "immediate": "{{ True if precedence is defined and 'immediate' in precedence }}", + "internet": "{{ True if precedence is defined and 'internet' in precedence }}", + "network": "{{ True if precedence is defined and 'network' in precedence }}", + "priority": "{{ True if precedence is defined and 'priority' in precedence }}", + "routine": "{{ True if precedence is defined and 'routine' in precedence }}", + }, + "qos_group": "{{ qos_group.split('qos-group ')[1] if qos_group is defined }}", + "tos": { + "set": "{{ True if tos is defined and tos.split(' ')|length == 1 }}", + "max_reliability": "{{ True if tos is defined and 'max-reliability' in tos }}", + "max_throughput": "{{ True if tos is defined and 'max-throughput' in tos }}", + "min_delay": "{{ True if tos is defined and 'min-delay' in tos }}", + "min_monetary_cost": "{{ True if tos is defined and 'min-monetary-cost' in tos }}", + "normal": "{{ True if tos is defined and 'normal' in tos }}", + }, + "vrf": { + "name": "{{ vrf.split(' ')[1] if vrf is defined }}", + "address": "{{ vrf.split('next-hop ')[1] if vrf is defined and 'verify-availability' not in vrf }}", + "verify_availability": { + "set": "{{ True if vrf is defined and 'track' not in vrf }}", + "address": "{{ vrf.split(' ')[4] if vrf is defined and 'track' in vrf }}", + "sequence": "{{ vrf.split(' ')[5] if vrf is defined and 'track' in vrf }}", + "track": "{{ vrf.split('track ')[1] if vrf is defined and 'track' in vrf }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + "name": "set.ipv6", + "getval": re.compile( + r""" + \s+set* + \s*(?P<ipv6>ipv6)* + \s*(?P<address>address\sprefix-list\s\S+)* + \s*(?P<default>default\snext-hop\s(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\S+)* + \s*(?P<global>global\snext-hop\sverify-availability\s(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\S+\s\d+\strack\s\d+)* + \s*(?P<precedence>precedence\s\d+)* + \s*(?P<vrf>vrf\s\S+\snext-hop\sverify-availability\s(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\S+\s\d+\strack\s\d+)* + \s*(?P<next_hop>next-hop\s\S.*)* + $""", + re.VERBOSE, + ), + "setval": _tmplt_route_map_set_ipv6, + "compval": "set", + "result": { + "{{ route_map }}": { + "{{ action|d() + '_' + sequence|d() }}": { + "entries": { + "set": { + "ipv6": { + "address": "{{ address.split('address prefix-list ')[1] if address is defined }}", + "default": "{{ default.split('default next-hop ')[1] if default is defined }}", + "global_route": { + "verify_availability": { + "address": "{{ global.split(' ')[3] if global is defined and 'verify-availability' in global }}", + "sequence": "{{ global.split(' ')[4] if global is defined and 'verify-availability' in global }}", + "track": "{{ global.split('track ')[1] if global is defined and 'verify-availability' in global }}", + }, + "address": "{{ global.split(' ')[2] if global is defined and 'verify-availability' not in global }}", + }, + "next_hop": { + "address": "{{ next_hop.split('next-hop ')[1] if next_hop is defined and\ + next_hop.split(' ')|length == 2 and 'peer-address' not in next_hop }}", + "encapsulate": "{{ next_hop.split('next-hop encapsulate l3vpn ')[1] if next_hop is defined and\ + 'encapsulate' in next_hop}}", + "peer_address": "{{ True if next_hop is defined and next_hop.split(' ')|length == 2 and 'peer-address' in next_hop }}", + "recursive": "{{ next_hop.split('next-hop recursive ')[1] if next_hop is defined }}", + }, + "precedence": "{{ precedence.split(' ')[1] if precedence is defined }}", + "vrf": { + "name": "{{ vrf.split(' ')[1] if vrf is defined }}", + "verify_availability": { + "address": "{{ vrf.split(' ')[4] if vrf is defined and 'verify-availability' in vrf }}", + "sequence": "{{ vrf.split(' ')[5] if vrf is defined and 'verify-availability' in vrf }}", + "track": "{{ vrf.split('track ')[1] if vrf is defined and 'verify-availability' in vrf }}", + }, + }, + }, + }, + }, + }, + }, + }, + }, + ] diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/service.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/service.py new file mode 100644 index 000000000..992892db8 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/service.py @@ -0,0 +1,457 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Service parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +class ServiceTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(ServiceTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + # fmt: off + PARSERS = [ + { + "name": "call_home", + "getval": re.compile( + r""" + ^service\s(?P<call_home>call-home) + """, re.VERBOSE, + ), + "setval": "service call-home", + "result": { + "call_home": "{{ not not call_home }}", + }, + }, + { + "name": "compress_config", + "getval": re.compile( + r""" + ^service\s(?P<compress_config>compress-config) + """, re.VERBOSE, + ), + "setval": "service compress-config", + "result": { + "compress_config": "{{ not not compress_config }}", + }, + }, + { + "name": "config", + "getval": re.compile( + r""" + ^service\s(?P<config>config) + """, re.VERBOSE, + ), + "setval": "service config", + "result": { + "config": "{{ not not config }}", + }, + }, + { + "name": "counters", + "getval": re.compile( + r""" + ^service\scounters\smax\sage(\s(?P<max_age>\d+))? + """, re.VERBOSE, + ), + "setval": "service counters max age {{ counters }}", + "result": { + "counters": "{{ max_age|int }}", + }, + }, + { + "name": "dhcp", + "getval": re.compile( + r""" + ^service\sdhcp + """, re.VERBOSE, + ), + "setval": "service dhcp", + "result": { + "dhcp": True, + }, + }, + { + "name": "disable_ip_fast_frag", + "getval": re.compile( + r""" + ^service\s(?P<disable_ip_fast_frag>disable-ip-fast-frag) + """, re.VERBOSE, + ), + "setval": "service disable-ip-fast-frag", + "result": { + "disable_ip_fast_frag": "{{ not not disable_ip_fast_frag }}", + }, + }, + { + "name": "exec_callback", + "getval": re.compile( + r""" + ^service\s(?P<exec_callback>exec-callback) + """, re.VERBOSE, + ), + "setval": "service exec-callback", + "result": { + "exec_callback": "{{ not not exec_callback }}", + }, + }, + { + "name": "exec_wait", + "getval": re.compile( + r""" + ^service\s(?P<exec_wait>exec-wait) + """, re.VERBOSE, + ), + "setval": "service exec-wait", + "return": { + "exec_wait": "{{ not not exec_wait }}", + }, + }, + { + "name": "hide_telnet_addresses", + "getval": re.compile( + r""" + ^service\s(?P<hide_telnet_addresses>hide-telnet-addresses) + """, re.VERBOSE, + ), + "setval": "service hide-telnet-addresses", + "result": { + "hide_telnet_addresses": "{{ not not hide_telnet_addresses }}", + }, + }, + { + "name": "internal", + "getval": re.compile( + r""" + ^service\s(?P<internal>internal) + """, re.VERBOSE, + ), + "setval": "service internal", + "result": { + "internal": "{{ not not internal }}", + }, + }, + { + "name": "linenumber", + "getval": re.compile( + r""" + ^service\s(?P<linenumber>linenumber) + """, re.VERBOSE, + ), + "setval": "service linenumber", + "result": { + "linenumber": "{{ not not linenumber }}", + }, + }, + { + "name": "log", + "getval": re.compile( + r""" + ^service\slog(\s(?P<backtrace>backtrace))? + """, re.VERBOSE, + ), + "setval": "service log backtrace", + "result": { + "log": "{{ not not backtrace }}", + }, + }, + { + "name": "log_hidden", + "getval": re.compile( + r""" + ^service\s(?P<log_hidden>log-hidden) + """, re.VERBOSE, + ), + "setval": "service log-hidden", + "result": { + "log_hidden": "{{ not not log_hidden }}", + }, + }, + { + "name": "nagle", + "getval": re.compile( + r""" + ^service\s(?P<nagle>nagle) + """, re.VERBOSE, + ), + "setval": "service nagle", + "result": { + "nagle": "{{ not not nagle }}", + }, + }, + { + "name": "old_slip_prompts", + "getval": re.compile( + r""" + ^service\s(?P<old_slip_prompts>old-slip-prompts) + """, re.VERBOSE, + ), + "setval": "service old-slip-prompts", + "result": { + "old_slip_prompts": "{{ not not old_slip_prompts }}", + }, + }, + { + "name": "pad", + "getval": re.compile( + r""" + ^service\spad$ + """, re.VERBOSE, + ), + "setval": "service pad", + "result": { + "pad": True, + }, + }, + { + "name": "pad_cmns", + "getval": re.compile( + r""" + ^service\spad\scmns + """, re.VERBOSE, + ), + "setval": "service pad cmns", + "result": { + "pad_cmns": True, + }, + }, + { + "name": "pad_from_xot", + "getval": re.compile( + r""" + ^service\spad\sfrom-xot + """, re.VERBOSE, + ), + "setval": "service pad from-xot", + "result": { + "pad_from_xot": True, + }, + }, + { + "name": "pad_to_xot", + "getval": re.compile( + r""" + ^service\spad\sto-xot + """, re.VERBOSE, + ), + "setval": "service pad to-xot", + "result": { + "pad_to_xot": True, + }, + }, + { + "name": "password_encryption", + "getval": re.compile( + r""" + ^service\s(?P<password_encryption>password-encryption) + """, re.VERBOSE, + ), + "setval": "service password-encryption", + "result": { + "password_encryption": "{{ not not password_encryption }}", + }, + }, + { + "name": "password_recovery", + "getval": re.compile( + r""" + ^service\spassword-recovery + """, re.VERBOSE, + ), + "setval": "service password-recovery", + "remval": "service password-recovery\nyes", + "result": { + "password_recovery": True, + }, + }, + { + "name": "prompt", + "getval": re.compile( + r""" + ^service\sprompt\sconfig + """, re.VERBOSE, + ), + "setval": "service prompt config", + "result": { + "prompt": True, + }, + }, + { + "name": "private_config_encryption", + "getval": re.compile( + r""" + ^service\sprivate-config-encryption + """, re.VERBOSE, + ), + "setval": "service private-config-encryption", + "result": { + "private_config_encryption": True, + }, + }, + { + "name": "pt_vty_logging", + "getval": re.compile( + r""" + ^service\s(?P<pt_vty_logging>pt-vty-logging) + """, re.VERBOSE, + ), + "setval": "service pt-vty-logging", + "result": { + "pt_vty_logging": "{{ not not pt_vty_logging }}", + }, + }, + { + "name": "scripting", + "getval": re.compile( + r""" + ^service\s(?P<scripting>scripting) + """, re.VERBOSE, + ), + "setval": "service scripting", + "result": { + "scripting": "{{ not not scripting }}", + }, + }, + { + "name": "sequence_numbers", + "getval": re.compile( + r""" + ^service\s(?P<sequence_numbers>sequence-numbers) + """, re.VERBOSE, + ), + "setval": "service sequence-numbers", + "result": { + "sequence_numbers": "{{ not not sequence_numbers }}", + }, + }, + { + "name": "slave_coredump", + "getval": re.compile( + r""" + ^service\s(?P<slave_coredump>slave-coredump) + """, re.VERBOSE, + ), + "setval": "service slave-coredump", + "result": { + "slave_coredump": "{{ not not slave_coredump }}", + }, + }, + { + "name": "slave_log", + "getval": re.compile( + r""" + ^service\sslave-log + """, re.VERBOSE, + ), + "setval": "service slave-log", + "result": { + "slave_log": True, + }, + }, + { + "name": "tcp_keepalives_in", + "getval": re.compile( + r""" + ^service\stcp-keepalives-in + """, re.VERBOSE, + ), + "setval": "service tcp-keepalives-in", + "result": { + "tcp_keepalives_in": True, + }, + }, + { + "name": "tcp_keepalives_out", + "getval": re.compile( + r""" + ^service\stcp-keepalives-out + """, re.VERBOSE, + ), + "setval": "service tcp-keepalives-out", + "result": { + "tcp_keepalives_out": True, + }, + }, + { + "name": "telnet_zeroidle", + "getval": re.compile( + r""" + ^service\stelnet-zeroidle + """, re.VERBOSE, + ), + "setval": "service telnet-zeroidle", + "result": { + "telnet_zeroidle": True, + }, + }, + { + "name": "timestamps", + "getval": re.compile( + r""" + ^service\stimestamps + (\s(?P<msg>\S+))? + (\s(?P<timestamp>\S+))? + (\s(?P<msec>msec))? + (\s(?P<localtime>localtime))? + (\s(?P<show_timezone>show-timezone))? + (\s(?P<year>year))? + """, re.VERBOSE, + ), + "remval": "service timestamps{{ (' ' + msg) if msg is defined else '' }}", + "setval": "service timestamps" + "{{ (' ' + msg) if msg is defined else '' }}" + "{% if msg is defined %}" + "{{ (' ' + timestamp) if timestamp is defined else '' }}" + "{% if timestamp == 'datetime' and datetime_options is defined %}" + "{{ ' msec' if datetime_options.msec else '' }}" + "{{ ' localtime' if datetime_options.localtime else '' }}" + "{{ ' show-timezone' if datetime_options.show_timezone else '' }}" + "{{ ' year' if datetime_options.year else '' }}" + "{% endif %}" + "{% endif %}" + "", + "result": { + "timestamps": [ + { + "msg": "{{ msg if msg is defined else 'debug' }}", + "timestamp": "{{ timestamp if timestamp is defined else 'uptime' }}", + "datetime_options": { + "msec": "{{ True if msec else False}}", + "localtime": "{{ True if localtime else False }}", + "show_timezone": "{{ True if show_timezone else False }}", + "year": "{{ True if year else False }}", + }, + }, + ], + }, + }, + { + "name": "unsupported_transceiver", + "getval": re.compile( + r""" + ^service\sunsupported-transceiver + """, re.VERBOSE, + ), + "setval": "service unsupported-transceiver", + "result": { + "unsupported_transceiver": True, + }, + }, + ] diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/snmp_server.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/snmp_server.py new file mode 100644 index 000000000..87f9e7c07 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/snmp_server.py @@ -0,0 +1,1964 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Snmp_server parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +def cmd_option_engine_id(config_data): + cmd = "" + if config_data: + cmd = "snmp-server engineID " + if config_data.get("local"): + cmd += "local" + if config_data.get("remote"): + rm = config_data.get("remote") + if rm.get("host"): + cmd += "remote {host}".format(host=rm.get("host")) + if rm.get("udp_port"): + cmd += " udp-port {udp_port}".format(udp_port=rm.get("udp_port")) + if rm.get("vrf"): + cmd += " vrf {vrf}".format(vrf=rm.get("vrf")) + if config_data.get("id"): + cmd += " {id}".format(id=config_data.get("id")) + return cmd + + +def cmd_option_file_transfer(config_data): # contain sub list attr + cmd = "" + if config_data.get("file_transfer"): + conf = config_data.get("file_transfer") + cmd = "snmp-server file-transfer" + if conf.get("access_group"): + cmd += " access-group {ag}".format(ag=conf.get("access_group")) + if conf.get("protocol"): + cmd += " protocol" + for protocol in list(conf.get("protocol").keys()): + cmd += " {protocol}".format(protocol=protocol) + return cmd + + +def cmd_option_hosts(config_data): # contain sub list attr + cmd = "" + if config_data: + cmd = "snmp-server host" + if config_data.get("host"): + cmd += " {host}".format(host=config_data.get("host")) + if config_data.get("informs"): + cmd += " informs" + if config_data.get("version"): + cmd += " version {version}".format(version=config_data.get("version")) + if config_data.get("version_option"): + cmd += " {version}".format(version=config_data.get("version_option")) + if config_data.get("vrf"): + cmd += " vrf {vrf}".format(vrf=config_data.get("vrf")) + if config_data.get("community_string"): + cmd += " {community_string}".format( + community_string=config_data.get("community_string"), + ) + if config_data.get("traps"): + for protocol in list(config_data.get("traps").keys()): + cmd += " {protocol}".format(protocol=protocol) + return cmd + + +def cmd_option_trap_bgp(config_data): + cmd = "" + conf = config_data.get("traps", {}).get("bgp", {}) + if conf: + if conf.get("enable"): + cmd += "snmp-server enable traps bgp" + if conf.get("cbgp2"): + cmd += " cbgp2" + if conf.get("state_changes"): + if conf.get("state_changes").get("enable"): + cmd += " state-changes" + if conf.get("state_changes").get("all"): + cmd += " all" + if conf.get("state_changes").get("backward_trans"): + cmd += " backward-trans" + if conf.get("state_changes").get("limited"): + cmd += " limited" + if conf.get("threshold"): + cmd += " threshold" + if conf.get("threshold").get("prefix"): + cmd += " prefix" + return cmd + + +class Snmp_serverTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Snmp_serverTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + # fmt: off + PARSERS = [ + { + "name": "accounting", + "getval": re.compile( + r""" + ^snmp-server\saccounting\scommands + (\s(?P<command>\S+))? + """, re.VERBOSE, + ), + "setval": "snmp-server accounting commands {{ accounting.command }}", + "result": { + "accounting": { + "command": "{{ command }}", + }, + }, + }, + { + "name": "cache", + "getval": re.compile( + r""" + ^snmp-server\scache\sinterval + (\s(?P<interval>\d+))? + """, re.VERBOSE, + ), + "setval": "snmp-server cache interval {{ cache }}", + "result": { + "cache": "{{ interval }}", + }, + }, + { + "name": "chassis_id", + "getval": re.compile( + r""" + ^snmp-server\schassis-id + (\s(?P<uqString>.+$))? + """, re.VERBOSE, + ), + "setval": "snmp-server chassis-id {{ chassis_id }}", + "result": { + "chassis_id": "{{ uqString }}", + }, + }, + { + "name": "communities", + "getval": re.compile( + r""" + ^snmp-server\scommunity + (\s(?P<name>\S+))? + (\sview\s(?P<view>\S+))? + (\s(?P<ro>RO))? + (\s(?P<rw>RW))? + (\sipv6\s(?P<acl_v6>\S+))? + (\s(?P<acl_v4>\S+|\d+))? + """, re.VERBOSE, + ), + "setval": "snmp-server community " + "{{ name }}" + "{{ (' view ' + view) if view is defined else '' }}" + "{{ ' ro' if ro|d(False) else ''}}" + "{{ ' rw' if rw|d(False) else ''}}" + "{{ (' ipv6 ' + acl_v6) if acl_v6 is defined else '' }}" + "{{ (' ' + acl_v4) if acl_v4 is defined else '' }}", + "result": { + "communities": [ + { + "name": "{{ name }}", + "view": "{{ view }}", + "ro": "{{ not not ro }}", + "rw": "{{ not not rw }}", + "acl_v6": "{{ acl_v6 }}", + "acl_v4": "{{ acl_v4 }}", + }, + ], + }, + }, + { + "name": "contact", + "getval": re.compile( + r""" + ^snmp-server\scontact + (\s(?P<contact>.+$))? + """, re.VERBOSE, + ), + "setval": "snmp-server contact {{ contact }}", + "result": { + "contact": "{{ contact }}", + }, + }, + { + "name": "context", + "getval": re.compile( + r""" + ^snmp-server\scontext + (\s(?P<context>\S+))? + """, re.VERBOSE, + ), + "setval": "snmp-server context {{ context }}", + "result": { + "context": ["{{ context }}"], + }, + }, + { + "name": "drop", + "getval": re.compile( + r""" + ^snmp-server\sdrop + (\s(?P<vrf_traffic>vrf-traffic))? + (\s(?P<unknown_user>unknown-user))? + """, re.VERBOSE, + ), + "setval": "snmp-server drop" + "{{ ' vrf-traffic' if drop.vrf_traffic is defined else '' }}" + "{{ ' unknown-user' if drop.unknown_user is defined else '' }}", + "result": { + "drop": { + "vrf_traffic": "{{ not not vrf_traffic }}", + "unknown_user": "{{ not not unknown_user }}", + }, + }, + }, + { + "name": "engine_id", + "getval": re.compile( + r""" + ^snmp-server\sengineID + (\s(?P<local>local))? + (\sremote\s(?P<remotehost>\S+))? + (\sudp-port\s(?P<udp_port>\d+))? + (\svrf\s(?P<vrf>\S+))? + (\s(?P<id>\S+))? + """, re.VERBOSE, + ), + "setval": cmd_option_engine_id, + "result": { + "engine_id": [ + { + "id": "'{{ id }}'", + "local": "{{ not not local }}", + "remote": { + "host": "{{ remotehost }}", + "udp_port": "{{ udp_port }}", + "vrf": "{{ vrf }}", + }, + }, + ], + }, + }, + { + "name": "file_transfer", + "getval": re.compile( + r""" + ^snmp-server\sfile-transfer + (\saccess-group\s(?P<access_group>\S+))? + (\sprotocol\s(?P<protocol>ftp|rcp|tftp))? + """, re.VERBOSE, + ), + "setval": cmd_option_file_transfer, + "result": { + "file_transfer": { + "access_group": "{{ access_group }}", + "protocol": ["{{ protocol }}"], + }, + }, + }, + { + "name": "groups", + "getval": re.compile( + r""" + ^snmp-server\sgroup + (\s(?P<group>\S+))? + (\s(?P<version>v1|v3|v2c))? + (\s(?P<version_option>auth|noauth|priv))? + (\scontext\s(?P<context>\S+))? + (\sread\s(?P<read>\S+))? + (\swrite\s(?P<write>\S+))? + (\snotify\s(?P<notify>\S+))? + (\saccess\s(?P<acl_v4>\S+))? + (\saccess\sipv6\s(?P<acl_v6>\S+))? + """, re.VERBOSE, + ), + "setval": "snmp-server group " + "{{ group if group is defined else '' }}" + "{{ (' ' + version) if version is defined else '' }}" + "{{ (' ' + version_option) if version_option is defined else '' }}" + "{{ (' context ' + context) if context is defined else '' }}" + "{{ (' read ' + read) if read is defined else '' }}" + "{{ (' write ' + write) if write is defined else '' }}" + "{{ (' notify ' + notify) if notify is defined else '' }}" + "{{ (' access ' + acl_v4) if acl_v4 is defined else '' }}" + "{{ (' access ipv6 ' + acl_v6) if acl_v6 is defined else '' }}", + "result": { + "groups": [ + { + "group": "{{ group }}", + "version": "{{ version }}", + "version_option": "{{ version_option }}", + "context": "{{ context }}", + "notify": "{{ notify }}", + "read": "{{ read }}", + "write": "{{ write }}", + "acl_v4": "{{ acl_v4 }}", + "acl_v6": "{{ acl_v6 }}", + }, + ], + }, + }, + { + "name": "hosts", + "getval": re.compile( + r""" + ^snmp-server\shost + (\s(?P<host>\S+))? + (\s(?P<informs>informs))? + (\sversion\s(?P<version>1|3|2c))? + (\s(?P<version_option>auth|noauth|priv))? + (\svrf\s(?P<vrf>\S+))? + (\s(?P<community_string>\S+))? + (\s+(?P<traps>.+$))? + """, re.VERBOSE, + ), + "setval": cmd_option_hosts, + "result": { + "hosts": [ + { + "host": "{{ host }}", + "informs": "{{ not not informs }}", + "community_string": "{{ community_string }}", + "traps": "{{ traps }}", + "version": "{{ version }}", + "version_option": "{{ version_option }}", + "vrf": "{{ vrf }}", + }, + ], + }, + }, + { + "name": "password_policy", + "getval": re.compile( + r""" + ^snmp-server\spassword-policy + (\s(?P<policy_name>\S+))? + (\s(?P<define>define))? + (\suser\s(?P<username>\S+))? + (\smin-len\s(?P<min_len>\d+))? + (\smax-len\s(?P<max_len>\d+))? + (\supper-case\s(?P<upper_case>\d+))? + (\slower-case\s(?P<lower_case>\d+))? + (\sspecial-char\s(?P<special_char>\d+))? + (\sdigits\s(?P<digits>\d+))? + (\schange\s(?P<change>\d+))? + """, re.VERBOSE, + ), + "setval": "snmp-server password-policy " + "{{ policy_name if policy_name is defined else '' }}" + "{{ (' user ' + username) if username is defined else ' define' }}" + "{{ (' min-len ' + min_len|string) if min_len is defined else '' }}" + "{{ (' max-len ' + max_len|string) if max_len is defined else '' }}" + "{{ (' upper-case ' + upper_case|string) if upper_case is defined else '' }}" + "{{ (' lower-case ' + lower_case|string) if lower_case is defined else '' }}" + "{{ (' special-char ' + special_char|string) if special_char is defined else '' }}" + "{{ (' digits ' + digits|string) if digits is defined else '' }}" + "{{ (' change ' + change|string) if change is defined else '' }}", + "result": { + "password_policy": [ + { + "policy_name": "{{ policy_name }}", + "username": "{{ username }}", + "min_len": "{{ min_len }}", + "max_len": "{{ max_len }}", + "upper_case": "{{ upper_case }}", + "lower_case": "{{ lower_case }}", + "special_char": "{{ special_char }}", + "change": "{{ change }}", + "digits": "{{ digits }}", + }, + ], + }, + }, + { + "name": "users", + "getval": re.compile( + r""" + ^snmp-server\suser + (\s(?P<username>\S+))? + (\s(?P<group>\S+))? + (\sremote\s(?P<remote>\S+))? + (\sudp-port\s(?P<udp_port>\d+))? + (\s(?P<version>v1|v3|v2c))? + (\s(?P<version_option>auth|encrypted))? + (\saccess\sipv6\s(?P<acl_v6>\S+))? + (\saccess\s(?P<acl_v4>\S+|\d+))? + (\svrf\s(?P<vrf>\S+))? + """, re.VERBOSE, + ), + "setval": "snmp-server user " + "{{ username if username is defined else '' }}" + "{{ (' ' + group) if group is defined else '' }}" + "{{ (' remote ' + remote) if remote is defined else '' }}" + "{{ (' udp-port ' + udp_port|string) if udp_port is defined else '' }}" + "{{ (' ' + version) if version is defined else '' }}" + "{{ (' ' + version_option) if version_option is defined else '' }}" + "{{ (' auth ' + authentication.algorithm) if authentication is defined and authentication.algorithm is defined else '' }}" + "{{ (' ' + authentication.password) if authentication is defined and authentication.password is defined else '' }}" + "{{ (' priv ' + encryption.priv) if encryption is defined and encryption.priv is defined else '' }}" + "{{ (' ' + encryption.priv_option) if encryption is defined and encryption.priv_option is defined else '' }}" + "{{ (' ' + encryption.password) if encryption is defined and encryption.password is defined else '' }}" + "{{ (' access ' + acl_v4|string) if acl_v4 is defined else '' }}" + "{{ (' access ipv6 ' + acl_v6) if acl_v6 is defined else '' }}" + "{{ (' vrf ' + vrf) if vrf is defined else '' }}", + "result": { + "users": [ + { + "username": "{{ username }}", + "group": "{{ group }}", + "remote": "{{ remote }}", + "udp_port": "{{ udp_port }}", + "version": "{{ version }}", + "version_option": "{{ version_option }}", + "acl_v4": "{{ acl_v4 }}", + "acl_v6": "{{ acl_v6 }}", + "vrf": "{{ vrf }}", + }, + ], + }, + }, + { + "name": "views", + "getval": re.compile( + r""" + ^snmp-server\sview + (\s(?P<name>\S+))? + (\s(?P<family_name>[-\w]+))? + (\s(?P<excluded>excluded))? + (\s(?P<included>included))? + $""", re.VERBOSE, + ), + "setval": "snmp-server view " + "{{ name if name is defined else '' }}" + "{{ (' ' + family_name) if family_name is defined else '' }}" + "{{ ' excluded' if excluded is defined else '' }}" + "{{ ' included' if included is defined else '' }}", + "result": { + "views": [ + { + "name": "{{ name }}", + "family_name": "{{ family_name }}", + "excluded": "{{ not not excluded }}", + "included": "{{ not not included }}", + }, + ], + }, + }, + { + "name": "if_index", + "getval": re.compile( + r""" + ^snmp-server\sifindex + (\s(?P<if_index>persist))? + """, re.VERBOSE, + ), + "setval": "snmp-server ifindex persist", + "result": { + "if_index": "{{ not not if_index }}", + }, + }, + { + "name": "inform", + "getval": re.compile( + r""" + ^snmp-server\sinform + (\spending\s(?P<pending>\d+))? + (\sretries\s(?P<retries>\d+))? + (\stimeout\s(?P<timeout>\d+))? + """, re.VERBOSE, + ), + "setval": "snmp-server inform" + "{{ (' pending ' + inform.pending|string) if inform.pending is defined else '' }}" + "{{ (' retries ' + inform.retries|string) if inform.retries is defined else '' }}" + "{{ (' timeout ' + inform.timeout|string) if inform.timeout is defined else '' }}", + "result": { + "inform": { + "pending": "{{ pending }}", + "retries": "{{ retries }}", + "timeout": "{{ timeout }}", + }, + }, + }, + { + "name": "ip", + "getval": re.compile( + r""" + ^snmp-server\sip\sdscp + (\s(?P<dscp>\d+))? + (\sprecedence(?P<precedence>\d+))? + """, re.VERBOSE, + ), + "setval": "snmp-server ip dscp " + "{{ (ip.dscp|string) if ip.dscp is defined else '' }}" + "{{ (' precedence ' + ip.precedence) if ip.precedence is defined else '' }}", + "result": { + "ip": { + "dscp": "{{ dscp }}", + "precedence": "{{ precedence }}", + }, + }, + }, + { + "name": "location", + "getval": re.compile( + r""" + ^snmp-server\slocation + (\s(?P<location>.+))? + """, re.VERBOSE, + ), + "setval": "snmp-server location {{ location }}", + "result": { + "location": "{{ location }}", + }, + }, + { + "name": "manager", + "getval": re.compile( + r""" + ^snmp-server\smanager + (\ssession-timeout\s(?P<manager>\d+))? + """, re.VERBOSE, + ), + "setval": "snmp-server manager session-timeout {{ manager }}", + "result": { + "manager": "{{ manager }}", + }, + }, + { + "name": "packet_size", + "getval": re.compile( + r""" + ^snmp-server\spacketsize + (\s(?P<packet_size>\d+))? + """, re.VERBOSE, + ), + "setval": "snmp-server packetsize {{ packet_size }}", + "result": { + "packet_size": "{{ packet_size }}", + }, + }, + { + "name": "queue_length", + "getval": re.compile( + r""" + ^snmp-server\squeue-length + (\s(?P<queue_length>\d+))? + """, re.VERBOSE, + ), + "setval": "snmp-server queue-length {{ queue_length }}", + "result": { + "queue_length": "{{ queue_length }}", + }, + }, + { + "name": "trap_timeout", + "getval": re.compile( + r""" + ^snmp-server\strap\stimeout + (\s(?P<ttimeout>\d+))? + """, re.VERBOSE, + ), + "setval": "snmp-server trap timeout {{ trap_timeout }}", + "result": { + "trap_timeout": "{{ ttimeout }}", + }, + }, + { + "name": "source_interface", + "getval": re.compile( + r""" + ^snmp-server\ssource-interface + (\sinforms\s(?P<interface>\S+))? + """, re.VERBOSE, + ), + "setval": "snmp-server source-interface informs {{ source_interface }}", + "result": { + "source_interface": "{{ interface }}", + }, + }, + { + "name": "trap_source", + "getval": re.compile( + r""" + ^snmp-server\strap-source + (\s(?P<interface>\S+))? + """, re.VERBOSE, + ), + "setval": "snmp-server trap-source {{ trap_source }}", + "result": { + "trap_source": "{{ interface }}", + }, + }, + + { # only traps + "name": "traps.auth_framework", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sauth-framework + (\s(?P<sec_violation>sec-violation))? + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps" + "{{ (' auth-framework') if traps.auth_framework.enable is defined else '' }}" + "{{ (' sec-violation') if traps.auth_framework.sec_violation is defined else '' }}", + "result": { + "traps": { + "auth_framework": { + "enable": True, + "sec_violation": "{{ not not excluded }}", + }, + }, + }, + }, + { + "name": "traps.bfd", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sbfd + (\s(?P<session_down>session-down))? + (\s(?P<session_up>session-up))? + """, re.VERBOSE, + ), + "setval": "{{ 'snmp-server enable traps bfd' if traps.bfd.enable is defined else '' }}" + "{{ (' session-down') if traps.bfd.session_down is defined else '' }}" + "{{ (' session-up') if traps.bfd.session_up is defined else '' }}", + "result": { + "traps": { + "bfd": { + "enable": True, + "session_down": "{{ not not session_down }}", + "session_up": "{{ not not session_up }}", + }, + }, + }, + }, + { + "name": "traps.bgp", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sbgp + (\s(?P<cbgp2>cbgp2))? + (\s(?P<state_changes>state-changes))? + (\s(?P<all>all))? + (\s(?P<backward_trans>backward-trans))? + (\s(?P<limited>limited))? + (\sthreshold(?P<prefix>prefix))? + """, re.VERBOSE, + ), + "setval": cmd_option_trap_bgp, + "remval": "snmp-server enable traps bgp", + "result": { + "traps": { + "bgp": { + "cbgp2": "{{ not not cbgp2 }}", + "enable": True, + "state_changes": { + "enable": "{{ not not state_changes }}", + "all": "{{ not not all }}", + "backward_trans": "{{ not not backward_trans }}", + "limited": "{{ not not limited }}", + }, + "threshold": { + "prefix": "{{ not not prefix }}", + }, + }, + }, + }, + }, + { + "name": "traps.bridge", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sbridge + (\s(?P<newroot>newroot))? + (\s(?P<topologychange>topologychange))? + """, re.VERBOSE, + ), + "setval": "{{ 'snmp-server enable traps bridge' if traps.bridge.enable is defined else '' }}" + "{{ (' newroot') if traps.bridge.newroot|d(False) is defined else '' }}" + "{{ (' topologychange') if traps.bridge.topologychange|d(False) is defined else '' }}", + "result": { + "traps": { + "bridge": { + "newroot": "{{ not not newroot }}", + "enable": True, + "topologychange": "{{ not not topologychange }}", + }, + }, + }, + }, + { + "name": "traps.casa", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\scasa + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps casa", + "result": { + "traps": { + "casa": True, + }, + }, + }, + { + "name": "traps.cnpd", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\scnpd + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps cnpd", + "result": { + "traps": { + "cnpd": True, + }, + }, + }, + { + "name": "traps.config", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sconfig + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps config", + "result": { + "traps": { + "config": True, + }, + }, + }, + { + "name": "traps.isis", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sisis$ + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps isis", + "result": { + "traps": { + "isis": True, + }, + }, + }, + { + "name": "traps.config_copy", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sconfig-copy + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps config-copy", + "result": { + "traps": { + "config_copy": True, + }, + }, + }, + { + "name": "traps.config_ctid", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sconfig-ctid + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps config-ctid", + "result": { + "traps": { + "config_ctid": True, + }, + }, + }, + { + "name": "traps.dhcp", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sdhcp + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps dhcp", + "result": { + "traps": { + "dhcp": True, + }, + }, + }, + { + "name": "traps.eigrp", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\seigrp + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps eigrp", + "result": { + "traps": { + "eigrp": True, + }, + }, + }, + { + "name": "traps.entity", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sentity + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps entity", + "result": { + "traps": { + "entity": True, + }, + }, + }, + { + "name": "traps.energywise", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\senergywise$ + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps energywise", + "result": { + "traps": { + "energywise": True, + }, + }, + }, + { + "name": "traps.event_manager", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sevent-manager + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps event-manager", + "result": { + "traps": { + "event_manager": True, + }, + }, + }, + { + "name": "traps.flowmon", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sflowmon + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps flowmon", + "result": { + "traps": { + "flowmon": True, + }, + }, + }, + { + "name": "traps.fru_ctrl", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sfru-ctrl + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps fru-ctrl", + "result": { + "traps": { + "fru_ctrl": True, + }, + }, + }, + { + "name": "traps.hsrp", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\shsrp + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps hsrp", + "result": { + "traps": { + "hsrp": True, + }, + }, + }, + { + "name": "traps.ipsla", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sipsla + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ipsla", + "result": { + "traps": { + "ipsla": True, + }, + }, + }, + { + "name": "traps.msdp", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\smsdp$ + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps msdp", + "result": { + "traps": { + "msdp": True, + }, + }, + }, + { + "name": "traps.pw_vc", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\spw\svc$ + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps pw vc", + "result": { + "traps": { + "pw_vc": True, + }, + }, + }, + { + "name": "traps.mvpn", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\smvpn + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps mvpn", + "result": { + "traps": { + "mvpn": True, + }, + }, + }, + { + "name": "traps.mpls_vpn", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\smpls\svpn + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps mpls vpn", + "result": { + "traps": { + "mpls_vpn": True, + }, + }, + }, + { + "name": "traps.pki", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\spki + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps pki", + "result": { + "traps": { + "pki": True, + }, + }, + }, + { + "name": "traps.rsvp", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\srsvp + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps rsvp", + "result": { + "traps": { + "rsvp": True, + }, + }, + }, + { + "name": "traps.syslog", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\ssyslog + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps syslog", + "result": { + "traps": { + "syslog": True, + }, + }, + }, + { + "name": "traps.transceiver_all", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\stransceiver\sall + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps transceiver all", + "result": { + "traps": { + "transceiver_all": True, + }, + }, + }, + { + "name": "traps.tty", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\stty + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps tty", + "result": { + "traps": { + "tty": True, + }, + }, + }, + { + "name": "traps.envmon.shutdown", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\senvmon\sshutdown$ + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps envmon shutdown", + "result": { + "traps": { + "envmon": { + "shutdown": True, + }, + }, + }, + }, + { + "name": "traps.envmon.status", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\senvmon\sstatus$ + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps envmon status", + "result": { + "traps": { + "envmon": { + "status": True, + }, + }, + }, + }, + { + "name": "traps.envmon.supply", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\senvmon\ssupply$ + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps envmon supply", + "result": { + "traps": { + "envmon": { + "supply": True, + }, + }, + }, + }, + { + "name": "traps.envmon.temperature", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\senvmon\stemperature$ + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps envmon temperature", + "result": { + "traps": { + "envmon": { + "temperature": True, + }, + }, + }, + }, + { + "name": "traps.envmon.fan.enable", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\senvmon\sfan$ + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps envmon fan", + "result": { + "traps": { + "envmon": { + "fan": { + "enable": True, + }, + }, + }, + }, + }, + { + "name": "traps.envmon.fan.shutdown", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\senvmon\sfan\sshutdown$ + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps envmon fan shutdown", + "result": { + "traps": { + "envmon": { + "fan": { + "shutdown": True, + }, + }, + }, + }, + }, + { + "name": "traps.envmon.fan.status", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\senvmon\sfan\sstatus$ + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps envmon fan status", + "result": { + "traps": { + "envmon": { + "fan": { + "status": True, + }, + }, + }, + }, + }, + { + "name": "traps.envmon.fan.supply", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\senvmon\sfan\ssupply$ + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps envmon fan supply", + "result": { + "traps": { + "envmon": { + "fan": { + "supply": True, + }, + }, + }, + }, + }, + { + "name": "traps.envmon.fan.temperature", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\senvmon\sfan\stemperature$ + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps envmon fan temperature", + "result": { + "traps": { + "envmon": { + "fan": { + "temperature": True, + }, + }, + }, + }, + }, + { + "name": "traps.vrrp", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\svrrp + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps vrrp", + "result": { + "traps": { + "vrrp": True, + }, + }, + }, + { + "name": "traps.ipmulticast", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sipmulticast + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ipmulticast", + "result": { + "traps": { + "ipmulticast": True, + }, + }, + }, + { + "name": "traps.ike.policy.add", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sike\spolicy\sadd + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ike policy add", + "result": { + "traps": { + "ike": { + "policy": { + "add": True, + }, + }, + }, + }, + }, + { + "name": "traps.ike.policy.delete", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sike\spolicy\sdelete + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ike policy delete", + "result": { + "traps": { + "ike": { + "policy": { + "delete": True, + }, + }, + }, + }, + }, + { + "name": "traps.ike.tunnel.start", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sike\stunnel\sstart + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ike tunnel start", + "result": { + "traps": { + "ike": { + "tunnel": { + "start": True, + }, + }, + }, + }, + }, + { + "name": "traps.ike.tunnel.stop", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sike\stunnel\sstop + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ike tunnel stop", + "result": { + "traps": { + "ike": { + "tunnel": { + "stop": True, + }, + }, + }, + }, + }, + { + "name": "traps.ipsec.cryptomap.add", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sipsec\scryptomap\sadd + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ipsec cryptomap add", + "result": { + "traps": { + "ipsec": { + "cryptomap": { + "add": True, + }, + }, + }, + }, + }, + { + "name": "traps.ipsec.cryptomap.delete", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sipsec\scryptomap\sdelete + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ipsec cryptomap delete", + "result": { + "traps": { + "ipsec": { + "cryptomap": { + "delete": True, + }, + }, + }, + }, + }, + { + "name": "traps.ipsec.cryptomap.attach", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sipsec\scryptomap\sattach + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ipsec cryptomap attach", + "result": { + "traps": { + "ipsec": { + "cryptomap": { + "attach": True, + }, + }, + }, + }, + }, + { + "name": "traps.ipsec.cryptomap.detach", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sipsec\scryptomap\sdetach + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ipsec cryptomap detach", + "result": { + "traps": { + "ipsec": { + "cryptomap": { + "detach": True, + }, + }, + }, + }, + }, + { + "name": "traps.ipsec.tunnel.start", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sipsec\stunnel\sstart + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ipsec tunnel start", + "result": { + "traps": { + "ipsec": { + "tunnel": { + "start": True, + }, + }, + }, + }, + }, + { + "name": "traps.ipsec.tunnel.stop", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sipsec\stunnel\sstop + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ipsec tunnel stop", + "result": { + "traps": { + "ipsec": { + "tunnel": { + "stop": True, + }, + }, + }, + }, + }, + { + "name": "traps.ipsec.too_many_sas", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sipsec\stoo-many-sas + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ipsec too-many-sas", + "result": { + "traps": { + "ipsec": { + "too_many_sas": True, + }, + }, + }, + }, + { + "name": "traps.ospf.cisco_specific.error", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sospf\scisco-specific\serrors + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ospf cisco-specific errors", + "result": { + "traps": { + "ospf": { + "cisco_specific": { + "error": True, + }, + }, + }, + }, + }, + { + "name": "traps.ospf.cisco_specific.retransmit", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sospf\scisco-specific\sretransmit + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ospf cisco-specific retransmit", + "result": { + "traps": { + "ospf": { + "cisco_specific": { + "retransmit": True, + }, + }, + }, + }, + }, + { + "name": "traps.ospf.cisco_specific.lsa", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sospf\scisco-specific\slsa + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ospf cisco-specific lsa", + "result": { + "traps": { + "ospf": { + "cisco_specific": { + "lsa": True, + }, + }, + }, + }, + }, + { + "name": "traps.ospf.cisco_specific.state_change.nssa_trans_change", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sospf\scisco-specific\sstate-change\snssa-trans-change + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ospf cisco-specific state-change nssa-trans-change", + "result": { + "traps": { + "ospf": { + "cisco_specific": { + "state_change": { + "nssa_trans_change": True, + }, + }, + }, + }, + }, + }, + { + "name": "traps.ospf.cisco_specific.state_change.shamlink.interface", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sospf\scisco-specific\sstate-change\sshamlink\sinterface + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ospf cisco-specific state-change shamlink interface", + "result": { + "traps": { + "ospf": { + "cisco_specific": { + "state_change": { + "shamlink": { + "interface": True, + }, + }, + }, + }, + }, + }, + }, + { + "name": "traps.ospf.cisco_specific.state_change.shamlink.neighbor", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sospf\scisco-specific\sstate-change\sshamlink\sneighbor + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ospf cisco-specific state-change shamlink neighbor", + "result": { + "traps": { + "ospf": { + "cisco_specific": { + "state_change": { + "shamlink": { + "neighbor": True, + }, + }, + }, + }, + }, + }, + }, + { + "name": "traps.ospf.error", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sospf\serrors + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ospf errors", + "result": { + "traps": { + "ospf": { + "error": True, + }, + }, + }, + }, + { + "name": "traps.ospf.retransmit", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sospf\sretransmit + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ospf retransmit", + "result": { + "traps": { + "ospf": { + "retransmit": True, + }, + }, + }, + }, + { + "name": "traps.ospf.lsa", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sospf\slsa + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ospf lsa", + "result": { + "traps": { + "ospf": { + "lsa": True, + }, + }, + }, + }, + { + "name": "traps.ospf.state_change", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sospf\sstate-change + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ospf state-change", + "result": { + "traps": { + "ospf": { + "state_change": True, + }, + }, + }, + }, + { + "name": "traps.l2tun.pseudowire_status", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sl2tun\spseudowire\sstatus + (\s(?P<pseudowire_status>))? + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps l2tun pseudowire status", + "result": { + "traps": { + "l2tun": { + "pseudowire_status": True, + }, + }, + }, + }, + { + "name": "traps.l2tun.session", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sl2tun\ssession + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps l2tun session", + "result": { + "traps": { + "l2tun": { + "session": True, + }, + }, + }, + }, + { + "name": "traps.cpu", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\scpu + (\s(?P<threshold>threshold))? + """, re.VERBOSE, + ), + "setval": "{{ 'snmp-server enable traps cpu' if traps.cpu.enable is defined else '' }}" + "{{ ' threshold' if traps.cpu.threshold is defined else '' }}", + "result": { + "traps": { + "cpu": { + "enable": True, + "threshold": "{{ not not threshold }}", + }, + }, + }, + }, + { + "name": "traps.firewall", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sfirewall + (\s(?P<serverstatus>serverstatus))? + """, re.VERBOSE, + ), + "setval": "{{ 'snmp-server enable traps firewall' if traps.firewall.enable is defined else '' }}" + "{{ ' serverstatus' if traps.firewall.serverstatus|d(False) else ''}}", + "result": { + "traps": { + "firewall": { + "enable": True, + "serverstatus": "{{ not not serverstatus }}", + }, + }, + }, + }, + { + "name": "traps.pim", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\spim + (\s(?P<neighbor_change>neighbor-change))? + (\s(?P<rp_mapping_change>rp-mapping-change))? + (\s(?P<invalid_pim_message>invalid-pim-message))? + """, re.VERBOSE, + ), + "setval": "{{ 'snmp-server enable traps pim' if traps.pim.enable is defined else '' }}" + "{{ ' neighbor-change' if traps.pim.neighbor_change|d(False) else ''}}" + "{{ ' rp-mapping-change' if traps.pim.rp_mapping_change|d(False) else ''}}" + "{{ ' invalid-pim-message' if traps.pim.invalid_pim_message|d(False) else ''}}", + "result": { + "traps": { + "pim": { + "enable": True, + "neighbor_change": "{{ not not neighbor_change }}", + "rp_mapping_change": "{{ not not rp_mapping_change }}", + "invalid_pim_message": "{{ not not invalid_pim_message }}", + }, + }, + }, + }, + { + "name": "traps.vrfmib", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\svrfmib + (\s(?P<vrf_up>vrf-up))? + (\s(?P<vrf_down>vrf-down))? + (\s(?P<vnet_trunk_up>vnet-trunk-up))? + (\s(?P<vnet_trunk_down>vnet-trunk-down))? + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps vrfmib" + "{{ ' vrf-up' if traps.vrfmib.vrf_up|d(False) else ''}}" + "{{ ' vrf-down' if traps.vrfmib.vrf_down|d(False) else ''}}" + "{{ ' vnet-trunk-up' if traps.vrfmib.vnet_trunk_up|d(False) else ''}}" + "{{ ' vnet-trunk-down' if traps.vrfmib.vnet_trunk_down|d(False) else ''}}", + "result": { + "traps": { + "vrfmib": { + "vrf_up": "{{ not not vrf_up }}", + "vrf_down": "{{ not not vrf_down }}", + "vnet_trunk_up": "{{ not not vnet_trunk_up }}", + "vnet_trunk_down": "{{ not not vnet_trunk_down }}", + }, + }, + }, + }, + { + "name": "traps.snmp", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\ssnmp + (\s(?P<authentication>authentication))? + (\s(?P<linkdown>linkdown))? + (\s(?P<linkup>linkup))? + (\s(?P<coldstart>coldstart))? + (\s(?P<warmstart>warmstart))? + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps snmp" + "{{ ' authentication' if traps.snmp.authentication is defined else '' }}" + "{{ ' linkdown' if traps.snmp.linkdown|d(False) else ''}}" + "{{ ' linkup' if traps.snmp.linkup|d(False) else ''}}" + "{{ ' warmstart' if traps.snmp.warmstart|d(False) else ''}}" + "{{ ' coldstart' if traps.snmp.coldstart|d(False) else ''}}", + "result": { + "traps": { + "snmp": { + "authentication": "{{ not not authentication }}", + "linkdown": "{{ not not linkdown }}", + "linkup": "{{ not not linkup }}", + "coldstart": "{{ not not coldstart }}", + "warmstart": "{{ not not warmstart }}", + }, + }, + }, + }, + { + "name": "traps.frame_relay", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sframe-relay$ + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps frame-relay", + "result": { + "traps": { + "frame_relay": { + "enable": True, + }, + }, + }, + }, + { + "name": "traps.frame_relay.subif", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sframe-relay\ssubif + (\scount(?P<count>\d+))? + (\sinterval(?P<interval>\d+))? + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps frame-relay subif" + "{{ (' count ' + count|string) if traps.frame_relay.count else '' }}" + "{{ (' interval ' + interval|string) if traps.frame_relay.interval else '' }}", + "result": { + "traps": { + "frame_relay": { + "subif": { + "enable": "{{ not not subif }}", + "interval": "{{ interval }}", + "count": "{{ count }}", + }, + }, + }, + }, + }, + { + "name": "traps.cef", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\scef + (\s(?P<resource_failure>resource-failure))? + (\s(?P<peer_state_change>peer-state-change))? + (\s(?P<peer_fib_state_change>peer-fib-state-change))? + (\s(?P<inconsistency>inconsistency))? + """, re.VERBOSE, + ), + "setval": "{{ 'snmp-server enable traps cef' if traps.cef.enable is defined else '' }}" + "{{ ' resource-failure' if traps.cef.resource_failure|d(False) else ''}}" + "{{ ' peer-state-change' if traps.cef.peer_state_change|d(False) else ''}}" + "{{ ' peer-fib-state-change' if traps.cef.peer_fib_state_change|d(False) else ''}}" + "{{ ' inconsistency' if traps.cef.inconsistency|d(False) else ''}}", + "result": { + "traps": { + "cef": { + "enable": True, + "inconsistency": "{{ not not inconsistency }}", + "peer_fib_state_change": "{{ not not peer_fib_state_change }}", + "peer_state_change": "{{ not not peer_state_change }}", + "resource_failure": "{{ not not resource_failure }}", + }, + }, + }, + }, + { + "name": "traps.dlsw", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sdlsw + (\s(?P<circuit>circuit))? + (\s(?P<tconn>tconn))? + """, re.VERBOSE, + ), + "setval": "{{ 'snmp-server enable traps dlsw' if traps.dlsw.enable is defined else '' }}" + "{{ ' circuit' if traps.dlsw.circuit|d(False) else ''}}" + "{{ ' tconn' if traps.dlsw.tconn|d(False) else ''}}", + "result": { + "traps": { + "dlsw": { + "enable": True, + "circuit": "{{ not not circuit }}", + "tconn": "{{ not not tconn }}", + }, + }, + }, + }, + { + "name": "traps.ethernet.evc", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sethernet\sevc + (\s(?P<status>status))? + (\s(?P<create>create))? + (\s(?P<delete>delete))? + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ethernet evc" + "{{ ' create' if traps.ethernet.evc.create|d(False) else ''}}" + "{{ ' delete' if traps.ethernet.evc.delete|d(False) else ''}}" + "{{ ' status' if traps.ethernet.evc.status|d(False) else ''}}", + "result": { + "traps": { + "ethernet": { + "evc": { + "create": "{{ not not create }}", + "delete": "{{ not not delete }}", + "status": "{{ not not status }}", + }, + }, + }, + }, + }, + { + "name": "traps.ethernet.cfm.cc", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sethernet\scfm\scc + (\s(?P<mep_up>mep-up))? + (\s(?P<mep_down>mep-down))? + (\s(?P<cross_connect>cross-connect))? + (\s(?P<loop>loop))? + (\s(?P<config>config))? + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ethernet cfm cc" + "{{ ' mep-up' if traps.ethernet.cfm.cc.mep_up|d(False) else ''}}" + "{{ ' mep-down' if traps.ethernet.cfm.cc.mep_down|d(False) else ''}}" + "{{ ' cross-connect' if traps.ethernet.cfm.cc.cross_connect|d(False) else ''}}" + "{{ ' loop' if traps.ethernet.cfm.cc.loop|d(False) else ''}}" + "{{ ' config' if traps.ethernet.cfm.cc.config|d(False) else ''}}", + "result": { + "traps": { + "ethernet": { + "cfm": { + "cc": { + "mep_up": "{{ not not mep_up }}", + "mep_down": "{{ not not mep_down }}", + "cross_connect": "{{ not not cross_connect }}", + "loop": "{{ not not loop }}", + "config": "{{ not not config }}", + }, + }, + }, + }, + }, + }, + { + "name": "traps.ethernet.cfm.crosscheck", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sethernet\scfm\scrosscheck + (\s(?P<mep_missing>mep-missing))? + (\s(?P<mep_unknown>mep-unknown))? + (\s(?P<service_up>service-up))? + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ethernet cfm crosscheck" + "{{ ' mep-missing' if traps.ethernet.cfm.crosscheck.mep_missing|d(False) else ''}}" + "{{ ' mep-unknown' if traps.ethernet.cfm.crosscheck.mep_unknown|d(False) else ''}}" + "{{ ' service-up' if traps.ethernet.cfm.crosscheck.service_up|d(False) else ''}}", + "result": { + "traps": { + "ethernet": { + "cfm": { + "crosscheck": { + "mep_missing": "{{ not not mep_missing }}", + "mep_unknown": "{{ not not mep_unknown }}", + "service_up": "{{ not not service_up }}", + }, + }, + }, + }, + }, + }, + { + "name": "traps.ethernet.cfm.alarm", + "getval": re.compile( + r""" + ^snmp-server\senable\straps\sethernet\scfm\salarm + """, re.VERBOSE, + ), + "setval": "snmp-server enable traps ethernet cfm alarm", + "result": { + "traps": { + "ethernet": { + "cfm": { + "alarm": True, + }, + }, + }, + }, + }, + { + "name": "system_shutdown", + "getval": re.compile( + r""" + ^snmp-server\ssystem-shutdown + """, re.VERBOSE, + ), + "setval": "snmp-server system-shutdown", + "result": { + "system_shutdown": True, + }, + }, + ] + # fmt: on diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/static_routes.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/static_routes.py new file mode 100644 index 000000000..790f0c8df --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/static_routes.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- +# Copyright 2023 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +""" +The Static_routes parser templates file. This contains +a list of parser definitions and associated functions that +facilitates both facts gathering and native command generation for +the given network resource. +""" + +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( + NetworkTemplate, +) + + +class Static_routesTemplate(NetworkTemplate): + def __init__(self, lines=None, module=None): + super(Static_routesTemplate, self).__init__(lines=lines, tmplt=self, module=module) + + # fmt: off + PARSERS = [ + { + "name": "ipv4", + "getval": re.compile( + r""" + ^ip\sroute + (\stopology\s(?P<topology>\S+))? + (\svrf\s(?P<vrf>\S+))? + (\s(?P<dest>\S+)) + (\s(?P<netmask>\S+)) + (\s(?P<interface>(ACR|ATM-ACR|Analysis-Module|AppNav-Compress|AppNav-UnCompress|Async|Auto-Template|BD-VIF|BDI|BVI|Bluetooth|CDMA-Ix|CEM-ACR|CEM-PG|CTunnel|Container|Dialer|EsconPhy|Ethernet-Internal|Fcpa|Filter|Filtergroup|GigabitEthernet|IMA-ACR|LongReachEthernet|Loopback|Lspvif|MFR|Multilink|NVI|Null|PROTECTION_GROUP|Port-channel|Portgroup|Pos-channel|SBC|SDH_ACR|SERIAL-ACR|SONET_ACR|SSLVPN-VIF|SYSCLOCK|Serial-PG|Service-Engine|TLS-VIF|Tunnel|VPN|Vif|Vir-cem-ACR|Virtual-PPP|Virtual-TokenRing)\d+))? + (\s(?P<forward_router_address>(?!multicast|dhcp|global|tag|track|permanent|name)\S+))? + (\s(?P<distance_metric>\d+))? + (\stag\s(?P<tag>\d+))? + (\s(?P<permanent>permanent))? + (\sname\s(?P<next_hop_name>\S+))? + (\strack\s(?P<track>\d+))? + (\s(?P<multicast>multicast))? + (\s(?P<dhcp>dhcp))? + (\s(?P<global>global))? + $""", re.VERBOSE, + ), + "setval": "ip route" + "{{ (' topology ' + ipv4.topology) if ipv4.topology is defined else '' }}" + "{{ (' vrf ' + ipv4.vrf) if ipv4.vrf is defined else '' }}" + "{{ (' ' + ipv4.dest) if ipv4.dest is defined else '' }}" + "{{ (' ' + ipv4.interface) if ipv4.interface is defined else '' }}" + "{{ (' ' + ipv4.forward_router_address) if ipv4.forward_router_address is defined else '' }}" + "{{ (' ' + ipv4.distance_metric|string) if ipv4.distance_metric is defined else '' }}" + "{{ (' tag ' + ipv4.tag|string) if ipv4.tag is defined else '' }}" + "{{ (' permanent' ) if ipv4.permanent|d(False) else '' }}" + "{{ (' name ' + ipv4.name) if ipv4.name is defined else '' }}" + "{{ (' track ' + ipv4.track|string) if ipv4.track is defined else '' }}" + "{{ (' multicast' ) if ipv4.multicast|d(False) else '' }}" + "{{ (' dhcp' ) if ipv4.dhcp|d(False) else '' }}" + "{{ (' global' ) if ipv4.global|d(False) else '' }}", + "result": { + "{{ dest }}_{{ vrf|d() }}_{{ topology|d() }}_ipv4": [ + { + "_vrf": "{{ vrf }}", + "_topology": "{{ topology }}", + "_afi": "ipv4", + "_dest": "{{ dest }}", + "_netmask": "{{ netmask }}", + "interface": "{{ interface }}", + "forward_router_address": "{{ forward_router_address }}", + "distance_metric": "{{ distance_metric }}", + "tag": "{{ tag }}", + "permanent": "{{ not not permanent }}", + "name": "{{ next_hop_name }}", + "track": "{{ track }}", + "multicast": "{{ not not multicast }}", + "dhcp": "{{ not not dhcp }}", + "global": "{{ not not global }}", + }, + ], + }, + }, + { + "name": "ipv6", + "getval": re.compile( + r""" + ^ipv6\sroute + (\stopology\s(?P<topology>\S+))? + (\svrf\s(?P<vrf>\S+))? + (\s(?P<dest>\S+)) + (\s(?P<interface>(ACR|ATM-ACR|Analysis-Module|AppNav-Compress|AppNav-UnCompress|Async|Auto-Template|BD-VIF|BDI|BVI|Bluetooth|CDMA-Ix|CEM-ACR|CEM-PG|CTunnel|Container|Dialer|EsconPhy|Ethernet-Internal|Fcpa|Filter|Filtergroup|GigabitEthernet|IMA-ACR|LongReachEthernet|Loopback|Lspvif|MFR|Multilink|NVI|Null|PROTECTION_GROUP|Port-channel|Portgroup|Pos-channel|SBC|SDH_ACR|SERIAL-ACR|SONET_ACR|SSLVPN-VIF|SYSCLOCK|Serial-PG|Service-Engine|TLS-VIF|Tunnel|VPN|Vif|Vir-cem-ACR|Virtual-PPP|Virtual-TokenRing)\d+))? + (\s(?P<forward_router_address>(?!multicast|unicast|tag|track|permanent|name)\S+))? + (\s(?P<distance_metric>\d+))? + (\s(?P<multicast>multicast))? + (\s(?P<unicast>unicast))? + (\stag\s(?P<tag>\d+))? + (\strack\s(?P<track>\d+))? + (\s(?P<permanent>permanent))? + (\sname\s(?P<next_hop_name>\S+))? + $""", re.VERBOSE, + ), + "setval": "ipv6 route" + "{{ (' topology ' + ipv6.topology) if ipv6.topology is defined else '' }}" + "{{ (' vrf ' + ipv6.vrf) if ipv6.vrf is defined else '' }}" + "{{ (' ' + ipv6.dest) if ipv6.dest is defined else '' }}" + "{{ (' ' + ipv6.interface) if ipv6.interface is defined else '' }}" + "{{ (' ' + ipv6.forward_router_address) if ipv6.forward_router_address is defined else '' }}" + "{{ (' ' + ipv6.distance_metric|string) if ipv6.distance_metric is defined else '' }}" + "{{ (' multicast' ) if ipv6.multicast|d(False) else '' }}" + "{{ (' unicast' ) if ipv6.unicast|d(False) else '' }}" + "{{ (' tag ' + ipv6.tag|string) if ipv6.tag is defined else '' }}" + "{{ (' track ' + ipv6.track|string) if ipv6.track is defined else '' }}" + "{{ (' permanent' ) if ipv6.permanent|d(False) else '' }}" + "{{ (' name ' + ipv6.name) if ipv6.name is defined else '' }}", + "result": { + "{{ dest }}_{{ vrf|d() }}_{{ topology|d() }}_ipv6": [ + { + "_vrf": "{{ vrf }}", + "_topology": "{{ topology }}", + "_afi": "ipv6", + "_dest": "{{ dest }}", + "interface": "{{ interface }}", + "forward_router_address": "{{ forward_router_address }}", + "distance_metric": "{{ distance_metric }}", + "tag": "{{ tag }}", + "permanent": "{{ not not permanent }}", + "name": "{{ next_hop_name }}", + "track": "{{ track }}", + "multicast": "{{ not not multicast }}", + "unicast": "{{ not not unicast }}", + }, + ], + }, + }, + ] + # fmt: on diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/utils.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/utils.py new file mode 100644 index 000000000..80b4b084d --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/utils.py @@ -0,0 +1,423 @@ +# +# -*- 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) + +# utils + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import socket + +from itertools import count, groupby + +from ansible.module_utils.common.network import is_masklen, to_netmask +from ansible.module_utils.six import iteritems + + +def remove_command_from_config_list(interface, cmd, commands): + # To delete the passed config + if interface not in commands: + commands.insert(0, interface) + commands.append("no %s" % cmd) + return commands + + +def add_command_to_config_list(interface, cmd, commands): + # To set the passed config + if interface not in commands: + commands.insert(0, interface) + commands.append(cmd) + + +def reverify_diff_py35(want, have): + """Function to re-verify the set diff for py35 as it doesn't maintains dict order which results + into unexpected set diff + :param config: want and have set config + :returns: True/False post checking if there's any actual diff b/w want and have sets + """ + if not have: + return True + for each_want in want: + diff = True + for each_have in have: + if each_have == sorted(each_want) or sorted(each_have) == sorted(each_want): + diff = False + if diff: + return True + return False + + +def check_n_return_valid_ipv6_addr(module, input_list, filtered_ipv6_list): + # To verify the valid ipv6 address + try: + for each in input_list: + if "::" in each: + if "/" in each: + each = each.split("/")[0] + if socket.inet_pton(socket.AF_INET6, each): + filtered_ipv6_list.append(each) + return filtered_ipv6_list + except socket.error: + module.fail_json(msg="Incorrect IPV6 address!") + + +def new_dict_to_set(input_dict, temp_list, test_set, count=0): + # recursive function to convert input dict to set for comparision + test_dict = dict() + if isinstance(input_dict, dict): + input_dict_len = len(input_dict) + for k, v in sorted(iteritems(input_dict)): + count += 1 + if isinstance(v, list): + temp_list.append(k) + for each in v: + if isinstance(each, dict): + if [True for i in each.values() if type(i) == list]: + new_dict_to_set(each, temp_list, test_set, count) + else: + new_dict_to_set(each, temp_list, test_set, 0) + else: + if v is not None: + test_dict.update({k: v}) + try: + if tuple(iteritems(test_dict)) not in test_set and count == input_dict_len: + test_set.add(tuple(iteritems(test_dict))) + count = 0 + except TypeError: + temp_dict = {} + + def expand_dict(dict_to_expand): + temp = dict() + for k, v in iteritems(dict_to_expand): + if isinstance(v, dict): + expand_dict(v) + else: + if v is not None: + temp.update({k: v}) + temp_dict.update(tuple(iteritems(temp))) + + new_dict = {k: v} + expand_dict(new_dict) + if tuple(iteritems(temp_dict)) not in test_set: + test_set.add(tuple(iteritems(temp_dict))) + + +def dict_to_set(sample_dict): + # Generate a set with passed dictionary for comparison + test_dict = dict() + if isinstance(sample_dict, dict): + for k, v in iteritems(sample_dict): + if v is not None: + if isinstance(v, list): + if isinstance(v[0], dict): + li = [] + for each in v: + for key, value in iteritems(each): + if isinstance(value, list): + each[key] = tuple(value) + li.append(tuple(iteritems(each))) + v = tuple(li) + else: + v = tuple(v) + elif isinstance(v, dict): + li = [] + for key, value in iteritems(v): + if isinstance(value, list): + v[key] = tuple(value) + li.extend(tuple(iteritems(v))) + v = tuple(li) + test_dict.update({k: v}) + return_set = set(tuple(iteritems(test_dict))) + else: + return_set = set(sample_dict) + return return_set + + +def filter_dict_having_none_value(want, have): + # Generate dict with have dict value which is None in want dict + test_dict = dict() + name = want.get("name") + if name: + test_dict["name"] = name + diff_ip = False + for k, v in iteritems(want): + if isinstance(v, dict): + for key, value in iteritems(v): + test_key_dict = dict() + if value is None: + if have.get(k): + dict_val = have.get(k).get(key) + test_key_dict.update({key: dict_val}) + elif k == "ipv6" and value.lower() != have.get(k)[0].get(key).lower(): + # as multiple IPV6 address can be configured on same + # interface, for replace state in place update will + # actually create new entry, which isn't as expected + # for replace state, so in case of IPV6 address + # every time 1st delete the existing IPV6 config and + # then apply the new change + dict_val = have.get(k)[0].get(key) + test_key_dict.update({key: dict_val}) + if test_key_dict: + test_dict.update({k: test_key_dict}) + if isinstance(v, list): + for key, value in iteritems(v[0]): + test_key_dict = dict() + if value is None: + if have.get(k) and key in have.get(k): + dict_val = have.get(k)[0].get(key) + test_key_dict.update({key: dict_val}) + elif have.get(k): + if k == "ipv6" and value.lower() != have.get(k)[0].get(key).lower(): + dict_val = have.get(k)[0].get(key) + test_key_dict.update({key: dict_val}) + if test_key_dict: + test_dict.update({k: test_key_dict}) + # below conditions checks are added to check if + # secondary IP is configured, if yes then delete + # the already configured IP if want and have IP + # is different else if it's same no need to delete + for each in v: + if each.get("secondary"): + want_ip = each.get("address").split("/") + have_ip = have.get("ipv4") + if len(want_ip) > 1 and have_ip and have_ip[0].get("secondary"): + have_ip = have_ip[0]["address"].split(" ")[0] + if have_ip != want_ip[0]: + diff_ip = True + if each.get("secondary") and diff_ip is True: + test_key_dict.update({"secondary": True}) + test_dict.update({"ipv4": test_key_dict}) + if v is None: + val = have.get(k) + test_dict.update({k: val}) + return test_dict + + +def remove_duplicate_interface(commands): + # Remove duplicate interface from commands + set_cmd = [] + for each in commands: + if "interface" in each: + if each not in set_cmd: + set_cmd.append(each) + else: + set_cmd.append(each) + + return set_cmd + + +def validate_ipv4(value, module): + if value: + address = value.split("/") + if len(address) != 2: + module.fail_json( + msg="address format is <ipv4 address>/<mask>, got invalid format {0}".format(value), + ) + + if not is_masklen(address[1]): + module.fail_json( + msg="invalid value for mask: {0}, mask should be in range 0-32".format(address[1]), + ) + + +def validate_ipv6(value, module): + if value: + address = value.split("/") + if len(address) != 2: + module.fail_json( + msg="address format is <ipv6 address>/<mask>, got invalid format {0}".format(value), + ) + else: + if not 0 <= int(address[1]) <= 128: + module.fail_json( + msg="invalid value for mask: {0}, mask should be in range 0-128".format( + address[1], + ), + ) + + +def validate_n_expand_ipv4(module, want): + # Check if input IPV4 is valid IP and expand IPV4 with its subnet mask + ip_addr_want = want.get("address") + if len(ip_addr_want.split(" ")) > 1: + return ip_addr_want + validate_ipv4(ip_addr_want, module) + ip = ip_addr_want.split("/") + if len(ip) == 2: + ip_addr_want = "{0} {1}".format(ip[0], to_netmask(ip[1])) + + return ip_addr_want + + +def netmask_to_cidr(netmask): + # convert netmask to cidr and returns the cidr notation + return str(sum([bin(int(x)).count("1") for x in netmask.split(".")])) + + +def is_valid_ip(ip_str): + valid = True + try: + if "::" in ip_str: + socket.inet_pton(socket.AF_INET6, ip_str) # for IPv6 + else: + socket.inet_pton(socket.AF_INET, ip_str) # for IPv4 + except socket.error: + valid = False + return valid + + +def normalize_interface(name): + """Return the normalized interface name""" + if not name: + return + + def _get_number(name): + digits = "" + for char in name: + if char.isdigit() or char in "/.": + digits += char + return digits + + if name.lower().startswith("gi"): + if_type = "GigabitEthernet" + elif name.lower().startswith("twe"): + if_type = "TwentyFiveGigE" + elif name.lower().startswith("tw"): + if_type = "TwoGigabitEthernet" + elif name.lower().startswith("te"): + if_type = "TenGigabitEthernet" + elif name.lower().startswith("fa"): + if_type = "FastEthernet" + elif name.lower().startswith("fo"): + if_type = "FortyGigabitEthernet" + elif name.lower().startswith("fi"): + if_type = "FiveGigabitEthernet" + elif name.lower().startswith("long"): + if_type = "LongReachEthernet" + elif name.lower().startswith("et"): + if_type = "Ethernet" + elif name.lower().startswith("vl"): + if_type = "Vlan" + elif name.lower().startswith("lo"): + if_type = "loopback" + elif name.lower().startswith("po"): + if_type = "Port-channel" + elif name.lower().startswith("nv"): + if_type = "nve" + elif name.lower().startswith("hu"): + if_type = "HundredGigE" + elif name.lower().startswith("virtual-te"): + if_type = "Virtual-Template" + elif name.lower().startswith("tu"): + if_type = "Tunnel" + elif name.lower().startswith("se"): + if_type = "Serial" + else: + if_type = None + + number_list = name.split(" ") + if len(number_list) == 2: + number = number_list[-1].strip() + else: + number = _get_number(name) + + if if_type: + proper_interface = if_type + number + else: + proper_interface = name + + return proper_interface + + +def get_interface_type(interface): + """Gets the type of interface""" + + if interface.upper().startswith("GI"): + return "GigabitEthernet" + elif interface.upper().startswith("TW"): + return "TwoGigabitEthernet" + elif interface.upper().startswith("TE"): + return "TenGigabitEthernet" + elif interface.upper().startswith("FA"): + return "FastEthernet" + elif interface.upper().startswith("FO"): + return "FortyGigabitEthernet" + elif interface.upper().startswith("FI"): + return "FiveGigabitEthernet" + elif interface.upper().startswith("LON"): + return "LongReachEthernet" + elif interface.upper().startswith("ET"): + return "Ethernet" + elif interface.upper().startswith("VL"): + return "Vlan" + elif interface.upper().startswith("LO"): + return "loopback" + elif interface.upper().startswith("PO"): + return "Port-channel" + elif interface.upper().startswith("NV"): + return "nve" + elif interface.upper().startswith("TWE"): + return "TwentyFiveGigE" + elif interface.upper().startswith("HU"): + return "HundredGigE" + elif interface.upper().startswith("VIRTUAL-TE"): + return "Virtual-Template" + elif interface.upper().startswith("TU"): + return "Tunnel" + elif interface.upper().startswith("SE"): + return "Serial" + else: + return "unknown" + + +def get_ranges(data): + """ + Returns a generator object that yields lists of + consecutive integers from a list of integers. + """ + for _k, group in groupby(data, lambda t, c=count(): int(t) - next(c)): + yield list(group) + + +def numerical_sort(string_int_list): + """Sorts list of integers that are digits in numerical order.""" + + as_int_list = [] + + for vlan in string_int_list: + as_int_list.append(int(vlan)) + as_int_list.sort() + return as_int_list + + +def vlan_list_to_range(cmd): + """ + Converts a comma separated list of vlan IDs + into ranges. + """ + ranges = [] + for v in get_ranges(cmd): + ranges.append("-".join(map(str, (v[0], v[-1])[: len(v)]))) + return ",".join(ranges) + + +def vlan_range_to_list(vlans): + result = [] + if vlans: + for part in vlans: + if part == "none": + break + if "-" in part: + a, b = part.split("-") + a, b = int(a), int(b) + result.extend(range(a, b + 1)) + else: + a = int(part) + result.append(a) + return numerical_sort(result) + return result diff --git a/ansible_collections/cisco/ios/plugins/modules/__init__.py b/ansible_collections/cisco/ios/plugins/modules/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_acl_interfaces.py b/ansible_collections/cisco/ios/plugins/modules/ios_acl_interfaces.py new file mode 100644 index 000000000..dfdc08528 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_acl_interfaces.py @@ -0,0 +1,629 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_acl_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_acl_interfaces +short_description: Resource module to configure ACL interfaces. +description: + This module configures and manages the access-control (ACL) attributes + of interfaces on IOS platforms. +version_added: 1.0.0 +author: + - Sumit Jaiswal (@justjais) + - Sagar Paul (@KB-perByte) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: A dictionary of ACL interfaces options + type: list + elements: dict + suboptions: + name: + description: + Full name of the interface excluding any logical unit number, + i.e. GigabitEthernet0/1. + type: str + required: true + access_groups: + description: Specify access-group for IP access list (standard or extended). + type: list + elements: dict + suboptions: + afi: + description: Specifies the AFI for the ACLs to be configured on this interface. + type: str + required: true + choices: + - ipv4 + - ipv6 + acls: + description: Specifies the ACLs for the provided AFI. + type: list + elements: dict + suboptions: + name: + description: Specifies the name of the IPv4/IPv4 ACL for the interface. + type: str + required: true + direction: + description: + - Specifies the direction of packets that the ACL will be applied + on. + - With one direction already assigned, other acl direction cannot + be same. + type: str + required: true + choices: + - in + - out + running_config: + description: + - The module, by default, will connect to the remote device and retrieve the current + running-config to use as a base for comparing against the contents of source. + There are times when it is not desirable to have the task get the current running-config + for every task in a playbook. The I(running_config) argument allows the implementer + to pass in the configuration to use as the base config for comparison. This + value of this option should be the output received from device by executing + command. + type: str + state: + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config | + include ^interface|ip access-group|ipv6 traffic-filter) executed on device. + For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged +""" + +EXAMPLES = """ +# Using Merged + +# Before state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# interface GigabitEthernet0/2 +# ip access-group 123 out + +- name: Merge module attributes of given access-groups + cisco.ios.ios_acl_interfaces: + config: + - name: GigabitEthernet0/1 + access_groups: + - afi: ipv4 + acls: + - name: 110 + direction: in + - name: 123 + direction: out + - afi: ipv6 + acls: + - name: test_v6 + direction: out + - name: temp_v6 + direction: in + - name: GigabitEthernet0/2 + access_groups: + - afi: ipv4 + acls: + - name: 100 + direction: in + state: merged + +# Commands Fired: +# --------------- +# +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 100 in + +# After state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +# Using Replaced + +# Before state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +- name: Replace module attributes of given access-groups + cisco.ios.ios_acl_interfaces: + config: + - name: GigabitEthernet0/1 + access_groups: + - afi: ipv4 + acls: + - name: 100 + direction: out + - name: 110 + direction: in + state: replaced + +# Commands Fired: +# --------------- +# +# interface GigabitEthernet0/1 +# no ip access-group 123 out +# no ipv6 traffic-filter temp_v6 in +# no ipv6 traffic-filter test_v6 out +# ip access-group 100 out + +# After state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 100 out +# ip access-group 110 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +# Using Overridden + +# Before state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +- name: Overridden module attributes of given access-groups + cisco.ios.ios_acl_interfaces: + config: + - name: GigabitEthernet0/1 + access_groups: + - afi: ipv4 + acls: + - name: 100 + direction: out + - name: 110 + direction: in + state: overridden + +# Commands Fired: +# --------------- +# +# interface GigabitEthernet0/1 +# no ip access-group 123 out +# no ipv6 traffic-filter test_v6 out +# no ipv6 traffic-filter temp_v6 in +# ip access-group 100 out +# interface GigabitEthernet0/2 +# no ip access-group 110 in +# no ip access-group 123 out + +# After state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 100 out +# ip access-group 110 in +# interface GigabitEthernet0/2 + +# Using Deleted + +# Before state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +- name: Delete module attributes of given Interface + cisco.ios.ios_acl_interfaces: + config: + - name: GigabitEthernet0/1 + state: deleted + +# Commands Fired: +# --------------- +# +# interface GigabitEthernet0/1 +# no ip access-group 110 in +# no ip access-group 123 out +# no ipv6 traffic-filter test_v6 out +# no ipv6 traffic-filter temp_v6 in + +# After state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +# Using DELETED without any config passed +#"(NOTE: This will delete all of configured resource module attributes from each configured interface)" + +# Before state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +- name: Delete module attributes of given access-groups from ALL Interfaces + cisco.ios.ios_acl_interfaces: + config: + state: deleted + +# Commands Fired: +# --------------- +# +# interface GigabitEthernet0/1 +# no ip access-group 110 in +# no ip access-group 123 out +# no ipv6 traffic-filter test_v6 out +# no ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# no ip access-group 110 out +# no ip access-group 123 out + +# After state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# interface GigabitEthernet0/2 + +# Using Gathered + +# Before state: +# ------------- +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +- name: Gather listed acl interfaces with provided configurations + cisco.ios.ios_acl_interfaces: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "name": "Loopback888" +# }, +# { +# "name": "GigabitEthernet0/0" +# }, +# { +# "access_groups": [ +# { +# "acls": [ +# { +# "direction": "in", +# "name": "110" +# }, +# { +# "direction": "out", +# "name": "123" +# } +# ], +# "afi": "ipv4" +# }, +# { +# "acls": [ +# { +# "direction": "in", +# "name": "temp_v6" +# }, +# { +# "direction": "out", +# "name": "test_v6" +# } +# ], +# "afi": "ipv6" +# } +# ], +# "name": "GigabitEthernet0/1" +# }, +# { +# "access_groups": [ +# { +# "acls": [ +# { +# "direction": "in", +# "name": "100" +# }, +# { +# "direction": "out", +# "name": "123" +# } +# ], +# "afi": "ipv4" +# } +# ], +# "name": "GigabitEthernet0/2" +# } +# ] + +# After state: +# ------------ +# +# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter +# interface Loopback888 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter test_v6 out +# ipv6 traffic-filter temp_v6 in +# interface GigabitEthernet0/2 +# ip access-group 110 in +# ip access-group 123 out + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_acl_interfaces: + config: + - name: GigabitEthernet0/1 + access_groups: + - afi: ipv4 + acls: + - name: 110 + direction: in + - name: 123 + direction: out + - afi: ipv6 + acls: + - name: test_v6 + direction: out + - name: temp_v6 + direction: in + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "interface GigabitEthernet0/1", +# "ip access-group 110 in", +# "ip access-group 123 out", +# "ipv6 traffic-filter temp_v6 in", +# "ipv6 traffic-filter test_v6 out" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# interface GigabitEthernet0/1 +# ip access-group 110 in +# ip access-group 123 out +# ipv6 traffic-filter temp_v6 in +# ipv6 traffic-filter test_v6 out + +- name: Parse the commands for provided configuration + cisco.ios.ios_acl_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "access_groups": [ +# { +# "acls": [ +# { +# "direction": "in", +# "name": "110" +# } +# ], +# "afi": "ipv4" +# }, +# { +# "acls": [ +# { +# "direction": "in", +# "name": "temp_v6" +# } +# ], +# "afi": "ipv6" +# } +# ], +# "name": "GigabitEthernet0/1" +# } +# ] +""" + + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: list + sample: + - "interface GigabitEthernet0/1" + - "no ip access-group 123 out" + - "no ipv6 traffic-filter test_v6 out" +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - "interface GigabitEthernet0/1" + - "no ip access-group 123 out" + - "no ipv6 traffic-filter test_v6 out" +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.acl_interfaces.acl_interfaces import ( + Acl_interfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.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, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Acl_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_acls.py b/ansible_collections/cisco/ios/plugins/modules/ios_acls.py new file mode 100644 index 000000000..00c5b563c --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_acls.py @@ -0,0 +1,1508 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_acls +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +author: + - Sumit Jaiswal (@justjais) + - Sagar Paul (@KB-perByte) +description: This module configures and manages the named or numbered ACLs on IOS platforms. +module: ios_acls +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - Module behavior is not idempotent when sequence for aces are not mentioned + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: A list of ACL configuration options. + elements: dict + suboptions: + acls: + description: + - A list of Access Control Lists (ACL) attributes. + elements: dict + suboptions: + aces: + description: The entries within the ACL. + elements: dict + suboptions: + destination: + description: Specify the packet destination. + suboptions: + address: + description: Host address to match, or any single host address. + type: str + any: + description: Match any source address. + type: bool + host: + description: A single destination host + type: str + object_group: + description: Destination network object group + type: str + port_protocol: + description: + - Specify the destination port along with protocol. + - Note, Valid with TCP/UDP protocol_options + 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: Port group. + suboptions: + end: + description: Specify the end of the port range. + type: int + start: + description: Specify the start of the port range. + type: int + type: dict + type: dict + wildcard_bits: + description: Destination wildcard bits, valid with IPV4 address. + type: str + type: dict + dscp: + description: Match packets with given dscp value. + type: str + evaluate: + description: Evaluate an access list + type: str + fragments: + description: + - Check non-initial fragments. + - This option is DEPRECATED and is replaced with enable_fragments which + accepts bool as input this attribute will be removed after 2024-01-01. + type: str + enable_fragments: + description: Enable non-initial fragments. + type: bool + grant: + choices: + - permit + - deny + description: Specify the action. + type: str + log: + description: Log matches against this entry. + suboptions: + set: + description: Enable Log matches against this entry + type: bool + user_cookie: + description: User defined cookie (max of 64 char) + type: str + type: dict + log_input: + description: Log matches against this entry, including input interface. + suboptions: + set: + description: Enable Log matches against this entry, including input interface. + type: bool + user_cookie: + description: User defined cookie (max of 64 char) + type: str + type: dict + option: + description: + - Match packets with given IP Options value. + - Valid only for named acls. + suboptions: + add_ext: + description: Match packets with Address Extension Option (147). + type: bool + any_options: + description: Match packets with ANY Option. + type: bool + com_security: + description: Match packets with Commercial Security Option (134). + type: bool + dps: + description: Match packets with Dynamic Packet State Option (151). + type: bool + encode: + description: Match packets with Encode Option (15). + type: bool + eool: + description: Match packets with End of Options (0). + type: bool + ext_ip: + description: Match packets with Extended IP Option (145). + type: bool + ext_security: + description: Match packets with Extended Security Option (133). + type: bool + finn: + description: Match packets with Experimental Flow Control Option (205). + type: bool + imitd: + description: Match packets with IMI Traffic Desriptor Option (144). + type: bool + lsr: + description: Match packets with Loose Source Route Option (131). + type: bool + mtup: + description: Match packets with MTU Probe Option (11). + type: bool + mtur: + description: Match packets with MTU Reply Option (12). + type: bool + no_op: + description: Match packets with No Operation Option (1). + type: bool + nsapa: + description: Match packets with NSAP Addresses Option (150). + type: bool + record_route: + description: Match packets with Record Route Option (7). + type: bool + router_alert: + description: Match packets with Router Alert Option (148). + type: bool + sdb: + description: Match packets with Selective Directed Broadcast Option (149). + type: bool + security: + description: Match packets with Basic Security Option (130). + type: bool + ssr: + description: Match packets with Strict Source Routing Option (137). + type: bool + stream_id: + description: Match packets with Stream ID Option (136). + type: bool + timestamp: + description: Match packets with Time Stamp Option (68). + type: bool + traceroute: + description: Match packets with Trace Route Option (82). + type: bool + ump: + description: Match packets with Upstream Multicast Packet Option (152). + type: bool + visa: + description: Match packets with Experimental Access Control Option (142). + type: bool + zsu: + description: Match packets with Experimental Measurement Option (10). + type: bool + type: dict + precedence: + description: Match packets with given precedence value. + type: str + protocol: + description: + - Specify the protocol to match. + - Refer to vendor documentation for valid values. + type: str + protocol_options: + description: protocol type. + suboptions: + ahp: + description: Authentication Header Protocol. + type: bool + eigrp: + description: Cisco's EIGRP routing protocol. + type: bool + esp: + description: Encapsulation Security Payload. + type: bool + gre: + description: Cisco's GRE tunneling. + type: bool + hbh: + description: Hop by Hop options header. Valid for IPV6 + type: bool + icmp: + description: Internet Control Message Protocol. + 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 + 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_request + type: bool + 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 exceededs + 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 + type: dict + igmp: + description: Internet Gateway Message Protocol. + suboptions: + dvmrp: + description: Distance Vector Multicast Routing Protocol(2) + type: bool + host_query: + description: IGMP Membership Query(0) + type: bool + mtrace_resp: + description: Multicast Traceroute Response(7) + type: bool + mtrace_route: + description: Multicast Traceroute(8) + type: bool + pim: + description: Protocol Independent Multicast(3) + type: bool + trace: + description: Multicast trace(4) + type: bool + v1host_report: + description: IGMPv1 Membership Report(1) + type: bool + v2host_report: + description: IGMPv2 Membership Report(5) + type: bool + v2leave_group: + description: IGMPv2 Leave Group(6) + type: bool + v3host_report: + description: IGMPv3 Membership Report(9) + type: bool + type: dict + ip: + description: Any Internet Protocol. + type: bool + ipinip: + description: IP in IP tunneling. + type: bool + ipv6: + description: Any IPv6. + type: bool + nos: + description: KA9Q NOS compatible IP over IP tunneling. + type: bool + ospf: + description: OSPF routing protocol. + type: bool + pcp: + description: Payload Compression Protocol. + type: bool + pim: + description: Protocol Independent Multicast. + type: bool + protocol_number: + description: An IP protocol number + type: int + sctp: + description: Stream Control Transmission Protocol. + type: bool + tcp: + description: Match TCP packet flags + 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 + type: dict + udp: + description: User Datagram Protocol. + type: bool + type: dict + remarks: + description: The remarks/description of the ACL. + elements: str + type: list + sequence: + description: + - Sequence Number for the Access Control Entry(ACE). + - Refer to vendor documentation for valid values. + type: int + source: + description: Specify the packet source. + suboptions: + address: + description: Source network address. + type: str + any: + description: Match any source address. + type: bool + host: + description: A single source host + type: str + object_group: + description: Source network object group + type: str + port_protocol: + description: + - Specify the source port along with protocol. + - Note, Valid with TCP/UDP protocol_options + 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: Port group. + suboptions: + end: + description: Specify the end of the port range. + type: int + start: + description: Specify the start of the port range. + type: int + type: dict + type: dict + wildcard_bits: + description: Source wildcard bits, valid with IPV4 address. + type: str + type: dict + time_range: + description: Specify a time-range. + type: str + tos: + description: + - Match packets with given TOS value. + - Note, DSCP and TOS are mutually exclusive + suboptions: + max_reliability: + description: Match packets with max reliable TOS (2). + type: bool + max_throughput: + description: Match packets with max throughput TOS (4). + type: bool + min_delay: + description: Match packets with min delay TOS (8). + type: bool + min_monetary_cost: + description: Match packets with min monetary cost TOS (1). + type: bool + normal: + description: Match packets with normal TOS (0). + type: bool + service_value: + description: Type of service value + type: int + type: dict + ttl: + description: Match packets with given TTL value. + suboptions: + eq: + description: Match only packets on a given TTL number. + type: int + gt: + description: Match only packets with a greater TTL number. + type: int + lt: + description: Match only packets with a lower TTL number. + type: int + neq: + description: Match only packets not on a given TTL number. + type: int + range: + description: Match only packets in the range of TTLs. + suboptions: + end: + description: Specify the end of the port range. + type: int + start: + description: Specify the start of the port range. + type: int + type: dict + type: dict + type: list + acl_type: + choices: + - extended + - standard + description: + - ACL type + - Note, it's mandatory and required for Named ACL, but for Numbered ACL it's not mandatory. + type: str + name: + description: The name or the number of the ACL. + required: true + type: str + type: list + afi: + choices: + - ipv4 + - ipv6 + description: + - The Address Family Indicator (AFI) for the Access Control Lists (ACL). + required: true + type: str + type: list + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from + the IOS device by executing the command B(sh 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 + state: + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged + description: + - The state the configuration should be left in + - The state I(merged) is the default state which merges the want and + have config, but for ACL module as the IOS platform doesn't allow + update of ACE over an pre-existing ACE sequence in ACL, same way ACLs + resource module will error out for respective scenario and only addition + of new ACE over new sequence will be allowed with merge state. + - The states I(rendered), I(gathered) and I(parsed) does not perform any + change on the device. + - The state I(rendered) will transform the configuration in C(config) + option to platform specific CLI commands which will be returned in + the I(rendered) key within the result. For state I(rendered) active + connection to remote host is not required. + - The state I(gathered) will fetch the running configuration from device + and transform it into structured data in the format as per the resource + module argspec and the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option + and transforms it into JSON format as per the resource module parameters + and the value is returned in the I(parsed) key within the result. The + value of C(running_config) option should be the same format as the output + of commands I(show access-list) and + I(show running-config | include ip(v6)* access-list|remark) executed on + device. Config data from both the commands should be kept together one after + another for the parsers to pick the commands correctly. + For state I(parsed) active connection to remote host is not required. + - The state I(overridden), modify/add the ACLs defined, deleted all other ACLs. + - The state I(replaced), modify/add only the ACEs of the ACLs defined only. + It does not perform any other change on the device. + - The state I(deleted), deletes only the specified ACLs, or all if not specified. + type: str +short_description: Resource module to configure ACLs. +version_added: 1.0.0 +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# vios#sh access-lists +# Extended IP access list 100 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 echo dscp ef ttl eq 10 + +- name: Merge provided configuration with device configuration + cisco.ios.ios_acls: + config: + - afi: ipv4 + acls: + - name: 100 + aces: + - sequence: 10 + protocol_options: + icmp: + traceroute: true + state: merged + +# After state: +# ------------ +# +# Play Execution fails, with error: +# Cannot update existing sequence 10 of ACLs 100 with state merged. +# Please use state replaced or overridden. + +# Before state: +# ------------- +# +# vios#sh access-lists +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 echo dscp ef ttl eq 10 + +- name: Merge provided configuration with device configuration + cisco.ios.ios_acls: + config: + - afi: ipv4 + acls: + - name: std_acl + acl_type: standard + aces: + - grant: deny + source: + address: 192.168.1.200 + - grant: deny + source: + address: 192.168.2.0 + wildcard_bits: 0.0.0.255 + - name: 110 + aces: + - sequence: 10 + protocol_options: + icmp: + traceroute: true + - grant: deny + protocol_options: + tcp: + ack: true + source: + host: 198.51.100.0 + destination: + host: 198.51.110.0 + port_protocol: + eq: telnet + - name: test + acl_type: extended + aces: + - grant: deny + protocol_options: + tcp: + fin: true + source: + address: 192.0.2.0 + wildcard_bits: 0.0.0.255 + destination: + address: 192.0.3.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: www + option: + traceroute: true + ttl: + eq: 10 + - name: 123 + aces: + - remarks: + - "remarks for extended ACL 1" + - "check ACL" + - grant: deny + protocol_options: + tcp: + ack: true + source: + address: 198.51.100.0 + wildcard_bits: 0.0.0.255 + destination: + address: 198.51.101.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + tos: + service_value: 12 + - grant: deny + protocol_options: + tcp: + ack: true + source: + address: 192.0.3.0 + wildcard_bits: 0.0.0.255 + destination: + address: 192.0.4.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: www + dscp: ef + ttl: + lt: 20 + - afi: ipv6 + acls: + - name: R1_TRAFFIC + aces: + - grant: deny + protocol_options: + tcp: + ack: true + source: + any: true + port_protocol: + eq: www + destination: + any: true + port_protocol: + eq: telnet + dscp: af11 + state: merged + +# Commands fired: +# --------------- +# +# - ip access-list standard std_acl +# - deny 192.168.1.200 +# - deny 192.168.2.0 0.0.0.255 +# - ip access-list extended 110 +# - 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# - deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# - ip access-list extended test +# - deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# - ip access-list extended 123 +# - deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# - deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# - remark remarks for extended ACL 1 +# - remark check ACL +# - ipv6 access-list R1_TRAFFIC +# - deny tcp any eq www any eq telnet ack dscp af11 + +# After state: +# ------------ +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 100 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 echo dscp ef ttl eq 10 +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# 20 deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +# Using replaced + +# Before state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# 20 deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +- name: Replaces device configuration of listed acls with provided configuration + cisco.ios.ios_acls: + config: + - afi: ipv4 + acls: + - name: 110 + aces: + - grant: deny + protocol_options: + tcp: + syn: true + source: + address: 192.0.2.0 + wildcard_bits: 0.0.0.255 + destination: + address: 192.0.3.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: www + dscp: ef + ttl: + eq: 10 + - name: 150 + aces: + - grant: deny + sequence: 20 + protocol_options: + tcp: + syn: true + source: + address: 198.51.100.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + destination: + address: 198.51.110.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + dscp: ef + ttl: + eq: 10 + state: replaced + +# Commands fired: +# --------------- +# +# - no ip access-list extended 110 +# - ip access-list extended 110 +# - deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www syn dscp ef ttl eq 10 +# - ip access-list extended 150 +# - 20 deny tcp 198.51.100.0 0.0.0.255 eq telnet 198.51.110.0 0.0.0.255 eq telnet syn dscp ef ttl eq 10 + +# After state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www syn dscp ef ttl eq 10 +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list 150 +# 20 deny tcp 198.51.100.0 0.0.0.255 eq telnet 198.51.110.0 0.0.0.255 eq telnet syn dscp ef ttl eq 10 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +# Using overridden + +# Before state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# 20 deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +- name: Override device configuration of all acls with provided configuration + cisco.ios.ios_acls: + config: + - afi: ipv4 + acls: + - name: 110 + aces: + - grant: deny + sequence: 20 + protocol_options: + tcp: + ack: true + source: + address: 198.51.100.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + destination: + address: 198.51.110.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: www + dscp: ef + ttl: + eq: 10 + - name: 150 + aces: + - grant: deny + sequence: 10 + protocol_options: + tcp: + syn: true + source: + address: 198.51.100.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + destination: + address: 198.51.110.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + dscp: ef + ttl: + eq: 10 + state: overridden + +# Commands fired: +# --------------- +# +# - no ip access-list standard std_acl +# - no ip access-list extended 110 +# - no ip access-list extended 123 +# - no ip access-list extended 150 +# - no ip access-list extended test +# - no ipv6 access-list R1_TRAFFIC +# - ip access-list extended 150 +# - 10 deny tcp 198.51.100.0 0.0.0.255 eq telnet 198.51.110.0 0.0.0.255 eq telnet syn dscp ef ttl eq 10 +# - ip access-list extended 110 +# - 20 deny tcp 198.51.100.0 0.0.0.255 eq telnet 198.51.110.0 0.0.0.255 eq www ack dscp ef ttl eq 10 + +# After state: +# ------------- +# +# vios#sh access-lists +# Extended IP access list 110 +# 20 deny tcp 198.51.100.0 0.0.0.255 eq telnet 198.51.110.0 0.0.0.255 eq www ack dscp ef ttl eq 10 +# Extended IP access list 150 +# 10 deny tcp 198.51.100.0 0.0.0.255 eq telnet 198.51.110.0 0.0.0.255 eq telnet syn dscp ef ttl eq 10 + +# Using Deleted + +# Before state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# 20 deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +- name: "Delete ACLs (Note: This won't delete the all configured ACLs)" + cisco.ios.ios_acls: + config: + - afi: ipv4 + acls: + - name: test + acl_type: extended + - name: 110 + - afi: ipv6 + acls: + - name: R1_TRAFFIC + state: deleted + +# Commands fired: +# --------------- +# +# - no ip access-list extended test +# - no ip access-list extended 110 +# - no ipv6 access-list R1_TRAFFIC + +# After state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 + +# Before state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# 20 deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +- name: "Delete ACLs based on AFI (Note: This won't delete the all configured ACLs)" + cisco.ios.ios_acls: + config: + - afi: ipv4 + state: deleted + +# Commands fired: +# --------------- +# +# - no ip access-list standard std_acl +# - no ip access-list extended test +# - no ip access-list extended 110 +# - no ip access-list extended 123 + +# After state: +# ------------- +# +# vios#sh access-lists +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +# Using Deleted without any config passed +#"(NOTE: This will delete all of configured ACLs)" + +# Before state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# 20 deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +- name: + "Delete ALL of configured ACLs (Note: This WILL delete the all configured + ACLs)" + cisco.ios.ios_acls: + state: deleted + +# Commands fired: +# --------------- +# +# - no ip access-list extended test +# - no ip access-list extended 110 +# - no ip access-list extended 123 +# - no ip access-list extended test +# - no ipv6 access-list R1_TRAFFIC + +# After state: +# ------------- +# +# vios#sh access-lists + +# Using Gathered + +# Before state: +# ------------- +# +# vios#sh access-lists +# Standard IP access list std_acl +# 10 deny 192.168.1.200 +# 20 deny 192.168.2.0, wildcard bits 0.0.0.255 +# Extended IP access list 110 +# 10 deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 traceroute dscp ef ttl eq 10 +# 20 deny tcp host 198.51.100.0 host 198.51.110.0 eq telnet ack +# Extended IP access list 123 +# 10 deny tcp 198.51.100.0 0.0.0.255 198.51.101.0 0.0.0.255 eq telnet ack tos 12 +# 20 deny tcp 192.0.3.0 0.0.0.255 192.0.4.0 0.0.0.255 eq www ack dscp ef ttl lt 20 +# Extended IP access list test +# 10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www fin option traceroute ttl eq 10 +# IPv6 access list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 sequence 10 + +- name: Gather listed acls with provided configurations + cisco.ios.ios_acls: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "acls": [ +# { +# "aces": [ +# { +# "destination": { +# "address": "192.0.3.0", +# "wildcard_bits": "0.0.0.255" +# }, +# "dscp": "ef", +# "grant": "deny", +# "protocol_options": { +# "icmp": { +# "echo": true +# } +# }, +# "sequence": 10, +# "source": { +# "address": "192.0.2.0", +# "wildcard_bits": "0.0.0.255" +# }, +# "ttl": { +# "eq": 10 +# } +# } +# ], +# "acl_type": "extended", +# "name": "110" +# }, +# { +# "aces": [ +# { +# "destination": { +# "address": "198.51.101.0", +# "port_protocol": { +# "eq": "telnet" +# }, +# "wildcard_bits": "0.0.0.255" +# }, +# "grant": "deny", +# "protocol_options": { +# "tcp": { +# "ack": true +# } +# }, +# "sequence": 10, +# "source": { +# "address": "198.51.100.0", +# "wildcard_bits": "0.0.0.255" +# }, +# "tos": { +# "service_value": 12 +# } +# }, +# { +# "destination": { +# "address": "192.0.4.0", +# "port_protocol": { +# "eq": "www" +# }, +# "wildcard_bits": "0.0.0.255" +# }, +# "dscp": "ef", +# "grant": "deny", +# "protocol_options": { +# "tcp": { +# "ack": true +# } +# }, +# "sequence": 20, +# "source": { +# "address": "192.0.3.0", +# "wildcard_bits": "0.0.0.255" +# }, +# "ttl": { +# "lt": 20 +# } +# } +# ], +# "acl_type": "extended", +# "name": "123" +# }, +# { +# "aces": [ +# { +# "destination": { +# "address": "192.0.3.0", +# "port_protocol": { +# "eq": "www" +# }, +# "wildcard_bits": "0.0.0.255" +# }, +# "grant": "deny", +# "option": { +# "traceroute": true +# }, +# "protocol_options": { +# "tcp": { +# "fin": true +# } +# }, +# "sequence": 10, +# "source": { +# "address": "192.0.2.0", +# "wildcard_bits": "0.0.0.255" +# }, +# "ttl": { +# "eq": 10 +# } +# } +# ], +# "acl_type": "extended", +# "name": "test_acl" +# } +# ], +# "afi": "ipv4" +# }, +# { +# "acls": [ +# { +# "aces": [ +# { +# "destination": { +# "any": true, +# "port_protocol": { +# "eq": "telnet" +# } +# }, +# "dscp": "af11", +# "grant": "deny", +# "protocol_options": { +# "tcp": { +# "ack": true +# } +# }, +# "sequence": 10, +# "source": { +# "any": true, +# "port_protocol": { +# "eq": "www" +# } +# } +# } +# ], +# "name": "R1_TRAFFIC" +# } +# ], +# "afi": "ipv6" +# } +# ] + +# Using Rendered + +- name: Rendered the provided configuration with the existing running configuration + cisco.ios.ios_acls: + config: + - afi: ipv4 + acls: + - name: 110 + aces: + - grant: deny + sequence: 10 + protocol_options: + tcp: + syn: true + source: + address: 192.0.2.0 + wildcard_bits: 0.0.0.255 + destination: + address: 192.0.3.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: www + dscp: ef + ttl: + eq: 10 + - name: 150 + aces: + - grant: deny + protocol_options: + tcp: + syn: true + source: + address: 198.51.100.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + destination: + address: 198.51.110.0 + wildcard_bits: 0.0.0.255 + port_protocol: + eq: telnet + dscp: ef + ttl: + eq: 10 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "ip access-list extended 110", +# "10 deny tcp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 eq www syn dscp ef ttl eq 10", +# "ip access-list extended 150", +# "deny tcp 198.51.100.0 0.0.0.255 eq telnet 198.51.110.0 0.0.0.255 eq telnet syn dscp ef ttl eq 10" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# IPv6 access-list R1_TRAFFIC +# deny tcp any eq www any eq telnet ack dscp af11 + +- name: Parse the commands for provided configuration + cisco.ios.ios_acls: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "acls": [ +# { +# "aces": [ +# { +# "destination": { +# "any": true, +# "port_protocol": { +# "eq": "telnet" +# } +# }, +# "dscp": "af11", +# "grant": "deny", +# "protocol_options": { +# "tcp": { +# "ack": true +# } +# }, +# "source": { +# "any": true, +# "port_protocol": { +# "eq": "www" +# } +# } +# } +# ], +# "name": "R1_TRAFFIC" +# } +# ], +# "afi": "ipv6" +# } +# ] +""" + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: list + sample: + - ip access-list extended 110 + - deny icmp 192.0.2.0 0.0.0.255 192.0.3.0 0.0.0.255 echo dscp ef ttl eq 10 + - permit ip host 2.2.2.2 host 3.3.3.3 +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - ip access-list extended test + - permit ip host 2.2.2.2 host 3.3.3.3 + - permit tcp host 1.1.1.1 host 5.5.5.5 eq www +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.acls.acls import ( + AclsArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.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, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Acls(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_banner.py b/ansible_collections/cisco/ios/plugins/modules/ios_banner.py new file mode 100644 index 000000000..7c937b122 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_banner.py @@ -0,0 +1,205 @@ +#!/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: ios_banner +author: Ricardo Carrillo Cruz (@rcarrillocruz) +short_description: Module to configure multiline banners. +description: + - This will configure both login and motd banners on remote devices running Cisco + IOS. It allows playbooks to add or remote banner text from the active running configuration. +version_added: 1.0.0 +extends_documentation_fragment: + - cisco.ios.ios +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + banner: + description: + - Specifies which banner should be configured on the remote device. In Ansible + 2.4 and earlier only I(login) and I(motd) were supported. + required: true + choices: + - login + - motd + - exec + - incoming + - slip-ppp + type: str + multiline_delimiter: + description: + - Specify the delimiting character than will be used for configuration. + default: "@" + 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 + type: str + choices: + - present + - absent +""" + +EXAMPLES = """ +- name: Configure the login banner + cisco.ios.ios_banner: + banner: login + text: | + this is my login banner + that contains a multiline + string + state: present + +- name: Remove the motd banner + cisco.ios.ios_banner: + banner: motd + state: absent + +- name: Configure banner from file + cisco.ios.ios_banner: + banner: motd + text: "{{ lookup('file', './config_partial/raw_banner.cfg') }}" # Use unix formatted text files (LF not CRLF) to avoid idempotency issues. + state: present + +- name: Configure the login banner using delimiter + cisco.ios.ios_banner: + banner: login + multiline_delimiter: x + text: this is my login banner + state: present +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - banner login + - this is my login banner + - that contains a multiline + - string +""" +from re import M, search + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) + + +def map_obj_to_commands(updates, module): + commands = list() + want, have = updates + state = module.params["state"] + multiline_delimiter = module.params.get("multiline_delimiter") + if state == "absent" and "text" in have.keys() and have["text"]: + commands.append("no banner %s" % module.params["banner"]) + elif state == "present": + if have.get("text") and len(have.get("text")) > 1: + haved = have.get("text").rstrip("\n") + else: + haved = "" + if want["text"] and (want["text"].rstrip("\n") != haved): + banner_cmd = "banner %s" % module.params["banner"] + banner_cmd += " {0}\n".format(multiline_delimiter) + banner_cmd += want["text"].strip("\n") + banner_cmd += "\n{0}".format(multiline_delimiter) + commands.append(banner_cmd) + return commands + + +def map_config_to_obj(module): + """ + This function gets the banner config without stripping any whitespaces, + and then fetches the required banner from it. + :param module: + :return: banner config dict object. + """ + out = get_config(module, flags="| begin banner %s" % module.params["banner"]) + if out: + regex = search("banner " + module.params["banner"] + " \\^C{1,}\n", out, M) + if regex: + regex = regex.group() + output = str((out.split(regex))[1].split("^C\n")[0]) + else: + output = None + else: + output = None + 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": text, + "multiline_delimiter": module.params["multiline_delimiter"], + "state": module.params["state"], + } + + +def main(): + """main entry point for module execution""" + argument_spec = dict( + banner=dict(required=True, choices=["login", "motd", "exec", "incoming", "slip-ppp"]), + multiline_delimiter=dict(default="@"), + 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: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_bgp.py b/ansible_collections/cisco/ios/plugins/modules/ios_bgp.py new file mode 100644 index 000000000..b01f99428 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_bgp.py @@ -0,0 +1,504 @@ +#!/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: ios_bgp +author: Nilashish Chakraborty (@NilashishC) +short_description: Module to configure BGP protocol settings. +description: + - This module provides configuration management of global BGP parameters on devices + running Cisco IOS +version_added: 1.0.0 +deprecated: + alternative: ios_bgp_global + why: Newer and updated modules released with more functionality + removed_at_date: "2023-08-24" +notes: + - Tested against Cisco IOS Version 15.6(3)M2 +options: + config: + description: + - Specifies the BGP related configuration. + type: dict + suboptions: + bgp_as: + description: + - Specifies the BGP Autonomous System (AS) number to configure on the device. + type: int + required: true + router_id: + description: + - Configures the BGP routing process router-id value. + type: str + default: + log_neighbor_changes: + description: + - Enable/disable logging neighbor up/down and reset reason. + type: bool + neighbors: + description: + - Specifies BGP neighbor related configurations. + type: list + elements: dict + suboptions: + neighbor: + description: + - Neighbor router address. + required: true + type: str + remote_as: + description: + - Remote AS of the BGP neighbor to configure. + type: int + required: true + update_source: + description: + - Source of the routing updates. + type: str + password: + description: + - Password to authenticate the BGP peer connection. + type: str + enabled: + description: + - Administratively shutdown or enable a neighbor. + type: bool + description: + description: + - Neighbor specific description. + type: str + ebgp_multihop: + description: + - Specifies the maximum hop count for EBGP neighbors not on directly connected + networks. + - The range is from 1 to 255. + type: int + peer_group: + description: + - Name of the peer group that the neighbor is a member of. + type: str + timers: + description: + - Specifies BGP neighbor timer related configurations. + type: dict + suboptions: + keepalive: + description: + - Frequency (in seconds) with which the device sends keepalive messages + to its peer. + - The range is from 0 to 65535. + type: int + required: true + holdtime: + description: + - Interval (in seconds) after not receiving a keepalive message that + IOS declares a peer dead. + - The range is from 0 to 65535. + type: int + required: true + min_neighbor_holdtime: + description: + - Interval (in seconds) specifying the minimum acceptable hold-time + from a BGP neighbor. + - The minimum acceptable hold-time must be less than, or equal to, + the interval specified in the holdtime argument. + - The range is from 0 to 65535. + type: int + local_as: + description: + - The local AS number for the neighbor. + type: int + networks: + description: + - Specify Networks to announce via BGP. + - For operation replace, this option is mutually exclusive with networks option + under address_family. + - For operation replace, if the device already has an address family activated, + this option is not allowed. + type: list + elements: dict + suboptions: + prefix: + description: + - Network ID to announce via BGP. + required: true + type: str + masklen: + description: + - Subnet mask length for the Network to announce(e.g, 8, 16, 24, etc.). + type: int + route_map: + description: + - Route map to modify the attributes. + type: str + address_family: + description: + - Specifies BGP address family related configurations. + type: list + elements: dict + suboptions: + afi: + description: + - Type of address family to configure. + choices: + - ipv4 + - ipv6 + required: true + type: str + safi: + description: + - Specifies the type of cast for the address family. + choices: + - flowspec + - unicast + - multicast + - labeled-unicast + default: unicast + type: str + synchronization: + description: + - Enable/disable IGP synchronization. + type: bool + auto_summary: + description: + - Enable/disable automatic network number summarization. + type: bool + redistribute: + description: + - Specifies the redistribute information from another routing protocol. + type: list + elements: dict + suboptions: + protocol: + description: + - Specifies the protocol for configuring redistribute information. + choices: + - ospf + - ospfv3 + - eigrp + - isis + - static + - connected + - odr + - lisp + - mobile + - rip + required: true + type: str + id: + description: + - Identifier for the routing protocol for configuring redistribute + information. + - Valid for protocols 'ospf', 'ospfv3' and 'eigrp'. + type: str + metric: + description: + - Specifies the metric for redistributed routes. + type: int + route_map: + description: + - Specifies the route map reference. + type: str + networks: + description: + - Specify Networks to announce via BGP. + - For operation replace, this option is mutually exclusive with root level + networks option. + type: list + elements: dict + suboptions: + prefix: + description: + - Network ID to announce via BGP. + required: true + type: str + masklen: + description: + - Subnet mask length for the Network to announce(e.g, 8, 16, 24, etc.). + type: int + route_map: + description: + - Route map to modify the attributes. + type: str + neighbors: + description: + - Specifies BGP neighbor related configurations in Address Family configuration + mode. + type: list + elements: dict + suboptions: + neighbor: + description: + - Neighbor router address. + required: true + type: str + advertisement_interval: + description: + - Minimum interval between sending BGP routing updates for this neighbor. + type: int + route_reflector_client: + description: + - Specify a neighbor as a route reflector client. + type: bool + route_server_client: + description: + - Specify a neighbor as a route server client. + type: bool + activate: + description: + - Enable the Address Family for this Neighbor. + type: bool + remove_private_as: + description: + - Remove the private AS number from outbound updates. + type: bool + next_hop_self: + description: + - Enable/disable the next hop calculation for this neighbor. + type: bool + next_hop_unchanged: + description: + - Propagate next hop unchanged for iBGP paths to this neighbor. + type: bool + maximum_prefix: + description: + - Maximum number of prefixes to accept from this peer. + - The range is from 1 to 2147483647. + type: int + prefix_list_in: + description: + - Name of ip prefix-list to apply to incoming prefixes. + type: str + prefix_list_out: + description: + - Name of ip prefix-list to apply to outgoing prefixes. + type: str + operation: + description: + - Specifies the operation to be performed on the BGP process configured on the + device. + - In case of merge, the input configuration will be merged with the existing BGP + configuration on the device. + - In case of replace, if there is a diff between the existing configuration and + the input configuration, the existing configuration will be replaced by the + input configuration for every option that has the diff. + - In case of override, all the existing BGP configuration will be removed from + the device and replaced with the input configuration. + - In case of delete the existing BGP configuration will be removed from the device. + default: merge + type: str + choices: + - merge + - replace + - override + - delete + +""" + +EXAMPLES = """ +- name: Configure global bgp as 64496 + cisco.ios.ios_bgp: + config: + bgp_as: 64496 + router_id: 192.0.2.1 + log_neighbor_changes: true + neighbors: + - neighbor: 203.0.113.5 + remote_as: 64511 + timers: + keepalive: 300 + holdtime: 360 + min_neighbor_holdtime: 360 + - neighbor: 198.51.100.2 + remote_as: 64498 + networks: + - prefix: 198.51.100.0 + route_map: RMAP_1 + - prefix: 192.0.2.0 + masklen: 23 + address_family: + - afi: ipv4 + safi: unicast + redistribute: + - protocol: ospf + id: 223 + metric: 10 + operation: merge + +- name: Configure BGP neighbors + cisco.ios.ios_bgp: + config: + bgp_as: 64496 + neighbors: + - neighbor: 192.0.2.10 + remote_as: 64496 + password: ansible + description: IBGP_NBR_1 + ebgp_multihop: 100 + timers: + keepalive: 300 + holdtime: 360 + min_neighbor_holdtime: 360 + - neighbor: 192.0.2.15 + remote_as: 64496 + description: IBGP_NBR_2 + ebgp_multihop: 150 + operation: merge + +- name: Configure root-level networks for BGP + cisco.ios.ios_bgp: + config: + bgp_as: 64496 + networks: + - prefix: 203.0.113.0 + masklen: 27 + route_map: RMAP_1 + - prefix: 203.0.113.32 + masklen: 27 + route_map: RMAP_2 + operation: merge + +- name: Configure BGP neighbors under address family mode + cisco.ios.ios_bgp: + config: + bgp_as: 64496 + address_family: + - afi: ipv4 + safi: unicast + neighbors: + - neighbor: 203.0.113.10 + activate: true + maximum_prefix: 250 + advertisement_interval: 120 + - neighbor: 192.0.2.15 + activate: true + route_reflector_client: true + operation: merge + +- name: Remove bgp as 64496 from config + cisco.ios.ios_bgp: + config: + bgp_as: 64496 + operation: delete +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - router bgp 64496 + - bgp router-id 192.0.2.1 + - bgp log-neighbor-changes + - neighbor 203.0.113.5 remote-as 64511 + - neighbor 203.0.113.5 timers 300 360 360 + - neighbor 198.51.100.2 remote-as 64498 + - network 198.51.100.0 route-map RMAP_1 + - network 192.0.2.0 mask 255.255.254.0 + - address-family ipv4 + - redistribute ospf 223 metric 70 + - exit-address-family +""" +from ansible.module_utils._text import to_text + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.cli.config.bgp.process import ( + REDISTRIBUTE_PROTOCOLS, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.module import ( + NetworkModule, +) + + +def main(): + """main entry point for module execution""" + network_spec = {"prefix": dict(required=True), "masklen": dict(type="int"), "route_map": dict()} + redistribute_spec = { + "protocol": dict(choices=REDISTRIBUTE_PROTOCOLS, required=True), + "id": dict(), + "metric": dict(type="int"), + "route_map": dict(), + } + timer_spec = { + "keepalive": dict(type="int", required=True), + "holdtime": dict(type="int", required=True), + "min_neighbor_holdtime": dict(type="int"), + } + neighbor_spec = { + "neighbor": dict(required=True), + "remote_as": dict(type="int", required=True), + "local_as": dict(type="int"), + "update_source": dict(), + "password": dict(no_log=True), + "enabled": dict(type="bool"), + "description": dict(), + "ebgp_multihop": dict(type="int"), + "timers": dict(type="dict", options=timer_spec), + "peer_group": dict(), + } + af_neighbor_spec = { + "neighbor": dict(required=True), + "activate": dict(type="bool"), + "advertisement_interval": dict(type="int"), + "remove_private_as": dict(type="bool"), + "next_hop_self": dict(type="bool"), + "next_hop_unchanged": dict(type="bool"), + "route_reflector_client": dict(type="bool"), + "route_server_client": dict(type="bool"), + "maximum_prefix": dict(type="int"), + "prefix_list_in": dict(), + "prefix_list_out": dict(), + } + address_family_spec = { + "afi": dict(choices=["ipv4", "ipv6"], required=True), + "safi": dict( + choices=["flowspec", "labeled-unicast", "multicast", "unicast"], + default="unicast", + ), + "auto_summary": dict(type="bool"), + "synchronization": dict(type="bool"), + "networks": dict(type="list", elements="dict", options=network_spec), + "redistribute": dict(type="list", elements="dict", options=redistribute_spec), + "neighbors": dict(type="list", elements="dict", options=af_neighbor_spec), + } + config_spec = { + "bgp_as": dict(type="int", required=True), + "router_id": dict(), + "log_neighbor_changes": dict(type="bool"), + "neighbors": dict(type="list", elements="dict", options=neighbor_spec), + "address_family": dict(type="list", elements="dict", options=address_family_spec), + "networks": dict(type="list", elements="dict", options=network_spec), + } + argument_spec = { + "config": dict(type="dict", options=config_spec), + "operation": dict(default="merge", choices=["merge", "replace", "override", "delete"]), + } + module = NetworkModule(argument_spec=argument_spec, supports_check_mode=True) + try: + result = module.edit_config(config_filter="| section ^router bgp") + except Exception as exc: + module.fail_json(msg=to_text(exc)) + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_bgp_address_family.py b/ansible_collections/cisco/ios/plugins/modules/ios_bgp_address_family.py new file mode 100644 index 000000000..2021d2aec --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_bgp_address_family.py @@ -0,0 +1,3010 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_bgp_address_family +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_bgp_address_family +short_description: Resource module to configure BGP Address family. +description: This module configures and manages the attributes of bgp address family on Cisco IOS. +version_added: 1.2.0 +author: + - Sagar Paul (@KB-perByte) + - Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) + - The module examples uses callback plugin (stdout_callback = yaml) to generate task + output in yaml format. +options: + config: + description: A list of configurations for bgp address family. + type: dict + suboptions: + as_number: + description: Autonomous system number. + type: str + address_family: + description: A list of configurations for bgp address family. + type: list + elements: dict + suboptions: + afi: + description: Address Family + type: str + choices: + ["ipv4", "ipv6", "l2vpn", "nsap", "rtfilter", "vpnv4", "vpnv6"] + safi: + description: Address Family modifier + type: str + choices: ["flowspec", "mdt", "multicast", "mvpn", "evpn", "unicast"] + vrf: + description: Specify parameters for a VPN Routing/Forwarding instance + type: str + aggregate_addresses: + description: Configure BGP aggregate entries + type: list + elements: dict + aliases: + - aggregate_address + suboptions: + address: + description: Aggregate address(A.B.C.D) + type: str + netmask: + description: Aggregate mask(A.B.C.D) + type: str + advertise_map: + description: Set condition to advertise attribute + type: str + as_confed_set: + description: Generate AS confed set path information + type: bool + as_set: + description: Generate AS set path information + type: bool + attribute_map: + description: Set attributes of aggregate + type: str + summary_only: + description: Filter more specific routes from updates + type: bool + suppress_map: + description: Conditionally filter more specific routes from updates + type: str + auto_summary: + description: Enable automatic network number summarization + type: bool + bgp: + description: Configure BGP aggregate entries + type: dict + suboptions: + additional_paths: + description: Additional paths in the BGP table + type: dict + suboptions: + receive: + description: Receive additional paths from neighbors + type: bool + select: + description: Selection criteria to pick the paths + type: dict + suboptions: + all: + description: Select all available paths + type: bool + backup: + description: Select backup path + type: bool + best: + description: Select best N paths (2-3). + type: int + best_external: + description: Select best-external path + type: bool + group_best: + description: Select group-best path + type: bool + send: + description: Send additional paths to neighbors + type: bool + install: + description: Additional paths to install into RIB + type: bool + aggregate_timer: + description: + - Configure Aggregation Timer + - Please refer vendor documentation for valid values + type: int + dampening: + description: Enable route-flap dampening + type: dict + suboptions: + penalty_half_time: + description: + - Half-life time for the penalty + - Please refer vendor documentation for valid values + type: int + reuse_route_val: + description: + - Value to start reusing a route + - Please refer vendor documentation for valid values + type: int + suppress_route_val: + description: + - Value to start suppressing a route + - Please refer vendor documentation for valid values + type: int + max_suppress: + description: + - Maximum duration to suppress a stable route + - Please refer vendor documentation for valid values + type: int + route_map: + description: Route-map to specify criteria for dampening + type: str + dmzlink_bw: + description: Use DMZ Link Bandwidth as weight for BGP multipaths + type: bool + nexthop: + description: Nexthop tracking commands + type: dict + suboptions: + route_map: + description: Route map for valid nexthops + type: str + trigger: + description: Nexthop triggering + type: dict + suboptions: + delay: + description: + - Set the delay to tigger nexthop tracking + - Please refer vendor documentation for valid values + type: int + enable: + description: Enable nexthop tracking + type: bool + redistribute_internal: + description: Allow redistribution of iBGP into IGPs (dangerous) + type: bool + route_map: + description: + - route-map control commands + - Have route-map set commands take priority over BGP commands such as next-hop unchanged + type: bool + scan_time: + description: + - Configure background scanner interval + - Please refer vendor documentation for valid values + type: int + slow_peer: + description: + - Nexthop triggering + - This option is DEPRECATED and replaced with slow_peer_options, + this attribute will be removed after 2025-01-01. + type: list + elements: dict + suboptions: + detection: + description: Slow-peer detection + type: dict + suboptions: + enable: + description: Enable slow-peer detection + type: bool + threshold: + description: + - Set the slow-peer detection threshold + - Threshold value (seconds) + - Please refer vendor documentation for valid values + type: int + split_update_group: + description: Configure slow-peer split-update-group + type: dict + suboptions: + dynamic: + description: Dynamically split the slow peer to slow-update group + type: bool + permanent: + description: Keep the slow-peer permanently in slow-update group + type: bool + slow_peer_options: + description: Nexthop triggering + type: dict + suboptions: + detection: + description: Slow-peer detection + type: dict + suboptions: + enable: + description: Enable slow-peer detection + type: bool + threshold: + description: + - Set the slow-peer detection threshold + - Threshold value (seconds) + - Please refer vendor documentation for valid values + type: int + split_update_group: + description: Configure slow-peer split-update-group + type: dict + suboptions: + dynamic: + description: Dynamically split the slow peer to slow-update group + type: bool + permanent: + description: Keep the slow-peer permanently in slow-update group + type: bool + soft_reconfig_backup: + description: Use soft-reconfiguration inbound only when route-refresh is not negotiated + type: bool + update_group: + description: + - Manage peers in bgp update groups + - Split update groups based on Policy + - Keep peers with as-override in different update groups + type: bool + default: + description: Set a command to its defaults + type: bool + default_information: + description: + - Distribution of default information + - Distribute default route + type: bool + default_metric: + description: Set metric of redistributed routes + type: int + distance: + description: Define an administrative distance + type: dict + suboptions: + external: + description: Distance for routes external to the AS + type: int + internal: + description: Distance for routes internal to the AS + type: int + local: + description: Distance for local routes + type: int + neighbors: + description: Specify a neighbor router + type: list + elements: dict + aliases: + - neighbor + suboptions: + neighbor_address: + description: + - Neighbor address (A.B.C.D) + - Neighbor tag + - Neighbor ipv6 address (X:X:X:X::X) + type: str + address: + description: + - Neighbor address (A.B.C.D) + - This option is DEPRECATED and replaced with neighbor_address, + this attribute will be removed after 2025-01-01. + type: str + tag: + description: + - Neighbor tag + - This option is DEPRECATED and replaced with neighbor_address, + this attribute will be removed after 2025-01-01. + type: str + ipv6_address: + description: + - Neighbor ipv6 address (X:X:X:X::X) + - This option is DEPRECATED and replaced with neighbor_address, + this attribute will be removed after 2025-01-01. + type: str + aliases: + - ipv6_adddress + activate: + description: Enable the Address Family for this Neighbor + type: bool + additional_paths: + description: Negotiate additional paths capabilities with this neighbor + type: dict + suboptions: + disable: + description: Disable additional paths for this neighbor + type: bool + receive: + description: Receive additional paths from neighbors + type: bool + send: + description: Send additional paths to this neighbor + type: bool + advertise: + description: + - Advertise to this neighbor + - Advertise additional paths + type: dict + suboptions: + all: + description: Select all available paths + type: bool + best: + description: Select best N paths (2-3). + type: int + group_best: + description: Select group-best path + type: bool + advertises: + description: Advertise to this neighbor + type: dict + suboptions: + additional_paths: + description: Advertise additional paths + type: dict + suboptions: + all: + description: Select all available paths + type: bool + best: + description: Select best N paths (2-3). + type: int + group_best: + description: Select group-best path + type: bool + best_external: + description: Advertise best-external (at RRs best-internal) path + type: bool + diverse_path: + description: Advertise additional paths + type: dict + suboptions: + backup: + description: Diverse path can be backup path + type: bool + mpath: + description: Diverse path can be multipath + type: bool + advertise_map: + description: specify route-map for conditional advertisement + type: dict + suboptions: + name: + description: advertise route-map name + type: str + exist_map: + description: + - advertise prefix only if prefix is in the condition exists + - condition route-map name + type: str + non_exist_map: + description: + - advertise prefix only if prefix in the condition does not exist + - condition route-map name + type: str + advertisement_interval: + description: Minimum interval between sending BGP routing updates + type: int + aigp: + description: Enable a AIGP on neighbor + type: dict + suboptions: + enable: + description: Enable a AIGP on neighbor + type: str + send: + description: Cost community or MED carrying AIGP VALUE + type: dict + suboptions: + cost_community: + description: Cost extended community carrying AIGP Value + type: dict + suboptions: + id: + description: + - Community ID + - Please refer vendor documentation for valid values + type: int + poi: + description: Point of Insertion + type: dict + suboptions: + igp_cost: + description: Point of Insertion After IGP + type: bool + pre_bestpath: + description: Point of Insertion At Beginning + type: bool + transitive: + description: Cost community is Transitive + type: bool + med: + description: Med carrying AIGP Value + type: bool + allow_policy: + description: Enable the policy support for this IBGP Neighbor + type: bool + allowas_in: + description: + - Accept as-path with my AS present in it + - Please refer vendor documentation for valid values + type: int + as_override: + description: Override matching AS-number while sending update + type: dict + suboptions: + set: + description: Enable AS override + type: bool + split_horizon: + description: Maintain Split Horizon while sending update + type: bool + bmp_activate: + description: Activate the BMP monitoring for a BGP peer + type: dict + suboptions: + all: + description: Activate BMP monitoring for all servers + type: bool + server: + description: + - Activate BMP for server + - BMP Server Number + - Please refer vendor documentation for valid values + type: int + capability: + description: + - Advertise capability to the peer + - Advertise ORF capability to the peer + - Advertise prefixlist ORF capability to this neighbor + type: dict + suboptions: + both: + description: Capability to SEND and RECEIVE the ORF to/from this neighbor + type: bool + receive: + description: Capability to RECEIVE the ORF from this neighbor + type: bool + send: + description: Capability to SEND the ORF to this neighbor + type: bool + cluster_id: + description: + - Configure Route-Reflector Cluster-id (peers may reset) + - Route-Reflector Cluster-id as 32 bit quantity, or + Route-Reflector Cluster-id in IP address format (A.B.C.D) + type: str + default_originate: + description: Originate default route to this neighbor + type: dict + suboptions: + set: + description: Set default route to this neighbor + type: bool + route_map: + description: Route-map to specify criteria to originate default + type: str + description: + description: Neighbor specific description + type: str + disable_connected_check: + description: one-hop away EBGP peer using loopback address + type: bool + distribute_list: + description: Filter updates to/from this neighbor + type: dict + suboptions: + acl: + description: ACL id/name + type: str + in: + description: Filter incoming updates + type: bool + out: + description: Filter outgoing updates + type: bool + dmzlink_bw: + description: Propagate the DMZ link bandwidth + type: bool + ebgp_multihop: + description: Allow EBGP neighbors not on directly connected networks + type: dict + suboptions: + enable: + description: Allow EBGP neighbors not on directly connected networks + type: bool + hop_count: + description: + - Maximum hop count + - Please refer vendor documentation for valid values + type: int + fall_over: + description: Session fall on peer route lost + type: dict + suboptions: + bfd: + description: Use BFD to detect failure + type: dict + suboptions: + set: + description: set bfd + type: bool + multi_hop: + description: Force BFD multi-hop to detect failure + type: bool + single_hop: + description: Force BFD single-hop to detect failure + type: bool + route_map: + description: Route map for peer route + type: str + filter_list: + description: Establish BGP filters + type: dict + suboptions: + as_path_acl: + description: + - AS path access list + - Please refer vendor documentation for valid values + type: int + in: + description: Filter incoming updates + type: bool + out: + description: Filter outgoing updates + type: bool + ha_mode: + description: high availability mode + type: dict + suboptions: + set: + description: set ha-mode and graceful-restart for this peer + type: bool + disable: + description: disable graceful-restart + type: bool + inherit: + description: + - Inherit a template + - Inherit a peer-policy template + type: str + internal_vpn_client: + description: Stack iBGP-CE Neighbor Path in ATTR_SET for vpn update + type: bool + local_as: + description: Specify a local-as number + type: dict + suboptions: + set: + description: set local-as number + type: bool + number: + description: + - AS number used as local AS + - Please refer vendor documentation for valid values + type: int + dual_as: + description: Accept either real AS or local AS from the ebgp peer + type: bool + no_prepend: + description: Do not prepend local-as to updates from ebgp peers + type: dict + suboptions: + set: + description: Set prepend + type: bool + replace_as: + description: Replace real AS with local AS in the EBGP updates + type: bool + log_neighbor_changes: + description: Log neighbor up/down and reset reason + type: dict + suboptions: + set: + description: set Log neighbor up/down and reset + type: bool + disable: + description: disable Log neighbor up/down and reset + type: bool + maximum_prefix: + description: Establish BGP filters + type: dict + suboptions: + number: + description: + - maximum no. of prefix limit + - Please refer vendor documentation for valid values + type: int + threshold_value: + description: + - Threshold value (%) at which to generate a warning msg + - Please refer vendor documentation for valid values + type: int + restart: + description: Restart bgp connection after limit is exceeded + type: int + warning_only: + description: Only give warning message when limit is exceeded + type: bool + next_hop_self: + description: + - Disable the next hop calculation for this neighbor + - This option is DEPRECATED and is replaced with nexthop_self which + accepts dict as input this attribute will be removed after 2023-06-01. + type: bool + nexthop_self: + description: Disable the next hop calculation for this neighbor + type: dict + suboptions: + set: + description: set the next hop self + type: bool + all: + description: Enable next-hop-self for both eBGP and iBGP received paths + type: bool + next_hop_unchanged: + description: + - Propagate next hop unchanged for iBGP paths to this neighbor + - Propagate next hop unchanged for all paths (iBGP and eBGP) to this neighbor + type: dict + suboptions: + set: + description: Enable next-hop-unchanged + type: bool + allpaths: + description: Propagate next hop unchanged for all paths (iBGP and eBGP) to this neighbor + type: bool + password: + description: + - Set a password + - This option is DEPRECATED and is replaced with password_options which + accepts dict as input, this attribute will be removed after 2024-06-01. + type: str + password_options: + description: Set a password with encryption type + type: dict + suboptions: + encryption: + description: Encryption type (0 to disable encryption, 7 for proprietary) + type: int + pass_key: + description: The password + type: str + path_attribute: + description: BGP optional attribute filtering + type: dict + suboptions: + discard: + description: Discard matching path-attribute for this neighbor + type: dict + suboptions: + type: + description: + - path attribute type + - Please refer vendor documentation for valid values + type: int + range: + description: path attribute range + type: dict + suboptions: + start: + description: + - path attribute range start value + - Please refer vendor documentation for valid values + type: int + end: + description: + - path attribute range end value + - Please refer vendor documentation for valid values + type: int + in: + description: Perform inbound path-attribute filtering + type: bool + treat_as_withdraw: + description: Treat-as-withdraw matching path-attribute for this neighbor + type: dict + suboptions: + type: + description: + - path attribute type + - Please refer vendor documentation for valid values + type: int + range: + description: path attribute range + type: dict + suboptions: + start: + description: + - path attribute range start value + - Please refer vendor documentation for valid values + type: int + end: + description: + - path attribute range end value + - Please refer vendor documentation for valid values + type: int + in: + description: Perform inbound path-attribute filtering + type: bool + peer_group: + description: Member of the peer-group + type: bool + peer_group_name: + description: Member of the peer-group + type: str + prefix_list: + description: + - Filter updates to/from this neighbor + - This option is DEPRECATED and is replaced with prefix_lists which + accepts list of dict as input + type: dict + suboptions: + name: + description: Name of a prefix list + type: str + in: + description: Filter incoming updates + type: bool + out: + description: Filter outgoing updates + type: bool + prefix_lists: + description: Filter updates to/from this neighbor + type: list + elements: dict + suboptions: + name: + description: Name of a prefix list + type: str + in: + description: Filter incoming updates + type: bool + out: + description: Filter outgoing updates + type: bool + remote_as: + description: + - Specify a BGP neighbor + - AS of remote neighbor + type: int + remove_private_as: + description: Remove private AS number from outbound updates + type: dict + suboptions: + set: + description: Remove private AS number from outbound updates + type: bool + all: + description: Remove all private AS numbers + type: bool + replace_as: + description: Replace all private AS numbers with local AS + type: bool + route_map: + description: + - Apply route map to neighbor + - This option is DEPRECATED and is replaced with route_maps which + accepts list of dict as input + type: dict + suboptions: + name: + description: Name of route map + type: str + in: + description: Apply map to incoming routes + type: bool + out: + description: Apply map to outbound routes + type: bool + route_maps: + description: Apply route map to neighbor + type: list + elements: dict + suboptions: + name: + description: Name of route map + type: str + in: + description: Apply map to incoming routes + type: bool + out: + description: Apply map to outbound routes + type: bool + route_reflector_client: + description: Configure a neighbor as Route Reflector client + type: bool + route_server_client: + description: Configure a neighbor as Route Server client + type: bool + send_community: + description: Send Community attribute to this neighbor + type: dict + suboptions: + set: + description: + - Send Standard Community attribute. + - Maintains backwards compatibility for configurations that do not specify a send-community type. + type: bool + both: + description: Send Standard and Extended Community attributes + type: bool + extended: + description: Send Extended Community attribute + type: bool + standard: + description: Send Standard Community attribute + type: bool + shutdown: + description: Administratively shut down this neighbor + type: dict + suboptions: + set: + description: shut down + type: bool + graceful: + description: + - Gracefully shut down this neighbor + - time in seconds + - Please refer vendor documentation for valid values + type: int + slow_peer: + description: + - Configure slow-peer + - This option is DEPRECATED and replaced with slow_peer_options, + this attribute will be removed after 2025-01-01. + type: list + elements: dict + suboptions: + detection: + description: Configure slow-peer + type: dict + suboptions: + enable: + description: Enable slow-peer detection + type: bool + disable: + description: Disable slow-peer detection + type: bool + threshold: + description: Set the slow-peer detection threshold + type: int + split_update_group: + description: Configure slow-peer + type: dict + suboptions: + dynamic: + description: Configure slow-peer + type: dict + suboptions: + enable: + description: Configure slow-peer + type: bool + disable: + description: Configure slow-peer + type: bool + permanent: + description: Configure slow-peer + type: bool + static: + description: Configure slow-peer + type: bool + slow_peer_options: + description: Configure slow-peer options + type: dict + suboptions: + detection: + description: Configure slow-peer + type: dict + suboptions: + enable: + description: Enable slow-peer detection + type: bool + disable: + description: Disable slow-peer detection + type: bool + threshold: + description: Set the slow-peer detection threshold + type: int + split_update_group: + description: Configure slow-peer + type: dict + suboptions: + dynamic: + description: Configure slow-peer + type: dict + suboptions: + enable: + description: Configure slow-peer + type: bool + disable: + description: Configure slow-peer + type: bool + permanent: + description: Configure slow-peer + type: bool + static: + description: Configure slow-peer + type: bool + soft_reconfiguration: + description: + - Per neighbor soft reconfiguration + - Allow inbound soft reconfiguration for this neighbor + type: bool + soo: + description: Site-of-Origin extended community + type: str + timers: + description: BGP per neighbor timers + type: dict + suboptions: + interval: + description: Keepalive interval + type: int + holdtime: + description: Holdtime + type: int + min_holdtime: + description: Minimum hold time from neighbor + type: int + transport: + description: Transport options + type: dict + suboptions: + connection_mode: + description: Specify passive or active connection + type: dict + suboptions: + active: + description: Actively establish the TCP session + type: bool + passive: + description: Passively establish the TCP session + type: bool + multi_session: + description: Use Multi-session for transport + type: bool + path_mtu_discovery: + description: Use transport path MTU discovery + type: dict + suboptions: + set: + description: Use path MTU discovery + type: bool + disable: + description: disable + type: bool + ttl_security: + description: + - BGP ttl security check + - maximum number of hops + - Please refer vendor documentation for valid values + type: int + unsuppress_map: + description: Route-map to selectively unsuppress suppressed routes + type: str + version: + description: + - Set the BGP version to match a neighbor + - Neighbor's BGP version + - Please refer vendor documentation for valid values + type: int + weight: + description: Set default weight for routes from this neighbor + type: int + networks: + description: Specify a network to announce via BGP + type: list + elements: dict + aliases: + - network + suboptions: + address: + description: Network number (A.B.C.D) + type: str + mask: + description: Network mask (A.B.C.D) + type: str + backdoor: + description: Specify a BGP backdoor route + type: bool + evpn: + description: Advertise or Export to EVPN address-family + type: bool + route_map: + description: Route-map to modify the attributes + type: str + redistribute: + description: + - Redistribute information from another routing protocol + type: list + elements: dict + suboptions: + application: + description: Application + type: dict + suboptions: + name: + description: Application name + type: str + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + bgp: + description: Border Gateway Protocol (BGP) + type: dict + suboptions: + as_number: + description: Autonomous system number + type: str + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + connected: + description: Connected + type: dict + suboptions: + set: + description: + - Redistribute automatically established IP connected routes. + - This only needs to be set if metric or route_map aren't used. + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + eigrp: + description: Enhanced Interior Gateway Routing Protocol (EIGRP) + type: dict + suboptions: + as_number: + description: Autonomous system number + type: str + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + isis: + description: ISO IS-IS + type: dict + suboptions: + area_tag: + description: ISO routing area tag + type: str + clns: + description: Redistribution of OSI dynamic routes + type: bool + ip: + description: Redistribution of IP dynamic routes + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + iso_igrp: + description: IGRP for OSI networks + type: dict + suboptions: + area_tag: + description: ISO routing area tag + type: str + route_map: + description: Route map reference + type: str + lisp: + description: Locator ID Separation Protocol (LISP) + type: dict + suboptions: + set: + description: Set the top level attribute + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + mobile: + description: Mobile routes + type: dict + suboptions: + set: + description: Set the top level attribute + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + odr: + description: On Demand stub Routes + type: dict + suboptions: + set: + description: Set the top level attribute + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + ospf: + description: Open Shortest Path First (OSPF) + type: dict + suboptions: + process_id: + description: Process ID + type: int + include_connected: + description: Include connected. Only applicable under IPv6 AFI + type: bool + match: + description: Redistribute matched routes + type: dict + suboptions: + external: + description: + - Redistribute OSPF external routes + - This option is DEPRECATED and replaced with externals option, + this attribute will be removed after 2025-01-01. + type: bool + externals: + description: Redistribute OSPF external routes + type: dict + suboptions: + type_1: + description: Redistribute OSPF External type 1 routes + type: bool + type_2: + description: Redistribute OSPF External type 1 routes + type: bool + internal: + description: Redistribute OSPF internal routes + type: bool + nssa_external: + description: + - Redistribute OSPF NSSA external routes + - This option is DEPRECATED and replaced with nssa_externals option, + this attribute will be removed after 2025-01-01. + type: bool + type_1: + description: + - Redistribute NSSA external type 1 routes + - This option is DEPRECATED and replaced with nssa_externals.type_1 option, + this attribute will be removed after 2025-01-01. + type: bool + type_2: + description: + - Redistribute NSSA external type 2 routes + - This option is DEPRECATED and replaced with nssa_externals.type_2 option, + this attribute will be removed after 2025-01-01. + type: bool + nssa_externals: + description: Redistribute OSPF NSSA external routes + type: dict + suboptions: + type_1: + description: Redistribute NSSA external type 1 routes + type: bool + type_2: + description: Redistribute NSSA external type 2 routes + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + vrf: + description: VPN Routing/Forwarding Instance + type: str + ospfv3: + description: OSPFv3 + type: dict + suboptions: + process_id: + description: Process ID + type: int + match: + description: Redistribute matched routes + type: dict + suboptions: + external: + description: + - Redistribute OSPF external routes + - This option is DEPRECATED and replaced with externals, + this attribute will be removed after 2025-01-01. + type: bool + externals: + description: Redistribute OSPF external routes + type: dict + suboptions: + type_1: + description: Redistribute OSPF External type 1 routes + type: bool + type_2: + description: Redistribute OSPF External type 1 routes + type: bool + internal: + description: Redistribute OSPF internal routes + type: bool + nssa_external: + description: + - Redistribute OSPF internal routes + - This option is DEPRECATED and replaced with nssa_externals, + this attribute will be removed after 2025-01-01. + type: bool + type_1: + description: + - Redistribute NSSA external type 1 routes + - This option is DEPRECATED and replaced with nssa_externals.type_1 option, + this attribute will be removed after 2025-01-01. + type: bool + type_2: + description: + - Redistribute NSSA external type 2 routes + - This option is DEPRECATED and replaced with nssa_externals.type_2 option, + this attribute will be removed after 2025-01-01. + type: bool + nssa_externals: + description: Redistribute OSPF NSSA external routes + type: dict + suboptions: + type_1: + description: Redistribute NSSA external type 1 routes + type: bool + type_2: + description: Redistribute NSSA external type 2 routes + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + rip: + description: Routing Information Protocol (RIP) + type: dict + suboptions: + set: + description: Set the top level attribute + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + static: + description: Static routes + type: dict + suboptions: + set: + description: Set the top level attribute + type: bool + clns: + description: Redistribution of OSI static routes + type: bool + ip: + description: Redistribution of IP static routes + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + vrf: + description: Specify a source VRF + type: dict + suboptions: + name: + description: Source VRF name + type: str + global: + description: global VRF + type: bool + snmp: + description: Modify snmp parameters + type: dict + suboptions: + context: + description: + - Configure a SNMP context + - Context Name + type: dict + suboptions: + name: + description: Context Name + type: str + community: + description: Configure a SNMP v2c Community string and access privs + type: dict + suboptions: + snmp_community: + description: SNMP community string + type: str + acl: + description: + - Standard IP accesslist allowing access with this community string + - Expanded IP accesslist allowing access with this community string + - Access-list name + type: str + ipv6: + description: + - Specify IPv6 Named Access-List + - IPv6 Access-list name + type: str + ro: + description: Read-only access with this community string + type: bool + rw: + description: Read-write access with this community string + type: bool + user: + description: Configure a SNMP v3 user + type: dict + suboptions: + name: + description: SNMP community string + type: str + access: + description: specify an access-list associated with this group + type: dict + suboptions: + acl: + description: SNMP community string + type: str + ipv6: + description: + - Specify IPv6 Named Access-List + - IPv6 Access-list name + type: str + auth: + description: authentication parameters for the user + type: dict + suboptions: + md5: + description: + - Use HMAC MD5 algorithm for authentication + - authentication password for user + type: str + sha: + description: + - Use HMAC SHA algorithm for authentication + - authentication password for user + type: str + priv: + description: encryption parameters for the user + type: dict + suboptions: + des: + description: Use 56 bit DES algorithm for encryption + type: str + des56: + description: Use 56 bit DES algorithm for encryption + type: str + aes128: + description: Use 128 bit AES algorithm for encryption + type: str + aes192: + description: Use 192 bit 3DES algorithm for encryption + type: str + aes256: + description: Use 256 bit DES algorithm for encryption + type: str + credential: + description: If the user password is already configured and saved + type: bool + encrypted: + description: specifying passwords as MD5 or SHA digests + type: bool + table_map: + description: Map external entry attributes into routing table + type: dict + suboptions: + name: + description: route-map name + type: str + filter: + description: Selective route download + type: bool + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS + device by executing the command B(sh running-config | section ^router bgp). + - 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 + state: + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp nopeerup-delay cold-boot 20 + +- name: Merge provided configuration with device configuration + cisco.ios.ios_bgp_address_family: + config: + as_number: 65000 + address_family: + - afi: ipv4 + safi: multicast + vrf: blue + aggregate_address: + - address: 192.0.2.1 + netmask: 255.255.255.255 + as_confed_set: true + bgp: + aggregate_timer: 10 + dampening: + penalty_half_time: 1 + reuse_route_val: 1 + suppress_route_val: 1 + max_suppress: 1 + slow_peer: + - detection: + threshold: 150 + neighbor: + - address: 198.51.100.1 + aigp: + send: + cost_community: + id: 100 + poi: + igp_cost: true + transitive: true + slow_peer: + - detection: + threshold: 150 + remote_as: 10 + route_map: + - name: test-route-out + out: true + - name: test-route-in + in: true + route_server_client: true + network: + - address: 198.51.110.10 + mask: 255.255.255.255 + backdoor: true + snmp: + context: + name: snmp_con + community: + snmp_community: community + ro: true + acl: 10 + - afi: ipv4 + safi: mdt + bgp: + dmzlink_bw: true + dampening: + penalty_half_time: 1 + reuse_route_val: 10 + suppress_route_val: 100 + max_suppress: 5 + soft_reconfig_backup: true + - afi: ipv4 + safi: multicast + aggregate_address: + - address: 192.0.3.1 + netmask: 255.255.255.255 + as_confed_set: true + default_metric: 12 + distance: + external: 10 + internal: 10 + local: 100 + network: + - address: 198.51.111.11 + mask: 255.255.255.255 + route_map: test + table_map: + name: test_tableMap + filter: true + state: merged + +# Task Output: +# ------------ +# +# before: {} +# commands: +# - router bgp 65000 +# - address-family ipv4 multicast vrf blue +# - bgp aggregate-timer 10 +# - bgp dampening 1 1 1 1 +# - bgp slow-peer detection threshold 150 +# - snmp context snmp_con community community ro 10 +# - neighbor 198.51.100.1 remote-as 10 +# - neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# - neighbor 198.51.100.1 slow-peer detection threshold 150 +# - network 198.51.110.10 mask 255.255.255.255 backdoor +# - aggregate-address 192.0.2.1 255.255.255.255 as-confed-set +# - address-family ipv4 multicast +# - default-metric 12 +# - distance bgp 10 10 100 +# - table-map test_tableMap filter +# - network 198.51.111.11 mask 255.255.255.255 route-map test +# - aggregate-address 192.0.3.1 255.255.255.255 as-confed-set +# - address-family ipv4 mdt +# - bgp dmzlink-bw +# - bgp soft-reconfig-backup +# - bgp dampening 1 10 100 5 +# after: +# address_family: +# - afi: ipv4 +# aggregate_addresses: +# - address: 192.0.2.1 +# as_confed_set: true +# netmask: 255.255.255.255 +# bgp: +# aggregate_timer: 10 +# dampening: +# max_suppress: 1 +# penalty_half_time: 1 +# reuse_route_val: 1 +# suppress_route_val: 1 +# slow_peer_options: +# detection: +# threshold: 150 +# neighbors: +# - activate: true +# aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# neighbor_address: 198.51.100.1 +# slow_peer_options: +# detection: +# threshold: 150 +# networks: +# - address: 198.51.110.10 +# backdoor: true +# mask: 255.255.255.255 +# snmp: +# context: +# community: +# acl: '10' +# ro: true +# snmp_community: community +# name: snmp_con +# - afi: ipv4 +# aggregate_addresses: +# - address: 192.0.3.1 +# as_confed_set: true +# netmask: 255.255.255.255 +# default_metric: 12 +# distance: +# external: 10 +# internal: 10 +# local: 100 +# networks: +# - address: 198.51.111.11 +# mask: 255.255.255.255 +# route_map: test +# safi: multicast +# table_map: +# filter: true +# name: test_tableMap +# - afi: ipv4 +# bgp: +# dampening: +# max_suppress: 5 +# penalty_half_time: 1 +# reuse_route_val: 10 +# suppress_route_val: 100 +# dmzlink_bw: true +# soft_reconfig_backup: true +# safi: mdt +# as_number: '65000' + +# After state: +# ------------ +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# snmp context snmp_con community community RO 10 +# neighbor 198.51.100.1 remote-as 10 +# ! +# address-family ipv4 +# snmp context snmp_con community community RO 10 +# bgp aggregate-timer 10 +# bgp slow-peer detection threshold 150 +# bgp dampening 1 1 1 1 +# network 198.51.110.10 mask 255.255.255.255 backdoor +# aggregate-address 192.0.2.1 255.255.255.255 as-confed-set +# neighbor 198.51.100.1 activate +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 slow-peer detection threshold 150 +# exit-address-family +# ! +# address-family ipv4 multicast +# table-map test_tableMap filter +# network 198.51.111.11 mask 255.255.255.255 route-map test +# aggregate-address 192.0.3.1 255.255.255.255 as-confed-set +# default-metric 12 +# distance bgp 10 10 100 +# exit-address-family +# ! +# address-family ipv4 mdt +# bgp dampening 1 10 100 5 +# bgp dmzlink-bw +# bgp soft-reconfig-backup +# exit-address-family + +# Using replaced + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# snmp context snmp_con community community RO 10 +# neighbor 198.51.100.1 remote-as 10 +# ! +# address-family ipv4 +# snmp context snmp_con community community RO 10 +# bgp aggregate-timer 10 +# bgp slow-peer detection threshold 150 +# bgp dampening 1 1 1 1 +# network 198.51.110.10 mask 255.255.255.255 backdoor +# aggregate-address 192.0.2.1 255.255.255.255 as-confed-set +# neighbor 198.51.100.1 activate +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 slow-peer detection threshold 150 +# exit-address-family +# ! +# address-family ipv4 multicast +# table-map test_tableMap filter +# network 198.51.111.11 mask 255.255.255.255 route-map test +# aggregate-address 192.0.3.1 255.255.255.255 as-confed-set +# default-metric 12 +# distance bgp 10 10 100 +# exit-address-family +# ! +# address-family ipv4 mdt +# bgp dampening 1 10 100 5 +# bgp dmzlink-bw +# bgp soft-reconfig-backup +# exit-address-family + +- name: Replaces device configuration of listed AF BGP with provided configuration + cisco.ios.ios_bgp_address_family: + config: + as_number: 65000 + address_family: + - afi: ipv4 + safi: multicast + vrf: blue + aggregate_address: + - address: 192.0.2.1 + netmask: 255.255.255.255 + as_confed_set: true + bgp: + aggregate_timer: 10 + dampening: + penalty_half_time: 1 + reuse_route_val: 1 + suppress_route_val: 1 + max_suppress: 1 + slow_peer: + - detection: + threshold: 150 + neighbor: + - address: 198.51.110.1 + activate: true + aigp: + send: + cost_community: + id: 200 + poi: + igp_cost: true + transitive: true + slow_peer: + - detection: + threshold: 150 + remote_as: 10 + network: + - address: 198.51.110.10 + mask: 255.255.255.255 + backdoor: true + - afi: ipv4 + safi: multicast + bgp: + aggregate_timer: 10 + dampening: + penalty_half_time: 10 + reuse_route_val: 10 + suppress_route_val: 10 + max_suppress: 10 + slow_peer: + - detection: + threshold: 200 + network: + - address: 192.0.2.1 + mask: 255.255.255.255 + route_map: test + state: replaced + +# Task Output: +# ------------ +# before: +# address_family: +# - afi: ipv4 +# aggregate_addresses: +# - address: 192.0.2.1 +# as_confed_set: true +# netmask: 255.255.255.255 +# bgp: +# aggregate_timer: 10 +# dampening: +# max_suppress: 1 +# penalty_half_time: 1 +# reuse_route_val: 1 +# suppress_route_val: 1 +# slow_peer_options: +# detection: +# threshold: 150 +# neighbors: +# - activate: true +# aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# neighbor_address: 198.51.100.1 +# slow_peer_options: +# detection: +# threshold: 150 +# networks: +# - address: 198.51.110.10 +# backdoor: true +# mask: 255.255.255.255 +# snmp: +# context: +# community: +# acl: '10' +# ro: true +# snmp_community: community +# name: snmp_con +# - afi: ipv4 +# aggregate_addresses: +# - address: 192.0.3.1 +# as_confed_set: true +# netmask: 255.255.255.255 +# default_metric: 12 +# distance: +# external: 10 +# internal: 10 +# local: 100 +# networks: +# - address: 198.51.111.11 +# mask: 255.255.255.255 +# route_map: test +# safi: multicast +# table_map: +# filter: true +# name: test_tableMap +# - afi: ipv4 +# bgp: +# dampening: +# max_suppress: 5 +# penalty_half_time: 1 +# reuse_route_val: 10 +# suppress_route_val: 100 +# dmzlink_bw: true +# soft_reconfig_backup: true +# safi: mdt +# as_number: '65000' +# commands: +# - router bgp 65000 +# - address-family ipv4 multicast vrf blue +# - bgp aggregate-timer 10 +# - bgp dampening 1 1 1 1 +# - bgp slow-peer detection threshold 150 +# - neighbor 198.51.110.1 remote-as 10 +# - neighbor 198.51.110.1 activate +# - neighbor 198.51.110.1 aigp send cost-community 200 poi igp-cost transitive +# - neighbor 198.51.110.1 slow-peer detection threshold 150 +# - network 198.51.110.10 mask 255.255.255.255 backdoor +# - aggregate-address 192.0.2.1 255.255.255.255 as-confed-set +# - address-family ipv4 multicast +# - no default-metric 12 +# - no distance bgp 10 10 100 +# - no table-map test_tableMap filter +# - bgp aggregate-timer 10 +# - bgp dampening 10 10 10 10 +# - bgp slow-peer detection threshold 200 +# - network 192.0.2.1 mask 255.255.255.255 route-map test +# - no network 198.51.111.11 mask 255.255.255.255 route-map test +# - no aggregate-address 192.0.3.1 255.255.255.255 as-confed-set +# after: +# address_family: +# - afi: ipv4 +# aggregate_addresses: +# - address: 192.0.2.1 +# as_confed_set: true +# netmask: 255.255.255.255 +# bgp: +# aggregate_timer: 10 +# dampening: +# max_suppress: 1 +# penalty_half_time: 1 +# reuse_route_val: 1 +# suppress_route_val: 1 +# slow_peer_options: +# detection: +# threshold: 150 +# neighbors: +# - activate: true +# aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# neighbor_address: 198.51.100.1 +# slow_peer_options: +# detection: +# threshold: 150 +# - activate: true +# aigp: +# send: +# cost_community: +# id: 200 +# poi: +# igp_cost: true +# transitive: true +# neighbor_address: 198.51.110.1 +# slow_peer_options: +# detection: +# threshold: 150 +# networks: +# - address: 198.51.110.10 +# backdoor: true +# mask: 255.255.255.255 +# snmp: +# context: +# community: +# acl: '10' +# ro: true +# snmp_community: community +# name: snmp_con +# - afi: ipv4 +# bgp: +# aggregate_timer: 10 +# dampening: +# max_suppress: 10 +# penalty_half_time: 10 +# reuse_route_val: 10 +# suppress_route_val: 10 +# slow_peer_options: +# detection: +# threshold: 200 +# networks: +# - address: 192.0.2.1 +# mask: 255.255.255.255 +# route_map: test +# safi: multicast +# - afi: ipv4 +# bgp: +# dampening: +# max_suppress: 5 +# penalty_half_time: 1 +# reuse_route_val: 10 +# suppress_route_val: 100 +# dmzlink_bw: true +# soft_reconfig_backup: true +# safi: mdt +# as_number: '65000' + +# After state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# snmp context snmp_con community community RO 10 +# neighbor 198.51.100.1 remote-as 10 +# neighbor 198.51.110.1 remote-as 10 +# ! +# address-family ipv4 +# snmp context snmp_con community community RO 10 +# bgp aggregate-timer 10 +# bgp slow-peer detection threshold 150 +# bgp dampening 1 1 1 1 +# network 198.51.110.10 mask 255.255.255.255 backdoor +# aggregate-address 192.0.2.1 255.255.255.255 as-confed-set +# neighbor 198.51.100.1 activate +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 slow-peer detection threshold 150 +# neighbor 198.51.110.1 activate +# neighbor 198.51.110.1 aigp send cost-community 200 poi igp-cost transitive +# neighbor 198.51.110.1 slow-peer detection threshold 150 +# exit-address-family +# ! +# address-family ipv4 multicast +# bgp aggregate-timer 10 +# bgp slow-peer detection threshold 200 +# bgp dampening 10 10 10 10 +# network 192.0.2.1 mask 255.255.255.255 route-map test +# exit-address-family +# ! +# address-family ipv4 mdt +# bgp dampening 1 10 100 5 +# bgp dmzlink-bw +# bgp soft-reconfig-backup +# exit-address-family + +# Using overridden + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# snmp context snmp_con community community RO 10 +# neighbor 198.51.100.1 remote-as 10 +# neighbor 198.51.110.1 remote-as 10 +# ! +# address-family ipv4 +# snmp context snmp_con community community RO 10 +# bgp aggregate-timer 10 +# bgp slow-peer detection threshold 150 +# bgp dampening 1 1 1 1 +# network 198.51.110.10 mask 255.255.255.255 backdoor +# aggregate-address 192.0.2.1 255.255.255.255 as-confed-set +# neighbor 198.51.100.1 activate +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 slow-peer detection threshold 150 +# neighbor 198.51.110.1 activate +# neighbor 198.51.110.1 aigp send cost-community 200 poi igp-cost transitive +# neighbor 198.51.110.1 slow-peer detection threshold 150 +# exit-address-family +# ! +# address-family ipv4 multicast +# bgp aggregate-timer 10 +# bgp slow-peer detection threshold 200 +# bgp dampening 10 10 10 10 +# network 192.0.2.1 mask 255.255.255.255 route-map test +# exit-address-family +# ! +# address-family ipv4 mdt +# bgp dampening 1 10 100 5 +# bgp dmzlink-bw +# bgp soft-reconfig-backup +# exit-address-family + +- name: Override device configuration of all AF BGP with provided configuration + cisco.ios.ios_bgp_address_family: + config: + as_number: 65000 + address_family: + - afi: ipv4 + safi: multicast + vrf: blue + aggregate_address: + - address: 192.0.2.1 + netmask: 255.255.255.255 + as_confed_set: true + bgp: + aggregate_timer: 10 + dampening: + penalty_half_time: 10 + reuse_route_val: 10 + suppress_route_val: 100 + max_suppress: 50 + slow_peer: + - detection: + threshold: 150 + neighbor: + - address: 198.51.110.1 + activate: true + log_neighbor_changes: + disable: true + maximum_prefix: + number: 1 + threshold_value: 10 + restart: 100 + slow_peer: + - detection: + threshold: 150 + remote_as: 100 + version: 4 + network: + - address: 198.51.110.10 + mask: 255.255.255.255 + backdoor: true + - afi: ipv6 + safi: multicast + default_information: true + bgp: + aggregate_timer: 10 + dampening: + penalty_half_time: 10 + reuse_route_val: 10 + suppress_route_val: 10 + max_suppress: 10 + slow_peer: + - detection: + threshold: 200 + network: + - address: 2001:DB8:0:3::/64 + route_map: test_ipv6 + state: overridden + +# Task Output: +# ------------ +# +# before: +# address_family: +# - afi: ipv4 +# aggregate_addresses: +# - address: 192.0.2.1 +# as_confed_set: true +# netmask: 255.255.255.255 +# bgp: +# aggregate_timer: 10 +# dampening: +# max_suppress: 10 +# penalty_half_time: 10 +# reuse_route_val: 10 +# suppress_route_val: 10 +# slow_peer_options: +# detection: +# threshold: 200 +# default_information: true +# neighbors: +# - activate: true +# aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# neighbor_address: 198.51.100.1 +# slow_peer_options: +# detection: +# threshold: 150 +# - activate: true +# aigp: +# send: +# cost_community: +# id: 200 +# poi: +# igp_cost: true +# transitive: true +# maximum_prefix: +# number: 1 +# restart: 100 +# threshold_value: 10 +# neighbor_address: 198.51.110.1 +# slow_peer_options: +# detection: +# threshold: 150 +# networks: +# - address: 198.51.110.10 +# backdoor: true +# mask: 255.255.255.255 +# - afi: ipv4 +# safi: multicast +# - afi: ipv4 +# safi: mdt +# - afi: ipv6 +# safi: multicast +# as_number: '65000' +# commands: +# - router bgp 65000 +# - address-family ipv4 +# - no default-information originate +# - no bgp aggregate-timer 10 +# - no bgp dampening 10 10 10 10 +# - no bgp slow-peer detection threshold 200 +# - no neighbor 198.51.100.1 +# - no neighbor 198.51.110.1 +# - no network 198.51.110.10 mask 255.255.255.255 backdoor +# - no aggregate-address 192.0.2.1 255.255.255.255 as-confed-set +# - address-family ipv4 multicast vrf blue +# - bgp aggregate-timer 10 +# - bgp dampening 10 10 100 50 +# - bgp slow-peer detection threshold 150 +# - neighbor 198.51.110.1 remote-as 100 +# - neighbor 198.51.110.1 activate +# - neighbor 198.51.110.1 disable +# - neighbor 198.51.110.1 maximum-prefix 1 10 restart 100 +# - neighbor 198.51.110.1 slow-peer detection threshold 150 +# - neighbor 198.51.110.1 version 4 +# - network 198.51.110.10 mask 255.255.255.255 backdoor +# - aggregate-address 192.0.2.1 255.255.255.255 as-confed-set +# - address-family ipv6 multicast +# - default-information originate +# - bgp aggregate-timer 10 +# - bgp dampening 10 10 10 10 +# - bgp slow-peer detection threshold 200 +# after: +# address_family: +# - afi: ipv4 +# aggregate_addresses: +# - address: 192.0.2.1 +# as_confed_set: true +# netmask: 255.255.255.255 +# bgp: +# aggregate_timer: 10 +# dampening: +# max_suppress: 10 +# penalty_half_time: 10 +# reuse_route_val: 10 +# suppress_route_val: 10 +# slow_peer_options: +# detection: +# threshold: 200 +# default_information: true +# neighbors: +# - activate: true +# aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# neighbor_address: 198.51.100.1 +# slow_peer_options: +# detection: +# threshold: 150 +# - activate: true +# aigp: +# send: +# cost_community: +# id: 200 +# poi: +# igp_cost: true +# transitive: true +# maximum_prefix: +# number: 1 +# restart: 100 +# threshold_value: 10 +# neighbor_address: 198.51.110.1 +# slow_peer_options: +# detection: +# threshold: 150 +# networks: +# - address: 198.51.110.10 +# backdoor: true +# mask: 255.255.255.255 +# - afi: ipv4 +# safi: multicast +# - afi: ipv4 +# safi: mdt +# - afi: ipv6 +# safi: multicast +# as_number: '65000' + +# After state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# neighbor 198.51.100.1 remote-as 10 +# neighbor 198.51.110.1 remote-as 100 +# neighbor 198.51.110.1 disable-connected-check +# neighbor 198.51.110.1 version 4 +# ! +# address-family ipv4 +# bgp aggregate-timer 10 +# bgp slow-peer detection threshold 200 +# bgp dampening 10 10 10 10 +# network 198.51.110.10 mask 255.255.255.255 backdoor +# aggregate-address 192.0.2.1 255.255.255.255 as-confed-set +# neighbor 198.51.100.1 activate +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 slow-peer detection threshold 150 +# neighbor 198.51.110.1 activate +# neighbor 198.51.110.1 aigp send cost-community 200 poi igp-cost transitive +# neighbor 198.51.110.1 slow-peer detection threshold 150 +# neighbor 198.51.110.1 maximum-prefix 1 10 restart 100 +# default-information originate +# exit-address-family +# ! +# address-family ipv4 multicast +# exit-address-family +# ! +# address-family ipv4 mdt +# exit-address-family +# ! +# address-family ipv6 multicast +# exit-address-family + +# Using deleted + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# neighbor 198.51.100.1 remote-as 10 +# neighbor 198.51.110.1 remote-as 100 +# neighbor 198.51.110.1 disable-connected-check +# neighbor 198.51.110.1 version 4 +# ! +# address-family ipv4 +# bgp aggregate-timer 10 +# bgp slow-peer detection threshold 200 +# bgp dampening 10 10 10 10 +# network 198.51.110.10 mask 255.255.255.255 backdoor +# aggregate-address 192.0.2.1 255.255.255.255 as-confed-set +# neighbor 198.51.100.1 activate +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 slow-peer detection threshold 150 +# neighbor 198.51.110.1 activate +# neighbor 198.51.110.1 aigp send cost-community 200 poi igp-cost transitive +# neighbor 198.51.110.1 slow-peer detection threshold 150 +# neighbor 198.51.110.1 maximum-prefix 1 10 restart 100 +# default-information originate +# exit-address-family +# ! +# address-family ipv4 multicast +# exit-address-family +# ! +# address-family ipv4 mdt +# exit-address-family +# ! +# address-family ipv6 multicast +# exit-address-family + +- name: "Delete AF BGP (Note: This won't delete the all configured AF BGP)" + cisco.ios.ios_bgp_address_family: + config: + as_number: 65000 + address_family: + - afi: ipv4 + safi: multicast + - afi: ipv4 + safi: mdt + state: deleted + +# Task Output: +# ------------ +# +# before: +# address_family: +# - afi: ipv4 +# aggregate_addresses: +# - address: 192.0.2.1 +# as_confed_set: true +# netmask: 255.255.255.255 +# bgp: +# aggregate_timer: 10 +# dampening: +# max_suppress: 10 +# penalty_half_time: 10 +# reuse_route_val: 10 +# suppress_route_val: 10 +# slow_peer_options: +# detection: +# threshold: 200 +# default_information: true +# neighbors: +# - activate: true +# aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# neighbor_address: 198.51.100.1 +# slow_peer_options: +# detection: +# threshold: 150 +# - activate: true +# aigp: +# send: +# cost_community: +# id: 200 +# poi: +# igp_cost: true +# transitive: true +# maximum_prefix: +# number: 1 +# restart: 100 +# threshold_value: 10 +# neighbor_address: 198.51.110.1 +# slow_peer_options: +# detection: +# threshold: 150 +# networks: +# - address: 198.51.110.10 +# backdoor: true +# mask: 255.255.255.255 +# - afi: ipv4 +# safi: multicast +# - afi: ipv4 +# safi: mdt +# - afi: ipv6 +# safi: multicast +# as_number: '65000' +# commands: +# - router bgp 65000 +# - no address-family ipv4 multicast +# - no address-family ipv4 mdt +# after: +# address_family: +# - afi: ipv4 +# aggregate_addresses: +# - address: 192.0.2.1 +# as_confed_set: true +# netmask: 255.255.255.255 +# bgp: +# aggregate_timer: 10 +# dampening: +# max_suppress: 10 +# penalty_half_time: 10 +# reuse_route_val: 10 +# suppress_route_val: 10 +# slow_peer_options: +# detection: +# threshold: 200 +# default_information: true +# neighbors: +# - activate: true +# aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# neighbor_address: 198.51.100.1 +# slow_peer_options: +# detection: +# threshold: 150 +# - activate: true +# aigp: +# send: +# cost_community: +# id: 200 +# poi: +# igp_cost: true +# transitive: true +# maximum_prefix: +# number: 1 +# restart: 100 +# threshold_value: 10 +# neighbor_address: 198.51.110.1 +# slow_peer_options: +# detection: +# threshold: 150 +# networks: +# - address: 198.51.110.10 +# backdoor: true +# mask: 255.255.255.255 +# - afi: ipv6 +# safi: multicast +# as_number: '65000' + +# After state: +# ------------- +# +# vios#sh running-config | section ^router bg +# hostname#show running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# neighbor 198.51.100.1 remote-as 10 +# neighbor 198.51.110.1 remote-as 100 +# neighbor 198.51.110.1 disable-connected-check +# neighbor 198.51.110.1 version 4 +# ! +# address-family ipv4 +# bgp aggregate-timer 10 +# bgp slow-peer detection threshold 200 +# bgp dampening 10 10 10 10 +# network 198.51.110.10 mask 255.255.255.255 backdoor +# aggregate-address 192.0.2.1 255.255.255.255 as-confed-set +# neighbor 198.51.100.1 activate +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 slow-peer detection threshold 150 +# neighbor 198.51.110.1 activate +# neighbor 198.51.110.1 aigp send cost-community 200 poi igp-cost transitive +# neighbor 198.51.110.1 slow-peer detection threshold 150 +# neighbor 198.51.110.1 maximum-prefix 1 10 restart 100 +# default-information originate +# exit-address-family +# ! +# address-family ipv6 multicast +# exit-address-family + +# Using Deleted without any config passed (delete all) + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bg +# hostname#show running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# neighbor 198.51.100.1 remote-as 10 +# neighbor 198.51.110.1 remote-as 100 +# neighbor 198.51.110.1 disable-connected-check +# neighbor 198.51.110.1 version 4 +# ! +# address-family ipv4 +# bgp aggregate-timer 10 +# bgp slow-peer detection threshold 200 +# bgp dampening 10 10 10 10 +# network 198.51.110.10 mask 255.255.255.255 backdoor +# aggregate-address 192.0.2.1 255.255.255.255 as-confed-set +# neighbor 198.51.100.1 activate +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 slow-peer detection threshold 150 +# neighbor 198.51.110.1 activate +# neighbor 198.51.110.1 aigp send cost-community 200 poi igp-cost transitive +# neighbor 198.51.110.1 slow-peer detection threshold 150 +# neighbor 198.51.110.1 maximum-prefix 1 10 restart 100 +# default-information originate +# exit-address-family +# ! +# address-family ipv6 multicast +# exit-address-family + +- name: "Delete ALL of configured AF BGP" + cisco.ios.ios_bgp_address_family: + state: deleted + +# Task Output: +# ------------ +# +# before: +# address_family: +# - afi: ipv4 +# aggregate_addresses: +# - address: 192.0.2.1 +# as_confed_set: true +# netmask: 255.255.255.255 +# bgp: +# aggregate_timer: 10 +# dampening: +# max_suppress: 10 +# penalty_half_time: 10 +# reuse_route_val: 10 +# suppress_route_val: 10 +# slow_peer_options: +# detection: +# threshold: 200 +# default_information: true +# neighbors: +# - activate: true +# aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# neighbor_address: 198.51.100.1 +# slow_peer_options: +# detection: +# threshold: 150 +# - activate: true +# aigp: +# send: +# cost_community: +# id: 200 +# poi: +# igp_cost: true +# transitive: true +# maximum_prefix: +# number: 1 +# restart: 100 +# threshold_value: 10 +# neighbor_address: 198.51.110.1 +# slow_peer_options: +# detection: +# threshold: 150 +# networks: +# - address: 198.51.110.10 +# backdoor: true +# mask: 255.255.255.255 +# - afi: ipv6 +# safi: multicast +# as_number: '65000' +# commands: +# - router bgp 65000 +# - no address-family ipv4 +# - no address-family ipv6 multicast +# after: +# address_family: +# - afi: ipv4 +# bgp: +# aggregate_timer: 10 +# as_number: '65000' + +# After state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# neighbor 198.51.100.1 remote-as 10 +# neighbor 198.51.110.1 remote-as 100 +# neighbor 198.51.110.1 disable-connected-check +# neighbor 198.51.110.1 version 4 +# ! +# address-family ipv4 +# bgp aggregate-timer 10 +# no neighbor 198.51.100.1 activate +# no neighbor 198.51.110.1 activate +# exit-address-family + +# Using gathered + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# snmp context snmp_con community community RO 10 +# neighbor 198.51.100.1 remote-as 10 +# ! +# address-family ipv4 +# snmp context snmp_con community community RO 10 +# bgp aggregate-timer 10 +# bgp slow-peer detection threshold 150 +# bgp dampening 1 1 1 1 +# network 198.51.110.10 mask 255.255.255.255 backdoor +# aggregate-address 192.0.2.1 255.255.255.255 as-confed-set +# neighbor 198.51.100.1 activate +# neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.51.100.1 slow-peer detection threshold 150 +# exit-address-family +# ! +# address-family ipv4 multicast +# table-map test_tableMap filter +# network 198.51.111.11 mask 255.255.255.255 route-map test +# aggregate-address 192.0.3.1 255.255.255.255 as-confed-set +# default-metric 12 +# distance bgp 10 10 100 +# exit-address-family +# ! +# address-family ipv4 mdt +# bgp dampening 1 10 100 5 +# bgp dmzlink-bw +# bgp soft-reconfig-backup +# exit-address-family + +- name: Gather listed AF BGP with provided configurations + cisco.ios.ios_bgp_address_family: + config: + state: gathered + +# Task Output: +# ------------ +# +# gathered: +# address_family: +# - afi: ipv4 +# aggregate_addresses: +# - address: 192.0.2.1 +# as_confed_set: true +# netmask: 255.255.255.255 +# bgp: +# aggregate_timer: 10 +# dampening: +# max_suppress: 1 +# penalty_half_time: 1 +# reuse_route_val: 1 +# suppress_route_val: 1 +# slow_peer_options: +# detection: +# threshold: 150 +# neighbors: +# - activate: true +# aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# neighbor_address: 198.51.100.1 +# slow_peer_options: +# detection: +# threshold: 150 +# networks: +# - address: 198.51.110.10 +# backdoor: true +# mask: 255.255.255.255 +# snmp: +# context: +# community: +# acl: '10' +# ro: true +# snmp_community: community +# name: snmp_con +# - afi: ipv4 +# aggregate_addresses: +# - address: 192.0.3.1 +# as_confed_set: true +# netmask: 255.255.255.255 +# default_metric: 12 +# distance: +# external: 10 +# internal: 10 +# local: 100 +# networks: +# - address: 198.51.111.11 +# mask: 255.255.255.255 +# route_map: test +# safi: multicast +# table_map: +# filter: true +# name: test_tableMap +# - afi: ipv4 +# bgp: +# dampening: +# max_suppress: 5 +# penalty_half_time: 1 +# reuse_route_val: 10 +# suppress_route_val: 100 +# dmzlink_bw: true +# soft_reconfig_backup: true +# safi: mdt +# as_number: '65000' + +# Using rendered + +- name: Rendered the provided configuration with the existing running configuration + cisco.ios.ios_bgp_address_family: + config: + as_number: 65000 + address_family: + - afi: ipv4 + safi: multicast + vrf: blue + aggregate_address: + - address: 192.0.2.1 + netmask: 255.255.255.255 + as_confed_set: true + bgp: + aggregate_timer: 10 + dampening: + penalty_half_time: 1 + reuse_route_val: 1 + suppress_route_val: 1 + max_suppress: 1 + slow_peer: + - detection: + threshold: 150 + neighbor: + - address: 198.51.100.1 + aigp: + send: + cost_community: + id: 100 + poi: + igp_cost: true + transitive: true + slow_peer: + - detection: + threshold: 150 + remote_as: 10 + route_maps: + - name: test-route + out: true + route_server_client: true + network: + - address: 198.51.110.10 + mask: 255.255.255.255 + backdoor: true + snmp: + context: + name: snmp_con + community: + snmp_community: community + ro: true + acl: 10 + - afi: ipv4 + safi: mdt + bgp: + dmzlink_bw: true + dampening: + penalty_half_time: 1 + reuse_route_val: 10 + suppress_route_val: 100 + max_suppress: 5 + soft_reconfig_backup: true + state: rendered + +# Task Output: +# ------------ +# +# rendered: +# - router bgp 65000 +# - address-family ipv4 multicast vrf blue +# - bgp aggregate-timer 10 +# - bgp dampening 1 1 1 1 +# - bgp slow-peer detection threshold 150 +# - snmp context snmp_con community community ro 10 +# - neighbor 198.51.100.1 remote-as 10 +# - neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive +# - neighbor 198.51.100.1 slow-peer detection threshold 150 +# - network 198.51.110.10 mask 255.255.255.255 backdoor +# - aggregate-address 192.0.2.1 255.255.255.255 as-confed-set +# - address-family ipv4 mdt +# - bgp dmzlink-bw +# - bgp soft-reconfig-backup +# - bgp dampening 1 10 100 5 + +# Using parsed + +# File: parsed.cfg +# ---------------- +# router bgp 65000 +# bgp log-neighbor-changes +# bgp nopeerup-delay cold-boot 20 +# ! +# address-family ipv4 multicast +# table-map test_tableMap filter +# network 1.1.1.1 mask 255.255.255.255 route-map test +# aggregate-address 192.0.3.1 255.255.255.255 as-confed-set +# default-metric 12 +# distance bgp 10 10 100 +# exit-address-family +# ! +# address-family ipv4 mdt +# bgp dampening 1 10 100 5 +# bgp dmzlink-bw +# bgp soft-reconfig-backup +# exit-address-family +# ! + +- name: Parse the commands for provided configuration + cisco.ios.ios_bgp_address_family: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Task Output: +# ------------ +# +# parsed: +# address_family: +# - afi: ipv4 +# aggregate_addresses: +# - address: 192.0.2.1 +# as_confed_set: true +# netmask: 255.255.255.255 +# bgp: +# aggregate_timer: 10 +# dampening: +# max_suppress: 1 +# penalty_half_time: 1 +# reuse_route_val: 1 +# suppress_route_val: 1 +# slow_peer_options: +# detection: +# threshold: 150 +# neighbors: +# - activate: true +# aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# neighbor_address: 198.51.100.1 +# slow_peer_options: +# detection: +# threshold: 150 +# networks: +# - address: 198.51.110.10 +# backdoor: true +# mask: 255.255.255.255 +# snmp: +# context: +# community: +# acl: '10' +# ro: true +# snmp_community: community +# name: snmp_con +# - afi: ipv4 +# aggregate_addresses: +# - address: 192.0.3.1 +# as_confed_set: true +# netmask: 255.255.255.255 +# default_metric: 12 +# distance: +# external: 10 +# internal: 10 +# local: 100 +# networks: +# - address: 198.51.111.11 +# mask: 255.255.255.255 +# route_map: test +# safi: multicast +# table_map: +# filter: true +# name: test_tableMap +# - afi: ipv4 +# bgp: +# dampening: +# max_suppress: 5 +# penalty_half_time: 1 +# reuse_route_val: 10 +# suppress_route_val: 100 +# dmzlink_bw: true +# soft_reconfig_backup: true +# safi: mdt +# as_number: '65000' +""" + + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: list + sample: + - router bgp 65000 + - address-family ipv4 multicast + - table-map test_tableMap filter + - network 1.1.1.1 mask 255.255.255.255 route-map test + - aggregate-address 192.0.3.1 255.255.255.255 as-confed-set +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - router bgp 65000 + - address-family ipv4 multicast + - table-map test_tableMap filter + - network 1.1.1.1 mask 255.255.255.255 route-map test + - aggregate-address 192.0.3.1 255.255.255.255 as-confed-set +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.bgp_address_family.bgp_address_family import ( + Bgp_address_familyArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.bgp_address_family.bgp_address_family import ( + Bgp_address_family, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Bgp_address_familyArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Bgp_address_family(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_bgp_global.py b/ansible_collections/cisco/ios/plugins/modules/ios_bgp_global.py new file mode 100644 index 000000000..0639b6e8a --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_bgp_global.py @@ -0,0 +1,3063 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_bgp_global +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_bgp_global +short_description: Resource module to configure BGP. +description: This module configures and manages the attributes of global bgp on Cisco IOS. +version_added: 1.3.0 +author: + - Sumit Jaiswal (@justjais) + - Sagar Paul (@KB-perByte) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) + - The module examples uses callback plugin (stdout_callback = yaml) to generate task + output in yaml format. +options: + config: + description: A dictionary of options for bgp configurations. + type: dict + suboptions: + as_number: + description: Autonomous system number + type: str + aggregate_address: + description: + - Configure BGP aggregate entry + - This option is DEPRECATED and is replaced with aggregate_addresses which + accepts list of dict as input, this attribute will be removed after 2024-06-01. + type: dict + suboptions: + address: + description: Specify aggregate address + type: str + netmask: + description: Specify aggregate mask + type: str + advertise_map: + description: Set condition to advertise attribute + type: str + as_confed_set: + description: Generate AS confed set path information + type: bool + as_set: + description: Generate AS set path information + type: bool + attribute_map: + description: Set attributes of aggregate + type: str + summary_only: + description: Filter more specific routes from updates + type: bool + suppress_map: + description: Conditionally filter more specific routes from updates + type: str + aggregate_addresses: + description: Configure BGP aggregate entries + type: list + elements: dict + suboptions: + address: + description: Specify aggregate address + type: str + netmask: + description: Specify aggregate mask + type: str + advertise_map: + description: Set condition to advertise attribute + type: str + as_confed_set: + description: Generate AS confed set path information + type: bool + as_set: + description: Generate AS set path information + type: bool + attribute_map: + description: Set attributes of aggregate + type: str + summary_only: + description: Filter more specific routes from updates + type: bool + suppress_map: + description: Conditionally filter more specific routes from updates + type: str + auto_summary: + description: Enable automatic network number summarization + type: bool + bgp: + description: Enable address family and enter its config mode + type: dict + suboptions: + additional_paths: + description: Additional paths in the BGP table + type: dict + suboptions: + install: + description: Additional paths to install into RIB + type: bool + receive: + description: Receive additional paths from neighbors + type: bool + select: + description: Selection criteria to pick the paths + type: dict + suboptions: + all: + description: Select all available paths + type: bool + best: + description: Select best N paths (2-3). + type: int + best_external: + description: Select best-external path + type: bool + group_best: + description: Select group-best path + type: bool + send: + description: Send additional paths to neighbors + type: bool + advertise_best_external: + description: Advertise best external path to internal peers + type: bool + aggregate_timer: + description: + - Configure Aggregation Timer + - Please refer vendor documentation for valid values + type: int + always_compare_med: + description: Allow comparing MED from different neighbors + type: bool + asnotation: + description: + - Change the default as plain notation + - asdot notation + type: bool + bestpath: + description: + - Change the default bestpath selection + - This option is DEPRECATED and replaced with bestpath_options of type dict, + this attribute will be removed after 2024-06-01. + type: list + elements: dict + suboptions: + aigp: + description: + - if both paths doesn't have aigp ignore on bestpath comparison + - ignore + type: bool + compare_routerid: + description: Compare router-id for identical EBGP paths + type: bool + cost_community: + description: cost community + type: bool + igp_metric: + description: + - igp metric + - Ignore igp metric in bestpath selection + type: bool + med: + description: MED attribute + type: dict + suboptions: + confed: + description: Compare MED among confederation paths + type: bool + missing_as_worst: + description: Treat missing MED as the least preferred one + type: bool + bestpath_options: + description: + - Change the default bestpath selection + type: dict + suboptions: + aigp: + description: + - if both paths doesn't have aigp ignore on bestpath comparison + - ignore + type: bool + compare_routerid: + description: Compare router-id for identical EBGP paths + type: bool + cost_community: + description: cost community + type: bool + igp_metric: + description: + - igp metric + - Ignore igp metric in bestpath selection + type: bool + med: + description: MED attribute + type: dict + suboptions: + confed: + description: Compare MED among confederation paths + type: bool + missing_as_worst: + description: Treat missing MED as the least preferred one + type: bool + client_to_client: + description: Configure client to client route reflection + type: dict + suboptions: + set: + description: set reflection of routes allowed + type: bool + all: + description: inter-cluster and intra-cluster (default) + type: bool + intra_cluster: + description: + - intra cluster reflection + - intra-cluster reflection for cluster-id + type: str + cluster_id: + description: + - Configure Route-Reflector Cluster-id (peers may reset) + - A.B.C.D/Please refer vendor documentation for valid Route-Reflector Cluster-id + type: str + confederation: + description: AS confederation parameters + type: dict + suboptions: + identifier: + description: + - Set routing domain confederation AS + - AS number + type: str + peers: + description: + - Peer ASs in BGP confederation + - AS number + type: str + consistency_checker: + description: Consistency-checker + type: dict + suboptions: + auto_repair: + description: Auto-Repair + type: dict + suboptions: + set: + description: Enable Auto-Repair + type: bool + interval: + description: + - Set the bgp consistency checker + - Please refer vendor documentation for valid values + type: int + error_message: + description: Log Error-Msg + type: dict + suboptions: + set: + description: Enable Error-Msg + type: bool + interval: + description: + - Set the bgp consistency checker + - Please refer vendor documentation for valid values + type: int + dampening: + description: Enable route-flap dampening + type: dict + suboptions: + penalty_half_time: + description: + - Half-life time for the penalty + - Please refer vendor documentation for valid values + type: int + reuse_route_val: + description: + - Value to start reusing a route + - Please refer vendor documentation for valid values + type: int + suppress_route_val: + description: + - Value to start suppressing a route + - Please refer vendor documentation for valid values + type: int + max_suppress: + description: + - Maximum duration to suppress a stable route + - Please refer vendor documentation for valid values + type: int + route_map: + description: Route-map to specify criteria for dampening + type: str + deterministic_med: + description: Pick the best-MED path among paths advertised from the neighboring AS + type: bool + dmzlink_bw: + description: Use DMZ Link Bandwidth as weight for BGP multipaths + type: bool + enforce_first_as: + description: Enforce the first AS for EBGP routes(default) + type: bool + enhanced_error: + description: Enabled BGP Enhanced error handling + type: bool + fast_external_fallover: + description: Immediately reset session if a link to a directly connected external peer goes down + type: bool + graceful_restart: + description: Graceful restart capability parameters + type: dict + suboptions: + set: + description: Set Graceful-Restart + type: bool + extended: + description: Enable Graceful-Restart Extension + type: bool + restart_time: + description: + - Set the max time needed to restart and come back up + - Please refer vendor documentation for valid values + type: int + stalepath_time: + description: + - Set the max time to hold onto restarting peer's stale paths + - Please refer vendor documentation for valid values + type: int + graceful_shutdown: + description: Graceful shutdown capability parameters + type: dict + suboptions: + neighbors: + description: Gracefully shut down all neighbors + type: dict + suboptions: + time: + description: + - time in seconds + - Please refer vendor documentation for valid values + type: int + activate: + description: Activate graceful shutdown of all neighbors + type: bool + vrfs: + description: Gracefully shut down all vrf neighbors + type: dict + suboptions: + time: + description: + - time in seconds + - Please refer vendor documentation for valid values + type: int + activate: + description: Activate graceful shutdown of all neighbors + type: bool + community: + description: + - Set Community for Gshut routes + - community number/community number in aa:nn format + type: str + local_preference: + description: + - Set Local Preference for Gshut routes + - Please refer vendor documentation for valid values + type: int + inject_map: + description: + - Routemap which specifies prefixes to inject + - This option is DEPRECATED and is updated with inject_maps which is a + list of dict, this attribute will be removed after 2024-06-01. + type: dict + suboptions: + name: + description: route-map name + type: str + exist_map_name: + description: route-map name + type: str + copy_attributes: + description: Copy attributes from aggregate + type: bool + inject_maps: + description: Routemap which specifies prefixes to inject + type: list + elements: dict + suboptions: + name: + description: route-map name + type: str + exist_map_name: + description: route-map name + type: str + copy_attributes: + description: Copy attributes from aggregate + type: bool + listen: + description: Neighbor subnet range listener + type: dict + suboptions: + limit: + description: + - Set the max limit for the dynamic subnet range neighbors + - Please refer vendor documentation for valid values + type: int + range: + description: Subnet network range + type: dict + suboptions: + ipv4_with_subnet: + description: + - IPv4 subnet range(A.B.C.D/nn) + - This option is DEPRECATED and is updated with host_with_subnet which is a + common attribute for address, this attribute will be removed after 2024-06-01. + type: str + ipv6_with_subnet: + description: + - IPv6 subnet range(X:X:X:X::X/<0-128>) + - This option is DEPRECATED and is updated with host_with_subnet which is a + common attribute for address attribute will be removed after 2024-06-01. + type: str + host_with_subnet: + description: + - IPv4 subnet range(A.B.C.D/nn) + - IPv6 subnet range(X:X:X:X::X/<0-128>) + type: str + peer_group: + description: Member of the peer-group + type: str + log_neighbor_changes: + description: Log neighbor up/down and reset reason + type: bool + maxas_limit: + description: + - Allow AS-PATH attribute from any neighbor imposing a limit on number of ASes + - Please refer vendor documentation for valid values + type: int + maxcommunity_limit: + description: + - Allow COMMUNITY attribute from any neighbor imposing a limit on number of communities + - Please refer vendor documentation for valid values + type: int + maxextcommunity_limit: + description: + - Allow EXTENDED COMMUNITY attribute from any neighbor imposing a limit on number of extended communities + - Please refer vendor documentation for valid values + type: int + nexthop: + description: Nexthop tracking commands + type: dict + suboptions: + route_map: + description: Route map for valid nexthops + type: str + trigger: + description: nexthop trackings + type: dict + suboptions: + delay: + description: + - Set the delay to trigger nexthop tracking + - Please refer vendor documentation for valid values + type: int + enable: + description: Enable nexthop tracking + type: bool + nopeerup_delay: + description: + - Set how long BGP will wait for the first peer to come up before beginning the update delay or + graceful restart timers (in seconds) + - This option is DEPRECATED and is replaced with nopeerup_delay_options which is of type dict, + this attribute will be removed after 2024-06-01. + type: list + elements: dict + suboptions: + cold_boot: + description: + - How long to wait for the first peer to come up upon a cold boot + - Please refer vendor documentation for valid values + type: int + nsf_switchover: + description: + - How long to wait for the first peer, post NSF switchover + - Please refer vendor documentation for valid values + type: int + post_boot: + description: + - How long to wait for the first peer to come up once the system is already + booted and all peers go down + - Please refer vendor documentation for valid values + type: int + user_initiated: + description: + - How long to wait for the first peer, post a manual clear of BGP peers by the admin user + - Please refer vendor documentation for valid values + type: int + nopeerup_delay_options: + description: + Set how long BGP will wait for the first peer to come up before beginning the update delay or + graceful restart timers (in seconds) + type: dict + suboptions: + cold_boot: + description: + - How long to wait for the first peer to come up upon a cold boot + - Please refer vendor documentation for valid values + type: int + nsf_switchover: + description: + - How long to wait for the first peer, post NSF switchover + - Please refer vendor documentation for valid values + type: int + post_boot: + description: + - How long to wait for the first peer to come up once the system is already + booted and all peers go down + - Please refer vendor documentation for valid values + type: int + user_initiated: + description: + - How long to wait for the first peer, post a manual clear of BGP peers by the admin user + - Please refer vendor documentation for valid values + type: int + recursion: + description: + - recursion rule for the nexthops + - recursion via host for the nexthops + type: bool + redistribute_internal: + description: Allow redistribution of iBGP into IGPs (dangerous) + type: bool + refresh: + description: refresh + type: dict + suboptions: + max_eor_time: + description: + - Configure refresh max-eor time + - Please refer vendor documentation for valid values + type: int + stalepath_time: + description: + - Configure refresh stale-path time + - Please refer vendor documentation for valid values + type: int + regexp: + description: + - Select regular expression engine + - Enable bounded-execution-time regular expression engine + type: bool + route_map: + description: + - route-map control commands + - Have route-map set commands take priority over BGP commands such as next-hop unchanged + type: bool + router_id: + description: Override configured router identifier (peers will reset) + type: dict + suboptions: + address: + description: Manually configured router identifier(A.B.C.D) + type: str + interface: + description: Use IPv4 address on interface + type: str + vrf: + description: + - vrf-specific router id configuration + - Automatically assign per-vrf bgp router id + type: bool + scan_time: + description: + - Configure background scanner interval + - Please refer vendor documentation for valid values + type: int + slow_peer: + description: Configure slow-peer + type: dict + suboptions: + detection: + description: Slow-peer detection + type: dict + suboptions: + set: + description: Slow-peer detection + type: bool + threshold: + description: + - Set the slow-peer detection threshold + - Please refer vendor documentation for valid values + type: int + split_update_group: + description: Configure slow-peer split-update-group + type: dict + suboptions: + dynamic: + description: Dynamically split the slow peer to slow-update group + type: bool + permanent: + description: Keep the slow-peer permanently in slow-update group + type: bool + snmp: + description: + - BGP SNMP options + - BGP SNMP trap options + - Use cbgp Peer2Type as part of index for traps + type: bool + sso: + description: + - Stateful Switchover + - Enable SSO only for Route-Refresh capable peers + type: bool + soft_reconfig_backup: + description: Use soft-reconfiguration inbound only when route-refresh is not negotiated + type: bool + suppress_inactive: + description: Suppress routes that are not in the routing table + type: bool + transport: + description: + - Global enable/disable transport session parameters + - Transport path MTU discovery + type: bool + update_delay: + description: + - Set the max initial delay for sending update + - Please refer vendor documentation for valid values + type: int + update_group: + description: + - Manage peers in bgp update groups + - Split update groups based on Policy + - Keep peers with as-override in different update groups + type: bool + upgrade_cli: + description: Upgrade to hierarchical AFI mode + type: dict + suboptions: + set: + description: enable upgrade to hierarchical AFI mode + type: bool + af_mode: + description: Upgrade to AFI mode + type: bool + bmp: + description: BGP Monitoring Protocol + type: dict + suboptions: + buffer_size: + description: + - BMP Buffer Size + - Please refer vendor documentation for valid values + type: int + initial_refresh: + description: Initial Refresh options + type: dict + suboptions: + delay: + description: Delay before Initial Refresh + type: int + skip: + description: skip all refreshes + type: bool + server: + description: + - Server Information + - Please refer vendor documentation for valid values + type: int + server_options: + description: bmp server options + type: dict + suboptions: + activate: + description: activate server + type: bool + address: + description: skip all refreshes + type: dict + suboptions: + host: + description: host address + type: str + port: + description: port number BMP server + type: int + default_information: + description: + - Control distribution of default information + - Distribute a default route + type: bool + default_metric: + description: + - Set metric of redistributed routes + - Please refer vendor documentation for valid values + type: int + distance: + description: Define an administrative distance + type: dict + suboptions: + admin: + description: Administrative distance + type: dict + suboptions: + distance: + description: + - Administrative distance + - Please refer vendor documentation for valid values + type: int + address: + description: IP Source address (A.B.C.D) + type: str + wildcard_bit: + description: Wildcard bits (A.B.C.D) + type: str + acl: + description: + - IP Standard access list number + - IP Standard expanded access list number + - Standard access-list name + type: str + bgp: + description: BGP distance + type: dict + suboptions: + routes_external: + description: + - Distance for routes external to the AS + - Please refer vendor documentation for valid values + type: int + routes_internal: + description: + - Distance for routes internal to the AS + - Please refer vendor documentation for valid values + type: int + routes_local: + description: + - Distance for local routes + - Please refer vendor documentation for valid values + type: int + mbgp: + description: MBGP distance + type: dict + suboptions: + routes_external: + description: + - Distance for routes external to the AS + - Please refer vendor documentation for valid values + type: int + routes_internal: + description: + - Distance for routes internal to the AS + - Please refer vendor documentation for valid values + type: int + routes_local: + description: + - Distance for local routes + - Please refer vendor documentation for valid values + type: int + distributes: + description: Filter networks in routing updates + type: list + elements: dict + suboptions: + prefix: + description: Filtering incoming updates based on gateway + type: str + gateway: + description: Filter prefixes in routing updates + type: str + acl: + description: IP access list number/name + type: str + in: + description: Filter incoming routing updates + type: bool + out: + description: Filter outgoing routing updates + type: bool + interface: + description: interface details + type: str + distribute_list: + description: + - Filter networks in routing updates + - This option is DEPRECATED and is replaced with distributes which is of type list of dict, + this attribute will be removed after 2024-06-01. + type: dict + suboptions: + acl: + description: IP access list number/name + type: str + in: + description: Filter incoming routing updates + type: bool + out: + description: Filter outgoing routing updates + type: bool + interface: + description: interface details + type: str + maximum_paths: + description: Forward packets over multiple paths + type: dict + suboptions: + paths: + description: Number of paths + type: int + eibgp: + description: Both eBGP and iBGP paths as multipath + type: int + ibgp: + description: iBGP-multipath + type: int + maximum_secondary_paths: + description: Maximum secondary paths + type: dict + suboptions: + paths: + description: Number of secondary paths + type: int + eibgp: + description: Both eBGP and iBGP paths as secondary multipath + type: int + ibgp: + description: iBGP-secondary-multipath + type: int + neighbors: + description: Specify a neighbor router + type: list + elements: dict + aliases: + - neighbor + suboptions: + neighbor_address: + description: + - Neighbor address (A.B.C.D) + - Neighbor tag + - Neighbor ipv6 address (X:X:X:X::X) + type: str + address: + description: + - Neighbor address (A.B.C.D) + - This option is DEPRECATED and replaced with neighbor_address, + this attribute will be removed after 2024-06-01. + type: str + tag: + description: + - Neighbor tag + - This option is DEPRECATED and replaced with neighbor_address, + this attribute will be removed after 2024-06-01. + type: str + ipv6_adddress: + description: + - Neighbor ipv6 address (X:X:X:X::X) + - This option is DEPRECATED and replaced with neighbor_address, + this attribute will be removed after 2024-06-01. + type: str + activate: + description: Enable the Address Family for this Neighbor + type: bool + additional_paths: + description: Negotiate additional paths capabilities with this neighbor + type: dict + suboptions: + disable: + description: Disable additional paths for this neighbor + type: bool + receive: + description: Receive additional paths from neighbors + type: bool + send: + description: Send additional paths to neighbors + type: bool + advertise: + description: Advertise to this neighbor + type: dict + suboptions: + additional_paths: + description: Advertise additional paths + type: dict + suboptions: + all: + description: Select all available paths + type: bool + best: + description: Select best N paths (2-3). + type: int + group_best: + description: Select group-best path + type: bool + best_external: + description: Advertise best-external (at RRs best-internal) path + type: bool + diverse_path: + description: Advertise additional paths + type: dict + suboptions: + backup: + description: Diverse path can be backup path + type: bool + mpath: + description: Diverse path can be multipath + type: bool + advertise_map: + description: specify route-map for conditional advertisement + type: dict + suboptions: + name: + description: advertise route-map name + type: str + exist_map: + description: + - advertise prefix only if prefix is in the condition exists + - condition route-map name + type: str + non_exist_map: + description: + - advertise prefix only if prefix in the condition does not exist + - condition route-map name + type: str + advertisement_interval: + description: Minimum interval between sending BGP routing updates + type: int + aigp: + description: AIGP on neighbor + type: dict + suboptions: + enable: + description: Enable AIGP + type: bool + send: + description: Cost community or MED carrying AIGP VALUE + type: dict + suboptions: + cost_community: + description: Cost extended community carrying AIGP Value + type: dict + suboptions: + id: + description: + - Community ID + - Please refer vendor documentation for valid values + type: int + poi: + description: Point of Insertion + type: dict + suboptions: + igp_cost: + description: Point of Insertion After IGP + type: bool + pre_bestpath: + description: Point of Insertion At Beginning + type: bool + transitive: + description: Cost community is Transitive + type: bool + med: + description: Med carrying AIGP Value + type: bool + allow_policy: + description: Enable the policy support for this IBGP Neighbor + type: bool + allowas_in: + description: Accept as-path with my AS present in it + type: int + as_override: + description: + - Override matching AS-number while sending update + - Maintain Split Horizon while sending update + type: bool + bmp_activate: + description: Activate the BMP monitoring for a BGP peer + type: dict + suboptions: + all: + description: Activate BMP monitoring for all servers + type: bool + server: + description: + - Activate BMP for server + - BMP Server Number + - Please refer vendor documentation for valid values + type: int + capability: + description: + - Advertise capability to the peer + - Advertise ORF capability to the peer + - Advertise prefix-list ORF capability to this neighbor + type: dict + suboptions: + both: + description: Capability to SEND and RECEIVE the ORF to/from this neighbor + type: bool + receive: + description: Capability to RECEIVE the ORF from this neighbor + type: bool + send: + description: Capability to SEND the ORF to this neighbor + type: bool + cluster_id: + description: + - Configure Route-Reflector Cluster-id (peers may reset) + - Route-Reflector Cluster-id as 32 bit quantity, or + Route-Reflector Cluster-id in IP address format (A.B.C.D) + type: str + default_originate: + description: Originate default route to this neighbor + type: dict + suboptions: + set: + description: Originate default route to this neighbor + type: bool + route_map: + description: Route-map to specify criteria to originate default + type: str + description: + description: Neighbor specific description + type: str + disable_connected_check: + description: one-hop away EBGP peer using loopback address + type: bool + distribute_list: + description: Filter updates to/from this neighbor + type: dict + suboptions: + acl: + description: IP access list number/name + type: str + in: + description: Filter incoming updates + type: bool + out: + description: Filter outgoing updates + type: bool + dmzlink_bw: + description: Propagate the DMZ link bandwidth + type: bool + ebgp_multihop: + description: Allow EBGP neighbors not on directly connected networks + type: dict + suboptions: + enable: + description: Allow EBGP neighbors not on directly connected networks + type: bool + hop_count: + description: + - Maximum hop count + - Please refer vendor documentation for valid values + type: int + fall_over: + description: Session fall on peer route lost + type: dict + suboptions: + bfd: + description: Use BFD to detect failure + type: dict + suboptions: + set: + description: set bfd + type: bool + multi_hop: + description: Force BFD multi-hop to detect failure + type: bool + single_hop: + description: Force BFD single-hop to detect failure + type: bool + route_map: + description: Route map for peer route + type: str + filter_list: + description: Establish BGP filters + type: dict + suboptions: + path_acl: + description: AS path access list + type: str + in: + description: Filter incoming updates + type: bool + out: + description: Filter outgoing updates + type: bool + ha_mode: + description: high availability mode + type: dict + suboptions: + set: + description: set ha-mode and graceful-restart for this peer + type: bool + disable: + description: disable graceful-restart + type: bool + inherit: + description: + - Inherit a template + - Inherit a peer-session template and Template name + type: str + local_as: + description: Specify a local-as number + type: dict + suboptions: + set: + description: set local-as number + type: bool + number: + description: + - AS number used as local AS + - Please refer vendor documentation for valid values + type: int + dual_as: + description: Accept either real AS or local AS from the ebgp peer + type: bool + no_prepend: + description: Do not prepend local-as to updates from ebgp peers + type: dict + suboptions: + set: + description: Set prepend + type: bool + replace_as: + description: Replace real AS with local AS in the EBGP updates + type: bool + log_neighbor_changes: + description: Log neighbor up/down and reset reason + type: dict + suboptions: + set: + description: set Log neighbor up/down and reset + type: bool + disable: + description: disable Log neighbor up/down and reset + type: bool + maximum_prefix: + description: Maximum number of prefixes accepted from this peer + type: dict + suboptions: + max_no: + description: maximum no. of prefix limit + type: int + threshold_val: + description: Threshold value (%) at which to generate a warning msg + type: int + restart: + description: Restart bgp connection after limit is exceeded + type: int + warning_only: + description: Only give warning message when limit is exceeded + type: bool + next_hop_self: + description: Disable the next hop calculation for this neighbor + type: dict + suboptions: + set: + description: Enable next-hop-self + type: bool + all: + description: Enable next-hop-self for both eBGP and iBGP received paths + type: bool + next_hop_unchanged: + description: + - Propagate next hop unchanged for iBGP paths to this neighbor + - Propagate next hop unchanged for all paths (iBGP and eBGP) to this neighbor + type: dict + suboptions: + set: + description: Enable next-hop-unchanged + type: bool + allpaths: + description: Propagate next hop unchanged for all paths (iBGP and eBGP) to this neighbor + type: bool + password: + description: + - Set a password + - This option is DEPRECATED and is replaced with password_options which + accepts dict as input, this attribute will be removed after 2024-06-01. + type: str + password_options: + description: Set a password with encryption type + type: dict + suboptions: + encryption: + description: Encryption type (0 to disable encryption, 7 for proprietary) + type: int + pass_key: + description: The password + type: str + path_attribute: + description: BGP optional attribute filtering + type: dict + suboptions: + discard: + description: Discard matching path-attribute for this neighbor + type: dict + suboptions: + type: + description: + - path attribute type + - Please refer vendor documentation for valid values + type: int + range: + description: path attribute range + type: dict + suboptions: + start: + description: + - path attribute range start value + - Please refer vendor documentation for valid values + type: int + end: + description: + - path attribute range end value + - Please refer vendor documentation for valid values + type: int + in: + description: Perform inbound path-attribute filtering + type: bool + treat_as_withdraw: + description: Treat-as-withdraw matching path-attribute for this neighbor + type: dict + suboptions: + type: + description: + - path attribute type + - Please refer vendor documentation for valid values + type: int + range: + description: path attribute range + type: dict + suboptions: + start: + description: + - path attribute range start value + - Please refer vendor documentation for valid values + type: int + end: + description: + - path attribute range end value + - Please refer vendor documentation for valid values + type: int + in: + description: Perform inbound path-attribute filtering + type: bool + peer_group: + description: Member of the peer-group + type: str + remote_as: + description: + - Specify a BGP neighbor + - AS of remote neighbor + type: str + remove_private_as: + description: Remove private AS number from outbound updates + type: dict + suboptions: + set: + description: Remove private AS number + type: bool + all: + description: Remove all private AS numbers + type: bool + replace_as: + description: Replace all private AS numbers with local AS + type: bool + route_map: + description: + - Apply route map to neighbor + - This option is DEPRECATED and is replaced with route_maps which + accepts list of dict as input, this attribute will be removed after 2024-06-01. + type: dict + suboptions: + name: + description: Replace all private AS numbers with local AS + type: str + in: + description: Apply map to incoming routes + type: bool + out: + description: Apply map to outbound routes + type: bool + route_maps: + description: Apply a list of route maps to neighbor + type: list + elements: dict + suboptions: + name: + description: Replace all private AS numbers with local AS + type: str + in: + description: Apply map to incoming routes + type: bool + out: + description: Apply map to outbound routes + type: bool + route_reflector_client: + description: Configure a neighbor as Route Reflector client + type: bool + route_server_client: + description: Configure a neighbor as Route Server client + type: dict + suboptions: + set: + description: Set Route Server client + type: bool + context: + description: + - Specify Route Server context for neighbor + - Route Server context name + type: str + send_community: + description: Send Community attribute to this neighbor + type: dict + suboptions: + set: + description: Set send Community attribute to this neighbor + type: bool + both: + description: Send Standard and Extended Community attributes + type: bool + extended: + description: Send Extended Community attribute + type: bool + standard: + description: Send Standard Community attribute + type: bool + send_label: + description: Send NLRI + MPLS Label to this peer + type: dict + suboptions: + set: + description: Set send NLRI + MPLS Label to this peer + type: bool + explicit_null: + description: Advertise Explicit Null label in place of Implicit Null + type: bool + shutdown: + description: Administratively shut down this neighbor + type: dict + suboptions: + set: + description: shut down + type: bool + graceful: + description: + - Gracefully shut down this neighbor + - time in seconds + - Please refer vendor documentation for valid values + type: int + community: + description: Set Community for Gshut routes + type: int + local_preference: + description: Set Local Preference for Gshut routes + type: bool + slow_peer: + description: Configure slow-peer + type: dict + suboptions: + detection: + description: Configure slow-peer + type: dict + suboptions: + enable: + description: Enable slow-peer detection + type: bool + disable: + description: Disable slow-peer detection + type: bool + threshold: + description: Set the slow-peer detection threshold + type: int + split_update_group: + description: Configure slow-peer split-update-group + type: dict + suboptions: + dynamic: + description: Dynamically split the slow peer to slow-update group + type: dict + suboptions: + enable: + description: Enable slow-peer detection + type: bool + disable: + description: Disable slow-peer detection + type: bool + permanent: + description: Keep the slow-peer permanently in slow-update group + type: bool + static: + description: Static slow-peer + type: bool + soft_reconfiguration: + description: + - Per neighbor soft reconfiguration + - Allow inbound soft reconfiguration for this neighbor + type: bool + timers: + description: BGP per neighbor timers + type: dict + suboptions: + interval: + description: Keepalive interval + type: int + holdtime: + description: Holdtime + type: int + min_holdtime: + description: Minimum hold time from neighbor + type: int + translate_update: + description: Translate Update to MBGP format + type: dict + suboptions: + set: + description: Set Translate Update + type: bool + nlri: + description: Specify type of nlri to translate to + type: dict + suboptions: + multicast: + description: Translate Update to multicast nlri + type: bool + unicast: + description: Process Update as unicast nlri + type: bool + transport: + description: Transport options + type: dict + suboptions: + connection_mode: + description: Specify passive or active connection + type: dict + suboptions: + active: + description: Actively establish the TCP session + type: bool + passive: + description: Passively establish the TCP session + type: bool + multi_session: + description: Use Multi-session for transport + type: bool + path_mtu_discovery: + description: Use transport path MTU discovery + type: dict + suboptions: + set: + description: Use path MTU discovery + type: bool + disable: + description: disable + type: bool + ttl_security: + description: + - BGP ttl security check + - maximum number of hops + - Please refer vendor documentation for valid values + type: int + unsuppress_map: + description: + - Route-map to selectively un-suppress suppressed routes + - Name of route map + type: str + update_source: + description: Source of routing updates + type: str + version: + description: + - Set the BGP version to match a neighbor + - Neighbor's BGP version + - Please refer vendor documentation for valid values + type: int + weight: + description: Set default weight for routes from this neighbor + type: int + networks: + description: Specify a network to announce via BGP + type: list + elements: dict + suboptions: + address: + description: Specify network address + type: str + netmask: + description: Specify network mask + type: str + route_map: + description: Route-map to modify the attributes + type: str + backdoor: + description: Specify a BGP backdoor route + type: bool + redistribute: + description: Redistribute information from another routing protocol + type: list + elements: dict + suboptions: + application: + description: Application + type: dict + suboptions: + name: + description: Application name + type: str + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + bgp: + description: Border Gateway Protocol (BGP) + type: dict + suboptions: + as_number: + description: Autonomous system number + type: str + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + connected: + description: Connected + type: dict + suboptions: + set: + description: Set the top level attribute + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + eigrp: + description: Enhanced Interior Gateway Routing Protocol (EIGRP) + type: dict + suboptions: + as_number: + description: Autonomous system number + type: str + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + isis: + description: ISO IS-IS + type: dict + suboptions: + area_tag: + description: ISO routing area tag + type: str + clns: + description: Redistribution of OSI dynamic routes + type: bool + ip: + description: Redistribution of IP dynamic routes + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + iso_igrp: + description: IGRP for OSI networks + type: dict + suboptions: + area_tag: + description: ISO routing area tag + type: str + route_map: + description: Route map reference + type: str + lisp: + description: Locator ID Separation Protocol (LISP) + type: dict + suboptions: + set: + description: Set the top level attribute + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + mobile: + description: Mobile routes + type: dict + suboptions: + set: + description: Set the top level attribute + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + odr: + description: On Demand stub Routes + type: dict + suboptions: + set: + description: Set the top level attribute + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + ospf: + description: Open Shortest Path First (OSPF) + type: dict + suboptions: + process_id: + description: Process ID + type: int + match: + description: On Demand stub Routes + type: dict + suboptions: + external: + description: Redistribute OSPF external routes + type: bool + internal: + description: Redistribute OSPF internal routes + type: bool + nssa_external: + description: Redistribute OSPF NSSA external routes + type: bool + type_1: + description: Redistribute NSSA external type 1 routes + type: bool + type_2: + description: Redistribute NSSA external type 2 routes + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + vrf: + description: VPN Routing/Forwarding Instance + type: str + ospfv3: + description: OSPFv3 + type: dict + suboptions: + process_id: + description: Process ID + type: int + match: + description: On Demand stub Routes + type: dict + suboptions: + external: + description: Redistribute OSPF external routes + type: bool + internal: + description: Redistribute OSPF internal routes + type: bool + nssa_external: + description: Redistribute OSPF NSSA external routes + type: bool + type_1: + description: Redistribute NSSA external type 1 routes + type: bool + type_2: + description: Redistribute NSSA external type 2 routes + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + rip: + description: Routing Information Protocol (RIP) + type: dict + suboptions: + set: + description: Set the top level attribute + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + static: + description: Static routes + type: dict + suboptions: + set: + description: Set the top level attribute + type: bool + clns: + description: Redistribution of OSI static routes + type: bool + ip: + description: Redistribution of IP static routes + type: bool + metric: + description: Metric for redistributed routes + type: int + route_map: + description: Route map reference + type: str + vrf: + description: Specify a source VRF + type: dict + suboptions: + name: + description: Source VRF name + type: str + global: + description: global VRF + type: bool + route_server_context: + description: + - Enter route server context command mode + - This option is DEPRECATED as it is out of scope of the module, + this attribute will be removed after 2024-06-01. + type: dict + suboptions: + name: + description: Name of route server context + type: str + address_family: + description: Enter address family command mode + type: dict + suboptions: + afi: + description: Address family + type: str + choices: ["ipv4", "ipv6"] + modifier: + description: Address Family modifier + type: str + choices: ["multicast", "unicast"] + import_map: + description: + - Import matching routes using a route map + - Name of route map + type: str + description: + description: Textual description of the router server context + type: str + scope: + description: + - Enter scope command mode + - This option is DEPRECATED as is not valid within the scope of module, + this attribute will be removed after 2024-06-01. + type: dict + suboptions: + global: + description: Global scope + type: bool + vrf: + description: + - VRF scope + - VPN Routing/Forwarding instance name + type: str + synchronization: + description: Perform IGP synchronization + type: bool + table_map: + description: Map external entry attributes into routing table + type: dict + suboptions: + name: + description: route-map name + type: str + filter: + description: Selective route download + type: bool + template: + description: + - Enter template command mode + - This option is DEPRECATED as is not valid within the scope of module, + this attribute will be removed after 2024-06-01. + type: dict + suboptions: + peer_policy: + description: Template configuration for policy parameters + type: str + peer_session: + description: Template configuration for session parameters + type: str + timers: + description: + - Adjust routing timers + - BGP timers + type: dict + suboptions: + keepalive: + description: Keepalive interval + type: int + holdtime: + description: Holdtime + type: int + min_holdtime: + description: Minimum hold time from neighbor + type: int + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS + device by executing the command B(sh running-config | section ^router bgp). + - 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 + state: + choices: + - merged + - replaced + - deleted + - purged + - gathered + - rendered + - parsed + default: merged + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp + +- name: Merge provided configuration with device configuration + cisco.ios.ios_bgp_global: + config: + as_number: 65000 + bgp: + advertise_best_external: true + bestpath: + - compare_routerid: true + dampening: + penalty_half_time: 1 + reuse_route_val: 1 + suppress_route_val: 1 + max_suppress: 1 + graceful_shutdown: + neighbors: + time: 50 + community: 100 + local_preference: 100 + log_neighbor_changes: true + nopeerup_delay: + - post_boot: 10 + networks: + - address: 192.0.2.3 + - address: 192.0.2.2 + neighbor: + - address: 192.0.2.1 + description: merge neighbor + remote_as: 100 + aigp: + send: + cost_community: + id: 100 + poi: + igp_cost: true + transitive: true + route_map: + name: test-route + out: true + redistribute: + - connected: + metric: 10 + timers: + keepalive: 100 + holdtime: 200 + min_holdtime: 150 + state: merged + +# Task Output: +# ------------ +# +# before: {} +# commands: +# - router bgp 65000 +# - timers bgp 100 200 150 +# - bgp advertise-best-external +# - bgp bestpath compare-routerid +# - bgp dampening 1 1 1 1 +# - bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# - bgp log-neighbor-changes +# - bgp nopeerup-delay post-boot 10 +# - network 192.0.2.3 +# - network 192.0.2.2 +# - neighbor 192.0.2.1 remote-as 100 +# - neighbor 192.0.2.1 description merge neighbor +# - neighbor 192.0.2.1 aigp send cost-community 100 poi igp-cost transitive +# - neighbor 192.0.2.1 route-map test-route out +# - redistribute connected metric 10 +# after: +# as_number: '65000' +# bgp: +# advertise_best_external: true +# bestpath_options: +# compare_routerid: true +# dampening: +# max_suppress: 1 +# penalty_half_time: 1 +# reuse_route_val: 1 +# suppress_route_val: 1 +# graceful_shutdown: +# community: '100' +# local_preference: 100 +# neighbors: +# time: 50 +# log_neighbor_changes: true +# nopeerup_delay_options: +# post_boot: 10 +# neighbors: +# - aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# description: merge neighbor +# neighbor_address: 192.0.2.1 +# remote_as: '100' +# route_maps: +# - name: test-route +# out: true +# networks: +# - address: 192.0.2.2 +# - address: 192.0.2.3 +# redistribute: +# - connected: +# metric: 10 +# set: true +# timers: +# holdtime: 200 +# keepalive: 100 +# min_holdtime: 150 + +# After state: +# ------------ +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp nopeerup-delay post-boot 10 +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# bgp bestpath compare-routerid +# bgp dampening 1 1 1 1 +# bgp advertise-best-external +# network 192.0.2.2 +# network 192.0.2.3 +# timers bgp 100 200 150 +# redistribute connected metric 10 +# neighbor 192.0.2.1 remote-as 100 +# neighbor 192.0.2.1 description merge neighbor +# neighbor 192.0.2.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 192.0.2.1 route-map test-route out + +# Using replaced + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp nopeerup-delay post-boot 10 +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# bgp bestpath compare-routerid +# bgp dampening 1 1 1 1 +# network 192.0.2.2 +# network 192.0.2.3 +# bgp advertise-best-external +# neighbor 198.0.2.1 remote-as 100 +# neighbor 198.0.2.1 description merge neighbor +# neighbor 198.0.2.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 198.0.2.1 route-map test-route out + +- name: Replaces device configuration of listed global BGP with provided configuration + cisco.ios.ios_bgp_global: + config: + as_number: 65000 + bgp: + advertise_best_external: true + bestpath: + - med: + confed: true + log_neighbor_changes: true + nopeerup_delay: + - post_boot: 10 + cold_boot: 20 + networks: + - address: 192.0.2.4 + neighbor: + - address: 192.0.2.5 + description: replace neighbor + remote_as: 100 + slow_peer: + detection: + disable: true + state: replaced + +# Task Output: +# ------------ +# +# before: +# as_number: '65000' +# bgp: +# advertise_best_external: true +# bestpath_options: +# compare_routerid: true +# dampening: +# max_suppress: 1 +# penalty_half_time: 1 +# reuse_route_val: 1 +# suppress_route_val: 1 +# graceful_shutdown: +# community: '100' +# local_preference: 100 +# neighbors: +# time: 50 +# log_neighbor_changes: true +# nopeerup_delay_options: +# post_boot: 10 +# neighbors: +# - aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# description: merge neighbor +# neighbor_address: 198.0.2.1 +# remote_as: '100' +# route_maps: +# - name: test-route +# out: true +# networks: +# - address: 192.0.2.2 +# - address: 192.0.2.3 +# commands: +# - router bgp 65000 +# - no bgp bestpath compare-routerid +# - bgp bestpath med confed +# - no bgp dampening 1 1 1 1 +# - no bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# - bgp nopeerup-delay cold-boot 20 +# - network 192.0.2.4 +# - no network 192.0.2.2 +# - no network 192.0.2.3 +# - neighbor 192.0.2.5 remote-as 100 +# - neighbor 192.0.2.5 description replace neighbor +# - neighbor 192.0.2.5 slow-peer detection disable +# - no neighbor 198.0.2.1 +# after: +# as_number: '65000' +# bgp: +# advertise_best_external: true +# bestpath_options: +# med: +# confed: true +# log_neighbor_changes: true +# nopeerup_delay_options: +# cold_boot: 20 +# post_boot: 10 +# neighbors: +# - description: replace neighbor +# neighbor_address: 192.0.2.5 +# remote_as: '100' +# slow_peer: +# detection: +# disable: true +# networks: +# - address: 192.0.2.4 + +# After state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp nopeerup-delay cold-boot 20 +# bgp nopeerup-delay post-boot 10 +# bgp bestpath med confed +# bgp advertise-best-external +# network 192.0.2.4 +# neighbor 192.0.2.5 remote-as 100 +# neighbor 192.0.2.5 description replace neighbor +# neighbor 192.0.2.5 slow-peer detection disable + +# Using Deleted + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp nopeerup-delay post-boot 10 +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# bgp bestpath compare-routerid +# bgp dampening 1 1 1 1 +# bgp advertise-best-external +# neighbor 192.0.2.1 remote-as 100 +# neighbor 192.0.2.1 description merge neighbor +# neighbor 192.0.2.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 192.0.2.1 route-map test-route out + +- name: "Delete global BGP (Note: This won't delete the configured global BGP)" + cisco.ios.ios_bgp_global: + config: + as_number: 65000 + state: deleted + +# Task Output: +# ------------ +# +# before: +# as_number: '65000' +# bgp: +# advertise_best_external: true +# bestpath_options: +# compare_routerid: true +# dampening: +# max_suppress: 1 +# penalty_half_time: 1 +# reuse_route_val: 1 +# suppress_route_val: 1 +# graceful_shutdown: +# community: '100' +# local_preference: 100 +# neighbors: +# time: 50 +# log_neighbor_changes: true +# nopeerup_delay_options: +# post_boot: 10 +# neighbors: +# - aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# description: merge neighbor +# neighbor_address: 192.0.2.1 +# remote_as: '100' +# route_maps: +# - name: test-route +# out: true +# commands: +# - router bgp 65000 +# - no bgp advertise-best-external +# - no bgp bestpath compare-routerid +# - no bgp dampening 1 1 1 1 +# - no bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# - no bgp log-neighbor-changes +# - no bgp nopeerup-delay post-boot 10 +# - no neighbor 192.0.2.1 +# after: +# as_number: '65000' + +# After state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 + +# Using Deleted without any config passed + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp nopeerup-delay post-boot 10 +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# bgp bestpath compare-routerid +# bgp dampening 1 1 1 1 +# bgp advertise-best-external +# neighbor 192.0.2.1 remote-as 100 +# neighbor 192.0.2.1 description merge neighbor +# neighbor 192.0.2.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 192.0.2.1 route-map test-route out + +- name: Delete global BGP without config + cisco.ios.ios_bgp_global: + state: deleted + +# Task Output: +# ------------ +# +# before: +# as_number: '65000' +# bgp: +# advertise_best_external: true +# bestpath_options: +# compare_routerid: true +# dampening: +# max_suppress: 1 +# penalty_half_time: 1 +# reuse_route_val: 1 +# suppress_route_val: 1 +# graceful_shutdown: +# community: '100' +# local_preference: 100 +# neighbors: +# time: 50 +# log_neighbor_changes: true +# nopeerup_delay_options: +# post_boot: 10 +# neighbors: +# - aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# description: merge neighbor +# neighbor_address: 192.0.2.1 +# remote_as: '100' +# route_maps: +# - name: test-route +# out: true +# commands: +# - router bgp 65000 +# - no bgp advertise-best-external +# - no bgp bestpath compare-routerid +# - no bgp dampening 1 1 1 1 +# - no bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# - no bgp nopeerup-delay post-boot 10 +# - no neighbor 198.51.100.1 +# after: +# as_number: '65000' + +# After state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 + +# Using purged - would delete all configuration + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp nopeerup-delay post-boot 10 +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# bgp bestpath compare-routerid +# bgp dampening 1 1 1 1 +# bgp advertise-best-external +# neighbor 192.0.2.1 remote-as 100 +# neighbor 192.0.2.1 description merge neighbor +# neighbor 192.0.2.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 192.0.2.1 route-map test-route out + +- name: + "Delete the configured global BGP (Note: This WILL delete the the configured + global BGP)" + cisco.ios.ios_bgp_global: + state: purged + +# Task Output: +# ------------ +# +# before: +# as_number: '65000' +# bgp: +# advertise_best_external: true +# bestpath_options: +# compare_routerid: true +# dampening: +# max_suppress: 1 +# penalty_half_time: 1 +# reuse_route_val: 1 +# suppress_route_val: 1 +# graceful_shutdown: +# community: '100' +# local_preference: 100 +# neighbors: +# time: 50 +# log_neighbor_changes: true +# nopeerup_delay_options: +# post_boot: 10 +# neighbors: +# - aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# description: merge neighbor +# neighbor_address: 192.0.2.1 +# remote_as: '100' +# route_maps: +# - name: test-route +# out: true +# commands: +# - no router bgp 65000 +# after: {} + +# After state: +# ------------- +# +# vios#sh running-config | section ^router bgp + +# Using gathered + +# Before state: +# ------------- +# +# vios#sh running-config | section ^router bgp +# router bgp 65000 +# bgp log-neighbor-changes +# bgp nopeerup-delay post-boot 10 +# bgp graceful-shutdown all neighbors 50 local-preference 100 community 100 +# bgp bestpath compare-routerid +# bgp dampening 1 1 1 1 +# bgp advertise-best-external +# network 192.0.2.3 +# timers bgp 100 200 150 +# redistribute connected metric 10 +# neighbor 192.0.2.1 remote-as 100 +# neighbor 192.0.2.1 description merge neighbor +# neighbor 192.0.2.1 aigp send cost-community 100 poi igp-cost transitive +# neighbor 192.0.2.1 route-map test-route out + +- name: Gather facts for bgp_global + cisco.ios.ios_bgp_global: + config: + state: gathered + +# Task Output: +# ------------ +# +# gathered: +# as_number: '65000' +# bgp: +# advertise_best_external: true +# bestpath_options: +# compare_routerid: true +# dampening: +# max_suppress: 1 +# penalty_half_time: 1 +# reuse_route_val: 1 +# suppress_route_val: 1 +# graceful_shutdown: +# community: '100' +# local_preference: 100 +# neighbors: +# time: 50 +# log_neighbor_changes: true +# nopeerup_delay_options: +# post_boot: 10 +# neighbors: +# - aigp: +# send: +# cost_community: +# id: 100 +# poi: +# igp_cost: true +# transitive: true +# description: merge neighbor +# neighbor_address: 192.0.2.1 +# remote_as: '100' +# route_maps: +# - name: test-route +# out: true +# networks: +# - address: 192.0.2.3 +# redistribute: +# - connected: +# metric: 10 +# set: true +# timers: +# holdtime: 200 +# keepalive: 100 +# min_holdtime: 150 + +# Using Rendered + +- name: Rendered the provided configuration with the existing running configuration + cisco.ios.ios_bgp_global: + config: + aggregate_addresses: + - address: 192.0.2.1 + attribute_map: testMap1 + netmask: 255.255.255.0 + summary_only: true + - address: 192.0.2.2 + as_set: true + netmask: 255.255.255.0 + - address: 192.0.2.3 + as_set: true + netmask: 255.255.255.0 + as_number: "65000" + auto_summary: true + bgp: + additional_paths: + install: true + receive: true + aggregate_timer: 0 + always_compare_med: true + asnotation: true + bestpath_options: + aigp: true + compare_routerid: true + med: + confed: true + missing_as_worst: true + confederation: + identifier: "22" + consistency_checker: + error_message: + interval: 10 + set: true + dampening: + route_map: routeMap1Test + deterministic_med: true + graceful_restart: + restart_time: 2 + stalepath_time: 22 + graceful_shutdown: + community: "77" + local_preference: 230 + vrfs: + time: 31 + inject_maps: + - copy_attributes: true + exist_map_name: Testmap3 + name: map2 + - copy_attributes: true + exist_map_name: Testmap2 + name: map1 + listen: + limit: 200 + range: + host_with_subnet: 192.0.2.1/24 + peer_group: PaulNetworkGroup + log_neighbor_changes: true + maxas_limit: 2 + maxcommunity_limit: 3 + maxextcommunity_limit: 3 + nexthop: + route_map: RouteMap1 + trigger: + delay: 2 + nopeerup_delay_options: + cold_boot: 2 + nsf_switchover: 10 + post_boot: 22 + user_initiated: 22 + recursion: true + redistribute_internal: true + refresh: + max_eor_time: 700 + stalepath_time: 800 + router_id: + vrf: true + scan_time: 22 + slow_peer: + detection: + threshold: 345 + split_update_group: + dynamic: true + permanent: true + sso: true + suppress_inactive: true + update_delay: 2 + update_group: true + bmp: + buffer_size: 22 + server: 2 + distance: + bgp: + routes_external: 2 + routes_internal: 3 + routes_local: 4 + mbgp: + routes_external: 2 + routes_internal: 3 + routes_local: 5 + distributes: + - in: true + prefix: prefixTest + - gateway: gatewayTest + out: true + - acl: "300" + interface: Loopback0 + out: true + maximum_paths: + ibgp: 2 + paths: 2 + maximum_secondary_paths: + ibgp: 22 + paths: 22 + neighbors: + - neighbor_address: 192.0.2.10 + remote_as: "64500" + update_source: Loopback1 + - activate: true + neighbor_address: 192.0.2.11 + remote_as: "45000" + send_community: + extended: true + - activate: true + neighbor_address: 192.0.2.12 + remote_as: "45000" + - neighbor_address: 192.0.2.13 + remote_as: "6553601" + - advertise: + diverse_path: + backup: true + neighbor_address: 192.0.2.8 + route_reflector_client: true + - neighbor_address: 192.0.2.9 + remote_as: "64500" + route_maps: + - in: true + name: rmp1 + - in: true + name: rmp2 + update_source: Loopback0 + - activate: true + advertise: + additional_paths: + group_best: true + neighbor_address: 2001:DB8::1037 + - neighbor_address: testNebTag + peer_group: "5" + soft_reconfiguration: true + version: 4 + networks: + - address: 192.0.2.15 + backdoor: true + netmask: 55.255.255.0 + route_map: mp1 + - address: 192.0.2.16 + backdoor: true + netmask: 255.255.255.0 + route_map: mp2 + - address: 192.0.2.17 + backdoor: true + netmask: 255.255.255.0 + route_map: mp2 + redistribute: + - static: + metric: 33 + route_map: rmp1 + set: true + - application: + metric: 22 + name: app1 + - application: + metric: 33 + name: app2 + route_map: mp1 + - connected: + metric: 22 + set: true + - mobile: + metric: 211 + set: true + state: rendered + +# Task Output: +# ------------ +# +# rendered: +# - router bgp 65000 +# - auto-summary +# - bmp buffer-size 22 +# - bmp server 2 +# - distance bgp 2 3 4 +# - distance mbgp 2 3 5 +# - maximum-paths 2 +# - maximum-paths ibgp 2 +# - maximum-secondary-paths 22 +# - maximum-secondary-paths ibgp 22 +# - bgp additional-paths install receive +# - bgp aggregate-timer 0 +# - bgp always-compare-med +# - bgp asnotation dot +# - bgp bestpath aigp ignore +# - bgp bestpath compare-routerid +# - bgp bestpath med confed missing-as-worst +# - bgp confederation identifier 22 +# - bgp consistency-checker error-message interval 10 +# - bgp dampening route-map routeMap1Test +# - bgp deterministic-med +# - bgp graceful-restart restart-time 2 +# - bgp graceful-restart stalepath-time 22 +# - bgp graceful-shutdown all vrfs 31 local-preference 230 community 77 +# - bgp listen limit 200 +# - bgp listen range 192.0.2.1/24 peer-group PaulNetworkGroup +# - bgp log-neighbor-changes +# - bgp maxas-limit 2 +# - bgp maxcommunity-limit 3 +# - bgp maxextcommunity-limit 3 +# - bgp nexthop route-map RouteMap1 +# - bgp nexthop trigger delay 2 +# - bgp nopeerup-delay cold-boot 2 +# - bgp nopeerup-delay post-boot 22 +# - bgp nopeerup-delay nsf-switchover 10 +# - bgp nopeerup-delay user-initiated 22 +# - bgp recursion host +# - bgp redistribute-internal +# - bgp refresh max-eor-time 700 +# - bgp refresh stalepath-time 800 +# - bgp router-id vrf auto-assign +# - bgp scan-time 22 +# - bgp slow-peer detection threshold 345 +# - bgp slow-peer split-update-group dynamic permanent +# - bgp sso route-refresh-enable +# - bgp suppress-inactive +# - bgp update-delay 2 +# - bgp update-group split as-override +# - bgp inject-map map2 exist-map Testmap3 copy-attributes +# - bgp inject-map map1 exist-map Testmap2 copy-attributes +# - distribute-list prefix prefixTest in +# - distribute-list gateway gatewayTest out +# - distribute-list 300 out Loopback0 +# - aggregate-address 192.0.2.1 255.255.255.0 summary-only attribute-map testMap1 +# - aggregate-address 192.0.2.2 255.255.255.0 as-set +# - aggregate-address 192.0.2.3 255.255.255.0 as-set +# - network 192.0.2.15 mask 55.255.255.0 route-map mp1 backdoor +# - network 192.0.2.16 mask 255.255.255.0 route-map mp2 backdoor +# - network 192.0.2.17 mask 255.255.255.0 route-map mp2 backdoor +# - neighbor 192.0.2.10 remote-as 64500 +# - neighbor 192.0.2.10 update-source Loopback1 +# - neighbor 192.0.2.11 remote-as 45000 +# - neighbor 192.0.2.11 activate +# - neighbor 192.0.2.11 send-community extended +# - neighbor 192.0.2.12 remote-as 45000 +# - neighbor 192.0.2.12 activate +# - neighbor 192.0.2.13 remote-as 6553601 +# - neighbor 192.0.2.8 advertise diverse-path backup +# - neighbor 192.0.2.8 route-reflector-client +# - neighbor 192.0.2.9 remote-as 64500 +# - neighbor 192.0.2.9 update-source Loopback0 +# - neighbor 192.0.2.9 route-map rmp1 in +# - neighbor 192.0.2.9 route-map rmp2 in +# - neighbor 2001:DB8::1037 activate +# - neighbor 2001:DB8::1037 advertise additional-paths group-best +# - neighbor testNebTag peer-group 5 +# - neighbor testNebTag soft-reconfiguration inbound +# - neighbor testNebTag version 4 +# - redistribute static metric 33 route-map rmp1 +# - redistribute application app1 metric 22 +# - redistribute application app2 metric 33 route-map mp1 +# - redistribute connected metric 22 +# - redistribute mobile metric 211 + +# Using parsed + +# File: parsed.cfg +# ---------------- +# +# router bgp 65000 +# auto-summary +# bmp buffer-size 22 +# bmp server 2 +# distance bgp 2 3 4 +# distance mbgp 2 3 5 +# maximum-paths 2 +# maximum-paths ibgp 2 +# maximum-secondary-paths 22 +# maximum-secondary-paths ibgp 22 +# description checking description as line +# bgp additional-paths install receive +# bgp aggregate-timer 0 +# bgp always-compare-med +# bgp asnotation dot +# bgp bestpath aigp ignore +# bgp bestpath compare-routerid +# bgp bestpath med confed missing-as-worst +# bgp confederation identifier 22 +# bgp consistency-checker error-message interval 10 +# bgp dampening route-map routeMap1Test +# bgp deterministic-med +# bgp graceful-restart restart-time 2 +# bgp graceful-restart stalepath-time 22 +# bgp graceful-shutdown all vrfs 31 local-preference 230 community 77 +# bgp listen limit 200 +# bgp listen range 192.0.2.1/24 peer-group PaulNetworkGroup +# bgp log-neighbor-changes +# bgp maxas-limit 2 +# bgp maxcommunity-limit 3 +# bgp maxextcommunity-limit 3 +# bgp nexthop route-map RouteMap1 +# bgp nexthop trigger delay 2 +# bgp nopeerup-delay cold-boot 2 +# bgp nopeerup-delay post-boot 22 +# bgp nopeerup-delay nsf-switchover 10 +# bgp nopeerup-delay user-initiated 22 +# bgp recursion host +# bgp redistribute-internal +# bgp refresh max-eor-time 700 +# bgp refresh stalepath-time 800 +# bgp router-id vrf auto-assign +# bgp scan-time 22 +# bgp slow-peer detection threshold 345 +# bgp slow-peer split-update-group dynamic permanent +# bgp sso route-refresh-enable +# bgp suppress-inactive +# bgp update-delay 2 +# bgp update-group split as-override +# bgp inject-map map2 exist-map Testmap3 copy-attributes +# bgp inject-map map1 exist-map Testmap2 copy-attributes +# distribute-list prefix prefixTest in +# distribute-list gateway gatewayTest out +# distribute-list 300 out Loopback0 +# aggregate-address 192.0.2.1 255.255.255.0 summary-only attribute-map testMap1 +# aggregate-address 192.0.2.2 255.255.255.0 as-set +# aggregate-address 192.0.2.3 255.255.255.0 as-set +# network 192.0.2.15 mask 55.255.255.0 route-map mp1 backdoor +# network 192.0.2.16 mask 255.255.255.0 route-map mp2 backdoor +# network 192.0.2.17 mask 255.255.255.0 route-map mp2 backdoor +# neighbor 192.0.2.8 advertise diverse-path backup +# neighbor 192.0.2.8 route-reflector-client +# neighbor 192.0.2.9 remote-as 64500 +# neighbor 192.0.2.9 update-source Loopback0 +# neighbor 192.0.2.9 route-map rmp1 in +# neighbor 192.0.2.9 route-map rmp2 in +# neighbor 192.0.2.10 remote-as 64500 +# neighbor 192.0.2.10 update-source Loopback1 +# neighbor 192.0.2.11 remote-as 45000 +# neighbor 192.0.2.11 activate +# neighbor 192.0.2.11 send-community extended +# neighbor 192.0.2.12 remote-as 45000 +# neighbor 192.0.2.12 activate +# neighbor 192.0.2.13 remote-as 6553601 +# neighbor 192.0.2.13 shutdown graceful 10 community 20 +# neighbor 2001:DB8::1037 activate +# neighbor 2001:DB8::1037 advertise additional-paths group-best +# neighbor testNebTag peer-group 5 +# neighbor testNebTag soft-reconfiguration inbound +# neighbor testNebTag version 4 +# redistribute static metric 33 route-map rmp1 +# redistribute application app1 metric 22 +# redistribute application app2 metric 33 route-map mp1 +# redistribute connected metric 22 +# redistribute mobile metric 21 + +- name: Parse the commands for provided configuration + cisco.ios.ios_bgp_global: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Task Output: +# ------------ +# +# parsed: +# aggregate_addresses: +# - address: 192.0.2.1 +# attribute_map: testMap1 +# netmask: 255.255.255.0 +# summary_only: true +# - address: 192.0.2.2 +# as_set: true +# netmask: 255.255.255.0 +# - address: 192.0.2.3 +# as_set: true +# netmask: 255.255.255.0 +# as_number: '65000' +# auto_summary: true +# bgp: +# additional_paths: +# install: true +# receive: true +# aggregate_timer: 0 +# always_compare_med: true +# asnotation: true +# bestpath_options: +# aigp: true +# compare_routerid: true +# med: +# confed: true +# missing_as_worst: true +# confederation: +# identifier: '22' +# consistency_checker: +# error_message: +# interval: 10 +# set: true +# dampening: +# route_map: routeMap1Test +# deterministic_med: true +# graceful_restart: +# restart_time: 2 +# stalepath_time: 22 +# graceful_shutdown: +# community: '77' +# local_preference: 230 +# vrfs: +# time: 31 +# inject_maps: +# - copy_attributes: true +# exist_map_name: Testmap3 +# name: map2 +# - copy_attributes: true +# exist_map_name: Testmap2 +# name: map1 +# listen: +# limit: 200 +# range: +# host_with_subnet: 192.0.2.1/24 +# peer_group: PaulNetworkGroup +# log_neighbor_changes: true +# maxas_limit: 2 +# maxcommunity_limit: 3 +# maxextcommunity_limit: 3 +# nexthop: +# route_map: RouteMap1 +# trigger: +# delay: 2 +# nopeerup_delay_options: +# cold_boot: 2 +# nsf_switchover: 10 +# post_boot: 22 +# user_initiated: 22 +# recursion: true +# redistribute_internal: true +# refresh: +# max_eor_time: 700 +# stalepath_time: 800 +# router_id: +# vrf: true +# scan_time: 22 +# slow_peer: +# detection: +# threshold: 345 +# split_update_group: +# dynamic: true +# permanent: true +# sso: true +# suppress_inactive: true +# update_delay: 2 +# update_group: true +# bmp: +# buffer_size: 22 +# server: 2 +# distance: +# bgp: +# routes_external: 2 +# routes_internal: 3 +# routes_local: 4 +# mbgp: +# routes_external: 2 +# routes_internal: 3 +# routes_local: 5 +# distributes: +# - in: true +# prefix: prefixTest +# - gateway: gatewayTest +# out: true +# - acl: '300' +# interface: Loopback0 +# out: true +# maximum_paths: +# ibgp: 2 +# paths: 2 +# maximum_secondary_paths: +# ibgp: 22 +# paths: 22 +# neighbors: +# - neighbor_address: 192.0.2.10 +# remote_as: '64500' +# update_source: Loopback1 +# - activate: true +# neighbor_address: 192.0.2.11 +# remote_as: '45000' +# send_community: +# extended: true +# - activate: true +# neighbor_address: 192.0.2.12 +# remote_as: '45000' +# - neighbor_address: 192.0.2.13 +# remote_as: '6553601' +# - advertise: +# diverse_path: +# backup: true +# neighbor_address: 192.0.2.8 +# route_reflector_client: true +# - neighbor_address: 192.0.2.9 +# remote_as: '64500' +# route_maps: +# - in: true +# name: rmp1 +# - in: true +# name: rmp2 +# update_source: Loopback0 +# - activate: true +# advertise: +# additional_paths: +# group_best: true +# neighbor_address: 2001:DB8::1037 +# - neighbor_address: testNebTag +# peer_group: '5' +# soft_reconfiguration: true +# version: 4 +# networks: +# - address: 192.0.2.15 +# backdoor: true +# netmask: 55.255.255.0 +# route_map: mp1 +# - address: 192.0.2.16 +# backdoor: true +# netmask: 255.255.255.0 +# route_map: mp2 +# - address: 192.0.2.17 +# backdoor: true +# netmask: 255.255.255.0 +# route_map: mp2 +# redistribute: +# - static: +# metric: 33 +# route_map: rmp1 +# set: true +# - application: +# metric: 22 +# name: app1 +# - application: +# metric: 33 +# name: app2 +# route_map: mp1 +# - connected: +# metric: 22 +# set: true +# - mobile: +# metric: 21 +# set: true +""" + + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: list + sample: + - "router bgp 65000" + - "neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive" + - "bgp graceful-shutdown all neighbors 50 local-preference 100 community 100" +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - "router bgp 65000" + - "neighbor 198.51.100.1 aigp send cost-community 100 poi igp-cost transitive" + - "bgp graceful-shutdown all neighbors 50 local-preference 100 community 100" +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.bgp_global.bgp_global import ( + Bgp_globalArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.bgp_global.bgp_global import ( + Bgp_global, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Bgp_globalArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Bgp_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_command.py b/ansible_collections/cisco/ios/plugins/modules/ios_command.py new file mode 100644 index 000000000..e73f8212f --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_command.py @@ -0,0 +1,431 @@ +#!/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: ios_command +author: Peter Sprygada (@privateip) +short_description: Module to run commands on remote devices. +description: + - Sends arbitrary commands to an ios node and returns the results read from the device. + This module includes an argument that will cause the module to wait for a specific + condition before returning or timing out if the condition is not met. + - This module does not support running commands in configuration mode. Please use + L(ios_config,https://docs.ansible.com/ansible/latest/collections/cisco/ios/ios_config_module.html#ansible-collections-cisco-ios-ios-config-module) + to configure IOS devices. +version_added: 1.0.0 +extends_documentation_fragment: + - cisco.ios.ios +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + commands: + description: + - List of commands to send to the remote ios device over the configured provider. + The resulting output from the command is returned. If the I(wait_for) argument + is provided, the module is not returned until the condition is satisfied or + the number of retries has expired. If a command sent to the device requires + answering a prompt, it is possible to pass a dict containing I(command), I(answer) + and I(prompt). Common answers are 'y' or "\\r" (carriage return, must be double + quotes). See examples. + required: true + type: list + elements: raw + wait_for: + description: + - List of conditions to evaluate against the output of the command. The task will + wait for each condition to be true before moving forward. If the conditional + is not true within the configured number of retries, the task fails. See examples. + aliases: + - waitfor + type: list + elements: str + match: + description: + - The I(match) argument is used in conjunction with the I(wait_for) argument to + specify the match policy. Valid values are C(all) or C(any). If the value + is set to C(all) then all conditionals in the wait_for must be satisfied. If + the value is set to C(any) then only one of the values must be satisfied. + default: all + type: str + choices: + - any + - all + retries: + description: + - Specifies the number of retries a command should by tried before it is considered + failed. The command is run on the target device every retry and evaluated against + the I(wait_for) conditions. + default: 9 + type: int + interval: + description: + - Configures the interval in seconds to wait between retries of the command. If + the command does not pass the specified conditions, the interval indicates how + long to wait before trying the command again. + default: 1 + type: int +""" + +EXAMPLES = r""" +- name: Run show version on remote devices + cisco.ios.ios_command: + commands: show version' + +# output- + +# ok: [iosxeappliance] => { +# "changed": false, +# "invocation": { +# "module_args": { +# "commands": [ +# "show version" +# ], +# "interval": 1, +# "match": "all", +# "retries": 10, +# "wait_for": null +# } +# }, +# "stdout": [ +# "Cisco IOS XE Software, Version 17.03.04a\nCisco IOS Software [Amsterdam], Virtual XE Software ... register is 0x2102" +# ], +# "stdout_lines": [ +# [ +# "Cisco IOS XE Software, Version 17.03.04a", +# "Cisco IOS Software [Amsterdam], Virtual XE Software", +# "..." +# "Configuration register is 0x2102" +# ] +# ] +# } + +- name: Run show version and check to see if output contains IOS + cisco.ios.ios_command: + commands: show version + wait_for: result[0] contains IOS + +# output- + +# ok: [iosxeappliance] => { +# "changed": false, +# "invocation": { +# "module_args": { +# "commands": [ +# "show version" +# ], +# "interval": 1, +# "match": "all", +# "retries": 10, +# "wait_for": [ +# "result[0] contains IOS" +# ] +# } +# }, +# "stdout": [ +# "Cisco IOS XE Software, Version 17.03.04a\nCisco IOS Software [Amsterdam], Virtual XE Software ... register is 0x2102" +# ], +# "stdout_lines": [ +# [ +# "Cisco IOS XE Software, Version 17.03.04a", +# "Cisco IOS Software [Amsterdam], Virtual XE Software", +# "..." +# "Configuration register is 0x2102" +# ] +# ] +# } + +- name: Run multiple commands on remote nodes + cisco.ios.ios_command: + commands: + - show version + - show interfaces + +# output- + +# ok: [iosxeappliance] => { +# "changed": false, +# "invocation": { +# "module_args": { +# "commands": [ +# "show version", +# "show interfaces" +# ], +# "interval": 1, +# "match": "all", +# "retries": 10, +# "wait_for": null +# } +# }, +# "stdout": [ +# "Cisco IOS XE Software, Version 17.03.04a\nCisco IOS Software [Amsterdam], Virtual XE Software Configuration register is 0x2102", +# "Loopback999 is up, line protocol is up ...failures, 0 output buffers swapped out" +# ], +# "stdout_lines": [ +# [ +# "Cisco IOS XE Software, Version 17.03.04a", +# "Cisco IOS Software [Amsterdam], Virtual XE Software", +# "..." +# "Configuration register is 0x2102" +# ], +# [ +# "Loopback999 is up, line protocol is up ", +# " Hardware is Loopback", +# " Description: this is a test", +# " MTU 1514 bytes, BW 8000000 Kbit/sec, DLY 5000 usec, ", +# " reliability 255/255, txload 1/255, rxload 1/255", +# " Encapsulation LOOPBACK, loopback not set", +# " Keepalive set (10 sec)", +# " Last input never, output never, output hang never", +# " Last clearing of \"show interface\" counters never", +# " Input queue: 0/75/0/0 (size/max/drops/flushes); Total output drops: 0", +# " Queueing strategy: fifo", +# " Output queue: 0/0 (size/max)", +# " 5 minute input rate 0 bits/sec, 0 packets/sec", +# " 5 minute output rate 0 bits/sec, 0 packets/sec", +# " 0 packets input, 0 bytes, 0 no buffer", +# " Received 0 broadcasts (0 IP multicasts)", +# " 0 runts, 0 giants, 0 throttles ", +# " 0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored, 0 abort", +# " 0 packets output, 0 bytes, 0 underruns", +# " Output 0 broadcasts (0 IP multicasts)", +# " 0 output errors, 0 collisions, 0 interface resets", +# " 0 unknown protocol drops", +# " 0 output buffer failures, 0 output buffers swapped out" +# ] +# ] +# } + +- name: Run multiple commands and evaluate the output + cisco.ios.ios_command: + commands: + - show version + - show interfaces + wait_for: + - result[0] contains IOS + - result[1] contains Loopback0 + +# output- +# failed play as result[1] contains Loopback0 is false + +# fatal: [iosxeappliance]: FAILED! => { +# "changed": false, +# "failed_conditions": [ +# "result[1] contains Loopback0" +# ], +# "invocation": { +# "module_args": { +# "commands": [ +# "show version", +# "show interfaces" +# ], +# "interval": 1, +# "match": "all", +# "retries": 10, +# "wait_for": [ +# "result[0] contains IOS", +# "result[1] contains Loopback0" +# ] +# } +# }, +# "msg": "One or more conditional statements have not been satisfied" +# } + +- name: Run commands that require answering a prompt + cisco.ios.ios_command: + commands: + - command: "clear counters GigabitEthernet2" + prompt: 'Clear "show interface" counters on this interface \[confirm\]' + answer: "y" + - command: "clear counters GigabitEthernet3" + prompt: "[confirm]" + answer: "\r" + +# output- + +# ok: [iosxeappliance] => { +# "changed": false, +# "invocation": { +# "module_args": { +# "commands": [ +# { +# "answer": "y", +# "check_all": false, +# "command": "clear counters GigabitEthernet2", +# "newline": true, +# "output": null, +# "prompt": "Clear \"show interface\" counters on this interface \\[confirm\\]", +# "sendonly": false +# }, +# { +# "answer": "\r", +# "check_all": false, +# "command": "clear counters GigabitEthernet3", +# "newline": true, +# "output": null, +# "prompt": "[confirm]", +# "sendonly": false +# } +# ], +# "interval": 1, +# "match": "all", +# "retries": 10, +# "wait_for": null +# } +# }, +# "stdout": [ +# "Clear \"show interface\" counters on this interface [confirm]y", +# "Clear \"show interface\" counters on this interface [confirm]" +# ], +# "stdout_lines": [ +# [ +# "Clear \"show interface\" counters on this interface [confirm]y" +# ], +# [ +# "Clear \"show interface\" counters on this interface [confirm]" +# ] +# ] +# } + +- name: Run commands with complex values like special characters in variables + cisco.ios.ios_command: + commands: + ["{{ 'test aaa group TEST ' ~ user ~ ' ' ~ password ~ ' new-code' }}"] + vars: + user: "dummy" + password: "!dummy" + +# ok: [iosxeappliance] => { +# "changed": false, +# "invocation": { +# "module_args": { +# "commands": [ +# "test aaa group group test !dummy new-code" +# ], +# "interval": 1, +# "match": "all", +# "retries": 10, +# "wait_for": null +# } +# }, +# "stdout": [ +# "User was successfully authenticated." +# ], +# "stdout_lines": [ +# [ +# "User was successfully authenticated." +# ] +# ] +# } + +""" + +RETURN = """ +stdout: + description: The set of responses from the commands + returned: always apart from low level errors (such as action plugin) + type: list + sample: ['...', '...'] +stdout_lines: + description: The value of stdout split into a list + returned: always apart from low level errors (such as action plugin) + type: list + sample: [['...', '...'], ['...'], ['...']] +failed_conditions: + description: The list of conditionals that have failed + returned: failed + type: list + sample: ['...', '...'] +""" +import time + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import ( + Conditional, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + to_lines, + transform_commands, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import run_commands + + +def parse_commands(module, warnings): + commands = transform_commands(module) + if module.check_mode: + for item in list(commands): + if not item["command"].startswith("show"): + warnings.append( + "Only show commands are supported when using check mode, not executing %s" + % item["command"], + ) + commands.remove(item) + return commands + + +def main(): + """main entry point for module execution""" + argument_spec = dict( + commands=dict(type="list", elements="raw", required=True), + wait_for=dict(type="list", elements="str", aliases=["waitfor"]), + match=dict(default="all", choices=["all", "any"]), + retries=dict(default=9, type="int"), + interval=dict(default=1, type="int"), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + warnings = list() + result = {"changed": False, "warnings": warnings} + commands = parse_commands(module, warnings) + wait_for = module.params["wait_for"] or list() + conditionals = [] + try: + conditionals = [Conditional(c) for c in wait_for] + except AttributeError as exc: + module.fail_json(msg=to_text(exc)) + retries = module.params["retries"] + interval = module.params["interval"] + match = module.params["match"] + while retries >= 0: + responses = run_commands(module, commands) + for item in list(conditionals): + if item(responses): + if match == "any": + conditionals = list() + break + conditionals.remove(item) + if not conditionals: + break + time.sleep(interval) + retries -= 1 + if conditionals: + failed_conditions = [item.raw for item in conditionals] + msg = "One or more conditional statements have not been satisfied" + module.fail_json(msg=msg, failed_conditions=failed_conditions) + result.update({"stdout": responses, "stdout_lines": list(to_lines(responses))}) + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_config.py b/ansible_collections/cisco/ios/plugins/modules/ios_config.py new file mode 100644 index 000000000..d86a7dd9d --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_config.py @@ -0,0 +1,584 @@ +#!/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: ios_config +author: Peter Sprygada (@privateip) +short_description: Module to manage configuration sections. +description: + - Cisco IOS configurations use a simple block indent file syntax for segmenting configuration + into sections. This module provides an implementation for working with IOS configuration + sections in a deterministic way. +version_added: 1.0.0 +extends_documentation_fragment: + - cisco.ios.ios +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - Abbreviated commands are NOT idempotent, see + U(https://docs.ansible.com/ansible/latest/network/user_guide/faq.html#why-do-the-config-modules-always-return-changed-true-with-abbreviated-commands) + - To ensure idempotency and correct diff the configuration lines in the relevant module options should be similar to how they + appear if present in the running configuration on device including the indentation. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + lines: + description: + - The ordered set of commands that should be configured in the section. The commands + must be the exact same commands as found in the device running-config to ensure + idempotency and correct diff. Be sure to note the configuration command syntax as + some commands are automatically modified by the device config parser. + type: list + elements: str + aliases: + - commands + parents: + description: + - The ordered set of parents that uniquely identify the section or hierarchy the + commands should be checked against. If the parents argument is omitted, the + commands are checked against the set of top level or global commands. + type: list + elements: str + src: + description: + - Specifies the source path to the file that contains the configuration or configuration + template to load. The path to the source file can either be the full path on + the Ansible control host or a relative path from the playbook or role root directory. This + argument is mutually exclusive with I(lines), I(parents). The configuration lines in the + source file should be similar to how it will appear if present in the running-configuration + of the device including the indentation to ensure idempotency and correct diff. + type: str + before: + description: + - The ordered set of commands to push on to the command stack if a change needs + to be made. This allows the playbook designer the opportunity to perform configuration + commands prior to pushing any changes without affecting how the set of commands + are matched against the system. + type: list + elements: str + after: + description: + - The ordered set of commands to append to the end of the command stack if a change + needs to be made. Just like with I(before) this allows the playbook designer + to append a set of commands to be executed after the command set. + type: list + elements: str + match: + description: + - Instructs the module on the way to perform the matching of the set of commands + against the current device config. If match is set to I(line), commands are + matched line by line. If match is set to I(strict), command lines are matched + with respect to position. If match is set to I(exact), command lines must be + an equal match. Finally, if match is set to I(none), the module will not attempt + to compare the source configuration with the running configuration on the remote + device. + choices: + - line + - strict + - exact + - none + type: str + default: line + replace: + description: + - Instructs the module on the way to perform the configuration on the device. + If the replace argument is set to I(line) then the modified lines are pushed + to the device in configuration mode. If the replace argument is set to I(block) + then the entire command block is pushed to the device in configuration mode + if any line is not correct. + default: line + choices: + - line + - block + type: str + multiline_delimiter: + description: + - This argument is used when pushing a multiline configuration element to the + IOS device. It specifies the character to use as the delimiting character. This + only applies to the configuration action. + default: "@" + type: str + backup: + description: + - This argument will cause the module to create a full backup of the current C(running-config) + from the remote device before any changes are made. If the C(backup_options) + value is not given, the backup file is written to the C(backup) folder in the + playbook root directory or role root directory, if playbook is part of an ansible + role. If the directory does not exist, it is created. + type: bool + default: false + running_config: + description: + - The module, by default, will connect to the remote device and retrieve the current + running-config to use as a base for comparing against the contents of source. + There are times when it is not desirable to have the task get the current running-config + for every task in a playbook. The I(running_config) argument allows the implementer + to pass in the configuration to use as the base config for comparison. + The configuration lines for this option should be similar to how it will appear if present + in the running-configuration of the device including the indentation to ensure idempotency + and correct diff. + type: str + aliases: + - config + defaults: + description: + - This argument specifies whether or not to collect all defaults when getting + the remote device running config. When enabled, the module will get the current + config by issuing the command C(show running-config all). + type: bool + default: false + save_when: + description: + - When changes are made to the device running-configuration, the changes are not + copied to non-volatile storage by default. Using this argument will change + that before. If the argument is set to I(always), then the running-config will + always be copied to the startup-config and the I(modified) flag will always + be set to True. If the argument is set to I(modified), then the running-config + will only be copied to the startup-config if it has changed since the last save + to startup-config. If the argument is set to I(never), the running-config will + never be copied to the startup-config. If the argument is set to I(changed), + then the running-config will only be copied to the startup-config if the task + has made a change. I(changed) was added in Ansible 2.5. + default: never + choices: + - always + - never + - modified + - changed + type: str + diff_against: + description: + - When using the C(ansible-playbook --diff) command line argument the module can + generate diffs against different sources. + - When this option is configure as I(startup), the module will return the diff + of the running-config against the startup-config. + - When this option is configured as I(intended), the module will return the diff + of the running-config against the configuration provided in the C(intended_config) + argument. + - When this option is configured as I(running), the module will return the before + and after diff of the running-config with respect to any changes made to the + device configuration. + type: str + choices: + - running + - startup + - intended + diff_ignore_lines: + description: + - Use this argument to specify one or more lines that should be ignored during + the diff. This is used for lines in the configuration that are automatically + updated by the system. This argument takes a list of regular expressions or + exact line matches. + type: list + elements: str + intended_config: + description: + - The C(intended_config) provides the master configuration that the node should + conform to and is used to check the final running-config against. This argument + will not modify any settings on the remote device and is strictly used to check + the compliance of the current device's configuration against. When specifying + this argument, the task should also modify the C(diff_against) value and set + it to I(intended). The configuration lines for this value should be similar to how it + will appear if present in the running-configuration of the device including the indentation + to ensure correct diff. + type: str + backup_options: + description: + - This is a dict object containing configurable options related to backup file + path. The value of this option is read only when C(backup) is set to I(yes), + if C(backup) is set to I(no) this option will be silently ignored. + suboptions: + filename: + description: + - The filename to be used to store the backup configuration. If the filename + is not given it will be generated based on the hostname, current time and + date in format defined by <hostname>_config.<current-date>@<current-time> + type: str + dir_path: + description: + - This option provides the path ending with directory name in which the backup + configuration file will be stored. If the directory does not exist it will + be first created and the filename is either the value of C(filename) or + default filename as described in C(filename) options description. If the + path value is not given in that case a I(backup) directory will be created + in the current working directory and backup configuration will be copied + in C(filename) within I(backup) directory. + type: path + type: dict +""" + +EXAMPLES = """ +- name: Configure top level configuration + cisco.ios.ios_config: + lines: hostname {{ inventory_hostname }} + +- name: Configure interface settings + cisco.ios.ios_config: + lines: + - description test interface + - ip address 172.31.1.1 255.255.255.0 + parents: interface Ethernet1 + +- name: Configure ip helpers on multiple interfaces + cisco.ios.ios_config: + lines: + - ip helper-address 172.26.1.10 + - ip helper-address 172.26.3.8 + parents: "{{ item }}" + with_items: + - interface Ethernet1 + - interface Ethernet2 + - interface GigabitEthernet1 + +- name: Configure policer in Scavenger class + cisco.ios.ios_config: + lines: + - conform-action transmit + - exceed-action drop + parents: + - policy-map Foo + - class Scavenger + - police cir 64000 + +- name: Load new acl into device + cisco.ios.ios_config: + lines: + - 10 permit ip host 192.0.2.1 any log + - 20 permit ip host 192.0.2.2 any log + - 30 permit ip host 192.0.2.3 any log + - 40 permit ip host 192.0.2.4 any log + - 50 permit ip host 192.0.2.5 any log + parents: ip access-list extended test + before: no ip access-list extended test + match: exact + +- name: Check the running-config against master config + cisco.ios.ios_config: + diff_against: intended + intended_config: "{{ lookup('file', 'master.cfg') }}" + +- name: Check the startup-config against the running-config + cisco.ios.ios_config: + diff_against: startup + diff_ignore_lines: + - ntp clock .* + +- name: Save running to startup when modified + cisco.ios.ios_config: + save_when: modified + +- name: For idempotency, use full-form commands + cisco.ios.ios_config: + lines: + # - shut + - shutdown + # parents: int gig1/0/11 + parents: interface GigabitEthernet1/0/11 + +# Set boot image based on comparison to a group_var (version) and the version +# that is returned from the `ios_facts` module +- name: Setting boot image + cisco.ios.ios_config: + lines: + - no boot system + - boot system flash bootflash:{{new_image}} + host: "{{ inventory_hostname }}" + when: ansible_net_version != version + +- name: Render a Jinja2 template onto an IOS device + cisco.ios.ios_config: + backup: true + src: ios_template.j2 + +- name: Configurable backup path + cisco.ios.ios_config: + src: ios_template.j2 + backup: true + backup_options: + filename: backup.cfg + dir_path: /home/user + +# Example ios_template.j2 +# ip access-list extended test +# permit ip host 192.0.2.1 any log +# permit ip host 192.0.2.2 any log +# permit ip host 192.0.2.3 any log +# permit ip host 192.0.2.4 any log + +""" + +RETURN = """ +updates: + description: The set of commands that will be pushed to the remote device + returned: always + type: list + sample: ['hostname foo', 'router ospf 1', 'router-id 192.0.2.1'] +commands: + description: The set of commands that will be pushed to the remote device + returned: always + type: list + sample: ['hostname foo', 'router ospf 1', 'router-id 192.0.2.1'] +backup_path: + description: The full path to the backup file + returned: when backup is yes + type: str + sample: /playbooks/ansible/backup/ios_config.2016-07-16@22:28:34 +filename: + description: The name of the backup file + returned: when backup is yes and filename is not specified in backup options + type: str + sample: ios_config.2016-07-16@22:28:34 +shortname: + description: The full path to the backup file excluding the timestamp + returned: when backup is yes and filename is not specified in backup options + type: str + sample: /playbooks/ansible/backup/ios_config +date: + description: The date extracted from the backup file name + returned: when backup is yes + type: str + sample: "2016-07-16" +time: + description: The time extracted from the backup file name + returned: when backup is yes + type: str + sample: "22:28:34" +""" +import json + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import ConnectionError +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( + NetworkConfig, + dumps, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + get_connection, + get_defaults_flag, + run_commands, +) + + +def check_args(module, warnings): + if module.params["multiline_delimiter"]: + if len(module.params["multiline_delimiter"]) != 1: + module.fail_json(msg="multiline_delimiter value can only be a single character") + + +def edit_config_or_macro(connection, commands): + # only catch the macro configuration command, + # not negated 'no' variation. + if commands[0].startswith("macro name"): + connection.edit_macro(candidate=commands) + else: + connection.edit_config(candidate=commands) + + +def get_candidate_config(module): + candidate = "" + if module.params["src"]: + candidate = module.params["src"] + elif module.params["lines"]: + candidate_obj = NetworkConfig(indent=1) + parents = module.params["parents"] or list() + candidate_obj.add(module.params["lines"], parents=parents) + candidate = dumps(candidate_obj, "raw") + return candidate + + +def get_running_config(module, current_config=None, flags=None): + running = module.params["running_config"] + if not running: + if not module.params["defaults"] and current_config: + running = current_config + else: + running = get_config(module, flags=flags) + return running + + +def save_config(module, result): + result["changed"] = True + if not module.check_mode: + run_commands(module, "copy running-config startup-config\r") + else: + module.warn( + "Skipping command `copy running-config startup-config` due to check_mode. Configuration not copied to non-volatile storage", + ) + + +def main(): + """main entry point for module execution""" + backup_spec = dict(filename=dict(), dir_path=dict(type="path")) + argument_spec = dict( + src=dict(type="str"), + lines=dict(aliases=["commands"], type="list", elements="str"), + parents=dict(type="list", elements="str"), + before=dict(type="list", elements="str"), + after=dict(type="list", elements="str"), + match=dict(default="line", choices=["line", "strict", "exact", "none"]), + replace=dict(default="line", choices=["line", "block"]), + multiline_delimiter=dict(default="@"), + running_config=dict(aliases=["config"]), + intended_config=dict(), + defaults=dict(type="bool", default=False), + backup=dict(type="bool", default=False), + backup_options=dict(type="dict", options=backup_spec), + save_when=dict(choices=["always", "never", "modified", "changed"], default="never"), + diff_against=dict(choices=["startup", "intended", "running"]), + diff_ignore_lines=dict(type="list", elements="str"), + ) + mutually_exclusive = [("lines", "src"), ("parents", "src")] + required_if = [ + ("match", "strict", ["lines"]), + ("match", "exact", ["lines"]), + ("replace", "block", ["lines"]), + ("diff_against", "intended", ["intended_config"]), + ] + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=mutually_exclusive, + required_if=required_if, + supports_check_mode=True, + ) + result = {"changed": False} + warnings = list() + check_args(module, warnings) + result["warnings"] = warnings + diff_ignore_lines = module.params["diff_ignore_lines"] + config = None + contents = None + flags = get_defaults_flag(module) if module.params["defaults"] else [] + connection = get_connection(module) + if module.params["backup"] or module._diff and module.params["diff_against"] == "running": + contents = get_config(module, flags=flags) + config = NetworkConfig(indent=1, contents=contents) + if module.params["backup"]: + result["__backup__"] = contents + if any((module.params["lines"], module.params["src"])): + match = module.params["match"] + replace = module.params["replace"] + path = module.params["parents"] + candidate = get_candidate_config(module) + running = get_running_config(module, contents, flags=flags) + try: + response = connection.get_diff( + candidate=candidate, + running=running, + diff_match=match, + diff_ignore_lines=diff_ignore_lines, + path=path, + diff_replace=replace, + ) + except ConnectionError as exc: + module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) + config_diff = response["config_diff"] + banner_diff = response["banner_diff"] + if config_diff or banner_diff: + commands = config_diff.split("\n") + if module.params["before"]: + commands[:0] = module.params["before"] + if module.params["after"]: + commands.extend(module.params["after"]) + result["commands"] = commands + result["updates"] = commands + result["banners"] = banner_diff + + # send the configuration commands to the device and merge + # them with the current running config + if not module.check_mode: + if commands: + edit_config_or_macro(connection, commands) + if banner_diff: + connection.edit_banner( + candidate=json.dumps(banner_diff), + multiline_delimiter=module.params["multiline_delimiter"], + ) + result["changed"] = True + running_config = module.params["running_config"] + startup_config = None + if module.params["save_when"] == "always": + save_config(module, result) + elif module.params["save_when"] == "modified": + output = run_commands(module, ["show running-config", "show startup-config"]) + running_config = NetworkConfig(indent=1, contents=output[0], ignore_lines=diff_ignore_lines) + startup_config = NetworkConfig(indent=1, contents=output[1], ignore_lines=diff_ignore_lines) + if running_config.sha1 != startup_config.sha1: + save_config(module, result) + elif module.params["save_when"] == "changed" and result["changed"]: + save_config(module, result) + if module._diff: + if not running_config: + output = run_commands(module, "show running-config") + contents = output[0] + else: + contents = running_config + + # recreate the object in order to process diff_ignore_lines + running_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines) + if module.params["diff_against"] == "running": + if module.check_mode: + module.warn("unable to perform diff against running-config due to check mode") + contents = None + else: + contents = config.config_text + elif module.params["diff_against"] == "startup": + if not startup_config: + output = run_commands(module, "show startup-config") + contents = output[0] + else: + contents = startup_config.config_text + elif module.params["diff_against"] == "intended": + contents = module.params["intended_config"] + if contents is not None: + base_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines) + if running_config.sha1 != base_config.sha1: + before, after = "", "" + if module.params["diff_against"] == "intended": + before = running_config + after = base_config + elif module.params["diff_against"] in ("startup", "running"): + before = base_config + after = running_config + result.update( + {"changed": True, "diff": {"before": str(before), "after": str(after)}}, + ) + + if result.get("changed") and any((module.params["src"], module.params["lines"])): + msg = ( + "To ensure idempotency and correct diff the input configuration lines should be" + " similar to how they appear if present in" + " the running configuration on device" + ) + if module.params["src"]: + msg += " including the indentation" + if "warnings" in result: + result["warnings"].append(msg) + else: + result["warnings"] = msg + + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_facts.py b/ansible_collections/cisco/ios/plugins/modules/ios_facts.py new file mode 100644 index 000000000..fd4c786f5 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_facts.py @@ -0,0 +1,246 @@ +#!/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: ios_facts +author: + - Peter Sprygada (@privateip) + - Sumit Jaiswal (@justjais) +short_description: Module to collect facts from remote devices. +description: + - Collects a base set of device facts from a remote device that is running IOS. This + module prepends all of the base network fact keys with C(ansible_net_<fact>). The + facts module will always collect a base set of facts from the device and can enable + or disable collection of additional facts. +version_added: 1.0.0 +extends_documentation_fragment: + - cisco.ios.ios +notes: + - Tested against Cisco IOSXE Version 17.3 on CML and IOS 15.6 for L2 specific resource. + - Facts gathering for L3 devices are supposed to produce blank output for unsupported + resources like vlan. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + gather_subset: + description: + - When supplied, this argument restricts the facts collected to a given subset. + - Possible values for this argument include C(all), C(min), C(default), C(hardware), C(config), + and C(interfaces). + - Specify a list of values to include a larger subset. + - Use a value with an initial C(!) to collect all facts except that subset. + required: false + default: "min" + type: list + elements: str + gather_network_resources: + description: + - When supplied, this argument will restrict the facts collected to a given subset. + Possible values for this argument include all and the resources like interfaces, + vlans etc. Can specify a list of values to include a larger subset. Values can + also be used with an initial C(!) to specify that a specific subset should + not be collected. Valid subsets are 'bgp_global', 'l3_interfaces', 'lag_interfaces', + 'ntp_global', 'acls', 'hostname', 'interfaces', 'lldp_interfaces', 'logging_global', + 'ospf_interfaces', 'ospfv2', 'prefix_lists', 'static_routes', 'acl_interfaces', + 'all', 'bgp_address_family', 'l2_interfaces', 'lacp', 'lacp_interfaces', 'lldp_global', + 'ospfv3', 'snmp_server', 'vlans', 'service'. + type: list + elements: str + available_network_resources: + description: When 'True' a list of network resources for which resource modules are available will be provided. + type: bool + default: false +""" + +EXAMPLES = """ +- name: Gather all legacy facts + cisco.ios.ios_facts: + gather_subset: all + +- name: Gather only the config and default facts + cisco.ios.ios_facts: + gather_subset: + - config + +- name: Do not gather hardware facts + cisco.ios.ios_facts: + gather_subset: + - "!hardware" + +- name: Gather legacy and resource facts + cisco.ios.ios_facts: + gather_subset: all + gather_network_resources: all + +- name: Gather only the interfaces resource facts and no legacy facts + cisco.ios.ios_facts: + gather_subset: + - "!all" + - "!min" + gather_network_resources: + - interfaces + +- name: Gather interfaces resource and minimal legacy facts + cisco.ios.ios_facts: + gather_subset: min + gather_network_resources: interfaces + +- name: Gather L2 interfaces resource and minimal legacy facts + cisco.ios.ios_facts: + gather_subset: min + gather_network_resources: l2_interfaces + +- name: Gather L3 interfaces resource and minimal legacy facts + cisco.ios.ios_facts: + gather_subset: min + gather_network_resources: l3_interfaces +""" + +RETURN = """ +ansible_net_gather_subset: + description: The list of fact subsets collected from the device + returned: always + type: list + +ansible_net_gather_network_resources: + description: The list of fact for network resource subsets collected from the device + returned: when the resource is configured + type: list + +# default +ansible_net_model: + description: The model name returned from the device + returned: always + type: str +ansible_net_serialnum: + description: The serial number of the remote device + returned: always + type: str +ansible_net_version: + description: The operating system version running on the remote device + returned: always + type: str +ansible_net_iostype: + description: The operating system type (IOS or IOS-XE) running on the remote device + returned: always + type: str +ansible_net_hostname: + description: The configured hostname of the device + returned: always + type: str +ansible_net_image: + description: The image file the device is running + returned: always + type: str +ansible_net_stacked_models: + description: The model names of each device in the stack + returned: when multiple devices are configured in a stack + type: list +ansible_net_stacked_serialnums: + description: The serial numbers of each device in the stack + returned: when multiple devices are configured in a stack + type: list +ansible_net_api: + description: The name of the transport + returned: always + type: str +ansible_net_python_version: + description: The Python version Ansible controller is using + returned: always + type: str + +# hardware +ansible_net_filesystems: + description: All file system names available on the device + returned: when hardware is configured + type: list +ansible_net_filesystems_info: + description: A hash of all file systems containing info about each file system (e.g. free and total space) + returned: when hardware is configured + type: dict +ansible_net_memfree_mb: + description: The available free memory on the remote device in Mb + returned: when hardware is configured + type: int +ansible_net_memtotal_mb: + description: The total memory on the remote device in Mb + returned: when hardware is configured + type: int + +# config +ansible_net_config: + description: The current active config from the device + returned: when config is configured + type: str + +# interfaces +ansible_net_all_ipv4_addresses: + description: All IPv4 addresses configured on the device + returned: when interfaces is configured + type: list +ansible_net_all_ipv6_addresses: + description: All IPv6 addresses configured on the device + returned: when interfaces is configured + type: list +ansible_net_interfaces: + description: A hash of all interfaces running on the system + returned: when interfaces is configured + type: dict +ansible_net_neighbors: + description: + - The list of CDP and LLDP neighbors from the remote device. If both, + CDP and LLDP neighbor data is present on one port, CDP is preferred. + returned: when interfaces is configured + type: dict +""" +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.facts.facts import ( + FactsArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import ( + FACT_RESOURCE_SUBSETS, + Facts, +) + + +def main(): + """ + Main entry point for module execution + + :returns: ansible_facts + """ + argument_spec = FactsArgs.argument_spec + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + warnings = [] + + ansible_facts = {} + if module.params.get("available_network_resources"): + ansible_facts["available_network_resources"] = sorted(FACT_RESOURCE_SUBSETS.keys()) + result = Facts(module).get_facts() + additional_facts, additional_warnings = result + ansible_facts.update(additional_facts) + warnings.extend(additional_warnings) + module.exit_json(ansible_facts=ansible_facts, warnings=warnings) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_hostname.py b/ansible_collections/cisco/ios/plugins/modules/ios_hostname.py new file mode 100644 index 000000000..2949fc584 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_hostname.py @@ -0,0 +1,332 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_hostname +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_hostname +short_description: Resource module to configure hostname. +description: + - This module provides declarative management of hostname on Cisco IOS devices. +version_added: 2.7.0 +author: + - Sagar Paul (@KB-perByte) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). +options: + config: + description: A dictionary of hostname options + type: dict + suboptions: + hostname: + description: set hostname for IOS + type: str + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device + by executing the command B(show running-config | section ^hostname). + - 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 + state: + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + default: merged + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The states I(merged), I(replaced) and I(overridden) have identical + behaviour for this module. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command + I(show running-config | section ^hostname) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str +""" + +EXAMPLES = """ +# Using state: merged + +# Before state: +# ------------- + +# router-ios#show running-config | section ^hostname +# hostname Router + +# Merged play: +# ------------ + +- name: Apply the provided configuration + cisco.ios.ios_hostname: + config: + hostname: Router1 + state: merged + +# Commands Fired: +# --------------- + +# "commands": [ +# "hostname Router1", +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section ^hostname +# hostname Router1 + +# Using state: deleted + +# Before state: +# ------------- + +# router-ios#show running-config | section ^hostname +# hostname RouterTest + +# Deleted play: +# ------------- + +- name: Remove all existing configuration + cisco.ios.ios_hostname: + state: deleted + +# Commands Fired: +# --------------- + +# "commands": [ +# "no hostname RouterTest", +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section ^hostname +# hostname Router + +# Using state: overridden + +# Before state: +# ------------- + +# router-ios#show running-config | section ^hostname +# hostname Router + +# Overridden play: +# ---------------- + +- name: Override commands with provided configuration + cisco.ios.ios_hostname: + config: + hostname: RouterTest + state: overridden + +# Commands Fired: +# --------------- +# "commands": [ +# "hostname RouterTest", +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section ^hostname +# hostname RouterTest + +# Using state: replaced + +# Before state: +# ------------- + +# router-ios#show running-config | section ^hostname +# hostname RouterTest + +# Replaced play: +# -------------- + +- name: Replace commands with provided configuration + cisco.ios.ios_hostname: + config: + hostname: RouterTest + state: replaced + +# Commands Fired: +# --------------- + +# "commands": [], + +# After state: +# ------------ + +# router-ios#show running-config | section ^hostname +# hostname RouterTest + +# Using state: gathered + +# Before state: +# ------------- + +#router-ios#show running-config | section ^hostname +# hostname RouterTest + +# Gathered play: +# -------------- + +- name: Gather listed hostname config + cisco.ios.ios_hostname: + state: gathered + +# Module Execution Result: +# ------------------------ + +# "gathered": { +# "hostname": "RouterTest" +# }, + +# Using state: rendered + +# Rendered play: +# -------------- + +- name: Render the commands for provided configuration + cisco.ios.ios_hostname: + config: + hostname: RouterTest + state: rendered + +# Module Execution Result: +# ------------------------ + +# "rendered": [ +# "hostname RouterTest", +# ] + +# Using state: parsed + +# File: parsed.cfg +# ---------------- + +# hostname RouterTest + +# Parsed play: +# ------------ + +- name: Parse the provided configuration with the existing running configuration + cisco.ios.ios_hostname: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ + +# "parsed": { +# "hostname": "RouterTest" +# } +""" + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: list + sample: + - hostname Router1 +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - hostname Switch1 +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.hostname.hostname import ( + HostnameArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.hostname.hostname import ( + Hostname, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=HostnameArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Hostname(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_interfaces.py b/ansible_collections/cisco/ios/plugins/modules/ios_interfaces.py new file mode 100644 index 000000000..b87cc1408 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_interfaces.py @@ -0,0 +1,1009 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_interfaces +short_description: Resource module to configure interfaces. +description: This module manages the interface attributes of Cisco IOS network devices. +version_added: 1.0.0 +author: + - Sumit Jaiswal (@justjais) + - Sagar Paul (@KB-perByte) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) + - The module examples uses callback plugin (stdout_callback = yaml) to generate task + output in yaml format. +options: + config: + description: A dictionary of interface options + type: list + elements: dict + suboptions: + name: + description: + - Full name of interface, e.g. GigabitEthernet0/2, loopback999. + type: str + required: true + description: + description: + - Interface description. + type: str + enabled: + description: + - Administrative state of the interface. + - Set the value to C(true) to administratively enable the interface or C(false) + to disable it. + type: bool + default: true + speed: + description: + - Interface link speed. Applicable for Ethernet interfaces only. + type: str + mtu: + description: + - MTU for a specific interface. Applicable for Ethernet interfaces only. + - Refer to vendor documentation for valid values. + type: int + mode: + description: + - Manage Layer2 or Layer3 state of the interface. + - For a Layer 2 appliance mode Layer2 adds switchport command ( default impacts idempotency). + - For a Layer 2 appliance mode Layer3 adds no switchport command. + - For a Layer 3 appliance mode Layer3/2 has no impact rather command fails on apply. + choices: + - layer2 + - layer3 + type: str + duplex: + description: + - Interface link status. Applicable for Ethernet interfaces only, either in + half duplex, full duplex or in automatic state which negotiates the duplex + automatically. + type: str + choices: + - full + - half + - auto + template: + description: + - IOS template name. + type: str + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS 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 + state: + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - purged + - parsed + default: merged + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + - The state I(purged) negates virtual/logical interfaces that are specified in task + from running-config. + type: str +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# Router#sh running-config | section interface +# interface Loopback888 +# no ip address +# interface Loopback999 +# no ip address +# interface GigabitEthernet1 +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# description Configured and Merged by Ansible Network +# ip address dhcp +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# no ip address +# speed 1000 +# no negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: Merge provided configuration with device configuration + cisco.ios.ios_interfaces: + config: + - name: GigabitEthernet2 + description: Configured and Merged by Ansible Network + enabled: true + - name: GigabitEthernet3 + description: Configured and Merged by Ansible Network + mtu: 3800 + enabled: false + speed: 100 + duplex: full + state: merged + +# Task Output +# ----------- +# +# before: +# - enabled: true +# name: GigabitEthernet1 +# - description: Configured and Merged by Ansible Network +# enabled: true +# name: GigabitEthernet2 +# speed: '1000' +# - description: Configured and Merged by Ansible Network +# enabled: false +# mtu: 3800 +# name: GigabitEthernet3 +# speed: '1000' +# - enabled: false +# name: GigabitEthernet4 +# - enabled: true +# name: Loopback888 +# - enabled: true +# name: Loopback999 +# commands: +# - interface GigabitEthernet3 +# - description Configured and Merged by Ansible Network +# - speed 100 +# - mtu 3800 +# - duplex full +# - shutdown +# after: +# - enabled: true +# name: GigabitEthernet1 +# - description: Configured and Merged by Ansible Network +# enabled: true +# name: GigabitEthernet2 +# speed: '1000' +# - description: Configured and Merged by Ansible Network +# enabled: true +# mtu: 2800 +# name: GigabitEthernet3 +# speed: '1000' +# - enabled: false +# name: GigabitEthernet4 +# - enabled: true +# name: Loopback888 +# - enabled: true +# name: Loopback999 + +# After state: +# ------------ +# +# Router#show running-config | section ^interface +# interface Loopback888 +# no ip address +# interface Loopback999 +# no ip address +# interface GigabitEthernet1 +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# description Configured and Merged by Ansible Network +# ip address dhcp +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Merged by Ansible Network +# mtu 3800 +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +# Using merged - with mode attribute + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface GigabitEthernet1 +# description Configured by Ansible +# interface GigabitEthernet2 +# description This is test +# interface GigabitEthernet3 +# description This is test +# no switchport + +- name: Merge provided configuration with device configuration + cisco.ios.ios_interfaces: + config: + - name: GigabitEthernet2 + description: Configured and Merged by Ansible Network + enabled: true + mode: layer2 + - name: GigabitEthernet3 + description: Configured and Merged by Ansible Network + mode: layer3 + state: merged + +# Task Output +# ----------- +# +# before: +# - enabled: true +# name: GigabitEthernet1 +# - description: Configured and Merged by Ansible Network +# name: GigabitEthernet2 +# - description: Configured and Merged by Ansible Network +# name: GigabitEthernet3 +# commands: +# - interface GigabitEthernet2 +# - description Configured and Merged by Ansible Network +# - switchport +# - interface GigabitEthernet3 +# - description Configured and Merged by Ansible Network +# after: +# - enabled: true +# name: GigabitEthernet1 +# - description: Configured and Merged by Ansible Network +# enabled: true +# name: GigabitEthernet2 +# - description: Configured and Merged by Ansible Network +# name: GigabitEthernet3 +# mode: layer3 + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface GigabitEthernet1 +# description Configured by Ansible +# interface GigabitEthernet2 +# description Configured and Merged by Ansible Network +# interface GigabitEthernet3 +# description Configured and Merged by Ansible Network +# no switchport + +# Using replaced + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback888 +# no ip address +# interface Loopback999 +# no ip address +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address dhcp +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# no ip address +# speed 1000 +# no negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto +# interface Vlan50 +# ip address dhcp hostname testHostname + +- name: Replaces device configuration of listed interfaces with provided configuration + cisco.ios.ios_interfaces: + config: + - name: GigabitEthernet3 + description: Configured and Replaced by Ansible Network + enabled: false + speed: 1000 + state: replaced + +# Task Output +# ----------- +# +# before: +# - description: Management interface do not change +# enabled: true +# name: GigabitEthernet1 +# - enabled: true +# name: GigabitEthernet2 +# speed: '1000' +# - enabled: true +# name: GigabitEthernet3 +# speed: '1000' +# - enabled: false +# name: GigabitEthernet4 +# - enabled: true +# name: Loopback888 +# - enabled: true +# name: Loopback999 +# - enabled: true +# name: Vlan50 +# commands: +# - interface GigabitEthernet3 +# - description Configured and Replaced by Ansible Network +# - shutdown +# after: +# - description: Management interface do not change +# enabled: true +# name: GigabitEthernet1 +# - enabled: true +# name: GigabitEthernet2 +# speed: '1000' +# - description: Configured and Replaced by Ansible Network +# enabled: false +# name: GigabitEthernet3 +# speed: '1000' +# - enabled: false +# name: GigabitEthernet4 +# - enabled: true +# name: Loopback888 +# - enabled: true +# name: Loopback999 +# - enabled: true +# name: Vlan50 + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback888 +# no ip address +# interface Loopback999 +# no ip address +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address dhcp +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Replaced by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto +# interface Vlan50 +# ip address dhcp hostname testHostname + +# Using overridden + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback888 +# no ip address +# interface Loopback999 +# no ip address +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address dhcp +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Replaced by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto +# interface Vlan50 +# ip address dhcp hostname testHostname + +- name: Override device configuration of all interfaces with provided configuration + cisco.ios.ios_interfaces: + config: + - description: Management interface do not change + enabled: true + name: GigabitEthernet1 + - name: GigabitEthernet2 + description: Configured and Overridden by Ansible Network + speed: 10000 + - name: GigabitEthernet3 + description: Configured and Overridden by Ansible Network + enabled: false + state: overridden + +# Task Output +# ----------- +# +# before: +# - description: Management interface do not change +# enabled: true +# name: GigabitEthernet1 +# - enabled: true +# name: GigabitEthernet2 +# speed: '1000' +# - description: Configured and Replaced by Ansible Network +# enabled: false +# name: GigabitEthernet3 +# speed: '1000' +# - enabled: false +# name: GigabitEthernet4 +# - enabled: true +# name: Loopback888 +# - enabled: true +# name: Loopback999 +# - enabled: true +# name: Vlan50 +# commands: +# - interface loopback888 +# - shutdown +# - interface loopback999 +# - shutdown +# - interface Vlan50 +# - shutdown +# - interface GigabitEthernet2 +# - description Configured and Overridden by Ansible Network +# - speed 10000 +# - interface GigabitEthernet3 +# - description Configured and Overridden by Ansible Network +# - no speed 1000 +# after: +# - description: Management interface do not change +# enabled: true +# name: GigabitEthernet1 +# - description: Configured and Overridden by Ansible Network +# enabled: true +# name: GigabitEthernet2 +# speed: '10000' +# - description: Configured and Overridden by Ansible Network +# enabled: false +# name: GigabitEthernet3 +# speed: '1000' +# - enabled: false +# name: GigabitEthernet4 +# - enabled: false +# name: Loopback888 +# - enabled: false +# name: Loopback999 +# - enabled: false +# name: Vlan50 + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback888 +# no ip address +# shutdown +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# description Configured and Overridden by Ansible Network +# ip address dhcp +# speed 10000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto +# interface Vlan50 +# ip address dhcp hostname testHostname +# shutdown + +# Using Deleted + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback888 +# no ip address +# shutdown +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# description Configured and Overridden by Ansible Network +# ip address dhcp +# speed 10000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto +# interface Vlan50 +# ip address dhcp hostname testHostname +# shutdown + +- name: "Delete interface attributes (Note: This won't delete the interface itself)" + cisco.ios.ios_interfaces: + config: + - name: GigabitEthernet2 + state: deleted + +# Task Output +# ----------- +# +# before: +# - description: Management interface do not change +# enabled: true +# name: GigabitEthernet1 +# - description: Configured and Overridden by Ansible Network +# enabled: true +# name: GigabitEthernet2 +# speed: '10000' +# - description: Configured and Overridden by Ansible Network +# enabled: false +# name: GigabitEthernet3 +# speed: '1000' +# - enabled: false +# name: GigabitEthernet4 +# - enabled: false +# name: Loopback888 +# - enabled: false +# name: Loopback999 +# - enabled: false +# name: Vlan50 +# commands: +# - interface GigabitEthernet2 +# - no description Configured and Overridden by Ansible Network +# - no speed 10000 +# - shutdown +# after: +# - description: Management interface do not change +# enabled: true +# name: GigabitEthernet1 +# - enabled: false +# name: GigabitEthernet2 +# speed: '1000' +# - description: Configured and Overridden by Ansible Network +# enabled: false +# name: GigabitEthernet3 +# speed: '1000' +# - enabled: false +# name: GigabitEthernet4 +# - enabled: false +# name: Loopback888 +# - enabled: false +# name: Loopback999 +# - enabled: false +# name: Vlan50 + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback888 +# no ip address +# shutdown +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address dhcp +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto +# interface Vlan50 +# ip address dhcp hostname testHostname +# shutdown + +# Using Purged + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback888 +# no ip address +# shutdown +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address dhcp +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto +# interface Vlan50 +# ip address dhcp hostname testHostname +# shutdown + +- name: "Purge given interfaces (Note: This will delete the interface itself)" + cisco.ios.ios_interfaces: + config: + - name: Loopback888 + - name: Vlan50 + state: purged + +# Task Output +# ----------- +# +# before: +# - description: Management interface do not change +# enabled: true +# name: GigabitEthernet1 +# - enabled: false +# name: GigabitEthernet2 +# speed: '1000' +# - description: Configured and Overridden by Ansible Network +# enabled: false +# name: GigabitEthernet3 +# speed: '1000' +# - enabled: false +# name: GigabitEthernet4 +# - enabled: false +# name: Loopback888 +# - enabled: false +# name: Loopback999 +# - enabled: false +# name: Vlan50 +# commands: +# - no interface loopback888 +# - no interface Vlan50 +# after: +# - description: Management interface do not change +# enabled: true +# name: GigabitEthernet1 +# - enabled: false +# name: GigabitEthernet2 +# speed: '1000' +# - description: Configured and Overridden by Ansible Network +# enabled: false +# name: GigabitEthernet3 +# speed: '1000' +# - enabled: false +# name: GigabitEthernet4 +# - enabled: false +# name: Loopback999 + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address dhcp +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +# Using gathered + +# Before state: +# ------------- +# +# vios#sh running-config | section ^interface +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address dhcp +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: Gather facts of interfaces + cisco.ios.ios_interfaces: + config: + state: gathered + +# Task Output +# ----------- +# +# gathered: +# - description: Management interface do not change +# enabled: true +# name: GigabitEthernet1 +# - enabled: false +# name: GigabitEthernet2 +# speed: '1000' +# - description: Configured and Overridden by Ansible Network +# enabled: false +# name: GigabitEthernet3 +# speed: '1000' +# - enabled: false +# name: GigabitEthernet4 +# - enabled: false +# name: Loopback999 + +# Using rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_interfaces: + config: + - name: GigabitEthernet1 + description: Configured by Ansible-Network + mtu: 110 + enabled: true + duplex: half + - name: GigabitEthernet2 + description: Configured by Ansible-Network + mtu: 2800 + enabled: false + speed: 100 + duplex: full + state: rendered + +# Task Output +# ----------- +# +# rendered: +# - interface GigabitEthernet1 +# - description Configured by Ansible-Network +# - mtu 110 +# - duplex half +# - no shutdown +# - interface GigabitEthernet2 +# - description Configured by Ansible-Network +# - speed 100 +# - mtu 2800 +# - duplex full +# - shutdown + +# Using parsed + +# File: parsed.cfg +# ---------------- +# +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address dhcp +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: Parse the provided configuration + cisco.ios.ios_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Task Output +# ----------- +# +# parsed: +# - description: Management interface do not change +# enabled: true +# name: GigabitEthernet1 +# - enabled: false +# name: GigabitEthernet2 +# speed: '1000' +# - description: Configured and Overridden by Ansible Network +# enabled: false +# name: GigabitEthernet3 +# speed: '1000' +# - enabled: false +# name: GigabitEthernet4 +# - enabled: false +# name: Loopback999 +""" + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: list + sample: + - interface GigabitEthernet2 + - speed 1200 + - mtu 1800 +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - interface GigabitEthernet1 + - description Interface description + - shutdown +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. + +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.interfaces.interfaces import ( + InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.interfaces.interfaces import ( + Interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=InterfacesArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "purged", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_l2_interfaces.py b/ansible_collections/cisco/ios/plugins/modules/ios_l2_interfaces.py new file mode 100644 index 000000000..16c9ff468 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_l2_interfaces.py @@ -0,0 +1,754 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_l2_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_l2_interfaces +short_description: Resource module to configure L2 interfaces. +description: + This module provides declarative management of Layer-2 interface on Cisco + IOS devices. +version_added: 1.0.0 +author: + - Sagar Paul (@KB-petByte) + - Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSv Version 15.2 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) + - The module examples uses callback plugin (stdout_callback = yaml) to generate task + output in yaml format. +options: + config: + description: A dictionary of Layer-2 interface options + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface excluding any logical unit + number, i.e GigabitEthernet0/1. + type: str + required: true + access: + description: + - Switchport mode access command to configure the interface as a layer 2 access. + type: dict + suboptions: + vlan: + description: + - Configure given VLAN in access port. It's used as the access VLAN ID. + type: int + vlan_name: + description: + - Set VLAN when interface is in access mode. + type: str + voice: + description: + - Switchport mode voice command to configure the interface with a voice vlan. + type: dict + suboptions: + vlan: + description: + - Configure given voice VLAN on access port. It's used as the voice VLAN + ID. + type: int + vlan_tag: + description: + - Set VLAN Tag. + dot1p (Priority tagged on PVID) + none (Don't tell telephone about voice vlan) + untagged (Untagged on PVID) + choices: + - dot1p + - none + - untagged + type: str + vlan_name: + description: + - Set VLAN when interface is in access mode. + type: str + trunk: + description: + - Switchport mode trunk command to configure the interface as a Layer 2 trunk. + Note The encapsulation is always set to dot1q. + type: dict + suboptions: + allowed_vlans: + description: + - List of allowed VLANs in a given trunk port. These are the only VLANs + that will be configured on the trunk. + type: list + elements: str + native_vlan: + description: + - Native VLAN to be configured in trunk port. It's used as the trunk native + VLAN ID. + type: int + encapsulation: + description: + - Trunking encapsulation when interface is in trunking mode. + choices: + - dot1q + - isl + - negotiate + type: str + pruning_vlans: + description: + - Pruning VLAN to be configured in trunk port. It's used as the trunk + pruning VLAN ID. + type: list + elements: str + mode: + description: + - Mode in which interface needs to be configured. + - An interface whose trunk encapsulation is "Auto" can not be configured to + "trunk" mode. + type: str + choices: + - access + - trunk + - dot1q_tunnel + - dynamic + - dynamic_auto + - dynamic_desirable + - private_vlan_host + - private_vlan_promiscuous + - private_vlan_trunk + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS 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 + state: + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + default: merged + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and + transform it into structured data in the format as per the resource module + argspec and the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of + C(running_config) option should be the same format as the output of + command I(show running-config | include ip route|ipv6 route) executed on device. + For state I(parsed) active connection to remote host is not required. + type: str +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# media-type rj45 +# negotiation auto + +- name: Merge provided configuration with device configuration + cisco.ios.ios_l2_interfaces: + config: + - name: GigabitEthernet0/1 + mode: access + access: + vlan: 10 + voice: + vlan: 40 + - name: GigabitEthernet0/2 + mode: trunk + trunk: + allowed_vlans: 10-20,40 + native_vlan: 20 + pruning_vlans: 10,20 + encapsulation: dot1q + state: merged + +# Task Output +# ----------- +# +# before: +# - name: GigabitEthernet0/1 +# - access: +# vlan: 20 +# name: GigabitEthernet0/2 +# commands: +# - interface GigabitEthernet0/1 +# - switchport access vlan 10 +# - switchport voice vlan 40 +# - switchport mode access +# - interface GigabitEthernet0/2 +# - switchport mode trunk +# - switchport trunk encapsulation dot1q +# - switchport trunk native vlan 20 +# - switchport trunk allowed vlan 10-20,40 +# - switchport trunk pruning vlan 10,20 +# after: +# - access: +# vlan: 10 +# mode: access +# name: GigabitEthernet0/1 +# voice: +# vlan: 40 +# - mode: trunk +# name: GigabitEthernet0/2 +# trunk: +# allowed_vlans: +# - 10-20 +# - '40' +# encapsulation: dot1q +# native_vlan: 20 +# pruning_vlans: +# - '10' +# - '20' + +# After state: +# ------------ +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 10 +# switchport voice vlan 40 +# switchport mode access +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport trunk allowed vlan 10-20,40 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 20 +# switchport trunk pruning vlan 10,20 +# switchport mode trunk +# media-type rj45 +# negotiation auto + +# Using replaced + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# media-type rj45 +# negotiation auto + +- name: Replaces device configuration with provided configuration + cisco.ios.ios_l2_interfaces: + config: + - name: GigabitEthernet0/2 + trunk: + allowed_vlans: 20-25,40 + native_vlan: 20 + pruning_vlans: 10 + encapsulation: isl + state: replaced + +# Task Output +# ----------- +# +# before: +# - name: GigabitEthernet0/1 +# - access: +# vlan: 20 +# name: GigabitEthernet0/2 +# commands: +# - interface GigabitEthernet0/2 +# - no switchport access vlan +# - switchport trunk encapsulation isl +# - switchport trunk native vlan 20 +# - switchport trunk allowed vlan 20-25,40 +# - switchport trunk pruning vlan 10 +# after: +# - access: +# vlan: 20 +# name: GigabitEthernet0/1 +# - name: GigabitEthernet0/2 +# trunk: +# allowed_vlans: +# - 20-25 +# - '40' +# encapsulation: isl +# native_vlan: 20 +# pruning_vlans: +# - '10' + +# After state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport trunk allowed vlan 20-25,40 +# switchport trunk encapsulation isl +# switchport trunk native vlan 20 +# switchport trunk pruning vlan 10 +# media-type rj45 +# negotiation auto + +# Using overridden + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 20 +# media-type rj45 +# negotiation auto + +- name: Override device configuration of all l2 interfaces with provided configuration + cisco.ios.ios_l2_interfaces: + config: + - name: GigabitEthernet0/2 + access: + vlan: 20 + voice: + vlan: 40 + state: overridden + +# Task Output +# ----------- +# +# before: +# - name: GigabitEthernet0/1 +# trunk: +# encapsulation: dot1q +# native_vlan: 20 +# - access: +# vlan: 20 +# name: GigabitEthernet0/2 +# trunk: +# encapsulation: dot1q +# native_vlan: 20 +# commands: +# - interface GigabitEthernet0/1 +# - no switchport trunk encapsulation +# - no switchport trunk native vlan +# - interface GigabitEthernet0/2 +# - switchport voice vlan 40 +# - no switchport trunk encapsulation +# - no switchport trunk native vlan +# after: +# - name: GigabitEthernet0/1 +# - access: +# vlan: 20 +# name: GigabitEthernet0/2 +# voice: +# vlan: 40 + +# After state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport voice vlan 40 +# media-type rj45 +# negotiation auto + +# Using deleted + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport trunk allowed vlan 20-40,60,80 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 10 +# switchport trunk pruning vlan 10 +# media-type rj45 +# negotiation auto + +- name: Delete IOS L2 interfaces as in given arguments + cisco.ios.ios_l2_interfaces: + config: + - name: GigabitEthernet0/1 + state: deleted + +# Task Output +# ----------- +# +# before: +# - access: +# vlan: 20 +# name: GigabitEthernet0/1 +# - access: +# vlan: 20 +# name: GigabitEthernet0/2 +# trunk: +# allowed_vlans: +# - 20-40 +# - '60' +# - '80' +# encapsulation: dot1q +# native_vlan: 10 +# pruning_vlans: +# - '10' +# commands: +# - interface GigabitEthernet0/1 +# - no switchport access vlan +# after: +# - name: GigabitEthernet0/1 +# - access: +# vlan: 20 +# name: GigabitEthernet0/2 +# trunk: +# allowed_vlans: +# - 20-40 +# - '60' +# - '80' +# encapsulation: dot1q +# native_vlan: 10 +# pruning_vlans: +# - '10' + +# After state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport trunk allowed vlan 20-40,60,80 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 10 +# switchport trunk pruning vlan 10 +# media-type rj45 +# negotiation auto + +# Using deleted without config - delete all configuration + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport trunk allowed vlan 20-40,60,80 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 10 +# switchport trunk pruning vlan 10 +# media-type rj45 +# negotiation auto + +- name: Delete IOS L2 interfaces as in given arguments + cisco.ios.ios_l2_interfaces: + state: deleted + +# Task Output +# ----------- +# +# before: +# - access: +# vlan: 20 +# name: GigabitEthernet0/1 +# - access: +# vlan: 20 +# name: GigabitEthernet0/2 +# trunk: +# allowed_vlans: +# - 20-40 +# - '60' +# - '80' +# encapsulation: dot1q +# native_vlan: 10 +# pruning_vlans: +# - '10' +# commands: +# - interface GigabitEthernet0/1 +# - no switchport access vlan +# - interface GigabitEthernet0/2 +# - no switchport access vlan +# - no switchport trunk encapsulation +# - no switchport trunk native vlan +# - no switchport trunk allowed vlan +# - no switchport trunk pruning vlan +# after: +# - name: GigabitEthernet0/1 +# - name: GigabitEthernet0/2 + +# After state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# media-type rj45 +# negotiation auto + +# Using gathered + +# Before state: +# ------------- +# +# vios#sh running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport trunk allowed vlan 20-40,60,80 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 10 +# switchport trunk pruning vlan 10 +# media-type rj45 +# negotiation auto + +- name: Gather facts for l2 interfaces + cisco.ios.ios_l2_interfaces: + config: + state: gathered + +# Task Output +# ----------- +# +# gathered: +# - access: +# vlan: 20 +# name: GigabitEthernet0/1 +# - access: +# vlan: 20 +# name: GigabitEthernet0/2 +# trunk: +# allowed_vlans: +# - 20-40 +# - '60' +# - '80' +# encapsulation: dot1q +# native_vlan: 10 +# pruning_vlans: +# - '10' + +# Using rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_l2_interfaces: + config: + - name: GigabitEthernet0/1 + access: + vlan: 30 + - name: GigabitEthernet0/2 + trunk: + allowed_vlans: 10-20,40 + native_vlan: 20 + pruning_vlans: 10,20 + encapsulation: dot1q + state: rendered + +# Task Output +# ----------- +# +# rendered: +# - interface GigabitEthernet0/1 +# - switchport access vlan 30 +# - interface GigabitEthernet0/2 +# - switchport trunk encapsulation dot1q +# - switchport trunk native vlan 20 +# - switchport trunk allowed vlan 10-20,40 +# - switchport trunk pruning vlan 10,20 + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport trunk allowed vlan 20-40,60,80 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 10 +# switchport trunk pruning vlan 10 +# media-type rj45 +# negotiation auto + +- name: Parse the commands for provided configuration + cisco.ios.ios_l2_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Task Output +# ----------- +# +# parsed: +# - access: +# vlan: 20 +# name: GigabitEthernet0/1 +# - access: +# vlan: 20 +# name: GigabitEthernet0/2 +# trunk: +# allowed_vlans: +# - 20-40 +# - '60' +# - '80' +# encapsulation: dot1q +# native_vlan: 10 +# pruning_vlans: +# - '10' +""" + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: list + sample: + - interface GigabitEthernet0/2 + - switchport trunk allowed vlan 15-20,40 + - switchport trunk encapsulation dot1q +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - interface GigabitEthernet0/1 + - switchport access vlan 30 + - switchport trunk encapsulation dot1q +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.l2_interfaces.l2_interfaces import ( + L2_interfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.l2_interfaces.l2_interfaces import ( + L2_interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=L2_interfacesArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = L2_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_l3_interfaces.py b/ansible_collections/cisco/ios/plugins/modules/ios_l3_interfaces.py new file mode 100644 index 000000000..7ea6d0d7a --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_l3_interfaces.py @@ -0,0 +1,914 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_l3_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_l3_interfaces +short_description: Resource module to configure L3 interfaces. +description: + - This module provides declarative management of Layer-3 interface on Cisco IOS devices. +version_added: 1.0.0 +author: + - Sagar Paul (@KB-perByte) + - Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - Using deleted state without config will delete all l3 attributes from all the interfaces. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) + - The module examples uses callback plugin (stdout_callback = yaml) to generate task + output in yaml format. +options: + config: + description: A dictionary of Layer-3 interface options + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface excluding any logical unit number, i.e. GigabitEthernet0/1. + type: str + required: true + ipv4: + description: + - IPv4 address to be set for the Layer-3 interface mentioned in I(name) option. + The address format is <ipv4 address>/<mask>, the mask is number in range + 0-32 eg. 192.168.0.1/24. + type: list + elements: dict + suboptions: + address: + description: + - Configures the IPv4 address for Interface. + type: str + secondary: + description: + - Configures the IP address as a secondary address. + type: bool + dhcp_client: + description: + - Configures and specifies client-id to use over DHCP ip. Note, This option + shall work only when dhcp is configured as IP. + - GigabitEthernet interface number + - This option is DEPRECATED and is replaced with dhcp which + accepts dict as input this attribute will be removed after 2023-08-01. + type: str + dhcp_hostname: + description: + - Configures and specifies value for hostname option over DHCP ip. Note, + This option shall work only when dhcp is configured as IP. + - This option is DEPRECATED and is replaced with dhcp which + accepts dict as input this attribute will be removed after 2023-08-01. + type: str + dhcp: + description: IP Address negotiated via DHCP. + type: dict + suboptions: + enable: + description: Enable dhcp. + type: bool + client_id: + description: Specify client-id to use. + type: str + hostname: + description: Specify value for hostname option. + type: str + pool: + description: IP Address auto-configured from a local DHCP pool. + type: str + ipv6: + description: + - IPv6 address to be set for the Layer-3 interface mentioned in I(name) option. + - The address format is <ipv6 address>/<mask>, the mask is number in range + 0-128 eg. fd5d:12c9:2201:1::1/64 + type: list + elements: dict + suboptions: + address: + description: + - Configures the IPv6 address for Interface. + type: str + autoconfig: + description: Obtain address using auto-configuration. + type: dict + suboptions: + enable: + description: enable auto-configuration. + type: bool + default: + description: Insert default route. + type: bool + dhcp: + description: Obtain a ipv6 address using DHCP. + type: dict + suboptions: + enable: + description: Enable dhcp. + type: bool + rapid_commit: + description: Enable Rapid-Commit. + type: bool + anycast: + description: Configure as an anycast + type: bool + cga: + description: Use CGA interface identifier + type: bool + eui: + description: Use eui-64 interface identifier + type: bool + link_local: + description: Use link-local address + type: bool + segment_routing: + description: Segment Routing submode + type: dict + suboptions: + enable: + description: Enable segmented routing. + type: bool + default: + description: Set a command to its defaults. + type: bool + ipv6_sr: + description: Set ipv6_sr. + type: bool + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS 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 + state: + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + default: merged + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command + I(show running-config | section ^interface) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# Router#show running-config | section ^interface +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address dhcp +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: Merge provided configuration with device configuration + cisco.ios.ios_l3_interfaces: + config: + - name: GigabitEthernet0/1 + ipv4: + - address: 192.168.0.1/24 + secondary: true + - name: GigabitEthernet2 + ipv4: + - address: 192.168.0.2/24 + - name: GigabitEthernet3 + ipv6: + - address: fd5d:12c9:2201:1::1/64 + - name: GigabitEthernet3.100 + ipv4: + - address: 192.168.0.3/24 + state: merged + +# Task Output +# ----------- +# +# before: +# - ipv4: +# - dhcp: +# enable: true +# name: GigabitEthernet1 +# - ipv4: +# - dhcp: +# enable: true +# name: GigabitEthernet2 +# - name: GigabitEthernet3 +# - name: GigabitEthernet4 +# - name: Loopback999 +# commands: +# - interface GigabitEthernet2 +# - ip address 192.168.0.2 255.255.255.0 +# - interface GigabitEthernet3 +# - ipv6 address fd5d:12c9:2201:1::1/64 +# - interface GigabitEthernet3.100 +# - ip address 192.168.0.3 255.255.255.0 +# after: +# - ipv4: +# - dhcp: +# enable: true +# name: GigabitEthernet1 +# - ipv4: +# - address: 192.168.0.2/24 +# name: GigabitEthernet2 +# - ipv6: +# - address: FD5D:12C9:2201:1::1/64 +# name: GigabitEthernet3 +# - name: GigabitEthernet3.100 +# ipv4: +# - address: 192.168.0.3/24 +# - name: GigabitEthernet4 +# - name: Loopback999 + +# After state: +# ------------ +# +# Router#show running-config | section ^interface +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address 192.168.0.2 255.255.255.0 +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# ipv6 address FD5D:12C9:2201:1::1/64 +# interface GigabitEthernet3.100 +# ip address 192.168.0.3 255.255.255.0 +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +# Using replaced + +# Before state: +# ------------- +# +# Router#show running-config | section ^interface +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address 192.168.0.2 255.255.255.0 +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# ipv6 address FD5D:12C9:2201:1::1/64 +# interface GigabitEthernet3.100 +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: Replaces device configuration of listed interfaces with provided configuration + cisco.ios.ios_l3_interfaces: + config: + - name: GigabitEthernet2 + ipv4: + - address: 192.168.2.0/24 + - name: GigabitEthernet3 + ipv4: + - dhcp: + client_id: GigabitEthernet2 + hostname: test.com + state: replaced + +# Task Output +# ----------- +# +# before: +# - ipv4: +# - dhcp: +# enable: true +# name: GigabitEthernet1 +# - ipv4: +# - address: 192.168.0.2/24 +# name: GigabitEthernet2 +# - ipv6: +# - address: FD5D:12C9:2201:1::1/64 +# name: GigabitEthernet3 +# - name: GigabitEthernet3.100 +# - name: GigabitEthernet4 +# - name: Loopback999 +# commands: +# - interface GigabitEthernet2 +# - ip address 192.168.0.3 255.255.255.0 +# - no ip address 192.168.0.2 255.255.255.0 +# - interface GigabitEthernet3 +# - ip address dhcp client-id GigabitEthernet2 hostname test.com +# - no ipv6 address fd5d:12c9:2201:1::1/64 +# after: +# - ipv4: +# - dhcp: +# enable: true +# name: GigabitEthernet1 +# - ipv4: +# - address: 192.168.0.3/24 +# name: GigabitEthernet2 +# - ipv4: +# - dhcp: +# client_id: GigabitEthernet2 +# enable: true +# hostname: test.com +# name: GigabitEthernet3 +# - name: GigabitEthernet3.100 +# - name: GigabitEthernet4 +# - name: Loopback999 + +# After state: +# ------------ +# +# router-ios#show running-config | section ^interface +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address 192.168.0.3 255.255.255.0 +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# ip address dhcp client-id GigabitEthernet2 hostname test.com +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3.100 +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +# Using overridden + +# Before state: +# ------------- +# +# router-ios#show running-config | section ^interface +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address 192.168.0.3 255.255.255.0 +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# ip address dhcp client-id GigabitEthernet2 hostname test.com +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3.100 +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: Override device configuration of all interfaces with provided configuration + cisco.ios.ios_l3_interfaces: + config: + - ipv4: + - dhcp: + enable: true + name: GigabitEthernet1 + - name: GigabitEthernet2 + ipv4: + - address: 192.168.0.1/24 + - name: GigabitEthernet3 + state: overridden + +# Task Output +# ----------- +# before: +# - ipv4: +# - dhcp: +# enable: true +# name: GigabitEthernet1 +# - ipv4: +# - address: 192.168.0.3/24 +# name: GigabitEthernet2 +# - ipv4: +# - dhcp: +# client_id: GigabitEthernet2 +# enable: true +# hostname: test.com +# name: GigabitEthernet3 +# - name: GigabitEthernet3.100 +# - name: GigabitEthernet4 +# - name: Loopback999 +# commands: +# - interface GigabitEthernet2 +# - ip address 192.168.0.1 255.255.255.0 +# - no ip address 192.168.0.3 255.255.255.0 +# - interface GigabitEthernet3 +# - no ip address dhcp client-id GigabitEthernet2 hostname test.com +# after: +# - ipv4: +# - dhcp: +# enable: true +# name: GigabitEthernet1 +# - ipv4: +# - address: 192.168.0.1/24 +# name: GigabitEthernet2 +# - name: GigabitEthernet3 +# - name: GigabitEthernet3.100 +# - name: GigabitEthernet4 +# - name: Loopback999 + +# After state: +# ------------ +# +# router-ios#show running-config | section ^interface +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address 192.168.0.1 255.255.255.0 +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3.100 +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +# Using deleted + +# Before state: +# ------------- +# +# router-ios#show running-config | section ^interface +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address 192.168.0.1 255.255.255.0 +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3.100 +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: "Delete attributes of given interfaces (NOTE: This won't delete the interfaces)" + cisco.ios.ios_l3_interfaces: + config: + - name: GigabitEthernet2 + - name: GigabitEthernet3.100 + state: deleted + +# Task Output +# ----------- +# +# before: +# - ipv4: +# - dhcp: +# enable: true +# name: GigabitEthernet1 +# - ipv4: +# - address: 192.168.0.1/24 +# name: GigabitEthernet2 +# - name: GigabitEthernet3 +# - name: GigabitEthernet3.100 +# - name: GigabitEthernet4 +# - name: Loopback999 +# commands: +# - interface GigabitEthernet2 +# - no ip address 192.168.0.1 255.255.255.0 +# after: +# - ipv4: +# - dhcp: +# enable: true +# name: GigabitEthernet1 +# - name: GigabitEthernet2 +# - name: GigabitEthernet3 +# - name: GigabitEthernet3.100 +# - name: GigabitEthernet4 +# - name: Loopback999 + +# After state: +# ------------- +# +# router-ios#show running-config | section ^interface +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3.100 +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +# Using deleted without config passed, only interface's configuration will be negated + +# Before state: +# ------------- + +# router-ios#show running-config | section ^interface +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address 192.168.0.2 255.255.255.0 +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# ipv6 address FD5D:12C9:2201:1::1/64 +# interface GigabitEthernet3.100 +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: "Delete L3 config of all interfaces" + cisco.ios.ios_l3_interfaces: + state: deleted + +# Task Output +# ----------- +# +# before: +# - ipv4: +# - dhcp: +# enable: true +# name: GigabitEthernet1 +# - ipv4: +# - address: 192.168.0.2/24 +# name: GigabitEthernet2 +# - ipv6: +# - address: FD5D:12C9:2201:1::1/64 +# name: GigabitEthernet3 +# - name: GigabitEthernet3.100 +# - name: GigabitEthernet4 +# - name: Loopback999 +# commands: +# - interface GigabitEthernet1 +# - no ip address dhcp +# - interface GigabitEthernet2 +# - no ip address 192.168.0.2 255.255.255.0 +# - interface GigabitEthernet3 +# - no ipv6 address fd5d:12c9:2201:1::1/64 +# after: +# - name: GigabitEthernet1 +# - name: GigabitEthernet2 +# - name: GigabitEthernet3 +# - name: GigabitEthernet3.100 +# - name: GigabitEthernet4 +# - name: Loopback999 + +# After state: +# ------------- +# +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# negotiation auto +# interface GigabitEthernet2 +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# no ip address +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3.100 +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +# Using gathered + +# Before state: +# ------------- +# +# Router#show running-config | section ^interface +# interface Loopback999 +# no ip address +# shutdown +# interface GigabitEthernet1 +# description Management interface do not change +# ip address dhcp +# negotiation auto +# interface GigabitEthernet2 +# ip address 192.168.0.3 255.255.255.0 +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3 +# description Configured and Overridden by Ansible Network +# ip address dhcp client-id GigabitEthernet2 hostname test.com +# shutdown +# speed 1000 +# no negotiation auto +# interface GigabitEthernet3.100 +# interface GigabitEthernet4 +# no ip address +# shutdown +# negotiation auto + +- name: Gather facts for l3 interfaces + cisco.ios.ios_l3_interfaces: + state: gathered + +# Task Output +# ----------- +# +# gathered: +# - ipv4: +# - dhcp: +# enable: true +# name: GigabitEthernet1 +# - ipv4: +# - address: 192.168.0.3/24 +# name: GigabitEthernet2 +# - ipv4: +# - dhcp: +# client_id: GigabitEthernet2 +# enable: true +# hostname: test.com +# name: GigabitEthernet3 +# - name: GigabitEthernet3.100 +# - name: GigabitEthernet4 +# - name: Loopback999 + +# Using rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_l3_interfaces: + config: + - name: GigabitEthernet1 + ipv4: + - dhcp: + client_id: GigabitEthernet0/0 + hostname: test.com + - name: GigabitEthernet2 + ipv4: + - address: 198.51.100.1/24 + secondary: true + - address: 198.51.100.2/24 + ipv6: + - address: 2001:db8:0:3::/64 + state: rendered + +# Task Output +# ----------- +# +# rendered: +# - interface GigabitEthernet1 +# - ip address dhcp client-id GigabitEthernet0/0 hostname test.com +# - interface GigabitEthernet2 +# - ip address 198.51.100.1 255.255.255.0 secondary +# - ip address 198.51.100.2 255.255.255.0 +# - ipv6 address 2001:db8:0:3::/64 + +# Using parsed + +# File: parsed.cfg +# ---------------- +# +# interface GigabitEthernet0/1 +# ip address dhcp client-id GigabitEthernet 0/0 hostname test.com +# interface GigabitEthernet0/2 +# ip address 198.51.100.1 255.255.255.0 +# ip address 198.51.100.2 255.255.255.0 secondary +# ipv6 address 2001:db8:0:3::/64 + +- name: Parse the provided configuration + cisco.ios.ios_l3_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Task Output +# ----------- +# +# parsed: +# - ipv4: +# - dhcp: +# client_id: GigabitEthernet0/0 +# hostname: test.com +# name: GigabitEthernet0/1 +# - ipv4: +# - address: 198.51.100.1/24 +# secondary: true +# - address: 198.51.100.2/24 +# ipv6: +# - address: 2001:db8:0:3::/64 +# name: GigabitEthernet0/2 + +""" + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when state is I(merged), I(replaced), I(overridden), I(deleted) or I(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when state is I(merged), I(replaced), I(overridden), I(deleted) or I(purged) + type: list + sample: + - "ip address 192.168.0.3 255.255.255.0" + - "ipv6 address dhcp rapid-commit" + - "ipv6 address fd5d:12c9:2201:1::1/64 anycast" +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when state is I(rendered) + type: list + sample: + - "ipv6 address FD5D:12C9:2201:1::1/64" + - "ip address 192.168.0.3 255.255.255.0" + - "ip address autoconfig" +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when state is I(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when state is I(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.l3_interfaces.l3_interfaces import ( + L3_interfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.l3_interfaces.l3_interfaces import ( + L3_interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=L3_interfacesArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = L3_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_lacp.py b/ansible_collections/cisco/ios/plugins/modules/ios_lacp.py new file mode 100644 index 000000000..705b0fced --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_lacp.py @@ -0,0 +1,275 @@ +#!/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/>. +# +""" +The module file for ios_lacp +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: ios_lacp +short_description: Resource module to configure LACP. +description: + This module provides declarative management of Global LACP on Cisco IOS + network devices. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: The provided configurations. + type: dict + suboptions: + system: + description: This option sets the default system parameters for LACP. + type: dict + suboptions: + priority: + description: + - LACP priority for the system. + - Refer to vendor documentation for valid values. + type: int + required: true + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by + executing the command B(show lacp sys-id). + - 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 + state: + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - deleted + - rendered + - parsed + - gathered + default: merged +""" + +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# vios#show lacp sys-id +# 32768, 5e00.0000.8000 + +- name: Merge provided configuration with device configuration + cisco.ios.ios_lacp: + config: + system: + priority: 123 + state: merged + +# After state: +# ------------ +# +# vios#show lacp sys-id +# 123, 5e00.0000.8000 + +# Using replaced +# +# Before state: +# ------------- +# +# vios#show lacp sys-id +# 500, 5e00.0000.8000 + +- name: Replaces Global LACP configuration + cisco.ios.ios_lacp: + config: + system: + priority: 123 + state: replaced + +# After state: +# ------------ +# +# vios#show lacp sys-id +# 123, 5e00.0000.8000 + +# Using Deleted +# +# Before state: +# ------------- +# +# vios#show lacp sys-id +# 500, 5e00.0000.8000 + +- name: Delete Global LACP attribute + cisco.ios.ios_lacp: + state: deleted + +# After state: +# ------------- +# +# vios#show lacp sys-id +# 32768, 5e00.0000.8000 + +# Using Gathered + +# Before state: +# ------------- +# +# vios#show lacp sys-id +# 123, 5e00.0000.8000 + +- name: Gather listed LACP with provided configurations + cisco.ios.ios_lacp: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": { +# "system": { +# "priority": 500 +# } +# } + +# After state: +# ------------ +# +# vios#show lacp sys-id +# 123, 5e00.0000.8000 + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_lacp: + config: + system: + priority: 123 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "lacp system-priority 10" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# lacp system-priority 123 + +- name: Parse the commands for provided configuration + cisco.ios.ios_lacp: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": { +# "system": { +# "priority": 123 +# } +# } +""" + +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: ['lacp system-priority 10'] +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lacp.lacp import ( + LacpArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.lacp.lacp import Lacp + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=LacpArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = Lacp(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_lacp_interfaces.py b/ansible_collections/cisco/ios/plugins/modules/ios_lacp_interfaces.py new file mode 100644 index 000000000..fe8e92bcc --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_lacp_interfaces.py @@ -0,0 +1,514 @@ +#!/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/>. +# +""" +The module file for ios_lacp_interfaces +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: ios_lacp_interfaces +short_description: Resource module to configure LACP interfaces. +description: + This module provides declarative management of LACP on Cisco IOS network + devices lacp_interfaces. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: A dictionary of LACP lacp_interfaces option + type: list + elements: dict + suboptions: + name: + description: + - Name of the Interface for configuring LACP. + type: str + required: true + port_priority: + description: + - LACP priority on this interface. + - Refer to vendor documentation for valid port values. + type: int + fast_switchover: + description: + - LACP fast switchover supported on this port channel. + type: bool + max_bundle: + description: + - LACP maximum number of ports to bundle in this port channel. + - Refer to vendor documentation for valid port values. + type: int + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS 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 + state: + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + default: merged +""" + +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# interface GigabitEthernet0/2 +# shutdown +# interface GigabitEthernet0/3 +# shutdown + +- name: Merge provided configuration with device configuration + cisco.ios.ios_lacp_interfaces: + config: + - name: GigabitEthernet0/1 + port_priority: 10 + - name: GigabitEthernet0/2 + port_priority: 20 + - name: GigabitEthernet0/3 + port_priority: 30 + - name: Port-channel10 + fast_switchover: true + max_bundle: 5 + state: merged + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# lacp fast-switchover +# lacp max-bundle 5 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# lacp port-priority 10 +# interface GigabitEthernet0/2 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/3 +# shutdown +# lacp port-priority 30 + +# Using overridden +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# lacp fast-switchover +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# lacp port-priority 10 +# interface GigabitEthernet0/2 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/3 +# shutdown +# lacp port-priority 30 + +- name: Override device configuration of all lacp_interfaces with provided configuration + cisco.ios.ios_lacp_interfaces: + config: + - name: GigabitEthernet0/1 + port_priority: 20 + - name: Port-channel10 + max_bundle: 2 + state: overridden + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# lacp max-bundle 2 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/2 +# shutdown +# interface GigabitEthernet0/3 +# shutdown + +# Using replaced +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# lacp max-bundle 5 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# lacp port-priority 10 +# interface GigabitEthernet0/2 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/3 +# shutdown +# lacp port-priority 30 + +- name: Replaces device configuration of listed lacp_interfaces with provided configuration + cisco.ios.ios_lacp_interfaces: + config: + - name: GigabitEthernet0/3 + port_priority: 40 + - name: Port-channel10 + fast_switchover: true + max_bundle: 2 + state: replaced + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# lacp fast-switchover +# lacp max-bundle 2 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# lacp port-priority 10 +# interface GigabitEthernet0/2 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/3 +# shutdown +# lacp port-priority 40 + +# Using Deleted +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# flowcontrol receive on +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# lacp port-priority 10 +# interface GigabitEthernet0/2 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/3 +# shutdown +# lacp port-priority 30 + +- name: "Delete LACP attributes of given interfaces (Note: This won't delete the interface itself)" + cisco.ios.ios_lacp_interfaces: + config: + - name: GigabitEthernet0/1 + state: deleted + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# interface GigabitEthernet0/2 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/3 +# shutdown +# lacp port-priority 30 + +# Using Deleted without any config passed +# "(NOTE: This will delete all of configured LLDP module attributes)" +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# lacp fast-switchover +# interface Port-channel20 +# lacp max-bundle 2 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# lacp port-priority 10 +# interface GigabitEthernet0/2 +# shutdown +# lacp port-priority 20 +# interface GigabitEthernet0/3 +# shutdown +# lacp port-priority 30 + +- name: "Delete LACP attributes for all configured interfaces (Note: This won't delete the interface itself)" + cisco.ios.ios_lacp_interfaces: + state: deleted + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# interface GigabitEthernet0/2 +# shutdown +# interface GigabitEthernet0/3 +# shutdown + +# Using Gathered + +# Before state: +# ------------- +# +# vios#sh running-config | section ^interface +# interface Port-channel10 +# lacp fast-switchover +# lacp max-bundle 2 +# interface Port-channel40 +# lacp max-bundle 5 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# lacp port-priority 30 +# interface GigabitEthernet0/2 +# lacp port-priority 20 + +- name: Gather listed LACP interfaces with provided configurations + cisco.ios.ios_lacp_interfaces: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "fast_switchover": true, +# "max_bundle": 2, +# "name": "Port-channel10" +# }, +# { +# "max_bundle": 5, +# "name": "Port-channel40" +# }, +# { +# "name": "GigabitEthernet0/0" +# }, +# { +# "name": "GigabitEthernet0/1", +# "port_priority": 30 +# }, +# { +# "name": "GigabitEthernet0/2", +# "port_priority": 20 +# } +# ] + +# After state: +# ------------ +# +# vios#sh running-config | section ^interface +# interface Port-channel10 +# lacp fast-switchover +# lacp max-bundle 2 +# interface Port-channel40 +# lacp max-bundle 5 +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# lacp port-priority 30 +# interface GigabitEthernet0/2 +# lacp port-priority 20 + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_lacp_interfaces: + config: + - name: GigabitEthernet0/1 + port_priority: 10 + - name: GigabitEthernet0/2 + port_priority: 20 + - name: Port-channel10 + fast_switchover: true + max_bundle: 2 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "interface GigabitEthernet0/1", +# "lacp port-priority 10", +# "interface GigabitEthernet0/2", +# "lacp port-priority 20", +# "interface Port-channel10", +# "lacp max-bundle 2", +# "lacp fast-switchover" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# interface GigabitEthernet0/1 +# lacp port-priority 10 +# interface GigabitEthernet0/2 +# lacp port-priority 20 +# interface Port-channel10 +# lacp max-bundle 2 fast-switchover + +- name: Parse the commands for provided configuration + cisco.ios.ios_lacp_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "name": "GigabitEthernet0/1", +# "port_priority": 10 +# }, +# { +# "name": "GigabitEthernet0/2", +# "port_priority": 20 +# }, +# { +# "fast_switchover": true, +# "max_bundle": 2, +# "name": "Port-channel10" +# } +# ] + +""" + +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 GigabitEthernet 0/1', 'lacp port-priority 30'] +""" +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lacp_interfaces.lacp_interfaces import ( + Lacp_InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.lacp_interfaces.lacp_interfaces import ( + Lacp_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=Lacp_InterfacesArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = Lacp_Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_lag_interfaces.py b/ansible_collections/cisco/ios/plugins/modules/ios_lag_interfaces.py new file mode 100644 index 000000000..64a43f700 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_lag_interfaces.py @@ -0,0 +1,647 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_lag_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_lag_interfaces +short_description: Resource module to configure LAG interfaces. +description: + This module manages properties of Link Aggregation Group on Cisco IOS + devices. +version_added: 1.0.0 +author: + - Sagar Paul (@KB-perByte) + - Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: A list of link aggregation group configurations. + type: list + elements: dict + suboptions: + name: + description: + - ID of Ethernet Channel of interfaces. + - Refer to vendor documentation for valid port values. + type: str + required: true + members: + description: + - Interface options for the link aggregation group. + type: list + elements: dict + suboptions: + member: + description: + - Interface member of the link aggregation group. + type: str + mode: + description: + - Etherchannel Mode of the interface for link aggregation. + - On mode has to be quoted as 'on' or else pyyaml will convert + to True before it gets to Ansible. + type: str + choices: + - auto + - "on" + - desirable + - active + - passive + link: + description: + - Assign a link identifier used for load-balancing. + - Refer to vendor documentation for valid values. + - NOTE, parameter only supported on Cisco IOS XE platform. + type: int + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS 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 + state: + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - parsed + - gathered + default: merged +""" + +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface GigabitEthernet0/1 +# shutdown +# interface GigabitEthernet0/2 +# shutdown +# interface GigabitEthernet0/3 +# shutdown +# interface GigabitEthernet0/4 +# shutdown + +- name: Merge provided configuration with device configuration + cisco.ios.ios_lag_interfaces: + config: + - name: Port-channel10 + members: + - member: GigabitEthernet0/1 + mode: auto + - member: GigabitEthernet0/2 + mode: auto + - name: Port-channel20 + members: + - member: GigabitEthernet0/3 + mode: on + - name: Port-channel30 + members: + - member: GigabitEthernet0/4 + mode: active + state: merged + +# Task Output: +# --------------- + +# commands: +# - interface GigabitEthernet0/1 +# - channel-group 10 mode auto +# - interface GigabitEthernet0/2 +# - channel-group 10 mode auto +# - interface GigabitEthernet0/3 +# - channel-group 20 mode on +# - interface GigabitEthernet0/4 +# - channel-group 30 mode active + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/2 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 20 mode on +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +# Using overridden +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/2 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 20 mode on +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +- name: Override device configuration of all interfaces with provided configuration + cisco.ios.ios_lag_interfaces: + config: + - name: Port-channel20 + members: + - member: GigabitEthernet0/2 + mode: auto + - member: GigabitEthernet0/3 + mode: auto + state: overridden + +# Task Output: +# --------------- + +# commands: +# - interface GigabitEthernet0/1 +# - no channel-group 10 mode auto +# - interface GigabitEthernet0/2 +# - no channel-group 10 mode auto +# - interface GigabitEthernet0/4 +# - no channel-group 30 mode active +# - interface GigabitEthernet0/2 +# - channel-group 20 mode auto +# - interface GigabitEthernet0/3 +# - channel-group 20 mode auto + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# interface GigabitEthernet0/2 +# shutdown +# channel-group 20 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 20 mode auto +# interface GigabitEthernet0/4 +# shutdown + +# Using replaced +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/2 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 20 mode on +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +- name: Replaces device configuration of listed interfaces with provided configuration + cisco.ios.ios_lag_interfaces: + config: + - name: Port-channel30 + members: + - member: GigabitEthernet0/3 + mode: auto + state: replaced + +# Task Output: +# --------------- + +# commands: +# - interface GigabitEthernet0/3 +# - channel-group 30 mode auto +# - interface GigabitEthernet0/4 +# - no channel-group 30 mode active + +# After state: +# ------------ +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/2 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 30 mode auto +# interface GigabitEthernet0/4 +# shutdown + +# Using Deleted +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/2 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 20 mode on +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +- name: "Delete LAG attributes of given interfaces (Note: This won't delete the interface itself)" + cisco.ios.ios_lag_interfaces: + config: + - name: Port-channel10 + - name: Port-channel20 + state: deleted + +# Task Output: +# --------------- + +# commands: +# - interface GigabitEthernet0/1 +# - no channel-group 10 mode auto +# - interface GigabitEthernet0/2 +# - no channel-group 10 mode auto +# - interface GigabitEthernet0/3 +# - no channel-group 20 mode on + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# interface GigabitEthernet0/2 +# shutdown +# interface GigabitEthernet0/3 +# shutdown +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +# Using Deleted without any config passed +#"(NOTE: This will delete all of configured LLDP module attributes)" + +# +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/2 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 20 mode on +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +- name: "Delete all configured LAG attributes for interfaces (Note: This won't delete the interface itself)" + cisco.ios.ios_lag_interfaces: + state: deleted + +# Task Output: +# --------------- + +# commands: +# - interface GigabitEthernet0/1 +# - no channel-group 10 mode auto +# - interface GigabitEthernet0/2 +# - no channel-group 10 mode auto +# - interface GigabitEthernet0/3 +# - no channel-group 20 mode on +# - interface GigabitEthernet0/4 +# - no channel-group 30 mode active + +# After state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# interface GigabitEthernet0/2 +# shutdown +# interface GigabitEthernet0/3 +# shutdown +# interface GigabitEthernet0/4 +# shutdown + +# Using Gathered + +# Before state: +# ------------- +# +# vios#show running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/2 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 20 mode on +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +- name: Gather listed LAG interfaces with provided configurations + cisco.ios.ios_lag_interfaces: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "members": [ +# { +# "member": "GigabitEthernet0/1", +# "mode": "auto" +# }, +# { +# "member": "GigabitEthernet0/2", +# "mode": "auto" +# } +# ], +# "name": "Port-channel10" +# }, +# { +# "members": [ +# { +# "member": "GigabitEthernet0/3", +# "mode": "on" +# } +# ], +# "name": "Port-channel20" +# }, +# { +# "members": [ +# { +# "member": "GigabitEthernet0/4", +# "mode": "active" +# } +# ], +# "name": "Port-channel30" +# } +# ] + +# After state: +# ------------ +# +# vios#sh running-config | section ^interface +# interface Port-channel10 +# interface Port-channel20 +# interface Port-channel30 +# interface GigabitEthernet0/1 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/2 +# shutdown +# channel-group 10 mode auto +# interface GigabitEthernet0/3 +# shutdown +# channel-group 20 mode on +# interface GigabitEthernet0/4 +# shutdown +# channel-group 30 mode active + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_lag_interfaces: + config: + - name: Port-channel11 + members: + - member: GigabitEthernet0/1 + mode: active + - name: Port-channel22 + members: + - member: GigabitEthernet0/2 + mode: passive + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "interface GigabitEthernet0/1", +# "channel-group 11 mode active", +# "interface GigabitEthernet0/2", +# "channel-group 22 mode passive", +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# interface GigabitEthernet0/1 +# channel-group 11 mode active +# interface GigabitEthernet0/2 +# channel-group 22 mode passive + +- name: Parse the commands for provided configuration + cisco.ios.ios_lag_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "members": [ +# { +# "member": "GigabitEthernet0/1", +# "mode": "active" +# } +# ], +# "name": "Port-channel11" +# }, +# { +# "members": [ +# { +# "member": "GigabitEthernet0/2", +# "mode": "passive" +# } +# ], +# "name": "Port-channel22" +# } +# ] +""" + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: list + sample: + - interface GigabitEthernet0/1 + - channel-group 10 mode auto + - channel-group 10 mode active link 20 +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - interface GigabitEthernet0/2 + - channel-group 20 mode auto + - channel-group 20 mode active link 60 +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lag_interfaces.lag_interfaces import ( + Lag_InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.lag_interfaces.lag_interfaces import ( + Lag_interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Lag_InterfacesArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Lag_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_linkagg.py b/ansible_collections/cisco/ios/plugins/modules/ios_linkagg.py new file mode 100644 index 000000000..cadc5551c --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_linkagg.py @@ -0,0 +1,354 @@ +#!/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 = r""" +module: ios_linkagg +author: Trishna Guha (@trishnaguha) +short_description: Module to configure link aggregation groups. +deprecated: + alternative: ios_lag_interfaces + why: Updated modules released with more functionality. + removed_at_date: "2024-06-01" +description: + - This module provides declarative management of link aggregation groups on Cisco + IOS network devices. +version_added: 1.0.0 +notes: + - Tested against IOS 15.2 + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + group: + description: + - Channel-group number for the port-channel Link aggregation group. Range 1-255. + type: int + mode: + description: + - Mode of the link aggregation group. + - On mode has to be quoted as 'on' or else pyyaml will convert + to True before it gets to Ansible. + choices: + - active + - "on" + - passive + - auto + - desirable + type: str + members: + description: + - List of members of the link aggregation group. + type: list + elements: str + aggregate: + description: List of link aggregation definitions. + type: list + elements: dict + suboptions: + group: + description: + - Channel-group number for the port-channel Link aggregation group. Range 1-255. + type: str + required: true + mode: + description: + - Mode of the link aggregation group. + - On mode has to be quoted as 'on' or else pyyaml will convert + to True before it gets to Ansible. + choices: + - active + - "on" + - passive + - auto + - desirable + type: str + members: + description: + - List of members of the link aggregation group. + type: list + elements: str + state: + description: + - State of the link aggregation group. + choices: + - present + - absent + type: str + state: + description: + - State of the link aggregation group. + default: present + choices: + - present + - absent + type: str + purge: + description: + - Purge links not defined in the I(aggregate) parameter. + default: false + type: bool +extends_documentation_fragment: + - cisco.ios.ios +""" + +EXAMPLES = """ +- name: Create link aggregation group + cisco.ios.ios_linkagg: + group: 10 + state: present + +- name: Delete link aggregation group + cisco.ios.ios_linkagg: + group: 10 + state: absent + +- name: Set link aggregation group to members + cisco.ios.ios_linkagg: + group: 200 + mode: active + members: + - GigabitEthernet0/0 + - GigabitEthernet0/1 + +- name: Remove link aggregation group from GigabitEthernet0/0 + cisco.ios.ios_linkagg: + group: 200 + mode: active + members: + - GigabitEthernet0/1 + +- name: Create aggregate of linkagg definitions + cisco.ios.ios_linkagg: + aggregate: + - { group: 3, mode: on, members: [GigabitEthernet0/1] } + - { group: 100, mode: passive, members: [GigabitEthernet0/2] } + +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always, except for the platforms that use Netconf transport to manage the device. + type: list + sample: + - interface port-channel 30 + - interface GigabitEthernet0/3 + - channel-group 30 mode on + - no interface port-channel 30 +""" +import re + +from copy import deepcopy + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( + CustomNetworkConfig, +) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_default_spec, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) + + +def search_obj_in_list(group, lst): + for o in lst: + if o["group"] == group: + return o + + +def map_obj_to_commands(updates, module): + commands = list() + want, have = updates + purge = module.params["purge"] + for w in want: + group = w["group"] + mode = w["mode"] + members = w.get("members") or [] + state = w["state"] + del w["state"] + obj_in_have = search_obj_in_list(group, have) + if state == "absent": + if obj_in_have: + commands.append("no interface port-channel {0}".format(group)) + elif state == "present": + cmd = ["interface port-channel {0}".format(group), "end"] + if not obj_in_have: + if not group: + module.fail_json(msg="group is a required option") + commands.extend(cmd) + if members: + for m in members: + commands.append("interface {0}".format(m)) + commands.append("channel-group {0} mode {1}".format(group, mode)) + elif members: + if "members" not in obj_in_have.keys(): + for m in members: + commands.extend(cmd) + commands.append("interface {0}".format(m)) + commands.append("channel-group {0} mode {1}".format(group, mode)) + elif set(members) != set(obj_in_have["members"]): + missing_members = list(set(members) - set(obj_in_have["members"])) + for m in missing_members: + commands.extend(cmd) + commands.append("interface {0}".format(m)) + commands.append("channel-group {0} mode {1}".format(group, mode)) + superfluous_members = list(set(obj_in_have["members"]) - set(members)) + for m in superfluous_members: + commands.extend(cmd) + commands.append("interface {0}".format(m)) + commands.append("no channel-group {0} mode {1}".format(group, mode)) + if purge: + for h in have: + obj_in_want = search_obj_in_list(h["group"], want) + if not obj_in_want: + commands.append("no interface port-channel {0}".format(h["group"])) + return commands + + +def map_params_to_obj(module): + obj = [] + aggregate = module.params.get("aggregate") + if aggregate: + for item in aggregate: + for key in item: + if item.get(key) is None: + item[key] = module.params[key] + d = item.copy() + d["group"] = str(d["group"]) + obj.append(d) + else: + obj.append( + { + "group": str(module.params["group"]), + "mode": module.params["mode"], + "members": module.params["members"], + "state": module.params["state"], + }, + ) + return obj + + +def parse_mode(module, config, group, member): + mode = None + netcfg = CustomNetworkConfig(indent=1, contents=config) + parents = ["interface {0}".format(member)] + body = netcfg.get_section(parents) + match_int = re.findall("interface {0}\\n".format(member), body, re.M) + if match_int: + match = re.search("channel-group {0} mode (\\S+)".format(group), body, re.M) + if match: + mode = match.group(1) + return mode + + +def parse_members(module, config, group): + members = [] + for line in config.strip().split("!"): + l = line.strip() + if l.startswith("interface"): + match_group = re.findall("channel-group {0} mode".format(group), l, re.M) + if match_group: + match = re.search("interface (\\S+)", l, re.M) + if match: + members.append(match.group(1)) + return members + + +def get_channel(module, config, group): + match = re.findall("^interface (\\S+)", config, re.M) + if not match: + return {} + channel = {} + for item in set(match): + member = item + channel["mode"] = parse_mode(module, config, group, member) + channel["members"] = parse_members(module, config, group) + return channel + + +def map_config_to_obj(module): + objs = list() + config = get_config(module) + for line in config.split("\n"): + l = line.strip() + match = re.search("interface Port-channel(\\S+)", l, re.M) + if match: + obj = {} + group = match.group(1) + obj["group"] = group + obj.update(get_channel(module, config, group)) + objs.append(obj) + return objs + + +def main(): + """main entry point for module execution""" + element_spec = dict( + group=dict(type="int"), + mode=dict(choices=["active", "on", "passive", "auto", "desirable"]), + members=dict(type="list", elements="str"), + state=dict(default="present", choices=["present", "absent"]), + ) + aggregate_spec = deepcopy(element_spec) + aggregate_spec["group"] = dict(required=True) + required_one_of = [["group", "aggregate"]] + required_together = [["members", "mode"]] + mutually_exclusive = [["group", "aggregate"]] + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + argument_spec = dict( + aggregate=dict( + type="list", + elements="dict", + options=aggregate_spec, + required_together=required_together, + ), + purge=dict(default=False, type="bool"), + ) + argument_spec.update(element_spec) + module = AnsibleModule( + argument_spec=argument_spec, + required_one_of=required_one_of, + required_together=required_together, + mutually_exclusive=mutually_exclusive, + 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: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_lldp.py b/ansible_collections/cisco/ios/plugins/modules/ios_lldp.py new file mode 100644 index 000000000..5d3f53380 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_lldp.py @@ -0,0 +1,114 @@ +#!/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: ios_lldp +author: Ganesh Nalawade (@ganeshrn) +short_description: (deprecated, removed after 2024-06-01) Manage LLDP configuration on Cisco IOS network devices. +description: + - This module provides declarative management of LLDP service on Cisco IOS network + devices. +version_added: 1.0.0 +deprecated: + alternative: ios_lldp_global + why: Newer and updated modules released with more functionality. + removed_at_date: "2024-06-01" +notes: + - Tested against IOS 15.2 + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + state: + description: + - State of the LLDP configuration. If value is I(present) lldp will be enabled + else if it is I(absent) it will be disabled. + default: present + choices: + - present + - absent + - enabled + - disabled + type: str +extends_documentation_fragment: + - cisco.ios.ios +""" + +EXAMPLES = """ +- name: Enable LLDP service + cisco.ios.ios_lldp: + state: present + +- name: Disable LLDP service + cisco.ios.ios_lldp: + state: absent +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always, except for the platforms that use Netconf transport to manage the device. + type: list + sample: + - lldp run +""" +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + load_config, + run_commands, +) + + +def has_lldp(module): + output = run_commands(module, ["show lldp"]) + is_lldp_enable = False + if len(output) > 0 and "LLDP is not enabled" not in output[0]: + is_lldp_enable = True + return is_lldp_enable + + +def main(): + """main entry point for module execution""" + argument_spec = dict( + state=dict(default="present", choices=["present", "absent", "enabled", "disabled"]), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + warnings = list() + result = {"changed": False} + if warnings: + result["warnings"] = warnings + HAS_LLDP = has_lldp(module) + commands = [] + if module.params["state"] == "absent" and HAS_LLDP: + commands.append("no lldp run") + elif module.params["state"] == "present" and not HAS_LLDP: + commands.append("lldp run") + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_lldp_global.py b/ansible_collections/cisco/ios/plugins/modules/ios_lldp_global.py new file mode 100644 index 000000000..3cbeac6d1 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_lldp_global.py @@ -0,0 +1,356 @@ +#!/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/>. +# +""" +The module file for ios_lldp_global +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_lldp_global +short_description: Resource module to configure LLDP. +description: + This module configures and manages the Link Layer Discovery Protocol(LLDP) + attributes on IOS platforms. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: A dictionary of LLDP options + type: dict + suboptions: + holdtime: + description: + - LLDP holdtime (in sec) to be sent in packets. + - Refer to vendor documentation for valid values. + type: int + reinit: + description: + - Specify the delay (in secs) for LLDP to initialize. + - Refer to vendor documentation for valid values. + - NOTE, if LLDP reinit is configured with a starting value, idempotency won't + be maintained as the Cisco device doesn't record the starting reinit configured + value. As such, Ansible cannot verify if the respective starting reinit + value is already configured or not from the device side. If you try to apply + starting reinit value in every play run, Ansible will show changed as True. + For any other reinit value, idempotency will be maintained since any other + reinit value is recorded in the Cisco device. + type: int + enabled: + description: + - Enable LLDP + type: bool + timer: + description: + - Specify the rate at which LLDP packets are sent (in sec). + - Refer to vendor documentation for valid values. + type: int + tlv_select: + description: + - Selection of LLDP TLVs i.e. type-length-value to send + - NOTE, if tlv-select is configured idempotency won't be maintained as Cisco + device doesn't record configured tlv-select options. As such, Ansible cannot + verify if the respective tlv-select options is already configured or not + from the device side. If you try to apply tlv-select option in every play + run, Ansible will show changed as True. + type: dict + suboptions: + four_wire_power_management: + description: + - Cisco 4-wire Power via MDI TLV + type: bool + mac_phy_cfg: + description: + - IEEE 802.3 MAC/Phy Configuration/status TLV + type: bool + management_address: + description: + - Management Address TLV + type: bool + port_description: + description: + - Port Description TLV + type: bool + port_vlan: + description: + - Port VLAN ID TLV + type: bool + power_management: + description: + - IEEE 802.3 DTE Power via MDI TLV + type: bool + system_capabilities: + description: + - System Capabilities TLV + type: bool + system_description: + description: + - System Description TLV + type: bool + system_name: + description: + - System Name TLV + type: bool + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device + by executing the command B(show running-config | section ^lldp). + - 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 + state: + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - deleted + - rendered + - gathered + - parsed + default: merged +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# vios#sh running-config | section ^lldp +# vios1# + +- name: Merge provided configuration with device configuration + cisco.ios.ios_lldp_global: + config: + holdtime: 10 + enabled: true + reinit: 3 + timer: 10 + state: merged + +# After state: +# ------------ +# vios#sh running-config | section ^lldp +# lldp timer 10 +# lldp holdtime 10 +# lldp reinit 3 +# lldp run + +# Using replaced + +# Before state: +# ------------- +# vios#sh running-config | section ^lldp +# lldp timer 10 +# lldp holdtime 10 +# lldp reinit 3 +# lldp run + +- name: Replaces LLDP device configuration with provided configuration + cisco.ios.ios_lldp_global: + config: + holdtime: 20 + reinit: 5 + state: replaced + +# After state: +# ------------- +# vios#sh running-config | section ^lldp +# lldp holdtime 20 +# lldp reinit 5 + +# Using Deleted without any config passed +#"(NOTE: This will delete all of configured LLDP module attributes)" + +# Before state: +# ------------- +# vios#sh running-config | section ^lldp +# lldp timer 10 +# lldp holdtime 10 +# lldp reinit 3 +# lldp run + +- name: Delete LLDP attributes + cisco.ios.ios_lldp_global: + state: deleted + +# After state: +# ------------- +# vios#sh running-config | section ^lldp +# vios1# + +# Using Gathered + +# Before state: +# ------------- +# +# vios#sh running-config | section ^lldp +# lldp timer 10 +# lldp holdtime 10 +# lldp reinit 3 +# lldp run + +- name: Gather listed interfaces with provided configurations + cisco.ios.ios_lldp_global: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": { +# "enabled": true, +# "holdtime": 10, +# "reinit": 3, +# "timer": 10 +# } + +# After state: +# ------------ +# +# vios#sh running-config | section ^lldp +# lldp timer 10 +# lldp holdtime 10 +# lldp reinit 3 +# lldp run + +# Using Rendered +- name: Render the commands for provided configuration + cisco.ios.ios_lldp_global: + config: + holdtime: 10 + enabled: true + reinit: 3 + timer: 10 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "lldp holdtime 10", +# "lldp run", +# "lldp timer 10", +# "lldp reinit 3" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# lldp timer 10 +# lldp holdtime 10 +# lldp reinit 3 +# lldp run + +- name: Parse the commands for provided configuration + cisco.ios.ios_lldp_global: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": { +# "enabled": true, +# "holdtime": 10, +# "reinit": 3, +# "timer": 10 +# } + +""" + +RETURN = """ +before: + description: The configuration as structured data prior to module invocation. + returned: always + type: dict + 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: 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: ['lldp holdtime 10', 'lldp run', 'lldp timer 10'] +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lldp_global.lldp_global import ( + Lldp_globalArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.lldp_global.lldp_global import ( + Lldp_global, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + + module = AnsibleModule( + argument_spec=Lldp_globalArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = Lldp_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_lldp_interfaces.py b/ansible_collections/cisco/ios/plugins/modules/ios_lldp_interfaces.py new file mode 100644 index 000000000..09eacb11b --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_lldp_interfaces.py @@ -0,0 +1,671 @@ +#!/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/>. +# +""" +The module file for ios_lldp_interfaces +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: ios_lldp_interfaces +short_description: Resource module to configure LLDP interfaces. +description: + This module manages link layer discovery protocol (LLDP) attributes of + interfaces on Cisco IOS devices. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: A dictionary of LLDP options + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface excluding any logical unit number, i.e. GigabitEthernet0/1. + type: str + required: true + receive: + description: + - Enable LLDP reception on interface. + type: bool + transmit: + description: + - Enable LLDP transmission on interface. + type: bool + med_tlv_select: + description: + - Selection of LLDP MED TLVs to send + - NOTE, if med-tlv-select is configured idempotency won't be maintained as + Cisco device doesn't record configured med-tlv-select options. As such, + Ansible cannot verify if the respective med-tlv-select options is already + configured or not from the device side. If you try to apply med-tlv-select + option in every play run, Ansible will show changed as True. + type: dict + suboptions: + inventory_management: + description: + - LLDP MED Inventory Management TLV + type: bool + tlv_select: + description: + - Selection of LLDP type-length-value i.e. TLVs to send + - NOTE, if tlv-select is configured idempotency won't be maintained as Cisco + device doesn't record configured tlv-select options. As such, Ansible cannot + verify if the respective tlv-select options is already configured or not + from the device side. If you try to apply tlv-select option in every play + run, Ansible will show changed as True. + type: dict + suboptions: + power_management: + description: + - IEEE 802.3 DTE Power via MDI TLV + type: bool + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by + executing the command B(sh lldp 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 + state: + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + default: merged +""" + +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# + +- name: Merge provided configuration with device configuration + cisco.ios.ios_lldp_interfaces: + config: + - name: GigabitEthernet0/1 + receive: true + transmit: true + - name: GigabitEthernet0/2 + receive: true + - name: GigabitEthernet0/3 + transmit: true + state: merged + +# After state: +# ------------ +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: enabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: enabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# + +# Using overridden +# +# Before state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +- name: Override device configuration of all lldp_interfaces with provided configuration + cisco.ios.ios_lldp_interfaces: + config: + - name: GigabitEthernet0/2 + receive: true + transmit: true + state: overridden + +# After state: +# ------------ +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +# Using replaced +# +# Before state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# + +- name: Replaces device configuration of listed lldp_interfaces with provided configuration + cisco.ios.ios_lldp_interfaces: + config: + - name: GigabitEthernet0/2 + receive: true + transmit: true + - name: GigabitEthernet0/3 + receive: true + state: replaced + +# After state: +# ------------ +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: disabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# + +# Using Deleted +# +# Before state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +- name: "Delete LLDP attributes of given interfaces (Note: This won't delete the interface itself)" + cisco.ios.ios_lldp_interfaces: + config: + - name: GigabitEthernet0/1 + state: deleted + +# After state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# + +# Using Deleted without any config passed +# "(NOTE: This will delete all of configured LLDP module attributes)" +# +# Before state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +- name: "Delete LLDP attributes for all configured interfaces (Note: This won't delete the interface itself)" + cisco.ios.ios_lldp_interfaces: + state: deleted + +# After state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: INIT +# +# GigabitEthernet0/3: +# Tx: disabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +# Using Gathered + +# Before state: +# ------------- +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +- name: Gather listed LLDP interfaces with provided configurations + cisco.ios.ios_lldp_interfaces: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "name": "GigabitEthernet0/0", +# "receive": true, +# "transmit": true +# }, +# { +# "name": "GigabitEthernet0/1", +# "receive": true, +# "transmit": true +# }, +# { +# "name": "GigabitEthernet0/2", +# "receive": true, +# "transmit": true +# } +# ] + +# After state: +# ------------ +# +# vios#sh lldp interface +# GigabitEthernet0/0: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +# GigabitEthernet0/2: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_lldp_interfaces: + config: + - name: GigabitEthernet0/0 + receive: true + transmit: true + - name: GigabitEthernet0/1 + receive: true + transmit: true + - name: GigabitEthernet0/2 + receive: true + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "interface GigabitEthernet0/0", +# "lldp receive", +# "lldp transmit", +# "interface GigabitEthernet0/1", +# "lldp receive", +# "lldp transmit", +# "interface GigabitEthernet0/2", +# "lldp receive" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# GigabitEthernet0/0: +# Tx: enabled +# Rx: disabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/1: +# Tx: enabled +# Rx: enabled +# Tx state: IDLE +# Rx state: WAIT FOR FRAME +# +# GigabitEthernet0/2: +# Tx: disabled +# Rx: enabled +# Tx state: IDLE +# Rx state: INIT + +- name: Parse the commands for provided configuration + cisco.ios.ios_lldp_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "name": "GigabitEthernet0/0", +# "receive": false, +# "transmit": true +# }, +# { +# "name": "GigabitEthernet0/1", +# "receive": true, +# "transmit": true +# }, +# { +# "name": "GigabitEthernet0/2", +# "receive": true, +# "transmit": false +# } +# ] +""" + +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 GigabitEthernet 0/1', 'lldp transmit', 'lldp receive'] +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.lldp_interfaces.lldp_interfaces import ( + Lldp_InterfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.lldp_interfaces.lldp_interfaces import ( + Lldp_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=Lldp_InterfacesArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = Lldp_Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_logging.py b/ansible_collections/cisco/ios/plugins/modules/ios_logging.py new file mode 100644 index 000000000..492e94d85 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_logging.py @@ -0,0 +1,498 @@ +#!/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: ios_logging +author: Trishna Guha (@trishnaguha) +short_description: (deprecated, removed after 2023-06-01) Manage logging on network devices +description: + - This module provides declarative management of logging on Cisco Ios devices. +version_added: 1.0.0 +deprecated: + alternative: ios_logging_global + why: Newer and updated modules released with more functionality. + removed_at_date: "2023-06-01" +notes: + - Tested against IOS 15.6 + - The 'Default System Message Logging Configuration' of the ios device + like facility Local7 or logging on is not subjected to idempotency causes +options: + dest: + description: + - Destination of the logs. + - On dest has to be quoted as 'on' or else pyyaml will convert + to True before it gets to Ansible. + choices: + - "on" + - host + - console + - monitor + - buffered + - trap + type: str + name: + description: + - The hostname or IP address of the destination. + - Required when I(dest=host). + type: str + size: + description: + - Size of buffer. The acceptable value is in range from 4096 to 4294967295 bytes. + type: int + facility: + description: + - Set logging facility. + type: str + level: + description: + - Set logging severity levels. + default: debugging + choices: + - emergencies + - alerts + - critical + - errors + - warnings + - notifications + - informational + - debugging + type: str + aggregate: + description: List of logging definitions. + type: list + elements: dict + suboptions: + dest: + description: + - Destination of the logs. + - On dest has to be quoted as 'on' or else pyyaml will convert + to True before it gets to Ansible. + choices: + - "on" + - host + - console + - monitor + - buffered + - trap + type: str + name: + description: + - The hostname or IP address of the destination. + - Required when I(dest=host). + type: str + size: + description: + - Size of buffer. The acceptable value is in range from 4096 to 4294967295 bytes. + type: int + facility: + description: + - Set logging facility. + type: str + level: + description: + - Set logging severity levels. + type: str + choices: + - emergencies + - alerts + - critical + - errors + - warnings + - notifications + - informational + - debugging + state: + description: + - State of the logging configuration. + choices: + - present + - absent + type: str + state: + description: + - State of the logging configuration. + default: present + choices: + - present + - absent + type: str +extends_documentation_fragment: + - cisco.ios.ios +""" + +EXAMPLES = """ +- name: Configure host logging + cisco.ios.ios_logging: + dest: host + name: 172.16.0.1 + state: present + +- name: Remove host logging configuration + cisco.ios.ios_logging: + dest: host + name: 172.16.0.1 + state: absent + +- name: Configure console logging level and facility + cisco.ios.ios_logging: + dest: console + facility: local7 + level: debugging + state: present + +- name: Enable logging to all + cisco.ios.ios_logging: + dest: on + +- name: Configure buffer size + cisco.ios.ios_logging: + dest: buffered + size: 5000 + +- name: Configure logging using aggregate + cisco.ios.ios_logging: + aggregate: + - { dest: console, level: notifications } + - { dest: buffered, size: 9000 } + +- name: Remove logging using aggregate + cisco.ios.ios_logging: + aggregate: + - { dest: console, level: notifications } + - { dest: buffered, size: 9000 } + state: absent +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - logging facility local7 + - logging host 172.16.0.1 +""" +import re + +from copy import deepcopy + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.validation import check_required_if +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_default_spec, + validate_ip_address, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_capabilities, + get_config, + load_config, +) + + +def validate_size(value, module): + if value: + if not int(4096) <= int(value) <= int(4294967295): + module.fail_json(msg="size must be between 4096 and 4294967295") + else: + return value + + +def map_obj_to_commands(updates, module, os_version): + dest_group = "console", "monitor", "buffered", "on", "trap" + commands = list() + want, have = updates + for w in want: + dest = w["dest"] + name = w["name"] + size = w["size"] + facility = w["facility"] + level = w["level"] + state = w["state"] + del w["state"] + if facility: + w["dest"] = "facility" + if state == "absent" and w in have: + if dest: + if dest == "host": + if os_version.startswith("12."): + commands.append("no logging {0}".format(name)) + else: + commands.append("no logging host {0}".format(name)) + elif dest in dest_group: + commands.append("no logging {0}".format(dest)) + else: + module.fail_json( + msg="dest must be among console, monitor, buffered, host, on, trap", + ) + if facility: + commands.append("no logging facility {0}".format(facility)) + if state == "present" and w not in have: + if facility: + present = False + for entry in have: + if entry["dest"] == "facility" and entry["facility"] == facility: + present = True + if not present: + commands.append("logging facility {0}".format(facility)) + if dest == "host": + if os_version.startswith("12."): + commands.append("logging {0}".format(name)) + else: + commands.append("logging host {0}".format(name)) + elif dest == "on": + commands.append("logging on") + elif dest == "buffered" and size: + present = False + for entry in have: + if ( + entry["dest"] == "buffered" + and entry["size"] == size + and entry["level"] == level + ): + present = True + if not present: + if level and level != "debugging": + commands.append("logging buffered {0} {1}".format(size, level)) + else: + commands.append("logging buffered {0}".format(size)) + elif dest: + dest_cmd = "logging {0}".format(dest) + if level: + dest_cmd += " {0}".format(level) + commands.append(dest_cmd) + return commands + + +def parse_facility(line, dest): + facility = None + if dest == "facility": + match = re.search("logging facility (\\S+)", line, re.M) + if match: + facility = match.group(1) + return facility + + +def parse_size(line, dest): + size = None + if dest == "buffered": + match = re.search("logging buffered(?: (\\d+))?(?: [a-z]+)?", line, re.M) + if match: + if match.group(1) is not None: + size = match.group(1) + else: + size = "4096" + return size + + +def parse_name(line, dest): + if dest == "host": + match = re.search("logging host (\\S+)", line, re.M) + if match: + name = match.group(1) + else: + name = None + return name + + +def parse_level(line, dest): + level_group = ( + "emergencies", + "alerts", + "critical", + "errors", + "warnings", + "notifications", + "informational", + "debugging", + ) + if dest == "host": + level = "debugging" + else: + if dest == "buffered": + match = re.search("logging buffered(?: \\d+)?(?: ([a-z]+))?", line, re.M) + else: + match = re.search("logging {0} (\\S+)".format(dest), line, re.M) + if match and match.group(1) in level_group: + level = match.group(1) + else: + level = "debugging" + return level + + +def map_config_to_obj(module): + obj = [] + dest_group = ("console", "host", "monitor", "buffered", "on", "facility", "trap") + data = get_config(module, flags=["| include logging"]) + for line in data.split("\n"): + match = re.search("^logging (\\S+)", line, re.M) + if match: + if match.group(1) in dest_group: + dest = match.group(1) + obj.append( + { + "dest": dest, + "name": parse_name(line, dest), + "size": parse_size(line, dest), + "facility": parse_facility(line, dest), + "level": parse_level(line, dest), + }, + ) + elif validate_ip_address(match.group(1)): + dest = "host" + obj.append( + { + "dest": dest, + "name": match.group(1), + "size": parse_size(line, dest), + "facility": parse_facility(line, dest), + "level": parse_level(line, dest), + }, + ) + else: + ip_match = re.search("\\d+\\.\\d+\\.\\d+\\.\\d+", match.group(1), re.M) + if ip_match: + dest = "host" + obj.append( + { + "dest": dest, + "name": match.group(1), + "size": parse_size(line, dest), + "facility": parse_facility(line, dest), + "level": parse_level(line, dest), + }, + ) + return obj + + +def map_params_to_obj(module, required_if=None): + obj = [] + aggregate = module.params.get("aggregate") + if aggregate: + for item in aggregate: + for key in item: + if item.get(key) is None: + item[key] = module.params[key] + try: + check_required_if(required_if, item) + except TypeError as exc: + module.fail_json(to_text(exc)) + d = item.copy() + if d["dest"] != "host": + d["name"] = None + if d["dest"] == "buffered": + if "size" in d: + d["size"] = str(validate_size(d["size"], module)) + elif "size" not in d: + d["size"] = str(4096) + else: + pass + if d["dest"] != "buffered": + d["size"] = None + obj.append(d) + else: + if module.params["dest"] != "host": + module.params["name"] = None + if module.params["dest"] == "buffered": + if not module.params["size"]: + module.params["size"] = str(4096) + else: + module.params["size"] = None + if module.params["size"] is None: + obj.append( + { + "dest": module.params["dest"], + "name": module.params["name"], + "size": module.params["size"], + "facility": module.params["facility"], + "level": module.params["level"], + "state": module.params["state"], + }, + ) + else: + obj.append( + { + "dest": module.params["dest"], + "name": module.params["name"], + "size": str(validate_size(module.params["size"], module)), + "facility": module.params["facility"], + "level": module.params["level"], + "state": module.params["state"], + }, + ) + return obj + + +def main(): + """main entry point for module execution""" + element_spec = dict( + dest=dict(type="str", choices=["on", "host", "console", "monitor", "buffered", "trap"]), + name=dict(type="str"), + size=dict(type="int"), + facility=dict(type="str"), + level=dict( + type="str", + default="debugging", + choices=[ + "emergencies", + "alerts", + "critical", + "errors", + "warnings", + "notifications", + "informational", + "debugging", + ], + ), + state=dict(default="present", choices=["present", "absent"]), + ) + aggregate_spec = deepcopy(element_spec) + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + argument_spec = dict(aggregate=dict(type="list", elements="dict", options=aggregate_spec)) + argument_spec.update(element_spec) + required_if = [("dest", "host", ["name"])] + module = AnsibleModule( + argument_spec=argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + device_info = get_capabilities(module) + os_version = device_info["device_info"]["network_os_version"] + warnings = list() + result = {"changed": False} + if warnings: + result["warnings"] = warnings + want = map_params_to_obj(module, required_if=required_if) + have = map_config_to_obj(module) + commands = map_obj_to_commands((want, have), module, os_version) + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_logging_global.py b/ansible_collections/cisco/ios/plugins/modules/ios_logging_global.py new file mode 100644 index 000000000..a77ca1115 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_logging_global.py @@ -0,0 +1,1084 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_logging_global +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_logging_global +version_added: 2.2.0 +short_description: Resource module to configure logging. +description: This module manages the logging attributes of Cisco IOS network devices +author: Sagar Paul (@KB-perByte) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) + - The Configuration defaults of the Cisco IOS network devices + are supposed to hinder idempotent behavior of plays +options: + config: + description: A dictionary of logging options + type: dict + suboptions: + buffered: + description: Set buffered logging parameters + type: dict + suboptions: + size: &size + description: Logging buffer size + type: int + severity: &severity + description: Logging severity level + type: str + choices: &severity_subgroup + - alerts + - critical + - debugging + - emergencies + - errors + - informational + - notifications + - warnings + discriminator: &discriminator + description: Establish MD-Buffer association + type: str + filtered: &filtered + description: Enable filtered logging + type: bool + xml: &xml + description: Enable logging in XML to XML logging buffer + type: bool + buginf: + description: Enable buginf logging for debugging + type: bool + cns_events: + description: Set CNS Event logging level + type: str + choices: *severity_subgroup + console: + description: Set console logging parameters + type: dict + suboptions: + severity: + description: Logging severity level + type: str + choices: + [ + "alerts", + "critical", + "debugging", + "emergencies", + "errors", + "informational", + "notifications", + "warnings", + "guaranteed", + ] + discriminator: *discriminator + filtered: *filtered + xml: *xml + count: + description: Count every log message and timestamp last occurrence + type: bool + delimiter: + description: Append delimiter to syslog messages + type: dict + suboptions: + tcp: + description: Append delimiter to syslog messages over TCP + type: bool + discriminator: + description: Create or modify a message discriminator + type: list + elements: str + dmvpn: + description: DMVPN Configuration + type: dict + suboptions: + rate_limit: &rate_limit + description: rate in messages/minute, default is 600 messages/minute (1-10000) + type: int + esm: + description: Set ESM filter restrictions + type: dict + suboptions: + config: + description: Permit/Deny configuration changes from ESM filters + type: bool + exception: + description: Limit size of exception flush output (4096-2147483647) + type: int + facility: + description: Facility parameter for syslog messages + type: str + choices: + - auth + - cron + - daemon + - kern + - local0 + - local1 + - local2 + - local3 + - local4 + - local5 + - local6 + - local7 + - lpr + - mail + - news + - sys10 + - sys11 + - sys12 + - sys13 + - sys14 + - sys9 + - syslog + - user + - uucp + filter: + description: Specify logging filter + type: list + elements: dict + suboptions: + url: + description: Filter Uniform Resource Locator + type: str + order: + description: Order of filter execution + type: int + args: + description: Arguments passed to filter module. + type: str + history: + description: Configure syslog history table + type: dict + suboptions: + size: *size + severity: *severity + hosts: + description: Set syslog server IP address and parameters + type: list + elements: dict + suboptions: + discriminator: *discriminator + filtered: *filtered + sequence_num_session: &sequence_num_session + description: Include session sequence number tag in syslog message + type: bool + session_id: &session_id + description: Specify syslog message session ID tagging + type: dict + suboptions: &session_id_suboptions + tag: + description: Include hostname in session ID tag + type: str + choices: ["hostname", "ipv4", "ipv6"] + text: + description: Include custom string in session ID tag + type: str + stream: &stream + description: This server should only receive messages from a numbered stream + type: int + transport: &transport + description: Specify the transport protocol (default=UDP) + type: dict + suboptions: + tcp: + description: Transport Control Protocol + type: dict + suboptions: + audit: + description: Set this host for IOS firewall audit logging + type: bool + discriminator: *discriminator + stream: *stream + filtered: *filtered + port: + description: Specify the TCP port number (default=601) (1 - 65535) + type: int + sequence_num_session: *sequence_num_session + session_id: *session_id + xml: *xml + udp: + description: User Datagram Protocol + type: dict + suboptions: + discriminator: *discriminator + stream: *stream + filtered: *filtered + port: + description: Specify the UDP port number (default=514) (1 - 65535) + type: int + sequence_num_session: *sequence_num_session + session_id: *session_id + xml: *xml + vrf: + description: Set VRF option + type: str + xml: *xml + ipv6: + description: Configure IPv6 syslog server + type: str + host: + description: IP address of the syslog server + type: str + aliases: + - hostname + message_counter: + description: Configure log message to include certain counter value + type: list + elements: str + choices: ["log", "debug", "syslog"] + monitor: + description: Set terminal line (monitor) logging parameters + type: dict + suboptions: + severity: *severity + discriminator: *discriminator + filtered: *filtered + xml: *xml + logging_on: + description: Enable logging to all enabled destinations + type: str + choices: ["enable", "disable"] + origin_id: + description: Add origin ID to syslog messages + type: dict + suboptions: + tag: + description: Include hostname in session ID tag + type: str + choices: ["hostname", "ip", "ipv6"] + text: + description: Include custom string in session ID tag + type: str + persistent: + description: Set persistent logging parameters + type: dict + suboptions: + batch: + description: Set batch size for writing to persistent storage (4096-2142715904) + type: int + filesize: + description: Set size of individual log files (4096-2142715904) + type: int + immediate: + description: Write log entry to storage immediately (no buffering). + type: bool + notify: + description: Notify when show logging [persistent] is activated. + type: bool + protected: + description: Eliminates manipulation on logging-persistent files. + type: bool + size: + description: Set disk space for writing log messages (4096-2142715904) + type: int + threshold: + description: Set threshold for logging persistent + type: int + url: + description: URL to store logging messages + type: str + policy_firewall: + description: Firewall configuration + type: dict + suboptions: + rate_limit: + description: (0-3600) value in seconds, default is 30 Sec. + type: int + queue_limit: + description: Set logger message queue size + type: dict + suboptions: + size: + description: (100-2147483647) set new queue size + type: int + esm: + description: (100-2147483647) set new queue size + type: int + trap: + description: (100-2147483647) set new queue size + type: int + rate_limit: + description: Set messages per second limit + type: dict + suboptions: + size: &rate_limit_size + description: (1-10000) message per second + type: int + required: true + all: + description: (1-10000) message per second + type: bool + console: + description: (1-10000) message per second + type: bool + except_severity: + description: Messages of this severity or higher + type: str + choices: *severity_subgroup + reload: + description: Set reload logging level + type: dict + suboptions: + severity: *severity + message_limit: + description: Number of messages (1-4294967295) + type: int + server_arp: + description: Enable sending ARP requests for syslog servers when first configured + type: bool + snmp_trap: + description: Set syslog level for sending snmp trap + type: list + elements: str + choices: *severity_subgroup + source_interface: + description: Specify interface for source address in logging transactions + type: list + elements: dict + suboptions: + interface: + description: Interface name with number + type: str + vrf: + description: VPN Routing/Forwarding instance name + type: str + trap: + description: Set syslog server logging level + type: str + choices: *severity_subgroup + userinfo: + description: Enable logging of user info on privileged mode enabling + type: bool + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by + executing the command B(show running-config | include logging). + - 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 + state: + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged + description: + - The state the configuration should be left in + - With state I(replaced), for the listed logging configurations, + that are in running-config and can have multiple set of commands + but not in the task are negated. + - With state I(overridden), all configurations that are in running-config but + not in the task are negated. + - Please refer to examples for more details. + type: str +""" + +EXAMPLES = """ +# Using state: merged + +# Before state: +# ------------- + +# router-ios#show running-config | section logging +# no logging exception +# no logging buffered +# no logging reload +# no logging rate-limit +# no logging console +# no logging monitor +# no logging cns-events +# no logging trap + +- name: Apply the provided configuration + cisco.ios.ios_logging_global: + config: + buffered: + severity: notifications + size: 5099 + xml: true + console: + severity: critical + xml: true + facility: local5 + hosts: + - hostname: 172.16.1.12 + - hostname: 172.16.1.11 + xml: true + - hostname: 172.16.1.10 + filtered: true + stream: 10 + - hostname: 172.16.1.13 + transport: + tcp: + port: 514 + monitor: + severity: warnings + message_counter: log + snmp_trap: + - errors + trap: errors + userinfo: true + policy_firewall: + rate_limit: 10 + logging_on: enable + exception: 4099 + dmvpn: + rate_limit: 10 + cns_events: warnings + state: merged + +# Commands Fired: +# --------------- + +# "commands": [ +# "logging buffered xml 5099 notifications", +# "logging cns-events warnings", +# "logging console xml critical", +# "logging dmvpn rate-limit 10", +# "logging exception 4099", +# "logging facility local5", +# "logging monitor warnings", +# "logging on", +# "logging policy-firewall rate-limit 10", +# "logging trap errors", +# "logging userinfo", +# "logging host 172.16.1.12", +# "logging host 172.16.1.10 filtered stream 10", +# "logging host 172.16.1.13 transport tcp port 514", +# "logging message-counter log", +# "logging snmp-trap errors", +# "logging host 172.16.1.11 xml" +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section logging +# logging exception 4099 +# logging message-counter log +# logging userinfo +# logging buffered xml 5099 notifications +# no logging reload +# no logging rate-limit +# logging console xml critical +# logging monitor warnings +# logging cns-events warnings +# logging policy-firewall rate-limit 10 +# logging dmvpn rate-limit 10 +# logging trap errors +# logging facility local5 +# logging snmp-trap errors +# logging snmp-trap warnings +# logging host 172.16.1.13 transport tcp port 514 +# logging host 172.16.1.11 xml +# logging host 172.16.1.12 +# logging host 172.16.1.10 filtered stream 10 + +# Using state: deleted + +# Before state: +# ------------- + +# router-ios#show running-config | section logging +# logging exception 4099 +# logging message-counter log +# logging userinfo +# logging buffered xml 5099 notifications +# no logging reload +# no logging rate-limit +# logging console xml critical +# logging monitor warnings +# logging cns-events warnings +# logging policy-firewall rate-limit 10 +# logging dmvpn rate-limit 10 +# logging trap errors +# logging facility local5 +# logging snmp-trap errors +# logging host 172.16.1.13 transport tcp port 514 +# logging host 172.16.1.11 xml +# logging host 172.16.1.12 +# logging host 172.16.1.10 filtered stream 10 + +- name: Remove all existing configuration + cisco.ios.ios_logging_global: + state: deleted + +# Commands Fired: +# --------------- + +# "commands": [ +# "no logging message-counter log", +# "no logging snmp-trap errors", +# "no logging host 172.16.1.13", +# "no logging host 172.16.1.11", +# "no logging host 172.16.1.12", +# "no logging host 172.16.1.10", +# "no logging exception 4099", +# "no logging userinfo", +# "no logging buffered xml 5099 notifications", +# "no logging console xml critical", +# "no logging monitor warnings", +# "no logging cns-events warnings", +# "no logging policy-firewall rate-limit 10", +# "no logging dmvpn rate-limit 10", +# "no logging trap errors", +# "no logging facility local5" +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section logging +# no logging exception +# no logging buffered +# no logging reload +# no logging rate-limit +# no logging console +# no logging monitor +# no logging cns-events +# no logging trap + +# Using state: overridden + +# Before state: +# ------------- + +# router-ios#show running-config | section logging +# logging exception 4099 +# logging message-counter log +# logging userinfo +# logging buffered 6000 critical +# no logging reload +# no logging rate-limit +# logging console xml critical +# logging monitor warnings +# logging cns-events warnings +# logging policy-firewall rate-limit 10 +# logging dmvpn rate-limit 10 +# logging trap errors +# logging facility local6 +# logging host 172.16.1.13 transport tcp port 514 +# logging host 172.16.1.12 +# logging host 172.16.1.10 filtered stream 10 +# logging host 172.16.1.25 filtered + +- name: Override commands with provided configuration + cisco.ios.ios_logging_global: + config: + hosts: + - hostname: 172.16.1.27 + filtered: true + state: overridden + +# Commands Fired: +# --------------- +# "commands": [ +# "no logging message-counter log", +# "no logging host 172.16.1.12", +# "no logging host 172.16.1.10", +# "no logging host 172.16.1.13", +# "no logging exception 4099", +# "no logging userinfo", +# "no logging console xml critical", +# "no logging monitor warnings", +# "no logging cns-events warnings", +# "no logging policy-firewall rate-limit 10", +# "no logging dmvpn rate-limit 10", +# "no logging trap errors", +# "no logging buffered 6000 critical", +# "no logging facility local6", +# "logging host 172.16.1.27 filtered", +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section logging +# no logging exception +# no logging buffered +# no logging reload +# no logging rate-limit +# no logging console +# no logging monitor +# no logging cns-events +# no logging trap +# logging host 172.16.1.27 filtered + +# Using state: replaced + +# Before state: +# ------------- + +# router-ios#show running-config | section logging +# logging exception 4099 +# logging message-counter log +# logging userinfo +# logging buffered xml 5099 notifications +# no logging reload +# no logging rate-limit +# logging console xml critical +# logging monitor warnings +# logging cns-events warnings +# logging policy-firewall rate-limit 10 +# logging dmvpn rate-limit 10 +# logging trap errors +# logging facility local5 +# logging snmp-trap errors +# logging host 172.16.1.13 transport tcp port 514 +# logging host 172.16.1.11 xml +# logging host 172.16.1.12 +# logging host 172.16.1.10 filtered stream 10 + +- name: Replace commands with provided configuration + cisco.ios.ios_logging_global: + config: + buffered: + severity: alerts + size: 6025 + facility: local6 + hosts: + - hostname: 172.16.1.19 + - hostname: 172.16.1.10 + filtered: true + stream: 15 + state: replaced + +# Commands Fired: +# --------------- + +# "commands": [ +# "no logging host 172.16.1.13", +# "no logging host 172.16.1.11", +# "no logging host 172.16.1.12", +# "no logging host 172.16.1.10", +# "logging host 172.16.1.19", +# "logging host 172.16.1.10 filtered stream 15", +# "logging buffered 6025 alerts", +# "logging facility local6" +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section logging +# logging exception 4099 +# logging message-counter log +# logging userinfo +# logging buffered 6025 alerts +# no logging reload +# no logging rate-limit +# logging console xml critical +# logging monitor warnings +# logging cns-events warnings +# logging policy-firewall rate-limit 10 +# logging dmvpn rate-limit 10 +# logging trap errors +# logging facility local6 +# logging snmp-trap errors +# logging host 172.16.1.19 + +# Using state: gathered + +# Before state: +# ------------- + +#router-ios#show running-config | section logging +# logging exception 4099 +# logging message-counter log +# logging userinfo +# logging buffered xml 5099 notifications +# no logging reload +# no logging rate-limit +# logging console xml critical +# logging monitor warnings +# logging cns-events warnings +# logging policy-firewall rate-limit 10 +# logging dmvpn rate-limit 10 +# logging trap errors +# logging facility local5 +# logging snmp-trap errors +# logging host 172.16.1.13 transport tcp port 514 +# logging host 172.16.1.11 xml +# logging host 172.16.1.12 +# logging host 172.16.1.10 filtered stream 10 +# logging host 172.16.1.25 filtered + +- name: Gather listed logging config + cisco.ios.ios_logging_global: + state: gathered + +# Module Execution Result: +# ------------------------ + +# "gathered": { +# "buffered": { +# "severity": "notifications", +# "size": 5099, +# "xml": true +# }, +# "cns_events": "warnings", +# "console": { +# "severity": "critical", +# "xml": true +# }, +# "dmvpn": { +# "rate_limit": 10 +# }, +# "exception": 4099, +# "facility": "local5", +# "hosts": [ +# { +# "hostname": "172.16.1.11", +# "xml": true +# }, +# { +# "hostname": "172.16.1.12" +# }, +# { +# "filtered": true, +# "hostname": "172.16.1.10", +# "stream": 10 +# }, +# { +# "hostname": "172.16.1.13", +# "transport": { +# "tcp": { +# "port": 514 +# } +# } +# }, +# { +# "filtered": true, +# "hostname": "172.16.1.25" +# } +# ], +# "message_counter": [ +# "log" +# ], +# "monitor": { +# "severity": "warnings" +# }, +# "policy_firewall": { +# "rate_limit": 10 +# }, +# "snmp_trap": [ +# "errors" +# ], +# "trap": "errors", +# "userinfo": true +# }, + +# After state: +# ------------- + +# router-ios#show running-config | section logging +# logging exception 4099 +# logging message-counter log +# logging userinfo +# logging buffered xml 5099 notifications +# no logging reload +# no logging rate-limit +# logging console xml critical +# logging monitor warnings +# logging cns-events warnings +# logging policy-firewall rate-limit 10 +# logging dmvpn rate-limit 10 +# logging trap errors +# logging facility local5 +# logging snmp-trap errors +# logging host 172.16.1.13 transport tcp port 514 +# logging host 172.16.1.11 xml +# logging host 172.16.1.12 +# logging host 172.16.1.10 filtered stream 10 +# logging host 172.16.1.25 filtered + +# Using state: rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_logging_global: + config: + buffered: + severity: notifications + size: 5099 + xml: true + console: + severity: critical + xml: true + facility: local5 + hosts: + - hostname: 172.16.1.12 + - hostname: 172.16.1.11 + xml: true + - hostname: 172.16.1.10 + filtered: true + stream: 10 + - hostname: 172.16.1.13 + transport: + tcp: + port: 514 + monitor: + severity: warnings + message_counter: log + snmp_trap: errors + trap: errors + userinfo: true + policy_firewall: + rate_limit: 10 + logging_on: enable + exception: 10 + dmvpn: + rate_limit: 10 + cns_events: warnings + state: rendered + +# Module Execution Result: +# ------------------------ + +# "rendered": [ +# "logging host 172.16.1.12", +# "logging host 172.16.1.11 xml", +# "logging host 172.16.1.10 filtered stream 10", +# "logging host 172.16.1.13 transport tcp port 514", +# "logging message-counter log", +# "logging snmp-trap errors", +# "logging buffered xml 5099 notifications", +# "logging console xml critical", +# "logging facility local5", +# "logging monitor warnings", +# "logging trap errors", +# "logging userinfo", +# "logging policy-firewall rate-limit 10", +# "logging on", +# "logging exception 10", +# "logging dmvpn rate-limit 10", +# "logging cns-events warnings" +# ] + +# Using state: parsed + +# File: parsed.cfg +# ---------------- + +# logging on +# logging count +# logging userinfo +# logging trap errors +# logging reload alerts +# logging host 172.16.1.1 +# logging exception 4099 +# logging history alerts +# logging facility local5 +# logging snmp-trap errors +# logging monitor warnings +# logging origin-id hostname +# logging host 172.16.1.11 xml +# logging cns-events warnings +# logging dmvpn rate-limit 10 +# logging message-counter log +# logging console xml critical +# logging message-counter debug +# logging persistent batch 4444 +# logging host 172.16.1.25 filtered +# logging source-interface GBit1/0 +# logging source-interface CTunnel2 +# logging policy-firewall rate-limit 10 +# logging buffered xml 5099 notifications +# logging rate-limit all 2 except warnings +# logging host 172.16.1.10 filtered stream 10 +# logging host 172.16.1.13 transport tcp port 514 +# logging discriminator msglog01 severity includes 5 +# logging filter tftp://172.16.2.18/ESM/elate.tcl args TESTInst2 +# logging filter tftp://172.16.2.14/ESM/escalate.tcl args TESTInst + +- name: Parse the provided configuration with the existing running configuration + cisco.ios.ios_logging_global: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ + +# "parsed": { +# "buffered": { +# "severity": "notifications", +# "size": 5099, +# "xml": true +# }, +# "cns_events": "warnings", +# "console": { +# "severity": "critical", +# "xml": true +# }, +# "count": true, +# "discriminator": [ +# "msglog01 severity includes 5" +# ], +# "dmvpn": { +# "rate_limit": 10 +# }, +# "exception": 4099, +# "facility": "local5", +# "filter": [ +# { +# "args": "TESTInst2", +# "url": "tftp://172.16.2.18/ESM/elate.tcl" +# }, +# { +# "args": "TESTInst", +# "url": "tftp://172.16.2.14/ESM/escalate.tcl" +# } +# ], +# "history": { +# "severity": "alerts" +# }, +# "hosts": [ +# { +# "hostname": "172.16.1.1" +# }, +# { +# "hostname": "172.16.1.11", +# "xml": true +# }, +# { +# "filtered": true, +# "hostname": "172.16.1.25" +# }, +# { +# "filtered": true, +# "hostname": "172.16.1.10", +# "stream": 10 +# }, +# { +# "hostname": "172.16.1.13", +# "transport": { +# "tcp": { +# "port": 514 +# } +# } +# } +# ], +# "logging_on": "enable", +# "message_counter": [ +# "log", +# "debug" +# ], +# "monitor": { +# "severity": "warnings" +# }, +# "origin_id": { +# "tag": "hostname" +# }, +# "persistent": { +# "batch": 4444 +# }, +# "policy_firewall": { +# "rate_limit": 10 +# }, +# "rate_limit": { +# "all": true, +# "except_severity": "warnings", +# "size": 2 +# }, +# "reload": { +# "severity": "alerts" +# }, +# "snmp_trap": [ +# "errors" +# ], +# "source_interface": [ +# { +# "interface": "GBit1/0" +# }, +# { +# "interface": "CTunnel2" +# } +# ], +# "trap": "errors", +# "userinfo": true +# } +""" + +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: + - "logging on" + - "logging userinfo" + - "logging trap errors" + - "logging host 172.16.1.12" + - "logging console xml critical" + - "logging message-counter log" + - "logging policy-firewall rate-limit 10" +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.logging_global.logging_global import ( + Logging_globalArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.logging_global.logging_global import ( + Logging_global, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Logging_globalArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Logging_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_ntp.py b/ansible_collections/cisco/ios/plugins/modules/ios_ntp.py new file mode 100644 index 000000000..2eeb2b088 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_ntp.py @@ -0,0 +1,356 @@ +#!/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: ios_ntp +extends_documentation_fragment: + - cisco.ios.ios +short_description: (deprecated, removed after 2024-01-01) Manages core NTP configuration. +description: + - Manages core NTP configuration. +version_added: 1.0.0 +deprecated: + alternative: ios_ntp_global + why: Updated module released with more functionality. + removed_at_date: "2024-01-01" +author: + - Federico Olivieri (@Federico87) + - Joanie Sylvain (@JoanieAda) +options: + server: + description: + - Network address of NTP server. + type: str + source_int: + description: + - Source interface for NTP packets. + type: str + acl: + description: + - ACL for peer/server access restricition. + type: str + logging: + description: + - Enable NTP logs. Data type boolean. + type: bool + default: false + auth: + description: + - Enable NTP authentication. Data type boolean. + type: bool + default: false + auth_key: + description: + - md5 NTP authentication key of tye 7. + type: str + key_id: + description: + - auth_key id. Data type string + type: str + state: + description: + - Manage the state of the resource. + default: present + choices: + - present + - absent + type: str + vrf: + description: + - VRF configuration for NTP servers + type: str +""" + +EXAMPLES = """ +# Set new NTP server and source interface +- name: Example ntp play + cisco.ios.ios_ntp: + server: 10.0.255.10 + source_int: Loopback0 + logging: false + state: present + +# Remove NTP ACL and logging +- name: Example ntp play absent + cisco.ios.ios_ntp: + acl: NTP_ACL + logging: true + state: absent + +# Set NTP authentication +- name: Example ntp play auth + cisco.ios.ios_ntp: + key_id: 10 + auth_key: 15435A030726242723273C21181319000A + auth: true + state: present + +# Set new NTP configuration +- name: Example ntp play auth + cisco.ios.ios_ntp: + server: 10.0.255.10 + source_int: Loopback0 + acl: NTP_ACL + logging: true + vrf: mgmt + key_id: 10 + auth_key: 15435A030726242723273C21181319000A + auth: true + state: present + +""" +RETURN = """ +commands: + description: command sent to the device + returned: always + type: list + sample: ["no ntp server 10.0.255.10", "no ntp source Loopback0"] +""" +import re + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) + + +def parse_server(line, dest): + if dest == "server": + vrf, server = None, None + match = re.search( + "(ntp\\sserver\\s)(vrf\\s\\w+\\s)?(\\d+\\.\\d+\\.\\d+\\.\\d+)", + line, + re.M, + ) + + if match and match.group(2) and match.group(3): + vrf = match.group(2) + server = match.group(3) + return vrf, server + + if match and match.group(3): + server = match.group(3) + return vrf, server + + +def parse_source_int(line, dest): + if dest == "source": + match = re.search("(ntp\\ssource\\s)(\\S+)", line, re.M) + if match: + source = match.group(2) + return source + + +def parse_acl(line, dest): + if dest == "access-group": + match = re.search("ntp\\saccess-group\\s(?:peer|serve)(?:\\s+)(\\S+)", line, re.M) + if match: + acl = match.group(1) + return acl + + +def parse_logging(line, dest): + if dest == "logging": + logging = dest + return logging + + +def parse_auth_key(line, dest): + if dest == "authentication-key": + match = re.search("(ntp\\sauthentication-key\\s\\d+\\smd5\\s)(\\w+)", line, re.M) + if match: + auth_key = match.group(2) + return auth_key + + +def parse_key_id(line, dest): + if dest == "trusted-key": + match = re.search("(ntp\\strusted-key\\s)(\\d+)", line, re.M) + if match: + auth_key = match.group(2) + return auth_key + + +def parse_auth(dest): + if dest == "authenticate": + return dest + + +def map_config_to_obj(module): + obj_dict = dict() + obj = list() + server_list = list() + config = get_config(module, flags=["| include ntp"]) + for line in config.splitlines(): + match = re.search("ntp\\s(\\S+)", line, re.M) + + if match: + dest = match.group(1) + server = parse_server(line, dest) + source_int = parse_source_int(line, dest) + acl = parse_acl(line, dest) + logging = parse_logging(line, dest) + auth = parse_auth(dest) + auth_key = parse_auth_key(line, dest) + key_id = parse_key_id(line, dest) + + if server: + if server[0] is None: + server_list.append((server[0], server[1])) + else: + server_list.append((server[0].split()[1], server[1])) + if source_int: + obj_dict["source_int"] = source_int + if acl: + obj_dict["acl"] = acl + if logging: + obj_dict["logging"] = True + if auth: + obj_dict["auth"] = True + if auth_key: + obj_dict["auth_key"] = auth_key + if key_id: + obj_dict["key_id"] = key_id + obj_dict["server"] = server_list + obj.append(obj_dict) + return obj + + +def map_params_to_obj(module): + obj = list() + + obj.append( + { + "state": module.params["state"], + "server": module.params["server"], + "source_int": module.params["source_int"], + "logging": module.params["logging"], + "acl": module.params["acl"], + "auth": module.params["auth"], + "auth_key": module.params["auth_key"], + "key_id": module.params["key_id"], + "vrf": module.params["vrf"], + }, + ) + + return obj + + +def map_obj_to_commands(want, have, module): + commands = list() + + server_have = have[0].get("server", None) + source_int_have = have[0].get("source_int", None) + acl_have = have[0].get("acl", None) + logging_have = have[0].get("logging", None) + auth_have = have[0].get("auth", None) + auth_key_have = have[0].get("auth_key", None) + key_id_have = have[0].get("key_id", None) + + for w in want: + server = w["server"] + source_int = w["source_int"] + acl = w["acl"] + logging = w["logging"] + state = w["state"] + auth = w["auth"] + auth_key = w["auth_key"] + key_id = w["key_id"] + vrf = w["vrf"] + if vrf == "": + vrf = None + + if state == "absent": + if server_have and (vrf, server) in server_have: + if vrf is not None: + commands.append("no ntp server vrf {0} {1}".format(vrf, server)) + else: + commands.append("no ntp server {0}".format(server)) + if source_int and source_int_have: + commands.append("no ntp source {0}".format(source_int)) + if acl and acl_have: + commands.append("no ntp access-group peer {0}".format(acl)) + if logging is True and logging_have: + commands.append("no ntp logging") + if auth is True and auth_have: + commands.append("no ntp authenticate") + if key_id and key_id_have: + commands.append("no ntp trusted-key {0}".format(key_id)) + if auth_key and auth_key_have: + if key_id and key_id_have: + commands.append( + "no ntp authentication-key {0} md5 {1} 7".format(key_id, auth_key), + ) + elif state == "present": + if server is not None and (vrf, server) not in server_have: + if vrf is not None: + commands.append("ntp server vrf {0} {1}".format(vrf, server)) + else: + commands.append("ntp server {0}".format(server)) + if source_int is not None and source_int != source_int_have: + commands.append("ntp source {0}".format(source_int)) + if acl is not None and acl != acl_have: + commands.append("ntp access-group peer {0}".format(acl)) + if logging is not None and logging != logging_have and logging is not False: + commands.append("ntp logging") + if auth is not None and auth != auth_have and auth is not False: + commands.append("ntp authenticate") + if key_id is not None and key_id != key_id_have: + commands.append("ntp trusted-key {0}".format(key_id)) + if auth_key is not None and auth_key != auth_key_have: + if key_id is not None: + commands.append("ntp authentication-key {0} md5 {1} 7".format(key_id, auth_key)) + return commands + + +def main(): + argument_spec = dict( + server=dict(), + source_int=dict(), + acl=dict(), + logging=dict(type="bool", default=False), + auth=dict(type="bool", default=False), + auth_key=dict(no_log=True), + key_id=dict(), + state=dict(choices=["absent", "present"], default="present"), + vrf=dict(), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + result = {"changed": False} + warnings = list() + 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: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_ntp_global.py b/ansible_collections/cisco/ios/plugins/modules/ios_ntp_global.py new file mode 100644 index 000000000..87c37a25f --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_ntp_global.py @@ -0,0 +1,1097 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_ntp_global +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_ntp_global +short_description: Resource module to configure NTP. +description: + - This module provides declarative management of ntp on Cisco IOS devices. +version_added: 2.5.0 +author: + - Sagar Paul (@KB-perByte) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). +options: + config: + description: A dictionary of ntp options + type: dict + suboptions: + access_group: + description: Control NTP access + type: dict + suboptions: + peer: + description: Provide full access + type: list + elements: dict + suboptions: + access_list: &access_group_acl + description: name or number of access list + type: str + kod: &kod + description: Send a Kiss-o-Death packet for failing peers + type: bool + ipv4: &ipv4 + description: ipv4 access lists (Default not idempotent) + type: bool + ipv6: &ipv6 + description: ipv6 access lists (Default not idempotent) + type: bool + query_only: + description: Allow only control queries + type: list + elements: dict + suboptions: + access_list: *access_group_acl + kod: *kod + ipv4: *ipv4 + ipv6: *ipv6 + serve: + description: Provide server and query access + type: list + elements: dict + suboptions: + access_list: *access_group_acl + kod: *kod + ipv4: *ipv4 + ipv6: *ipv6 + serve_only: + description: Provide only server access + type: list + elements: dict + suboptions: + access_list: *access_group_acl + kod: *kod + ipv4: *ipv4 + ipv6: *ipv6 + allow: + description: Allow processing of packets + type: dict + suboptions: + control: + description: Allow processing control mode packets + type: dict + suboptions: + rate_limit: + description: Rate-limit delay. + type: int + private: + description: Allow processing private mode packets + type: bool + authenticate: + description: Authenticate time sources + type: bool + authentication_keys: + description: Authentication key for trusted time sources + type: list + elements: dict + suboptions: + id: + description: Key number + type: int + algorithm: + description: Authentication type + type: str + key: + description: Password + type: str + encryption: + description: Authentication key encryption type + type: int + broadcast_delay: + description: Estimated round-trip delay + type: int + clock_period: + description: Length of hardware clock tick, clock period in 2^-32 seconds + type: int + logging: + description: Enable NTP message logging + type: bool + master: + description: Act as NTP master clock + type: dict + suboptions: + enabled: + description: Enable master clock + type: bool + stratum: + description: Stratum number + type: int + max_associations: + description: Set maximum number of associations + type: int + max_distance: + description: Maximum Distance for synchronization + type: int + min_distance: + description: Minimum distance to consider for clockhop + type: int + orphan: + description: Threshold Stratum for orphan mode + type: int + panic_update: + description: Reject time updates > panic threshold (default 1000Sec) + type: bool + passive: + description: NTP passive mode + type: bool + peers: + description: Configure NTP peer + type: list + elements: dict + suboptions: + peer: + description: ipv4/ipv6 address or hostname of the peer + type: str + use_ipv4: &use_ipv4 + description: Use IP for DNS resolution + type: bool + use_ipv6: &use_ipv6 + description: Use IPv6 for DNS resolution + type: bool + vrf: &vrf + description: VPN Routing/Forwarding Information + type: str + burst: &burst + description: Send a burst when peer is reachable (Default) + type: bool + iburst: &iburst + description: Send a burst when peer is unreachable (Default) + type: bool + key_id: &key + description: Configure peer authentication key + type: int + aliases: + - key + maxpoll: &maxpoll + description: Maximum poll interval Poll value in Log2 + type: int + minpoll: &minpoll + description: Minimum poll interval Poll value in Log2 + type: int + normal_sync: &normal_sync + description: Disable rapid sync at startup + type: bool + prefer: &prefer + description: Prefer this peer when possible + type: bool + source: &source + description: Interface for source address + type: str + version: &version + description: Configure NTP version + type: int + servers: + description: Configure NTP server + type: list + elements: dict + suboptions: + server: + description: ipv4/ipv6 address or hostname of the server + type: str + use_ipv4: *use_ipv4 + use_ipv6: *use_ipv6 + vrf: *vrf + burst: *burst + iburst: *iburst + key_id: *key + maxpoll: *maxpoll + minpoll: *minpoll + normal_sync: *normal_sync + prefer: *prefer + source: *source + version: *version + source: + description: Configure interface for source address + type: str + trusted_keys: + description: Key numbers for trusted time sources + type: list + elements: dict + suboptions: + range_start: + description: Start / key number + type: int + range_end: + description: End key number + type: int + update_calendar: + description: Periodically update calendar with NTP time + type: bool + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device + by executing the command B(show running-config | section ^ntp). + - 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 + state: + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + default: merged + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The states I(replaced) and I(overridden) have identical + behaviour for this module. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command + I(show running-config | section ^ntp) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str +""" + +EXAMPLES = """ +# Using state: merged + +# Before state: +# ------------- + +# router-ios#show running-config | section ^ntp +# --------------------- EMPTY ----------------- + +# Merged play: +# ------------ + +- name: Apply the provided configuration + cisco.ios.ios_ntp_global: + config: + access_group: + peer: + - access_list: DHCP-Server + ipv4: true + kod: true + - access_list: preauth_ipv6_acl + ipv6: true + kod: true + - access_list: "2" + kod: true + query_only: + - access_list: "10" + allow: + control: + rate_limit: 4 + private: true + authenticate: true + authentication_keys: + - algorithm: md5 + encryption: 22 + id: 2 + key: SomeSecurePassword + broadcast_delay: 22 + clock_period: 5 + logging: true + master: + stratum: 4 + max_associations: 34 + max_distance: 3 + min_distance: 10 + orphan: 4 + panic_update: true + peers: + - peer: 172.16.1.10 + version: 2 + - key: 2 + minpoll: 5 + peer: 172.16.1.11 + prefer: true + version: 2 + - peer: checkPeerDomainIpv4.com + prefer: true + use_ipv4: true + - peer: checkPeerDomainIpv6.com + use_ipv6: true + - peer: testPeerDomainIpv6.com + prefer: true + use_ipv6: true + servers: + - server: 172.16.1.12 + version: 2 + - server: checkServerDomainIpv6.com + use_ipv6: true + - server: 172.16.1.13 + source: GigabitEthernet0/1 + source: GigabitEthernet0/1 + trusted_keys: + - range_end: 3 + range_start: 3 + - range_start: 21 + state: merged + +# Commands Fired: +# --------------- + +# "commands": [ +# "ntp allow mode control 4", +# "ntp allow mode private", +# "ntp authenticate", +# "ntp broadcastdelay 22", +# "ntp clock-period 5", +# "ntp logging", +# "ntp master 4", +# "ntp max-associations 34", +# "ntp maxdistance 3", +# "ntp mindistance 10", +# "ntp orphan 4", +# "ntp panic update", +# "ntp source GigabitEthernet0/1", +# "ntp access-group ipv4 peer DHCP-Server kod", +# "ntp access-group ipv6 peer preauth_ipv6_acl kod", +# "ntp access-group peer 2 kod", +# "ntp access-group query-only 10", +# "ntp authentication-key 2 md5 SomeSecurePassword 22", +# "ntp peer 172.16.1.10 version 2", +# "ntp peer 172.16.1.11 key 2 minpoll 5 prefer version 2", +# "ntp peer ip checkPeerDomainIpv4.com prefer", +# "ntp peer ipv6 checkPeerDomainIpv6.com", +# "ntp peer ipv6 testPeerDomainIpv6.com prefer", +# "ntp server 172.16.1.12 version 2", +# "ntp server ipv6 checkServerDomainIpv6.com", +# "ntp server 172.16.1.13 source GigabitEthernet0/1", +# "ntp trusted-key 3 - 3", +# "ntp trusted-key 21" +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section ^ntp +# ntp max-associations 34 +# ntp logging +# ntp allow mode control 4 +# ntp panic update +# ntp authentication-key 2 md5 0635002C497D0C1A1005173B0D17393C2B3A37 7 +# ntp authenticate +# ntp trusted-key 3 - 3 +# ntp trusted-key 21 +# ntp orphan 4 +# ntp mindistance 10 +# ntp maxdistance 3 +# ntp broadcastdelay 22 +# ntp source GigabitEthernet0/1 +# ntp access-group peer 2 kod +# ntp access-group ipv6 peer preauth_ipv6_acl kod +# ntp master 4 +# ntp peer 172.16.1.10 version 2 +# ntp peer 172.16.1.11 key 2 minpoll 5 prefer version 2 +# ntp server 172.16.1.12 version 2 +# ntp server 172.16.1.13 source GigabitEthernet0/1 +# ntp peer ip checkPeerDomainIpv4.com prefer +# ntp peer ipv6 checkPeerDomainIpv6.com +# ntp peer ipv6 testPeerDomainIpv6.com prefer +# ntp server ipv6 checkServerDomainIpv6.com + +# Using state: deleted + +# Before state: +# ------------- + +# router-ios#show running-config | section ^ntp +# ntp max-associations 34 +# ntp logging +# ntp allow mode control 4 +# ntp panic update +# ntp authentication-key 2 md5 0635002C497D0C1A1005173B0D17393C2B3A37 7 +# ntp authenticate +# ntp trusted-key 3 - 3 +# ntp trusted-key 21 +# ntp orphan 4 +# ntp mindistance 10 +# ntp maxdistance 3 +# ntp broadcastdelay 22 +# ntp source GigabitEthernet0/1 +# ntp access-group peer 2 kod +# ntp access-group ipv6 peer preauth_ipv6_acl kod +# ntp master 4 +# ntp peer 172.16.1.10 version 2 +# ntp peer 172.16.1.11 key 2 minpoll 5 prefer version 2 +# ntp server 172.16.1.12 version 2 +# ntp server 172.16.1.13 source GigabitEthernet0/1 +# ntp peer ip checkPeerDomainIpv4.com prefer +# ntp peer ipv6 checkPeerDomainIpv6.com +# ntp peer ipv6 testPeerDomainIpv6.com prefer +# ntp server ipv6 checkServerDomainIpv6.com + +# Deleted play: +# ------------- + +- name: Remove all existing configuration + cisco.ios.ios_ntp_global: + state: deleted + +# Commands Fired: +# --------------- + +# "commands": [ +# "no ntp allow mode control 4", +# "no ntp authenticate", +# "no ntp broadcastdelay 22", +# "no ntp logging", +# "no ntp master 4", +# "no ntp max-associations 34", +# "no ntp maxdistance 3", +# "no ntp mindistance 10", +# "no ntp orphan 4", +# "no ntp panic update", +# "no ntp source GigabitEthernet0/1", +# "no ntp access-group peer 2 kod", +# "no ntp access-group ipv6 peer preauth_ipv6_acl kod", +# "no ntp authentication-key 2 md5 0635002C497D0C1A1005173B0D17393C2B3A37 7", +# "no ntp peer 172.16.1.10 version 2", +# "no ntp peer 172.16.1.11 key 2 minpoll 5 prefer version 2", +# "no ntp peer ip checkPeerDomainIpv4.com prefer", +# "no ntp peer ipv6 checkPeerDomainIpv6.com", +# "no ntp peer ipv6 testPeerDomainIpv6.com prefer", +# "no ntp server 172.16.1.12 version 2", +# "no ntp server 172.16.1.13 source GigabitEthernet0/1", +# "no ntp server ipv6 checkServerDomainIpv6.com", +# "no ntp trusted-key 21", +# "no ntp trusted-key 3 - 3" +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section ^ntp +# --------------------- EMPTY ----------------- + +# Using state: overridden + +# Before state: +# ------------- + +# router-ios#show running-config | section ^ntp +# ntp panic update +# ntp authentication-key 2 md5 00371C0B01680E051A33497E080A16001D1908 7 +# ntp authenticate +# ntp trusted-key 3 - 4 +# ntp trusted-key 21 +# ntp source GigabitEthernet0/1 +# ntp peer 172.16.1.10 version 2 +# ntp server 172.16.1.12 version 2 +# ntp peer ip checkPeerDomainIpv4.com prefer +# ntp server ipv6 checkServerDomainIpv6.com + +# Overridden play: +# ---------------- + +- name: Override commands with provided configuration + cisco.ios.ios_ntp_global: + config: + peers: + - peer: ipv6DomainNew.com + use_ipv6: true + - peer: 172.16.1.100 + prefer: true + use_ipv4: true + access_group: + peer: + - access_list: DHCP-Server + ipv6: true + state: overridden + +# Commands Fired: +# --------------- +# "commands": [ +# "no ntp authenticate", +# "no ntp panic update", +# "no ntp source GigabitEthernet0/1", +# "ntp access-group ipv6 peer DHCP-Server", +# "no ntp authentication-key 2 md5 00371C0B01680E051A33497E080A16001D1908 7", +# "ntp peer ipv6 ipv6DomainNew.com", +# "ntp peer 172.16.1.100 prefer", +# "no ntp peer 172.16.1.10 version 2", +# "no ntp peer ip checkPeerDomainIpv4.com prefer", +# "no ntp server 172.16.1.12 version 2", +# "no ntp server ipv6 checkServerDomainIpv6.com", +# "no ntp trusted-key 21", +# "no ntp trusted-key 3 - 4" +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section ^ntp +# ntp access-group ipv6 peer DHCP-Server +# ntp peer ipv6 ipv6DomainNew.com +# ntp peer 172.16.1.100 prefer + +# Using state: replaced + +# Before state: +# ------------- + +# router-ios#show running-config | section ^ntp +# ntp access-group ipv6 peer DHCP-Server +# ntp peer ipv6 ipv6DomainNew.com +# ntp peer 172.16.1.100 prefer + +# Replaced play: +# -------------- + +- name: Replace commands with provided configuration + cisco.ios.ios_ntp_global: + config: + broadcast_delay: 22 + clock_period: 5 + logging: true + master: + stratum: 4 + max_associations: 34 + max_distance: 3 + min_distance: 10 + orphan: 4 + state: replaced + +# Commands Fired: +# --------------- + +# "commands": [ +# "ntp broadcastdelay 22", +# "ntp clock-period 5", +# "ntp logging", +# "ntp master 4", +# "ntp max-associations 34", +# "ntp maxdistance 3", +# "ntp mindistance 10", +# "ntp orphan 4", +# "no ntp access-group ipv6 peer DHCP-Server", +# "no ntp peer 172.16.1.100 prefer", +# "no ntp peer ipv6 ipv6DomainNew.com" +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section ^ntp +# ntp max-associations 34 +# ntp logging +# ntp orphan 4 +# ntp mindistance 10 +# ntp maxdistance 3 +# ntp broadcastdelay 22 +# ntp master 4 + +# Using state: gathered + +# Before state: +# ------------- + +#router-ios#show running-config | section ^ntp +# ntp max-associations 34 +# ntp logging +# ntp allow mode control 4 +# ntp panic update +# ntp authentication-key 2 md5 0635002C497D0C1A1005173B0D17393C2B3A37 7 +# ntp authenticate +# ntp trusted-key 3 - 3 +# ntp trusted-key 21 +# ntp orphan 4 +# ntp mindistance 10 +# ntp maxdistance 3 +# ntp broadcastdelay 22 +# ntp source GigabitEthernet0/1 +# ntp access-group peer 2 kod +# ntp access-group ipv6 peer preauth_ipv6_acl kod +# ntp master 4 +# ntp update-calendar +# ntp peer 172.16.1.10 version 2 +# ntp peer 172.16.1.11 key 2 minpoll 5 prefer version 2 +# ntp server 172.16.1.12 version 2 +# ntp server 172.16.1.13 source GigabitEthernet0/1 +# ntp peer ip checkPeerDomainIpv4.com prefer +# ntp peer ipv6 checkPeerDomainIpv6.com +# ntp peer ipv6 testPeerDomainIpv6.com prefer +# ntp server ipv6 checkServerDomainIpv6.com + +# Gathered play: +# -------------- + +- name: Gather listed ntp config + cisco.ios.ios_ntp_global: + state: gathered + +# Module Execution Result: +# ------------------------ + +# "gathered": { +# "access_group": { +# "peer": [ +# { +# "access_list": "2", +# "kod": true +# }, +# { +# "access_list": "preauth_ipv6_acl", +# "ipv6": true, +# "kod": true +# } +# ] +# }, +# "allow": { +# "control": { +# "rate_limit": 4 +# } +# }, +# "authenticate": true, +# "authentication_keys": [ +# { +# "algorithm": "md5", +# "encryption": 7, +# "id": 2, +# "key": "0635002C497D0C1A1005173B0D17393C2B3A37" +# } +# ], +# "broadcast_delay": 22, +# "logging": true, +# "master": { +# "stratum": 4 +# }, +# "max_associations": 34, +# "max_distance": 3, +# "min_distance": 10, +# "orphan": 4, +# "panic_update": true, +# "peers": [ +# { +# "peer": "172.16.1.10", +# "version": 2 +# }, +# { +# "key": 2, +# "minpoll": 5, +# "peer": "172.16.1.11", +# "prefer": true, +# "version": 2 +# }, +# { +# "peer": "checkPeerDomainIpv4.com", +# "prefer": true, +# "use_ipv4": true +# }, +# { +# "peer": "checkPeerDomainIpv6.com", +# "use_ipv6": true +# }, +# { +# "peer": "testPeerDomainIpv6.com", +# "prefer": true, +# "use_ipv6": true +# } +# ], +# "servers": [ +# { +# "server": "172.16.1.12", +# "version": 2 +# }, +# { +# "server": "172.16.1.13", +# "source": "GigabitEthernet0/1" +# }, +# { +# "server": "checkServerDomainIpv6.com", +# "use_ipv6": true +# } +# ], +# "source": "GigabitEthernet0/1", +# "trusted_keys": [ +# { +# "range_start": 21 +# }, +# { +# "range_end": 3, +# "range_start": 3 +# } +# ], +# "update_calendar": true +# }, + +# After state: +# ------------- + +# router-ios#show running-config | section ^ntp +# ntp max-associations 34 +# ntp logging +# ntp allow mode control 4 +# ntp panic update +# ntp authentication-key 2 md5 0635002C497D0C1A1005173B0D17393C2B3A37 7 +# ntp authenticate +# ntp trusted-key 3 - 3 +# ntp trusted-key 21 +# ntp orphan 4 +# ntp mindistance 10 +# ntp maxdistance 3 +# ntp broadcastdelay 22 +# ntp source GigabitEthernet0/1 +# ntp access-group peer 2 kod +# ntp access-group ipv6 peer preauth_ipv6_acl kod +# ntp master 4 +# ntp update-calendar +# ntp peer 172.16.1.10 version 2 +# ntp peer 172.16.1.11 key 2 minpoll 5 prefer version 2 +# ntp server 172.16.1.12 version 2 +# ntp server 172.16.1.13 source GigabitEthernet0/1 +# ntp peer ip checkPeerDomainIpv4.com prefer +# ntp peer ipv6 checkPeerDomainIpv6.com +# ntp peer ipv6 testPeerDomainIpv6.com prefer +# ntp server ipv6 checkServerDomainIpv6.com + +# Using state: rendered + +# Rendered play: +# -------------- + +- name: Render the commands for provided configuration + cisco.ios.ios_ntp_global: + config: + access_group: + peer: + - access_list: DHCP-Server + ipv4: true + kod: true + - access_list: preauth_ipv6_acl + ipv6: true + kod: true + - access_list: "2" + kod: true + query_only: + - access_list: "10" + allow: + control: + rate_limit: 4 + private: true + authenticate: true + authentication_keys: + - algorithm: md5 + encryption: 22 + id: 2 + key: SomeSecurePassword + broadcast_delay: 22 + clock_period: 5 + logging: true + master: + stratum: 4 + max_associations: 34 + max_distance: 3 + min_distance: 10 + orphan: 4 + panic_update: true + peers: + - peer: 172.16.1.10 + version: 2 + - key: 2 + minpoll: 5 + peer: 172.16.1.11 + prefer: true + version: 2 + - peer: checkPeerDomainIpv4.com + prefer: true + use_ipv4: true + - peer: checkPeerDomainIpv6.com + use_ipv6: true + - peer: testPeerDomainIpv6.com + prefer: true + use_ipv6: true + servers: + - server: 172.16.1.12 + version: 2 + - server: checkServerDomainIpv6.com + use_ipv6: true + - server: 172.16.1.13 + source: GigabitEthernet0/1 + source: GigabitEthernet0/1 + trusted_keys: + - range_end: 3 + range_start: 10 + - range_start: 21 + update_calendar: true + state: rendered + +# Module Execution Result: +# ------------------------ + +# "rendered": [ +# "ntp allow mode control 4", +# "ntp allow mode private", +# "ntp authenticate", +# "ntp broadcastdelay 22", +# "ntp clock-period 5", +# "ntp logging", +# "ntp master 4", +# "ntp max-associations 34", +# "ntp maxdistance 3", +# "ntp mindistance 10", +# "ntp orphan 4", +# "ntp panic update", +# "ntp source GigabitEthernet0/1", +# "ntp update-calendar", +# "ntp access-group ipv4 peer DHCP-Server kod", +# "ntp access-group ipv6 peer preauth_ipv6_acl kod", +# "ntp access-group peer 2 kod", +# "ntp access-group query-only 10", +# "ntp authentication-key 2 md5 SomeSecurePassword 22", +# "ntp peer 172.16.1.10 version 2", +# "ntp peer 172.16.1.11 key 2 minpoll 5 prefer version 2", +# "ntp peer ip checkPeerDomainIpv4.com prefer", +# "ntp peer ipv6 checkPeerDomainIpv6.com", +# "ntp peer ipv6 testPeerDomainIpv6.com prefer", +# "ntp server 172.16.1.12 version 2", +# "ntp server ipv6 checkServerDomainIpv6.com", +# "ntp server 172.16.1.13 source GigabitEthernet0/1", +# "ntp trusted-key 3 - 3", +# "ntp trusted-key 21" +# ] + +# Using state: parsed + +# File: parsed.cfg +# ---------------- + +# ntp allow mode control 4 +# ntp allow mode private +# ntp authenticate +# ntp broadcastdelay 22 +# ntp clock-period 5 +# ntp logging +# ntp master 4 +# ntp max-associations 34 +# ntp maxdistance 3 +# ntp mindistance 10 +# ntp orphan 4 +# ntp panic update +# ntp source GigabitEthernet0/1 +# ntp update-calendar +# ntp access-group ipv4 peer DHCP-Server kod +# ntp access-group ipv6 peer preauth_ipv6_acl kod +# ntp access-group peer 2 kod +# ntp access-group query-only 10 +# ntp authentication-key 2 md5 SomeSecurePassword 22 +# ntp peer 172.16.1.10 version 2 +# ntp peer 172.16.1.11 key 2 minpoll 5 prefer version 2 +# ntp peer ip checkPeerDomainIpv4.com prefer +# ntp peer ipv6 checkPeerDomainIpv6.com +# ntp peer ipv6 testPeerDomainIpv6.com prefer +# ntp server 172.16.1.12 version 2 +# ntp server ipv6 checkServerDomainIpv6.com +# ntp server 172.16.1.13 source GigabitEthernet0/1 +# ntp trusted-key 3 - 13 +# ntp trusted-key 21 + +# Parsed play: +# ------------ + +- name: Parse the provided configuration with the existing running configuration + cisco.ios.ios_ntp_global: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ + +# "parsed": { +# "access_group": { +# "peer": [ +# { +# "access_list": "2", +# "kod": true +# }, +# { +# "access_list": "DHCP-Server", +# "ipv4": true, +# "kod": true +# }, +# { +# "access_list": "preauth_ipv6_acl", +# "ipv6": true, +# "kod": true +# } +# ], +# "query_only": [ +# { +# "access_list": "10" +# } +# ] +# }, +# "allow": { +# "control": { +# "rate_limit": 4 +# }, +# "private": true +# }, +# "authenticate": true, +# "authentication_keys": [ +# { +# "algorithm": "md5", +# "encryption": 22, +# "id": 2, +# "key": "SomeSecurePassword" +# } +# ], +# "broadcast_delay": 22, +# "clock_period": 5, +# "logging": true, +# "master": { +# "stratum": 4 +# }, +# "max_associations": 34, +# "max_distance": 3, +# "min_distance": 10, +# "orphan": 4, +# "panic_update": true, +# "peers": [ +# { +# "peer": "172.16.1.10", +# "version": 2 +# }, +# { +# "peer": "checkPeerDomainIpv6.com", +# "use_ipv6": true +# } +# ], +# "servers": [ +# { +# "server": "172.16.1.12", +# "version": 2 +# }, +# { +# "server": "172.16.1.13", +# "source": "GigabitEthernet0/1" +# }, +# { +# "server": "checkServerDomainIpv6.com", +# "use_ipv6": true +# } +# ], +# "source": "GigabitEthernet0/1", +# "trusted_keys": [ +# { +# "range_start": 21 +# }, +# { +# "range_end": 13, +# "range_start": 3 +# } +# ], +# "update_calendar": true +# } +""" + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: list + sample: + - ntp peer 20.18.11.3 key 6 minpoll 15 prefer version 2 + - ntp access-group ipv4 peer DHCP-Server kod + - ntp trusted-key 9 - 96 + - ntp master stratum 2 + - ntp orphan 4 + - ntp panic update +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when state is I(rendered) + type: list + sample: + - ntp master stratum 2 + - ntp server ip testserver.com prefer + - ntp authentication-key 2 md5 testpass 22 + - ntp allow mode control 4 + - ntp max-associations 34 + - ntp broadcastdelay 22 +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ntp_global.ntp_global import ( + Ntp_globalArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.ntp_global.ntp_global import ( + Ntp_global, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Ntp_globalArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Ntp_global(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_ospf_interfaces.py b/ansible_collections/cisco/ios/plugins/modules/ios_ospf_interfaces.py new file mode 100644 index 000000000..386020f3a --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_ospf_interfaces.py @@ -0,0 +1,1107 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2023 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_ospf_interfaces +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_ospf_interfaces +short_description: Resource module to configure OSPF interfaces. +description: + This module configures and manages the Open Shortest Path First (OSPF) + version 2 on IOS platforms. +version_added: 1.0.0 +author: + - Sagar Paul (@KB-perByte) + - Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: A dictionary of OSPF interfaces options. + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface excluding any logical unit number, + i.e. GigabitEthernet0/1. + type: str + required: true + address_family: + description: + - OSPF interfaces settings on the interfaces in address-family + context. + type: list + elements: dict + suboptions: + afi: + description: + - Address Family Identifier (AFI) for OSPF interfaces settings + on the interfaces. + type: str + choices: + - ipv4 + - ipv6 + required: true + process: + description: OSPF interfaces process config + type: dict + suboptions: + id: + description: + - Address Family Identifier (AFI) for OSPF interfaces settings + on the interfaces. Please refer vendor documentation of Valid + values. + type: int + area_id: + description: + - OSPF interfaces area ID as a decimal value. Please + refer vendor documentation of Valid values. + - OSPF interfaces area ID in IP address format(e.g. + A.B.C.D) + type: str + secondaries: + description: + - Include or exclude secondary IP addresses. + - Valid only with IPv4 config + type: bool + instance_id: + description: + - Set the OSPF instance based on ID + - Valid only with IPv6 OSPF config + type: int + adjacency: + description: Adjacency staggering + type: bool + authentication: + description: Enable authentication + type: dict + suboptions: + key_chain: + description: Use a key-chain for cryptographic + authentication keys + type: str + message_digest: + description: Use message-digest authentication + type: bool + "null": + description: Use no authentication + type: bool + bfd: + description: + - BFD configuration commands + - Enable/Disable BFD on this interface + type: bool + cost: + description: Interface cost + type: dict + suboptions: + interface_cost: + description: Interface cost or Route cost of this interface + type: int + dynamic_cost: + description: + - Specify dynamic cost options + - Valid only with IPv6 OSPF config + type: dict + suboptions: + default: + description: Specify default link metric value + type: int + hysteresis: + description: Specify hysteresis value for LSA dampening + type: dict + suboptions: + percent: + description: Specify hysteresis percent changed. + Please refer vendor documentation of Valid values. + type: int + threshold: + description: Specify hysteresis threshold value. + Please refer vendor documentation of Valid values. + type: int + weight: + description: Specify weight to be placed on individual + metrics + type: dict + suboptions: + l2_factor: + description: + - Specify weight to be given to L2-factor metric + - Percentage weight of L2-factor metric. Please refer + vendor documentation of Valid values. + type: int + latency: + description: + - Specify weight to be given to latency metric. + - Percentage weight of latency metric. Please refer + vendor documentation of Valid values. + type: int + oc: + description: + - Specify weight to be given to cdr/mdr for oc + - Give 100 percent weightage for current data rate(0 + for maxdatarate) + type: bool + resources: + description: + - Specify weight to be given to resources metric + - Percentage weight of resources metric. Please refer + vendor documentation of Valid values. + type: int + throughput: + description: + - Specify weight to be given to throughput metric + - Percentage weight of throughput metric. Please refer + vendor documentation of Valid values. + type: int + database_filter: + description: Filter OSPF LSA during synchronization and flooding + type: bool + dead_interval: + description: Interval after which a neighbor is declared dead + type: dict + suboptions: + time: + description: time in seconds + type: int + minimal: + description: + - Set to 1 second and set multiplier for Hellos + - Number of Hellos sent within 1 second. Please refer + vendor documentation of Valid values. + - Valid only with IP OSPF config + type: int + demand_circuit: + description: OSPF Demand Circuit, enable or disable + the demand circuit' + type: dict + suboptions: + enable: + description: Enable Demand Circuit + type: bool + ignore: + description: Ignore demand circuit auto-negotiation requests + type: bool + disable: + description: + - Disable demand circuit on this interface + - Valid only with IPv6 OSPF config + type: bool + flood_reduction: + description: OSPF Flood Reduction + type: bool + hello_interval: + description: + - Time between HELLO packets + - Please refer vendor documentation of Valid values. + type: int + lls: + description: + - Link-local Signaling (LLS) support + - Valid only with IP OSPF config + type: bool + manet: + description: + - Mobile Adhoc Networking options + - MANET Peering options + - Valid only with IPv6 OSPF config + type: dict + suboptions: + cost: + description: Redundant path cost improvement required to peer + type: dict + suboptions: + percent: + description: Relative incremental path cost. + Please refer vendor documentation of Valid values. + type: int + threshold: + description: Absolute incremental path cost. + Please refer vendor documentation of Valid values. + type: int + link_metrics: + description: Redundant path cost improvement required to peer + type: dict + suboptions: + set: + description: Enable link-metrics + type: bool + cost_threshold: + description: Minimum link cost threshold. + Please refer vendor documentation of Valid values. + type: int + mtu_ignore: + description: Ignores the MTU in DBD packets + type: bool + multi_area: + description: + - Set the OSPF multi-area ID + - Valid only with IP OSPF config + type: dict + suboptions: + id: + description: + - OSPF multi-area ID as a decimal value. Please refer vendor + documentation of Valid values. + - OSPF multi-area ID in IP address format(e.g. A.B.C.D) + type: int + cost: + description: Interface cost + type: int + neighbor: + description: + - OSPF neighbor link-local IPv6 address (X:X:X:X::X) + - Valid only with IPv6 OSPF config + type: dict + suboptions: + address: + description: Neighbor link-local IPv6 address + type: str + cost: + description: OSPF cost for point-to-multipoint neighbor + type: int + database_filter: + description: Filter OSPF LSA during synchronization and flooding for point-to-multipoint neighbor + type: bool + poll_interval: + description: OSPF dead-router polling interval + type: int + priority: + description: OSPF priority of non-broadcast neighbor + type: int + network: + description: Network type + type: dict + suboptions: + broadcast: + description: Specify OSPF broadcast multi-access network + type: bool + manet: + description: + - Specify MANET OSPF interface type + - Valid only with IPv6 OSPF config + type: bool + non_broadcast: + description: Specify OSPF NBMA network + type: bool + point_to_multipoint: + description: Specify OSPF point-to-multipoint network + type: bool + point_to_point: + description: Specify OSPF point-to-point network + type: bool + prefix_suppression: + description: Enable/Disable OSPF prefix suppression + type: bool + priority: + description: Router priority. Please refer vendor documentation + of Valid values. + type: int + resync_timeout: + description: Interval after which adjacency is reset if oob-resync + is not started. Please refer vendor documentation of Valid values. + type: int + retransmit_interval: + description: Time between retransmitting lost link state + advertisements. Please refer vendor documentation of Valid values. + type: int + shutdown: + description: Set OSPF protocol's state to disable under + current interface + type: bool + transmit_delay: + description: Link state transmit delay. + Please refer vendor documentation of Valid values. + type: int + ttl_security: + description: + - TTL security check + - Valid only with IPV4 OSPF config + type: dict + suboptions: + set: + description: Enable TTL Security on all interfaces + type: bool + hops: + description: + - Maximum number of IP hops allowed + - Please refer vendor documentation of Valid values. + type: int + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS + device by executing the command B(sh 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 + state: + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any + change on the device. + - The state I(rendered) will transform the configuration in C(config) + option to platform specific CLI commands which will be returned in the + I(rendered) key within the result. For state I(rendered) active + connection to remote host is not required. + - The state I(gathered) will fetch the running configuration from device + and transform it into structured data in the format as per the resource + module argspec and the value is returned in the I(gathered) key within + the result. + - The state I(parsed) reads the configuration from C(running_config) + option and transforms it into JSON format as per the resource module + parameters and the value is returned in the I(parsed) key within the + result. The value of C(running_config) option should be the same format + as the output of command I(show running-config | section ^interface) executed on device. For state I(parsed) active connection to + remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +""" + +EXAMPLES = """ +# Using deleted + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ipv6 ospf 55 area 105 +# ipv6 ospf priority 20 +# ipv6 ospf transmit-delay 30 +# ipv6 ospf adjacency stagger disable +# interface GigabitEthernet0/2 +# ip ospf priority 40 +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf 10 area 20 +# ip ospf cost 30 + +- name: Delete provided OSPF Interface config + cisco.ios.ios_ospf_interfaces: + config: + - name: GigabitEthernet0/1 + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "interface GigabitEthernet0/1", +# "no ipv6 ospf 55 area 105", +# "no ipv6 ospf adjacency stagger disable", +# "no ipv6 ospf priority 20", +# "no ipv6 ospf transmit-delay 30" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# interface GigabitEthernet0/2 +# ip ospf priority 40 +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf 10 area 20 +# ip ospf cost 30 + +# Using deleted without any config passed (NOTE: This will delete all OSPF Interfaces configuration from device) + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ipv6 ospf 55 area 105 +# ipv6 ospf priority 20 +# ipv6 ospf transmit-delay 30 +# ipv6 ospf adjacency stagger disable +# interface GigabitEthernet0/2 +# ip ospf priority 40 +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf 10 area 20 +# ip ospf cost 30 + +- name: Delete all OSPF config from interfaces + cisco.ios.ios_ospf_interfaces: + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "interface GigabitEthernet0/2", +# "no ip ospf 10 area 20", +# "no ip ospf adjacency stagger disable", +# "no ip ospf cost 30", +# "no ip ospf priority 40", +# "no ip ospf ttl-security hops 50", +# "interface GigabitEthernet0/1", +# "no ipv6 ospf 55 area 105", +# "no ipv6 ospf adjacency stagger disable", +# "no ipv6 ospf priority 20", +# "no ipv6 ospf transmit-delay 30" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# interface GigabitEthernet0/2 + +# Using merged + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# router-ios# + +- name: Merge provided OSPF Interfaces configuration + cisco.ios.ios_ospf_interfaces: + config: + - name: GigabitEthernet0/1 + address_family: + - afi: ipv4 + process: + id: 10 + area_id: 30 + adjacency: true + bfd: true + cost: + interface_cost: 5 + dead_interval: + time: 5 + demand_circuit: + ignore: true + network: + broadcast: true + priority: 25 + resync_timeout: 10 + shutdown: true + ttl_security: + hops: 50 + - afi: ipv6 + process: + id: 35 + area_id: 45 + adjacency: true + database_filter: true + manet: + link_metrics: + cost_threshold: 10 + priority: 55 + transmit_delay: 45 + state: merged + +# Commands Fired: +# --------------- +# +# "commands": [ +# "interface GigabitEthernet0/1", +# "ip ospf 10 area 30", +# "ip ospf adjacency stagger disable", +# "ip ospf bfd", +# "ip ospf cost 5", +# "ip ospf dead-interval 5", +# "ip ospf demand-circuit ignore", +# "ip ospf network broadcast", +# "ip ospf priority 25", +# "ip ospf resync-timeout 10", +# "ip ospf shutdown", +# "ip ospf ttl-security hops 50", +# "ipv6 ospf 35 area 45", +# "ipv6 ospf adjacency stagger disable", +# "ipv6 ospf database-filter all out", +# "ipv6 ospf manet peering link-metrics 10", +# "ipv6 ospf priority 55", +# "ipv6 ospf transmit-delay 45" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip ospf network broadcast +# ip ospf resync-timeout 10 +# ip ospf dead-interval 5 +# ip ospf priority 25 +# ip ospf demand-circuit ignore +# ip ospf bfd +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf shutdown +# ip ospf 10 area 30 +# ip ospf cost 5 +# ipv6 ospf 35 area 45 +# ipv6 ospf priority 55 +# ipv6 ospf transmit-delay 45 +# ipv6 ospf database-filter all out +# ipv6 ospf adjacency stagger disable +# ipv6 ospf manet peering link-metrics 10 +# interface GigabitEthernet0/2 + +# Using overridden + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip ospf network broadcast +# ip ospf resync-timeout 10 +# ip ospf dead-interval 5 +# ip ospf priority 25 +# ip ospf demand-circuit ignore +# ip ospf bfd +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf shutdown +# ip ospf 10 area 30 +# ip ospf cost 5 +# ipv6 ospf 35 area 45 +# ipv6 ospf priority 55 +# ipv6 ospf transmit-delay 45 +# ipv6 ospf database-filter all out +# ipv6 ospf adjacency stagger disable +# ipv6 ospf manet peering link-metrics 10 +# interface GigabitEthernet0/2 + +- name: Override provided OSPF Interfaces configuration + cisco.ios.ios_ospf_interfaces: + config: + - name: GigabitEthernet0/1 + address_family: + - afi: ipv6 + process: + id: 55 + area_id: 105 + adjacency: true + priority: 20 + transmit_delay: 30 + - name: GigabitEthernet0/2 + address_family: + - afi: ipv4 + process: + id: 10 + area_id: 20 + adjacency: true + cost: + interface_cost: 30 + priority: 40 + ttl_security: + hops: 50 + state: overridden + +# Commands Fired: +# --------------- +# +# "commands": [ +# "interface GigabitEthernet0/2", +# "ip ospf 10 area 20", +# "ip ospf adjacency stagger disable", +# "ip ospf cost 30", +# "ip ospf priority 40", +# "ip ospf ttl-security hops 50", +# "interface GigabitEthernet0/1", +# "ipv6 ospf 55 area 105", +# "no ipv6 ospf database-filter all out", +# "no ipv6 ospf manet peering link-metrics 10", +# "ipv6 ospf priority 20", +# "ipv6 ospf transmit-delay 30", +# "no ip ospf 10 area 30", +# "no ip ospf adjacency stagger disable", +# "no ip ospf bfd", +# "no ip ospf cost 5", +# "no ip ospf dead-interval 5", +# "no ip ospf demand-circuit ignore", +# "no ip ospf network broadcast", +# "no ip ospf priority 25", +# "no ip ospf resync-timeout 10", +# "no ip ospf shutdown", +# "no ip ospf ttl-security hops 50" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ipv6 ospf 55 area 105 +# ipv6 ospf priority 20 +# ipv6 ospf transmit-delay 30 +# ipv6 ospf adjacency stagger disable +# interface GigabitEthernet0/2 +# ip ospf priority 40 +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf 10 area 20 +# ip ospf cost 30 + +# Using replaced + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip ospf network broadcast +# ip ospf resync-timeout 10 +# ip ospf dead-interval 5 +# ip ospf priority 25 +# ip ospf demand-circuit ignore +# ip ospf bfd +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf shutdown +# ip ospf 10 area 30 +# ip ospf cost 5 +# ipv6 ospf 35 area 45 +# ipv6 ospf priority 55 +# ipv6 ospf transmit-delay 45 +# ipv6 ospf database-filter all out +# ipv6 ospf adjacency stagger disable +# ipv6 ospf manet peering link-metrics 10 +# interface GigabitEthernet0/2 + +- name: Replaced provided OSPF Interfaces configuration + cisco.ios.ios_ospf_interfaces: + config: + - name: GigabitEthernet0/2 + address_family: + - afi: ipv6 + process: + id: 55 + area_id: 105 + adjacency: true + priority: 20 + transmit_delay: 30 + state: replaced + +# Commands Fired: +# --------------- +# "commands": [ +# "interface GigabitEthernet0/2", +# "ipv6 ospf 55 area 105", +# "ipv6 ospf adjacency stagger disable", +# "ipv6 ospf priority 20", +# "ipv6 ospf transmit-delay 30" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip ospf network broadcast +# ip ospf resync-timeout 10 +# ip ospf dead-interval 5 +# ip ospf priority 25 +# ip ospf demand-circuit ignore +# ip ospf bfd +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf shutdown +# ip ospf 10 area 30 +# ip ospf cost 5 +# ipv6 ospf 35 area 45 +# ipv6 ospf priority 55 +# ipv6 ospf transmit-delay 45 +# ipv6 ospf database-filter all out +# ipv6 ospf adjacency stagger disable +# ipv6 ospf manet peering link-metrics 10 +# interface GigabitEthernet0/2 +# ipv6 ospf 55 area 105 +# ipv6 ospf priority 20 +# ipv6 ospf transmit-delay 30 +# ipv6 ospf adjacency stagger disable + +# Using Gathered + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip ospf network broadcast +# ip ospf resync-timeout 10 +# ip ospf dead-interval 5 +# ip ospf priority 25 +# ip ospf demand-circuit ignore +# ip ospf bfd +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf shutdown +# ip ospf 10 area 30 +# ip ospf cost 5 +# ipv6 ospf 35 area 45 +# ipv6 ospf priority 55 +# ipv6 ospf transmit-delay 45 +# ipv6 ospf database-filter all out +# ipv6 ospf adjacency stagger disable +# ipv6 ospf manet peering link-metrics 10 +# interface GigabitEthernet0/2 + +- name: Gather OSPF Interfaces provided configurations + cisco.ios.ios_ospf_interfaces: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "name": "GigabitEthernet0/2" +# }, +# { +# "address_family": [ +# { +# "adjacency": true, +# "afi": "ipv4", +# "bfd": true, +# "cost": { +# "interface_cost": 5 +# }, +# "dead_interval": { +# "time": 5 +# }, +# "demand_circuit": { +# "ignore": true +# }, +# "network": { +# "broadcast": true +# }, +# "priority": 25, +# "process": { +# "area_id": "30", +# "id": 10 +# }, +# "resync_timeout": 10, +# "shutdown": true, +# "ttl_security": { +# "hops": 50 +# } +# }, +# { +# "adjacency": true, +# "afi": "ipv6", +# "database_filter": true, +# "manet": { +# "link_metrics": { +# "cost_threshold": 10 +# } +# }, +# "priority": 55, +# "process": { +# "area_id": "45", +# "id": 35 +# }, +# "transmit_delay": 45 +# } +# ], +# "name": "GigabitEthernet0/1" +# }, +# { +# "name": "GigabitEthernet0/0" +# } +# ] + +# After state: +# ------------ +# +# router-ios#sh running-config | section ^interface +# interface GigabitEthernet0/0 +# interface GigabitEthernet0/1 +# ip ospf network broadcast +# ip ospf resync-timeout 10 +# ip ospf dead-interval 5 +# ip ospf priority 25 +# ip ospf demand-circuit ignore +# ip ospf bfd +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf shutdown +# ip ospf 10 area 30 +# ip ospf cost 5 +# ipv6 ospf 35 area 45 +# ipv6 ospf priority 55 +# ipv6 ospf transmit-delay 45 +# ipv6 ospf database-filter all out +# ipv6 ospf adjacency stagger disable +# ipv6 ospf manet peering link-metrics 10 +# interface GigabitEthernet0/2 + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_ospf_interfaces: + config: + - name: GigabitEthernet0/1 + address_family: + - afi: ipv4 + process: + id: 10 + area_id: 30 + adjacency: true + bfd: true + cost: + interface_cost: 5 + dead_interval: + time: 5 + demand_circuit: + ignore: true + network: + broadcast: true + priority: 25 + resync_timeout: 10 + shutdown: true + ttl_security: + hops: 50 + - afi: ipv6 + process: + id: 35 + area_id: 45 + adjacency: true + database_filter: true + manet: + link_metrics: + cost_threshold: 10 + priority: 55 + transmit_delay: 45 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "interface GigabitEthernet0/1", +# "ip ospf 10 area 30", +# "ip ospf adjacency stagger disable", +# "ip ospf bfd", +# "ip ospf cost 5", +# "ip ospf dead-interval 5", +# "ip ospf demand-circuit ignore", +# "ip ospf network broadcast", +# "ip ospf priority 25", +# "ip ospf resync-timeout 10", +# "ip ospf shutdown", +# "ip ospf ttl-security hops 50", +# "ipv6 ospf 35 area 45", +# "ipv6 ospf adjacency stagger disable", +# "ipv6 ospf database-filter all out", +# "ipv6 ospf manet peering link-metrics 10", +# "ipv6 ospf priority 55", +# "ipv6 ospf transmit-delay 45" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# interface GigabitEthernet0/2 +# interface GigabitEthernet0/1 +# ip ospf network broadcast +# ip ospf resync-timeout 10 +# ip ospf dead-interval 5 +# ip ospf priority 25 +# ip ospf demand-circuit ignore +# ip ospf bfd +# ip ospf adjacency stagger disable +# ip ospf ttl-security hops 50 +# ip ospf shutdown +# ip ospf 10 area 30 +# ip ospf cost 5 +# ipv6 ospf 35 area 45 +# ipv6 ospf priority 55 +# ipv6 ospf transmit-delay 45 +# ipv6 ospf database-filter all out +# ipv6 ospf adjacency stagger disable +# ipv6 ospf manet peering link-metrics 10 +# interface GigabitEthernet0/0 + +- name: Parse the provided configuration with the existing running configuration + cisco.ios.ios_ospf_interfaces: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# }, +# { +# "name": "GigabitEthernet0/2" +# }, +# { +# "address_family": [ +# { +# "adjacency": true, +# "afi": "ipv4", +# "bfd": true, +# "cost": { +# "interface_cost": 5 +# }, +# "dead_interval": { +# "time": 5 +# }, +# "demand_circuit": { +# "ignore": true +# }, +# "network": { +# "broadcast": true +# }, +# "priority": 25, +# "process": { +# "area_id": "30", +# "id": 10 +# }, +# "resync_timeout": 10, +# "shutdown": true, +# "ttl_security": { +# "hops": 50 +# } +# }, +# { +# "adjacency": true, +# "afi": "ipv6", +# "database_filter": true, +# "manet": { +# "link_metrics": { +# "cost_threshold": 10 +# } +# }, +# "priority": 55, +# "process": { +# "area_id": "45", +# "id": 35 +# }, +# "transmit_delay": 45 +# } +# ], +# "name": "GigabitEthernet0/1" +# }, +# { +# "name": "GigabitEthernet0/0" +# } +# ] +""" + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: list + sample: + - interface GigabitEthernet2 + - ip ospf priority 40 + - ip ospf adjacency stagger disable +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - interface GigabitEthernet2 + - ip ospf priority 40 + - ip ospf adjacency stagger disable +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ospf_interfaces.ospf_interfaces import ( + Ospf_interfacesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.ospf_interfaces.ospf_interfaces import ( + Ospf_interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Ospf_interfacesArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Ospf_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_ospfv2.py b/ansible_collections/cisco/ios/plugins/modules/ios_ospfv2.py new file mode 100644 index 000000000..a00b8d05d --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_ospfv2.py @@ -0,0 +1,1728 @@ +#!/usr/bin/python +# +# -*- coding: utf-8 -*- +# Copyright 2020 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 ios_ospfv2 +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: ios_ospfv2 +short_description: Resource module to configure OSPFv2. +description: + This module configures and manages the Open Shortest Path First (OSPF) + version 2 on IOS platforms. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: A dictionary of OSPF options. + type: dict + suboptions: + processes: + description: + - List of OSPF instance configurations. + type: list + elements: dict + suboptions: + process_id: + description: Process ID + required: true + type: int + vrf: + description: Specify parameters for a VPN Routing/Forwarding instance + type: str + address_family: + description: Router Address Family configuration mode + type: dict + suboptions: + default: + description: Set a command to its defaults + type: bool + snmp_context: + description: + - Modify snmp parameters + - Configure SNMP context name + type: str + topology: + description: Associate the routing protocol to a topology instance + type: dict + suboptions: + name: + description: Routing topology instance name + type: str + base: + description: Entering router topology sub mode + type: bool + tid: + description: + - Configuring the routing protocol topology tid + - Note, please refer vendor documentation for valid values + type: bool + adjacency: + description: To configure control adjacency formation + type: dict + suboptions: + min_adjacency: + description: + - Initial number of adjacencies allowed to be forming in an area + - Please refer vendor documentation for valid values + type: int + max_adjacency: + description: + - Maximum number of adjacencies allowed to be forming + - Please refer vendor documentation for valid values + type: int + none: + description: No initial + type: bool + areas: + description: OSPF area parameters + type: list + elements: dict + suboptions: + area_id: + description: + - OSPF area ID as a decimal value. Please refer vendor documentation + of Valid values. + - OSPF area ID in IP address format(e.g. A.B.C.D) + type: str + authentication: + description: Area authentication + type: dict + suboptions: + enable: + description: Enable area authentication + type: bool + message_digest: + description: Use IPsec authentication + type: bool + capability: + description: + - Enable area specific capability + - Enable exclusion of links from base topology + type: bool + default_cost: + description: + - Set the summary default-cost of a NSSA/stub area + - Stub's advertised external route metric + - Note, please refer vendor documentation for respective valid values + type: int + filter_list: + description: Filter networks between OSPF areas + type: list + elements: dict + suboptions: + name: + description: Name of an IP prefix-list + type: str + direction: + description: The direction to apply on the filter networks sent to and from this area. + type: str + choices: ["in", "out"] + required: true + nssa: + description: Specify a NSSA area + type: dict + suboptions: + set: + description: Enable a NSSA area + type: bool + default_information_originate: + description: Originate Type 7 default into NSSA area + type: dict + suboptions: + metric: + description: OSPF default metric + type: int + metric_type: + description: + - OSPF metric type for default routes + - OSPF Link State type + type: int + choices: [1, 2] + nssa_only: + description: Limit default advertisement to this NSSA area + type: bool + no_ext_capability: + description: Do not send domain specific capabilities into NSSA + type: bool + no_redistribution: + description: No redistribution into this NSSA area + type: bool + no_summary: + description: Do not send summary LSA into NSSA + type: bool + translate: + description: + - Translate LSA + - Always translate LSAs on this ABR + - Suppress forwarding address in translated LSAs + type: str + choices: ["always", "suppress-fa"] + ranges: + description: Summarize routes matching address/mask (border routers only) + type: list + elements: dict + suboptions: + address: + description: IP address to match + type: str + netmask: + description: IP mask for address + type: str + advertise: + description: + - Advertise this range (default) + - Since, advertise when enabled is not shown in running-config + idempotency won't be maintained for the play in the second or + next run of the play. + type: bool + cost: + description: User specified metric for this range + type: int + not_advertise: + description: DoNotAdvertise this range + type: bool + sham_link: + description: Define a sham link and its parameters + type: dict + suboptions: + source: + description: IP addr associated with sham-link source (A.B.C.D) + type: str + destination: + description: IP addr associated with sham-link destination (A.B.C.D) + type: str + cost: + description: + - Associate a cost with the sham-link + - Cost of the sham-link + - Note, please refer vendor documentation for respective valid values + type: int + ttl_security: + description: + - TTL security check + - Maximum number of IP hops allowed + type: int + stub: + description: + - Specify a stub area + - Backbone can not be configured as stub area + type: dict + suboptions: + set: + description: Enable a stub area + type: bool + no_ext_capability: + description: Do not send domain specific capabilities into stub area + type: bool + no_summary: + description: Do not send summary LSA into stub area + type: bool + auto_cost: + description: Calculate OSPF interface cost according to bandwidth + type: dict + suboptions: + set: + description: Enable OSPF auto-cost + type: bool + reference_bandwidth: + description: + - Use reference bandwidth method to assign OSPF cost + - Note, refer vendor documentation for respective valid values + type: int + bfd: + description: + - BFD configuration commands + - Enable BFD on all interfaces + type: bool + capability: + description: Enable specific OSPF feature + type: dict + suboptions: + lls: + description: Link-local Signaling (LLS) support + type: bool + opaque: + description: Opaque LSA + type: bool + transit: + description: Transit Area + type: bool + vrf_lite: + description: Do not perform PE specific checks + type: bool + compatible: + description: OSPF router compatibility list + type: dict + suboptions: + rfc1583: + description: compatible with RFC 1583 + type: bool + rfc1587: + description: compatible with RFC 1587 + type: bool + rfc5243: + description: supports DBD exchange optimization + type: bool + default_information: + description: Control distribution of default information + type: dict + suboptions: + originate: + description: Distribute a default route + type: bool + always: + description: Always advertise default route + type: bool + metric: + description: + - OSPF default metric + - Note, refer vendor documentation for respective valid values + type: int + metric_type: + description: + - OSPF metric type for default routes + - Note, please refer vendor documentation for respective valid range + type: int + route_map: + description: Route-map reference name + type: str + default_metric: + description: Set metric of redistributed routes + type: int + discard_route: + description: Enable or disable discard-route installation + type: dict + suboptions: + set: + description: Enable discard-route installation + type: bool + external: + description: + - Discard route for redistributed summarised routes + - Administrative distance for redistributed summarised routes + - Note, please refer vendor documentation for respective valid range + type: int + internal: + description: + - Discard route for summarised internal routes + - Administrative distance for summarised internal routes + - Note, please refer vendor documentation for respective valid range + type: int + distance: + description: Define an administrative distance + type: dict + suboptions: + admin_distance: + description: OSPF Administrative distance + type: dict + suboptions: + distance: + description: Administrative distance + type: int + address: + description: IP Source address + type: str + wildcard_bits: + description: Wildcard bits + type: str + acl: + description: Access-list name/number + type: str + ospf: + description: OSPF distance + type: dict + suboptions: + external: + description: External type 5 and type 7 routes + type: int + inter_area: + description: Inter-area routes + type: int + intra_area: + description: Intra-area routes + type: int + distribute_list: + description: Filter networks in routing updates + type: dict + suboptions: + acls: + description: IP access list + type: list + elements: dict + suboptions: + name: + description: IP access list name/number + type: str + required: true + direction: + description: Filter incoming and outgoing routing updates. + type: str + required: true + choices: ["in", "out"] + interface: + description: + - Interface configuration (GigabitEthernet A/B) + - Valid with incoming traffic + type: str + protocol: + description: + - Protocol config (bgp 1). + - Valid with outgoing traffic + type: str + prefix: + description: Filter prefixes in routing updates + type: dict + suboptions: + name: + description: Name of an IP prefix-list + type: str + required: true + gateway_name: + description: Gateway name for filtering incoming updates based on gateway + type: str + direction: + description: Filter incoming and outgoing routing updates. + type: str + required: true + choices: ["in", "out"] + interface: + description: + - Interface configuration (GigabitEthernet A/B) + - Valid with incoming traffic + type: str + protocol: + description: + - Protocol config (bgp 1). + - Valid with outgoing traffic + type: str + route_map: + description: Filter prefixes in routing updates + type: dict + suboptions: + name: + description: Route-map name + type: str + required: true + domain_id: + description: OSPF domain-id + type: dict + suboptions: + ip_address: + description: IP address + type: dict + suboptions: + address: + description: OSPF domain ID in IP address format + type: str + secondary: + description: Secondary Domain-ID + type: bool + "null": + description: Null Domain-ID + type: bool + domain_tag: + description: + - OSPF domain-tag which is OSPF domain tag - 32-bit value + - Note, please refer vendor documentation for respective valid range + type: int + event_log: + description: Event Logging + type: dict + suboptions: + enable: + description: Enable event Logging + type: bool + one_shot: + description: Disable Logging When Log Buffer Becomes Full + type: bool + pause: + description: Pause Event Logging + type: bool + size: + description: + - Maximum Number of Events Stored in the Event Log + - Note, refer vendor documentation for respective valid values + type: int + help: + description: Description of the interactive help system + type: bool + ignore: + description: + - Do not complain about specific event + - Do not complain upon receiving LSA of the specified type, MOSPF Type 6 LSA + type: bool + interface_id: + description: + - Source of the interface ID + - SNMP MIB ifIndex + type: bool + ispf: + description: Enable incremental SPF computation + type: bool + limit: + description: Limit a specific OSPF feature and LS update, DBD, and LS request retransmissions + type: dict + suboptions: + dc: + description: Demand circuit retransmissions + type: dict + suboptions: + number: + description: The maximum number of retransmissions + type: int + disable: + description: Disble the feature + type: bool + non_dc: + description: Non-demand-circuit retransmissions + type: dict + suboptions: + number: + description: The maximum number of retransmissions + type: int + disable: + description: Disble the feature + type: bool + local_rib_criteria: + description: Enable or disable usage of local RIB as route criteria + type: dict + suboptions: + enable: + description: Enable usage of local RIB as route criteria + type: bool + forwarding_address: + description: Local RIB used to validate external/NSSA forwarding addresses + type: bool + inter_area_summary: + description: Local RIB used as criteria for inter-area summaries + type: bool + nssa_translation: + description: Local RIB used as criteria for NSSA translation + type: bool + log_adjacency_changes: + description: Log changes in adjacency state + type: dict + suboptions: + set: + description: Log changes in adjacency state + type: bool + detail: + description: Log all state changes + type: bool + max_lsa: + description: Maximum number of non self-generated LSAs to accept + type: dict + suboptions: + number: + description: + - Maximum number of non self-generated LSAs to accept + - Note, refer vendor documentation for respective valid values + type: int + threshold_value: + description: + - Threshold value (%) at which to generate a warning msg + - Note, refer vendor documentation for respective valid values + type: int + ignore_count: + description: + - Maximum number of times adjacencies can be suppressed + - Note, refer vendor documentation for respective valid values + type: int + ignore_time: + description: + - Number of minutes during which all adjacencies are suppressed + - Note, refer vendor documentation for respective valid values + type: int + reset_time: + description: + - Number of minutes after which ignore-count is reset to zero + - Note, refer vendor documentation for respective valid values + type: int + warning_only: + description: Only give a warning message when limit is exceeded + type: bool + max_metric: + description: Set maximum metric + type: dict + suboptions: + router_lsa: + description: Maximum metric in self-originated router-LSAs + type: bool + required: true + external_lsa: + description: + - Override external-lsa metric with max-metric value + - Overriding metric in external-LSAs + - Note, refer vendor documentation for respective valid values + type: int + include_stub: + description: Set maximum metric for stub links in router-LSAs + type: bool + on_startup: + description: Set maximum metric temporarily after reboot + type: dict + suboptions: + time: + description: + - Time, in seconds, router-LSAs are originated with max-metric + - Note, please refer vendor documentation for respective valid range + type: int + wait_for_bgp: + description: Let BGP decide when to originate router-LSA with normal metric + type: bool + summary_lsa: + description: + - Override summary-lsa metric with max-metric value + - Note, please refer vendor documentation for respective valid range + type: int + maximum_paths: + description: + - Forward packets over multiple paths + - Number of paths + type: int + mpls: + description: Configure MPLS routing protocol parameters + type: dict + suboptions: + ldp: + description: routing protocol commands for MPLS LDP + type: dict + suboptions: + autoconfig: + description: routing protocol commands for MPLS LDP + type: dict + suboptions: + set: + description: Configure LDP automatic configuration and set the config + type: bool + area: + description: Configure an OSPF area to run MPLS LDP + type: str + sync: + description: Configure LDP-IGP Synchronization + type: bool + traffic_eng: + description: Let BGP decide when to originate router-LSA with normal metric + type: dict + suboptions: + area: + description: + - Configure an ospf area to run MPLS Traffic Engineering + - OSPF area ID as a decimal value or in IP address format + type: str + autoroute_exclude: + description: + - MPLS TE autoroute exclude + - Filter prefixes based on name of an IP prefix-list + type: str + interface: + description: MPLS TE interface configuration for this OSPF process + type: dict + suboptions: + interface_type: + description: TE Interface configuration (GigabitEthernet A/B) + type: str + area: + description: + - Advertise MPLS TE information for this interface into area + - OSPF area ID as a decimal value + type: int + mesh_group: + description: Traffic Engineering Mesh-Group advertisement + type: dict + suboptions: + id: + description: Mesh Group Id + type: int + interface: + description: Interface configuration (GigabitEthernet A/B) + type: str + area: + description: configure flooding scope as area + type: str + multicast_intact: + description: MPLS TE and PIM interaction + type: bool + router_id_interface: + description: Router Interface configuration (GigabitEthernet A/B) + type: str + neighbor: + description: Specify a neighbor router + type: dict + suboptions: + address: + description: Neighbor address (A.B.C.D) + type: str + cost: + description: + - OSPF cost for point-to-multipoint neighbor metric + - Note, please refer vendor documentation for respective valid range + type: int + database_filter: + description: + - Filter OSPF LSA during synchronization and flooding for point-to-multipoint neighbor + - Filter all outgoing LSA + type: bool + poll_interval: + description: OSPF dead-router polling interval of non-broadcast neighbor in Seconds + type: int + priority: + description: OSPF priority of non-broadcast neighbor priority + type: int + network: + description: Enable routing on an IP network + type: list + elements: dict + suboptions: + address: + description: Network number + type: str + wildcard_bits: + description: OSPF wild card bits + type: str + area: + description: Set the OSPF area ID + type: str + nsf: + description: Non-stop forwarding + type: dict + suboptions: + cisco: + description: Cisco Non-stop forwarding + type: dict + suboptions: + helper: + description: helper support + type: bool + disable: + description: disable helper support + type: bool + ietf: + description: IETF graceful restart + type: dict + suboptions: + helper: + description: helper support + type: bool + disable: + description: disable helper support + type: bool + strict_lsa_checking: + description: enable helper strict LSA checking + type: bool + passive_interface: + description: + - passive_interface param is deprecated and a newer param passive_interfaces + with added functionality's is introduced, please meke use of the new available + passive_interfaces instead. + - Suppress routing updates on an interface (GigabitEthernet A/B) + - Interface name with respective interface number + type: str + passive_interfaces: + description: Suppress routing updates on an interface + type: dict + suboptions: + default: + description: Suppress routing updates on all interfaces + type: bool + interface: + description: Suppress/Un-Suppress routing updates on interface + type: dict + suboptions: + set_interface: + description: Suppress/Un-Suppress routing updates + type: bool + name: + description: Name of interface (GigabitEthernet A/B) + type: list + elements: str + prefix_suppression: + description: Enable prefix suppression + type: bool + priority: + description: + - OSPF topology priority + - Note, refer vendor documentation for respective valid values + type: int + queue_depth: + description: Hello/Router process queue depth + type: dict + suboptions: + hello: + description: OSPF Hello process queue depth + type: dict + suboptions: + max_packets: + description: maximum number of packets in the queue + type: int + unlimited: + description: Unlimited queue depth + type: bool + update: + description: OSPF Router process queue depth + type: dict + suboptions: + max_packets: + description: maximum number of packets in the queue + type: int + unlimited: + description: Unlimited queue depth + type: bool + router_id: + description: + - Router-id address for this OSPF process + - OSPF router-id in IP address format (A.B.C.D) + type: str + shutdown: + description: Shutdown the router process + type: bool + summary_address: + description: Configure IP address summaries + type: dict + suboptions: + address: + description: IP summary address + type: str + mask: + description: IP Summary mask + type: str + not_advertise: + description: Do not advertise or translate + type: bool + nssa_only: + description: Limit summary to NSSA areas + type: bool + tag: + description: Set tag + type: int + timers: + description: Adjust routing timers + type: dict + suboptions: + lsa: + description: + - OSPF LSA timers, arrival timer + - The minimum interval in milliseconds between accepting the same LSA + - Note, refer vendor documentation for respective valid values + type: int + pacing: + description: OSPF pacing timers + type: dict + suboptions: + flood: + description: + - OSPF flood pacing timer + - The minimum interval in msec to pace limit flooding on interface + - Note, refer vendor documentation for respective valid values + type: int + lsa_group: + description: + - OSPF LSA group pacing timer + - Interval in sec between group of LSA being refreshed or maxaged + - Note, refer vendor documentation for respective valid values + type: int + retransmission: + description: + - OSPF retransmission pacing timer + - The minimum interval in msec between neighbor retransmissions + - Note, refer vendor documentation for respective valid values + type: int + throttle: + description: OSPF throttle timers + type: dict + suboptions: + lsa: + description: OSPF LSA throttle timers + type: dict + suboptions: + first_delay: + description: + - Delay to generate first occurrence of LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + min_delay: + description: + - Minimum delay between originating the same LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + max_delay: + description: + - Maximum delay between originating the same LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + spf: + description: OSPF SPF throttle timers + - Delay between receiving a change to SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: dict + suboptions: + receive_delay: + description: + - Delay between receiving a change to SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + between_delay: + description: + - Delay between first and second SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + max_delay: + description: + - Maximum wait time in milliseconds for SPF calculations + - Note, refer vendor documentation for respective valid values + type: int + traffic_share: + description: + - How to compute traffic share over alternate paths + - All traffic shared among min metric paths + - Use different interfaces for equal-cost paths + type: bool + ttl_security: + description: TTL security check + type: dict + suboptions: + set: + description: Enable TTL Security on all interfaces + type: bool + hops: + description: + - Maximum number of IP hops allowed + - Note, refer vendor documentation for respective valid values + type: int + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS + device by executing the command B(sh running-config | section ^router ospf). + - 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 + state: + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged +""" + +EXAMPLES = """ +# Using deleted + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +- name: Delete provided OSPF V2 processes + cisco.ios.ios_ospfv2: + config: + processes: + - process_id: 1 + - process_id: 200 + vrf: blue + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no router ospf 1" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in + +# Using deleted without any config passed (NOTE: This will delete all OSPFV2 configuration from device) + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +- name: Delete all OSPF processes + cisco.ios.ios_ospfv2: + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no router ospf 200 vrf blue", +# "no router ospf 1" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^router ospf +# router-ios# + +# Using merged + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router-ios# + +- name: Merge provided OSPF V2 configuration + cisco.ios.ios_ospfv2: + config: + processes: + - process_id: 1 + max_metric: + router_lsa: true + on_startup: + time: 110 + areas: + - area_id: "5" + capability: true + authentication: + enable: true + - area_id: "10" + authentication: + message_digest: true + nssa: + default_information_originate: + metric: 10 + translate: suppress-fa + default_cost: 10 + filter_list: + - name: test_prefix_in + direction: in + - name: test_prefix_out + direction: out + network: + - address: 198.51.100.0 + wildcard_bits: 0.0.0.255 + area: 5 + default_information: + originate: true + passive_interfaces: + default: true + interface: + set_interface: False + name: + - GigabitEthernet0/1 + - GigabitEthernet0/2 + - process_id: 200 + vrf: blue + domain_id: + ip_address: + address: 192.0.3.1 + max_metric: + router_lsa: true + on_startup: + time: 100 + auto_cost: + reference_bandwidth: 4 + areas: + - area_id: "10" + capability: true + distribute_list: + acls: + - name: 10 + direction: out + - name: 123 + direction: in + state: merged + +# Commands Fired: +# --------------- +# +# "commands": [ +# "router ospf 200 vrf blue", +# "auto-cost reference-bandwidth 4", +# "distribute-list 10 out", +# "distribute-list 123 in", +# "domain-id 192.0.3.1", +# "max-metric router-lsa on-startup 100", +# "area 10 capability default-exclusion", +# "router ospf 1", +# "default-information originate", +# "max-metric router-lsa on-startup 110", +# "network 198.51.100.0 0.0.0.255 area 5", +# "area 10 authentication message-digest", +# "area 10 default-cost 10", +# "area 10 nssa translate type7 suppress-fa", +# "area 10 nssa default-information-originate metric 10", +# "area 10 filter-list prefix test_prefix_out out", +# "area 10 filter-list prefix test_prefix_in in", +# "area 5 authentication", +# "area 5 capability default-exclusion" +# "passive-interface default" +# "no passive-interface GigabitEthernet0/1" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate +# passive-interface default +# no passive-interface GigabitEthernet0/1 +# no passive-interface GigabitEthernet0/2 + +# Using overridden + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +- name: Override provided OSPF V2 configuration + cisco.ios.ios_ospfv2: + config: + processes: + - process_id: 200 + vrf: blue + domain_id: + ip_address: + address: 192.0.4.1 + max_metric: + router_lsa: true + on_startup: + time: 200 + maximum_paths: 15 + ttl_security: + hops: 7 + areas: + - area_id: "10" + default_cost: 10 + authentication: + message_digest: true + - process_id: 100 + vrf: ospf_vrf + domain_id: + ip_address: + address: 192.0.5.1 + auto_cost: + reference_bandwidth: 5 + areas: + - area_id: "5" + authentication: + message_digest: true + nssa: + default_information_originate: + metric: 10 + translate: suppress-fa + state: overridden + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no router ospf 1", +# "router ospf 100 vrf ospf_vrf", +# "auto-cost reference-bandwidth 5", +# "domain-id 192.0.5.1", +# "area 5 authentication message-digest", +# "area 5 nssa translate type7 suppress-fa", +# "area 5 nssa default-information-originate metric 10", +# "router ospf 200 vrf blue", +# "no auto-cost reference-bandwidth 4", +# "no distribute-list 10 out", +# "no distribute-list 123 in", +# "domain-id 192.0.4.1", +# "max-metric router-lsa on-startup 200", +# "maximum-paths 15", +# "ttl-security all-interfaces hops 7", +# "area 10 authentication message-digest", +# "no area 10 capability default-exclusion", +# "area 10 default-cost 10" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.4.1 +# max-metric router-lsa on-startup 200 +# ttl-security all-interfaces hops 7 +# area 10 authentication message-digest +# area 10 default-cost 10 +# maximum-paths 15 +# router ospf 100 vrf ospf_vrf +# domain-id 192.0.5.1 +# auto-cost reference-bandwidth 5 +# area 5 authentication message-digest +# area 5 nssa default-information-originate metric 10 +# area 5 nssa translate type7 suppress-fa + +# Using replaced + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +- name: Replaced provided OSPF V2 configuration + cisco.ios.ios_ospfv2: + config: + processes: + - process_id: 200 + vrf: blue + domain_id: + ip_address: + address: 192.0.4.1 + max_metric: + router_lsa: true + on_startup: + time: 200 + maximum_paths: 15 + ttl_security: + hops: 7 + areas: + - area_id: "10" + default_cost: 10 + authentication: + message_digest: true + - process_id: 100 + vrf: ospf_vrf + domain_id: + ip_address: + address: 192.0.5.1 + auto_cost: + reference_bandwidth: 5 + areas: + - area_id: "5" + authentication: + message_digest: true + nssa: + default_information_originate: + metric: 10 + translate: suppress-fa + state: replaced + +# Commands Fired: +# --------------- +# "commands": [ +# "router ospf 100 vrf ospf_vrf", +# "auto-cost reference-bandwidth 5", +# "domain-id 192.0.5.1", +# "area 5 authentication message-digest", +# "area 5 nssa translate type7 suppress-fa", +# "area 5 nssa default-information-originate metric 10", +# "router ospf 200 vrf blue", +# "no auto-cost reference-bandwidth 4", +# "no distribute-list 10 out", +# "no distribute-list 123 in", +# "domain-id 192.0.4.1", +# "max-metric router-lsa on-startup 200", +# "maximum-paths 15", +# "ttl-security all-interfaces hops 7", +# "area 10 authentication message-digest", +# "no area 10 capability default-exclusion", +# "area 10 default-cost 10" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.4.1 +# max-metric router-lsa on-startup 200 +# ttl-security all-interfaces hops 7 +# area 10 authentication message-digest +# area 10 default-cost 10 +# maximum-paths 15 +# router ospf 100 vrf ospf_vrf +# domain-id 192.0.5.1 +# auto-cost reference-bandwidth 5 +# area 5 authentication message-digest +# area 5 nssa default-information-originate metric 10 +# area 5 nssa translate type7 suppress-fa +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 5 capability default-exclusion +# area 5 authentication +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_in in +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +# Using Gathered + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +- name: Gather OSPFV2 provided configurations + cisco.ios.ios_ospfv2: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "5", +# "authentication": { +# "enable": true +# }, +# "capability": true +# }, +# { +# "area_id": "10", +# "authentication": { +# "message_digest": true +# }, +# "default_cost": 10, +# "filter_list": [ +# { +# "direction": "in", +# "name": "test_prefix_in" +# }, +# { +# "direction": "out", +# "name": "test_prefix_out" +# } +# ], +# "nssa": { +# "default_information_originate": { +# "metric": 10 +# }, +# "translate": "suppress-fa" +# } +# } +# ], +# "default_information": { +# "originate": true +# }, +# "max_metric": { +# "on_startup": { +# "time": 110 +# }, +# "router_lsa": true +# }, +# "network": { +# "address": "198.51.100.0", +# "area": "5", +# "wildcard_bits": "0.0.0.255" +# }, +# "process_id": 1 +# }, +# { +# "areas": [ +# { +# "area_id": "10", +# "capability": true +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 4 +# }, +# "distribute_list": { +# "acls": [ +# { +# "direction": "out", +# "name": "10" +# }, +# { +# "direction": "in", +# "name": "123" +# } +# ] +# }, +# "domain_id": { +# "ip_address": { +# "address": "192.0.3.1" +# } +# }, +# "max_metric": { +# "on_startup": { +# "time": 100 +# }, +# "router_lsa": true +# }, +# "process_id": 200, +# "vrf": "blue" +# } +# ] +# } + +# After state: +# ------------ +# +# router-ios#sh running-config | section ^router ospf +# router ospf 200 vrf blue +# domain-id 192.0.3.1 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# area 10 capability default-exclusion +# distribute-list 10 out +# distribute-list 123 in +# router ospf 1 +# max-metric router-lsa on-startup 110 +# area 10 authentication message-digest +# area 10 nssa default-information-originate metric 10 +# area 10 nssa translate type7 suppress-fa +# area 10 default-cost 10 +# area 10 filter-list prefix test_prefix_out out +# network 198.51.100.0 0.0.0.255 area 5 +# default-information originate + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_ospfv2: + config: + processes: + - process_id: 1 + max_metric: + router_lsa: true + on_startup: + time: 110 + areas: + - area_id: "5" + capability: true + authentication: + enable: true + - area_id: "10" + authentication: + message_digest: true + nssa: + default_information_originate: + metric: 10 + translate: suppress-fa + default_cost: 10 + filter_list: + - name: test_prefix_in + direction: in + - name: test_prefix_out + direction: out + network: + - address: 198.51.100.0 + wildcard_bits: 0.0.0.255 + area: 5 + default_information: + originate: true + - process_id: 200 + vrf: blue + domain_id: + ip_address: + address: 192.0.3.1 + max_metric: + router_lsa: true + on_startup: + time: 100 + auto_cost: + reference_bandwidth: 4 + areas: + - area_id: "10" + capability: true + distribute_list: + acls: + - name: 10 + direction: out + - name: 123 + direction: in + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "router ospf 200 vrf blue", +# "auto-cost reference-bandwidth 4", +# "distribute-list 10 out", +# "distribute-list 123 in", +# "domain-id 192.0.3.1", +# "max-metric router-lsa on-startup 100", +# "area 10 capability default-exclusion", +# "router ospf 1", +# "default-information originate", +# "max-metric router-lsa on-startup 110", +# "network 198.51.100.0 0.0.0.255 area 5", +# "area 10 authentication message-digest", +# "area 10 default-cost 10", +# "area 10 nssa translate type7 suppress-fa", +# "area 10 nssa default-information-originate metric 10", +# "area 10 filter-list prefix test_prefix_out out", +# "area 10 filter-list prefix test_prefix_in in", +# "area 5 authentication", +# "area 5 capability default-exclusion" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# router ospf 100 +# auto-cost reference-bandwidth 5 +# domain-id 192.0.5.1 +# area 5 authentication message-digest +# area 5 nssa translate type7 suppress-fa +# area 5 nssa default-information-originate metric 10 + +- name: Parse the provided configuration with the existing running configuration + cisco.ios.ios_ospfv2: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": { +# "processes": [ +# { +# "areas": [ +# { +# "area_id": "5", +# "authentication": { +# "message_digest": true +# }, +# "nssa": { +# "default_information_originate": { +# "metric": 10 +# }, +# "translate": "suppress-fa" +# } +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 5 +# }, +# "domain_id": { +# "ip_address": { +# "address": "192.0.5.1" +# } +# }, +# "process_id": 100 +# } +# ] +# } +""" + +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['router ospf 200 vrf blue', 'auto-cost reference-bandwidth 5', 'domain-id 192.0.4.1'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ospfv2.ospfv2 import ( + Ospfv2Args, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.ospfv2.ospfv2 import ( + Ospfv2, +) + + +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=Ospfv2Args.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + + result = Ospfv2(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_ospfv3.py b/ansible_collections/cisco/ios/plugins/modules/ios_ospfv3.py new file mode 100644 index 000000000..376b23645 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_ospfv3.py @@ -0,0 +1,1978 @@ +#!/usr/bin/python +# +# -*- coding: utf-8 -*- +# Copyright 2020 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 ios_ospfv3 +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = """ +module: ios_ospfv3 +short_description: Resource module to configure OSPFv3. +description: + This module configures and manages the Open Shortest Path First (OSPF) + version 3 on IOS platforms. +version_added: 1.1.0 +author: Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: A list of configurations for ospfv3. + type: dict + suboptions: + processes: + description: List of OSPF instance configurations. + type: list + elements: dict + suboptions: + process_id: + description: Process ID + required: true + type: int + address_family: + description: Enter Address Family command mode + type: list + elements: dict + suboptions: + afi: + description: Enter Address Family command mode + type: str + choices: + - ipv4 + - ipv6 + unicast: + description: Address Family modifier + type: bool + vrf: + description: Specify parameters for a VPN Routing/Forwarding instance + type: str + adjacency: + description: Control adjacency formation + type: dict + suboptions: + min_adjacency: + description: + - Initial number of adjacencies allowed to be forming in an area + - Please refer vendor documentation for valid values + type: int + none: + description: No initial + type: bool + max_adjacency: + description: + - Maximum number of adjacencies allowed to be forming + - Please refer vendor documentation for valid values + type: int + disable: + description: Disable adjacency staggering + type: bool + areas: + description: OSPF area parameters + type: list + elements: dict + suboptions: + area_id: + description: + - OSPF area ID as a decimal value. Please refer vendor documentation + of Valid values. + - OSPF area ID in IP address format(e.g. A.B.C.D) + type: str + authentication: + description: Authentication parameters + type: dict + suboptions: + key_chain: + description: Use a key-chain for cryptographic authentication keys + type: str + "null": + description: Use no authentication + type: bool + default_cost: + description: + - Set the summary default-cost of a NSSA/stub area + - Stub's advertised external route metric + - Note, please refer vendor documentation for respective valid values + type: int + filter_list: + description: Filter networks between OSPFv3 areas + type: list + elements: dict + suboptions: + name: + description: Name of an IP prefix-list + type: str + direction: + description: The direction to apply on the filter networks sent to and from this area. + type: str + choices: ["in", "out"] + required: true + normal: + description: Specify a normal area type + type: bool + nssa: + description: Specify a NSSA area + type: dict + suboptions: + set: + description: Enable a NSSA area + type: bool + default_information_originate: + description: Originate Type 7 default into NSSA area + type: dict + suboptions: + metric: + description: OSPF default metric + type: int + metric_type: + description: + - OSPF metric type for default routes + - OSPF Link State type + type: int + choices: [1, 2] + nssa_only: + description: Limit default advertisement to this NSSA area + type: bool + no_redistribution: + description: No redistribution into this NSSA area + type: bool + no_summary: + description: Do not send summary LSA into NSSA + type: bool + translate: + description: + - Translate LSA + - Always translate LSAs on this ABR + - Suppress forwarding address in translated LSAs + type: str + choices: ["always", "suppress-fa"] + ranges: + description: Summarize routes matching address/mask (border routers only) + type: list + elements: dict + suboptions: + address: + description: IP address to match + type: str + netmask: + description: IP mask for address + type: str + advertise: + description: + - Advertise this range (default) + - Since, advertise when enabled is not shown in running-config + idempotency won't be maintained for the play in the second or + next run of the play. + type: bool + cost: + description: User specified metric for this range + type: int + not_advertise: + description: DoNotAdvertise this range + type: bool + sham_link: + description: Define a sham link and its parameters + type: dict + suboptions: + source: + description: IPv6 address associated with sham-link source (X:X:X:X::X) + type: str + destination: + description: IPv6 address associated with sham-link destination (X:X:X:X::X) + type: str + authentication: + description: Authentication parameters + type: dict + suboptions: + key_chain: + description: Use a key-chain for cryptographic authentication keys + type: str + "null": + description: Use no authentication + type: bool + cost: + description: + - Associate a cost with the sham-link + - Cost of the sham-link + type: int + ttl_security: + description: + - TTL security check + - maximum number of hops allowed + type: int + stub: + description: + - Specify a stub area + - Backbone can not be configured as stub area + type: dict + suboptions: + set: + description: Enable a stub area + type: bool + no_summary: + description: Do not send summary LSA into stub area + type: bool + authentication: + description: + - Authentication parameters + - Authentication operation mode + type: dict + suboptions: + deployment: + description: Deployment mode of operation + type: bool + normal: + description: Normal mode of operation + type: bool + auto_cost: + description: Calculate OSPF interface cost according to bandwidth + type: dict + suboptions: + set: + description: Enable OSPF auto-cost + type: bool + reference_bandwidth: + description: + - Use reference bandwidth method to assign OSPF cost + - Note, refer vendor documentation for respective valid values + type: int + bfd: + description: BFD configuration commands + type: dict + suboptions: + all_interfaces: + description: Enable BFD on all interfaces + type: bool + disable: + description: Disable BFD on all interfaces + type: bool + capability: + description: + - Enable a specific feature + - Do not perform PE specific checks + type: bool + compatible: + description: OSPFv3 router compatibility list + type: dict + suboptions: + rfc1583: + description: compatible with RFC 1583 + type: bool + rfc1587: + description: compatible with RFC 1587 + type: bool + rfc5243: + description: supports DBD exchange optimization + type: bool + default_information: + description: Control distribution of default information + type: dict + suboptions: + originate: + description: Distribute a default route + type: bool + always: + description: Always advertise default route + type: bool + metric: + description: + - OSPF default metric + - Note, refer vendor documentation for respective valid values + type: int + metric_type: + description: + - OSPF metric type for default routes + - Note, please refer vendor documentation for respective valid range + type: int + route_map: + description: Route-map reference name + type: str + default_metric: + description: Set metric of redistributed routes + type: int + discard_route: + description: Enable or disable discard-route installation + type: dict + suboptions: + sham_link: + description: Discard route for sham-link routes + type: bool + external: + description: Discard route for summarised redistributed routes + type: bool + internal: + description: Discard route for summarised inter-area routes + type: bool + distance: + description: + - Define an administrative distance + - Note, please refer vendor documentation for respective valid range + type: int + distribute_list: + description: Filter networks in routing updates + type: dict + suboptions: + acls: + description: IP access list + type: list + elements: dict + suboptions: + name: + description: IP access list name/number + type: str + required: true + direction: + description: Filter incoming and outgoing routing updates. + type: str + required: true + choices: ["in", "out"] + interface: + description: + - Interface configuration (GigabitEthernet A/B) + - Valid with incoming traffic + type: str + protocol: + description: + - Protocol config (bgp 1). + - Valid with outgoing traffic + type: str + prefix: + description: Filter prefixes in routing updates + type: dict + suboptions: + name: + description: Name of an IP prefix-list + type: str + required: true + gateway_name: + description: Gateway name for filtering incoming updates based on gateway + type: str + direction: + description: Filter incoming and outgoing routing updates. + type: str + required: true + choices: ["in", "out"] + interface: + description: + - Interface configuration (GigabitEthernet A/B) + - Valid with incoming traffic + type: str + protocol: + description: + - Protocol config (bgp 1). + - Valid with outgoing traffic + type: str + route_map: + description: Filter prefixes in routing updates + type: dict + suboptions: + name: + description: Route-map name + type: str + required: true + event_log: + description: Event Logging + type: dict + suboptions: + enable: + description: Enable event Logging + type: bool + one_shot: + description: Disable Logging When Log Buffer Becomes Full + type: bool + pause: + description: Pause Event Logging + type: bool + size: + description: + - Maximum Number of Events Stored in the Event Log + - Note, refer vendor documentation for respective valid values + type: int + graceful_restart: + description: + - Graceful-restart options + - helper support + type: dict + suboptions: + enable: + description: helper support enabled + type: bool + disable: + description: disable helper support + type: bool + strict_lsa_checking: + description: enable helper strict LSA checking + type: bool + interface_id: + description: Source of the interface ID + type: dict + suboptions: + ios_if_index: + description: IOS interface number + type: bool + snmp_if_index: + description: SNMP MIB ifIndex + type: bool + limit: + description: Limit a specific OSPF feature + type: dict + suboptions: + dc: + description: Demand circuit retransmissions + type: dict + suboptions: + number: + description: The maximum number of retransmissions + type: int + disable: + description: Disble the feature + type: bool + non_dc: + description: Non-demand-circuit retransmissions + type: dict + suboptions: + number: + description: The maximum number of retransmissions + type: int + disable: + description: Disble the feature + type: bool + local_rib_criteria: + description: Enable or disable usage of local RIB as route criteria + type: dict + suboptions: + enable: + description: Enable usage of local RIB as route criteria + type: bool + forwarding_address: + description: Local RIB used to validate external/NSSA forwarding addresses + type: bool + inter_area_summary: + description: Local RIB used as criteria for inter-area summaries + type: bool + nssa_translation: + description: Local RIB used as criteria for NSSA translation + type: bool + log_adjacency_changes: + description: Log changes in adjacency state + type: dict + suboptions: + set: + description: Log changes in adjacency state + type: bool + detail: + description: Log all state changes + type: bool + manet: + description: Specify MANET OSPF parameters + type: dict + suboptions: + cache: + description: Specify MANET cache sizes + type: dict + suboptions: + acknowledgement: + description: + - Specify MANET acknowledgement cache size + - Maximum number of acknowledgements in cache + type: int + update: + description: + - Specify MANET LSA cache size + - Maximum number of LSAs in cache + type: int + hello: + description: Unicast Hellos rather than multicast + type: dict + suboptions: + multicast: + description: Multicast Hello requests and responses rather than unicast + type: bool + unicast: + description: Unicast Hello requests and responses rather than multicast + type: bool + peering: + description: MANET OSPF Smart Peering + type: dict + suboptions: + set: + description: Enable selective peering + type: bool + disable: + description: Disable selective peering + type: bool + per_interface: + description: Select peers per interface rather than per node + type: bool + redundancy: + description: + - Redundant paths + - Number of redundant OSPF paths + type: int + willingness: + description: Specify and Relay willingness value + type: int + max_lsa: + description: Maximum number of non self-generated LSAs to accept + type: dict + suboptions: + number: + description: + - Maximum number of non self-generated LSAs to accept + - Note, refer vendor documentation for respective valid values + type: int + threshold_value: + description: + - Threshold value (%) at which to generate a warning msg + - Note, refer vendor documentation for respective valid values + type: int + ignore_count: + description: + - Maximum number of times adjacencies can be suppressed + - Note, refer vendor documentation for respective valid values + type: int + ignore_time: + description: + - Number of minutes during which all adjacencies are suppressed + - Note, refer vendor documentation for respective valid values + type: int + reset_time: + description: + - Number of minutes after which ignore-count is reset to zero + - Note, refer vendor documentation for respective valid values + type: int + warning_only: + description: Only give a warning message when limit is exceeded + type: bool + max_metric: + description: + - Set maximum metric + - Maximum metric in self-originated router-LSAs + type: dict + suboptions: + disable: + description: disable maximum metric in self-originated router-LSAs + type: bool + external_lsa: + description: + - Override external-lsa metric with max-metric value + - Overriding metric in external-LSAs + - Note, refer vendor documentation for respective valid values + type: int + inter_area_lsas: + description: + - Override inter-area-lsas metric with max-metric value + - Overriding metric in inter-area-LSAs + - Note, refer vendor documentation for respective valid values + type: int + on_startup: + description: Set maximum metric temporarily after reboot + type: dict + suboptions: + time: + description: + - Time, in seconds, router-LSAs are originated with max-metric + - Note, please refer vendor documentation for respective valid range + type: int + wait_for_bgp: + description: Let BGP decide when to originate router-LSA with normal metric + type: bool + stub_prefix_lsa: + description: Set maximum metric for stub links in prefix LSAs + type: bool + maximum_paths: + description: + - Forward packets over multiple paths + - Number of paths + type: int + passive_interface: + description: Suppress routing updates on an interface + type: str + prefix_suppression: + description: Prefix suppression + type: dict + suboptions: + enable: + description: Enable prefix suppression + type: bool + disable: + description: Disable prefix suppression + type: bool + queue_depth: + description: Hello/Router process queue depth + type: dict + suboptions: + hello: + description: OSPF Hello process queue depth + type: dict + suboptions: + max_packets: + description: maximum number of packets in the queue + type: int + unlimited: + description: Unlimited queue depth + type: bool + update: + description: OSPF Router process queue depth + type: dict + suboptions: + max_packets: + description: maximum number of packets in the queue + type: int + unlimited: + description: Unlimited queue depth + type: bool + router_id: + description: + - Router-id address for this OSPF process + - OSPF router-id in IP address format (A.B.C.D) + type: str + shutdown: + description: Shutdown the router process + type: dict + suboptions: + enable: + description: Shutdown the router process + type: bool + disable: + description: Disable Shutdown + type: bool + summary_prefix: + description: Configure IP address summaries + type: dict + suboptions: + address: + description: + - IP summary address (A.B.C.D) + - IP prefix <network>/<length> (A.B.C.D/nn) + type: str + mask: + description: IP Summary mask + type: str + not_advertise: + description: Do not advertise or translate + type: bool + nssa_only: + description: Limit summary to NSSA areas + type: bool + tag: + description: Set tag + type: int + timers: + description: Adjust routing timers + type: dict + suboptions: + lsa: + description: + - OSPF LSA timers, arrival timer + - The minimum interval in milliseconds between accepting the same LSA + - Note, refer vendor documentation for respective valid values + type: int + manet: + description: OSPF MANET timers + type: dict + suboptions: + cache: + description: Specify MANET cache sizes + type: dict + suboptions: + acknowledgement: + description: Specify MANET acknowledgement cache size + type: int + redundancy: + description: Specify MANET LSA cache size + type: int + hello: + description: + - Unicast Hellos rather than multicast + - Unicast Hello requests and responses rather than multicast + type: bool + peering: + description: MANET OSPF Smart Peering + type: dict + suboptions: + set: + description: Enable selective peering + type: bool + per_interface: + description: Select peers per interface rather than per node + type: bool + redundancy: + description: + - Redundant paths + - Number of redundant OSPF paths + type: int + willingness: + description: Specify and Relay willingness value + type: int + pacing: + description: OSPF pacing timers + type: dict + suboptions: + flood: + description: + - OSPF flood pacing timer + - The minimum interval in msec to pace limit flooding on interface + - Note, refer vendor documentation for respective valid values + type: int + lsa_group: + description: + - OSPF LSA group pacing timer + - Interval in sec between group of LSA being refreshed or maxaged + - Note, refer vendor documentation for respective valid values + type: int + retransmission: + description: + - OSPF retransmission pacing timer + - The minimum interval in msec between neighbor retransmissions + - Note, refer vendor documentation for respective valid values + type: int + throttle: + description: OSPF throttle timers + type: dict + suboptions: + lsa: + description: OSPF LSA throttle timers + type: dict + suboptions: + first_delay: + description: + - Delay to generate first occurrence of LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + min_delay: + description: + - Minimum delay between originating the same LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + max_delay: + description: + - Maximum delay between originating the same LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + spf: + description: OSPF SPF throttle timers + - Delay between receiving a change to SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: dict + suboptions: + receive_delay: + description: + - Delay between receiving a change to SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + between_delay: + description: + - Delay between first and second SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + max_delay: + description: + - Maximum wait time in milliseconds for SPF calculations + - Note, refer vendor documentation for respective valid values + type: int + adjacency: + description: Control adjacency formation + type: dict + suboptions: + min_adjacency: + description: + - Initial number of adjacencies allowed to be forming in an area + - Please refer vendor documentation for valid values + type: int + max_adjacency: + description: + - Maximum number of adjacencies allowed to be forming + - Please refer vendor documentation for valid values + type: int + none: + description: No initial + type: bool + areas: + description: OSPF area parameters + type: list + elements: dict + suboptions: + area_id: + description: + - OSPF area ID as a decimal value. Please refer vendor documentation + of Valid values. + - OSPF area ID in IP address format(e.g. A.B.C.D) + type: str + authentication: + description: Authentication parameters + type: dict + suboptions: + key_chain: + description: Use a key-chain for cryptographic authentication keys + type: str + ipsec: + description: Use IPsec authentication + type: dict + suboptions: + spi: + description: Set the SPI (Security Parameters Index) + type: int + md5: + description: Use MD5 authentication + type: int + sha1: + description: Use SHA-1 authentication + type: int + hex_string: + description: SHA-1 key (40 chars) + type: str + default_cost: + description: + - Set the summary default-cost of a NSSA/stub area + - Stub's advertised external route metric + - Note, please refer vendor documentation for respective valid values + type: int + nssa: + description: Specify a NSSA area + type: dict + suboptions: + set: + description: Enable a NSSA area + type: bool + default_information_originate: + description: Originate Type 7 default into NSSA area + type: dict + suboptions: + metric: + description: OSPF default metric + type: int + metric_type: + description: + - OSPF metric type for default routes + - OSPF Link State type + type: int + choices: [1, 2] + nssa_only: + description: Limit default advertisement to this NSSA area + type: bool + no_redistribution: + description: No redistribution into this NSSA area + type: bool + no_summary: + description: Do not send summary LSA into NSSA + type: bool + translate: + description: + - Translate LSA + - Always translate LSAs on this ABR + - Suppress forwarding address in translated LSAs + type: str + choices: ["always", "suppress-fa"] + stub: + description: + - Specify a stub area + - Backbone can not be configured as stub area + type: dict + suboptions: + set: + description: Enable a stub area + type: bool + no_summary: + description: Do not send summary LSA into stub area + type: bool + authentication: + description: + - Authentication parameter mode + - Deployment mode of operation + type: bool + auto_cost: + description: Calculate OSPF interface cost according to bandwidth + type: dict + suboptions: + set: + description: Enable OSPF auto-cost + type: bool + reference_bandwidth: + description: + - Use reference bandwidth method to assign OSPF cost + - Note, refer vendor documentation for respective valid values + type: int + bfd: + description: + - BFD configuration commands + - Enable BFD on all interfaces + type: bool + compatible: + description: OSPFv3 router compatibility list + type: dict + suboptions: + rfc1583: + description: compatible with RFC 1583 + type: bool + rfc1587: + description: compatible with RFC 1587 + type: bool + rfc5243: + description: supports DBD exchange optimization + type: bool + event_log: + description: Event Logging + type: dict + suboptions: + enable: + description: Enable event Logging + type: bool + one_shot: + description: Disable Logging When Log Buffer Becomes Full + type: bool + pause: + description: Pause Event Logging + type: bool + size: + description: + - Maximum Number of Events Stored in the Event Log + - Note, refer vendor documentation for respective valid values + type: int + graceful_restart: + description: Graceful-restart options for helper support + type: dict + suboptions: + disable: + description: disable helper support + type: bool + strict_lsa_checking: + description: enable helper strict LSA checking + type: bool + help: + description: Description of the interactive help system + type: bool + interface_id: + description: + - Source of the interface ID + - SNMP MIB ifIndex + type: bool + limit: + description: Limit a specific OSPF feature and LS update, DBD, and LS request retransmissions + type: dict + suboptions: + dc: + description: Demand circuit retransmissions + type: dict + suboptions: + number: + description: The maximum number of retransmissions + type: int + disable: + description: Disable the feature + type: bool + non_dc: + description: Non-demand-circuit retransmissions + type: dict + suboptions: + number: + description: The maximum number of retransmissions + type: int + disable: + description: Disable the feature + type: bool + local_rib_criteria: + description: Enable or disable usage of local RIB as route criteria + type: dict + suboptions: + enable: + description: Enable usage of local RIB as route criteria + type: bool + forwarding_address: + description: Local RIB used to validate external/NSSA forwarding addresses + type: bool + inter_area_summary: + description: Local RIB used as criteria for inter-area summaries + type: bool + nssa_translation: + description: Local RIB used as criteria for NSSA translation + type: bool + log_adjacency_changes: + description: Log changes in adjacency state + type: dict + suboptions: + set: + description: Log changes in adjacency state + type: bool + detail: + description: Log all state changes + type: bool + manet: + description: Specify MANET OSPF parameters + type: dict + suboptions: + cache: + description: Specify MANET cache sizes + type: dict + suboptions: + acknowledgement: + description: Specify MANET acknowledgement cache size + type: int + redundancy: + description: Specify MANET LSA cache size + type: int + hello: + description: + - Unicast Hellos rather than multicast + - Unicast Hello requests and responses rather than multicast + type: bool + peering: + description: MANET OSPF Smart Peering + type: dict + suboptions: + set: + description: Enable selective peering + type: bool + per_interface: + description: Select peers per interface rather than per node + type: bool + redundancy: + description: + - Redundant paths + - Number of redundant OSPF paths + type: int + willingness: + description: Specify and Relay willingness value + type: int + max_lsa: + description: Maximum number of non self-generated LSAs to accept + type: dict + suboptions: + number: + description: + - Maximum number of non self-generated LSAs to accept + - Note, refer vendor documentation for respective valid values + type: int + threshold_value: + description: + - Threshold value (%) at which to generate a warning msg + - Note, refer vendor documentation for respective valid values + type: int + ignore_count: + description: + - Maximum number of times adjacencies can be suppressed + - Note, refer vendor documentation for respective valid values + type: int + ignore_time: + description: + - Number of minutes during which all adjacencies are suppressed + - Note, refer vendor documentation for respective valid values + type: int + reset_time: + description: + - Number of minutes after which ignore-count is reset to zero + - Note, refer vendor documentation for respective valid values + type: int + warning_only: + description: Only give a warning message when limit is exceeded + type: bool + max_metric: + description: Set maximum metric + type: dict + suboptions: + router_lsa: + description: Maximum metric in self-originated router-LSAs + type: bool + required: true + external_lsa: + description: + - Override external-lsa metric with max-metric value + - Overriding metric in external-LSAs + - Note, refer vendor documentation for respective valid values + type: int + include_stub: + description: Set maximum metric for stub links in router-LSAs + type: bool + on_startup: + description: Set maximum metric temporarily after reboot + type: dict + suboptions: + time: + description: + - Time, in seconds, router-LSAs are originated with max-metric + - Note, please refer vendor documentation for respective valid range + type: int + wait_for_bgp: + description: Let BGP decide when to originate router-LSA with normal metric + type: bool + summary_lsa: + description: + - Override summary-lsa metric with max-metric value + - Note, please refer vendor documentation for respective valid range + type: int + passive_interface: + description: Suppress routing updates on an interface + type: str + prefix_suppression: + description: Enable prefix suppression + type: bool + queue_depth: + description: Hello/Router process queue depth + type: dict + suboptions: + hello: + description: OSPF Hello process queue depth + type: dict + suboptions: + max_packets: + description: maximum number of packets in the queue + type: int + unlimited: + description: Unlimited queue depth + type: bool + router_id: + description: + - Router-id address for this OSPF process + - OSPF router-id in IP address format (A.B.C.D) + type: str + shutdown: + description: Shutdown the router process + type: bool + timers: + description: Adjust routing timers + type: dict + suboptions: + lsa: + description: + - OSPF LSA timers, arrival timer + - The minimum interval in milliseconds between accepting the same LSA + - Note, refer vendor documentation for respective valid values + type: int + manet: + description: OSPF MANET timers + type: dict + suboptions: + cache: + description: Specify MANET cache sizes + type: dict + suboptions: + acknowledgement: + description: Specify MANET acknowledgement cache size + type: int + redundancy: + description: Specify MANET LSA cache size + type: int + hello: + description: + - Unicast Hellos rather than multicast + - Unicast Hello requests and responses rather than multicast + type: bool + peering: + description: MANET OSPF Smart Peering + type: dict + suboptions: + set: + description: Enable selective peering + type: bool + per_interface: + description: Select peers per interface rather than per node + type: bool + redundancy: + description: + - Redundant paths + - Number of redundant OSPF paths + type: int + willingness: + description: Specify and Relay willingness value + type: int + pacing: + description: OSPF pacing timers + type: dict + suboptions: + flood: + description: + - OSPF flood pacing timer + - The minimum interval in msec to pace limit flooding on interface + - Note, refer vendor documentation for respective valid values + type: int + lsa_group: + description: + - OSPF LSA group pacing timer + - Interval in sec between group of LSA being refreshed or maxaged + - Note, refer vendor documentation for respective valid values + type: int + retransmission: + description: + - OSPF retransmission pacing timer + - The minimum interval in msec between neighbor retransmissions + - Note, refer vendor documentation for respective valid values + type: int + throttle: + description: OSPF throttle timers + type: dict + suboptions: + lsa: + description: OSPF LSA throttle timers + type: dict + suboptions: + first_delay: + description: + - Delay to generate first occurrence of LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + min_delay: + description: + - Minimum delay between originating the same LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + max_delay: + description: + - Maximum delay between originating the same LSA in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + spf: + description: OSPF SPF throttle timers + - Delay between receiving a change to SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: dict + suboptions: + receive_delay: + description: + - Delay between receiving a change to SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + between_delay: + description: + - Delay between first and second SPF calculation in milliseconds + - Note, refer vendor documentation for respective valid values + type: int + max_delay: + description: + - Maximum wait time in milliseconds for SPF calculations + - Note, refer vendor documentation for respective valid values + type: int + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by + executing the command B(sh running-config | section ^router ospfv3). + - 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 + state: + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged +""" + +EXAMPLES = """ +# Using deleted + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +- name: Delete provided OSPF V3 processes + cisco.ios.ios_ospfv3: + config: + processes: + - process_id: 1 + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no router ospfv3 1" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +# Using deleted without any config passed (NOTE: This will delete all OSPFV3 configuration from device) + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +- name: Delete all OSPF processes + cisco.ios.ios_ospfv3: + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no router ospfv3 200", +# "no router ospfv3 1" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^router ospfv3 +# router-ios# + +# Using merged + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospfv3 +# router-ios# + +- name: Merge provided OSPFV3 configuration + cisco.ios.ios_ospfv3: + config: + processes: + - process_id: 1 + max_metric: + router_lsa: true + on_startup: + time: 110 + address_family: + - afi: ipv4 + unicast: true + vrf: blue + adjacency: + min_adjacency: 50 + max_adjacency: 50 + areas: + - area_id: 25 + nssa: + default_information_originate: + metric: 25 + nssa_only: true + areas: + - area_id: "10" + nssa: + default_information_originate: + metric: 10 + timers: + throttle: + lsa: + first_delay: 12 + min_delay: 14 + max_delay: 16 + - process_id: 200 + address_family: + - afi: ipv4 + unicast: true + adjacency: + min_adjacency: 200 + max_adjacency: 200 + max_metric: + router_lsa: true + on_startup: + time: 100 + auto_cost: + reference_bandwidth: 4 + state: merged + +# Commands Fired: +# --------------- +# +# "commands": [ +# "router ospfv3 1", +# "max-metric router-lsa on-startup 110", +# "area 10 nssa default-information-originate metric 10", +# "address-family ipv4 unicast vrf blue", +# "adjacency stagger 50 50", +# "area 25 nssa default-information-originate metric 25 nssa-only", +# "exit-address-family", +# "router ospfv3 200", +# "auto-cost reference-bandwidth 4", +# "max-metric router-lsa on-startup 100", +# "address-family ipv4 unicast", +# "adjacency stagger 200 200", +# "exit-address-family" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +# Using overridden + +# Before state: +# ------------- +# +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +- name: Override provided OSPFV3 configuration + cisco.ios.ios_ospfv3: + config: + processes: + - process_id: 200 + max_metric: + router_lsa: true + on_startup: + time: 200 + address_family: + - afi: ipv4 + unicast: true + adjacency: + min_adjacency: 50 + max_adjacency: 50 + areas: + - area_id: 200 + nssa: + default_information_originate: + metric: 200 + nssa_only: true + areas: + - area_id: "10" + nssa: + default_information_originate: + metric: 10 + state: overridden + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no router ospfv3 1", +# "router ospfv3 200", +# "no auto-cost reference-bandwidth 4", +# "max-metric router-lsa on-startup 200", +# "area 10 nssa default-information-originate metric 10", +# "address-family ipv4 unicast", +# "adjacency stagger 50 50", +# "area 200 nssa default-information-originate metric 200 nssa-only", +# "exit-address-family" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 200 +# max-metric router-lsa on-startup 200 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast +# adjacency stagger 50 50 +# area 200 nssa default-information-originate metric 200 nssa-only +# exit-address-family + +# Using replaced + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +- name: Replaced provided OSPFV3 configuration + cisco.ios.ios_ospfv3: + config: + processes: + - process_id: 200 + max_metric: + router_lsa: true + on_startup: + time: 200 + address_family: + - afi: ipv4 + unicast: true + adjacency: + min_adjacency: 50 + max_adjacency: 50 + areas: + - area_id: 200 + nssa: + default_information_originate: + metric: 200 + nssa_only: true + areas: + - area_id: "10" + nssa: + default_information_originate: + metric: 10 + state: replaced + +# Commands Fired: +# --------------- +# "commands": [ +# "router ospfv3 200", +# "no auto-cost reference-bandwidth 4", +# "max-metric router-lsa on-startup 200", +# "area 10 nssa default-information-originate metric 10", +# "address-family ipv4 unicast", +# "adjacency stagger 50 50", +# "area 200 nssa default-information-originate metric 200 nssa-only", +# "exit-address-family" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 200 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast +# adjacency stagger 50 50 +# area 200 nssa default-information-originate metric 200 nssa-only +# exit-address-family + +# Using Gathered + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +- name: Gather OSPFV3 provided configurations + cisco.ios.ios_ospfv3: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": { +# "processes": [ +# { +# "address_family": [ +# { +# "adjacency": { +# "max_adjacency": 50, +# "min_adjacency": 50 +# }, +# "afi": "ipv4", +# "areas": [ +# { +# "area_id": "25", +# "nssa": { +# "default_information_originate": { +# "metric": 25, +# "nssa_only": true +# } +# } +# } +# ], +# "unicast": true, +# "vrf": "blue" +# } +# ], +# "areas": [ +# { +# "area_id": "10", +# "nssa": { +# "default_information_originate": { +# "metric": 10 +# } +# } +# } +# ], +# "max_metric": { +# "on_startup": { +# "time": 110 +# }, +# "router_lsa": true +# }, +# "process_id": 1 +# }, +# { +# "address_family": [ +# { +# "adjacency": { +# "max_adjacency": 200, +# "min_adjacency": 200 +# }, +# "afi": "ipv4", +# "unicast": true +# } +# ], +# "auto_cost": { +# "reference_bandwidth": 4 +# }, +# "max_metric": { +# "on_startup": { +# "time": 100 +# }, +# "router_lsa": true +# }, +# "process_id": 200 +# } +# ] +# } + +# After state: +# ------------ +# +# router-ios#sh running-config | section ^router ospfv3 +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_ospfv3: + config: + processes: + - process_id: 1 + max_metric: + router_lsa: true + on_startup: + time: 110 + address_family: + - afi: ipv4 + unicast: true + vrf: blue + adjacency: + min_adjacency: 50 + max_adjacency: 50 + areas: + - area_id: 25 + nssa: + default_information_originate: + metric: 25 + nssa_only: true + areas: + - area_id: "10" + nssa: + default_information_originate: + metric: 10 + timers: + throttle: + lsa: + first_delay: 12 + min_delay: 14 + max_delay: 16 + - process_id: 200 + address_family: + - afi: ipv4 + unicast: true + adjacency: + min_adjacency: 200 + max_adjacency: 200 + max_metric: + router_lsa: true + on_startup: + time: 100 + auto_cost: + reference_bandwidth: 4 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "router ospfv3 1", +# "max-metric router-lsa on-startup 110", +# "area 10 nssa default-information-originate metric 10", +# "address-family ipv4 unicast vrf blue", +# "adjacency stagger 50 50", +# "area 25 nssa default-information-originate metric 25 nssa-only", +# "exit-address-family", +# "router ospfv3 200", +# "auto-cost reference-bandwidth 4", +# "max-metric router-lsa on-startup 100", +# "address-family ipv4 unicast", +# "adjacency stagger 200 200", +# "exit-address-family" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# router ospfv3 1 +# max-metric router-lsa on-startup 110 +# area 10 nssa default-information-originate metric 10 +# ! +# address-family ipv4 unicast vrf blue +# adjacency stagger 50 50 +# area 25 nssa default-information-originate metric 25 nssa-only +# exit-address-family +# router ospfv3 200 +# max-metric router-lsa on-startup 100 +# auto-cost reference-bandwidth 4 +# ! +# address-family ipv4 unicast +# adjacency stagger 200 200 +# exit-address-family + +- name: Parse the provided configuration with the existing running configuration + cisco.ios.ios_ospfv3: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": { +# "processes": [ +# { +# "address_family": [ +# { +# "adjacency": { +# "max_adjacency": 50, +# "min_adjacency": 50 +# }, +# "afi": "ipv4", +# "areas": [ +# { +# "area_id": "25", +# "nssa": { +# "default_information_originate": { +# "metric": 25, +# "nssa_only": true +# } +# } +# } +# ], +# "unicast": true, +# "vrf": "blue" +# } +# ], +# "areas": [ +# { +# "area_id": "10", +# "nssa": { +# "default_information_originate": { +# "metric": 10 +# } +# } +# } +# ], +# "max_metric": { +# "on_startup": { +# "time": 110 +# }, +# "router_lsa": true +# }, +# "process_id": 1 +# } +# ] +# } + +""" + +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: dict +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['router ospfv3 1', 'address-family ipv4 unicast vrf blue', 'adjacency stagger 50 50'] +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ospfv3.ospfv3 import ( + Ospfv3Args, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.ospfv3.ospfv3 import ( + Ospfv3, +) + + +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=Ospfv3Args.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + + result = Ospfv3(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_ping.py b/ansible_collections/cisco/ios/plugins/modules/ios_ping.py new file mode 100644 index 000000000..c5fdd8153 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_ping.py @@ -0,0 +1,171 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_ping +short_description: Tests reachability using ping from IOS switch. +description: + - Tests reachability using ping from switch to a remote destination. + - For a general purpose network module, see the L(net_ping,https://docs.ansible.com/ansible/latest/collections/ansible/netcommon/net_ping_module.html) + module. + - For Windows targets, use the L(win_ping,https://docs.ansible.com/ansible/latest/collections/ansible/windows/win_ping_module.html) + module instead. + - For targets running Python, use the L(ping,https://docs.ansible.com/ansible/latest/collections/ansible/builtin/ping_module.html) + module instead. +version_added: 1.0.0 +author: + - Jacob McGill (@jmcgill298) + - Sagar Paul (@KB-perByte) +options: + count: + description: + - Number of packets to send. + type: int + afi: + description: + - Define echo type ip or ipv6. + choices: + - ip + - ipv6 + default: ip + type: str + dest: + description: + - The IP Address or hostname (resolvable by switch) of the remote node. + required: true + type: str + df_bit: + description: + - Set the DF bit. + default: false + type: bool + source: + description: + - The source IP Address. + type: str + egress: + description: + - Force egress interface bypassing routing. + type: str + ingress: + description: + - LAN source interface for Ingress. + type: str + timeout: + description: + - specify timeout interval. + type: int + state: + description: + - Determines if the expected result is success or fail. + choices: + - absent + - present + default: present + type: str + vrf: + description: + - The VRF to use for forwarding. + type: str +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - For a general purpose network module, see the L(net_ping,https://docs.ansible.com/ansible/latest/collections/ansible/netcommon/net_ping_module.html) + module. + - For Windows targets, use the L(win_ping,https://docs.ansible.com/ansible/latest/collections/ansible/windows/win_ping_module.html) + module instead. + - For targets running Python, use the L(ping,https://docs.ansible.com/ansible/latest/collections/ansible/builtin/ping_module.html) module instead. +""" + +EXAMPLES = """ +- name: Test reachability to 198.51.100.251 using default vrf + cisco.ios.ios_ping: + dest: 198.51.100.251 + +- name: Test reachability to 198.51.100.252 using prod vrf + cisco.ios.ios_ping: + dest: 198.51.100.252 + vrf: prod + afi: ip + +- name: Test un reachability to 198.51.100.253 using default vrf + cisco.ios.ios_ping: + dest: 198.51.100.253 + state: absent + +- name: Test reachability to 198.51.100.250 using prod vrf and setting count and source + cisco.ios.ios_ping: + dest: 198.51.100.250 + source: loopback0 + vrf: prod + count: 20 + +- name: Test reachability to 198.51.100.249 using df-bit and size + cisco.ios.ios_ping: + dest: 198.51.100.249 + df_bit: true + size: 1400 + +- name: Test reachability to ipv6 address + cisco.ios.ios_ping: + dest: 2001:db8:ffff:ffff:ffff:ffff:ffff:ffff + afi: ipv6 +""" + +RETURN = """ +commands: + description: Show the command sent. + returned: always + type: list + sample: ["ping vrf prod 198.51.100.251 count 20 source loopback0"] +packet_loss: + description: Percentage of packets lost. + returned: always + type: str + sample: "0%" +packets_rx: + description: Packets successfully received. + returned: always + type: int + sample: 20 +packets_tx: + description: Packets successfully transmitted. + returned: always + type: int + sample: 20 +rtt: + description: Show RTT stats. + returned: always + type: dict + sample: {"avg": 2, "max": 8, "min": 1} +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.ping.ping import ( + PingArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.ping.ping import Ping + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=PingArgs.argument_spec) + + result = Ping(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_prefix_lists.py b/ansible_collections/cisco/ios/plugins/modules/ios_prefix_lists.py new file mode 100644 index 000000000..23c9de9e1 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_prefix_lists.py @@ -0,0 +1,922 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for cisco.ios_prefix_lists +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_prefix_lists +short_description: Resource module to configure prefix lists. +description: + - This module configures and manages the attributes of prefix list on Cisco IOS. +version_added: 2.2.0 +author: Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: A list of configurations for Prefix lists. + type: list + elements: dict + suboptions: + afi: + description: + - The Address Family Indicator (AFI) for the prefix list. + type: str + choices: ["ipv4", "ipv6"] + prefix_lists: + description: List of Prefix-lists. + type: list + elements: dict + suboptions: + name: + description: Name of a prefix-list + type: str + description: + description: Prefix-list specific description + type: str + entries: + description: Prefix-lists supported params. + type: list + elements: dict + suboptions: + action: + description: Specify packets to be rejected or forwarded + type: str + choices: ["deny", "permit"] + sequence: + description: sequence number of an entry + type: int + description: + description: + - Prefix-list specific description + - Description param at entries level is DEPRECATED + - New Description is introduced at prefix_lists level, please + use the Description param defined at prefix_lists level instead of + Description param at entries level, as at this level description option + will get removed in a future release. + type: str + prefix: + description: + - IPv4 prefix <network>/<length>, e.g., A.B.C.D/nn + - IPv6 prefix <network>/<length>, e.g., X:X:X:X::X/<0-128> + type: str + ge: + description: Minimum prefix length to be matched + type: int + le: + description: Maximum prefix length to be matched + type: int + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by + executing the command B(sh bgp). + - 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 + state: + description: + - The state the configuration should be left in + - The states I(merged) is the default state which merges the want and have config, but + for Prefix-List module as the IOS platform doesn't allow update of Prefix-List over an + pre-existing Prefix-List, same way Prefix-Lists resource module will error out for + respective scenario and only addition of new Prefix-List over new sequence will be + allowed with merge state. + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(sh running-config + | section ^ip prefix-list|^ipv6 prefix-list) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged +""" + +EXAMPLES = """ +# Using deleted by Name + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# ip prefix-list 10 description this is test description +# ip prefix-list 10 seq 5 deny 1.0.0.0/8 le 15 +# ip prefix-list 10 seq 10 deny 35.0.0.0/8 ge 10 +# ip prefix-list 10 seq 15 deny 12.0.0.0/8 ge 15 +# ip prefix-list 10 seq 20 deny 14.0.0.0/8 ge 20 le 21 +# ip prefix-list test description this is test +# ip prefix-list test seq 50 deny 12.0.0.0/8 ge 15 +# ip prefix-list test_prefix description this is for prefix-list +# ip prefix-list test_prefix seq 5 deny 35.0.0.0/8 ge 10 le 15 +# ip prefix-list test_prefix seq 10 deny 35.0.0.0/8 ge 20 +# ipv6 prefix-list test_ipv6 description this is ipv6 prefix-list +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 + +- name: Delete provided Prefix lists config by Prefix name + cisco.ios.ios_prefix_lists: + config: + - afi: ipv4 + prefix_lists: + - name: 10 + - name: test_prefix + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no ip prefix-list 10", +# "no ip prefix-list test_prefix" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# ip prefix-list test description this is test +# ip prefix-list test seq 50 deny 12.0.0.0/8 ge 15 +# ipv6 prefix-list test_ipv6 description this is ipv6 prefix-list +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 + +# Using deleted by AFI + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# ip prefix-list 10 description this is test description +# ip prefix-list 10 seq 5 deny 1.0.0.0/8 le 15 +# ip prefix-list 10 seq 10 deny 35.0.0.0/8 ge 10 +# ip prefix-list 10 seq 15 deny 12.0.0.0/8 ge 15 +# ip prefix-list 10 seq 20 deny 14.0.0.0/8 ge 20 le 21 +# ip prefix-list test description this is test +# ip prefix-list test seq 50 deny 12.0.0.0/8 ge 15 +# ip prefix-list test_prefix description this is for prefix-list +# ip prefix-list test_prefix seq 5 deny 35.0.0.0/8 ge 10 le 15 +# ip prefix-list test_prefix seq 10 deny 35.0.0.0/8 ge 20 +# ipv6 prefix-list test_ipv6 description this is ipv6 prefix-list +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 + +- name: Delete provided Prefix lists config by AFI + cisco.ios.ios_prefix_lists: + config: + - afi: ipv4 + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no ip prefix-list test", +# "no ip prefix-list 10", +# "no ip prefix-list test_prefix" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# ipv6 prefix-list test_ipv6 description this is ipv6 prefix-list +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 + +# Using deleted without any config passed (NOTE: This will delete all Prefix lists configuration from device) + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# ip prefix-list 10 description this is test description +# ip prefix-list 10 seq 5 deny 1.0.0.0/8 le 15 +# ip prefix-list 10 seq 10 deny 35.0.0.0/8 ge 10 +# ip prefix-list 10 seq 15 deny 12.0.0.0/8 ge 15 +# ip prefix-list 10 seq 20 deny 14.0.0.0/8 ge 20 le 21 +# ip prefix-list test description this is test +# ip prefix-list test seq 50 deny 12.0.0.0/8 ge 15 +# ip prefix-list test_prefix description this is for prefix-list +# ip prefix-list test_prefix seq 5 deny 35.0.0.0/8 ge 10 le 15 +# ip prefix-list test_prefix seq 10 deny 35.0.0.0/8 ge 20 +# ipv6 prefix-list test_ipv6 description this is ipv6 prefix-list +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 + +- name: Delete all Prefix lists config + cisco.ios.ios_prefix_lists: + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no ip prefix-list test", +# "no ip prefix-list 10", +# "no ip prefix-list test_prefix", +# "no ipv6 prefix-list test_ipv6" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# router-ios# + +# Using merged + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# ipv6 prefix-list test_ipv6 description this is ipv6 +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 + +- name: Merge provided Prefix lists configuration + cisco.ios.ios_prefix_lists: + config: + - afi: ipv6 + prefix_lists: + - name: test_ipv6 + description: this is ipv6 merge test + entries: + - action: deny + prefix: 2001:DB8:0:4::/64 + ge: 80 + le: 100 + sequence: 10 + state: merged + +# After state: +# ------------- +# +# Play Execution fails, with error: +# Cannot update existing sequence 10 of Prefix Lists test_ipv6 with state merged. +# Please use state replaced or overridden. + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# ipv6 prefix-list test_ipv6 description this is ipv6 +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 + +- name: Merge provided Prefix lists configuration + cisco.ios.ios_prefix_lists: + config: + - afi: ipv4 + prefix_lists: + - name: 10 + description: this is new merge test + entries: + - action: deny + prefix: 1.0.0.0/8 + le: 15 + sequence: 5 + - action: deny + prefix: 35.0.0.0/8 + ge: 10 + sequence: 10 + - action: deny + prefix: 12.0.0.0/8 + ge: 15 + sequence: 15 + - action: deny + prefix: 14.0.0.0/8 + ge: 20 + le: 21 + sequence: 20 + - name: test + description: this is merge test + entries: + - action: deny + prefix: 12.0.0.0/8 + ge: 15 + sequence: 50 + - name: test_prefix + description: this is for prefix-list + entries: + - action: deny + prefix: 35.0.0.0/8 + ge: 10 + le: 15 + sequence: 5 + - action: deny + prefix: 35.0.0.0/8 + ge: 20 + sequence: 10 + - afi: ipv6 + prefix_lists: + - name: test_ipv6 + description: this is ipv6 merge test + entries: + - action: deny + prefix: 2001:DB8:0:4::/64 + ge: 80 + le: 100 + sequence: 20 + state: merged + +# Commands Fired: +# --------------- +# +# "commands": [ +# "ip prefix-list test description this is merge test", +# "ip prefix-list test seq 50 deny 12.0.0.0/8 ge 15", +# "ip prefix-list 10 seq 15 deny 12.0.0.0/8 ge 15", +# "ip prefix-list 10 seq 10 deny 35.0.0.0/8 ge 10", +# "ip prefix-list 10 seq 5 deny 1.0.0.0/8 le 15", +# "ip prefix-list 10 description this is new merge test", +# "ip prefix-list 10 seq 20 deny 14.0.0.0/8 ge 20 le 21", +# "ip prefix-list test_prefix seq 10 deny 35.0.0.0/8 ge 20", +# "ip prefix-list test_prefix seq 5 deny 35.0.0.0/8 ge 10 le 15", +# "ip prefix-list test_prefix description this is for prefix-list", +# "ipv6 prefix-list test_ipv6 seq 20 deny 2001:DB8:0:4::/64 ge 80 le 100", +# "ipv6 prefix-list test_ipv6 description this is ipv6 merge test" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# ip prefix-list 10 description this is new merge test +# ip prefix-list 10 seq 5 deny 1.0.0.0/8 le 15 +# ip prefix-list 10 seq 10 deny 35.0.0.0/8 ge 10 +# ip prefix-list 10 seq 15 deny 12.0.0.0/8 ge 15 +# ip prefix-list 10 seq 20 deny 14.0.0.0/8 ge 20 le 21 +# ip prefix-list test description this is merge test +# ip prefix-list test seq 50 deny 12.0.0.0/8 ge 15 +# ip prefix-list test_prefix description this is for prefix-list +# ip prefix-list test_prefix seq 5 deny 35.0.0.0/8 ge 10 le 15 +# ip prefix-list test_prefix seq 10 deny 35.0.0.0/8 ge 20 +# ipv6 prefix-list test_ipv6 description this is ipv6 merge test +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 le 100 + +# Using overridden + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# ip prefix-list 10 description this is test description +# ip prefix-list 10 seq 5 deny 1.0.0.0/8 le 15 +# ip prefix-list 10 seq 10 deny 35.0.0.0/8 ge 10 +# ip prefix-list 10 seq 15 deny 12.0.0.0/8 ge 15 +# ip prefix-list 10 seq 20 deny 14.0.0.0/8 ge 20 le 21 +# ip prefix-list test description this is test +# ip prefix-list test seq 50 deny 12.0.0.0/8 ge 15 +# ip prefix-list test_prefix description this is for prefix-list +# ip prefix-list test_prefix seq 5 deny 35.0.0.0/8 ge 10 le 15 +# ip prefix-list test_prefix seq 10 deny 35.0.0.0/8 ge 20 +# ipv6 prefix-list test_ipv6 description this is ipv6 prefix-list +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 + +- name: Override provided Prefix lists configuration + cisco.ios.ios_prefix_lists: + config: + - afi: ipv4 + prefix_lists: + - name: 10 + description: this is override test + entries: + - action: deny + prefix: 12.0.0.0/8 + ge: 15 + sequence: 15 + - action: deny + prefix: 14.0.0.0/8 + ge: 20 + le: 21 + sequence: 20 + - name: test_override + description: this is override test + entries: + - action: deny + prefix: 35.0.0.0/8 + ge: 20 + sequence: 10 + - afi: ipv6 + prefix_lists: + - name: test_ipv6 + description: this is ipv6 override test + entries: + - action: deny + prefix: 2001:DB8:0:4::/64 + ge: 80 + le: 100 + sequence: 10 + state: overridden + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no ip prefix-list test", +# "no ip prefix-list test_prefix", +# "ip prefix-list 10 description this is override test", +# "no ip prefix-list 10 seq 10 deny 35.0.0.0/8 ge 10", +# "no ip prefix-list 10 seq 5 deny 1.0.0.0/8 le 15", +# "ip prefix-list test_override seq 10 deny 35.0.0.0/8 ge 20", +# "ip prefix-list test_override description this is override test", +# "no ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80", +# "ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 le 100", +# "ipv6 prefix-list test_ipv6 description this is ipv6 override test" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# ip prefix-list 10 description this is override test +# ip prefix-list 10 seq 15 deny 12.0.0.0/8 ge 15 +# ip prefix-list 10 seq 20 deny 14.0.0.0/8 ge 20 le 21 +# ip prefix-list test_override description this is override test +# ip prefix-list test_override seq 10 deny 35.0.0.0/8 ge 20 +# ipv6 prefix-list test_ipv6 description this is ipv6 override test +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 le 100 + +# Using replaced + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# ip prefix-list 10 description this is test description +# ip prefix-list 10 seq 5 deny 1.0.0.0/8 le 15 +# ip prefix-list 10 seq 10 deny 35.0.0.0/8 ge 10 +# ip prefix-list 10 seq 15 deny 12.0.0.0/8 ge 15 +# ip prefix-list 10 seq 20 deny 14.0.0.0/8 ge 20 le 21 +# ip prefix-list test description this is test +# ip prefix-list test seq 50 deny 12.0.0.0/8 ge 15 +# ip prefix-list test_prefix description this is for prefix-list +# ip prefix-list test_prefix seq 5 deny 35.0.0.0/8 ge 10 le 15 +# ip prefix-list test_prefix seq 10 deny 35.0.0.0/8 ge 20 +# ipv6 prefix-list test_ipv6 description this is ipv6 prefix-list +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 + +- name: Replaced provided Prefix lists configuration + cisco.ios.ios_prefix_lists: + config: + - afi: ipv4 + prefix_lists: + - name: 10 + description: this is replace test + entries: + - action: deny + prefix: 12.0.0.0/8 + ge: 15 + sequence: 15 + - action: deny + prefix: 14.0.0.0/8 + ge: 20 + le: 21 + sequence: 20 + - name: test_replace + description: this is replace test + entries: + - action: deny + prefix: 35.0.0.0/8 + ge: 20 + sequence: 10 + - afi: ipv6 + prefix_lists: + - name: test_ipv6 + description: this is ipv6 replace test + entries: + - action: deny + prefix: 2001:DB8:0:4::/64 + ge: 80 + le: 100 + sequence: 10 + state: replaced + +# Commands Fired: +# --------------- +# "commands": [ +# "ip prefix-list 10 description this is replace test", +# "no ip prefix-list 10 seq 10 deny 35.0.0.0/8 ge 10", +# "no ip prefix-list 10 seq 5 deny 1.0.0.0/8 le 15", +# "ip prefix-list test_replace seq 10 deny 35.0.0.0/8 ge 20", +# "ip prefix-list test_replace description this is replace test", +# "no ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80", +# "ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 le 100", +# "ipv6 prefix-list test_ipv6 description this is ipv6 replace test" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# ip prefix-list 10 description this is replace test +# ip prefix-list 10 seq 15 deny 12.0.0.0/8 ge 15 +# ip prefix-list 10 seq 20 deny 14.0.0.0/8 ge 20 le 21 +# ip prefix-list test description this is test +# ip prefix-list test seq 50 deny 12.0.0.0/8 ge 15 +# ip prefix-list test_prefix description this is for prefix-list +# ip prefix-list test_prefix seq 5 deny 35.0.0.0/8 ge 10 le 15 +# ip prefix-list test_prefix seq 10 deny 35.0.0.0/8 ge 20 +# ip prefix-list test_replace description this is replace test +# ip prefix-list test_replace seq 10 deny 35.0.0.0/8 ge 20 +# ipv6 prefix-list test_ipv6 description this is ipv6 replace test +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 le 100 + +# Using Gathered + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# ip prefix-list 10 description this is test description +# ip prefix-list 10 seq 5 deny 1.0.0.0/8 le 15 +# ip prefix-list 10 seq 10 deny 35.0.0.0/8 ge 10 +# ip prefix-list 10 seq 15 deny 12.0.0.0/8 ge 15 +# ip prefix-list 10 seq 20 deny 14.0.0.0/8 ge 20 le 21 +# ip prefix-list test description this is test +# ip prefix-list test seq 50 deny 12.0.0.0/8 ge 15 +# ip prefix-list test_prefix description this is for prefix-list +# ip prefix-list test_prefix seq 5 deny 35.0.0.0/8 ge 10 le 15 +# ip prefix-list test_prefix seq 10 deny 35.0.0.0/8 ge 20 +# ipv6 prefix-list test_ipv6 description this is ipv6 prefix-list +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 + +- name: Gather Prefix lists provided configurations + cisco.ios.ios_prefix_lists: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "afi": "ipv4", +# "prefix_lists": [ +# { +# "description": "this is test description" +# "entries": [ +# { +# "action": "deny", +# "le": 15, +# "prefix": "1.0.0.0/8", +# "sequence": 5 +# }, +# { +# "action": "deny", +# "ge": 10, +# "prefix": "35.0.0.0/8", +# "sequence": 10 +# }, +# { +# "action": "deny", +# "ge": 15, +# "prefix": "12.0.0.0/8", +# "sequence": 15 +# }, +# { +# "action": "deny", +# "ge": 20, +# "le": 21, +# "prefix": "14.0.0.0/8", +# "sequence": 20 +# } +# ], +# "name": "10" +# }, +# { +# "description": "this is test" +# "entries": [ +# { +# "action": "deny", +# "ge": 15, +# "prefix": "12.0.0.0/8", +# "sequence": 50 +# } +# ], +# "name": "test" +# }, +# { +# "description": "this is for prefix-list" +# "entries": [ +# { +# "action": "deny", +# "ge": 10, +# "le": 15, +# "prefix": "35.0.0.0/8", +# "sequence": 5 +# }, +# { +# "action": "deny", +# "ge": 20, +# "prefix": "35.0.0.0/8", +# "sequence": 10 +# } +# ], +# "name": "test_prefix" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "prefix_lists": [ +# { +# "description": "this is ipv6 prefix-list" +# "entries": [ +# { +# "action": "deny", +# "ge": 80, +# "prefix": "2001:DB8:0:4::/64", +# "sequence": 10 +# } +# ], +# "name": "test_ipv6" +# } +# ] +# } +# ] + +# After state: +# ------------ +# +# router-ios#sh running-config | section ^ip prefix-list|^ipv6 prefix-list +# ip prefix-list 10 description this is test description +# ip prefix-list 10 seq 5 deny 1.0.0.0/8 le 15 +# ip prefix-list 10 seq 10 deny 35.0.0.0/8 ge 10 +# ip prefix-list 10 seq 15 deny 12.0.0.0/8 ge 15 +# ip prefix-list 10 seq 20 deny 14.0.0.0/8 ge 20 le 21 +# ip prefix-list test description this is test +# ip prefix-list test seq 50 deny 12.0.0.0/8 ge 15 +# ip prefix-list test_prefix description this is for prefix-list +# ip prefix-list test_prefix seq 5 deny 35.0.0.0/8 ge 10 le 15 +# ip prefix-list test_prefix seq 10 deny 35.0.0.0/8 ge 20 +# ipv6 prefix-list test_ipv6 description this is ipv6 prefix-list +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_prefix_lists: + config: + - afi: ipv4 + prefix_lists: + - name: 10 + description: this is new merge test + entries: + - action: deny + prefix: 1.0.0.0/8 + le: 15 + sequence: 5 + - action: deny + prefix: 35.0.0.0/8 + ge: 10 + sequence: 10 + - action: deny + prefix: 12.0.0.0/8 + ge: 15 + sequence: 15 + - action: deny + prefix: 14.0.0.0/8 + ge: 20 + le: 21 + sequence: 20 + - name: test + description: this is merge test + entries: + - action: deny + prefix: 12.0.0.0/8 + ge: 15 + sequence: 50 + - name: test_prefix + description: this is for prefix-list + entries: + - action: deny + prefix: 35.0.0.0/8 + ge: 10 + le: 15 + sequence: 5 + - action: deny + prefix: 35.0.0.0/8 + ge: 20 + sequence: 10 + - afi: ipv6 + prefix_lists: + - name: test_ipv6 + description: this is ipv6 merge test + entries: + - action: deny + prefix: 2001:DB8:0:4::/64 + ge: 80 + le: 100 + sequence: 10 + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "ip prefix-list test description this is test", +# "ip prefix-list test seq 50 deny 12.0.0.0/8 ge 15", +# "ip prefix-list 10 seq 15 deny 12.0.0.0/8 ge 15", +# "ip prefix-list 10 seq 10 deny 35.0.0.0/8 ge 10", +# "ip prefix-list 10 seq 5 deny 1.0.0.0/8 le 15", +# "ip prefix-list 10 description this is test description", +# "ip prefix-list 10 seq 20 deny 14.0.0.0/8 ge 20 le 21", +# "ip prefix-list test_prefix seq 10 deny 35.0.0.0/8 ge 20", +# "ip prefix-list test_prefix seq 5 deny 35.0.0.0/8 ge 10 le 15", +# "ip prefix-list test_prefix description this is for prefix-list", +# "ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 l2 100", +# "ipv6 prefix-list test_ipv6 description this is ipv6 prefix-list" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# ip prefix-list 10 description this is test description +# ip prefix-list 10 seq 5 deny 1.0.0.0/8 le 15 +# ip prefix-list 10 seq 10 deny 35.0.0.0/8 ge 10 +# ip prefix-list 10 seq 15 deny 12.0.0.0/8 ge 15 +# ip prefix-list 10 seq 20 deny 14.0.0.0/8 ge 20 le 21 +# ip prefix-list test description this is test +# ip prefix-list test seq 50 deny 12.0.0.0/8 ge 15 +# ip prefix-list test_prefix description this is for prefix-list +# ip prefix-list test_prefix seq 5 deny 35.0.0.0/8 ge 10 le 15 +# ip prefix-list test_prefix seq 10 deny 35.0.0.0/8 ge 20 +# ipv6 prefix-list test_ipv6 description this is ipv6 prefix-list +# ipv6 prefix-list test_ipv6 seq 10 deny 2001:DB8:0:4::/64 ge 80 + +- name: Parse the provided configuration with the existing running configuration + cisco.ios.ios_prefix_lists: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "afi": "ipv4", +# "prefix_lists": [ +# { +# "description": "this is test description" +# "entries": [ +# { +# "action": "deny", +# "le": 15, +# "prefix": "1.0.0.0/8", +# "sequence": 5 +# }, +# { +# "action": "deny", +# "ge": 10, +# "prefix": "35.0.0.0/8", +# "sequence": 10 +# }, +# { +# "action": "deny", +# "ge": 15, +# "prefix": "12.0.0.0/8", +# "sequence": 15 +# }, +# { +# "action": "deny", +# "ge": 20, +# "le": 21, +# "prefix": "14.0.0.0/8", +# "sequence": 20 +# } +# ], +# "name": "10" +# }, +# { +# "description": "this is test" +# "entries": [ +# { +# "action": "deny", +# "ge": 15, +# "prefix": "12.0.0.0/8", +# "sequence": 50 +# } +# ], +# "name": "test" +# }, +# { +# "description": "this is for prefix-list" +# "entries": [ +# { +# "action": "deny", +# "ge": 10, +# "le": 15, +# "prefix": "35.0.0.0/8", +# "sequence": 5 +# }, +# { +# "action": "deny", +# "ge": 20, +# "prefix": "35.0.0.0/8", +# "sequence": 10 +# } +# ], +# "name": "test_prefix" +# } +# ] +# }, +# { +# "afi": "ipv6", +# "prefix_lists": [ +# { +# "description": "this is ipv6 prefix-list" +# "entries": [ +# { +# "action": "deny", +# "ge": 80, +# "prefix": "2001:DB8:0:4::/64", +# "sequence": 10 +# } +# ], +# "name": "test_ipv6" +# } +# ] +# } +# ] +""" + +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: list +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: list +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['ip prefix-list 10 description this is test description', 'ip prefix-list 10 seq 5 deny 1.0.0.0/8 le 15'] +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.prefix_lists.prefix_lists import ( + Prefix_listsArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.prefix_lists.prefix_lists import ( + Prefix_lists, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Prefix_listsArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Prefix_lists(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_route_maps.py b/ansible_collections/cisco/ios/plugins/modules/ios_route_maps.py new file mode 100644 index 000000000..5ec2782a3 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_route_maps.py @@ -0,0 +1,2358 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for cisco.ios_route_maps +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_route_maps +short_description: Resource module to configure route maps. +description: + - This module configures and manages the attributes of Route maps on Cisco IOS. +version_added: 2.1.0 +author: Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: A list of configurations for Route maps. + type: list + elements: dict + suboptions: + route_map: + description: Route map tag/name + type: str + entries: + description: A list of configurations entries for Route maps. + type: list + elements: dict + suboptions: + sequence: + description: + - Sequence to insert to/delete from existing route-map entry + - Please refer vendor documentation for valid values + type: int + action: + description: Route map set operations + type: str + choices: ["deny", "permit"] + continue_entry: + description: Continue on a different entry within the route-map + type: dict + suboptions: + set: + description: Set continue + type: bool + entry_sequence: + description: + - Route-map entry sequence number + - Please refer vendor documentation for valid values + type: int + description: + description: Route-map comment + type: str + match: + description: Match values from routing table + type: dict + suboptions: + additional_paths: + description: + - BGP Add-Path match policie + - BGP Add-Path advertise-set policy + type: dict + suboptions: + all: + description: BGP Add-Path advertise all paths + type: bool + best: + description: BGP Add-Path advertise best n paths (1-3) + type: int + best_range: + description: BGP Add-Path advertise best paths (range m to n) + type: dict + suboptions: + lower_limit: + description: BGP Add-Path best paths to advertise (lower limit) (1-3) + type: int + upper_limit: + description: BGP Add-Path best paths to advertise (upper limit) (1-3) + type: int + group_best: + description: BGP Add-Path advertise group-best path + type: bool + as_path: + description: Match BGP AS path list + type: dict + suboptions: + set: + description: Set AS path list + type: bool + acls: + description: + - AS path access-list + - Please refer vendor documentation for valid values + type: list + elements: int + clns: + description: CLNS information + type: dict + suboptions: + address: + description: Match address of route or match packet + type: str + next_hop: + description: Match next-hop address of route + type: str + route_source: + description: Match advertising source address of route + type: str + community: + description: Match BGP community list + type: dict + suboptions: + name: + description: + - Community-list number/Community-list name + - Please refer vendor documentation for valid values + type: list + elements: str + exact_match: + description: Do exact matching of communities + type: bool + extcommunity: + description: + - Match BGP/VPN extended community list + - Extended community-list number + - Please refer vendor documentation for valid values + type: list + elements: str + interfaces: + description: Match first hop interface of route + type: list + elements: str + ip: + description: IP specific information + type: dict + suboptions: + address: + description: Match address of route or match packet + type: dict + suboptions: + acls: &acls + description: + - Match entries of acl + - IP acl name/number + - Please refer vendor documentation for valid values + type: list + elements: str + prefix_lists: &prefix_lists + description: + - Match entries of prefix-lists + - IP prefix-list name + type: list + elements: str + flowspec: + description: Match src/dest prefix component of flowspec prefix + type: dict + suboptions: + dest_pfx: + description: Match dest prefix component of flowspec prefix + type: bool + src_pfx: + description: Match source prefix component of flowspec prefix + type: bool + acls: *acls + prefix_lists: *prefix_lists + next_hop: + description: Match next-hop address of route + type: dict + suboptions: + set: + description: Set next-hop address + type: bool + acls: *acls + prefix_lists: *prefix_lists + redistribution_source: + description: route redistribution source (EIGRP only) + type: dict + suboptions: + set: + description: Set redistribution-source + type: bool + acls: *acls + prefix_lists: *prefix_lists + route_source: + description: Match advertising source address of route + type: dict + suboptions: + set: + description: Set redistribution-source + type: bool + redistribution_source: + description: route redistribution source (EIGRP only) + type: bool + acls: *acls + prefix_lists: *prefix_lists + ipv6: + description: IPv6 specific information + type: dict + suboptions: + address: + type: dict + description: Match address of route or match packet + suboptions: + acl: + description: IPv6 access-list name + type: str + prefix_list: + description: IPv6 prefix-list name + type: str + flowspec: + description: Match next-hop address of route + type: dict + suboptions: + dest_pfx: + description: Match dest prefix component of flowspec prefix + type: bool + src_pfx: + description: Match source prefix component of flowspec prefix + type: bool + acl: + description: IPv6 access-list name + type: str + prefix_list: + description: IPv6 prefix-list name + type: str + next_hop: + description: Match next-hop address of route + type: dict + suboptions: + acl: + description: IPv6 access-list name + type: str + prefix_list: + description: IPv6 prefix-list name + type: str + route_source: + description: Match advertising source address of route + type: dict + suboptions: + acl: + description: IPv6 access-list name + type: str + prefix_list: + description: IPv6 prefix-list name + type: str + length: + description: Packet length + type: dict + suboptions: + minimum: + description: + - Minimum packet length + - Please refer vendor documentation for valid values + type: int + maximum: + description: + - Maximum packet length + - Please refer vendor documentation for valid values + type: int + local_preference: + description: Local preference for route + type: dict + suboptions: + set: + description: Set the Local preference for route + type: bool + value: + description: + - Local preference value + - Please refer vendor documentation for valid values + type: list + elements: str + mdt_group: + description: Match routes corresponding to MDT group + type: dict + suboptions: + set: + description: Set and Match routes corresponding to MDT group + type: bool + acls: + description: + - IP access-list number/IP standard access-list name + - Please refer vendor documentation for valid values + type: list + elements: str + metric: + description: Match metric of route + type: dict + suboptions: + value: + description: + - Metric value + - Please refer vendor documentation for valid values + type: int + external: + description: Match route using external protocol metric + type: bool + deviation: + description: Deviation option to match metric in a range + type: bool + deviation_value: + description: + - deviation value, 500 +- 10 creates the range 490 - 510 + - Please refer vendor documentation for valid values + type: int + mpls_label: + description: Match routes which have MPLS labels + type: bool + policy_lists: + description: Match IP policy list + type: list + elements: str + route_type: + description: Match route-type of route + type: dict + suboptions: + external: + description: external route (BGP, EIGRP and OSPF type 1/2) + type: dict + suboptions: + set: + description: Set external route + type: bool + type_1: + description: OSPF external type 1 route + type: bool + type_2: + description: OSPF external type 2 route + type: bool + internal: + description: internal route (including OSPF intra/inter area) + type: bool + level_1: + description: IS-IS level-1 route + type: bool + level_2: + description: IS-IS level-2 route + type: bool + local: + description: locally generated route + type: bool + nssa_external: + description: nssa-external route (OSPF type 1/2) + type: dict + suboptions: + set: + description: Set nssa-external route + type: bool + type_1: + description: OSPF external type 1 route + type: bool + type_2: + description: OSPF external type 2 route + type: bool + rpki: + description: Match RPKI state of route + type: dict + suboptions: + invalid: + description: RPKI Invalid State + type: bool + not_found: + description: RPKI Not Found State + type: bool + valid: + description: RPKI Valid State + type: bool + security_group: + description: Security Group + type: dict + suboptions: + source: + description: + - Source Security Group, source Security tag + - Please refer vendor documentation for valid values + type: list + elements: int + destination: + description: + - Destination Security Group, destination Security tag + - Please refer vendor documentation for valid values + type: list + elements: int + source_protocol: + description: Match source-protocol of route + type: dict + suboptions: + bgp: + description: + - Border Gateway Protocol (BGP) + - Autonomous system number + - Please refer vendor documentation for valid values + type: str + connected: + description: Connected + type: bool + eigrp: + description: + - Enhanced Interior Gateway Routing Protocol (EIGRP) + - Autonomous system number + - Please refer vendor documentation for valid values + type: int + isis: + description: ISO IS-IS + type: bool + lisp: + description: Locator ID Separation Protocol (LISP) + type: bool + mobile: + description: Mobile routes + type: bool + ospf: + description: + - Open Shortest Path First (OSPF) Process ID + - Please refer vendor documentation for valid values + type: int + ospfv3: + description: + - OSPFv3 Process ID + - Please refer vendor documentation for valid values + type: int + rip: + description: Routing Information Protocol (RIP) + type: bool + static: + description: Static routes + type: bool + tag: + description: Match tag of route + type: dict + suboptions: + value: + description: Tag value/Tag in Dotted Decimal eg, 10.10.10.10 + type: list + elements: str + tag_list: + description: Route Tag List/Tag list name + type: list + elements: str + track: + description: tracking object + type: int + set: + description: Match source-protocol of route + type: dict + suboptions: + aigp_metric: + description: accumulated metric value + type: dict + suboptions: + value: + description: manual value + type: int + igp_metric: + description: metric value from rib + type: bool + as_path: + description: Prepend string for a BGP AS-path attribute + type: dict + suboptions: + prepend: + description: Prepend to the as-path + type: dict + suboptions: + as_number: + description: + - AS number + - Please refer vendor documentation for valid values + type: list + elements: str + last_as: + description: + - Prepend last AS to the as-path + - Number of last-AS prepends + - Please refer vendor documentation for valid values + type: int + tag: + description: Set the tag as an AS-path attribute + type: bool + automatic_tag: + description: Automatically compute TAG value + type: bool + clns: + description: + - OSI summary address + - Next hop address + - CLNS summary prefix + type: str + comm_list: + description: + - set BGP community list (for deletion) + - Community-list name/number + - Delete matching communities + type: str + community: + description: BGP community attribute + type: dict + suboptions: + number: + description: + - community number + - community number in aa:nn format + - Please refer vendor documentation for valid values + type: str + additive: + description: Add to the existing community + type: bool + gshut: + description: Graceful Shutdown (well-known community) + type: bool + internet: + description: Internet (well-known community) + type: bool + local_as: + description: Do not send outside local AS (well-known community) + type: bool + no_advertise: + description: Do not advertise to any peer (well-known community) + type: bool + no_export: + description: Do not export to next AS (well-known community) + type: bool + none: + description: No community attribute + type: bool + dampening: + description: Set BGP route flap dampening parameters + type: dict + suboptions: + penalty_half_time: + description: + - half-life time for the penalty + - Please refer vendor documentation for valid values + type: int + reuse_route_val: + description: + - Penalty to start reusing a route + - Please refer vendor documentation for valid values + type: int + suppress_route_val: + description: + - Penalty to start suppressing a route + - Please refer vendor documentation for valid values + type: int + max_suppress: + description: + - Maximum duration to suppress a stable route + - Please refer vendor documentation for valid values + type: int + default: + description: + - Set default information + - Default output interface + type: str + extcomm_list: + description: + - Set BGP/VPN extended community list (for deletion) + - Extended community-list number/name + - Delete matching extended communities + type: str + extcommunity: + description: BGP extended community attribute + type: dict + suboptions: + cost: + description: Cost extended community + type: dict + suboptions: + id: + description: + - Community ID + - Please refer vendor documentation for valid values + type: str + cost_value: + description: + - Cost Value (No-preference Cost = 2147483647) + - Please refer vendor documentation for valid values + type: int + igp: + description: Compare following IGP cost comparison + type: bool + pre_bestpath: + description: Compare before all other steps in bestpath calculation + type: bool + rt: + description: Route Target extended community + type: dict + suboptions: + address: + description: VPN extended community + type: str + range: + description: Specify a range of extended community + type: dict + suboptions: + lower_limit: + description: VPN extended community + type: str + upper_limit: + description: VPN extended community + type: str + additive: + description: Add to the existing extcommunity + type: bool + soo: + description: Site-of-Origin extended community + type: str + vpn_distinguisher: + description: VPN Distinguisher + type: dict + suboptions: + address: + description: VPN extended community + type: str + range: + description: Specify a range of extended community + type: dict + suboptions: + lower_limit: + description: VPN extended community + type: str + upper_limit: + description: VPN extended community + type: str + additive: + description: Add to the existing extcommunity + type: bool + global_route: + description: Set to global routing table + type: bool + interfaces: + description: Output interface + type: list + elements: str + ip: + description: IP specific information + type: dict + suboptions: + address: + description: + - Specify IP address + - Prefix-list name to set ip address + type: str + df: + description: Set DF bit + choices: [0, 1] + type: int + global_route: + description: global routing table + type: dict + suboptions: + address: + description: IP address of next hop + type: str + verify_availability: + description: Verify if nexthop is reachable + type: dict + suboptions: + address: + description: IP address of next hop + type: str + sequence: + description: + - Sequence to insert into next-hop list + - Please refer vendor documentation for valid values + type: int + track: + description: + - Set the next hop depending on the state of a tracked object + - tracked object number + - Please refer vendor documentation for valid values + type: int + next_hop: + description: Next hop address + type: dict + suboptions: + address: + description: IP address of next hop + type: str + dynamic: + description: + - application dynamically sets next hop + - DHCP learned next hop + type: bool + encapsulate: + description: + - Encapsulation profile for VPN nexthop + - L3VPN + - Encapsulation profile name + type: str + peer_address: + description: Use peer address (for BGP only) + type: bool + recursive: + description: Recursive next-hop + type: dict + suboptions: + global_route: + description: global routing table + type: bool + vrf: + description: VRF + type: str + address: + description: IP address of recursive next hop + type: str + self: + description: Use self address (for BGP only) + type: bool + verify_availability: + description: Verify if nexthop is reachable + type: dict + suboptions: + set: + description: Set and Verify if nexthop is reachable + type: bool + address: + description: IP address of next hop + type: str + sequence: + description: + - Sequence to insert into next-hop list + - Please refer vendor documentation for valid values + type: int + track: + description: + - Set the next hop depending on the state of a tracked object + - tracked object number + - Please refer vendor documentation for valid values + type: int + precedence: + description: Set precedence field + type: dict + suboptions: + set: + description: Just set precedence field + type: bool + critical: + description: Set critical precedence (5) + type: bool + flash: + description: Set flash precedence (3) + type: bool + flash_override: + description: Set flash override precedence (4) + type: bool + immediate: + description: Set immediate precedence (2) + type: bool + internet: + description: Set internetwork control precedence (6) + type: bool + network: + description: Set network control precedence (7) + type: bool + priority: + description: Set priority precedence (1) + type: bool + routine: + description: Set routine precedence (0) + type: bool + qos_group: + description: + - Set QOS Group ID + - Please refer vendor documentation for valid values + type: int + tos: + description: Set type of service field + type: dict + suboptions: + set: + description: Just set type of service field + type: bool + max_reliability: + description: Set max reliable TOS (2) + type: bool + max_throughput: + description: Set max throughput TOS (4) + type: bool + min_delay: + description: Set min delay TOS (8) + type: bool + min_monetary_cost: + description: Set min monetary cost TOS (1) + type: bool + normal: + description: Set normal TOS (0) + type: bool + vrf: + description: VRF + type: dict + suboptions: + name: + description: VRF name + type: str + address: + description: IP address of next hop + type: str + verify_availability: + description: Verify if nexthop is reachable + type: dict + suboptions: + set: + description: Set and Verify if nexthop is reachable + type: bool + address: + description: IP address of next hop + type: str + sequence: + description: + - Sequence to insert into next-hop list + - Please refer vendor documentation for valid values + type: int + track: + description: + - Set the next hop depending on the state of a tracked object + - tracked object number + - Please refer vendor documentation for valid values + type: int + ipv6: + description: IPv6 specific information + type: dict + suboptions: + address: + description: + - IPv6 address + - IPv6 prefix-list + type: str + default: + description: Set default information + type: bool + global_route: + description: global routing table + type: dict + suboptions: + address: + description: Next hop address (X:X:X:X::X) + type: str + verify_availability: + description: Verify if nexthop is reachable + type: dict + suboptions: + address: + description: Next hop address (X:X:X:X::X) + type: str + sequence: + description: + - Sequence to insert into next-hop list + - Please refer vendor documentation for valid values + type: int + track: + description: + - Set the next hop depending on the state of a tracked object + - tracked object number + - Please refer vendor documentation for valid values + type: int + next_hop: + description: IPv6 Next hop + type: dict + suboptions: + address: + description: Next hop address (X:X:X:X::X) + type: str + encapsulate: + description: + - Encapsulation profile for VPN nexthop + - L3VPN + - Encapsulation profile name + type: str + peer_address: + description: Use peer address (for BGP only) + type: bool + recursive: + description: + - Recursive next-hop + - IPv6 address of recursive next-hop + type: str + precedence: + description: + - Set precedence field + - Precedence value + - Please refer vendor documentation for valid values + type: int + vrf: + description: VRF name + type: dict + suboptions: + name: + description: VRF name + type: str + verify_availability: + description: Verify if nexthop is reachable + type: dict + suboptions: + address: + description: IPv6 address of next hop + type: str + sequence: + description: + - Sequence to insert into next-hop list + - Please refer vendor documentation for valid values + type: int + track: + description: + - Set the next hop depending on the state of a tracked object + - tracked object number + - Please refer vendor documentation for valid values + type: int + level: + description: Where to import route + type: dict + suboptions: + level_1: + description: Import into a level-1 area + type: bool + level_1_2: + description: Import into level-1 and level-2 + type: bool + level_2: + description: Import into level-2 sub-domain + type: bool + nssa_only: + description: Import only into OSPF NSSA areas and don't propagate + type: bool + lisp: + description: + - Locator ID Separation Protocol specific information + - Specify a locator-set to use in LISP route-import + - The name of the locator set + type: str + local_preference: + description: + - BGP local preference path attribute + - Please refer vendor documentation for valid values + type: int + metric: + description: Metric value for destination routing protocol + type: dict + suboptions: + deviation: + description: Add or subtract metric + choices: ["plus", "minus"] + type: str + metric_value: + description: + - Metric value or Bandwidth in Kbits per second + - Please refer vendor documentation for valid values + type: int + eigrp_delay: + description: + - EIGRP delay metric, in 10 microsecond units + - Please refer vendor documentation for valid values + type: int + metric_reliability: + description: + - EIGRP reliability metric where 255 is 100 reliable + - Please refer vendor documentation for valid values + type: int + metric_bandwidth: + description: + - EIGRP Effective bandwidth metric (Loading) where 255 is 100 loaded + - Please refer vendor documentation for valid values + type: int + mtu: + description: + - EIGRP MTU of the path + - Please refer vendor documentation for valid values + type: int + metric_type: + description: Type of metric for destination routing protocol + type: dict + suboptions: + external: + description: IS-IS external metric + type: bool + internal: + description: IS-IS internal metric or Use IGP metric as the MED for BGP + type: bool + type_1: + description: OSPF external type 1 metric + type: bool + type_2: + description: OSPF external type 2 metric + type: bool + mpls_label: + description: Set MPLS label for prefix + type: bool + origin: + description: BGP origin code + type: dict + suboptions: + igp: + description: local IGP + type: bool + incomplete: + description: unknown heritage + type: bool + tag: + description: + - Tag value for destination routing protocol + - Tag value A.B.C.D(dotted decimal format)/Tag value + type: str + traffic_index: + description: + - BGP traffic classification number for accounting + - Please refer vendor documentation for valid values + type: int + vrf: + description: + - Define VRF name + - VPN Routing/Forwarding instance name + type: str + weight: + description: + - BGP weight for routing table + - Please refer vendor documentation for valid values + type: int + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS + device by executing the command B(sh running-config | section ^route-map). + - 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 + state: + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - parsed + - rendered + default: merged + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(sh running-config + | section ^route-map) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str +""" + +EXAMPLES = """ +# Using deleted + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^route-map +# route-map test_1 deny 10 +# description this is test route +# match ip next-hop prefix-list test_2_new test_1_new +# match ip route-source 10 +# match security-group source tag 10 20 +# match local-preference 100 50 +# match mpls-label +# route-map test_1 deny 20 +# match track 105 +# match tag list test_match_tag +# match route-type level-1 +# match additional-paths advertise-set all group-best +# match as-path 200 100 +# match ipv6 address test_acl_20 +# continue 100 +# route-map test_2 deny 10 +# match security-group source tag 10 20 +# match local-preference 55 105 +# match mpls-label +# match ipv6 address test_ip_acl +# match ipv6 next-hop prefix-list test_new +# match ipv6 route-source route_src_acl +# set automatic-tag +# set ip precedence critical +# set ip address prefix-list 192.0.2.1 +# set aigp-metric 100 +# set extcommunity cost pre-bestpath 10 100 +# set ip df 1 +# set ip next-hop verify-availability 198.51.111.1 100 track 10 +# set ip next-hop recursive global 198.51.110.1 + +- name: Delete provided Route maps config + cisco.ios.ios_route_maps: + config: + - route_map: test_1 + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no route-map test_1" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^route-map +# route-map test_2 deny 10 +# match security-group source tag 10 20 +# match local-preference 55 105 +# match mpls-label +# match ipv6 address test_ip_acl +# match ipv6 next-hop prefix-list test_new +# match ipv6 route-source route_src_acl +# set automatic-tag +# set ip precedence critical +# set ip address prefix-list 192.0.2.1 +# set aigp-metric 100 +# set extcommunity cost pre-bestpath 10 100 +# set ip df 1 +# set ip next-hop verify-availability 198.51.111.1 100 track 10 +# set ip next-hop recursive global 198.51.110.1 + +# Using deleted without any config passed (NOTE: This will delete all Route maps configuration from device) + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^route-map +# route-map test_1 deny 10 +# description this is test route +# match ip next-hop prefix-list test_2_new test_1_new +# match ip route-source 10 +# match security-group source tag 10 20 +# match local-preference 100 50 +# match mpls-label +# route-map test_1 deny 20 +# match track 105 +# match tag list test_match_tag +# match route-type level-1 +# match additional-paths advertise-set all group-best +# match as-path 200 100 +# match ipv6 address test_acl_20 +# continue 100 +# route-map test_2 deny 10 +# match security-group source tag 10 20 +# match local-preference 55 105 +# match mpls-label +# match ipv6 address test_ip_acl +# match ipv6 next-hop prefix-list test_new +# match ipv6 route-source route_src_acl +# set automatic-tag +# set ip precedence critical +# set ip address prefix-list 192.0.2.1 +# set aigp-metric 100 +# set extcommunity cost pre-bestpath 10 100 +# set ip df 1 +# set ip next-hop verify-availability 198.51.111.1 100 track 10 +# set ip next-hop recursive global 198.51.110.1 + +- name: Delete all Route maps config + cisco.ios.ios_route_maps: + state: deleted + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no route-map test_1", +# "no route-map test_2" +# ] + +# After state: +# ------------- +# router-ios#sh running-config | section ^route-map +# router-ios# + +# Using merged + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^route-map +# router-ios# + +- name: Merge provided Route maps configuration + cisco.ios.ios_route_maps: + config: + - route_map: test_1 + entries: + - sequence: 10 + action: deny + description: this is test route + match: + ip: + next_hop: + prefix_lists: + - test_1_new + - test_2_new + route_source: + acls: + - 10 + security_group: + source: + - 10 + - 20 + local_preference: + value: + - 50 + - 100 + mpls_label: true + - sequence: 20 + action: deny + continue_entry: + entry_sequence: 100 + match: + additional_paths: + all: true + group_best: true + as_path: + acls: + - 100 + - 200 + ipv6: + address: + acl: test_acl_20 + route_type: + level_1: true + tag: + tag_list: + - test_match_tag + track: 105 + - route_map: test_2 + entries: + - sequence: 10 + action: deny + match: + ipv6: + address: + acl: test_ip_acl + next_hop: + prefix_list: test_new + route_source: + acl: route_src_acl + security_group: + source: + - 10 + - 20 + local_preference: + value: + - 55 + - 105 + mpls_label: true + set: + aigp_metric: + value: 100 + automatic_tag: true + extcommunity: + cost: + id: 10 + cost_value: 100 + pre_bestpath: true + ip: + address: 192.0.2.1 + df: 1 + next_hop: + recursive: + global_route: true + address: 198.51.110.1 + verify_availability: + address: 198.51.111.1 + sequence: 100 + track: 10 + precedence: + critical: true + state: merged + +# Commands Fired: +# --------------- +# +# "commands": [ +# "route-map test_2 deny 10", +# "match security-group source tag 10 20", +# "match local-preference 55 105", +# "match mpls-label", +# "match ipv6 next-hop prefix-list test_new", +# "match ipv6 route-source route_src_acl", +# "match ipv6 address test_ip_acl", +# "set extcommunity cost pre-bestpath 10 100", +# "set ip df 1", +# "set ip next-hop recursive global 198.51.110.1", +# "set ip next-hop verify-availability 198.51.111.1 100 track 10", +# "set ip precedence critical", +# "set ip address prefix-list 192.0.2.1", +# "set automatic-tag", +# "set aigp-metric 100", +# "route-map test_1 deny 20", +# "continue 100", +# "match track 105", +# "match tag list test_match_tag", +# "match ipv6 address test_acl_20", +# "match route-type level-1", +# "match as-path 200 100", +# "match additional-paths advertise-set all group-best", +# "route-map test_1 deny 10", +# "description this is test route", +# "match security-group source tag 10 20", +# "match ip next-hop prefix-list test_2_new test_1_new", +# "match ip route-source 10", +# "match local-preference 100 50", +# "match mpls-label" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^route-map +# route-map test_1 deny 10 +# description this is test route +# match ip next-hop prefix-list test_2_new test_1_new +# match ip route-source 10 +# match security-group source tag 10 20 +# match local-preference 100 50 +# match mpls-label +# route-map test_1 deny 20 +# match track 105 +# match tag list test_match_tag +# match route-type level-1 +# match additional-paths advertise-set all group-best +# match as-path 200 100 +# match ipv6 address test_acl_20 +# continue 100 +# route-map test_2 deny 10 +# match security-group source tag 10 20 +# match local-preference 55 105 +# match mpls-label +# match ipv6 address test_ip_acl +# match ipv6 next-hop prefix-list test_new +# match ipv6 route-source route_src_acl +# set automatic-tag +# set ip precedence critical +# set ip address prefix-list 192.0.2.1 +# set aigp-metric 100 +# set extcommunity cost pre-bestpath 10 100 +# set ip df 1 +# set ip next-hop verify-availability 198.51.111.1 100 track 10 +# set ip next-hop recursive global 198.51.110.1 + +# Using overridden + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^route-map +# route-map test_1 deny 10 +# description this is test route +# match ip next-hop prefix-list test_2_new test_1_new +# match ip route-source 10 +# match security-group source tag 10 20 +# match local-preference 100 50 +# match mpls-label +# route-map test_1 deny 20 +# match track 105 +# match tag list test_match_tag +# match route-type level-1 +# match additional-paths advertise-set all group-best +# match as-path 200 100 +# match ipv6 address test_acl_20 +# continue 100 +# route-map test_2 deny 10 +# match security-group source tag 10 20 +# match local-preference 55 105 +# match mpls-label +# match ipv6 address test_ip_acl +# match ipv6 next-hop prefix-list test_new +# match ipv6 route-source route_src_acl +# set automatic-tag +# set ip precedence critical +# set ip address prefix-list 192.0.2.1 +# set aigp-metric 100 +# set extcommunity cost pre-bestpath 10 100 +# set ip df 1 +# set ip next-hop verify-availability 198.51.111.1 100 track 10 +# set ip next-hop recursive global 198.51.110.1 + +- name: Override provided Route maps configuration + cisco.ios.ios_route_maps: + config: + - route_map: test_1 + entries: + - sequence: 10 + action: deny + description: this is override route + match: + ip: + next_hop: + acls: + - 10 + - test1_acl + flowspec: + dest_pfx: true + acls: + - test_acl_1 + - test_acl_2 + length: + minimum: 10 + maximum: 100 + metric: + value: 10 + external: true + security_group: + source: + - 10 + - 20 + mpls_label: true + set: + extcommunity: + vpn_distinguisher: + address: 192.0.2.1:12 + additive: true + metric: + metric_value: 100 + deviation: plus + eigrp_delay: 100 + metric_reliability: 10 + metric_bandwidth: 20 + mtu: 30 + - route_map: test_override + entries: + - sequence: 10 + action: deny + match: + ipv6: + address: + acl: test_acl + next_hop: + prefix_list: test_new + route_source: + acl: route_src_acl + security_group: + source: + - 15 + - 20 + local_preference: + value: + - 105 + - 110 + mpls_label: true + set: + aigp_metric: + value: 100 + automatic_tag: true + extcommunity: + cost: + id: 10 + cost_value: 100 + pre_bestpath: true + ip: + address: 192.0.2.1 + df: 1 + next_hop: + recursive: + global_route: true + address: 198.110.51.1 + verify_availability: + address: 198.110.51.2 + sequence: 100 + track: 10 + precedence: + critical: true + state: overridden + +# Commands Fired: +# --------------- +# +# "commands": [ +# "no route-map test_2", +# "route-map test_override deny 10", +# "match security-group source tag 15 20", +# "match local-preference 110 105", +# "match mpls-label", +# "match ipv6 next-hop prefix-list test_new", +# "match ipv6 route-source route_src_acl", +# "match ipv6 address test_acl", +# "set extcommunity cost pre-bestpath 10 100", +# "set ip df 1", +# "set ip next-hop recursive global 198.110.51.1", +# "set ip next-hop verify-availability 198.110.51.2 100 track 10", +# "set ip precedence critical", +# "set ip address prefix-list 192.0.2.1", +# "set automatic-tag", +# "set aigp-metric 100", +# "route-map test_1 deny 10", +# "no description this is test route", +# "description this is override route", +# "match ip flowspec dest-pfx test_acl_1 test_acl_2", +# "no match ip next-hop prefix-list test_2_new test_1_new", +# "match ip next-hop test1_acl 10", +# "no match ip route-source 10", +# "match metric external 10", +# "match length 10 100", +# "no match local-preference 100 50", +# "set extcommunity vpn-distinguisher 192.0.2.1:12 additive", +# "set metric 100 +100 10 20 30", +# "no route-map test_1 deny 20" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^route-map +# route-map test_override deny 10 +# match security-group source tag 15 20 +# match local-preference 110 105 +# match mpls-label +# match ipv6 address test_acl +# match ipv6 next-hop prefix-list test_new +# match ipv6 route-source route_src_acl +# set automatic-tag +# set ip precedence critical +# set ip address prefix-list 192.0.2.1 +# set aigp-metric 100 +# set extcommunity cost pre-bestpath 10 100 +# set ip df 1 +# set ip next-hop verify-availability 198.110.51.2 100 track 10 +# set ip next-hop recursive global 198.110.51.1 +# route-map test_1 deny 10 +# description this is override route +# match ip flowspec dest-pfx test_acl_1 test_acl_2 +# match ip next-hop test1_acl 10 +# match security-group source tag 10 20 +# match metric external 10 +# match mpls-label +# match length 10 100 +# set metric 100 +100 10 20 30 +# set extcommunity vpn-distinguisher 192.0.2.1:12 additive + +# Using replaced + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^route-map +# route-map test_1 deny 10 +# description this is test route +# match ip next-hop prefix-list test_2_new test_1_new +# match ip route-source 10 +# match security-group source tag 10 20 +# match local-preference 100 50 +# match mpls-label +# route-map test_1 deny 20 +# match track 105 +# match tag list test_match_tag +# match route-type level-1 +# match additional-paths advertise-set all group-best +# match as-path 200 100 +# match ipv6 address test_acl_20 +# continue 100 +# route-map test_2 deny 10 +# match security-group source tag 10 20 +# match local-preference 55 105 +# match mpls-label +# match ipv6 address test_ip_acl +# match ipv6 next-hop prefix-list test_new +# match ipv6 route-source route_src_acl +# set automatic-tag +# set ip precedence critical +# set ip address prefix-list 192.0.2.1 +# set aigp-metric 100 +# set extcommunity cost pre-bestpath 10 100 +# set ip df 1 +# set ip next-hop verify-availability 198.51.111.1 100 track 10 +# set ip next-hop recursive global 198.51.110.1 + +- name: Replaced provided Route maps configuration + cisco.ios.ios_route_maps: + config: + - route_map: test_1 + entries: + - sequence: 10 + action: deny + description: this is replaced route + match: + ip: + next_hop: + acls: + - 10 + - test1_acl + flowspec: + dest_pfx: true + acls: + - test_acl_1 + - test_acl_2 + length: + minimum: 10 + maximum: 100 + metric: + value: 10 + external: true + security_group: + source: + - 10 + - 20 + mpls_label: true + set: + extcommunity: + vpn_distinguisher: + address: 192.0.2.1:12 + additive: true + metric: + metric_value: 100 + deviation: plus + eigrp_delay: 100 + metric_reliability: 10 + metric_bandwidth: 20 + mtu: 30 + - route_map: test_replaced + entries: + - sequence: 10 + action: deny + match: + ipv6: + address: + acl: test_acl + next_hop: + prefix_list: test_new + route_source: + acl: route_src_acl + security_group: + source: + - 15 + - 20 + local_preference: + value: + - 105 + - 110 + mpls_label: true + set: + aigp_metric: + value: 100 + automatic_tag: true + extcommunity: + cost: + id: 10 + cost_value: 100 + pre_bestpath: true + ip: + address: 192.0.2.1 + df: 1 + next_hop: + recursive: + global_route: true + address: 198.110.51.1 + verify_availability: + address: 198.110.51.2 + sequence: 100 + track: 10 + precedence: + critical: true + state: replaced + +# Commands Fired: +# --------------- +# "commands": [ +# "route-map test_replaced deny 10", +# "match security-group source tag 15 20", +# "match local-preference 110 105", +# "match mpls-label", +# "match ipv6 next-hop prefix-list test_new", +# "match ipv6 route-source route_src_acl", +# "match ipv6 address test_acl", +# "set extcommunity cost pre-bestpath 10 100", +# "set ip df 1", +# "set ip next-hop recursive global 198.110.51.1", +# "set ip next-hop verify-availability 198.110.51.2 100 track 10", +# "set ip precedence critical", +# "set ip address prefix-list 192.0.2.1", +# "set automatic-tag", +# "set aigp-metric 100", +# "route-map test_1 deny 10", +# "no description this is test route", +# "description this is replaced route", +# "match ip flowspec dest-pfx test_acl_1 test_acl_2", +# "no match ip next-hop prefix-list test_2_new test_1_new", +# "match ip next-hop test1_acl 10", +# "no match ip route-source 10", +# "match metric external 10", +# "match length 10 100", +# "no match local-preference 100 50", +# "set extcommunity vpn-distinguisher 192.0.2.1:12 additive", +# "set metric 100 +100 10 20 30", +# "no route-map test_1 deny 20" +# ] + +# After state: +# ------------- +# +# router-ios#sh running-config | section ^route-map +# route-map test_replaced deny 10 +# match security-group source tag 15 20 +# match local-preference 110 105 +# match mpls-label +# match ipv6 address test_acl +# match ipv6 next-hop prefix-list test_new +# match ipv6 route-source route_src_acl +# set automatic-tag +# set ip precedence critical +# set ip address prefix-list 192.0.2.1 +# set aigp-metric 100 +# set extcommunity cost pre-bestpath 10 100 +# set ip df 1 +# set ip next-hop verify-availability 198.110.51.2 100 track 10 +# set ip next-hop recursive global 198.110.51.1 +# route-map test_1 deny 10 +# description this is replaced route +# match ip flowspec dest-pfx test_acl_1 test_acl_2 +# match ip next-hop test1_acl 10 +# match security-group source tag 10 20 +# match metric external 10 +# match mpls-label +# match length 10 100 +# set metric 100 +100 10 20 30 +# set extcommunity vpn-distinguisher 192.0.2.1:12 additive +# route-map test_2 deny 10 +# match security-group source tag 10 20 +# match local-preference 55 105 +# match mpls-label +# match ipv6 address test_ip_acl +# match ipv6 next-hop prefix-list test_new +# match ipv6 route-source route_src_acl +# set automatic-tag +# set ip precedence critical +# set ip address prefix-list 192.0.2.1 +# set aigp-metric 100 +# set extcommunity cost pre-bestpath 10 100 +# set ip df 1 +# set ip next-hop verify-availability 198.51.111.1 100 track 10 +# set ip next-hop recursive global 198.51.110.1 + +# Using Gathered + +# Before state: +# ------------- +# +# router-ios#sh running-config | section ^route-map +# route-map test_1 deny 10 +# description this is test route +# match ip next-hop prefix-list test_2_new test_1_new +# match ip route-source 10 +# match security-group source tag 10 20 +# match local-preference 100 50 +# match mpls-label +# route-map test_1 deny 20 +# match track 105 +# match tag list test_match_tag +# match route-type level-1 +# match additional-paths advertise-set all group-best +# match as-path 200 100 +# match ipv6 address test_acl_20 +# continue 100 +# route-map test_2 deny 10 +# match security-group source tag 10 20 +# match local-preference 55 105 +# match mpls-label +# match ipv6 address test_ip_acl +# match ipv6 next-hop prefix-list test_new +# match ipv6 route-source route_src_acl +# set automatic-tag +# set ip precedence critical +# set ip address prefix-list 192.0.2.1 +# set aigp-metric 100 +# set extcommunity cost pre-bestpath 10 100 +# set ip df 1 +# set ip next-hop verify-availability 198.51.111.1 100 track 10 +# set ip next-hop recursive global 198.51.110.1 + +- name: Gather Route maps provided configurations + cisco.ios.ios_route_maps: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "entries": [ +# { +# "action": "deny", +# "description": "this is test route", +# "match": { +# "ip": { +# "next_hop": { +# "prefix_lists": [ +# "test_2_new", +# "test_1_new" +# ] +# }, +# "route_source": { +# "acls": [ +# "10" +# ] +# } +# }, +# "local_preference": { +# "value": [ +# "100", +# "50" +# ] +# }, +# "mpls_label": true, +# "security_group": { +# "source": [ +# 10, +# 20 +# ] +# } +# }, +# "sequence": 10 +# }, +# { +# "action": "deny", +# "continue_entry": { +# "entry_sequence": 100 +# }, +# "match": { +# "additional_paths": { +# "all": true, +# "group_best": true +# }, +# "as_path": { +# "acls": [ +# 200, +# 100 +# ] +# }, +# "ipv6": { +# "address": { +# "acl": "test_acl_20" +# } +# }, +# "route_type": { +# "external": { +# "set": true +# }, +# "level_1": true, +# "nssa_external": { +# "set": true +# } +# }, +# "tag": { +# "tag_list": [ +# "test_match_tag" +# ] +# }, +# "track": 105 +# }, +# "sequence": 20 +# } +# ], +# "route_map": "test_1" +# }, +# { +# "entries": [ +# { +# "action": "deny", +# "match": { +# "ipv6": { +# "address": { +# "acl": "test_ip_acl" +# }, +# "next_hop": { +# "prefix_list": "test_new" +# }, +# "route_source": { +# "acl": "route_src_acl" +# } +# }, +# "local_preference": { +# "value": [ +# "55", +# "105" +# ] +# }, +# "mpls_label": true, +# "security_group": { +# "source": [ +# 10, +# 20 +# ] +# } +# }, +# "sequence": 10, +# "set": { +# "aigp_metric": { +# "value": 100 +# }, +# "automatic_tag": true, +# "extcommunity": { +# "cost": { +# "cost_value": 100, +# "id": "10", +# "pre_bestpath": true +# } +# }, +# "ip": { +# "address": "192.0.2.1", +# "df": 1, +# "next_hop": { +# "recursive": { +# "address": "198.51.110.1", +# "global_route": true +# }, +# "verify_availability": { +# "address": "198.51.111.1", +# "sequence": 100, +# "track": 10 +# } +# }, +# "precedence": { +# "critical": true +# } +# } +# } +# } +# ], +# "route_map": "test_2" +# } +# ] + +# After state: +# ------------ +# +# router-ios#sh running-config | section ^route-map +# route-map test_1 deny 10 +# description this is test route +# match ip next-hop prefix-list test_2_new test_1_new +# match ip route-source 10 +# match security-group source tag 10 20 +# match local-preference 100 50 +# match mpls-label +# route-map test_1 deny 20 +# match track 105 +# match tag list test_match_tag +# match route-type level-1 +# match additional-paths advertise-set all group-best +# match as-path 200 100 +# match ipv6 address test_acl_20 +# continue 100 +# route-map test_2 deny 10 +# match security-group source tag 10 20 +# match local-preference 55 105 +# match mpls-label +# match ipv6 address test_ip_acl +# match ipv6 next-hop prefix-list test_new +# match ipv6 route-source route_src_acl +# set automatic-tag +# set ip precedence critical +# set ip address prefix-list 192.0.2.1 +# set aigp-metric 100 +# set extcommunity cost pre-bestpath 10 100 +# set ip df 1 +# set ip next-hop verify-availability 198.51.111.1 100 track 10 +# set ip next-hop recursive global 198.51.110.1 + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_route_maps: + config: + - route_map: test_1 + entries: + - sequence: 10 + action: deny + description: this is test route + match: + ip: + next_hop: + prefix_lists: + - test_1_new + - test_2_new + route_source: + acls: + - 10 + security_group: + source: + - 10 + - 20 + local_preference: + value: + - 50 + - 100 + mpls_label: true + - sequence: 20 + action: deny + continue_entry: + entry_sequence: 100 + match: + additional_paths: + all: true + group_best: true + as_path: + acls: + - 100 + - 200 + ipv6: + address: + acl: test_acl_20 + route_type: + level_1: true + tag: + tag_list: + - test_match_tag + track: 105 + - route_map: test_2 + entries: + - sequence: 10 + action: deny + match: + ipv6: + address: + acl: test_ip_acl + next_hop: + prefix_list: test_new + route_source: + acl: route_src_acl + security_group: + source: + - 10 + - 20 + local_preference: + value: + - 55 + - 105 + mpls_label: true + set: + aigp_metric: + value: 100 + automatic_tag: true + extcommunity: + cost: + id: 10 + cost_value: 100 + pre_bestpath: true + ip: + address: 192.0.2.1 + df: 1 + next_hop: + recursive: + global_route: true + address: 198.51.110.1 + verify_availability: + address: 198.51.111.1 + sequence: 100 + track: 10 + precedence: + critical: true + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "route-map test_2 deny 10", +# "match security-group source tag 10 20", +# "match local-preference 55 105", +# "match mpls-label", +# "match ipv6 next-hop prefix-list test_new", +# "match ipv6 route-source route_src_acl", +# "match ipv6 address test_ip_acl", +# "set extcommunity cost pre-bestpath 10 100", +# "set ip df 1", +# "set ip next-hop recursive global 198.51.110.1", +# "set ip next-hop verify-availability 198.51.111.1 100 track 10", +# "set ip precedence critical", +# "set ip address prefix-list 192.0.2.1", +# "set automatic-tag", +# "set aigp-metric 100", +# "route-map test_1 deny 20", +# "continue 100", +# "match track 105", +# "match tag list test_match_tag", +# "match ipv6 address test_acl_20", +# "match route-type level-1", +# "match as-path 200 100", +# "match additional-paths advertise-set all group-best", +# "route-map test_1 deny 10", +# "description this is test route", +# "match security-group source tag 10 20", +# "match ip next-hop prefix-list test_2_new test_1_new", +# "match ip route-source 10", +# "match local-preference 100 50", +# "match mpls-label" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# route-map test_1 deny 10 +# description this is test route +# match ip next-hop prefix-list test_2_new test_1_new +# match ip route-source 10 +# match security-group source tag 10 20 +# match local-preference 100 50 +# match mpls-label +# route-map test_1 deny 20 +# match track 105 +# match tag list test_match_tag +# match route-type level-1 +# match additional-paths advertise-set all group-best +# match as-path 200 100 +# match ipv6 address test_acl_20 +# continue 100 +# route-map test_2 deny 10 +# match security-group source tag 10 20 +# match local-preference 55 105 +# match mpls-label +# match ipv6 address test_ip_acl +# match ipv6 next-hop prefix-list test_new +# match ipv6 route-source route_src_acl +# set automatic-tag +# set ip precedence critical +# set ip address prefix-list 192.0.2.1 +# set aigp-metric 100 +# set extcommunity cost pre-bestpath 10 100 +# set ip df 1 +# set ip next-hop verify-availability 198.51.111.1 100 track 10 +# set ip next-hop recursive global 198.51.110.1 + +- name: Parse the provided configuration with the existing running configuration + cisco.ios.ios_route_maps: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "entries": [ +# { +# "action": "deny", +# "description": "this is test route", +# "match": { +# "ip": { +# "next_hop": { +# "prefix_lists": [ +# "test_2_new", +# "test_1_new" +# ] +# }, +# "route_source": { +# "acls": [ +# "10" +# ] +# } +# }, +# "local_preference": { +# "value": [ +# "100", +# "50" +# ] +# }, +# "mpls_label": true, +# "security_group": { +# "source": [ +# 10, +# 20 +# ] +# } +# }, +# "sequence": 10 +# }, +# { +# "action": "deny", +# "continue_entry": { +# "entry_sequence": 100 +# }, +# "match": { +# "additional_paths": { +# "all": true, +# "group_best": true +# }, +# "as_path": { +# "acls": [ +# 200, +# 100 +# ] +# }, +# "ipv6": { +# "address": { +# "acl": "test_acl_20" +# } +# }, +# "route_type": { +# "external": { +# "set": true +# }, +# "level_1": true, +# "nssa_external": { +# "set": true +# } +# }, +# "tag": { +# "tag_list": [ +# "test_match_tag" +# ] +# }, +# "track": 105 +# }, +# "sequence": 20 +# } +# ], +# "route_map": "test_1" +# }, +# { +# "entries": [ +# { +# "action": "deny", +# "match": { +# "ipv6": { +# "address": { +# "acl": "test_ip_acl" +# }, +# "next_hop": { +# "prefix_list": "test_new" +# }, +# "route_source": { +# "acl": "route_src_acl" +# } +# }, +# "local_preference": { +# "value": [ +# "55", +# "105" +# ] +# }, +# "mpls_label": true, +# "security_group": { +# "source": [ +# 10, +# 20 +# ] +# } +# }, +# "sequence": 10, +# "set": { +# "aigp_metric": { +# "value": 100 +# }, +# "automatic_tag": true, +# "extcommunity": { +# "cost": { +# "cost_value": 100, +# "id": "10", +# "pre_bestpath": true +# } +# }, +# "ip": { +# "address": "192.0.2.1", +# "df": 1, +# "next_hop": { +# "recursive": { +# "address": "198.51.110.1", +# "global_route": true +# }, +# "verify_availability": { +# "address": "198.51.111.1", +# "sequence": 100, +# "track": 10 +# } +# }, +# "precedence": { +# "critical": true +# } +# } +# } +# } +# ], +# "route_map": "test_2" +# } +# ] +""" + +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: list +after: + description: The resulting configuration model invocation. + returned: when changed + sample: > + The configuration returned will always be in the same format + of the parameters above. + type: list +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: ['route-map test_1 deny 10', 'description this is test route', 'match ip route-source 10', 'match track 105'] +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.route_maps.route_maps import ( + Route_mapsArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.route_maps.route_maps import ( + Route_maps, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Route_mapsArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Route_maps(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_service.py b/ansible_collections/cisco/ios/plugins/modules/ios_service.py new file mode 100644 index 000000000..89fe2ed47 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_service.py @@ -0,0 +1,690 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2023 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_service +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: ios_service +short_description: Resource module to configure service. +description: + - This module configures and manages service attributes on IOS platforms +version_added: 4.6.0 +author: + - Ambroise Rosset (@earendilfr) +notes: + - Tested against Cisco IOSXE Version 16.9 + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: A dictionnary of service configuration + suboptions: + call_home: + description: Cisco call-home service + type: bool + compress_config: + description: Compress the nvram configuration file + type: bool + config: + description: TFTP load config files + type: bool + counters: + description: + - Control aging of interface counters by setting the maximum counter aging threshold + type: int + default: 0 + dhcp: + description: Enable DHCP server and relay agent + type: bool + default: true + disable_ip_fast_frag: + description: Disable IP particle-based fast fragmentation + type: bool + exec_callback: + description: Enable exec callback + type: bool + exec_wait: + description: Delay EXEC startup on noisy lines + type: bool + hide_telnet_addresses: + description: Hide destination addresses in telnet command + type: bool + internal: + description: Enable/Disable Internal commands + type: bool + linenumber: + description: enable line number banner for each exec + type: bool + log: + description: log backtrace + type: bool + log_hidden: + description: Enable syslog msgs for hidden/internal commands + type: bool + nagle: + description: Enable Nagle's congestion control algorithm + type: bool + old_slip_prompts: + description: Allow old scripts to operate with slip/ppp + type: bool + pad: + description: Enable PAD commands + type: bool + pad_cmns: + description: Enable PAD over CMNS connections + type: bool + pad_from_xot: + description: Accept XOT to PAD connections + type: bool + pad_to_xot: + description: Allow outgoing PAD over XOT connections + type: bool + password_encryption: + description: Encrypt system passwords + type: bool + password_recovery: + description: Password recovery + type: bool + default: true + prompt: + description: Enable mode specific prompt + type: bool + default: true + private_config_encryption: + description: Enable private config file encryption + type: bool + pt_vty_logging: + description: Log significant VTY-Async events + type: bool + scripting: + description: scripting + type: bool + sequence_numbers: + description: Stamp logger messages with a sequence number + type: bool + slave_coredump: + description: slave-coredump + type: bool + slave_log: + description: Enable log capability of slave IPs + type: bool + default: true + tcp_keepalives_in: + description: Generate keepalives on idle incoming network connections + type: bool + tcp_keepalives_out: + description: Generate keepalives on idle outgoing network connections + type: bool + tcp_small_servers: + description: + - TCP and UDP small servers are servers (daemons, in Unix parlance) that run in the + router which are useful for diagnostics. + suboptions: + enable: + description: Enable small TCP servers (e.g., ECHO) + type: bool + max_servers: + description: + - Set number of allowable TCP small servers + - 1 to 2147483647 or no-limit + type: str + type: dict + telnet_zeroidle: + description: Set TCP window 0 when connection is idle + type: bool + timestamps: + description: Timestamp debug/log messages + elements: dict + suboptions: + msg: + description: Timestamp log or debug messages + choices: + - debug + - log + type: str + enable: + description: Enable timestamp for the choosen message + type: bool + timestamp: + description: Timestamp with date and time or with system uptime + choices: + - datetime + - uptime + type: str + datetime_options: + description: Options for date and time timestamp + suboptions: + localtime: + description: Use local time zone for timestamps + type: bool + msec: + description: Include milliseconds in timestamp + type: bool + show_timezone: + description: Add time zone information to timestamp + type: bool + year: + description: Include year in timestam + type: bool + type: dict + type: list + udp_small_servers: + description: + - TCP and UDP small servers are servers (daemons, in Unix parlance) that run in the + router which are useful for diagnostics. + suboptions: + enable: + description: Enable small UDP servers (e.g., ECHO) + type: bool + max_servers: + description: + - Set number of allowable TCP small servers + - 1 to 2147483647 or no-limit + type: str + type: dict + unsupported_transceiver: + description: enable support for third-party transceivers + type: bool + type: dict + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by + executing the command B(show running-config | section ^service|^no service). + - 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 + state: + choices: + - merged + - replaced + - deleted + - gathered + - rendered + - parsed + default: merged + description: + - The state the configuration should be left in. + - Refer to examples for more details. + type: str +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- + +# router-ios#show running-config all | section ^service +# service slave-log +# service timestamps debug datetime msec +# service timestamps log datetime msec +# service private-config-encryption +# service prompt config +# service counters max age 0 +# service dhcp +# service call-home +# service password-recovery + +- name: Merge provided configuration with device configuration + cisco.ios.ios_service: + config: + tcp_keepalives_in: true + tcp_keepalives_out: true + timestamps: + - msg: debug + enable: true + timestamp: datetime + - msg: log + enable: true + timestamp: datetime + pad: false + password_encryption: true + state: merged + +# Task Output +# ----------- +# +# before: +# call_home: true +# counters: 0 +# dhcp: true +# password_recovery: true +# private_config_encryption: true +# prompt: true +# slave_log: true +# timestamps: +# - datetime_options: +# msec: true +# msg: debug +# timestamp: datetime +# - datetime_options: +# msec: true +# msg: log +# timestamp: datetime +# commands: +# - service password-encryption +# - service tcp-keepalives-in +# - service tcp-keepalives-out +# after: +# call_home: true +# counters: 0 +# dhcp: true +# password_encryption: true +# password_recovery: true +# private_config_encryption: true +# prompt: true +# slave_log: true +# tcp_keepalives_in: true +# tcp_keepalives_out: true +# timestamps: +# - datetime_options: +# msec: true +# msg: debug +# timestamp: datetime +# - datetime_options: +# msec: true +# msg: log +# timestamp: datetime + +# After state: +# ------------ + +# router-ios#show running-config all | section ^service +# service slave-log +# service tcp-keepalives-in +# service tcp-keepalives-out +# service timestamps debug datetime msec +# service timestamps log datetime msec +# service password-encryption +# service private-config-encryption +# service prompt config +# service counters max age 0 +# service dhcp +# service call-home +# service password-recovery + +# Using replaced + +# Before state: +# ------------- + +# router-ios#show running-config all | section ^service +# service slave-log +# service tcp-keepalives-in +# service tcp-keepalives-out +# service timestamps debug datetime msec +# service timestamps log datetime msec +# service password-encryption +# service private-config-encryption +# service prompt config +# service counters max age 0 +# service dhcp +# service call-home +# service password-recovery + +- name: Replaces device configuration of services with provided configuration + cisco.ios.ios_service: + config: + timestamps: + - msg: log + enable: true + timestamp: datetime + datetime_options: + localtime: true + msec: true + show_timezone: true + year: true + - msg: debug + enable: true + timestamp: datetime + pad: false + password_encryption: true + state: "replaced" + +# Task Output +# ----------- +# +# before: +# call_home: true +# counters: 0 +# dhcp: true +# password_encryption: true +# password_recovery: true +# private_config_encryption: true +# prompt: true +# slave_log: true +# tcp_keepalives_in: true +# tcp_keepalives_out: true +# timestamps: +# - datetime_options: +# msec: true +# msg: debug +# timestamp: datetime +# - datetime_options: +# msec: true +# msg: log +# timestamp: datetime +# commands: +# - no service call-home +# - no service tcp-keepalives-in +# - no service tcp-keepalives-out +# - no service timestamps log +# - service timestamps log datetime msec localtime show-timezone year +# - no service timestamps debug +# - service timestamps debug datetime +# after: +# counters: 0 +# dhcp: true +# password_encryption: true +# password_recovery: true +# private_config_encryption: true +# prompt: true +# slave_log: true +# timestamps: +# - msg: debug +# timestamp: datetime +# - datetime_options: +# localtime: true +# msec: true +# show_timezone: true +# year: true +# msg: log +# timestamp: datetime + +# After state: +# ------------ + +# router-ios#show running-config all | section ^service +# service slave-log +# service timestamps debug datetime +# service timestamps log datetime msec localtime show-timezone year +# service password-encryption +# service private-config-encryption +# service prompt config +# service counters max age 0 +# service dhcp +# service password-recovery + +# Using Deleted + +# Before state: +# ------------- + +# router-ios#show running-config all | section ^service +# service slave-log +# service timestamps debug datetime +# service timestamps log datetime msec localtime show-timezone year +# service password-encryption +# service private-config-encryption +# service prompt config +# service counters max age 0 +# service dhcp +# service password-recovery + +- name: "Delete service configuration and restore default configuration for some importants service (those with a default value in module)" + cisco.ios.ios_service: + state: deleted + +# Task Output +# ----------- +# +# before: +# counters: 0 +# dhcp: true +# password_encryption: true +# password_recovery: true +# private_config_encryption: true +# prompt: true +# slave_log: true +# timestamps: +# - msg: debug +# timestamp: datetime +# - datetime_options: +# localtime: true +# msec: true +# show_timezone: true +# year: true +# msg: log +# timestamp: datetime +# commands: +# - no service password-encryption +# - no service timestamps debug +# - no service timestamps log +# after: +# counters: 0 +# dhcp: true +# password_recovery: true +# private_config_encryption: true +# prompt: true +# slave_log: true + +#·After·state: +#·------------ +# +# router-ios#show running-config all | section ^service +# service slave-log +# service private-config-encryption +# service prompt config +# service counters max age 0 +# service dhcp +# service password-recovery + +# Using gathered + +# Before state: +# ------------- +# +# router-ios#show running-config all | section ^service +# service slave-log +# service timestamps debug datetime +# service timestamps log datetime msec localtime show-timezone year +# service password-encryption +# service private-config-encryption +# service prompt config +# service counters max age 0 +# service dhcp +# service password-recovery + +- name: Gather facts of interfaces + cisco.ios.ios_service: + config: + state: gathered + +# Task Output +# ----------- +# +# gathered: +# counters: 0 +# dhcp: true +# password_encryption: true +# password_recovery: true +# private_config_encryption: true +# prompt: true +# slave_log: true +# timestamps: +# - msg: debug +# timestamp: datetime +# - datetime_options: +# localtime: true +# msec: true +# show_timezone: true +# year: true +# msg: log +# timestamp: datetime + +# Using rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_service: + config: + timestamps: + - msg: log + enable: true + timestamp: datetime + datetime_options: + localtime: true + msec: true + show_timezone: true + year: true + - msg: debug + enable: true + timestamp: datetime + pad: false + password_encryption: true + state: rendered + +# ·Task·Output +# ----------- +# +# rendered: +# - service dhcp +# - service password-encryption +# - service password-recovery +# - service prompt config +# - service slave-log +# - service timestamps log datetime msec localtime show-timezone year +# - service timestamps debug datetime + +# Using parsed + +# File: parsed.cfg +# ---------------- +# +# no service pad +# service password-encryption +# service tcp-keepalives-in +# service tcp-keepalives-out +# service timestamps debug datetime msec localtime show-timezone year +# service timestamps log datetime msec localtime show-timezone year +# service counters max age 5 + +- name: Parse the provided configuration + cisco.ios.ios_service: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Task Output +# ----------- +# +# parsed: +# counters: 5 +# dhcp: true +# password_encryption: true +# password_recovery: true +# prompt: true +# slave_log: true +# tcp_keepalives_in: true +# tcp_keepalives_out: true +# timestamps: +# - datetime_options: +# localtime: true +# msec: true +# show_timezone: true +# year: true +# msg: debug +# timestamp: datetime +# - datetime_options: +# localtime: true +# msec: true +# show_timezone: true +# year: true +# msg: log +# timestamp: datetime +""" + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: list + sample: + - no service config + - service tcp-keepalives-in + - service tcp-keepalives-out +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - service dhcp + - service password-encryption + - service password-recovery + - service prompt config + - service slave-log +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.service.service import ( + ServiceArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.service.service import ( + Service, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=ServiceArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Service(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_snmp_server.py b/ansible_collections/cisco/ios/plugins/modules/ios_snmp_server.py new file mode 100644 index 000000000..5cfc735d8 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_snmp_server.py @@ -0,0 +1,2026 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2022 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_snmp_server +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +author: + - Sagar Paul (@KB-perByte) +description: + - This module provides declarative management of SNMP server on Cisco IOS devices. +module: ios_snmp_server +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). +options: + config: + description: A dictionary of SNMP server configuration + suboptions: + accounting: + description: SNMP Accounting parameters + suboptions: + command: + description: For SNMP set commands + type: str + type: dict + cache: + description: Enable SNMP cache and MIB expiry interval + type: int + chassis_id: + description: String to uniquely identify this chassis (Hexadecimal) + type: str + communities: + description: Community name configuration. + elements: dict + suboptions: + acl_v4: + description: standard access-list name + type: str + acl_v6: + description: IPv6 access list name + type: str + name: + description: Community name (default RO) + type: str + ro: + description: Only reads are permitted + type: bool + rw: + description: Read-write access + type: bool + view: + description: MIB view name + type: str + type: list + contact: + description: Text for mib object sysContact + type: str + context: + description: Create/Delete a context apart from default + elements: str + type: list + drop: + description: Silently drop SNMP packets + suboptions: + unknown_user: + description: Silently drop unknown v3 user packets + type: bool + vrf_traffic: + description: Silently drop SNMP packets that come on VRF interfaces + type: bool + type: dict + engine_id: + description: Configure a local or remote SNMPv3 engineID + elements: dict + suboptions: + id: + description: engine ID octet string + type: str + local: + description: Local SNMP agent + type: bool + remote: + description: Remote SNMP agent + suboptions: + host: + description: Hostname or IP address of remote SNMP notification host + type: str + udp_port: + description: The remote SNMP notification host's UDP port number. + type: int + vrf: + description: The remote notification host's VPN routing instance + type: str + type: dict + type: list + file_transfer: + description: File transfer related commands + suboptions: + access_group: + description: Access control for file transfers + type: str + protocol: + description: Access control protocol for file transfers + type: list + elements: str + type: dict + groups: + description: Define a User Security Model group + elements: dict + suboptions: + context: + description: Specify a context to associate with the group + type: str + version_option: + choices: + - auth + - noauth + - priv + description: community name to the host. + type: str + group: + description: SNMP group for the user + type: str + notify: + description: View to restrict notifications + type: str + read: + description: View to restrict read access + type: str + version: + choices: + - v1 + - v3 + - v2c + description: snmp security group version + type: str + write: + description: View to restrict write access + type: str + acl_v4: + description: specify an access-list associated with this group + type: str + acl_v6: + description: specify an access-list associated with this group + type: str + type: list + hosts: + description: Specify hosts to receive SNMP notifications + elements: dict + suboptions: + host: + description: Hostname or IP address of SNMP notification host. + type: str + informs: + description: Use SNMP inform messages. + type: bool + community_string: + description: SNMPv1/v2c community string or SNMPv3 user name + type: str + traps: + description: Use SNMP trap messages + type: list + elements: str + version: + choices: + - "1" + - 2c + - "3" + description: Notification message SNMP version. + type: str + version_option: + choices: + - auth + - noauth + - priv + description: community name to the host. + type: str + vrf: + description: Specify the VRF in which the host is configured + type: str + type: list + if_index: + description: Enable ifindex persistence + type: bool + inform: + description: Configure SNMP Informs options + suboptions: + pending: + description: Set number of unacked informs to hold + type: int + retries: + description: Set retry count for informs + type: int + timeout: + description: Set timeout for informs + type: int + type: dict + ip: + description: IP ToS configuration for SNMP traffic + suboptions: + dscp: + description: IP DSCP value for SNMP traffic + type: int + precedence: + description: IP Precedence value for SNMP traffic + type: int + type: dict + location: + description: Text for mib object sysLocation + type: str + manager: + description: Modify SNMP manager parameters + type: int + packet_size: + description: Largest SNMP packet size + type: int + password_policy: + description: SNMP v3 users password policy + elements: dict + suboptions: + change: + description: Number of Character changes b/w old and new password + type: int + digits: + description: Number of digits + type: int + lower_case: + description: Number of lower-case characters + type: int + max_len: + description: Maximum password length + type: int + min_len: + description: Minimum password length + type: int + policy_name: + description: Name of the policy + type: str + special_char: + description: Number of special case character + type: int + upper_case: + description: Number of upper-case characters + type: int + username: + description: Name of the user + type: str + type: list + queue_length: + description: Message queue length for each TRAP host + type: int + source_interface: + description: Source interface to be used for sending out SNMP notifications. + type: str + system_shutdown: + description: Enable use of the SNMP reload command + type: bool + trap_source: + description: Assign an interface for the source address of all traps + type: str + trap_timeout: + description: Set timeout for TRAP message retransmissions + type: int + traps: + description: Enable SNMP Traps + suboptions: + auth_framework: + description: Enable SNMP CISCO-AUTH-FRAMEWORK-MIB traps + suboptions: + sec_violation: + description: Mode sec_violation + type: bool + enable: + description: Enable/disable auth framework + type: bool + type: dict + bfd: + description: Allow SNMP BFD traps + suboptions: + enable: + description: Enable/disable bfd + type: bool + session_down: + description: Enable BFD session down traps + type: bool + session_up: + description: Enable BFD session up traps + type: bool + type: dict + bgp: + description: Allow bgp traps + suboptions: + cbgp2: + description: Enable BGP MIBv2 traps + type: bool + enable: + description: Enable/disable bgp traps + type: bool + state_changes: + description: Traps for FSM state changes + suboptions: + all: + description: CISCO specific trap for all fsm state changes + type: bool + backward_trans: + description: CISCO specific trap for backward transition + type: bool + limited: + description: Trap for standard backward transition and established + type: bool + enable: + description: Enable/disable bgp state_changes traps + type: bool + type: dict + threshold: + description: Mode threshold + suboptions: + prefix: + description: Enable/disable bgp threshold traps + type: bool + type: dict + type: dict + bridge: + description: Allow bridge related traps + suboptions: + newroot: + description: Enable SNMP STP Bridge MIB newroot traps + type: bool + enable: + description: Enable/disable bridge traps + type: bool + topologychange: + description: Enable SNMP STP Bridge MIB topologychange traps + type: bool + type: dict + casa: + description: Enable SNMP config casa traps + type: bool + cef: + description: Allow cef related traps + suboptions: + inconsistency: + description: Enable SNMP CEF Inconsistency traps + type: bool + peer_fib_state_change: + description: Enable SNMP CEF Peer FIB State change traps + type: bool + peer_state_change: + description: Enable SNMP CEF Peer state change traps + type: bool + resource_failure: + description: Enable SNMP CEF Resource Failure traps + type: bool + enable: + description: Enable/disable cef traps + type: bool + type: dict + cnpd: + description: Enable SNMP cnpd traps + type: bool + config: + description: Enable SNMP config traps + type: bool + config_copy: + description: Enable SNMP config copy traps + type: bool + config_ctid: + description: Enable SNMP config ctid traps + type: bool + cpu: + description: Allow CPU related traps + suboptions: + enable: + description: Enable/disable cpu traps + type: bool + threshold: + description: Mode threshold + type: bool + type: dict + dhcp: + description: Enable SNMP dhcp traps + type: bool + dlsw: + description: Allow dlsw related traps + suboptions: + circuit: + description: Enable SNMP dlsw circuit traps + type: bool + enable: + description: Enable/disable cef traps + type: bool + tconn: + description: Enable SNMP dlsw peer transport connection traps + type: bool + type: dict + eigrp: + description: Enable SNMP eigrp traps + type: bool + entity: + description: Enable SNMP entity traps + type: bool + energywise: + description: Enable SNMP energywise traps + type: bool + envmon: + description: Allow envmon related traps + suboptions: + fan: + description: Enable SNMP envmon fan traps + suboptions: + shutdown: + description: Enable SNMP environmental monitor shutdown traps + type: bool + enable: + description: Enable/disable fan traps + type: bool + status: + description: Enable SNMP environmental status change traps + type: bool + supply: + description: Enable SNMP environmental monitor supply traps + type: bool + temperature: + description: Enable SNMP environmental monitor temperature traps + type: bool + type: dict + shutdown: + description: Enable SNMP environmental monitor shutdown traps + type: bool + status: + description: Enable SNMP environmental status change traps + type: bool + supply: + description: Enable SNMP environmental monitor supply traps + type: bool + temperature: + description: Enable SNMP environmental monitor temperature traps + type: bool + type: dict + ethernet: + description: Allow ethernet traps + suboptions: + cfm: + description: Enable SNMP Ethernet CFM traps + suboptions: + alarm: + description: Enable SNMP Ethernet CFM fault alarm trap + type: bool + cc: + description: Enable SNMP Ethernet CC trap + type: dict + suboptions: + config: + description: Enable SNMP Ethernet CFM configuration error traps + type: bool + cross_connect: + description: Enable SNMP Ethernet CFM cross-connect traps + type: bool + loop: + description: Enable SNMP Ethernet CFM loop traps + type: bool + mep_down: + description: Enable SNMP Ethernet CFM CC Down traps + type: bool + mep_up: + description: Enable SNMP Ethernet CFM CC Up traps + type: bool + crosscheck: + description: Enable SNMP Ethernet CC crosscheck trap + type: dict + suboptions: + mep_missing: + description: Enable SNMP Ethernet CC crosscheck missing trap + type: bool + mep_unknown: + description: Enable SNMP Ethernet CC crosscheck unknown traps + type: bool + service_up: + description: Enable SNMP Ethernet CC crosscheck service traps + type: bool + type: dict + evc: + description: Enable SNMP Ethernet EVC traps + suboptions: + create: + description: Enable SNMP Ethernet EVC create traps + type: bool + delete: + description: Enable SNMP Ethernet EVC delete traps + type: bool + status: + description: Enable SNMP Ethernet EVC status traps + type: bool + type: dict + type: dict + event_manager: + description: Enable SNMP event-manager traps + type: bool + flowmon: + description: Enable SNMP flowmon traps + type: bool + firewall: + description: Enable SNMP firewall traps + suboptions: + enable: + description: Enable/disable firewall traps + type: bool + serverstatus: + description: Enable firewall server status change trap + type: bool + type: dict + frame_relay: + description: Allow frame-relay traps + suboptions: + enable: + description: Enable/disable frame-relay traps + type: bool + subif: + description: Enable SNMP frame-relay subinterface traps + suboptions: + count: + description: Maximum number of traps sent per interval + type: int + interval: + description: Interval duration in which to limit the number of traps sent + type: int + enable: + description: Enable/disable subif traps + type: bool + type: dict + type: dict + fru_ctrl: + description: Enable SNMP fru-ctrl traps + type: bool + hsrp: + description: Enable SNMP hsrp traps + type: bool + ike: + description: Allow ike traps + suboptions: + policy: + description: Enable IKE Policy traps + suboptions: + add: + description: Enable IKE Policy add trap + type: bool + delete: + description: Enable IKE Policy delete trap + type: bool + type: dict + tunnel: + description: Enable IKE Tunnel traps + suboptions: + start: + description: Enable IKE Tunnel start trap + type: bool + stop: + description: Enable IKE Tunnel stop trap + type: bool + type: dict + type: dict + ipmulticast: + description: Enable SNMP ip multi cast traps + type: bool + ipsec: + description: Allow ike traps + suboptions: + cryptomap: + description: Enable IPsec Cryptomap traps + suboptions: + add: + description: Enable IPsec Cryptomap add trap + type: bool + attach: + description: Enable IPsec Cryptomap Attach trap + type: bool + delete: + description: Enable IPsec Cryptomap delete trap + type: bool + detach: + description: Enable IPsec Cryptomap Detach trap + type: bool + type: dict + too_many_sas: + description: Enable IPsec Tunnel Start trap + type: bool + tunnel: + description: Enable IPsec Tunnel traps + suboptions: + start: + description: Enable IPsec Tunnel start trap + type: bool + stop: + description: Enable IPsec Tunnel stop trap + type: bool + type: dict + type: dict + ipsla: + description: Enable SNMP ipsla traps + type: bool + l2tun: + description: Allow SNMP l2tun traps + suboptions: + pseudowire_status: + description: Enable BFD pseudo wire status traps + type: bool + session: + description: Enable BFD session traps + type: bool + type: dict + msdp: + description: Enable SNMP msdp traps + type: bool + mvpn: + description: Enable SNMP mvpn traps + type: bool + mpls_vpn: + description: Enable SNMP mpls traps + type: bool + ospf: + description: Allow ospf related traps + suboptions: + cisco_specific: + description: Cisco specific traps + suboptions: + error: + description: error traps + type: bool + lsa: + description: Lsa related traps + type: bool + retransmit: + description: Packet retransmit traps + type: bool + state_change: + description: state change traps + suboptions: + nssa_trans_change: + description: Nssa translator state changes + type: bool + shamlink: + description: Config mismatch errors on virtual interfaces + suboptions: + interface: + description: Sham link interface state changes + type: bool + neighbor: + description: Sham link neighbor state changes + type: bool + type: dict + type: dict + type: dict + error: + description: Enable error traps + type: bool + retransmit: + description: Enable/disable ospf retransmit traps + type: bool + lsa: + description: Enable/disable ospf lsa traps + type: bool + state_change: + description: Enable/disable state change traps + type: bool + type: dict + pim: + description: Allow PIM traps + suboptions: + invalid_pim_message: + description: Enable invalid pim message trap + type: bool + neighbor_change: + description: Enable neighbor change trap + type: bool + rp_mapping_change: + description: Enable rp mapping change trap + type: bool + enable: + description: Enable/disable PIM traps + type: bool + type: dict + vrfmib: + description: Allow vrfmib traps + suboptions: + vrf_up: + description: Enable vrf-up trap + type: bool + vrf_down: + description: Enable vrf-down trap + type: bool + vnet_trunk_up: + description: Enable vnet-trunk-up trap + type: bool + vnet_trunk_down: + description: Enable vnet-trunk-down traps + type: bool + type: dict + pki: + description: Enable SNMP pki traps + type: bool + rsvp: + description: Enable SNMP RSVP traps + type: bool + isis: + description: Enable SNMP isis traps + type: bool + pw_vc: + description: Enable SNMP pw vc traps + type: bool + snmp: + description: Enable SNMP traps + suboptions: + authentication: + description: Enable authentication trap + type: bool + coldstart: + description: Enable coldStart trap + type: bool + linkdown: + description: Enable linkDown trap + type: bool + linkup: + description: Enable linkUp trap + type: bool + warmstart: + description: Enable warmStart trap + type: bool + type: dict + syslog: + description: Enable SNMP syslog traps + type: bool + transceiver_all: + description: Enable SNMP transceiver traps + type: bool + tty: + description: Enable SNMP tty TCP connection traps + type: bool + vrrp: + description: Enable SNMP vrrp traps + type: bool + type: dict + users: + description: Define a user who can access the SNMP engine + elements: dict + suboptions: + acl_v6: + description: Access list ipv6 associated + type: str + acl_v4: + description: Access list ipv4 associated + type: str + authentication: + description: + - Authentication parameters for the user. + - Effects idempotency of module as configuration applied is not reflected + in running-config. + type: dict + suboptions: + algorithm: + description: Select algorithm for authentication. + type: str + choices: ["md5", "sha"] + password: + description: + - Authentication password for user. + type: str + encryption: + description: + - Encryption parameters for the user. + - Effects idempotency of module as configuration applied is not reflected + in running-config. + type: dict + suboptions: + priv: + description: Select algorithm for encryption. + type: str + choices: ["3des", "aes", "des"] + priv_option: + description: Add extra option for specific priv if any. + type: str + password: + description: + - Authentication password for user. + type: str + group: + description: SNMP group for the user. + type: str + remote: + description: System where an SNMPv3 user is hosted + type: str + udp_port: + description: UDP port used by the remote SNMP system + type: int + username: + description: SNMP user name + type: str + version: + choices: + - v1 + - v2c + - v3 + description: SNMP security version + type: str + version_option: + choices: + - encrypted + description: Enable encrypted version option. + type: str + vrf: + description: The remote SNMP entity's VPN Routing instance + type: str + type: list + views: + description: Define an SNMPv2 MIB view + elements: dict + suboptions: + excluded: + description: MIB family is excluded from the view + type: bool + family_name: + description: MIB view family name + type: str + included: + description: MIB family is included in the view + type: bool + name: + description: Name of the view + type: str + type: list + type: dict + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device by + executing the command B(show running-config | include snmp-server). + - 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 + state: + choices: + - merged + - replaced + - overridden + - deleted + - parsed + - gathered + - rendered + default: merged + description: + - The state the configuration should be left in. + - Refer to examples for more details. + - The states I(replaced) and I(overridden) have identical + behaviour for this module. + type: str +short_description: Resource module to configure snmp server. +version_added: 2.6.0 +""" + +EXAMPLES = """ +# Using state: merged + +# Before state: +# ------------- + +# router-ios#show running-config | section ^snmp-server +# --------------------- EMPTY ----------------- + +# Merged play: +# ------------ + +- name: Apply the provided configuration + cisco.ios.ios_snmp_server: + config: + communities: + - acl_v4: testACL + name: mergedComm + rw: true + contact: contact updated using merged + engine_id: + - id: AB0C5342FF0F + remote: + host: 172.16.0.12 + udp_port: 25 + groups: + - group: mergedGroup + version: v3 + version_option: auth + file_transfer: + access_group: test + protocol: + - ftp + hosts: + - community_string: mergedComm + host: 172.16.2.9 + informs: true + traps: + - msdp + - stun + - pki + version: 2c + - community_string: mergedComm + host: 172.16.2.9 + traps: + - slb + - pki + password_policy: + - change: 3 + digits: 23 + lower_case: 12 + max_len: 24 + policy_name: MergedPolicy + special_char: 32 + upper_case: 12 + - change: 43 + min_len: 12 + policy_name: MergedPolicy2 + special_char: 22 + upper_case: 12 + - change: 11 + digits: 23 + max_len: 12 + min_len: 12 + policy_name: policy3 + special_char: 22 + upper_case: 12 + traps: + cef: + enable: true + inconsistency: true + peer_fib_state_change: true + peer_state_change: true + resource_failure: true + msdp: true + ospf: + cisco_specific: + error: true + lsa: true + retransmit: true + state_change: + nssa_trans_change: true + shamlink: + interface: true + neighbor: true + error: true + lsa: true + retransmit: true + state_change: true + syslog: true + tty: true + users: + - acl_v4: "24" + group: dev + username: userPaul + version: v1 + state: merged + +# Commands Fired: +# --------------- + +# "commands": [ +# "snmp-server contact contact updated using merged", +# "snmp-server file-transfer access-group test protocol ftp", +# "snmp-server enable traps msdp", +# "snmp-server enable traps syslog", +# "snmp-server enable traps tty", +# "snmp-server enable traps ospf cisco-specific errors", +# "snmp-server enable traps ospf cisco-specific retransmit", +# "snmp-server enable traps ospf cisco-specific lsa", +# "snmp-server enable traps ospf cisco-specific state-change nssa-trans-change", +# "snmp-server enable traps ospf cisco-specific state-change shamlink interface", +# "snmp-server enable traps ospf cisco-specific state-change shamlink neighbor", +# "snmp-server enable traps ospf errors", +# "snmp-server enable traps ospf retransmit", +# "snmp-server enable traps ospf lsa", +# "snmp-server enable traps ospf state-change", +# "snmp-server enable traps cef resource-failure peer-state-change peer-fib-state-change inconsistency", +# "snmp-server host 172.16.2.9 informs version 2c mergedComm msdp stun pki", +# "snmp-server host 172.16.2.9 mergedComm slb pki", +# "snmp-server group mergedGroup v3 auth", +# "snmp-server engineID remote 172.16.0.12 udp-port 25 AB0C5342FF0F", +# "snmp-server community mergedComm rw testACL", +# "snmp-server password-policy MergedPolicy define max-len 24 upper-case 12 lower-case 12 special-char 32 digits 23 change 3", +# "snmp-server password-policy MergedPolicy2 define min-len 12 upper-case 12 special-char 22 change 43", +# "snmp-server password-policy policy3 define min-len 12 max-len 12 upper-case 12 special-char 22 digits 23 change 11", +# "snmp-server user userPaul dev v1 access 24" +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section ^snmp-server +# snmp-server engineID remote 172.16.0.12 udp-port 25 AB0C5342FF0F +# snmp-server user userPaul dev v1 access 24 +# snmp-server group mergedGroup v3 auth +# snmp-server community mergedComm RW testACL +# snmp-server contact contact updated using merged +# snmp-server enable traps tty +# snmp-server enable traps ospf state-change +# snmp-server enable traps ospf errors +# snmp-server enable traps ospf retransmit +# snmp-server enable traps ospf lsa +# snmp-server enable traps ospf cisco-specific state-change nssa-trans-change +# snmp-server enable traps ospf cisco-specific state-change shamlink interface +# snmp-server enable traps ospf cisco-specific state-change shamlink neighbor +# snmp-server enable traps ospf cisco-specific errors +# snmp-server enable traps ospf cisco-specific retransmit +# snmp-server enable traps ospf cisco-specific lsa +# snmp-server enable traps cef resource-failure peer-state-change peer-fib-state-change inconsistency +# snmp-server enable traps msdp +# snmp-server enable traps syslog +# snmp-server host 172.16.2.9 informs version 2c mergedComm msdp stun pki +# snmp-server host 172.16.2.9 mergedComm slb pki +# snmp-server file-transfer access-group test protocol ftp +# snmp-server password-policy MergedPolicy define max-len 24 upper-case 12 lower-case 12 special-char 32 digits 23 change 3 +# snmp-server password-policy MergedPolicy2 define min-len 12 upper-case 12 special-char 22 change 43 +# snmp-server password-policy policy3 define min-len 12 max-len 12 upper-case 12 special-char 22 digits 23 change 11 + +# Using state: deleted + +# Before state: +# ------------- + +# router-ios#show running-config | section ^snmp-server +# snmp-server engineID remote 172.16.0.12 udp-port 25 AB0C5342FF0F +# snmp-server user userPaul dev v1 access 24 +# snmp-server group mergedGroup v3 auth +# snmp-server community mergedComm RW testACL +# snmp-server contact contact updated using merged +# snmp-server enable traps tty +# snmp-server enable traps ospf state-change +# snmp-server enable traps ospf errors +# snmp-server enable traps ospf retransmit +# snmp-server enable traps ospf lsa +# snmp-server enable traps ospf cisco-specific state-change nssa-trans-change +# snmp-server enable traps ospf cisco-specific state-change shamlink interface +# snmp-server enable traps ospf cisco-specific state-change shamlink neighbor +# snmp-server enable traps ospf cisco-specific errors +# snmp-server enable traps ospf cisco-specific retransmit +# snmp-server enable traps ospf cisco-specific lsa +# snmp-server enable traps cef resource-failure peer-state-change peer-fib-state-change inconsistency +# snmp-server enable traps msdp +# snmp-server enable traps syslog +# snmp-server host 172.16.2.9 informs version 2c mergedComm msdp stun pki +# snmp-server host 172.16.2.9 mergedComm slb pki +# snmp-server file-transfer access-group test protocol ftp +# snmp-server password-policy MergedPolicy define max-len 24 upper-case 12 lower-case 12 special-char 32 digits 23 change 3 +# snmp-server password-policy MergedPolicy2 define min-len 12 upper-case 12 special-char 22 change 43 +# snmp-server password-policy policy3 define min-len 12 max-len 12 upper-case 12 special-char 22 digits 23 change 11 + +# Deleted play: +# ------------- + +- name: Remove all existing configuration + cisco.ios.ios_snmp_server: + state: deleted + +# Commands Fired: +# --------------- + +# "commands": [ +# "no snmp-server contact contact updated using merged", +# "no snmp-server file-transfer access-group test protocol ftp", +# "no snmp-server enable traps msdp", +# "no snmp-server enable traps syslog", +# "no snmp-server enable traps tty", +# "no snmp-server enable traps ospf cisco-specific errors", +# "no snmp-server enable traps ospf cisco-specific retransmit", +# "no snmp-server enable traps ospf cisco-specific lsa", +# "no snmp-server enable traps ospf cisco-specific state-change nssa-trans-change", +# "no snmp-server enable traps ospf cisco-specific state-change shamlink interface", +# "no snmp-server enable traps ospf cisco-specific state-change shamlink neighbor", +# "no snmp-server enable traps ospf errors", +# "no snmp-server enable traps ospf retransmit", +# "no snmp-server enable traps ospf lsa", +# "no snmp-server enable traps ospf state-change", +# "no snmp-server enable traps cef resource-failure peer-state-change peer-fib-state-change inconsistency", +# "no snmp-server host 172.16.2.9 informs version 2c mergedComm msdp stun pki", +# "no snmp-server host 172.16.2.9 mergedComm slb pki", +# "no snmp-server group mergedGroup v3 auth", +# "no snmp-server engineID remote 172.16.0.12 udp-port 25 AB0C5342FF0F", +# "no snmp-server community mergedComm rw testACL", +# "no snmp-server password-policy MergedPolicy define max-len 24 upper-case 12 lower-case 12 special-char 32 digits 23 change 3", +# "no snmp-server password-policy MergedPolicy2 define min-len 12 upper-case 12 special-char 22 change 43", +# "no snmp-server password-policy policy3 define min-len 12 max-len 12 upper-case 12 special-char 22 digits 23 change 11", +# "no snmp-server user userPaul dev v1 access 24" +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section ^snmp-server +# --------------------- EMPTY ----------------- + +# Using state: overridden + +# Before state: +# ------------- + +# router-ios#show running-config | section ^snmp-server +# snmp-server engineID remote 172.16.0.12 udp-port 25 AB0C5342FF0F +# snmp-server user userPaul dev v1 access 24 +# snmp-server group mergedGroup v3 auth +# snmp-server community mergedComm RW testACL +# snmp-server contact contact updated using merged +# snmp-server enable traps tty +# snmp-server enable traps ospf state-change +# snmp-server enable traps ospf errors +# snmp-server enable traps ospf retransmit +# snmp-server enable traps ospf lsa +# snmp-server enable traps ospf cisco-specific state-change nssa-trans-change +# snmp-server enable traps ospf cisco-specific state-change shamlink interface +# snmp-server enable traps ospf cisco-specific state-change shamlink neighbor +# snmp-server enable traps ospf cisco-specific errors +# snmp-server enable traps ospf cisco-specific retransmit +# snmp-server enable traps ospf cisco-specific lsa +# snmp-server enable traps cef resource-failure peer-state-change peer-fib-state-change inconsistency +# snmp-server enable traps msdp +# snmp-server enable traps syslog +# snmp-server host 172.16.2.9 informs version 2c mergedComm msdp stun pki +# snmp-server host 172.16.2.9 mergedComm slb pki +# snmp-server file-transfer access-group test protocol ftp +# snmp-server password-policy MergedPolicy define max-len 24 upper-case 12 lower-case 12 special-char 32 digits 23 change 3 +# snmp-server password-policy MergedPolicy2 define min-len 12 upper-case 12 special-char 22 change 43 +# snmp-server password-policy policy3 define min-len 12 max-len 12 upper-case 12 special-char 22 digits 23 change 11 + +# Overridden play: +# ---------------- + +- name: Override commands with provided configuration + cisco.ios.ios_snmp_server: + config: + location: "location entry for snmp" + packet_size: 500 + communities: + - acl_v4: acl_uq + name: communityOverriden + rw: true + state: overridden + +# Commands Fired: +# --------------- +# "commands": [ +# "no snmp-server contact contact updated using merged", +# "no snmp-server file-transfer access-group test protocol ftp", +# "snmp-server location location entry for snmp", +# "snmp-server packetsize 500", +# "no snmp-server enable traps msdp", +# "no snmp-server enable traps syslog", +# "no snmp-server enable traps tty", +# "no snmp-server enable traps ospf cisco-specific errors", +# "no snmp-server enable traps ospf cisco-specific retransmit", +# "no snmp-server enable traps ospf cisco-specific lsa", +# "no snmp-server enable traps ospf cisco-specific state-change nssa-trans-change", +# "no snmp-server enable traps ospf cisco-specific state-change shamlink interface", +# "no snmp-server enable traps ospf cisco-specific state-change shamlink neighbor", +# "no snmp-server enable traps ospf errors", +# "no snmp-server enable traps ospf retransmit", +# "no snmp-server enable traps ospf lsa", +# "no snmp-server enable traps ospf state-change", +# "no snmp-server enable traps cef resource-failure peer-state-change peer-fib-state-change inconsistency", +# "no snmp-server host 172.16.2.9 informs version 2c mergedComm msdp stun pki", +# "no snmp-server host 172.16.2.9 mergedComm slb pki", +# "no snmp-server group mergedGroup v3 auth", +# "no snmp-server engineID remote 172.16.0.12 udp-port 25 AB0C5342FF0F", +# "snmp-server community communityOvverriden rw acl_uq", +# "no snmp-server community mergedComm rw testACL", +# "no snmp-server password-policy MergedPolicy define max-len 24 upper-case 12 lower-case 12 special-char 32 digits 23 change 3", +# "no snmp-server password-policy MergedPolicy2 define min-len 12 upper-case 12 special-char 22 change 43", +# "no snmp-server password-policy policy3 define min-len 12 max-len 12 upper-case 12 special-char 22 digits 23 change 11", +# "no snmp-server user userPaul dev v1 access 24" +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section ^snmp-server +# snmp-server community communityOverriden RW acl_uq +# snmp-server packetsize 500 +# snmp-server location location entry for snmp + +# Using state: replaced + +# Before state: +# ------------- + +# router-ios#show running-config | section ^snmp-server +# snmp-server community communityOverriden RW acl_uq +# snmp-server packetsize 500 +# snmp-server location location entry for snmp + +# Replaced play: +# -------------- + +- name: Replace commands with provided configuration + cisco.ios.ios_snmp_server: + config: + location: "updated location entry" + packet_size: 500 + communities: + - acl_v4: acl_uq + name: communityOverriden + rw: true + state: replaced + +# Commands Fired: +# --------------- + +# "commands": [ +# "snmp-server location updated location entry" +# ], + +# After state: +# ------------ + +# router-ios#show running-config | section ^snmp-server +# snmp-server community communityOverriden RW acl_uq +# snmp-server packetsize 500 +# snmp-server location updated location entry + +# Using state: gathered + +# Before state: +# ------------- + +#router-ios#show running-config | section ^snmp-server +# snmp-server engineID remote 172.16.0.12 udp-port 25 AB0C5342FF0F +# snmp-server user userPaul dev v1 access 24 +# snmp-server group mergedGroup v3 auth +# snmp-server community communityOvverriden RW acl_uq +# snmp-server community mergedComm RW testACL +# snmp-server packetsize 500 +# snmp-server location updated location entry +# snmp-server contact contact updated using merged +# snmp-server enable traps tty +# snmp-server enable traps ospf state-change +# snmp-server enable traps ospf errors +# snmp-server enable traps ospf retransmit +# snmp-server enable traps ospf lsa +# snmp-server enable traps ospf cisco-specific state-change nssa-trans-change +# snmp-server enable traps ospf cisco-specific state-change shamlink interface +# snmp-server enable traps ospf cisco-specific state-change shamlink neighbor +# snmp-server enable traps ospf cisco-specific errors +# snmp-server enable traps ospf cisco-specific retransmit +# snmp-server enable traps ospf cisco-specific lsa +# snmp-server enable traps cef resource-failure peer-state-change peer-fib-state-change inconsistency +# snmp-server enable traps msdp +# snmp-server enable traps syslog +# snmp-server host 172.16.2.9 informs version 2c mergedComm msdp stun pki +# snmp-server host 172.16.2.9 mergedComm slb pki +# snmp-server file-transfer access-group test protocol ftp +# snmp-server password-policy MergedPolicy define max-len 24 upper-case 12 lower-case 12 special-char 32 digits 23 change 3 +# snmp-server password-policy MergedPolicy2 define min-len 12 upper-case 12 special-char 22 change 43 +# snmp-server password-policy policy3 define min-len 12 max-len 12 upper-case 12 special-char 22 digits 23 change 11 + +# Gathered play: +# -------------- + +- name: Gather listed snmp config + cisco.ios.ios_snmp_server: + state: gathered + +# Module Execution Result: +# ------------------------ + +# "gathered": { +# "communities": [ +# { +# "acl_v4": "acl_uq", +# "name": "communityOvverriden", +# "rw": true +# }, +# { +# "acl_v4": "testACL", +# "name": "mergedComm", +# "rw": true +# } +# ], +# "contact": "contact updated using merged", +# "engine_id": [ +# { +# "id": "AB0C5342FF0F", +# "remote": { +# "host": "172.16.0.12", +# "udp_port": 25 +# } +# } +# ], +# "file_transfer": { +# "access_group": "test", +# "protocol": [ +# "ftp" +# ] +# }, +# "groups": [ +# { +# "group": "mergedGroup", +# "version": "v3", +# "version_option": "auth" +# } +# ], +# "hosts": [ +# { +# "community_string": "mergedComm", +# "host": "172.16.2.9", +# "informs": true, +# "traps": [ +# "msdp", +# "stun", +# "pki" +# ], +# "version": "2c" +# }, +# { +# "community_string": "mergedComm", +# "host": "172.16.2.9", +# "traps": [ +# "slb", +# "pki" +# ] +# } +# ], +# "location": "updated location entry", +# "packet_size": 500, +# "password_policy": [ +# { +# "change": 3, +# "digits": 23, +# "lower_case": 12, +# "max_len": 24, +# "policy_name": "MergedPolicy", +# "special_char": 32, +# "upper_case": 12 +# }, +# { +# "change": 43, +# "min_len": 12, +# "policy_name": "MergedPolicy2", +# "special_char": 22, +# "upper_case": 12 +# }, +# { +# "change": 11, +# "digits": 23, +# "max_len": 12, +# "min_len": 12, +# "policy_name": "policy3", +# "special_char": 22, +# "upper_case": 12 +# } +# ], +# "traps": { +# "cef": { +# "enable": true, +# "inconsistency": true, +# "peer_fib_state_change": true, +# "peer_state_change": true, +# "resource_failure": true +# }, +# "msdp": true, +# "ospf": { +# "cisco_specific": { +# "error": true, +# "lsa": true, +# "retransmit": true, +# "state_change": { +# "nssa_trans_change": true, +# "shamlink": { +# "interface": true, +# "neighbor": true +# } +# } +# }, +# "error": true, +# "lsa": true, +# "retransmit": true, +# "state_change": true +# }, +# "syslog": true, +# "tty": true +# }, +# "users": [ +# { +# "acl_v4": "24", +# "group": "dev", +# "username": "userPaul", +# "version": "v1" +# } +# ] +# }, + +# Using state: rendered + +# Rendered play: +# -------------- + +- name: Render the commands for provided configuration + cisco.ios.ios_snmp_server: + config: + accounting: + command: default + cache: 2 + chassis_id: entry for chassis id + communities: + - acl_v6: te + name: test + ro: true + view: terst1 + - acl_v4: "1322" + name: wete + ro: true + - acl_v4: paul + name: weteww + rw: true + contact: details contact + context: + - contextA + - contextB + engine_id: + - id: AB0C5342FA0A + local: true + - id: AB0C5342FAAB + remote: + host: 172.16.0.2 + udp_port: 23 + - id: AB0C5342FAAA + remote: + host: 172.16.0.1 + udp_port: 22 + file_transfer: + access_group: testAcl + protocol: + - ftp + - rcp + groups: + - group: grpFamily + version: v3 + version_option: auth + - context: mycontext + group: grpFamily + version: v1 + - acl_v4: "2" + group: grp1 + notify: me + version: v1 + - group: newtera + version: v3 + version_option: priv + - group: relaplacing + version: v3 + version_option: noauth + hosts: + - community_string: check + host: 172.16.2.99 + informs: true + traps: + - msdp + - stun + version: 2c + - community_string: check + host: 172.16.2.99 + traps: + - slb + - pki + - community_string: checktrap + host: 172.16.2.99 + traps: + - isis + - hsrp + - community_string: newtera + host: 172.16.2.1 + traps: + - rsrb + - pim + - rsvp + - slb + - pki + version: "3" + version_option: priv + - community_string: relaplacing + host: 172.16.2.1 + traps: + - slb + - pki + version: "3" + version_option: noauth + - community_string: trapsac + host: 172.16.2.1 + traps: + - tty + - bgp + version: 2c + - community_string: www + host: 172.16.1.1 + traps: + - tty + - bgp + version: "3" + version_option: auth + inform: + pending: 2 + ip: + dscp: 2 + location: "entry for snmp location" + packet_size: 500 + password_policy: + - change: 3 + digits: 23 + lower_case: 12 + max_len: 24 + policy_name: policy1 + special_char: 32 + upper_case: 12 + - change: 9 + min_len: 12 + policy_name: policy2 + special_char: 22 + upper_case: 12 + - change: 11 + digits: 23 + max_len: 12 + min_len: 12 + policy_name: policy3 + special_char: 22 + upper_case: 12 + queue_length: 2 + source_interface: Loopback999 + system_shutdown: true + trap_source: GigabitEthernet0/0 + trap_timeout: 2 + traps: + auth_framework: + enable: true + bgp: + cbgp2: true + enable: true + bfd: + enable: true + session_down: true + session_up: true + bridge: + enable: true + newroot: true + topologychange: true + casa: true + cef: + enable: true + inconsistency: true + peer_fib_state_change: true + peer_state_change: true + resource_failure: true + dlsw: + enable: true + eigrp: true + ethernet: + cfm: + alarm: true + evc: + status: true + event_manager: true + flowmon: true + frame_relay: + enable: true + subif: + enable: true + hsrp: true + ike: + policy: + add: true + delete: true + tunnel: + start: true + stop: true + ipmulticast: true + ipsec: + cryptomap: + add: true + attach: true + delete: true + detach: true + too_many_sas: true + tunnel: + start: true + stop: true + ipsla: true + l2tun: + pseudowire_status: true + session: true + msdp: true + ospf: + cisco_specific: + error: true + lsa: true + retransmit: true + state_change: + nssa_trans_change: true + shamlink: + interface: true + neighbor: true + error: true + lsa: true + retransmit: true + state_change: true + pim: + enable: true + invalid_pim_message: true + neighbor_change: true + rp_mapping_change: true + pki: true + rsvp: true + snmp: + authentication: true + coldstart: true + linkdown: true + linkup: true + warmstart: true + syslog: true + tty: true + users: + - acl_v4: "24" + group: groupFamily + username: paul + version: v1 + - acl_v4: ipv6 + group: groupFamily + username: domnic + version: v3 + - group: relaplacing + username: relaplacing + version: v3 + state: rendered + +# Module Execution Result: +# ------------------------ + +# "rendered": [ +# "snmp-server accounting commands default", +# "snmp-server cache interval 2", +# "snmp-server chassis-id entry for chassis id", +# "snmp-server contact details contact", +# "snmp-server file-transfer access-group testAcl protocol ftp rcp", +# "snmp-server inform pending 2", +# "snmp-server ip dscp 2", +# "snmp-server location entry for snmp location", +# "snmp-server packetsize 500", +# "snmp-server queue-length 2", +# "snmp-server trap timeout 2", +# "snmp-server source-interface informs Loopback999", +# "snmp-server trap-source GigabitEthernet0/0", +# "snmp-server system-shutdown", +# "snmp-server enable traps auth-framework", +# "snmp-server enable traps bfd session-down session-up", +# "snmp-server enable traps bgp cbgp2", +# "snmp-server enable traps bridge newroot topologychange", +# "snmp-server enable traps casa", +# "snmp-server enable traps eigrp", +# "snmp-server enable traps event-manager", +# "snmp-server enable traps flowmon", +# "snmp-server enable traps hsrp", +# "snmp-server enable traps ipsla", +# "snmp-server enable traps msdp", +# "snmp-server enable traps pki", +# "snmp-server enable traps rsvp", +# "snmp-server enable traps syslog", +# "snmp-server enable traps tty", +# "snmp-server enable traps ipmulticast", +# "snmp-server enable traps ike policy add", +# "snmp-server enable traps ike policy delete", +# "snmp-server enable traps ike tunnel start", +# "snmp-server enable traps ike tunnel stop", +# "snmp-server enable traps ipsec cryptomap add", +# "snmp-server enable traps ipsec cryptomap delete", +# "snmp-server enable traps ipsec cryptomap attach", +# "snmp-server enable traps ipsec cryptomap detach", +# "snmp-server enable traps ipsec tunnel start", +# "snmp-server enable traps ipsec tunnel stop", +# "snmp-server enable traps ipsec too-many-sas", +# "snmp-server enable traps ospf cisco-specific errors", +# "snmp-server enable traps ospf cisco-specific retransmit", +# "snmp-server enable traps ospf cisco-specific lsa", +# "snmp-server enable traps ospf cisco-specific state-change nssa-trans-change", +# "snmp-server enable traps ospf cisco-specific state-change shamlink interface", +# "snmp-server enable traps ospf cisco-specific state-change shamlink neighbor", +# "snmp-server enable traps ospf errors", +# "snmp-server enable traps ospf retransmit", +# "snmp-server enable traps ospf lsa", +# "snmp-server enable traps ospf state-change", +# "snmp-server enable traps l2tun pseudowire status", +# "snmp-server enable traps l2tun session", +# "snmp-server enable traps pim neighbor-change rp-mapping-change invalid-pim-message", +# "snmp-server enable traps snmp authentication linkdown linkup warmstart coldstart", +# "snmp-server enable traps frame-relay", +# "snmp-server enable traps cef resource-failure peer-state-change peer-fib-state-change inconsistency", +# "snmp-server enable traps dlsw", +# "snmp-server enable traps ethernet evc status", +# "snmp-server enable traps ethernet cfm alarm", +# "snmp-server host 172.16.2.99 informs version 2c check msdp stun", +# "snmp-server host 172.16.2.99 check slb pki", +# "snmp-server host 172.16.2.99 checktrap isis hsrp", +# "snmp-server host 172.16.2.1 version 3 priv newtera rsrb pim rsvp slb pki", +# "snmp-server host 172.16.2.1 version 3 noauth relaplacing slb pki", +# "snmp-server host 172.16.2.1 version 2c trapsac tty bgp", +# "snmp-server host 172.16.1.1 version 3 auth www tty bgp", +# "snmp-server group grpFamily v1 context mycontext", +# "snmp-server group grp1 v1 notify me access 2", +# "snmp-server group newtera v3 priv", +# "snmp-server group relaplacing v3 noauth", +# "snmp-server engineID local AB0C5342FA0A", +# "snmp-server engineID remote 172.16.0.2 udp-port 23 AB0C5342FAAB", +# "snmp-server engineID remote 172.16.0.1 udp-port 22 AB0C5342FAAA", +# "snmp-server community test view terst1 ro ipv6 te", +# "snmp-server community wete ro 1322", +# "snmp-server community weteww rw paul", +# "snmp-server context contextA", +# "snmp-server context contextB", +# "snmp-server password-policy policy1 define max-len 24 upper-case 12 lower-case 12 special-char 32 digits 23 change 3", +# "snmp-server password-policy policy2 define min-len 12 upper-case 12 special-char 22 change 9", +# "snmp-server password-policy policy3 define min-len 12 max-len 12 upper-case 12 special-char 22 digits 23 change 11", +# "snmp-server user paul groupFamily v1 access 24", +# "snmp-server user domnic groupFamily v3 access ipv6", +# "snmp-server user relaplacing relaplacing v3" +# ] + +# Using state: parsed + +# File: parsed.cfg +# ---------------- + +# snmp-server engineID local AB0C5342FA0A +# snmp-server engineID remote 172.16.0.2 udp-port 23 AB0C5342FAAB +# snmp-server engineID remote 172.16.0.1 udp-port 22 AB0C5342FAAA +# snmp-server user newuser newfamily v1 access 24 +# snmp-server user paul familypaul v3 access ipv6 ipv6acl +# snmp-server user replaceUser replaceUser v3 +# snmp-server group group0 v3 auth +# snmp-server group group1 v1 notify me access 2 +# snmp-server group group2 v3 priv +# snmp-server group replaceUser v3 noauth +# snmp-server community commu1 view view1 RO ipv6 te +# snmp-server community commu2 RO 1322 +# snmp-server community commu3 RW paul +# snmp-server trap timeout 2 +# snmp-server trap-source GigabitEthernet0/0 +# snmp-server source-interface informs Loopback999 +# snmp-server packetsize 500 +# snmp-server enable traps vrfmib vrf-up vrf-down vnet-trunk-up vnet-trunk-down +# snmp-server host 172.16.2.99 informs version 2c check msdp stun +# snmp-server host 172.16.2.1 version 2c trapsac tty bgp +# snmp-server host 172.16.1.1 version 3 auth group0 tty bgp +# snmp-server context contextWord1 +# snmp-server context contextWord2 +# snmp-server file-transfer access-group testAcl protocol ftp +# snmp-server file-transfer access-group testAcl protocol rcp +# snmp-server cache interval 2 +# snmp-server password-policy policy2 define min-len 12 upper-case 12 special-char 22 change 9 +# snmp-server password-policy policy3 define min-len 12 max-len 12 upper-case 12 special-char 22 digits 23 change 11 +# snmp-server accounting commands default +# snmp-server inform pending 2 + +# Parsed play: +# ------------ + +- name: Parse the provided configuration with the existing running configuration + cisco.ios.ios_snmp_server: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": { +# "accounting": { +# "command": "default" +# }, +# "cache": 2, +# "communities": [ +# { +# "acl_v6": "te", +# "name": "commu1", +# "ro": true, +# "view": "view1" +# }, +# { +# "acl_v4": "1322", +# "name": "commu2", +# "ro": true +# }, +# { +# "acl_v4": "paul", +# "name": "commu3", +# "rw": true +# } +# ], +# "context": [ +# "contextWord1", +# "contextWord2" +# ], +# "engine_id": [ +# { +# "id": "AB0C5342FA0A", +# "local": true +# }, +# { +# "id": "AB0C5342FAAA", +# "remote": { +# "host": "172.16.0.1", +# "udp_port": 22 +# } +# }, +# { +# "id": "AB0C5342FAAB", +# "remote": { +# "host": "172.16.0.2", +# "udp_port": 23 +# } +# } +# ], +# "file_transfer": { +# "access_group": "testAcl", +# "protocol": [ +# "rcp", +# "ftp" +# ] +# }, +# "groups": [ +# { +# "group": "group0", +# "version": "v3", +# "version_option": "auth" +# }, +# { +# "acl_v4": "2", +# "group": "group1", +# "notify": "me", +# "version": "v1" +# }, +# { +# "group": "group2", +# "version": "v3", +# "version_option": "priv" +# }, +# { +# "group": "replaceUser", +# "version": "v3", +# "version_option": "noauth" +# } +# ], +# "hosts": [ +# { +# "community_string": "group0", +# "host": "172.16.1.1", +# "traps": [ +# "tty", +# "bgp" +# ], +# "version": "3", +# "version_option": "auth" +# }, +# { +# "community_string": "trapsac", +# "host": "172.16.2.1", +# "traps": [ +# "tty", +# "bgp" +# ], +# "version": "2c" +# }, +# { +# "community_string": "check", +# "host": "172.16.2.99", +# "informs": true, +# "traps": [ +# "msdp", +# "stun" +# ], +# "version": "2c" +# } +# ], +# "inform": { +# "pending": 2 +# }, +# "packet_size": 500, +# "password_policy": [ +# { +# "change": 9, +# "min_len": 12, +# "policy_name": "policy2", +# "special_char": 22, +# "upper_case": 12 +# }, +# { +# "change": 11, +# "digits": 23, +# "max_len": 12, +# "min_len": 12, +# "policy_name": "policy3", +# "special_char": 22, +# "upper_case": 12 +# } +# ], +# "source_interface": "Loopback999", +# "trap_source": "GigabitEthernet0/0", +# "trap_timeout": 2, +# "traps": { +# "vrfmib": { +# "vnet_trunk_down": true, +# "vnet_trunk_up": true, +# "vrf_down": true, +# "vrf_up": true +# } +# }, +# "users": [ +# { +# "acl_v4": "24", +# "group": "newfamily", +# "username": "newuser", +# "version": "v1" +# }, +# { +# "acl_v4": "ipv6", +# "group": "familypaul", +# "username": "paul", +# "version": "v3" +# }, +# { +# "group": "replaceUser", +# "username": "replaceUser", +# "version": "v3" +# } +# ] +# } + +""" + +RETURN = """ +before: + description: The configuration prior to the module execution. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: dict + sample: > + This output will always be in the same format as the + module argspec. +after: + description: The resulting configuration after module execution. + returned: when changed + type: dict + sample: > + This output will always be in the same format as the + module argspec. +commands: + description: The set of commands pushed to the remote device. + returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) + type: list + sample: + - snmp-server host 172.16.2.99 informs version 2c check msdp stun + - snmp-server engineID remote 172.16.0.2 udp-port 23 AB0C5342FAAB + - snmp-server group grp1 v1 notify me access 2 +rendered: + description: The provided configuration in the task rendered in device-native format (offline). + returned: when I(state) is C(rendered) + type: list + sample: + - snmp-server enable traps ipsec cryptomap attach + - snmp-server password-policy policy1 define max-len 24 upper-case 12 lower-case 12 special-char 32 digits 23 change 3 + - snmp-server cache interval 2 +gathered: + description: Facts about the network resource gathered from the remote device as structured data. + returned: when I(state) is C(gathered) + type: list + sample: > + This output will always be in the same format as the + module argspec. +parsed: + description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. + returned: when I(state) is C(parsed) + type: list + sample: > + This output will always be in the same format as the + module argspec. +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.snmp_server.snmp_server import ( + Snmp_serverArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.snmp_server.snmp_server import ( + Snmp_server, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Snmp_serverArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Snmp_server(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_static_routes.py b/ansible_collections/cisco/ios/plugins/modules/ios_static_routes.py new file mode 100644 index 000000000..ffa7290e4 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_static_routes.py @@ -0,0 +1,1014 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2023 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" +The module file for ios_static_routes +""" + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_static_routes +short_description: Resource module to configure static routes. +description: This module configures and manages the static routes on IOS platforms. +version_added: 1.0.0 +author: + - Sagar Paul (@KB-perByte) + - Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) + - The module examples uses callback plugin (stdout_callback = yaml) to generate task + output in yaml format. +options: + config: + description: A dictionary of static route options + type: list + elements: dict + suboptions: + vrf: + description: + - IP VPN Routing/Forwarding instance name. + - NOTE, In case of IPV4/IPV6 VRF routing table should pre-exist before configuring. + - NOTE, if the vrf information is not provided then the routes shall be configured + under global vrf. + type: str + address_families: + elements: dict + description: + - Address family to use for the static routes + type: list + suboptions: + afi: + description: + - Top level address family indicator. + required: true + type: str + choices: + - ipv4 + - ipv6 + routes: + description: Configuring static route + type: list + elements: dict + suboptions: + dest: + description: Destination prefix with its subnet mask + type: str + required: true + topology: + description: + - Configure static route for a Topology Routing/Forwarding instance + - NOTE, VRF and Topology can be used together only with Multicast + and Topology should pre-exist before it can be used + type: str + next_hops: + description: + - next hop address or interface + type: list + elements: dict + suboptions: + forward_router_address: + description: Forwarding router's address + type: str + interface: + description: Interface for directly connected static routes + type: str + dhcp: + description: Default gateway obtained from DHCP + type: bool + distance_metric: + description: Distance metric for this route + type: int + global: + description: Next hop address is global + type: bool + name: + description: Specify name of the next hop + type: str + multicast: + description: multicast route + type: bool + unicast: + description: unicast route (ipv6 specific) + type: bool + permanent: + description: permanent route + type: bool + tag: + description: + - Set tag for this route + - Refer to vendor documentation for valid values. + type: int + track: + description: + - Install route depending on tracked item with tracked object + number. + - Tracking does not support multicast + - Refer to vendor documentation for valid values. + type: int + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS + device by executing the command B(show running-config | include ip route|ipv6 route). + - 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 + state: + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - gathered + - rendered + - parsed + default: merged +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route + +- name: Merge provided configuration with device configuration + cisco.ios.ios_static_routes: + config: + - vrf: blue + address_families: + - afi: ipv4 + routes: + - dest: 192.0.2.0/24 + next_hops: + - forward_router_address: 192.0.2.1 + name: merged_blue + tag: 50 + track: 150 + - address_families: + - afi: ipv4 + routes: + - dest: 198.51.100.0/24 + next_hops: + - forward_router_address: 198.51.101.1 + name: merged_route_1 + distance_metric: 110 + tag: 40 + multicast: true + - forward_router_address: 198.51.101.2 + name: merged_route_2 + distance_metric: 30 + - forward_router_address: 198.51.101.3 + name: merged_route_3 + - afi: ipv6 + routes: + - dest: 2001:DB8:0:3::/64 + next_hops: + - forward_router_address: 2001:DB8:0:3::2 + name: merged_v6 + tag: 105 + state: merged + +# Task Output +# ----------- +# +# before: +# - {} +# commands: +# - ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name merged_v6 +# - ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name merged_route_1 multicast +# - ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name merged_route_2 +# - ip route 198.51.100.0 255.255.255.0 198.51.101.3 name merged_route_3 +# - ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name merged_blue track 150 +# after: +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 198.51.100.0/24 +# next_hops: +# - forward_router_address: 198.51.101.3 +# name: merged_route_3 +# - distance_metric: 30 +# forward_router_address: 198.51.101.2 +# name: merged_route_2 +# - distance_metric: 110 +# forward_router_address: 198.51.101.1 +# multicast: true +# name: merged_route_1 +# tag: 40 +# - afi: ipv6 +# routes: +# - dest: 2001:DB8:0:3::/64 +# next_hops: +# - forward_router_address: 2001:DB8:0:3::2 +# name: merged_v6 +# tag: 105 +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 192.0.2.0/24 +# next_hops: +# - forward_router_address: 192.0.2.1 +# name: merged_blue +# tag: 50 +# track: 150 +# vrf: blue + +# After state: +# ------------ +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name merged_blue track 150 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name merged_route_3 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name merged_route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name merged_route_1 multicast +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name merged_v6 + +# Using replaced + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name merged_blue track 150 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name merged_route_3 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name merged_route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name merged_route_1 multicast +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name merged_v6 + +- name: Replace provided configuration with device configuration + cisco.ios.ios_static_routes: + config: + - address_families: + - afi: ipv4 + routes: + - dest: 198.51.100.0/24 + next_hops: + - forward_router_address: 198.51.101.1 + name: replaced_route + distance_metric: 175 + tag: 70 + multicast: true + state: replaced + +# Task Output +# ----------- +# +# before: +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 198.51.100.0/24 +# next_hops: +# - forward_router_address: 198.51.101.3 +# name: merged_route_3 +# - distance_metric: 30 +# forward_router_address: 198.51.101.2 +# name: merged_route_2 +# - distance_metric: 110 +# forward_router_address: 198.51.101.1 +# multicast: true +# name: merged_route_1 +# tag: 40 +# - afi: ipv6 +# routes: +# - dest: 2001:DB8:0:3::/64 +# next_hops: +# - forward_router_address: 2001:DB8:0:3::2 +# name: merged_v6 +# tag: 105 +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 192.0.2.0/24 +# next_hops: +# - forward_router_address: 192.0.2.1 +# name: merged_blue +# tag: 50 +# track: 150 +# vrf: blue +# commands: +# - ip route 198.51.100.0 255.255.255.0 198.51.101.1 175 tag 70 name replaced_route multicast +# - no ip route 198.51.100.0 255.255.255.0 198.51.101.3 name merged_route_3 +# - no ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name merged_route_2 +# after: +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 198.51.100.0/24 +# next_hops: +# - distance_metric: 175 +# forward_router_address: 198.51.101.1 +# multicast: true +# name: replaced_route +# tag: 70 +# - afi: ipv6 +# routes: +# - dest: 2001:DB8:0:3::/64 +# next_hops: +# - forward_router_address: 2001:DB8:0:3::2 +# name: merged_v6 +# tag: 105 +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 192.0.2.0/24 +# next_hops: +# - forward_router_address: 192.0.2.1 +# name: merged_blue +# tag: 50 +# track: 150 +# vrf: blue + +# After state: +# ------------ +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name merged_blue track 150 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 175 tag 70 name replaced_route multicast +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name merged_v6 + +# Using overridden + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name merged_blue track 150 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 175 tag 70 name replaced_route multicast +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name merged_v6 + +- name: Override provided configuration with device configuration + cisco.ios.ios_static_routes: + config: + - vrf: blue + address_families: + - afi: ipv4 + routes: + - dest: 192.0.2.0/24 + next_hops: + - forward_router_address: 192.0.2.1 + name: override_vrf + tag: 50 + track: 150 + state: overridden + +# Task Output +# ----------- +# +# before: +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 198.51.100.0/24 +# next_hops: +# - distance_metric: 175 +# forward_router_address: 198.51.101.1 +# multicast: true +# name: replaced_route +# tag: 70 +# - afi: ipv6 +# routes: +# - dest: 2001:DB8:0:3::/64 +# next_hops: +# - forward_router_address: 2001:DB8:0:3::2 +# name: merged_v6 +# tag: 105 +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 192.0.2.0/24 +# next_hops: +# - forward_router_address: 192.0.2.1 +# name: merged_blue +# tag: 50 +# track: 150 +# vrf: blue +# commands: +# - ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name override_vrf track 150 +# - no ip route 198.51.100.0 255.255.255.0 198.51.101.1 175 tag 70 name replaced_route multicast +# - no ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name merged_v6 +# after: +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 192.0.2.0/24 +# next_hops: +# - forward_router_address: 192.0.2.1 +# name: override_vrf +# tag: 50 +# track: 150 +# vrf: blue + +# After state: +# ------------ +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name override_vrf track 150 + +# Using deleted + +# Before state: +# ------------- +# vios#show running-config | include ip route|ipv6 route +# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 + +- name: Delete the exact static routes, with all the static routes explicitly mentioned in want + cisco.ios.ios_static_routes: + config: + - vrf: blue + address_families: + - afi: ipv4 + routes: + - dest: 192.0.2.0/24 + next_hops: + - forward_router_address: 192.0.2.1 + name: test_vrf + tag: 50 + track: 150 + - address_families: + - afi: ipv4 + routes: + - dest: 198.51.100.0/24 + next_hops: + - forward_router_address: 198.51.101.1 + name: route_1 + distance_metric: 110 + tag: 40 + multicast: true + - forward_router_address: 198.51.101.2 + name: route_2 + distance_metric: 30 + - forward_router_address: 198.51.101.3 + name: route_3 + - afi: ipv6 + routes: + - dest: 2001:DB8:0:3::/64 + next_hops: + - forward_router_address: 2001:DB8:0:3::2 + name: test_v6 + tag: 105 + state: deleted + +# Task Output +# ----------- +# +# before: +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 198.51.100.0/24 +# next_hops: +# - forward_router_address: 198.51.101.3 +# name: route_3 +# - distance_metric: 30 +# forward_router_address: 198.51.101.2 +# name: route_2 +# - distance_metric: 110 +# forward_router_address: 198.51.101.1 +# multicast: true +# name: route_1 +# tag: 40 +# - afi: ipv6 +# routes: +# - dest: 2001:DB8:0:3::/64 +# next_hops: +# - forward_router_address: 2001:DB8:0:3::2 +# name: test_v6 +# tag: 105 +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 192.0.2.0/24 +# next_hops: +# - forward_router_address: 192.0.2.1 +# name: test_vrf +# tag: 50 +# track: 150 +# vrf: blue +# commands: +# - no ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150 +# - no ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# - no ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# - no ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast +# - no ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 +# after: {} + +# After state: +# ------------ +# +# vios#show running-config | include ip route|ipv6 route + +# Using deleted - delete based on specific routes + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 + +- name: Delete destination specific static routes + cisco.ios.ios_static_routes: + config: + - address_families: + - afi: ipv4 + routes: + - dest: 198.51.100.0/24 + state: deleted + +# Task Output +# ----------- +# +# before: +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 198.51.100.0/24 +# next_hops: +# - forward_router_address: 198.51.101.3 +# name: route_3 +# - distance_metric: 30 +# forward_router_address: 198.51.101.2 +# name: route_2 +# - distance_metric: 110 +# forward_router_address: 198.51.101.1 +# multicast: true +# name: route_1 +# tag: 40 +# - afi: ipv6 +# routes: +# - dest: 2001:DB8:0:3::/64 +# next_hops: +# - forward_router_address: 2001:DB8:0:3::2 +# name: test_v6 +# tag: 105 +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 192.0.2.0/24 +# next_hops: +# - forward_router_address: 192.0.2.1 +# name: test_vrf +# tag: 50 +# track: 150 +# vrf: blue +# commands: +# - no ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# - no ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# - no ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast +# after: +# - address_families: +# - afi: ipv6 +# routes: +# - dest: 2001:DB8:0:3::/64 +# next_hops: +# - forward_router_address: 2001:DB8:0:3::2 +# name: test_v6 +# tag: 105 +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 192.0.2.0/24 +# next_hops: +# - forward_router_address: 192.0.2.1 +# name: test_vrf +# tag: 50 +# track: 150 +# vrf: blue + +# After state: +# ------------ +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150 +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 + +# Using deleted - delete based on vrfs + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 + +- name: Delete vrf specific static routes + cisco.ios.ios_static_routes: + config: + - vrf: blue + state: deleted + +# Task Output +# ----------- +# +# before: +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 198.51.100.0/24 +# next_hops: +# - forward_router_address: 198.51.101.3 +# name: route_3 +# - distance_metric: 30 +# forward_router_address: 198.51.101.2 +# name: route_2 +# - distance_metric: 110 +# forward_router_address: 198.51.101.1 +# multicast: true +# name: route_1 +# tag: 40 +# - afi: ipv6 +# routes: +# - dest: 2001:DB8:0:3::/64 +# next_hops: +# - forward_router_address: 2001:DB8:0:3::2 +# name: test_v6 +# tag: 105 +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 192.0.2.0/24 +# next_hops: +# - forward_router_address: 192.0.2.1 +# name: test_vrf +# tag: 50 +# track: 150 +# vrf: blue +# commands: +# - no ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150 +# after: +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 198.51.100.0/24 +# next_hops: +# - forward_router_address: 198.51.101.3 +# name: route_3 +# - distance_metric: 30 +# forward_router_address: 198.51.101.2 +# name: route_2 +# - distance_metric: 110 +# forward_router_address: 198.51.101.1 +# multicast: true +# name: route_1 +# tag: 40 +# - afi: ipv6 +# routes: +# - dest: 2001:DB8:0:3::/64 +# next_hops: +# - forward_router_address: 2001:DB8:0:3::2 +# name: test_v6 +# tag: 105 + +# After state: +# ------------ +# +# vios#show running-config | include ip route|ipv6 route +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 + +# Using deleted - delete all + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 + +- name: Delete ALL configured static routes + cisco.ios.ios_static_routes: + state: deleted + +# Task Output +# ----------- +# +# before: +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 198.51.100.0/24 +# next_hops: +# - forward_router_address: 198.51.101.3 +# name: route_3 +# - distance_metric: 30 +# forward_router_address: 198.51.101.2 +# name: route_2 +# - distance_metric: 110 +# forward_router_address: 198.51.101.1 +# multicast: true +# name: route_1 +# tag: 40 +# - afi: ipv6 +# routes: +# - dest: 2001:DB8:0:3::/64 +# next_hops: +# - forward_router_address: 2001:DB8:0:3::2 +# name: test_v6 +# tag: 105 +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 192.0.2.0/24 +# next_hops: +# - forward_router_address: 192.0.2.1 +# name: test_vrf +# tag: 50 +# track: 150 +# vrf: blue +# commands: +# - no ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# - no ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# - no ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast +# - no ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 +# - no ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150 +# after: {} + +# After state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route + +# Using gathered + +# Before state: +# ------------- +# +# vios#show running-config | include ip route|ipv6 route +# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 + +- name: Gather facts of static routes + cisco.ios.ios_static_routes: + config: + state: gathered + +# Task Output +# ----------- +# +# gathered: +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 198.51.100.0/24 +# next_hops: +# - forward_router_address: 198.51.101.3 +# name: route_3 +# - distance_metric: 30 +# forward_router_address: 198.51.101.2 +# name: route_2 +# - distance_metric: 110 +# forward_router_address: 198.51.101.1 +# multicast: true +# name: route_1 +# tag: 40 +# - afi: ipv6 +# routes: +# - dest: 2001:DB8:0:3::/64 +# next_hops: +# - forward_router_address: 2001:DB8:0:3::2 +# name: test_v6 +# tag: 105 +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 192.0.2.0/24 +# next_hops: +# - forward_router_address: 192.0.2.1 +# name: test_vrf +# tag: 50 +# track: 150 +# vrf: blue + +# Using rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_static_routes: + config: + - vrf: blue + address_families: + - afi: ipv4 + routes: + - dest: 192.0.2.0/24 + next_hops: + - forward_router_address: 192.0.2.1 + name: test_vrf + tag: 50 + track: 150 + - address_families: + - afi: ipv4 + routes: + - dest: 198.51.100.0/24 + next_hops: + - forward_router_address: 198.51.101.1 + name: route_1 + distance_metric: 110 + tag: 40 + multicast: true + - forward_router_address: 198.51.101.2 + name: route_2 + distance_metric: 30 + - forward_router_address: 198.51.101.3 + name: route_3 + - afi: ipv6 + routes: + - dest: 2001:DB8:0:3::/64 + next_hops: + - forward_router_address: 2001:DB8:0:3::2 + name: test_v6 + tag: 105 + state: rendered + +# Task Output +# ----------- +# +# rendered: +# - ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150 +# - ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast +# - ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# - ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# - ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 + +# Using parsed + +# File: parsed.cfg +# ---------------- +# +# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150 +# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 +# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2 +# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast +# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 + +- name: Parse the provided configuration + cisco.ios.ios_static_routes: + running_config: "{{ lookup('file', 'parsed.cfg') }}" + state: parsed + +# Task Output +# ----------- +# +# parsed: +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 198.51.100.0/24 +# next_hops: +# - forward_router_address: 198.51.101.3 +# name: route_3 +# - distance_metric: 30 +# forward_router_address: 198.51.101.2 +# name: route_2 +# - distance_metric: 110 +# forward_router_address: 198.51.101.1 +# multicast: true +# name: route_1 +# tag: 40 +# - afi: ipv6 +# routes: +# - dest: 2001:DB8:0:3::/64 +# next_hops: +# - forward_router_address: 2001:DB8:0:3::2 +# name: test_v6 +# tag: 105 +# - address_families: +# - afi: ipv4 +# routes: +# - dest: 192.0.2.0/24 +# next_hops: +# - forward_router_address: 192.0.2.1 +# name: test_vrf +# tag: 50 +# track: 150 +# vrf: blue + +""" + +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: ['ip route vrf test 172.31.10.0 255.255.255.0 10.10.10.2 name new_test multicast'] +rendered: + description: The set of CLI commands generated from the value in C(config) option + returned: When C(state) is I(rendered) + type: list + sample: + - ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3 + - ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6 +gathered: + description: + - The configuration as structured data transformed for the running configuration + fetched from remote host + returned: When C(state) is I(gathered) + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +parsed: + description: + - The configuration as structured data transformed for the value of + C(running_config) option + returned: When C(state) is I(parsed) + type: list + sample: > + The configuration returned will always be in the same format + of the parameters above. +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.static_routes.static_routes import ( + Static_routesArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.static_routes.static_routes import ( + Static_routes, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule( + argument_spec=Static_routesArgs.argument_spec, + mutually_exclusive=[["config", "running_config"]], + required_if=[ + ["state", "merged", ["config"]], + ["state", "replaced", ["config"]], + ["state", "overridden", ["config"]], + ["state", "rendered", ["config"]], + ["state", "parsed", ["running_config"]], + ], + supports_check_mode=True, + ) + + result = Static_routes(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_system.py b/ansible_collections/cisco/ios/plugins/modules/ios_system.py new file mode 100644 index 000000000..33e3c936d --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_system.py @@ -0,0 +1,351 @@ +#!/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: ios_system +author: Peter Sprygada (@privateip) +short_description: Module to manage the system attributes. +description: + - This module provides declarative management of node system attributes on Cisco IOS + devices. It provides an option to configure host system parameters or remove those + parameters from the device active configuration. +version_added: 1.0.0 +extends_documentation_fragment: + - cisco.ios.ios +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + hostname: + description: + - Configure the device hostname parameter. This option takes an ASCII string value. + type: str + domain_name: + description: + - Configure the IP domain name on the remote device to the provided value. Value + should be in the dotted name form and will be appended to the C(hostname) to + create a fully-qualified domain name. + type: list + elements: raw + domain_search: + description: + - Provides the list of domain suffixes to append to the hostname for the purpose + of doing name resolution. This argument accepts a list of names and will be + reconciled with the current active configuration on the running node. + type: list + elements: raw + lookup_source: + description: + - Provides one or more source interfaces to use for performing DNS lookups. The + interface provided in C(lookup_source) must be a valid interface configured + on the device. + type: str + lookup_enabled: + description: + - Administrative control for enabling or disabling DNS lookups. When this argument + is set to True, lookups are performed and when it is set to False, lookups are + not performed. + type: bool + name_servers: + description: + - List of DNS name servers by IP address to use to perform name resolution lookups. This + argument accepts either a list of DNS servers See examples. + type: list + elements: raw + state: + description: + - State of the configuration values in the device's current active configuration. When + set to I(present), the values should be configured in the device active configuration + and when set to I(absent) the values should not be in the device active configuration + default: present + choices: + - present + - absent + type: str +""" + +EXAMPLES = """ +- name: Configure hostname and domain name + cisco.ios.ios_system: + hostname: ios01 + domain_name: test.example.com + domain_search: + - ansible.com + - redhat.com + - cisco.com + +- name: Remove configuration + cisco.ios.ios_system: + state: absent + +- name: Configure DNS lookup sources + cisco.ios.ios_system: + lookup_source: MgmtEth0/0/CPU0/0 + lookup_enabled: true + +- name: Configure name servers + cisco.ios.ios_system: + name_servers: + - 8.8.8.8 + - 8.8.4.4 +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - hostname ios01 + - ip domain name test.example.com +""" +import re + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + ComplexList, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) + + +_CONFIGURED_VRFS = None + + +def has_vrf(module, vrf): + global _CONFIGURED_VRFS + if _CONFIGURED_VRFS is not None: + return vrf in _CONFIGURED_VRFS + config = get_config(module) + _CONFIGURED_VRFS = re.findall("vrf definition (\\S+)", config) + return vrf in _CONFIGURED_VRFS + + +def requires_vrf(module, vrf): + if not has_vrf(module, vrf): + module.fail_json(msg="vrf %s is not configured" % vrf) + + +def diff_list(want, have): + adds = [w for w in want if w not in have] + removes = [h for h in have if h not in want] + return adds, removes + + +def map_obj_to_commands(want, have, module): + commands = list() + state = module.params["state"] + + def needs_update(x): + return want.get(x) is not None and want.get(x) != have.get(x) + + if state == "absent": + if have["hostname"] != "Router": + commands.append("no hostname") + if have["lookup_source"]: + commands.append("no ip domain lookup source-interface %s" % have["lookup_source"]) + if have["lookup_enabled"] is False: + commands.append("ip domain lookup") + vrfs = set() + for item in have["domain_name"]: + if item["vrf"] and item["vrf"] not in vrfs: + vrfs.add(item["vrf"]) + commands.append("no ip domain name vrf %s" % item["vrf"]) + elif None not in vrfs: + vrfs.add(None) + commands.append("no ip domain name") + vrfs = set() + for item in have["domain_search"]: + if item["vrf"] and item["vrf"] not in vrfs: + vrfs.add(item["vrf"]) + commands.append("no ip domain list vrf %s" % item["vrf"]) + elif None not in vrfs: + vrfs.add(None) + commands.append("no ip domain list") + vrfs = set() + for item in have["name_servers"]: + if item["vrf"] and item["vrf"] not in vrfs: + vrfs.add(item["vrf"]) + commands.append("no ip name-server vrf %s" % item["vrf"]) + elif None not in vrfs: + vrfs.add(None) + commands.append("no ip name-server") + elif state == "present": + if needs_update("hostname"): + commands.append("hostname %s" % want["hostname"]) + if needs_update("lookup_source"): + commands.append("ip domain lookup source-interface %s" % want["lookup_source"]) + if needs_update("lookup_enabled"): + cmd = "ip domain lookup" + if want["lookup_enabled"] is False: + cmd = "no %s" % cmd + commands.append(cmd) + if want["domain_name"]: + adds, removes = diff_list(want["domain_name"], have["domain_name"]) + for item in removes: + if item["vrf"]: + commands.append("no ip domain name vrf %s %s" % (item["vrf"], item["name"])) + else: + commands.append("no ip domain name %s" % item["name"]) + for item in adds: + if item["vrf"]: + requires_vrf(module, item["vrf"]) + commands.append("ip domain name vrf %s %s" % (item["vrf"], item["name"])) + else: + commands.append("ip domain name %s" % item["name"]) + if want["domain_search"]: + adds, removes = diff_list(want["domain_search"], have["domain_search"]) + for item in removes: + if item["vrf"]: + commands.append("no ip domain list vrf %s %s" % (item["vrf"], item["name"])) + else: + commands.append("no ip domain list %s" % item["name"]) + for item in adds: + if item["vrf"]: + requires_vrf(module, item["vrf"]) + commands.append("ip domain list vrf %s %s" % (item["vrf"], item["name"])) + else: + commands.append("ip domain list %s" % item["name"]) + if want["name_servers"]: + adds, removes = diff_list(want["name_servers"], have["name_servers"]) + for item in removes: + if item["vrf"]: + commands.append("no ip name-server vrf %s %s" % (item["vrf"], item["server"])) + else: + commands.append("no ip name-server %s" % item["server"]) + for item in adds: + if item["vrf"]: + requires_vrf(module, item["vrf"]) + commands.append("ip name-server vrf %s %s" % (item["vrf"], item["server"])) + else: + commands.append("ip name-server %s" % item["server"]) + return commands + + +def parse_hostname(config): + match = re.search("^hostname (\\S+)", config, re.M) + return match.group(1) + + +def parse_domain_name(config): + match = re.findall("^ip domain[- ]name (?:vrf (\\S+) )*(\\S+)", config, re.M) + matches = list() + for vrf, name in match: + if not vrf: + vrf = None + matches.append({"name": name, "vrf": vrf}) + return matches + + +def parse_domain_search(config): + match = re.findall("^ip domain[- ]list (?:vrf (\\S+) )*(\\S+)", config, re.M) + matches = list() + for vrf, name in match: + if not vrf: + vrf = None + matches.append({"name": name, "vrf": vrf}) + return matches + + +def parse_name_servers(config): + match = re.findall("^ip name-server (?:vrf (\\S+) )*(.*)", config, re.M) + matches = list() + for vrf, servers in match: + if not vrf: + vrf = None + for server in servers.split(): + matches.append({"server": server, "vrf": vrf}) + return matches + + +def parse_lookup_source(config): + match = re.search("ip domain[- ]lookup source-interface (\\S+)", config, re.M) + if match: + return match.group(1) + + +def map_config_to_obj(module): + config = get_config(module) + return { + "hostname": parse_hostname(config), + "domain_name": parse_domain_name(config), + "domain_search": parse_domain_search(config), + "lookup_source": parse_lookup_source(config), + "lookup_enabled": "no ip domain lookup" not in config + and "no ip domain-lookup" not in config, + "name_servers": parse_name_servers(config), + } + + +def map_params_to_obj(module): + obj = { + "hostname": module.params["hostname"], + "lookup_source": module.params["lookup_source"], + "lookup_enabled": module.params["lookup_enabled"], + } + domain_name = ComplexList(dict(name=dict(key=True), vrf=dict()), module) + domain_search = ComplexList(dict(name=dict(key=True), vrf=dict()), module) + name_servers = ComplexList(dict(server=dict(key=True), vrf=dict()), module) + for arg, cast in [ + ("domain_name", domain_name), + ("domain_search", domain_search), + ("name_servers", name_servers), + ]: + if module.params[arg]: + obj[arg] = cast(module.params[arg]) + else: + obj[arg] = None + return obj + + +def main(): + """Main entry point for Ansible module execution""" + argument_spec = dict( + hostname=dict(), + domain_name=dict(type="list", elements="raw"), + domain_search=dict(type="list", elements="raw"), + name_servers=dict(type="list", elements="raw"), + lookup_source=dict(), + lookup_enabled=dict(type="bool"), + state=dict(choices=["present", "absent"], default="present"), + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + result = {"changed": False} + warnings = list() + 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: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_user.py b/ansible_collections/cisco/ios/plugins/modules/ios_user.py new file mode 100644 index 000000000..50f2f3455 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_user.py @@ -0,0 +1,600 @@ +#!/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: ios_user +author: Trishna Guha (@trishnaguha) +short_description: Module to manage the aggregates of local users. +description: + - This module provides declarative management of the local usernames configured on + network devices. It allows playbooks to manage either individual usernames or the + aggregate of usernames in the current running config. It also supports purging usernames + from the configuration that are not explicitly defined. +version_added: 1.0.0 +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + aggregate: + description: + - The set of username objects to be configured on the remote Cisco IOS device. + The list entries can either be the username or a hash of username and properties. + This argument is mutually exclusive with the C(name) argument. + aliases: + - users + - collection + type: list + elements: dict + suboptions: + name: + description: + - The username to be configured on the Cisco IOS device. This argument accepts + a string value and is mutually exclusive with the C(aggregate) argument. Please + note that this option is not same as C(provider username). + type: str + required: true + configured_password: + description: + - The password to be configured on the Cisco IOS device. The password needs to + be provided in clear and it will be encrypted on the device. Please note that + this option is not same as C(provider password). + type: str + update_password: + description: + - Since passwords are encrypted in the device running config, this argument will + instruct the module when to change the password. When set to C(always), the + password will always be updated in the device and when set to C(on_create) the + password will be updated only if the username is created. + choices: + - on_create + - always + type: str + password_type: + description: + - This argument determines whether a 'password' or 'secret' will be configured. + choices: + - secret + - password + type: str + hashed_password: + description: + - This option allows configuring hashed passwords on Cisco IOS devices. + type: dict + suboptions: + type: + description: + - Specifies the type of hash (e.g., 5 for MD5, 8 for PBKDF2, etc.) + - For this to work, the device needs to support the desired hash type + type: int + required: true + value: + description: + - The actual hashed password to be configured on the device + required: true + type: str + privilege: + description: + - The C(privilege) argument configures the privilege level of the user when logged + into the system. This argument accepts integer values in the range of 1 to 15. + type: int + view: + description: + - Configures the view for the username in the device running configuration. The + argument accepts a string value defining the view name. This argument does not + check if the view has been configured on the device. + aliases: + - role + type: str + sshkey: + description: + - Specifies one or more SSH public key(s) to configure for the given username. + - This argument accepts a valid SSH key value. + type: list + elements: str + nopassword: + description: + - Defines the username without assigning a password. This will allow the user + to login to the system without being authenticated by a password. + type: bool + state: + description: + - Configures the state of the username definition as it relates to the device + operational configuration. When set to I(present), the username(s) should be + configured in the device active configuration and when set to I(absent) the + username(s) should not be in the device active configuration + choices: + - present + - absent + type: str + name: + description: + - The username to be configured on the Cisco IOS device. This argument accepts + a string value and is mutually exclusive with the C(aggregate) argument. Please + note that this option is not same as C(provider username). + type: str + configured_password: + description: + - The password to be configured on the Cisco IOS device. The password needs to + be provided in clear and it will be encrypted on the device. Please note that + this option is not same as C(provider password). + type: str + update_password: + description: + - Since passwords are encrypted in the device running config, this argument will + instruct the module when to change the password. When set to C(always), the + password will always be updated in the device and when set to C(on_create) the + password will be updated only if the username is created. + default: always + choices: + - on_create + - always + type: str + password_type: + description: + - This argument determines whether a 'password' or 'secret' will be configured. + default: secret + choices: + - secret + - password + type: str + hashed_password: + description: + - This option allows configuring hashed passwords on Cisco IOS devices. + type: dict + suboptions: + type: + description: + - Specifies the type of hash (e.g., 5 for MD5, 8 for PBKDF2, etc.) + - For this to work, the device needs to support the desired hash type + type: int + required: true + value: + description: + - The actual hashed password to be configured on the device + required: true + type: str + privilege: + description: + - The C(privilege) argument configures the privilege level of the user when logged + into the system. This argument accepts integer values in the range of 1 to 15. + type: int + view: + description: + - Configures the view for the username in the device running configuration. The + argument accepts a string value defining the view name. This argument does not + check if the view has been configured on the device. + aliases: + - role + type: str + sshkey: + description: + - Specifies one or more SSH public key(s) to configure for the given username. + - This argument accepts a valid SSH key value. + type: list + elements: str + nopassword: + description: + - Defines the username without assigning a password. This will allow the user + to login to the system without being authenticated by a password. + type: bool + purge: + description: + - Instructs the module to consider the resource definition absolute. It will remove + any previously configured usernames on the device with the exception of the + `admin` user (the current defined set of users). + type: bool + default: false + state: + description: + - Configures the state of the username definition as it relates to the device + operational configuration. When set to I(present), the username(s) should be + configured in the device active configuration and when set to I(absent) the + username(s) should not be in the device active configuration + default: present + choices: + - present + - absent + type: str +extends_documentation_fragment: + - cisco.ios.ios +""" + +EXAMPLES = """ +- name: Create a new user + cisco.ios.ios_user: + name: ansible + nopassword: true + sshkey: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" + state: present + +- name: Create a new user with multiple keys + cisco.ios.ios_user: + name: ansible + sshkey: + - "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" + - "{{ lookup('file', '~/path/to/public_key') }}" + state: present + +- name: Remove all users except admin + cisco.ios.ios_user: + purge: true + +- name: Remove all users except admin and these listed users + cisco.ios.ios_user: + aggregate: + - name: testuser1 + - name: testuser2 + - name: testuser3 + purge: true + +- name: Set multiple users to privilege level 15 + cisco.ios.ios_user: + aggregate: + - name: netop + - name: netend + privilege: 15 + state: present + +- name: Set user view/role + cisco.ios.ios_user: + name: netop + view: network-operator + state: present + +- name: Change Password for User netop + cisco.ios.ios_user: + name: netop + configured_password: "{{ new_password }}" + update_password: always + state: present + +- name: Aggregate of users + cisco.ios.ios_user: + aggregate: + - name: ansibletest2 + - name: ansibletest3 + view: network-admin + +- name: Add a user specifying password type + cisco.ios.ios_user: + name: ansibletest4 + configured_password: "{{ new_password }}" + password_type: password + +- name: Add a user with MD5 hashed password + cisco.ios.ios_user: + name: ansibletest5 + hashed_password: + type: 5 + value: $3$8JcDilcYgFZi.yz4ApaqkHG2.8/ + +- name: Delete users with aggregate + cisco.ios.ios_user: + aggregate: + - name: ansibletest1 + - name: ansibletest2 + - name: ansibletest3 + state: absent +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - username ansible secret password + - username admin secret admin +""" +import base64 +import hashlib +import re + +from copy import deepcopy +from functools import partial + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + remove_default_spec, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) + + +def validate_privilege(value, module): + if value and not 1 <= value <= 15: + module.fail_json(msg="privilege must be between 1 and 15, got %s" % value) + + +def user_del_cmd(username): + return { + "command": "no username %s" % username, + "prompt": "This operation will remove all username related configurations with same name", + "answer": "y", + "newline": False, + } + + +def sshkey_fingerprint(sshkey): + # IOS will accept a MD5 fingerprint of the public key + # and is easier to configure in a single line + # we calculate this fingerprint here + if not sshkey: + return None + if " " in sshkey: + # ssh-rsa AAA...== comment + keyparts = sshkey.split(" ") + keyparts[1] = hashlib.md5(base64.b64decode(keyparts[1])).hexdigest().upper() + return " ".join(keyparts) + else: + # just the key, assume rsa type + return "ssh-rsa %s" % hashlib.md5(base64.b64decode(sshkey)).hexdigest().upper() + + +def map_obj_to_commands(updates, module): + commands = list() + update_password = module.params["update_password"] + password_type = module.params["password_type"] + + def needs_update(want, have, x): + return want.get(x) and want.get(x) != have.get(x) + + def add(command, want, x): + command.append("username %s %s" % (want["name"], x)) + + def add_hashed_password(command, want, x): + command.append("username %s secret %s %s" % (want["name"], x.get("type"), x.get("value"))) + + def add_ssh(command, want, x=None): + command.append("ip ssh pubkey-chain") + if x: + command.append("username %s" % want["name"]) + for item in x: + command.append("key-hash %s" % item) + command.append("exit") + else: + command.append("no username %s" % want["name"]) + command.append("exit") + + for update in updates: + want, have = update + if want["state"] == "absent": + if have["sshkey"]: + add_ssh(commands, want) + else: + commands.append(user_del_cmd(want["name"])) + if needs_update(want, have, "view"): + add(commands, want, "view %s" % want["view"]) + if needs_update(want, have, "privilege"): + add(commands, want, "privilege %s" % want["privilege"]) + if needs_update(want, have, "sshkey"): + add_ssh(commands, want, want["sshkey"]) + if needs_update(want, have, "configured_password"): + if update_password == "always" or not have: + if have and have["password_type"] and password_type != have["password_type"]: + module.fail_json( + msg="Can not have both a user password and a user secret." + + " Please choose one or the other.", + ) + add(commands, want, "%s %s" % (password_type, want["configured_password"])) + if needs_update(want, have, "hashed_password"): + add_hashed_password(commands, want, want["hashed_password"]) + if needs_update(want, have, "nopassword"): + if want["nopassword"]: + add(commands, want, "nopassword") + else: + add(commands, want, user_del_cmd(want["name"])) + return commands + + +def parse_view(data): + match = re.search("view (\\S+)", data, re.M) + if match: + return match.group(1) + + +def parse_sshkey(data, user): + sshregex = "username %s(\\n\\s+key-hash .+$)+" % user + sshcfg = re.search(sshregex, data, re.M) + key_list = [] + if sshcfg: + match = re.findall("key-hash (\\S+ \\S+(?: .+)?)$", sshcfg.group(), re.M) + if match: + key_list = match + return key_list + + +def parse_privilege(data): + match = re.search("privilege (\\S+)", data, re.M) + if match: + return int(match.group(1)) + + +def parse_password_type(data): + type = None + if data and data.split()[-3] in ["password", "secret"]: + type = data.split()[-3] + return type + + +def map_config_to_obj(module): + data = get_config(module, flags=["| section username"]) + match = re.findall("(?:^(?:u|\\s{2}u))sername (\\S+)", data, re.M) + if not match: + return list() + instances = list() + for user in set(match): + regex = "username %s .+$" % user + cfg = re.findall(regex, data, re.M) + cfg = "\n".join(cfg) + obj = { + "name": user, + "state": "present", + "nopassword": "nopassword" in cfg, + "configured_password": None, + "hashed_password": None, + "password_type": parse_password_type(cfg), + "sshkey": parse_sshkey(data, user), + "privilege": parse_privilege(cfg), + "view": parse_view(cfg), + } + instances.append(obj) + return instances + + +def get_param_value(key, item, module): + # if key doesn't exist in the item, get it from module.params + if not item.get(key): + value = module.params[key] + # if key does exist, do a type check on it to validate it + else: + value_type = module.argument_spec[key].get("type", "str") + type_checker = module._CHECK_ARGUMENT_TYPES_DISPATCHER[value_type] + type_checker(item[key]) + value = item[key] + # validate the param value (if validator func exists) + validator = globals().get("validate_%s" % key) + if all((value, validator)): + validator(value, module) + return value + + +def map_params_to_obj(module): + users = module.params["aggregate"] + if not users: + if not module.params["name"] and module.params["purge"]: + return list() + elif not module.params["name"]: + module.fail_json(msg="username is required") + else: + aggregate = [{"name": module.params["name"]}] + else: + aggregate = list() + for item in users: + if not isinstance(item, dict): + aggregate.append({"name": item}) + elif "name" not in item: + module.fail_json(msg="name is required") + else: + aggregate.append(item) + objects = list() + for item in aggregate: + get_value = partial(get_param_value, item=item, module=module) + item["configured_password"] = get_value("configured_password") + item["hashed_password"] = get_value("hashed_password") + item["nopassword"] = get_value("nopassword") + item["privilege"] = get_value("privilege") + item["view"] = get_value("view") + item["sshkey"] = render_key_list(get_value("sshkey")) + item["state"] = get_value("state") + objects.append(item) + return objects + + +def render_key_list(ssh_keys): + key_list = [] + if ssh_keys: + for item in ssh_keys: + key_list.append(sshkey_fingerprint(item)) + return key_list + + +def update_objects(want, have): + updates = list() + for entry in want: + item = next((i for i in have if i["name"] == entry["name"]), None) + if all((item is None, entry["state"] == "present")): + updates.append((entry, {})) + elif item: + for key, value in iteritems(entry): + if value and value != item[key]: + updates.append((entry, item)) + return updates + + +def main(): + """main entry point for module execution""" + hashed_password_spec = dict( + type=dict(type="int", required=True), + value=dict(no_log=True, required=True), + ) + element_spec = dict( + name=dict(), + configured_password=dict(no_log=True), + hashed_password=dict(no_log=True, type="dict", options=hashed_password_spec), + nopassword=dict(type="bool"), + update_password=dict(default="always", choices=["on_create", "always"]), + password_type=dict(default="secret", choices=["secret", "password"]), + privilege=dict(type="int"), + view=dict(aliases=["role"]), + sshkey=dict(type="list", elements="str", no_log=False), + state=dict(default="present", choices=["present", "absent"]), + ) + aggregate_spec = deepcopy(element_spec) + aggregate_spec["name"] = dict(required=True) + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + argument_spec = dict( + aggregate=dict( + type="list", + elements="dict", + options=aggregate_spec, + aliases=["users", "collection"], + ), + purge=dict(type="bool", default=False), + ) + argument_spec.update(element_spec) + mutually_exclusive = [ + ("name", "aggregate"), + ("nopassword", "hashed_password", "configured_password"), + ] + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + warnings = list() + result = {"changed": False, "warnings": warnings} + want = map_params_to_obj(module) + have = map_config_to_obj(module) + commands = map_obj_to_commands(update_objects(want, have), module) + if module.params["purge"]: + want_users = [x["name"] for x in want] + have_users = [x["name"] for x in have] + for item in set(have_users).difference(want_users): + if item != "admin": + commands.append(user_del_cmd(item)) + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_vlans.py b/ansible_collections/cisco/ios/plugins/modules/ios_vlans.py new file mode 100644 index 000000000..2877e4318 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_vlans.py @@ -0,0 +1,822 @@ +#!/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/>. +# +""" +The module file for ios_vlans +""" +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = """ +module: ios_vlans +short_description: Resource module to configure VLANs. +description: + This module provides declarative management of VLANs on Cisco IOS network + devices. +version_added: 1.0.0 +author: Sumit Jaiswal (@justjais) +notes: + - Tested against Cisco IOSl2 device with Version 15.2 on VIRL. + - Starting from v2.5.0, this module will fail when run against Cisco IOS devices that do + not support VLANs. The offline states (C(rendered) and C(parsed)) will work as expected. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + config: + description: A dictionary of VLANs options + type: list + elements: dict + suboptions: + name: + description: + - Ascii name of the VLAN. + - NOTE, I(name) should not be named/appended with I(default) as it is reserved + for device default vlans. + type: str + vlan_id: + description: + - ID of the VLAN. Range 1-4094 + type: int + required: true + mtu: + description: + - VLAN Maximum Transmission Unit. + - Refer to vendor documentation for valid values. + type: int + state: + description: + - Operational state of the VLAN + type: str + choices: + - active + - suspend + remote_span: + description: + - Configure as Remote SPAN VLAN + type: bool + shutdown: + description: + - Shutdown VLAN switching. + type: str + choices: + - enabled + - disabled + private_vlan: + description: + - Options for private vlan configuration. + type: dict + suboptions: + type: + description: + - Private VLAN type + type: str + choices: + - primary + - isolated + - community + associated: + description: + - "List of private VLANs associated with the primary . Only works with `type: primary`." + type: list + elements: int + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the IOS device + by executing the command B(show vlan). + - 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 + state: + description: + - The state the configuration should be left in + - The states I(rendered), I(gathered) and I(parsed) does not perform any change + on the device. + - The state I(rendered) will transform the configuration in C(config) option to + platform specific CLI commands which will be returned in the I(rendered) key + within the result. For state I(rendered) active connection to remote host is + not required. + - The state I(gathered) will fetch the running configuration from device and transform + it into structured data in the format as per the resource module argspec and + the value is returned in the I(gathered) key within the result. + - The state I(parsed) reads the configuration from C(running_config) option and + transforms it into JSON format as per the resource module parameters and the + value is returned in the I(parsed) key within the result. The value of C(running_config) + option should be the same format as the output of command I(show running-config + | include ip route|ipv6 route) executed on device. For state I(parsed) active + connection to remote host is not required. + type: str + choices: + - merged + - replaced + - overridden + - deleted + - rendered + - gathered + - parsed + default: merged +""" + +EXAMPLES = """ +# Using merged + +# Before state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 + +- name: Merge provided configuration with device configuration + cisco.ios.ios_vlans: + config: + - name: Vlan_10 + vlan_id: 10 + state: active + shutdown: disabled + remote_span: true + - name: Vlan_20 + vlan_id: 20 + mtu: 610 + state: active + shutdown: enabled + - name: Vlan_30 + vlan_id: 30 + state: suspend + shutdown: enabled + state: merged + +# After state: +# ------------ +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 610 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +# Using overridden + +# Before state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 610 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +- name: Override device configuration of all VLANs with provided configuration + cisco.ios.ios_vlans: + config: + - name: Vlan_10 + vlan_id: 10 + mtu: 1000 + state: overridden + +# After state: +# ------------ +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 Vlan_10 active +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1000 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 + +# Using replaced + +# Before state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 610 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +- name: Replaces device configuration of listed VLANs with provided configuration + cisco.ios.ios_vlans: + config: + - vlan_id: 20 + name: Test_VLAN20 + mtu: 700 + shutdown: disabled + - vlan_id: 50 + name: pvlan-isolated + private_vlan: + type: isolated + - vlan_id: 60 + name: pvlan-community + private_vlan: + type: community + - vlan_id: 70 + name: pvlan-primary + private_vlan: + type: primary + associated: + - 50 + - 60 + + state: replaced + +# After state: +# ------------ +# +# vios_l2#sh vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/0, Gi0/1, Gi0/2, Gi0/3 +# 10 Vlan_10 active +# 20 Test_VLAN20 active +# 50 pvlan-isolated active +# 60 pvlan-community active +# 70 pvlan-primary active +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1000 - - - - - 0 0 +# 20 enet 100020 700 - - - - - 0 0 +# 50 enet 100050 1500 - - - - - 0 0 +# 60 enet 100051 1500 - - - - - 0 0 +# 70 enet 100059 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# +# +# Primary Secondary Type Ports +# ------- --------- ----------------- ------------------------------------------ +# 70 50 isolated +# 70 60 community + +# Using deleted + +# Before state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 610 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +- name: Delete attributes of given VLANs + cisco.ios.ios_vlans: + config: + - vlan_id: 10 + - vlan_id: 20 + state: deleted + +# After state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 + +# Using Deleted without any config passed +#"(NOTE: This will delete all of configured vlans attributes)" + +# Before state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 610 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +- name: Delete attributes of ALL VLANs + cisco.ios.ios_vlans: + state: deleted + +# After state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 + +# Using Gathered + +# Before state: +# ------------- +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 610 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +- name: Gather listed vlans with provided configurations + cisco.ios.ios_vlans: + config: + state: gathered + +# Module Execution Result: +# ------------------------ +# +# "gathered": [ +# { +# "mtu": 1500, +# "name": "default", +# "shutdown": "disabled", +# "state": "active", +# "vlan_id": 1 +# }, +# { +# "mtu": 1500, +# "name": "VLAN0010", +# "shutdown": "disabled", +# "state": "active", +# "vlan_id": 10 +# }, +# { +# "mtu": 1500, +# "name": "VLAN0020", +# "shutdown": "disabled", +# "state": "active", +# "vlan_id": 20 +# }, +# { +# "mtu": 1500, +# "name": "VLAN0030", +# "shutdown": "disabled", +# "state": "active", +# "vlan_id": 30 +# }, +# { +# "mtu": 1500, +# "name": "fddi-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1002 +# }, +# { +# "mtu": 1500, +# "name": "token-ring-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1003 +# }, +# { +# "mtu": 1500, +# "name": "fddinet-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1004 +# }, +# { +# "mtu": 1500, +# "name": "trnet-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1005 +# } +# ] + +# After state: +# ------------ +# +# vios_l2#show vlan +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 610 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 +# +# Remote SPAN VLANs +# ------------------------------------------------------------------------------ +# 10 + +# Using Rendered + +- name: Render the commands for provided configuration + cisco.ios.ios_vlans: + config: + - name: Vlan_10 + vlan_id: 10 + state: active + shutdown: disabled + remote_span: true + - name: Vlan_20 + vlan_id: 20 + mtu: 610 + state: active + shutdown: enabled + - name: Vlan_30 + vlan_id: 30 + state: suspend + shutdown: enabled + state: rendered + +# Module Execution Result: +# ------------------------ +# +# "rendered": [ +# "vlan 10", +# "name Vlan_10", +# "state active", +# "remote-span", +# "no shutdown", +# "vlan 20", +# "name Vlan_20", +# "state active", +# "mtu 610", +# "shutdown", +# "vlan 30", +# "name Vlan_30", +# "state suspend", +# "shutdown" +# ] + +# Using Parsed + +# File: parsed.cfg +# ---------------- +# +# VLAN Name Status Ports +# ---- -------------------------------- --------- ------------------------------- +# 1 default active Gi0/1, Gi0/2 +# 10 vlan_10 active +# 20 vlan_20 act/lshut +# 30 vlan_30 sus/lshut +# 1002 fddi-default act/unsup +# 1003 token-ring-default act/unsup +# 1004 fddinet-default act/unsup +# 1005 trnet-default act/unsup +# +# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2 +# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------ +# 1 enet 100001 1500 - - - - - 0 0 +# 10 enet 100010 1500 - - - - - 0 0 +# 20 enet 100020 1500 - - - - - 0 0 +# 30 enet 100030 1500 - - - - - 0 0 +# 1002 fddi 101002 1500 - - - - - 0 0 +# 1003 tr 101003 1500 - - - - - 0 0 +# 1004 fdnet 101004 1500 - - - ieee - 0 0 +# 1005 trnet 101005 1500 - - - ibm - 0 0 + +- name: Parse the commands for provided configuration + cisco.ios.ios_vlans: + running_config: "{{ lookup('file', './parsed.cfg') }}" + state: parsed + +# Module Execution Result: +# ------------------------ +# +# "parsed": [ +# { +# "mtu": 1500, +# "name": "default", +# "shutdown": "disabled", +# "state": "active", +# "vlan_id": 1 +# }, +# { +# "mtu": 1500, +# "name": "vlan_10", +# "shutdown": "disabled", +# "state": "active", +# "vlan_id": 10 +# }, +# { +# "mtu": 1500, +# "name": "vlan_20", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 20 +# }, +# { +# "mtu": 1500, +# "name": "vlan_30", +# "shutdown": "enabled", +# "state": "suspend", +# "vlan_id": 30 +# }, +# { +# "mtu": 1500, +# "name": "fddi-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1002 +# }, +# { +# "mtu": 1500, +# "name": "token-ring-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1003 +# }, +# { +# "mtu": 1500, +# "name": "fddinet-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1004 +# }, +# { +# "mtu": 1500, +# "name": "trnet-default", +# "shutdown": "enabled", +# "state": "active", +# "vlan_id": 1005 +# } +# ] +""" + +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: ['vlan 20', 'name vlan_20', 'mtu 600', 'remote-span'] +""" +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vlans.vlans import ( + VlansArgs, +) +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.vlans.vlans import Vlans +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import get_connection + + +def _is_l2_device(module): + """fails module if device is L3.""" + connection = get_connection(module) + check_os_type = connection.get_device_info() + if check_os_type.get("network_os_type") == "L3": + return False + return True + + +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=VlansArgs.argument_spec, + required_if=required_if, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + + if _is_l2_device(module) or module.params.get("state") in ["rendered", "parsed"]: + result = Vlans(module).execute_module() + module.exit_json(**result) + else: + module.fail_json("""Resource VLAN is not valid for the target device.""") + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/modules/ios_vrf.py b/ansible_collections/cisco/ios/plugins/modules/ios_vrf.py new file mode 100644 index 000000000..4165bcae5 --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/modules/ios_vrf.py @@ -0,0 +1,741 @@ +#!/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: ios_vrf +author: Peter Sprygada (@privateip) +short_description: Module to configure VRF definitions. +description: + - This module provides declarative management of VRF definitions on Cisco IOS devices. It + allows playbooks to manage individual or the entire VRF collection. It also supports + purging VRF definitions from the configuration that are not explicitly defined. +version_added: 1.0.0 +extends_documentation_fragment: + - cisco.ios.ios +notes: + - Tested against Cisco IOSXE Version 17.3 on CML. + - This module works with connection C(network_cli). + See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) +options: + vrfs: + description: + - The set of VRF definition objects to be configured on the remote IOS device. Ths + list entries can either be the VRF name or a hash of VRF definitions and attributes. This + argument is mutually exclusive with the C(name) argument. + type: list + elements: raw + name: + description: + - The name of the VRF definition to be managed on the remote IOS device. The + VRF definition name is an ASCII string name used to uniquely identify the VRF. This + argument is mutually exclusive with the C(vrfs) argument + type: str + description: + description: + - Provides a short description of the VRF definition in the current active configuration. The + VRF definition value accepts alphanumeric characters used to provide additional + information about the VRF. + type: str + rd: + description: + - The router-distinguisher value uniquely identifies the VRF to routing processes + on the remote IOS system. The RD value takes the form of C(A:B) where C(A) + and C(B) are both numeric values. + type: str + interfaces: + description: + - Identifies the set of interfaces that should be configured in the VRF. Interfaces + must be routed interfaces in order to be placed into a VRF. + type: list + elements: str + associated_interfaces: + description: + - This is a intent option and checks the operational state of the for given vrf + C(name) for associated interfaces. If the value in the C(associated_interfaces) + does not match with the operational state of vrf interfaces on device it will + result in failure. + type: list + elements: str + delay: + description: + - Time in seconds to wait before checking for the operational state on remote + device. + default: 10 + type: int + purge: + description: + - Instructs the module to consider the VRF definition absolute. It will remove + any previously configured VRFs on the device. + default: false + type: bool + state: + description: + - Configures the state of the VRF definition as it relates to the device operational + configuration. When set to I(present), the VRF should be configured in the + device active configuration and when set to I(absent) the VRF should not be + in the device active configuration + default: present + choices: + - present + - absent + type: str + route_both: + description: + - Adds an export and import list of extended route target communities to the VRF. + type: list + elements: str + route_export: + description: + - Adds an export list of extended route target communities to the VRF. + type: list + elements: str + route_import: + description: + - Adds an import list of extended route target communities to the VRF. + type: list + elements: str + route_both_ipv4: + description: + - Adds an export and import list of extended route target communities in address-family + configuration submode to the VRF. + type: list + elements: str + route_export_ipv4: + description: + - Adds an export list of extended route target communities in address-family configuration + submode to the VRF. + type: list + elements: str + route_import_ipv4: + description: + - Adds an import list of extended route target communities in address-family configuration + submode to the VRF. + type: list + elements: str + route_both_ipv6: + description: + - Adds an export and import list of extended route target communities in address-family + configuration submode to the VRF. + type: list + elements: str + route_export_ipv6: + description: + - Adds an export list of extended route target communities in address-family configuration + submode to the VRF. + type: list + elements: str + route_import_ipv6: + description: + - Adds an import list of extended route target communities in address-family configuration + submode to the VRF. + type: list + elements: str +""" + +EXAMPLES = """ +- name: Configure a vrf named management + cisco.ios.ios_vrf: + name: management + description: oob mgmt vrf + interfaces: + - Management1 + +- name: Remove a vrf named test + cisco.ios.ios_vrf: + name: test + state: absent + +- name: Configure set of VRFs and purge any others + cisco.ios.ios_vrf: + vrfs: + - red + - blue + - green + purge: true + +- name: Creates a list of import RTs for the VRF with the same parameters + cisco.ios.ios_vrf: + name: test_import + rd: 1:100 + route_import: + - 1:100 + - 3:100 + +- name: + Creates a list of import RTs in address-family configuration submode for the + VRF with the same parameters + cisco.ios.ios_vrf: + name: test_import_ipv4 + rd: 1:100 + route_import_ipv4: + - 1:100 + - 3:100 + +- name: + Creates a list of import RTs in address-family configuration submode for the + VRF with the same parameters + cisco.ios.ios_vrf: + name: test_import_ipv6 + rd: 1:100 + route_import_ipv6: + - 1:100 + - 3:100 + +- name: Creates a list of export RTs for the VRF with the same parameters + cisco.ios.ios_vrf: + name: test_export + rd: 1:100 + route_export: + - 1:100 + - 3:100 + +- name: + Creates a list of export RTs in address-family configuration submode for the + VRF with the same parameters + cisco.ios.ios_vrf: + name: test_export_ipv4 + rd: 1:100 + route_export_ipv4: + - 1:100 + - 3:100 + +- name: + Creates a list of export RTs in address-family configuration submode for the + VRF with the same parameters + cisco.ios.ios_vrf: + name: test_export_ipv6 + rd: 1:100 + route_export_ipv6: + - 1:100 + - 3:100 + +- name: + Creates a list of import and export route targets for the VRF with the same + parameters + cisco.ios.ios_vrf: + name: test_both + rd: 1:100 + route_both: + - 1:100 + - 3:100 + +- name: + Creates a list of import and export route targets in address-family configuration + submode for the VRF with the same parameters + cisco.ios.ios_vrf: + name: test_both_ipv4 + rd: 1:100 + route_both_ipv4: + - 1:100 + - 3:100 + +- name: + Creates a list of import and export route targets in address-family configuration + submode for the VRF with the same parameters + cisco.ios.ios_vrf: + name: test_both_ipv6 + rd: 1:100 + route_both_ipv6: + - 1:100 + - 3:100 + +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - vrf definition ansible + - description management vrf + - rd: 1:100 +start: + description: The time the job started + returned: always + type: str + sample: "2016-11-16 10:38:15.126146" +end: + description: The time the job ended + returned: always + type: str + sample: "2016-11-16 10:38:25.595612" +delta: + description: The time elapsed to perform all operations + returned: always + type: str + sample: "0:00:10.469466" +""" +import re +import time + +from functools import partial + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import exec_command +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( + NetworkConfig, +) + +from ansible_collections.cisco.ios.plugins.module_utils.network.ios.ios import ( + get_config, + load_config, +) + + +def get_interface_type(interface): + if interface.upper().startswith("ET"): + return "ethernet" + elif interface.upper().startswith("VL"): + return "svi" + elif interface.upper().startswith("LO"): + return "loopback" + elif interface.upper().startswith("MG"): + return "management" + elif interface.upper().startswith("MA"): + return "management" + elif interface.upper().startswith("PO"): + return "portchannel" + elif interface.upper().startswith("NV"): + return "nve" + else: + return "unknown" + + +def add_command_to_vrf(name, cmd, commands): + if "vrf definition %s" % name not in commands: + commands.extend(["vrf definition %s" % name]) + commands.append(cmd) + + +def map_obj_to_commands(updates, module): + commands = list() + for update in updates: + want, have = update + + def needs_update(want, have, x): + if isinstance(want.get(x), list) and isinstance(have.get(x), list): + return ( + want.get(x) + and want.get(x) != have.get(x) + and not all(elem in have.get(x) for elem in want.get(x)) + ) + return want.get(x) and want.get(x) != have.get(x) + + if want["state"] == "absent": + commands.append("no vrf definition %s" % want["name"]) + continue + if not have.get("state"): + commands.extend(["vrf definition %s" % want["name"]]) + ipv6 = ( + len( + [ + k + for k, v in module.params.items() + if (k.endswith("_ipv6") or k.endswith("_both")) and v + ], + ) + != 0 + ) + ipv4 = ( + len( + [ + k + for k, v in module.params.items() + if (k.endswith("_ipv4") or k.endswith("_both")) and v + ], + ) + != 0 + ) + if ipv4: + commands.extend(["address-family ipv4", "exit"]) + if ipv6: + commands.extend(["address-family ipv6", "exit"]) + if needs_update(want, have, "description"): + cmd = "description %s" % want["description"] + add_command_to_vrf(want["name"], cmd, commands) + if needs_update(want, have, "rd"): + cmd = "rd %s" % want["rd"] + add_command_to_vrf(want["name"], cmd, commands) + if needs_update(want, have, "route_import"): + for route in want["route_import"]: + cmd = "route-target import %s" % route + add_command_to_vrf(want["name"], cmd, commands) + if needs_update(want, have, "route_export"): + for route in want["route_export"]: + cmd = "route-target export %s" % route + add_command_to_vrf(want["name"], cmd, commands) + if needs_update(want, have, "route_import_ipv4"): + cmd = "address-family ipv4" + add_command_to_vrf(want["name"], cmd, commands) + for route in want["route_import_ipv4"]: + cmd = "route-target import %s" % route + add_command_to_vrf(want["name"], cmd, commands) + cmd = "exit-address-family" + add_command_to_vrf(want["name"], cmd, commands) + if needs_update(want, have, "route_export_ipv4"): + cmd = "address-family ipv4" + add_command_to_vrf(want["name"], cmd, commands) + for route in want["route_export_ipv4"]: + cmd = "route-target export %s" % route + add_command_to_vrf(want["name"], cmd, commands) + cmd = "exit-address-family" + add_command_to_vrf(want["name"], cmd, commands) + if needs_update(want, have, "route_import_ipv6"): + cmd = "address-family ipv6" + add_command_to_vrf(want["name"], cmd, commands) + for route in want["route_import_ipv6"]: + cmd = "route-target import %s" % route + add_command_to_vrf(want["name"], cmd, commands) + cmd = "exit-address-family" + add_command_to_vrf(want["name"], cmd, commands) + if needs_update(want, have, "route_export_ipv6"): + cmd = "address-family ipv6" + add_command_to_vrf(want["name"], cmd, commands) + for route in want["route_export_ipv6"]: + cmd = "route-target export %s" % route + add_command_to_vrf(want["name"], cmd, commands) + cmd = "exit-address-family" + add_command_to_vrf(want["name"], cmd, commands) + if want["interfaces"] is not None: + for intf in set(have.get("interfaces", [])).difference(want["interfaces"]): + commands.extend(["interface %s" % intf, "no vrf forwarding %s" % want["name"]]) + for intf in set(want["interfaces"]).difference(have.get("interfaces", [])): + cfg = get_config(module) + configobj = NetworkConfig(indent=1, contents=cfg) + children = configobj["interface %s" % intf].children + intf_config = "\n".join(children) + commands.extend(["interface %s" % intf, "vrf forwarding %s" % want["name"]]) + match = re.search("ip address .+", intf_config, re.M) + if match: + commands.append(match.group()) + return commands + + +def parse_description(configobj, name): + cfg = configobj["vrf definition %s" % name] + cfg = "\n".join(cfg.children) + match = re.search("description (.+)$", cfg, re.M) + if match: + return match.group(1) + + +def parse_rd(configobj, name): + cfg = configobj["vrf definition %s" % name] + cfg = "\n".join(cfg.children) + match = re.search("rd (.+)$", cfg, re.M) + if match: + return match.group(1) + + +def parse_interfaces(configobj): + vrf_cfg = "vrf forwarding" + interfaces = dict() + for intf in set(re.findall("^interface .+", str(configobj), re.M)): + for line in configobj[intf].children: + if vrf_cfg in line: + try: + interfaces[line.split()[-1]].append(intf.split(" ")[1]) + except KeyError: + interfaces[line.split()[-1]] = [intf.split(" ")[1]] + return interfaces + + +def parse_import(configobj, name): + cfg = configobj["vrf definition %s" % name] + cfg = "\n".join(cfg.children) + matches = re.findall("route-target\\s+import\\s+(.+)", cfg, re.M) + return matches + + +def parse_export(configobj, name): + cfg = configobj["vrf definition %s" % name] + cfg = "\n".join(cfg.children) + matches = re.findall("route-target\\s+export\\s+(.+)", cfg, re.M) + return matches + + +def parse_both(configobj, name, address_family="global"): + rd_pattern = re.compile("(?P<rd>.+:.+)") + matches = list() + export_match = None + import_match = None + if address_family == "global": + export_match = parse_export(configobj, name) + import_match = parse_import(configobj, name) + elif address_family == "ipv4": + export_match = parse_export_ipv4(configobj, name) + import_match = parse_import_ipv4(configobj, name) + elif address_family == "ipv6": + export_match = parse_export_ipv6(configobj, name) + import_match = parse_import_ipv6(configobj, name) + if import_match and export_match: + for ex in export_match: + exrd = rd_pattern.search(ex) + exrd = exrd.groupdict().get("rd") + for im in import_match: + imrd = rd_pattern.search(im) + imrd = imrd.groupdict().get("rd") + if exrd == imrd: + matches.extend([exrd]) if exrd not in matches else None + matches.extend([imrd]) if imrd not in matches else None + return matches + + +def parse_import_ipv4(configobj, name): + cfg = configobj["vrf definition %s" % name] + try: + subcfg = cfg["address-family ipv4"] + subcfg = "\n".join(subcfg.children) + matches = re.findall("route-target\\s+import\\s+(.+)", subcfg, re.M) + return matches + except KeyError: + pass + + +def parse_export_ipv4(configobj, name): + cfg = configobj["vrf definition %s" % name] + try: + subcfg = cfg["address-family ipv4"] + subcfg = "\n".join(subcfg.children) + matches = re.findall("route-target\\s+export\\s+(.+)", subcfg, re.M) + return matches + except KeyError: + pass + + +def parse_import_ipv6(configobj, name): + cfg = configobj["vrf definition %s" % name] + try: + subcfg = cfg["address-family ipv6"] + subcfg = "\n".join(subcfg.children) + matches = re.findall("route-target\\s+import\\s+(.+)", subcfg, re.M) + return matches + except KeyError: + pass + + +def parse_export_ipv6(configobj, name): + cfg = configobj["vrf definition %s" % name] + try: + subcfg = cfg["address-family ipv6"] + subcfg = "\n".join(subcfg.children) + matches = re.findall("route-target\\s+export\\s+(.+)", subcfg, re.M) + return matches + except KeyError: + pass + + +def map_config_to_obj(module): + config = get_config(module) + configobj = NetworkConfig(indent=1, contents=config) + match = re.findall("^vrf definition (\\S+)", config, re.M) + if not match: + return list() + instances = list() + interfaces = parse_interfaces(configobj) + for item in set(match): + obj = { + "name": item, + "state": "present", + "description": parse_description(configobj, item), + "rd": parse_rd(configobj, item), + "interfaces": interfaces.get(item, []), + "route_import": parse_import(configobj, item), + "route_export": parse_export(configobj, item), + "route_both": parse_both(configobj, item), + "route_import_ipv4": parse_import_ipv4(configobj, item), + "route_export_ipv4": parse_export_ipv4(configobj, item), + "route_both_ipv4": parse_both(configobj, item, address_family="ipv4"), + "route_import_ipv6": parse_import_ipv6(configobj, item), + "route_export_ipv6": parse_export_ipv6(configobj, item), + "route_both_ipv6": parse_both(configobj, item, address_family="ipv6"), + } + instances.append(obj) + return instances + + +def get_param_value(key, item, module): + # if key doesn't exist in the item, get it from module.params + if not item.get(key): + value = module.params[key] + # if key does exist, do a type check on it to validate it + else: + value_type = module.argument_spec[key].get("type", "str") + type_checker = module._CHECK_ARGUMENT_TYPES_DISPATCHER[value_type] + type_checker(item[key]) + value = item[key] + # validate the param value (if validator func exists) + validator = globals().get("validate_%s" % key) + if validator: + validator(value, module) + return value + + +def map_params_to_obj(module): + vrfs = module.params.get("vrfs") + if not vrfs: + if not module.params["name"] and module.params["purge"]: + return list() + elif not module.params["name"]: + module.fail_json(msg="name is required") + collection = [{"name": module.params["name"]}] + else: + collection = list() + for item in vrfs: + if not isinstance(item, dict): + collection.append({"name": item}) + elif "name" not in item: + module.fail_json(msg="name is required") + else: + collection.append(item) + objects = list() + for item in collection: + get_value = partial(get_param_value, item=item, module=module) + item["description"] = get_value("description") + item["rd"] = get_value("rd") + item["interfaces"] = get_value("interfaces") + item["state"] = get_value("state") + item["route_import"] = get_value("route_import") + item["route_export"] = get_value("route_export") + item["route_both"] = get_value("route_both") + item["route_import_ipv4"] = get_value("route_import_ipv4") + item["route_export_ipv4"] = get_value("route_export_ipv4") + item["route_both_ipv4"] = get_value("route_both_ipv4") + item["route_import_ipv6"] = get_value("route_import_ipv6") + item["route_export_ipv6"] = get_value("route_export_ipv6") + item["route_both_ipv6"] = get_value("route_both_ipv6") + both_addresses_family = ["", "_ipv6", "_ipv4"] + for address_family in both_addresses_family: + if item["route_both%s" % address_family]: + if not item["route_export%s" % address_family]: + item["route_export%s" % address_family] = list() + if not item["route_import%s" % address_family]: + item["route_import%s" % address_family] = list() + item["route_export%s" % address_family].extend( + get_value("route_both%s" % address_family), + ) + item["route_import%s" % address_family].extend( + get_value("route_both%s" % address_family), + ) + item["associated_interfaces"] = get_value("associated_interfaces") + objects.append(item) + return objects + + +def update_objects(want, have): + updates = list() + for entry in want: + item = next((i for i in have if i["name"] == entry["name"]), None) + if all((item is None, entry["state"] == "present")): + updates.append((entry, {})) + else: + for key, value in iteritems(entry): + if value: + try: + if isinstance(value, list): + if sorted(value) != sorted(item[key]): + if (entry, item) not in updates: + updates.append((entry, item)) + elif value != item[key]: + if (entry, item) not in updates: + updates.append((entry, item)) + except TypeError: + pass + return updates + + +def check_declarative_intent_params(want, module, result): + if module.params["associated_interfaces"]: + if result["changed"]: + time.sleep(module.params["delay"]) + name = module.params["name"] + rc, out, err = exec_command(module, "show vrf | include {0}".format(name)) + if rc == 0: + data = out.strip().split() + if not data: + return + vrf = data[0] + interface = data[-1] + for w in want: + if w["name"] == vrf: + if w.get("associated_interfaces") is None: + continue + for i in w["associated_interfaces"]: + if get_interface_type(i) is not get_interface_type(interface): + module.fail_json( + msg="Interface %s not configured on vrf %s" % (interface, name), + ) + + +def main(): + """main entry point for module execution""" + argument_spec = dict( + vrfs=dict(type="list", elements="raw"), + name=dict(), + description=dict(), + rd=dict(), + route_export=dict(type="list", elements="str"), + route_import=dict(type="list", elements="str"), + route_both=dict(type="list", elements="str"), + route_export_ipv4=dict(type="list", elements="str"), + route_import_ipv4=dict(type="list", elements="str"), + route_both_ipv4=dict(type="list", elements="str"), + route_export_ipv6=dict(type="list", elements="str"), + route_import_ipv6=dict(type="list", elements="str"), + route_both_ipv6=dict(type="list", elements="str"), + interfaces=dict(type="list", elements="str"), + associated_interfaces=dict(type="list", elements="str"), + delay=dict(default=10, type="int"), + purge=dict(type="bool", default=False), + state=dict(default="present", choices=["present", "absent"]), + ) + mutually_exclusive = [("name", "vrfs")] + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + result = {"changed": False} + warnings = list() + result["warnings"] = warnings + want = map_params_to_obj(module) + have = map_config_to_obj(module) + commands = map_obj_to_commands(update_objects(want, have), module) + if module.params["purge"]: + want_vrfs = [x["name"] for x in want] + have_vrfs = [x["name"] for x in have] + for item in set(have_vrfs).difference(want_vrfs): + cmd = "no vrf definition %s" % item + if cmd not in commands: + commands.append(cmd) + result["commands"] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result["changed"] = True + check_declarative_intent_params(want, module, result) + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cisco/ios/plugins/plugin_utils/__init__.py b/ansible_collections/cisco/ios/plugins/plugin_utils/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/plugin_utils/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/terminal/__init__.py b/ansible_collections/cisco/ios/plugins/terminal/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/terminal/__init__.py diff --git a/ansible_collections/cisco/ios/plugins/terminal/ios.py b/ansible_collections/cisco/ios/plugins/terminal/ios.py new file mode 100644 index 000000000..7ba3491ef --- /dev/null +++ b/ansible_collections/cisco/ios/plugins/terminal/ios.py @@ -0,0 +1,144 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import json +import re + +from ansible.errors import AnsibleConnectionFailure +from ansible.module_utils._text import to_bytes, to_text +from ansible.utils.display import Display +from ansible_collections.ansible.netcommon.plugins.plugin_utils.terminal_base import TerminalBase + + +display = Display() + + +class TerminalModule(TerminalBase): + terminal_stdout_re = [re.compile(rb"[\r\n]?[\w\+\-\.:\/\[\]]+(?:\([^\)]+\)){0,3}(?:[>#]) ?$")] + + privilege_level_re = re.compile(r"Current privilege level is (\d+)$") + + terminal_stderr_re = [ + re.compile(rb"% ?Error"), + # re.compile(rb"^% \w+", re.M), + re.compile(rb"% ?Bad secret"), + re.compile(rb"[\r\n%] Bad passwords"), + re.compile(rb"invalid input", re.I), + re.compile(rb"(?:incomplete|ambiguous) command", re.I), + re.compile(rb"connection timed out", re.I), + re.compile(rb"[^\r\n]+ not found"), + re.compile(rb"'[^']' +returned error code: ?\d+"), + re.compile(rb"Bad mask", re.I), + re.compile(rb"% ?(\S+) ?overlaps with ?(\S+)", re.I), + re.compile(rb"[%\S] ?Error: ?[\s]+", re.I), + re.compile(rb"[%\S] ?Informational: ?[\s]+", re.I), + re.compile(rb"Command authorization failed"), + re.compile(rb"Command Rejected: ?[\s]+", re.I), + re.compile(rb"% General session commands not allowed under the address family", re.I), + re.compile(rb"% BGP: Error initializing topology", re.I), + ] + + terminal_config_prompt = re.compile(r"^.+\(config(-.*)?\)#$") + + def get_privilege_level(self): + try: + cmd = {"command": "show privilege"} + result = self._exec_cli_command(to_bytes(json.dumps(cmd), errors="surrogate_or_strict")) + except AnsibleConnectionFailure as e: + raise AnsibleConnectionFailure( + "unable to fetch privilege, with error: %s" % (e.message), + ) + + prompt = self.privilege_level_re.search(result) + if not prompt: + raise AnsibleConnectionFailure("unable to check privilege level [%s]" % result) + + return int(prompt.group(1)) + + def on_open_shell(self): + _is_sdWan = False # initialize to false for default IOS execution + try: + self._exec_cli_command(b"terminal length 0") + except AnsibleConnectionFailure: + try: + self._exec_cli_command(b"screen-length 0") # support to SD-WAN mode + _is_sdWan = True + except AnsibleConnectionFailure: # fails as length required for handling prompt + raise AnsibleConnectionFailure("unable to set terminal parameters") + try: + if _is_sdWan: + self._exec_cli_command(b"screen-width 512") # support to SD-WAN mode + else: + self._exec_cli_command(b"terminal width 512") + try: + self._exec_cli_command(b"terminal width 0") + except AnsibleConnectionFailure: + pass + except AnsibleConnectionFailure: + display.display( + "WARNING: Unable to set terminal/screen width, command responses may be truncated", + ) + + def on_become(self, passwd=None): + if self._get_prompt().endswith(b"#") and self.get_privilege_level() == 15: + return + + cmd = {"command": "enable"} + if passwd: + # Note: python-3.5 cannot combine u"" and r"" together. Thus make + # an r string and use to_text to ensure it's text on both py2 and py3. + cmd["prompt"] = to_text(r"[\r\n]?(?:.*)?[Pp]assword: ?$", errors="surrogate_or_strict") + cmd["answer"] = passwd + cmd["prompt_retry_check"] = True + try: + self._exec_cli_command(to_bytes(json.dumps(cmd), errors="surrogate_or_strict")) + prompt = self._get_prompt() + privilege_level = self.get_privilege_level() + except AnsibleConnectionFailure as e: + prompt = self._get_prompt() + raise AnsibleConnectionFailure( + "failed to elevate privilege to enable mode, at prompt [%s] with error: %s" + % (prompt, e.message), + ) + + if prompt is None or not prompt.endswith(b"#") or privilege_level != 15: + raise AnsibleConnectionFailure( + "failed to elevate privilege to enable mode, still at level [%d] and prompt [%s]" + % (privilege_level, prompt), + ) + + def on_unbecome(self): + prompt = self._get_prompt() + if prompt is None: + # if prompt is None most likely the terminal is hung up at a prompt + return + + if self.get_privilege_level() != 15: + return + + if b"(config" in prompt: + self._exec_cli_command(b"end") + self._exec_cli_command(b"disable") + + elif prompt.endswith(b"#"): + self._exec_cli_command(b"disable") |