Skip to content

Commit 556eb09

Browse files
authored
Merge branch 'develop' into patch-1
2 parents 7ab08e5 + 4d2cb53 commit 556eb09

File tree

10 files changed

+142
-30
lines changed

10 files changed

+142
-30
lines changed

.github/workflows/main_testing.yaml

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
- name: Setup python
1616
uses: actions/setup-python@v5
1717
with:
18-
python-version: '3.11'
18+
python-version: '3.12'
1919
architecture: x64
2020

2121
- name: Install poetry
@@ -25,7 +25,7 @@ jobs:
2525
- name: Set up Python
2626
uses: actions/setup-python@v5
2727
with:
28-
python-version: '3.11'
28+
python-version: '3.12'
2929
cache: 'poetry'
3030

3131
- name: Install dependencies
@@ -52,7 +52,7 @@ jobs:
5252
shell: bash
5353
strategy:
5454
matrix:
55-
python-version: [ '3.9', '3.10', '3.11', "3.12", "3.13" ]
55+
python-version: ['3.10', '3.11', "3.12", "3.13", "3.14"]
5656
platform: [ubuntu-24.04, windows-2022]
5757

5858
runs-on: ${{ matrix.platform }}
@@ -88,16 +88,15 @@ jobs:
8888
poetry run pytest -v -s tests/unit/test_connection.py
8989
poetry run pytest -v -s tests/unit/test_entry_points.py
9090
91-
# ARM/X86_64 issues on macos
92-
pytest-macos13:
91+
pytest-macos15:
9392
name: Std Test on Python ${{ matrix.python-version }} (${{ matrix.platform}})
9493
defaults:
9594
run:
9695
shell: bash
9796
strategy:
9897
matrix:
99-
python-version: [ '3.9', '3.10', '3.11' ]
100-
platform: [macos-13]
98+
python-version: [ '3.12', '3.13', '3.14' ]
99+
platform: [macos-15]
101100

102101
runs-on: ${{ matrix.platform }}
103102
steps:
@@ -138,7 +137,7 @@ jobs:
138137
shell: bash
139138
strategy:
140139
matrix:
141-
python-version: [ "3.12", "3.13.0-beta.2" ]
140+
python-version: [ "3.12", "3.13", "3.14" ]
142141
platform: [macos-14]
143142

144143
runs-on: ${{ matrix.platform }}

PLATFORMS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
- Calix B6
2626
- Casa Systems CMTS
2727
- Centec Networks
28+
- Check Point GAiA
2829
- Cisco AireOS (Wireless LAN Controllers)
2930
- Cisco ASA
3031
- Cisco S200
@@ -92,7 +93,6 @@
9293
- Cisco APIC (Linux)
9394
- Cisco Telepresence
9495
- Cisco Viptela
95-
- Check Point GAiA
9696
- Corelight Linux
9797
- Coriant
9898
- Cumulus VX Linux
@@ -114,6 +114,7 @@
114114
- Garderos GRS
115115
- Genexis Saturn SOLT33 (telnet only)
116116
- Lancom LCOS SX4
117+
- Moxa EDS
117118
- MRV Communications OptiSwitch
118119
- MRV LX
119120
- Nokia/Alcatel SR-OS
@@ -254,6 +255,7 @@
254255
- mellanox_mlnxos
255256
- mikrotik_routeros
256257
- mikrotik_switchos
258+
- moxa_nos
257259
- mrv_lx
258260
- mrv_optiswitch
259261
- nec_ix

netmiko/base_connection.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,6 +2014,31 @@ def check_enable_mode(self, check_string: str = "") -> bool:
20142014
output = self.read_until_prompt(read_entire_line=True)
20152015
return check_string in output
20162016

