datacomshell 0.1.2__tar.gz → 0.26.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: datacomshell
3
- Version: 0.1.2
3
+ Version: 0.26.1
4
4
  Summary: Add your description here
5
5
  Author: Saven
6
6
  Author-email: Saven <2416844857@qq.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "datacomshell"
3
- version = "0.1.2"
3
+ version = "0.26.1"
4
4
  description = "Add your description here"
5
5
  authors = [
6
6
  { name = "Saven", email = "2416844857@qq.com" },
@@ -0,0 +1,188 @@
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
+ import asyncio
5
+ import re
6
+
7
+ _DEFAULT_ENDING = '[saven-process-end]'
8
+ # 默认认证(无认证)
9
+ _DEFAILT_AUTH_OBG = Auth()
10
+
11
+ # type
12
+ _READER_TYPE = Union[TelnetReader, TelnetReaderUnicode]
13
+ _WRITER_TYPE = Union[TelnetWriter, TelnetWriterUnicode]
14
+
15
+ _DEFAULT_WAIT_INPUT_TAG_PARTTEN_LIST = [
16
+ re.compile(r'\<.*\>'),
17
+ re.compile(r'\[.*\]'),
18
+ ]
19
+
20
+
21
+ # __reader、__writer等操作对象为 null
22
+ class OperationObjNullError(RuntimeError):
23
+ def __init__(self, message: str) -> None: return super().__init__(message)
24
+
25
+
26
+ class ConnectParamError(RuntimeError):
27
+ def __init__(self, message: str) -> None: return super().__init__(message)
28
+
29
+
30
+ class LoginTimoutError(RuntimeError):
31
+ def __init__(self, message: str) -> None: return super().__init__(message)
32
+
33
+
34
+ class LoginTypeError(RuntimeError):
35
+ def __init__(self, message: str) -> None: return super().__init__(message)
36
+
37
+
38
+ class ExcCommandTimoutError(RuntimeError):
39
+ def __init__(self, message: str) -> None: return super().__init__(message)
40
+
41
+
42
+ class AsyncTelnetClientUtil(object):
43
+ """
44
+ AsyncTelnetClientUtil
45
+ """
46
+
47
+ def __init__(self,
48
+ host: str = '127.0.0.1',
49
+ port: int = 23,
50
+ auth: Auth = _DEFAILT_AUTH_OBG,
51
+ connect_timeout: float = 10.0,
52
+ login_timeout: float = 10.0,
53
+ exc_command_timeout: float = 10.0,
54
+ wait_input_tag_partten_list: list[re.Pattern] = _DEFAULT_WAIT_INPUT_TAG_PARTTEN_LIST,
55
+ ) -> None:
56
+ self.__host: str = host
57
+ self.__port: int = port
58
+ self.__connect_timeout: float = connect_timeout
59
+ self.__login_timeout: float = login_timeout
60
+ self.__exc_command_timout: float = exc_command_timeout
61
+
62
+ self.__auth: Auth = auth
63
+ self.__auth_type: AuthState = self.__auth.get_auth_type()
64
+
65
+ self.__reader: _READER_TYPE | None = None
66
+ self.__writer: _WRITER_TYPE | None = None
67
+
68
+ async def __initConnect(self) -> None:
69
+ if self.__auth_type == AuthState.NONE_AUTH:
70
+ res = await self.__none_auth()
71
+ self.__reader = res[0]
72
+ self.__writer = res[1]
73
+ elif self.__auth_type == AuthState.USERNAME_PASSWORD_AUTH:
74
+ res = await self.__username_password_auth()
75
+ self.__reader = res[0]
76
+ self.__writer = res[1]
77
+ else:
78
+ raise LoginTypeError("Auth type error")
79
+
80
+ @classmethod
81
+ async def create(cls,
82
+ host: str = '127.0.0.1',
83
+ port: int = 23,
84
+ auth: Auth = _DEFAILT_AUTH_OBG,
85
+ connect_timeout: float = 10.0,
86
+ login_timeout: float = 10.0,
87
+ exc_command_timeout: float = 10.0,
88
+ wait_input_tag_partten_list: list[re.Pattern] = _DEFAULT_WAIT_INPUT_TAG_PARTTEN_LIST,) -> 'AsyncTelnetClientUtil':
89
+
90
+ instance: AsyncTelnetClientUtil = cls(
91
+ host=host,
92
+ port=port,
93
+ auth=auth,
94
+ connect_timeout=connect_timeout,
95
+ login_timeout=login_timeout,
96
+ exc_command_timeout=exc_command_timeout,
97
+ wait_input_tag_partten_list=wait_input_tag_partten_list,
98
+ )
99
+
100
+ await instance.__initConnect()
101
+ print(f"""
102
+ [I] connect success: {instance.__host}:{instance.__port}
103
+ [I] login type: {instance.__auth.get_auth_type().name}
104
+ [I] login info: {instance.__auth.to_string()}
105
+ """)
106
+ return instance
107
+
108
+ def __command_wrapper(self, command: str) -> str:
109
+ return command + '\r\n'
110
+
111
+ async def __read_wait_wrapper(self, read) -> bytes:
112
+ return await asyncio.wait_for(read, timeout=self.__exc_command_timout)
113
+
114
+ async def __read_login_wait_wrapper(self, read):
115
+ return await asyncio.wait_for(read, timeout=self.__login_timeout)
116
+
117
+ async def __none_auth(self) -> Tuple[TelnetReader, TelnetWriter]:
118
+ return await open_connection(self.__host, self.__port, connect_timeout=self.__connect_timeout)
119
+
120
+ async def __login_of_username_password(self, reader: _READER_TYPE, writer: _WRITER_TYPE) -> None:
121
+ if reader == None and writer == None:
122
+ raise OperationObjNullError("[IE]: __reader and __writer is None")
123
+
124
+ if self.__auth_type != AuthState.USERNAME_PASSWORD_AUTH:
125
+ raise ConnectParamError(
126
+ "[IE] __auth_type is not AuthState.USERNAME_PASSWORD_AUTH")
127
+
128
+ # 变量类型整理
129
+ _reader = reader
130
+ _writer = cast(TelnetWriterUnicode, writer)
131
+ _auth = cast(UsernamePasswordAuth, self.__auth)
132
+
133
+ try:
134
+ await self.__read_login_wait_wrapper(_reader.readuntil(b'Username:'))
135
+ _writer.write(self.__command_wrapper(_auth.get_username()))
136
+ await _writer.drain()
137
+
138
+ await self.__read_login_wait_wrapper(_reader.readuntil(b'Password:'))
139
+ _writer.write(self.__command_wrapper(_auth.get_password()))
140
+ await _writer.drain()
141
+
142
+ await self.__read_login_wait_wrapper(_reader.readuntil(b'>'))
143
+ except asyncio.TimeoutError:
144
+ raise LoginTimoutError('Login timeout')
145
+
146
+ async def __username_password_auth(self) -> Tuple[TelnetReader, TelnetWriter]:
147
+ reader, writer = await open_connection(self.__host, self.__port, connect_timeout=self.__connect_timeout)
148
+
149
+ # 登录
150
+ await self.__login_of_username_password(reader, writer)
151
+ return reader, writer
152
+
153
+ async def execute(self, command: str) -> bytes:
154
+ if self.__reader == None and self.__writer == None:
155
+ raise OperationObjNullError("[IE]: __reader and __writer is None")
156
+
157
+ # 变量类型整理
158
+ _reader = cast(_READER_TYPE, self.__reader)
159
+ _writer = cast(TelnetWriterUnicode, self.__writer)
160
+
161
+ _writer.write('\r\n')
162
+ _writer.write(self.__command_wrapper(command))
163
+ _writer.write(_DEFAULT_ENDING)
164
+ await _writer.drain()
165
+
166
+ res = b'null'
167
+ try:
168
+ res = await self.__read_wait_wrapper(_reader.readuntil(_DEFAULT_ENDING.encode('utf-8')))
169
+ except asyncio.TimeoutError as e:
170
+ print("[I] reader buffer empty")
171
+
172
+ return res
173
+
174
+ async def execute_no_back(self, command: str) -> None:
175
+ await self.execute(command)
176
+
177
+ async def close(self) -> None:
178
+ if self.__reader == None and self.__writer == None:
179
+ raise OperationObjNullError("[IE]: __reader and __writer is None")
180
+
181
+ # 变量类型整理
182
+ _reader = cast(_READER_TYPE, self.__reader)
183
+ _writer = cast(TelnetWriterUnicode, self.__writer)
184
+
185
+ _reader.close()
186
+ _writer.close()
187
+
188
+ 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,3 +0,0 @@
1
-
2
- class TelnetShellClient(object):
3
- pass
@@ -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