datacomshell 0.1.2__tar.gz → 0.26.5__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.2 → datacomshell-0.26.5}/PKG-INFO +2 -1
- {datacomshell-0.1.2 → datacomshell-0.26.5}/pyproject.toml +2 -1
- datacomshell-0.26.5/src/datacomshell/AsyncTelnetClientUtil.py +259 -0
- datacomshell-0.26.5/src/datacomshell/Auth.py +46 -0
- datacomshell-0.1.2/src/datacomshell/AsyncTelnetUtil.py +0 -175
- datacomshell-0.1.2/src/datacomshell/TelnetShellClient.py +0 -3
- datacomshell-0.1.2/src/datacomshell/TelnetShellServer.py +0 -39
- {datacomshell-0.1.2 → datacomshell-0.26.5}/README.md +0 -0
- {datacomshell-0.1.2 → datacomshell-0.26.5}/src/datacomshell/__init__.py +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: datacomshell
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.26.5
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Author: Saven
|
|
6
6
|
Author-email: Saven <2416844857@qq.com>
|
|
7
|
+
Requires-Dist: aiofiles>=25.1.0
|
|
7
8
|
Requires-Dist: telnetlib3>=4.0.2
|
|
8
9
|
Requires-Python: >=3.14
|
|
9
10
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "datacomshell"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.26.5"
|
|
4
4
|
description = "Add your description here"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Saven", email = "2416844857@qq.com" },
|
|
@@ -8,6 +8,7 @@ authors = [
|
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
requires-python = ">=3.14"
|
|
10
10
|
dependencies = [
|
|
11
|
+
"aiofiles>=25.1.0",
|
|
11
12
|
"telnetlib3>=4.0.2",
|
|
12
13
|
]
|
|
13
14
|
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
from typing import Optional, Union, Tuple, cast
|
|
2
|
+
from datacomshell.Auth import Auth, AuthState, UsernamePasswordAuth
|
|
3
|
+
from telnetlib3 import TelnetReader, TelnetReaderUnicode, TelnetWriter, TelnetWriterUnicode, open_connection
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
import asyncio
|
|
6
|
+
import aiofiles
|
|
7
|
+
from aiofiles.threadpool.text import AsyncTextIOWrapper
|
|
8
|
+
import pathlib
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
_DEFAULT_ENDING = '[saven-process-end]'
|
|
13
|
+
# 默认认证(无认证)
|
|
14
|
+
_DEFAILT_AUTH_OBG = Auth()
|
|
15
|
+
|
|
16
|
+
# type
|
|
17
|
+
_READER_TYPE = Union[TelnetReader, TelnetReaderUnicode]
|
|
18
|
+
_WRITER_TYPE = Union[TelnetWriter, TelnetWriterUnicode]
|
|
19
|
+
|
|
20
|
+
_DEFAULT_WAIT_INPUT_TAG_PARTTEN_LIST = [
|
|
21
|
+
re.compile(r'\<.*\>'),
|
|
22
|
+
re.compile(r'\[.*\]'),
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# __reader、__writer等操作对象为 null
|
|
27
|
+
class OperationObjNullError(RuntimeError):
|
|
28
|
+
def __init__(self, message: str) -> None: return super().__init__(message)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ConnectParamError(RuntimeError):
|
|
32
|
+
def __init__(self, message: str) -> None: return super().__init__(message)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class LoginTimoutError(RuntimeError):
|
|
36
|
+
def __init__(self, message: str) -> None: return super().__init__(message)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class LoginTypeError(RuntimeError):
|
|
40
|
+
def __init__(self, message: str) -> None: return super().__init__(message)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ExcCommandTimoutError(RuntimeError):
|
|
44
|
+
def __init__(self, message: str) -> None: return super().__init__(message)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AsyncTelnetClientUtil(object):
|
|
48
|
+
"""
|
|
49
|
+
AsyncTelnetClientUtil
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self,
|
|
53
|
+
host: str = '127.0.0.1',
|
|
54
|
+
port: int = 23,
|
|
55
|
+
auth: Auth = _DEFAILT_AUTH_OBG,
|
|
56
|
+
connect_timeout: float = 10.0,
|
|
57
|
+
login_timeout: float = 10.0,
|
|
58
|
+
exc_command_timeout: float = 10.0,
|
|
59
|
+
log_folder_path: Optional[str] = None,
|
|
60
|
+
log_new_second: float = 1800,
|
|
61
|
+
wait_input_tag_partten_list: list[re.Pattern] = _DEFAULT_WAIT_INPUT_TAG_PARTTEN_LIST,
|
|
62
|
+
) -> None:
|
|
63
|
+
self.__host: str = host
|
|
64
|
+
self.__port: int = port
|
|
65
|
+
self.__connect_timeout: float = connect_timeout
|
|
66
|
+
self.__login_timeout: float = login_timeout
|
|
67
|
+
self.__exc_command_timout: float = exc_command_timeout
|
|
68
|
+
|
|
69
|
+
self.__auth: Auth = auth
|
|
70
|
+
self.__auth_type: AuthState = self.__auth.get_auth_type()
|
|
71
|
+
|
|
72
|
+
self.__reader: _READER_TYPE | None = None
|
|
73
|
+
self.__writer: _WRITER_TYPE | None = None
|
|
74
|
+
|
|
75
|
+
self.__log_folder_path: str | None = log_folder_path
|
|
76
|
+
self.__log_new_second: float = log_new_second
|
|
77
|
+
self.__timestemp: float = datetime.now().timestamp()
|
|
78
|
+
self.__log_file: AsyncTextIOWrapper | None = None
|
|
79
|
+
|
|
80
|
+
self.__is_log = True if log_folder_path != None else False
|
|
81
|
+
|
|
82
|
+
if self.__is_log and self.__log_folder_path != None:
|
|
83
|
+
pathlib.Path(self.__log_folder_path).mkdir(
|
|
84
|
+
parents=True, exist_ok=True)
|
|
85
|
+
|
|
86
|
+
async def __initConnect(self) -> None:
|
|
87
|
+
if self.__auth_type == AuthState.NONE_AUTH:
|
|
88
|
+
res = await self.__none_auth()
|
|
89
|
+
self.__reader = res[0]
|
|
90
|
+
self.__writer = res[1]
|
|
91
|
+
elif self.__auth_type == AuthState.USERNAME_PASSWORD_AUTH:
|
|
92
|
+
res = await self.__username_password_auth()
|
|
93
|
+
self.__reader = res[0]
|
|
94
|
+
self.__writer = res[1]
|
|
95
|
+
else:
|
|
96
|
+
raise LoginTypeError("Auth type error")
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
async def create(cls,
|
|
100
|
+
host: str = '127.0.0.1',
|
|
101
|
+
port: int = 23,
|
|
102
|
+
auth: Auth = _DEFAILT_AUTH_OBG,
|
|
103
|
+
connect_timeout: float = 10.0,
|
|
104
|
+
login_timeout: float = 10.0,
|
|
105
|
+
exc_command_timeout: float = 10.0,
|
|
106
|
+
log_folder_path: Optional[str] = None,
|
|
107
|
+
log_new_second: float = 1800,
|
|
108
|
+
wait_input_tag_partten_list: list[re.Pattern] = _DEFAULT_WAIT_INPUT_TAG_PARTTEN_LIST,) -> 'AsyncTelnetClientUtil':
|
|
109
|
+
|
|
110
|
+
instance: AsyncTelnetClientUtil = cls(
|
|
111
|
+
host=host,
|
|
112
|
+
port=port,
|
|
113
|
+
auth=auth,
|
|
114
|
+
connect_timeout=connect_timeout,
|
|
115
|
+
login_timeout=login_timeout,
|
|
116
|
+
exc_command_timeout=exc_command_timeout,
|
|
117
|
+
log_folder_path=log_folder_path,
|
|
118
|
+
log_new_second=log_new_second,
|
|
119
|
+
wait_input_tag_partten_list=wait_input_tag_partten_list,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
await instance.__initConnect()
|
|
123
|
+
await instance.__create_log_file()
|
|
124
|
+
print(f"""
|
|
125
|
+
[I] connect success: {instance.__host}:{instance.__port}
|
|
126
|
+
[I] login type: {instance.__auth.get_auth_type().name}
|
|
127
|
+
[I] login info: {instance.__auth.to_string()}
|
|
128
|
+
""")
|
|
129
|
+
return instance
|
|
130
|
+
|
|
131
|
+
def __command_wrapper(self, command: str) -> str:
|
|
132
|
+
return command + '\r\n'
|
|
133
|
+
|
|
134
|
+
async def __read_wait_wrapper(self, read) -> bytes:
|
|
135
|
+
return await asyncio.wait_for(read, timeout=self.__exc_command_timout)
|
|
136
|
+
|
|
137
|
+
async def __read_login_wait_wrapper(self, read):
|
|
138
|
+
return await asyncio.wait_for(read, timeout=self.__login_timeout)
|
|
139
|
+
|
|
140
|
+
async def __none_auth(self) -> Tuple[TelnetReader, TelnetWriter]:
|
|
141
|
+
return await open_connection(self.__host, self.__port, connect_timeout=self.__connect_timeout)
|
|
142
|
+
|
|
143
|
+
async def __login_of_username_password(self, reader: _READER_TYPE, writer: _WRITER_TYPE) -> None:
|
|
144
|
+
if reader == None and writer == None:
|
|
145
|
+
raise OperationObjNullError("[IE]: __reader and __writer is None")
|
|
146
|
+
|
|
147
|
+
if self.__auth_type != AuthState.USERNAME_PASSWORD_AUTH:
|
|
148
|
+
raise ConnectParamError(
|
|
149
|
+
"[IE] __auth_type is not AuthState.USERNAME_PASSWORD_AUTH")
|
|
150
|
+
|
|
151
|
+
# 变量类型整理
|
|
152
|
+
_reader = reader
|
|
153
|
+
_writer = cast(TelnetWriterUnicode, writer)
|
|
154
|
+
_auth = cast(UsernamePasswordAuth, self.__auth)
|
|
155
|
+
|
|
156
|
+
buffer = ''
|
|
157
|
+
try:
|
|
158
|
+
res: bytes = b''
|
|
159
|
+
res: bytes = await self.__read_login_wait_wrapper(_reader.readuntil(b'Username:'))
|
|
160
|
+
buffer = buffer + res.decode()
|
|
161
|
+
_writer.write(self.__command_wrapper(_auth.get_username()))
|
|
162
|
+
await _writer.drain()
|
|
163
|
+
|
|
164
|
+
res: bytes = b''
|
|
165
|
+
res: bytes = await self.__read_login_wait_wrapper(_reader.readuntil(b'Password:'))
|
|
166
|
+
buffer = buffer + res.decode()
|
|
167
|
+
_writer.write(self.__command_wrapper(_auth.get_password()))
|
|
168
|
+
await _writer.drain()
|
|
169
|
+
|
|
170
|
+
res: bytes = b''
|
|
171
|
+
res: bytes = await self.__read_login_wait_wrapper(_reader.readuntil(b'>'))
|
|
172
|
+
buffer = buffer + res.decode()
|
|
173
|
+
|
|
174
|
+
# Log
|
|
175
|
+
await self.__log(buffer)
|
|
176
|
+
except asyncio.TimeoutError:
|
|
177
|
+
raise LoginTimoutError('Login timeout')
|
|
178
|
+
|
|
179
|
+
async def __username_password_auth(self) -> Tuple[TelnetReader, TelnetWriter]:
|
|
180
|
+
reader, writer = await open_connection(self.__host, self.__port, connect_timeout=self.__connect_timeout)
|
|
181
|
+
|
|
182
|
+
# 登录
|
|
183
|
+
await self.__login_of_username_password(reader, writer)
|
|
184
|
+
return reader, writer
|
|
185
|
+
|
|
186
|
+
async def __close_log_file(self):
|
|
187
|
+
if self.__log_file != None:
|
|
188
|
+
await self.__log_file.close()
|
|
189
|
+
|
|
190
|
+
async def __create_log_file(self):
|
|
191
|
+
await self.__close_log_file()
|
|
192
|
+
filename = datetime.now().strftime('%Y-%m-%d_%H-%M-%S-%f') + '.log'
|
|
193
|
+
|
|
194
|
+
if self.__log_folder_path == None:
|
|
195
|
+
raise RuntimeError('[IE] log_folder_path error')
|
|
196
|
+
|
|
197
|
+
self.__timestemp = datetime.now().timestamp()
|
|
198
|
+
en_path = self.__log_folder_path + '/' + filename
|
|
199
|
+
self.__log_file = await aiofiles.open(en_path, 'a', encoding='utf-8')
|
|
200
|
+
|
|
201
|
+
await self.__log_file.write(f'================ {filename} ================')
|
|
202
|
+
|
|
203
|
+
async def __log(self, message: str):
|
|
204
|
+
if self.__is_log != True:
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
curren_timestemp: float = datetime.now().timestamp()
|
|
208
|
+
diff = curren_timestemp - self.__timestemp
|
|
209
|
+
|
|
210
|
+
# 新建
|
|
211
|
+
if diff > self.__log_new_second:
|
|
212
|
+
await self.__create_log_file()
|
|
213
|
+
|
|
214
|
+
# 记录
|
|
215
|
+
if self.__log_file == None:
|
|
216
|
+
raise RuntimeError('[IE] log_file is None')
|
|
217
|
+
else:
|
|
218
|
+
await self.__log_file.write(message)
|
|
219
|
+
|
|
220
|
+
async def execute(self, command: str) -> bytes:
|
|
221
|
+
if self.__reader == None and self.__writer == None:
|
|
222
|
+
raise OperationObjNullError("[IE]: __reader and __writer is None")
|
|
223
|
+
|
|
224
|
+
# 变量类型整理
|
|
225
|
+
_reader = cast(_READER_TYPE, self.__reader)
|
|
226
|
+
_writer = cast(TelnetWriterUnicode, self.__writer)
|
|
227
|
+
|
|
228
|
+
_writer.write('\r\n')
|
|
229
|
+
_writer.write(self.__command_wrapper(command))
|
|
230
|
+
_writer.write(_DEFAULT_ENDING)
|
|
231
|
+
await _writer.drain()
|
|
232
|
+
|
|
233
|
+
res = b'null'
|
|
234
|
+
try:
|
|
235
|
+
res = await self.__read_wait_wrapper(_reader.readuntil(_DEFAULT_ENDING.encode('utf-8')))
|
|
236
|
+
await self.__log(res.decode())
|
|
237
|
+
except asyncio.TimeoutError as e:
|
|
238
|
+
print("[I] reader buffer empty")
|
|
239
|
+
|
|
240
|
+
return res
|
|
241
|
+
|
|
242
|
+
async def execute_no_back(self, command: str) -> None:
|
|
243
|
+
await self.execute(command)
|
|
244
|
+
|
|
245
|
+
async def close(self) -> None:
|
|
246
|
+
if self.__reader == None and self.__writer == None:
|
|
247
|
+
raise OperationObjNullError("[IE]: __reader and __writer is None")
|
|
248
|
+
|
|
249
|
+
# 变量类型整理
|
|
250
|
+
_reader = cast(_READER_TYPE, self.__reader)
|
|
251
|
+
_writer = cast(TelnetWriterUnicode, self.__writer)
|
|
252
|
+
|
|
253
|
+
if self.__log_file != None:
|
|
254
|
+
await self.__log_file.close()
|
|
255
|
+
|
|
256
|
+
_reader.close()
|
|
257
|
+
_writer.close()
|
|
258
|
+
|
|
259
|
+
await _writer.wait_closed()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AuthState(Enum):
|
|
5
|
+
NONE_AUTH = 0
|
|
6
|
+
USERNAME_PASSWORD_AUTH = 1
|
|
7
|
+
SECRET_AUTH = 2
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Auth(object):
|
|
11
|
+
def __init__(self, auth: AuthState = AuthState.NONE_AUTH) -> None:
|
|
12
|
+
self.__type = auth
|
|
13
|
+
|
|
14
|
+
def get_auth_type(self) -> AuthState:
|
|
15
|
+
return self.__type
|
|
16
|
+
|
|
17
|
+
def to_string(self) -> str:
|
|
18
|
+
return 'NONE'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class UsernamePasswordAuth(Auth):
|
|
22
|
+
def __init__(self, username: str, password: str) -> None:
|
|
23
|
+
super().__init__(auth=AuthState.USERNAME_PASSWORD_AUTH)
|
|
24
|
+
self.__username = username
|
|
25
|
+
self.__password = password
|
|
26
|
+
|
|
27
|
+
def get_username(self) -> str:
|
|
28
|
+
return self.__username
|
|
29
|
+
|
|
30
|
+
def get_password(self) -> str:
|
|
31
|
+
return self.__password
|
|
32
|
+
|
|
33
|
+
def to_string(self) -> str:
|
|
34
|
+
return f'{self.__username}&{self.__password}'
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class SecretAuth(Auth):
|
|
38
|
+
def __init__(self, secret: str) -> None:
|
|
39
|
+
super().__init__(auth=AuthState.SECRET_AUTH)
|
|
40
|
+
self.__secret = secret
|
|
41
|
+
|
|
42
|
+
def get_secret(self) -> str:
|
|
43
|
+
return self.__secret
|
|
44
|
+
|
|
45
|
+
def to_string(self) -> str:
|
|
46
|
+
return self.__secret
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import re
|
|
3
|
-
from typing import Optional
|
|
4
|
-
import telnetlib3
|
|
5
|
-
from telnetlib3 import TelnetWriter, TelnetReader, TelnetWriterUnicode, TelnetReaderUnicode
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class ConnectError(RuntimeError):
|
|
9
|
-
def __init__(self, message: str) -> None:
|
|
10
|
-
super().__init__(message)
|
|
11
|
-
pass
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class LoginError(RuntimeError):
|
|
15
|
-
def __init__(self, message: str) -> None:
|
|
16
|
-
super().__init__(message)
|
|
17
|
-
pass
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
_CONST_COMMAND_RUNNED_PATTERN = re.compile(
|
|
21
|
-
r'<.*>|\[.*\]|<~.*>|\[~.*\]|<\*.*>|\[\*.*\]')
|
|
22
|
-
_CONST_COMMAND_RUNNED_PATTERN_LIST = [_CONST_COMMAND_RUNNED_PATTERN]
|
|
23
|
-
|
|
24
|
-
_CONST_COMMAND_ERROR_PATTERN = re.compile(r'%.*')
|
|
25
|
-
_CONST_COMMAND_ERROR_PATTERN_LIST = [_CONST_COMMAND_ERROR_PATTERN]
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class AsyncTelnetUtil(object):
|
|
29
|
-
def __init__(self,
|
|
30
|
-
host: str,
|
|
31
|
-
username: str,
|
|
32
|
-
password: str,
|
|
33
|
-
port: int = 23,
|
|
34
|
-
connect_timeout: Optional[float] = None
|
|
35
|
-
) -> None:
|
|
36
|
-
# 连接基本信息
|
|
37
|
-
self.__host = host
|
|
38
|
-
self.__port = port
|
|
39
|
-
self.__connect_timeout = connect_timeout
|
|
40
|
-
|
|
41
|
-
self.__username = username
|
|
42
|
-
self.__password = password
|
|
43
|
-
|
|
44
|
-
self.__login_status = False
|
|
45
|
-
self.__reader: TelnetReader | TelnetReaderUnicode | None = None
|
|
46
|
-
self.__writer: TelnetWriter | TelnetWriterUnicode | None = None
|
|
47
|
-
|
|
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
|
|
76
|
-
|
|
77
|
-
return False
|
|
78
|
-
|
|
79
|
-
# 读取命令返回数据
|
|
80
|
-
async def __read(self,
|
|
81
|
-
pattern_list: Optional[list[re.Pattern]] = None,
|
|
82
|
-
error_pattern_list: Optional[list[re.Pattern]] = None,
|
|
83
|
-
timeout: Optional[float] = 10.0,
|
|
84
|
-
size: int = 1024
|
|
85
|
-
) -> str | None:
|
|
86
|
-
buffer = ''
|
|
87
|
-
runStatus = True
|
|
88
|
-
|
|
89
|
-
if self.__reader is None or self.__writer is None:
|
|
90
|
-
raise ConnectError("Not connected to device")
|
|
91
|
-
|
|
92
|
-
while runStatus:
|
|
93
|
-
try:
|
|
94
|
-
res = await asyncio.wait_for(self.__reader.read(size), timeout)
|
|
95
|
-
except asyncio.TimeoutError:
|
|
96
|
-
break
|
|
97
|
-
|
|
98
|
-
if not res:
|
|
99
|
-
break
|
|
100
|
-
|
|
101
|
-
if res is bytes:
|
|
102
|
-
buffer += res.decode('utf-8')
|
|
103
|
-
elif res is str:
|
|
104
|
-
buffer += res
|
|
105
|
-
|
|
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)
|
|
114
|
-
|
|
115
|
-
if __temp_match_status:
|
|
116
|
-
runStatus = False
|
|
117
|
-
|
|
118
|
-
return buffer if runStatus else None
|
|
119
|
-
|
|
120
|
-
# 写入命令
|
|
121
|
-
def __write(self, command: str) -> None:
|
|
122
|
-
if self.__writer is None:
|
|
123
|
-
raise ConnectError("Not connected to device")
|
|
124
|
-
|
|
125
|
-
in_command = command + "\r\n"
|
|
126
|
-
|
|
127
|
-
# 根据 writer 类型决定使用字符串还是字节
|
|
128
|
-
if isinstance(self.__writer, TelnetWriterUnicode):
|
|
129
|
-
self.__writer.write(in_command)
|
|
130
|
-
else:
|
|
131
|
-
self.__writer.write(in_command.encode('utf-8'))
|
|
132
|
-
|
|
133
|
-
# 建立连接
|
|
134
|
-
async def connect(self) -> None:
|
|
135
|
-
try:
|
|
136
|
-
self.__reader, self.__writer = await telnetlib3.open_connection(
|
|
137
|
-
host=self.__host, port=self.__port, connect_timeout=self.__connect_timeout)
|
|
138
|
-
except asyncio.TimeoutError:
|
|
139
|
-
raise TimeoutError(
|
|
140
|
-
f"Connection to {self.__host}:{self.__port} timed out after {self.__connect_timeout} seconds")
|
|
141
|
-
|
|
142
|
-
if self.__reader is None or self.__writer is None:
|
|
143
|
-
raise ConnectionError("Failed to connect to device")
|
|
144
|
-
|
|
145
|
-
return
|
|
146
|
-
|
|
147
|
-
# 建立登录
|
|
148
|
-
async def login(self) -> None:
|
|
149
|
-
if self.__login_status:
|
|
150
|
-
return
|
|
151
|
-
|
|
152
|
-
if await self.__read(pattern_list=[re.compile(r"Username:")]) is not None:
|
|
153
|
-
self.__write(self.__username)
|
|
154
|
-
else:
|
|
155
|
-
raise LoginError("Failed to connect to device")
|
|
156
|
-
|
|
157
|
-
if await self.__read(pattern_list=[re.compile(r"Password:")]) is not None:
|
|
158
|
-
self.__write(self.__password)
|
|
159
|
-
else:
|
|
160
|
-
raise LoginError("Failed to connect to device")
|
|
161
|
-
|
|
162
|
-
if await self.__read(pattern_list=self.__DEFAULT_CONST_COMMAND_RUNNED_PATTERN_LIST) is not None:
|
|
163
|
-
self.__login_status = True
|
|
164
|
-
return
|
|
165
|
-
|
|
166
|
-
async def send_command(self, command: str):
|
|
167
|
-
self.__write(command)
|
|
168
|
-
return await self.__read()
|
|
169
|
-
|
|
170
|
-
def only_send_command(self, command: str) -> None:
|
|
171
|
-
self.__write(command)
|
|
172
|
-
|
|
173
|
-
async def build(self) -> None:
|
|
174
|
-
await self.connect()
|
|
175
|
-
await self.login()
|
|
@@ -1,39 +0,0 @@
|
|
|
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
|