2017+
def enable_secret_handler(
2018+
self,
2019+
pattern: str,
2020+
output: str,
2021+
re_flags: int = re.IGNORECASE,
2022+
) -> str:
2023+
"""
2024+
Some platforms require special handling when entering 'enable' mode.
2025+
2026+
Send the "secret" in response to password pattern
2027+
2028+
:param pattern: pattern to search for indicating device is waiting for password
2029+
2030+
:param output: Accumulated output from 'enable()' method.
2031+
2032+
:param re_flags: Regular expression flags used in conjunction with pattern
2033+
2034+
"""
2035+
if re.search(pattern, output, flags=re_flags):
2036+
self.write_channel(self.normalize_cmd(self.secret))
2037+
new_output = self.read_until_prompt()
2038+
else:
2039+
new_output = ""
2040+
return new_output
2041+
20172042
def enable(
20182043
self,
20192044
cmd: str = "",
@@ -2057,10 +2082,9 @@ def enable(
20572082
pattern=pattern, re_flags=re_flags, read_entire_line=True
20582083
)
20592084

2060-
# Send the "secret" in response to password pattern
2061-
if re.search(pattern, output, flags=re_flags):
2062-
self.write_channel(self.normalize_cmd(self.secret))
2063-
output += self.read_until_prompt()
2085+
output += self.enable_secret_handler(
2086+
pattern=pattern, output=output, re_flags=re_flags
2087+
)
20642088

20652089
# Search for terminating pattern if defined
20662090
if enable_pattern and not re.search(enable_pattern, output):

netmiko/checkpoint/checkpoint_gaia_ssh.py

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import time
22
import re
3+
from typing import Optional, Any
34

45
from netmiko.no_config import NoConfig
56
from netmiko.base_connection import BaseConnection
@@ -13,6 +14,13 @@ class CheckPointGaiaSSH(NoConfig, BaseConnection):
1314

1415
prompt_pattern = r"[>#]"
1516

17+
def __init__(self, *args: Any, **kwargs: Any) -> None:
18+
# Kept running into issues with command_echo and duplicate echoes of commands.
19+
self.fast_cli = False
20+
fast_cli = kwargs.get("fast_cli") or False
21+
kwargs["fast_cli"] = fast_cli
22+
return super().__init__(*args, **kwargs)
23+
1624
def session_preparation(self) -> None:
1725
"""
1826
Prepare the session after the connection has been established.
@@ -27,26 +35,63 @@ def session_preparation(self) -> None:
2735
time.sleep(0.3 * self.global_delay_factor)
2836
self.clear_buffer()
2937

30-
def command_echo_read(self, cmd: str, read_timeout: float) -> str:
31-
"""Check Point clish double echoes the command (at least sometimes)"""
38+
def check_enable_mode(self, check_string: str = "#") -> bool:
39+
"""Check if in enable mode. Return boolean."""
40+
return super().check_enable_mode(check_string=check_string)
3241

33-
re_cmd = re.escape(cmd)
34-
pattern = rf"{self.prompt_pattern}\s{re_cmd}"
42+
def enable_secret_handler(
43+
self,
44+
pattern: str,
45+
output: str,
46+
re_flags: int = re.IGNORECASE,
47+
) -> str:
48+
"""
49+
Check Point Gaia requires very particular timing for this 'expert'
50+
password handling to work.
3551
36-
# Make sure you read until you detect the command echo (avoid getting out of sync)
37-
new_data = self.read_until_pattern(pattern=pattern, read_timeout=read_timeout)
52+
Send the "secret" in response to password pattern
53+
"""
54+
if re.search(pattern, output, flags=re_flags):
55+
self.write_channel(self.secret)
56+
time.sleep(0.3 * self.global_delay_factor)
57+
self.write_channel(self.RETURN)
58+
time.sleep(0.3 * self.global_delay_factor)
59+
new_output = self.read_until_pattern(pattern=self.prompt_pattern)
60+
return new_output
3861

39-
# There can be echoed prompts that haven't been cleared before the cmd echo
40-
# this can later mess up the trailing prompt pattern detection. Clear this out.
41-
lines = new_data.split(cmd)
42-
if len(lines) in [2, 3]:
43-
# lines[-1] should realistically just be the null string
44-
new_data = f"{cmd}{lines[-1]}"
45-
else:
46-
# cmd exists in the output multiple times? Just retain the original output
47-
pass
62+
def enable(
63+
self,
64+
cmd: str = "expert",
65+
pattern: str = r"expert password",
66+
enable_pattern: Optional[str] = r"\#",
67+
check_state: bool = True,
68+
re_flags: int = re.IGNORECASE,
69+
) -> str:
70+
"""
71+
Enter expert mode.
72+
73+
Check Point Gaia is very finicky on the timing of sending this 'expert' password.
74+
"""
75+
output = super().enable(
76+
cmd=cmd,
77+
pattern=pattern,
78+
enable_pattern=enable_pattern,
79+
check_state=check_state,
80+
re_flags=re_flags,
81+
)
82+
self.set_base_prompt()
83+
return output
4884

49-
return new_data
85+
def exit_enable_mode(self, exit_command: str = "exit") -> str:
86+
"""Exit expert mode."""
87+
output = ""
88+
if self.check_enable_mode():
89+
self.write_channel(self.normalize_cmd(exit_command))
90+
output += self.read_until_pattern(pattern=r">")
91+
self.set_base_prompt()
92+
if self.check_enable_mode():
93+
raise ValueError("Failed to exit enable mode.")
94+
return output
5095

5196
def save_config(
5297
self, cmd: str = "", confirm: bool = False, confirm_response: str = ""

netmiko/garderos/garderos_grs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def session_preparation(self) -> None:
1818
"""Prepare the session after the connection has been established"""
1919
self.ansi_escape_codes = True
2020
self._test_channel_read()
21-
self.set_base_prompt(pri_prompt_terminator="#")
21+
self.set_base_prompt(pri_prompt_terminator="#", alt_prompt_terminator="$")
2222
self.clear_buffer()
2323

2424
def send_command(

netmiko/moxa/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from netmiko.moxa.moxa_nos import MoxaNosSSH
2+
3+
__all__ = ["MoxaNosSSH"]

netmiko/moxa/moxa_nos.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""
2+
Tested with
3+
4+
EDS-508A
5+
EDS-516A
6+
7+
Note:
8+
This only works in CLI mode. If the device is in Menu mode, you need to change that first.
9+
"""
10+
11+
from netmiko.cisco_base_connection import CiscoSSHConnection
12+
13+
14+
class MoxaNosBase(CiscoSSHConnection):
15+
"""MOXA base driver"""
16+
17+
pass
18+
19+
20+
class MoxaNosSSH(MoxaNosBase):
21+
"""MOXA SSH driver"""
22+
23+
pass

netmiko/ssh_autodetect.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,12 @@
349349
"priority": 99,
350350
"dispatch": "_autodetect_std",
351351
},
352+
"moxa_nos": {
353+
"cmd": "",
354+
"dispatch": "_autodetect_remote_version",
355+
"search_patterns": [r"[Mm]oxa"],
356+
"priority": 99,
357+
},
352358
"huawei_smartax": {
353359
"cmd": "display version",
354360
"search_patterns": [r"Huawei Integrated Access Software"],

netmiko/ssh_dispatcher.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
from netmiko.mikrotik import MikrotikRouterOsSSH, MikrotikRouterOsFileTransfer
122122
from netmiko.mikrotik import MikrotikSwitchOsSSH
123123
from netmiko.mellanox import MellanoxMlnxosSSH
124+
from netmiko.moxa import MoxaNosSSH
124125
from netmiko.mrv import MrvLxSSH
125126
from netmiko.mrv import MrvOptiswitchSSH
126127
from netmiko.netapp import NetAppcDotSSH
@@ -299,6 +300,7 @@
299300
"mikrotik_switchos": MikrotikSwitchOsSSH,
300301
"mellanox": MellanoxMlnxosSSH,
301302
"mellanox_mlnxos": MellanoxMlnxosSSH,
303+
"moxa_nos": MoxaNosSSH,
302304
"mrv_lx": MrvLxSSH,
303305
"mrv_optiswitch": MrvOptiswitchSSH,
304306
"nec_ix": NecIxSSH,

tests/test_netmiko_show.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,14 @@ def test_enable_mode(net_connect, commands, expected_responses):
374374
except AttributeError:
375375
assert True
376376

377+
# Now verify you can exit enable mode
378+
try:
379+
net_connect.exit_enable_mode()
380+
prompt = net_connect.find_prompt()
381+
assert prompt == expected_responses["router_prompt"]
382+
except AttributeError:
383+
assert True
384+
377385

378386
def test_disconnect(net_connect, commands, expected_responses):
379387
"""Terminate the SSH session."""

0 commit comments

Comments
 (0)