def test_textfsm_direct_template(): """Convert raw CLI output to structured data using TextFSM template (no index).""" raw_output = "Cisco IOS Software, Catalyst 4500 L3 Switch Software" result = utilities.get_structured_data( raw_output, platform="cisco_ios", command="show version", template=f"{RESOURCE_FOLDER}/cisco_ios_show_version.template", ) assert result == [{"model": "4500"}] # Should also work with no-platform or command result = utilities.get_structured_data( raw_output, template=f"{RESOURCE_FOLDER}/cisco_ios_show_version.template") assert result == [{"model": "4500"}]
def test_get_structured_data(): """Convert raw CLI output to structured data using TextFSM template""" environ["NET_TEXTFSM"] = RESOURCE_FOLDER raw_output = "Cisco IOS Software, Catalyst 4500 L3 Switch Software" result = utilities.get_structured_data( raw_output, platform="cisco_ios", command="show version" ) assert result == [{"model": "4500"}]
def test_textfsm_index_relative_path(): """Test relative path for textfsm ntc directory""" os.environ["NET_TEXTFSM"] = RELATIVE_RESOURCE_FOLDER raw_output = "Cisco IOS Software, Catalyst 4500 L3 Switch Software" result = utilities.get_structured_data(raw_output, platform="cisco_ios", command="show version") assert result == [{"model": "4500"}]
def test_textfsm_w_index(): """Convert raw CLI output to structured data using TextFSM template""" os.environ["NET_TEXTFSM"] = RESOURCE_FOLDER raw_output = "Cisco IOS Software, Catalyst 4500 L3 Switch Software" result = utilities.get_structured_data(raw_output, platform="cisco_ios", command="show version") assert result == [{"model": "4500"}]
def test_textfsm_missing_template(): """Verify raw_output is returned if TextFSM template is missing.""" raw_output = "Cisco IOS Software, Catalyst 4500 L3 Switch Software" result = utilities.get_structured_data( raw_output, platform="cisco_ios", command="show version", template=f"{RESOURCE_FOLDER}/nothinghere", ) assert result == raw_output
def test_textfsm_failed_parsing(): """Verify raw_output is returned if TextFSM template parsing fails.""" raw_output = "This is not 'show version' output" result = utilities.get_structured_data( raw_output, platform="cisco_ios", command="show version", template=f"{RESOURCE_FOLDER}/nothinghere", ) assert result == raw_output
def send_command_timing(self, command_string, delay_factor=1, max_loops=150, strip_prompt=True, strip_command=True, normalize=True, use_textfsm=False): """Execute command_string on the SSH channel using a delay-based mechanism. Generally used for show commands. :param command_string: The command to be executed on the remote device. :type command_string: str :param delay_factor: Multiplying factor used to adjust delays (default: 1). :type delay_factor: int or float :param max_loops: Controls wait time in conjunction with delay_factor. Will default to be based upon self.timeout. :type max_loops: int :param strip_prompt: Remove the trailing router prompt from the output (default: True). :type strip_prompt: bool :param strip_command: Remove the echo of the command from the output (default: True). :type strip_command: bool :param normalize: Ensure the proper enter is sent at end of command (default: True). :type normalize: bool :param use_textfsm: Process command output through TextFSM template (default: False). :type normalize: bool """ output = '' delay_factor = self.select_delay_factor(delay_factor) self.clear_buffer() if normalize: command_string = self.normalize_cmd(command_string) self.write_channel(command_string) output = self._read_channel_timing(delay_factor=delay_factor, max_loops=max_loops) output = self._sanitize_output(output, strip_command=strip_command, command_string=command_string, strip_prompt=strip_prompt) if use_textfsm: output = get_structured_data(output, platform=self.device_type, command=command_string.strip()) return output
def send_command(self, command_string, expect_string=None, delay_factor=1, max_loops=500, auto_find_prompt=True, strip_prompt=True, strip_command=True, normalize=True, use_textfsm=False): """Execute command_string on the SSH channel using a pattern-based mechanism. Generally used for show commands. By default this method will keep waiting to receive data until the network device prompt is detected. The current network device prompt will be determined automatically. :param command_string: The command to be executed on the remote device. :type command_string: str :param expect_string: Regular expression pattern to use for determining end of output. If left blank will default to being based on router prompt. :type expect_string: str :param delay_factor: Multiplying factor used to adjust delays (default: 1). :type delay_factor: int :param max_loops: Controls wait time in conjunction with delay_factor. Will default to be based upon self.timeout. :type max_loops: int :param strip_prompt: Remove the trailing router prompt from the output (default: True). :type strip_prompt: bool :param strip_command: Remove the echo of the command from the output (default: True). :type strip_command: bool :param normalize: Ensure the proper enter is sent at end of command (default: True). :type normalize: bool :param use_textfsm: Process command output through TextFSM template (default: False). :type normalize: bool """ # Time to delay in each read loop loop_delay = .2 # Default to making loop time be roughly equivalent to self.timeout (support old max_loops # and delay_factor arguments for backwards compatibility). delay_factor = self.select_delay_factor(delay_factor) if delay_factor == 1 and max_loops == 500: # Default arguments are being used; use self.timeout instead max_loops = int(self.timeout / loop_delay) # Find the current router prompt if expect_string is None: if auto_find_prompt: try: prompt = self.find_prompt(delay_factor=delay_factor) except ValueError: prompt = self.base_prompt else: prompt = self.base_prompt search_pattern = re.escape(prompt.strip()) else: search_pattern = expect_string if normalize: command_string = self.normalize_cmd(command_string) time.sleep(delay_factor * loop_delay) self.clear_buffer() self.write_channel(command_string) i = 1 output = '' # Keep reading data until search_pattern is found or until max_loops is reached. while i <= max_loops: new_data = self.read_channel() if new_data: output += new_data try: lines = output.split(self.RETURN) first_line = lines[0] # First line is the echo line containing the command. In certain situations # it gets repainted and needs filtered if BACKSPACE_CHAR in first_line: pattern = search_pattern + r'.*$' first_line = re.sub(pattern, repl='', string=first_line) lines[0] = first_line output = self.RETURN.join(lines) except IndexError: pass if re.search(search_pattern, output): break else: time.sleep(delay_factor * loop_delay) i += 1 else: # nobreak raise IOError("Search pattern never detected in send_command_expect: {}".format( search_pattern)) output = self._sanitize_output(output, strip_command=strip_command, command_string=command_string, strip_prompt=strip_prompt) if use_textfsm: output = get_structured_data(output, platform=self.device_type, command=command_string.strip()) return output