def test_response_parse_textfsm_no_template(): response = Response("localhost", channel_input="show ip arp", textfsm_platform="potato") response_bytes = b"" response._record_response(response_bytes) assert response.textfsm_parse_output() == []
def test_response_parse_ttp_fail(): response = Response("localhost", channel_input="show ip arp", genie_platform="iosxe") response_bytes = b"" response._record_response(response_bytes) assert response.ttp_parse_output(template="blah") == [{}]
def test_response_parse_genie_fail(): response = Response("localhost", channel_input="show ip arp", genie_platform="iosxe") response_bytes = b"" response._record_response(response_bytes) assert response.genie_parse_output() == []
def test_response_parse_ttp(): response = Response("localhost", channel_input="show ip arp", genie_platform="iosxe") # example data lifted straight out of ttp docs ttp_template = """ interface {{ interface }} ip address {{ ip }}/{{ mask }} description {{ description }} ip vrf {{ vrf }} """ response_bytes = b""" interface Loopback0 description Router-id-loopback ip address 192.168.0.113/24 ! interface Vlan778 description CPE_Acces_Vlan ip address 2002::fd37/124 ip vrf CPE1 ! """ response._record_response(response_bytes) result = response.ttp_parse_output(template=ttp_template) assert result[0][0]["ip"] == "192.168.0.113"
def test_response_parse_textfsm_fail(): response = Response("localhost", channel_input="show ip arp", textfsm_platform="cisco_ios") response_bytes = b"" response._record_response(response_bytes) assert response.textfsm_parse_output() == []
def test_post_send_configs(sync_cisco_iosxe_conn): response_one = Response("localhost", "some input", failed_when_contains=["something"]) response_one._record_response(result=b"greatsucccess") multi_response = MultiResponse() multi_response.append(response_one) updated_responses = sync_cisco_iosxe_conn._post_send_configs(responses=multi_response) assert updated_responses[0].textfsm_platform == "cisco_iosxe" assert updated_responses[0].genie_platform == "iosxe"
def mock_send_configs(cls, configs, strip_prompt): responses = [] response = Response(host="fake_as_heck", channel_input=configs[0]) response._record_response("some stuff about whatever") responses.append(response) response = Response(host="fake_as_heck", channel_input=configs[1]) response._record_response("some stuff about whatever") responses.append(response) return [response]
def test_response_parse_textfsm(parse_type): to_dict = parse_type[0] expected_result = parse_type[1] response = Response("localhost", channel_input="show ip arp", textfsm_platform="cisco_ios") response_str = """Protocol Address Age (min) Hardware Addr Type Interface Internet 172.31.254.1 - 0000.0c07.acfe ARPA Vlan254 Internet 172.31.254.2 - c800.84b2.e9c2 ARPA Vlan254 """ response._record_response(response_str) assert response.textfsm_parse_output(to_dict=to_dict)[0] == expected_result
def test_post_send_config_failed(sync_cisco_iosxe_conn): response_one = Response("localhost", "some input", failed_when_contains=["something"]) response_one._record_response(result=b"something") multi_response = MultiResponse() multi_response.append(response_one) updated_responses = sync_cisco_iosxe_conn._post_send_config( config="whocares", multi_response=multi_response ) assert updated_responses.textfsm_platform == "cisco_iosxe" assert updated_responses.genie_platform == "iosxe" assert updated_responses.failed is True
def test_response_parse_genie(): response = Response("localhost", channel_input="show ip arp", genie_platform="iosxe") response_str = """Protocol Address Age (min) Hardware Addr Type Interface Internet 172.31.254.1 - 0000.0c07.acfe ARPA Vlan254 Internet 172.31.254.2 - c800.84b2.e9c2 ARPA Vlan254 """ response._record_response(response_str) result = response.genie_parse_output() assert ( result["interfaces"]["Vlan254"]["ipv4"]["neighbors"]["172.31.254.1"]["ip"] == "172.31.254.1" )
def mock_send_interactive(cls, interact_events, failed_when_contains=None, privilege_level=""): response = Response( host="fake_as_heck", channel_input=", ".join([event[0] for event in interact_events]), ) response._record_response( b"clear logg\nClear logging buffer [confirm]\n\ncsr1000v#") return response
def mock_send_config( cls, config, strip_prompt, failed_when_contains="", stop_on_failed=False, privilege_level="", timeout_ops=None, ): response = Response(host="fake_as_heck", channel_input=config) response._record_response(b"some stuff about whatever") return response
def test_response_record_result(): response = Response("localhost", "ls -al") response_end_time = str(datetime.now())[:-7] response_bytes = b""" ls -al total 264 drwxr-xr-x 34 carl staff 1088 Jan 27 19:07 ./ drwxr-xr-x 21 carl staff 672 Jan 25 15:56 ../ -rw-r--r-- 1 carl staff 53248 Jan 27 19:07 .coverage drwxr-xr-x 12 carl staff 384 Jan 27 19:13 .git/""" response._record_response(response_bytes) assert str(response.finish_time)[:-7] == response_end_time assert response.result == response_bytes.decode() assert response.failed is False
def mock_send_commands_from_file( cls, file, strip_prompt, failed_when_contains="", stop_on_failed=False, privilege_level="", timeout_ops=None, ): with open(file, "r") as f: commands = f.read().splitlines() response = Response(host="fake_as_heck", channel_input=commands[0]) response._record_response(b"some stuff about whatever") return [response]
def test_response_record_result_failed_when_success(): response = Response("localhost", "ls -al", failed_when_contains=["!racecar!"]) response_end_time = str(datetime.now())[:-7] response_str = """ ls -al total 264 drwxr-xr-x 34 carl staff 1088 Jan 27 19:07 ./ drwxr-xr-x 21 carl staff 672 Jan 25 15:56 ../ -rw-r--r-- 1 carl staff 53248 Jan 27 19:07 .coverage drwxr-xr-x 12 carl staff 384 Jan 27 19:13 .git/""" response._record_response(response_str) assert str(response.finish_time)[:-7] == response_end_time assert response.result == response_str assert response.failed is False
def test_post_send_config(sync_cisco_iosxe_conn): response_one = Response("localhost", "some input", failed_when_contains=["something"]) response_one._record_response(result=b"greatsucccess") response_two = Response("localhost", "some input") response_two._record_response(result=b"alsosucess") multi_response = MultiResponse() multi_response.append(response_one) multi_response.append(response_two) unified_response = sync_cisco_iosxe_conn._post_send_config( config="interface loopback0\ndescription scrapli is neat", multi_response=multi_response) assert unified_response.failed is False assert unified_response.result == "greatsucccess\nalsosucess"
def mock_send_configs( cls, configs, strip_prompt, failed_when_contains="", stop_on_failed=False, privilege_level="", timeout_ops=None, ): responses = [] response = Response(host="fake_as_heck", channel_input=configs[0]) response._record_response(b"") responses.append(response) response = Response(host="fake_as_heck", channel_input=configs[1]) response._record_response(b"") responses.append(response) return responses
def mock_send_configs_from_file( cls, file, strip_prompt, failed_when_contains="", stop_on_failed=False, privilege_level="", timeout_ops=None, ): with open(file, "r") as f: configs = f.read().splitlines() responses = [] response = Response(host="fake_as_heck", channel_input=configs[0]) response._record_response(b"") responses.append(response) response = Response(host="fake_as_heck", channel_input=configs[1]) response._record_response(b"") responses.append(response) return responses
def _post_send_command(raw_response: bytes, processed_response: bytes, response: Response) -> Response: """ Handle post "send_command" tasks for consistency between sync/async versions Args: raw_response: raw response returned from the channel processed_response: processed response returned from the channel response: response object to update with channel results Returns: Response: Scrapli Response object Raises: N/A """ response._record_response(result=processed_response) # pylint: disable=W0212 response.raw_result = raw_response return response
def send_command( self, command: str, strip_prompt: bool = True, failed_when_contains: Optional[Union[str, List[str]]] = None, ) -> Response: """ Send a command Args: command: string to send to device in privilege exec mode strip_prompt: True/False strip prompt from returned output failed_when_contains: string or list of strings indicating failure if found in response Returns: Response: Scrapli Response object Raises: TypeError: if command is anything but a string """ if not isinstance(command, str): raise TypeError( f"`send_command` expects a single string, got {type(command)}. " "to send a list of commands use the `send_commands` method instead." ) response = Response( host=self.transport.host, channel_input=command, failed_when_contains=failed_when_contains, ) raw_response, processed_response = self.channel.send_input( channel_input=command, strip_prompt=strip_prompt ) response._record_response(result=processed_response) # pylint: disable=W0212 response.raw_result = raw_response return response
o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP a - application route + - replicated route, % - next hop override, p - overrides from PfR Gateway of last resort is not set 10.0.0.0/8 is variably subnetted, 2 subnets, 2 masks C 10.0.0.0/24 is directly connected, GigabitEthernet1 L 10.0.0.15/32 is directly connected, GigabitEthernet1""" RAW_RESULT = "\n".join([IOSXE_SHOW_VERSION, IOSXE_SHOW_IP_ROUTE]) TEST_SCRAPLI_RESPONSE_ONE = Response(host="sea-ios-1", channel_input="show version", textfsm_platform="cisco_iosxe") TEST_SCRAPLI_RESPONSE_ONE._record_response(result=IOSXE_SHOW_VERSION) TEST_SCRAPLI_RESPONSE_TWO = Response(host="sea-ios-1", channel_input="show ip route", textfsm_platform="cisco_iosxe") TEST_SCRAPLI_RESPONSE_TWO._record_response(result=IOSXE_SHOW_IP_ROUTE) TEST_SCRAPLI_RESPONSE = [TEST_SCRAPLI_RESPONSE_ONE, TEST_SCRAPLI_RESPONSE_TWO] TEST_HOST = Host(name="sea-ios-1") TEST_AGG_RESULT = AggregatedResult("send_commands") TEST_MULTI_RESULT = MultiResult("send_commands") TEST_RESULT = Result(host=TEST_HOST, result=RAW_RESULT, name="send_commands") setattr(TEST_RESULT, "scrapli_response", TEST_SCRAPLI_RESPONSE) TEST_MULTI_RESULT.append(TEST_RESULT) TEST_AGG_RESULT[TEST_HOST.name] = TEST_MULTI_RESULT
def send_interactive( self, interact_events: List[Tuple[str, str, Optional[bool]]], failed_when_contains: Optional[Union[str, List[str]]] = None, privilege_level: str = "", ) -> Response: """ Interact with a device with changing prompts per input. Used to interact with devices where prompts change per input, and where inputs may be hidden such as in the case of a password input. This can be used to respond to challenges from devices such as the confirmation for the command "clear logging" on IOSXE devices for example. You may have as many elements in the "interact_events" list as needed, and each element of that list should be a tuple of two or three elements. The first element is always the input to send as a string, the second should be the expected response as a string, and the optional third a bool for whether or not the input is "hidden" (i.e. password input) An example where we need this sort of capability: ``` 3560CX#copy flash: scp: Source filename []? test1.txt Address or name of remote host []? 172.31.254.100 Destination username [carl]? Writing test1.txt Password: Password: Sink: C0644 639 test1.txt ! 639 bytes copied in 12.066 secs (53 bytes/sec) 3560CX# ``` To accomplish this we can use the following: ``` interact = conn.channel.send_inputs_interact( [ ("copy flash: scp:", "Source filename []?", False), ("test1.txt", "Address or name of remote host []?", False), ("172.31.254.100", "Destination username [carl]?", False), ("carl", "Password:"******"super_secure_password", prompt, True), ] ) ``` If we needed to deal with more prompts we could simply continue adding tuples to the list of interact "events". Args: interact_events: list of tuples containing the "interactions" with the device each list element must have an input and an expected response, and may have an optional bool for the third and final element -- the optional bool specifies if the input that is sent to the device is "hidden" (ex: password), if the hidden param is not provided it is assumed the input is "normal" (not hidden) failed_when_contains: list of strings that, if present in final output, represent a failed command/interaction privilege_level: ignored in this base class; for LSP reasons for subclasses Returns: Response: scrapli Response object Raises: N/A """ _ = privilege_level joined_input = ", ".join([event[0] for event in interact_events]) response = Response( self.transport.host, channel_input=joined_input, failed_when_contains=failed_when_contains, ) raw_response, processed_response = self.channel.send_inputs_interact( interact_events=interact_events ) response._record_response(result=processed_response) # pylint: disable=W0212 response.raw_result = raw_response return response
def mock_edit_config(cls, config, target): response = Response(host="fake_as_heck", channel_input="blah") response._record_response(b"some stuff about whatever") return response
def mock_send_commands( cls, commands, strip_prompt, failed_when_contains, stop_on_failed, timeout_ops=None ): response = Response(host="fake_as_heck", channel_input=commands[0]) response._record_response(b"some stuff about whatever") return [response]
def mock_validate(cls, source): response = Response(host="fake_as_heck", channel_input="blah") response._record_response(b"some stuff about whatever") return response
def mock_rpc(cls, filter_): response = Response(host="fake_as_heck", channel_input="blah") response._record_response(b"some stuff about whatever") return response
def mock_get_config(cls, source, filters, filter_type): response = Response(host="fake_as_heck", channel_input="blah") response._record_response(b"some stuff about whatever") return response
def mock_send_commands(cls, commands, strip_prompt): response = Response(host="fake_as_heck", channel_input=commands[0]) response._record_response("some stuff about whatever") return [response]