From 2806339f0d1a66056c23d09fb78367ce8c4da026 Mon Sep 17 00:00:00 2001 From: Johan Marcusson Date: Mon, 21 Oct 2024 15:37:25 +0200 Subject: [PATCH] get running configed based on devicetypes in DEVICE_TYPES_WITH_INCLUDE_RUNNING_CONFIG, to be used together with jinja filter like: {{ running_config|get_config_section(firewall, junos) }} --- src/cnaas_nms/app_settings.py | 4 +- src/cnaas_nms/devicehandler/sync_devices.py | 17 ++++++- src/cnaas_nms/tools/jinja_filters.py | 51 +++++++++++++++------ 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/cnaas_nms/app_settings.py b/src/cnaas_nms/app_settings.py index ce64b238..6bd9f1af 100644 --- a/src/cnaas_nms/app_settings.py +++ b/src/cnaas_nms/app_settings.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Optional +from typing import List, Optional import yaml from pydantic import field_validator @@ -57,6 +57,7 @@ class ApiSettings(BaseSettings): COMMIT_CONFIRMED_TIMEOUT: int = 300 COMMIT_CONFIRMED_WAIT: int = 1 SETTINGS_OVERRIDE: Optional[dict] = None + DEVICE_TYPES_WITH_INCLUDE_RUNNING_CONFIG: List[str] = [] @field_validator("MGMTDOMAIN_PRIMARY_IP_VERSION") @classmethod @@ -118,6 +119,7 @@ def construct_api_settings() -> ApiSettings: COMMIT_CONFIRMED_TIMEOUT=config.get("commit_confirmed_timeout", 300), COMMIT_CONFIRMED_WAIT=config.get("commit_confirmed_wait", 1), SETTINGS_OVERRIDE=config.get("settings_override", None), + DEVICE_TYPES_WITH_INCLUDE_RUNNING_CONFIG=config.get("device_types_with_include_running_config", []), ) else: return ApiSettings() diff --git a/src/cnaas_nms/devicehandler/sync_devices.py b/src/cnaas_nms/devicehandler/sync_devices.py index 44e8764a..4b17fc7b 100644 --- a/src/cnaas_nms/devicehandler/sync_devices.py +++ b/src/cnaas_nms/devicehandler/sync_devices.py @@ -106,7 +106,7 @@ def get_mlag_vars(session, dev: Device) -> dict: def populate_device_vars( - session, dev: Device, ztp_hostname: Optional[str] = None, ztp_devtype: Optional[DeviceType] = None + task, session, dev: Device, ztp_hostname: Optional[str] = None, ztp_devtype: Optional[DeviceType] = None ): logger = get_logger() device_variables = { @@ -352,6 +352,19 @@ def populate_device_vars( ) device_variables = {**device_variables, **fabric_device_variables} + # if device type in api_settings.DEVICE_TYPES_WITH_INCLUDE_RUNNING_CONFIG + for dt_str in api_settings.DEVICE_TYPES_WITH_INCLUDE_RUNNING_CONFIG: + if dev.device_type.name.lower() == dt_str.lower(): + res = task.run(task=napalm_get, getters=["config"]) + + running_config = dict(res.result)["config"]["running"] + # Remove the first task result, which is the napalm_get result, since it's not needed anymore + del task.results[0] + if running_config is None: + raise Exception(f"Failed to get running configuration for {dev.hostname}") + + device_variables["running_config"] = running_config + # Add all environment variables starting with TEMPLATE_SECRET_ to # the list of configuration variables. The idea is to store secret # configuration outside of the templates repository. @@ -505,7 +518,7 @@ def push_sync_device( hostname = task.host.name with sqla_session() as session: dev: Device = session.query(Device).filter(Device.hostname == hostname).one() - template_vars = populate_device_vars(session, dev) + template_vars = populate_device_vars(task, session, dev) platform = dev.platform devtype = dev.device_type diff --git a/src/cnaas_nms/tools/jinja_filters.py b/src/cnaas_nms/tools/jinja_filters.py index e4e83f8d..469d7366 100644 --- a/src/cnaas_nms/tools/jinja_filters.py +++ b/src/cnaas_nms/tools/jinja_filters.py @@ -5,7 +5,15 @@ import ipaddress import re from typing import Any, Callable, Optional, Union -from netutils.config.parser import BaseConfigParser, JunosConfigParser + +from netutils.config.parser import ( + BaseConfigParser, + EOSConfigParser, + IOSConfigParser, + IOSXRConfigParser, + JunosConfigParser, + NXOSConfigParser, +) # This global dict can be used to update the Jinja environment filters dict to include all # registered template filter function @@ -214,28 +222,41 @@ def md5(s: str) -> str: @template_filter() -def get_config_block_regex(config:str, section: str = "firewall", parser: Optional[BaseConfigParser] = JunosConfigParser) -> str: +def get_config_section(config: str, section: str, parser: str) -> str: """ Get the configuration block for a specific section. - + Args: config (str): The config used to for parsing and search a specific section. - section (str, optional): The section to retrieve. Defaults to "firewall". Regex can be used as ^(firewall)\s*\{ - parser (str, optional): The parser corresponding to the config type. Defaults to "JunosConfigParser". - + section (str): The section to retrieve. Regex can be used as "^(firewall)\s*\{" + parser (str): The parser corresponding to the config type, e.g. junos, eos, nxos, iosxr, ios. + Returns: str: The text of the configuration block if found, empty string otherwise. - + test: - get_config_block_regex(config=firewall_config, section="firewall") - """ - - config_parser = parser(config) - config_relationship = config_parser.build_config_relationship() + get_config_section(config=firewall_config, section="firewall", parser="junos") + """ # noqa: W605 + if parser.lower() == "junos": + parser_obj = JunosConfigParser + elif parser.lower() == "eos": + parser_obj = EOSConfigParser + elif parser.lower() == "nxos": + parser_obj = NXOSConfigParser + elif parser.lower() == "iosxr": + parser_obj = IOSXRConfigParser + elif parser.lower() == "ios": + parser_obj = IOSConfigParser + else: + parser_obj = BaseConfigParser + config_parser = parser_obj(config) + config_parser.build_config_relationship() children = config_parser.find_all_children(section, match_type="regex") - + + if len(children) == 1: + return children[0] if len(children) > 1: collect = "\n".join(children) return collect + "\n}" if isinstance(config_parser, JunosConfigParser) else collect - - return [] + + return ""