datacomshell 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {datacomshell-0.1.0 → datacomshell-0.1.2}/PKG-INFO +1 -1
- {datacomshell-0.1.0 → datacomshell-0.1.2}/pyproject.toml +1 -1
- datacomshell-0.1.0/src/datacomshell/datacom_shell.py → datacomshell-0.1.2/src/datacomshell/AsyncTelnetUtil.py +59 -13
- datacomshell-0.1.2/src/datacomshell/TelnetShellClient.py +3 -0
- datacomshell-0.1.2/src/datacomshell/TelnetShellServer.py +39 -0
- {datacomshell-0.1.0 → datacomshell-0.1.2}/README.md +0 -0
- {datacomshell-0.1.0 → datacomshell-0.1.2}/src/datacomshell/__init__.py +0 -0
|
@@ -17,11 +17,15 @@ class LoginError(RuntimeError):
|
|
|
17
17
|
pass
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
_CONST_COMMAND_RUNNED_PATTERN = re.compile(
|
|
21
21
|
r'<.*>|\[.*\]|<~.*>|\[~.*\]|<\*.*>|\[\*.*\]')
|
|
22
|
+
_CONST_COMMAND_RUNNED_PATTERN_LIST = [_CONST_COMMAND_RUNNED_PATTERN]
|
|
22
23
|
|
|
24
|
+
_CONST_COMMAND_ERROR_PATTERN = re.compile(r'%.*')
|
|
25
|
+
_CONST_COMMAND_ERROR_PATTERN_LIST = [_CONST_COMMAND_ERROR_PATTERN]
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
|
|
28
|
+
class AsyncTelnetUtil(object):
|
|
25
29
|
def __init__(self,
|
|
26
30
|
host: str,
|
|
27
31
|
username: str,
|
|
@@ -29,6 +33,7 @@ class DatacomShell(object):
|
|
|
29
33
|
port: int = 23,
|
|
30
34
|
connect_timeout: Optional[float] = None
|
|
31
35
|
) -> None:
|
|
36
|
+
# 连接基本信息
|
|
32
37
|
self.__host = host
|
|
33
38
|
self.__port = port
|
|
34
39
|
self.__connect_timeout = connect_timeout
|
|
@@ -40,11 +45,41 @@ class DatacomShell(object):
|
|
|
40
45
|
self.__reader: TelnetReader | TelnetReaderUnicode | None = None
|
|
41
46
|
self.__writer: TelnetWriter | TelnetWriterUnicode | None = None
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
# 默认命令执行匹配
|
|
49
|
+
self.__DEFAULT_CONST_COMMAND_RUNNED_PATTERN_LIST = [
|
|
50
|
+
_CONST_COMMAND_RUNNED_PATTERN]
|
|
51
|
+
|
|
52
|
+
self.__DEFAULT_CONST_COMMAND_ERROR_PATTERN_LIST = [
|
|
53
|
+
_CONST_COMMAND_ERROR_PATTERN]
|
|
54
|
+
|
|
55
|
+
def __successfulMatch(self, pattern_list: Optional[list[re.Pattern]] = None, buffer: str = '') -> bool:
|
|
56
|
+
if pattern_list is not None:
|
|
57
|
+
for pattern in pattern_list:
|
|
58
|
+
if re.search(pattern=pattern, string=buffer) is not None:
|
|
59
|
+
return True
|
|
60
|
+
else:
|
|
61
|
+
for pattern in self.__DEFAULT_CONST_COMMAND_RUNNED_PATTERN_LIST:
|
|
62
|
+
if re.search(pattern=pattern, string=buffer) is not None:
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
def __errorMatch(self, error_pattern_list: Optional[list[re.Pattern]] = None, buffer: str = '') -> bool:
|
|
68
|
+
if error_pattern_list is not None:
|
|
69
|
+
for pattern in error_pattern_list:
|
|
70
|
+
if re.search(pattern=pattern, string=buffer) is not None:
|
|
71
|
+
return True
|
|
72
|
+
else:
|
|
73
|
+
for pattern in self.__DEFAULT_CONST_COMMAND_ERROR_PATTERN_LIST:
|
|
74
|
+
if re.search(pattern=pattern, string=buffer) is not None:
|
|
75
|
+
return True
|
|
44
76
|
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
# 读取命令返回数据
|
|
45
80
|
async def __read(self,
|
|
46
|
-
|
|
47
|
-
|
|
81
|
+
pattern_list: Optional[list[re.Pattern]] = None,
|
|
82
|
+
error_pattern_list: Optional[list[re.Pattern]] = None,
|
|
48
83
|
timeout: Optional[float] = 10.0,
|
|
49
84
|
size: int = 1024
|
|
50
85
|
) -> str | None:
|
|
@@ -54,7 +89,7 @@ class DatacomShell(object):
|
|
|
54
89
|
if self.__reader is None or self.__writer is None:
|
|
55
90
|
raise ConnectError("Not connected to device")
|
|
56
91
|
|
|
57
|
-
while
|
|
92
|
+
while runStatus:
|
|
58
93
|
try:
|
|
59
94
|
res = await asyncio.wait_for(self.__reader.read(size), timeout)
|
|
60
95
|
except asyncio.TimeoutError:
|
|
@@ -68,15 +103,21 @@ class DatacomShell(object):
|
|
|
68
103
|
elif res is str:
|
|
69
104
|
buffer += res
|
|
70
105
|
|
|
71
|
-
|
|
72
|
-
|
|
106
|
+
__temp_match_status = False
|
|
107
|
+
if not __temp_match_status:
|
|
108
|
+
__temp_match_status = self.__successfulMatch(
|
|
109
|
+
pattern_list=pattern_list, buffer=buffer)
|
|
110
|
+
|
|
111
|
+
if not __temp_match_status:
|
|
112
|
+
__temp_match_status = self.__errorMatch(
|
|
113
|
+
error_pattern_list=error_pattern_list, buffer=buffer)
|
|
73
114
|
|
|
74
|
-
if
|
|
115
|
+
if __temp_match_status:
|
|
75
116
|
runStatus = False
|
|
76
|
-
break
|
|
77
117
|
|
|
78
118
|
return buffer if runStatus else None
|
|
79
119
|
|
|
120
|
+
# 写入命令
|
|
80
121
|
def __write(self, command: str) -> None:
|
|
81
122
|
if self.__writer is None:
|
|
82
123
|
raise ConnectError("Not connected to device")
|
|
@@ -89,6 +130,7 @@ class DatacomShell(object):
|
|
|
89
130
|
else:
|
|
90
131
|
self.__writer.write(in_command.encode('utf-8'))
|
|
91
132
|
|
|
133
|
+
# 建立连接
|
|
92
134
|
async def connect(self) -> None:
|
|
93
135
|
try:
|
|
94
136
|
self.__reader, self.__writer = await telnetlib3.open_connection(
|
|
@@ -102,21 +144,22 @@ class DatacomShell(object):
|
|
|
102
144
|
|
|
103
145
|
return
|
|
104
146
|
|
|
147
|
+
# 建立登录
|
|
105
148
|
async def login(self) -> None:
|
|
106
149
|
if self.__login_status:
|
|
107
150
|
return
|
|
108
151
|
|
|
109
|
-
if self.__read(
|
|
152
|
+
if await self.__read(pattern_list=[re.compile(r"Username:")]) is not None:
|
|
110
153
|
self.__write(self.__username)
|
|
111
154
|
else:
|
|
112
155
|
raise LoginError("Failed to connect to device")
|
|
113
156
|
|
|
114
|
-
if self.__read(
|
|
157
|
+
if await self.__read(pattern_list=[re.compile(r"Password:")]) is not None:
|
|
115
158
|
self.__write(self.__password)
|
|
116
159
|
else:
|
|
117
160
|
raise LoginError("Failed to connect to device")
|
|
118
161
|
|
|
119
|
-
if self.__read(
|
|
162
|
+
if await self.__read(pattern_list=self.__DEFAULT_CONST_COMMAND_RUNNED_PATTERN_LIST) is not None:
|
|
120
163
|
self.__login_status = True
|
|
121
164
|
return
|
|
122
165
|
|
|
@@ -124,6 +167,9 @@ class DatacomShell(object):
|
|
|
124
167
|
self.__write(command)
|
|
125
168
|
return await self.__read()
|
|
126
169
|
|
|
170
|
+
def only_send_command(self, command: str) -> None:
|
|
171
|
+
self.__write(command)
|
|
172
|
+
|
|
127
173
|
async def build(self) -> None:
|
|
128
174
|
await self.connect()
|
|
129
175
|
await self.login()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
3
|
+
__EMPTY_SYNC_PIPELINE_INDEX = -1
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Command(object):
|
|
7
|
+
def __init__(self, command_str: str) -> None:
|
|
8
|
+
self.__command_id = None
|
|
9
|
+
self.__command_str = command_str
|
|
10
|
+
self.__command_has_result = True
|
|
11
|
+
self.__command_result = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TelnetShellServer(object):
|
|
15
|
+
"""
|
|
16
|
+
Telnet shell server.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self) -> None:
|
|
20
|
+
|
|
21
|
+
self.__sync_command_pipeline = []
|
|
22
|
+
self.__async_command_pipeline = []
|
|
23
|
+
self.__command_runned_result_dict = {}
|
|
24
|
+
|
|
25
|
+
self.__sync_current_command_index = __EMPTY_SYNC_PIPELINE_INDEX
|
|
26
|
+
|
|
27
|
+
self.__connect_pool = []
|
|
28
|
+
|
|
29
|
+
def __check_reset_result_dict_count(self) -> bool:
|
|
30
|
+
if self.__command_runned_result_dict.__len__() > 1000:
|
|
31
|
+
self.__command_runned_result_dict.clear()
|
|
32
|
+
return True
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
def gen_command_process_id(self) -> uuid.UUID:
|
|
36
|
+
return uuid.uuid4()
|
|
37
|
+
|
|
38
|
+
def sync_push(self, command: Command) -> None:
|
|
39
|
+
self.__sync_command_pipeline.insert(0, command)
|
|
File without changes
|
|
File without changes
|