ktbyers / netmiko

Multi-vendor library to simplify Paramiko SSH connections to network devices
MIT License
3.57k stars 1.3k forks source link

Work with VDOMs #3452

Closed zimbres closed 2 months ago

zimbres commented 3 months ago

Description of Issue/Question

Trying to work with a Firewall with Multiple VDOMs enabled.

The command net_connect.send_command("config vdom") returns an error: Pattern not detected: 'FW\-NUA\-FW01\ \$' in output.

Is there a way to temporary change the expected prompt or, how to work with VDOMs properly?

Setup

Netmiko version

(Paste verbatim output from pip freeze | grep netmiko between quotes below)

netmiko==4.3.0

Netmiko device_type (if relevant to the issue)

(Paste device_type between quotes below)

fortinet

Steps to Reproduce the Issue

Error Traceback

(Paste the complete traceback of the exception between quotes below)

net_connect.send_command("config vdom")
net_connect.send_command(f"edit {vdom}")

Relevant Python code

(Please try to essentialize your Python code to the minimum code needed to reproduce the issue) (Paste the code between the quotes below)

ktbyers commented 3 months ago

@zimbres Can you show me what the CLI interaction would look like if you did this manually via SSH (so I can compare this to the Netmiko Fortinet code to see how you can accomplish the task)

zimbres commented 3 months ago

Hi here it is, the same happens with a limited account where the prompt ends with $

image

ktbyers commented 2 months ago

Your error message has a $ as the trailing prompt whereas your output above had a #

From your error message

'FW\-NUA\-FW01\ \$'

Do you need to elevate privileges in some way?

For the commands that you are executing, you would need to do the following (assuming you don't need to elevate privileges):

net_connect.send_command("config vdom", expect_string=r"#")
net_connect.send_command(f"edit {vdom}", expect_string=r"#")

This is because the commands you are executing are changing the prompt.

zimbres commented 2 months ago

Hi, print and my first message are from different Firewalls because of confidential info.

Consider this response from Netmiko in the same firewall of the image:

Pattern not detected: 'FG30E\\ \\#' in output.

Things you might try to fix this:
1. Explicitly set your pattern using the expect_string argument.
2. Increase the read_timeout to a larger value.

You can also look at the Netmiko session_log or debug log for more information.
ktbyers commented 2 months ago

@zimbres Please include the Python code you used to execute this and the full exception stack trace (so I can see where the error is occuring).

zimbres commented 2 months ago

Hi, I achieved something working with your tip, "expect_string"

#!/usr/bin/env python3
import json
import statistics

from netmiko import ConnectHandler

env = json.load(open('env.json'))

def ping(host, target, source, port, vdom=None):
    device = {
        "device_type": "fortinet",
        "host": f"{host}",
        "port": f"{port}",
        "username": f"{env['USER']}",
        "password": f"{env['PASSWORD']}"
    }

    try:
        with ConnectHandler(**device, allow_auto_change=True) as net_connect:
            if vdom is not None:
                net_connect.send_command('config vdom', expect_string=r"\ \(\w+\)\ [#$]")
                net_connect.send_command(f'edit {vdom}', expect_string=r"\ \(\w+\)\ [#$]")
                net_connect.send_command(f'exec ping-options source {source}', expect_string=r"\ \(\w+\)\ [#$]")
                multi_lines = net_connect.send_command(f'execute ping {target}', read_timeout=60, expect_string=r"\ \(\w+\)\ [#$]")
            else:
                net_connect.send_command(f'exec ping-options source {source}')
                multi_lines = net_connect.send_command(f'execute ping {target}', read_timeout=60)

            lines = multi_lines.splitlines()
            ping_median = []

            if " 0% packet loss" not in multi_lines:
                return "fail"

            for line in lines:
                if "icmp_seq" in line:
                    ping_time = line.split()[-2].split('=')[-1]
                    ping_median.append(float(ping_time))

            return round(statistics.median(ping_median), 0)

    except Exception as e:
        print(e)
        return "fail"

if __name__ == "__main__":
    ping("192.168.100.31", "192.168.100.30", "192.168.100.50", 22)
ktbyers commented 2 months ago

Okay, so you have it all working now?

zimbres commented 2 months ago

Yes, thanks a lot!