"""ntc_templates.parse.""" import os # Due to TextFSM library issues on Windows, it is better to not fail on import # Instead fail at runtime (i.e. if method is actually used). try: from textfsm import clitable HAS_CLITABLE = True except ImportError: HAS_CLITABLE = False class ParsingException(Exception): """Error that is raised when TextFSM hits an `Error` state.""" def _get_template_dir(): template_dir = os.environ.get("NTC_TEMPLATES_DIR") if template_dir is None: package_dir = os.path.dirname(__file__) template_dir = os.path.join(package_dir, "templates") if not os.path.isdir(template_dir): project_dir = os.path.dirname(os.path.dirname(os.path.dirname(template_dir))) template_dir = os.path.join(project_dir, "templates") return template_dir def _clitable_to_dict(cli_table): """Convert TextFSM cli_table object to list of dictionaries.""" objs = [] for row in cli_table: temp_dict = {} for index, element in enumerate(row): temp_dict[cli_table.header[index].lower()] = element objs.append(temp_dict) return objs def parse_output( platform=None, command=None, data=None, template_dir=None, try_fallback=False, ): """Return the structured data based on the output from a network device. Args: platform: The platform the command was run on (e.g., `cisco_ios`). command: The command run on the platform (e.g., `show int status`). data: The output from running the command. template_dir: The directory to look for TextFSM templates. Defaults to setting of environment variable or default ntc-templates dir. The specified directory must have a properly configured index file. try_fallback: Whether to fallback to using the default template directory upon failure with `template_dir`. Returns: list: The TextFSM table entries as dictionaries. """ if not HAS_CLITABLE: msg = ( "The TextFSM library is not currently supported on Windows. If you are NOT using Windows " "you should be able to 'pip install textfsm' to fix this issue. If you are using Windows " "then you will need to install the patch referenced here:\n\n" "https://github.com/google/textfsm/pull/82\n\n" ) raise ImportError(msg) template_dir = template_dir or _get_template_dir() cli_table = clitable.CliTable("index", template_dir) attrs = {"Command": command, "Platform": platform} try: cli_table.ParseCmd(data, attrs) structured_data = _clitable_to_dict(cli_table) except clitable.CliTableError as err: if try_fallback and template_dir != _get_template_dir(): return parse_output(platform, command, data) raise ParsingException(f'Unable to parse command "{command}" on platform {platform} - {str(err)}') from err return structured_data