Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

H3C switches are too slow in executing commands #3552

Open
LeisureZhao opened this issue Dec 26, 2024 · 6 comments
Open

H3C switches are too slow in executing commands #3552

LeisureZhao opened this issue Dec 26, 2024 · 6 comments

Comments

@LeisureZhao
Copy link

Description of Issue/Question

Compared to Cisco switches, H3C switches are too slow in executing commands because
the value of the pattern is not set in the check_config_mode function.
This function is called twice in both the config_mode and exit_config_mode,
each time using read_channel_timing instead of read_until_pattern, which greatly
extends the duration.

# netmiko/cisco/cisco_nxos_ssh.py
    def check_config_mode(
        self,
        check_string: str = ")#",
        pattern: str = r"[>#]",
        force_regex: bool = False,
    ) -> bool:
        """Checks if the device is in configuration mode or not."""
        return super().check_config_mode(check_string=check_string, pattern=pattern)
# netmiko/hp/hp_comware.py
    def check_config_mode(
        self, check_string: str = "]", pattern: str = "", force_regex: bool = False
    ) -> bool:
        """Check whether device is in configuration mode. Return a boolean."""
        return super().check_config_mode(check_string=check_string)

So, can it be modified to the following code?

# vi netmiko/hp/hp_comware.py

    def check_config_mode(
        self, check_string: str = "]", pattern: str = r"[>]|]", force_regex: bool = False
    ) -> bool:
        """Check whether device is in configuration mode. Return a boolean."""
        return super().check_config_mode(check_string=check_string, pattern=pattern)

    def check_enable_mode(self, check_string: str = "]") -> bool:
        """enable mode on Comware is system-view."""
        return self.check_config_mode(check_string=check_string, pattern=r"[>]|]")

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)

'device_type': 'hp_comware'
@ktbyers
Copy link
Owner

ktbyers commented Jan 3, 2025

@LeisureZhao Can you quantify (roughly) how long the executing commands is taking?

@LeisureZhao
Copy link
Author

LeisureZhao commented Jan 6, 2025

@LeisureZhao Can you quantify (roughly) how long the executing commands is taking?

Discovered that when using Netmiko to test the execution of commands on H3C and Cisco switches, the H3C switch takes 11 seconds, while the Cisco switch only takes 1 second. The test code example is as follows.

from netmiko import ConnectHandler, ReadTimeout
from datetime import datetime
import logging

ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
fmt = '%(asctime)s %(levelname)s [%(threadName)s] %(module)s.%(funcName)s.%(lineno)s: %(message)s'
formatter = logging.Formatter(fmt)
ch.setFormatter(formatter)
logger = logging.getLogger('netmiko')
logger.setLevel(logging.DEBUG)
logger.addHandler(ch)


switch_config1 = {
'device_type': 'cisco_nxos',
'host': 'xxxx',
'username': 'xxxx',
'password': 'xxxx'
}

switch_config2 = {
'device_type': 'hp_comware',
'host': 'xxxx',
'username': 'xxxx',
'password': 'xxxx'
}

print(datetime.now(), ':start') 
net_connect = ConnectHandler(**switch_config1)
print(datetime.now(), ':connect') 

PLUG_PORT_TO_NETWORK_cisco = (
'interface Eth1/31',
'switchport mode access',
'switchport access vlan 840',
)

DELETE_PORT_cisco = (
'interface Eth1/31',
'no switchport access vlan 840',
'no switchport mode trunk',
'switchport trunk allowed vlan none'
)

PLUG_PORT_TO_NETWORK_h3c = (
'interface XGE1/0/2',
'port link-type access',
'port access vlan 840'
)
DELETE_PORT_h3c = (
'interface XGE1/0/2',
'undo port link-aggregation group',
'undo port access vlan'
)


output = net_connect.send_config_set(config_commands=PLUG_PORT_TO_NETWORK_h3c)
print(datetime.now(), ':output') 
net_connect.disconnect()
print(datetime.now(), ':disconnect') 

@TaylorTrz
Copy link

Same to me. It seems netmiko has spent too much time waiting results on hp_comware like devices...

@ktbyers
Copy link
Owner

ktbyers commented Jan 21, 2025

@LeisureZhao @TaylorTrz I think historically Comware devices didn't have a way to set the terminal width which causes cmd_verify to be unworkable (this is one of Netmiko's main ways of going faster).

From Comware driver:

class HPComwareBase(CiscoSSHConnection):
    def __init__(self, **kwargs: Any) -> None:
        # Comware doesn't have a way to set terminal width which breaks cmd_verify
        global_cmd_verify = kwargs.get("global_cmd_verify")
        if global_cmd_verify is None:
            kwargs["global_cmd_verify"] = False
        super().__init__(**kwargs)

Is there any way to set the terminal width on Comware devices?

You could possibly try setting global_cmd_verify=True and see if the performance improves (especially for configuration operations).

@LeisureZhao
Copy link
Author

@ktbyers The global_cmd_verify is set to False, which will only cause the read_channel_timing function to be called once during the send_config_set function. More significantly, the check_config_mode function is called four times throughout a complete command execution process. This includes entering the config mode twice and exiting the config mode twice. Each call will invoke read_channel_timing due to the pattern not being set. Therefore, setting global_cmd_verify to False saves 2 seconds, and setting a specific pattern reduces the time by 8 seconds. The test records are as follows.

# Unchanged
$ python3 /tmp/switch_test.py
2025-01-24 08:50:11.992453 :start
2025-01-24 08:50:12.686643 :connect
2025-01-24 08:50:23.838255 :output
2025-01-24 08:50:26.040920 :disconnect

# Only set global_cmd_verify=True
class HPComwareBase(CiscoSSHConnection):
    def __init__(self, **kwargs: Any) -> None:
        # Comware doesn't have a way to set terminal width which breaks cmd_verify
        global_cmd_verify = True
        super().__init__(**kwargs)

$ python3 /tmp/switch_test.py
2025-01-24 08:53:32.871574 :start
2025-01-24 08:53:33.504477 :connect
2025-01-24 08:53:42.524568 :output
2025-01-24 08:53:44.728172 :disconnect

# Set global_cmd_verify=True and set specific pattern for hp_comware
# netmiko/hp/hp_comware.py
    def check_config_mode(
        self, check_string: str = "]", pattern: str = r"[>]|]", force_regex: bool = False
    ) -> bool:
        """Check whether device is in configuration mode. Return a boolean."""
        return super().check_config_mode(check_string=check_string, pattern=pattern)
        
$ python3 /tmp/switch_test.py
2025-01-24 09:03:05.379280 :start
2025-01-24 09:03:06.140330 :connect
2025-01-24 09:03:06.389666 :output
2025-01-24 09:03:06.400510 :disconnect

@ktbyers
Copy link
Owner

ktbyers commented Jan 24, 2025

@LeisureZhao Sounds good--do you want to do a PR on that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants