Skip to content

Commit dbf847b

Browse files
Merge pull request #12 from SamBriskman/issues#11
Issues#11
2 parents 23a98ca + 5def930 commit dbf847b

File tree

9 files changed

+163
-33
lines changed

9 files changed

+163
-33
lines changed

packages/agent/src/netdriver_agent/client/session.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,23 @@ async def exec_cmd_in_vsys_and_mode(self, command: str, vsys: str = None, mode:
497497
line = lines[i].strip()
498498
output += await self.exec_cmd(line)
499499
i += 1
500-
return output
500+
return output
501+
502+
async def switch_vsys_by_mode(self, command: str, mode: Mode = None) -> str:
503+
"""
504+
Switch VSYS by mode and output
505+
"""
506+
output = ""
507+
if mode and self._mode != mode:
508+
output += await self.switch_mode(mode)
509+
lines = command.splitlines()
510+
line_size = len(lines)
511+
i = 0
512+
while i < line_size:
513+
line = lines[i].strip()
514+
output += await self.exec_cmd(line)
515+
i += 1
516+
return output
501517

502518
async def send_cmd(self, command: str, vsys: str = None, mode: Mode = None,
503519
timeout: float = 10, catch_error: bool = True, detail_output: bool = True) -> CmdTaskResult:

packages/agent/src/netdriver_agent/plugins/array/array_ag.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,15 @@ async def switch_vsys(self, vsys: str) -> str:
3737
self._logger.info(f"Switching vsys: {self._vsys} -> {vsys}")
3838

3939
output = ""
40-
# Already in the target vsys
41-
if vsys == self._vsys:
42-
return output
4340

4441
ret : str
4542
if vsys == ArrayBase._DEFAULT_VSYS:
4643
# Switch to default vsys
47-
ret = await self.exec_cmd_in_vsys_and_mode("exit", mode=Mode.ENABLE)
44+
ret = await self.switch_vsys_by_mode("exit", mode=Mode.ENABLE)
4845
output += ret
4946
else:
5047
# Switch to target vsys
51-
ret = await self.exec_cmd_in_vsys_and_mode(f"switch {vsys}", mode=Mode.ENABLE)
48+
ret = await self.switch_vsys_by_mode(f"switch {vsys}", mode=Mode.ENABLE)
5249
output += ret
5350

5451
# Check if there is any error

packages/agent/src/netdriver_agent/plugins/fortinet/fortinet_fortigate.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,19 @@ async def switch_vsys(self, vsys: str) -> str:
4444
self._logger.info(f"Switching vsys: {self._vsys} -> {vsys}")
4545

4646
output = ""
47-
# Already in the target vsys
48-
if vsys == self._vsys:
49-
return output
5047

5148
ret: str
5249
if vsys == self._DEFAULT_VSYS:
5350
# vsys -> default
54-
ret = await self.exec_cmd_in_vsys_and_mode("end", mode=Mode.ENABLE)
51+
ret = await self.switch_vsys_by_mode("end", mode=Mode.ENABLE)
5552
output += ret
5653
elif self._vsys == self._DEFAULT_VSYS:
5754
# default -> vsys
58-
ret = await self.exec_cmd_in_vsys_and_mode(f"config vdom\nedit {vsys}", mode=Mode.ENABLE)
55+
ret = await self.switch_vsys_by_mode(f"config vdom\nedit {vsys}", mode=Mode.ENABLE)
5956
output += ret
6057
else:
6158
# vsys1 -> vsys2
62-
ret = await self.exec_cmd_in_vsys_and_mode(f"next\nedit {vsys}", mode=Mode.ENABLE)
59+
ret = await self.switch_vsys_by_mode(f"next\nedit {vsys}", mode=Mode.ENABLE)
6360
output += ret
6461

6562
# check errors

packages/agent/src/netdriver_agent/plugins/huawei/huawei_usg.py

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,27 +38,17 @@ def decide_current_vsys(self, prompt: str):
3838
async def switch_vsys(self, vsys: str) -> str:
3939
self._logger.info(f"Switching vsys: {self._vsys} -> {vsys}")
4040

41-
output = ""
42-
# Already in the target vsys
43-
if vsys == self._vsys:
44-
return output
45-
46-
ret: str
47-
if vsys == self._DEFAULT_VSYS:
48-
ret = await self.exec_cmd_in_vsys_and_mode("quit", mode=Mode.ENABLE)
49-
output += ret
50-
else:
51-
ret = await self.exec_cmd_in_vsys_and_mode(f"switch vsys {vsys}", mode=Mode.CONFIG)
52-
output += ret
41+
output = await self.switch_vsys_by_mode(f"switch vsys {vsys}", mode=Mode.CONFIG)
5342

5443
# check errors
55-
err = utils.regex.catch_error_of_output(ret,
44+
err = utils.regex.catch_error_of_output(output,
5645
self.get_error_patterns(),
5746
self.get_ignore_error_patterns())
5847
if err:
5948
self._logger.error(f"Switch vsys failed: {err}")
6049
raise SwitchVsysFailed(err, output=output)
6150

6251
self._vsys = vsys
52+
self._mode = Mode.ENABLE
6353
self._logger.info(f"Switched vsys to: {self._vsys}")
6454
return output

packages/agent/src/netdriver_agent/plugins/maipu/maipu.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def get_error_patterns(self) -> list[re.Pattern]:
2828
def get_ignore_error_patterns(self) -> list[re.Pattern]:
2929
return MaiPuBase.PatternHelper.get_ignore_error_patterns()
3030

31-
def get_enable_password_pattern(self) -> re.Pattern:
31+
def get_enable_password_prompt_pattern(self) -> re.Pattern:
3232
return MaiPuBase.PatternHelper.get_enable_password_prompt_pattern()
3333

3434
def get_mode_prompt_patterns(self) -> dict[Mode, re.Pattern]:

packages/agent/src/netdriver_agent/plugins/venustech/venustech.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def get_error_patterns(self) -> list[re.Pattern]:
2727
def get_ignore_error_patterns(self) -> list[re.Pattern]:
2828
return VenustechBase.PatternHelper.get_ignore_error_patterns()
2929

30-
def get_enable_password_pattern(self) -> re.Pattern:
30+
def get_enable_password_prompt_pattern(self) -> re.Pattern:
3131
return VenustechBase.PatternHelper.get_enable_password_prompt_pattern()
3232

3333
def get_more_pattern(self) -> tuple[re.Pattern, str]:

packages/simunet/src/netdriver_simunet/server/handlers/fortinet/fortinet_fortigate.py

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,71 @@ def __init__(self, process: SSHServerProcess, conf_path: str = None):
3232
conf_path = f"{cwd_path}/fortinet_fortigate.yml"
3333
self.conf_path = conf_path
3434
super().__init__(process)
35+
self.level = []
36+
self.level_type = []
37+
38+
@property
39+
def prompt(self) -> str:
40+
prompt = self.config.hostname
41+
if self.level:
42+
prompt = f'{prompt} ({self.level[-1]})'
43+
return prompt + self.config.modes[self._mode].prompt
44+
45+
def switch_level(self, command: str) -> bool:
46+
if command.startswith('config'):
47+
commands = command.split(' ')
48+
self.level_type.append(commands[0])
49+
self.level.append(commands[-1])
50+
elif command.startswith('edit'):
51+
if self.level_type and self.level_type[-1] == 'config':
52+
commands = command.split(' ')
53+
self.level_type.append(commands[0])
54+
self.level.append(commands[-1])
55+
else:
56+
return False
57+
elif command == 'end':
58+
if self.level_type:
59+
is_edit = False
60+
if self.level_type[-1] == 'edit':
61+
is_edit = True
62+
self.level.pop()
63+
self.level_type.pop()
64+
if is_edit:
65+
self.level.pop()
66+
self.level_type.pop()
67+
else:
68+
return False
69+
elif command == 'next':
70+
if self.level_type and self.level_type[-1] == 'edit':
71+
self.level.pop()
72+
self.level_type.pop()
73+
else:
74+
return False
75+
elif command == 'exit' and self.level:
76+
return False
77+
return True
78+
79+
def exec_cmd_in_mode(self, command: str) -> str:
80+
""" Execute command in current mode """
81+
self._logger.info(f"Exec [{command} in {self._mode}]")
82+
if command in self.config.modes[self._mode].cmd_map:
83+
if not self.switch_level(command):
84+
return self.config.invalid_cmd_error
85+
return self.config.modes[self._mode].cmd_map[command]
86+
elif command in self.config.common_cmd_map:
87+
if not self.switch_level(command):
88+
return self.config.invalid_cmd_error
89+
return self.config.common_cmd_map[command]
90+
else:
91+
return self.config.invalid_cmd_error
3592

3693
async def switch_vsys(self, command: str) -> bool:
3794
return False
3895

3996
async def switch_mode(self, command: str) -> bool:
40-
if command not in self.config.modes[self._mode].switch_mode_cmds:
41-
return False
42-
4397
match self._mode:
4498
case Mode.ENABLE:
45-
if command == "exit":
99+
if command == "exit" and not self.level:
46100
# logout
47101
raise ClientExit
48102
case _:

packages/simunet/src/netdriver_simunet/server/handlers/fortinet/fortinet_fortigate.yml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,28 @@ line_feed: "\r\n"
55
modes:
66
enable:
77
prompt: " # "
8-
switch_mode_cmds:
9-
- "exit"
8+
switch_mode_cmds: []
109
cmds:
10+
- cmd: "config global"
11+
output: ""
1112
- cmd: "config system console"
1213
output: ""
1314
- cmd: "set output standard"
1415
output: ""
1516
- cmd: "end"
1617
output: ""
18+
- cmd: "config vdom"
19+
output: ""
20+
- cmd: "edit root"
21+
output: ""
22+
- cmd: "next"
23+
output: ""
24+
- cmd: "config firewall policy"
25+
output: ""
26+
- cmd: "edit 1"
27+
output: ""
28+
- cmd: "config firewall address"
29+
output: ""
1730
common:
1831
- cmd: "show full-configuration"
1932
output: |

tests/integration/test_fortinet_fortigate.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,66 @@ async def test_pull_config(test_client: TestClient, fortinet_fortigate_dev: dict
3838
assert response.headers.get("x-correlation-id") == trace_id
3939
assert not response.json().get("err_msg")
4040
assert len(response.json().get("result")) == 1
41+
42+
43+
@pytest.mark.integration
44+
@pytest.mark.asyncio
45+
async def test_switch_vsys(test_client: TestClient, fortinet_fortigate_dev: dict):
46+
trace_id = uuid4().hex
47+
response = test_client.post("/api/v1/cmd", headers={"x-correlation-id": trace_id}, json={
48+
"protocol": fortinet_fortigate_dev.get("protocol"),
49+
"ip": fortinet_fortigate_dev.get("ip"),
50+
"port": fortinet_fortigate_dev.get("port"),
51+
"username": fortinet_fortigate_dev.get("username"),
52+
"password": fortinet_fortigate_dev.get("password"),
53+
"enable_password": fortinet_fortigate_dev.get("enable_password"),
54+
"vendor": "fortinet",
55+
"model": "fortigate",
56+
"version": "7.2",
57+
"encode": "utf-8",
58+
"vsys": "default",
59+
"commands": [
60+
{
61+
"type": "raw",
62+
"mode": "enable",
63+
"command": "show full-configuration",
64+
"template": ""
65+
}
66+
],
67+
"timeout": 10
68+
})
69+
70+
assert response.status_code == 200
71+
assert response.json().get("code") == "OK"
72+
assert response.headers.get("x-correlation-id") == trace_id
73+
assert not response.json().get("err_msg")
74+
assert len(response.json().get("result")) == 1
75+
76+
response = test_client.post("/api/v1/cmd", headers={"x-correlation-id": trace_id}, json={
77+
"protocol": fortinet_fortigate_dev.get("protocol"),
78+
"ip": fortinet_fortigate_dev.get("ip"),
79+
"port": fortinet_fortigate_dev.get("port"),
80+
"username": fortinet_fortigate_dev.get("username"),
81+
"password": fortinet_fortigate_dev.get("password"),
82+
"enable_password": fortinet_fortigate_dev.get("enable_password"),
83+
"vendor": "fortinet",
84+
"model": "fortigate",
85+
"version": "7.2",
86+
"encode": "utf-8",
87+
"vsys": "root",
88+
"commands": [
89+
{
90+
"type": "raw",
91+
"mode": "enable",
92+
"command": "show full-configuration",
93+
"template": ""
94+
}
95+
],
96+
"timeout": 10
97+
})
98+
99+
assert response.status_code == 200
100+
assert response.json().get("code") == "OK"
101+
assert response.headers.get("x-correlation-id") == trace_id
102+
assert not response.json().get("err_msg")
103+
assert len(response.json().get("result")) == 1

0 commit comments

Comments
 (0)