codosdk 1.0.41__tar.gz → 1.0.43__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.
- {codosdk-1.0.41 → codosdk-1.0.43}/PKG-INFO +2 -2
- {codosdk-1.0.41 → codosdk-1.0.43}/codosdk.egg-info/PKG-INFO +2 -2
- {codosdk-1.0.41 → codosdk-1.0.43}/codosdk.egg-info/requires.txt +1 -1
- {codosdk-1.0.41 → codosdk-1.0.43}/setup.py +2 -2
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/mqhelper.py +132 -24
- {codosdk-1.0.41 → codosdk-1.0.43}/LICENSE +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/README.md +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/codosdk.egg-info/SOURCES.txt +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/codosdk.egg-info/dependency_links.txt +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/codosdk.egg-info/top_level.txt +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/opssdk/__init__.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/opssdk/utils/__init__.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/pyproject.toml +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/setup.cfg +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/__init__.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/api_set.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/__init__.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/admin_apis.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/agent_apis.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/cmdb_apis.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/kerrigan_apis.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/mgv4_apis.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/notice_apis.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/task_apis.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/application.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/base_handler.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/cache.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/cache_context.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/client.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/cloud/__init__.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/cloud/qcloud_api.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/cloud/ucloud_api.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/cloud_utils.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/configs.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/consts.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/crud_utils.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/db_context.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/error.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/fetch_coroutine.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/jwt_token.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/ldap.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/logger.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/model_utils.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/program.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/salt_api.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/sqlalchemy_pagination.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/tools.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/utils/__init__.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/utils/cc_crypto.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/utils/date_format.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/utils/pydantic_utils.py +0 -0
- {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/web_logs.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codosdk
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.43
|
|
4
4
|
Summary: CODO项目的Python SDK
|
|
5
5
|
Home-page: https://github.com/ss1917/codo_sdk/
|
|
6
6
|
Author: shenshuo
|
|
@@ -23,7 +23,7 @@ Requires-Dist: fire==0.5.0
|
|
|
23
23
|
Requires-Dist: shortuuid==1.0.11
|
|
24
24
|
Requires-Dist: pymysql==0.9.3
|
|
25
25
|
Requires-Dist: sqlalchemy==1.3.23
|
|
26
|
-
Requires-Dist: pika==1.3.
|
|
26
|
+
Requires-Dist: pika==1.3.2
|
|
27
27
|
Requires-Dist: PyJWT==2.0.1
|
|
28
28
|
Requires-Dist: requests==2.28.2
|
|
29
29
|
Requires-Dist: redis==4.5.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codosdk
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.43
|
|
4
4
|
Summary: CODO项目的Python SDK
|
|
5
5
|
Home-page: https://github.com/ss1917/codo_sdk/
|
|
6
6
|
Author: shenshuo
|
|
@@ -23,7 +23,7 @@ Requires-Dist: fire==0.5.0
|
|
|
23
23
|
Requires-Dist: shortuuid==1.0.11
|
|
24
24
|
Requires-Dist: pymysql==0.9.3
|
|
25
25
|
Requires-Dist: sqlalchemy==1.3.23
|
|
26
|
-
Requires-Dist: pika==1.3.
|
|
26
|
+
Requires-Dist: pika==1.3.2
|
|
27
27
|
Requires-Dist: PyJWT==2.0.1
|
|
28
28
|
Requires-Dist: requests==2.28.2
|
|
29
29
|
Requires-Dist: redis==4.5.1
|
|
@@ -9,7 +9,7 @@ desc : CODO SDK
|
|
|
9
9
|
import sys
|
|
10
10
|
from setuptools import setup, find_packages
|
|
11
11
|
|
|
12
|
-
VERSION = '1.0.
|
|
12
|
+
VERSION = '1.0.43'
|
|
13
13
|
|
|
14
14
|
if sys.version_info < (2, 7) or (3, 0) <= sys.version_info < (3, 6):
|
|
15
15
|
print('This program requires at least Python 2.7 or 3.6 to run.')
|
|
@@ -24,7 +24,7 @@ def get_data_files():
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def get_install_requires():
|
|
27
|
-
requires = ['fire==0.5.0', 'shortuuid==1.0.11', 'pymysql==0.9.3', 'sqlalchemy==1.3.23', 'pika==1.3.
|
|
27
|
+
requires = ['fire==0.5.0', 'shortuuid==1.0.11', 'pymysql==0.9.3', 'sqlalchemy==1.3.23', 'pika==1.3.2',
|
|
28
28
|
'PyJWT==2.0.1', 'requests==2.28.2', 'redis==4.5.1', 'tornado>=6.0', 'loguru>=0.6.0',
|
|
29
29
|
'cryptography==42.0.4', 'ldap3==2.9', 'pydantic>=1.10.5']
|
|
30
30
|
return requires
|
|
@@ -148,22 +148,31 @@ class RabbitMQConnectionPool:
|
|
|
148
148
|
mq_config = configs[const.MQ_CONFIG_ITEM][mq_key]
|
|
149
149
|
cls._validate_config(mq_config)
|
|
150
150
|
|
|
151
|
-
#
|
|
151
|
+
# 创建连接参数,优化超时和重试设置
|
|
152
152
|
credentials = pika.PlainCredentials(
|
|
153
153
|
mq_config[const.MQ_USER],
|
|
154
154
|
mq_config[const.MQ_PWD]
|
|
155
155
|
)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
156
|
+
|
|
157
|
+
connection_params = pika.ConnectionParameters(
|
|
158
|
+
host=mq_config[const.MQ_ADDR],
|
|
159
|
+
port=int(mq_config[const.MQ_PORT]),
|
|
160
|
+
virtual_host=mq_config[const.MQ_VHOST],
|
|
161
|
+
credentials=credentials,
|
|
162
|
+
heartbeat=300, # 减少心跳间隔
|
|
163
|
+
blocked_connection_timeout=60, # 减少阻塞超时
|
|
164
|
+
socket_timeout=30, # 添加socket超时
|
|
165
|
+
connection_attempts=3, # 连接重试次数
|
|
166
|
+
retry_delay=1, # 重试延迟
|
|
167
|
+
stack_timeout=30 # 栈超时
|
|
165
168
|
)
|
|
166
169
|
|
|
170
|
+
connection = pika.BlockingConnection(connection_params)
|
|
171
|
+
|
|
172
|
+
# 验证连接状态
|
|
173
|
+
if not connection.is_open:
|
|
174
|
+
raise Exception("Connection failed to open properly")
|
|
175
|
+
|
|
167
176
|
cls._connections[mq_key] = connection
|
|
168
177
|
logging.info(f"Created new RabbitMQ connection for key: {mq_key}")
|
|
169
178
|
|
|
@@ -255,12 +264,86 @@ class MessageQueueBase:
|
|
|
255
264
|
)
|
|
256
265
|
|
|
257
266
|
def _get_channel(self) -> Any:
|
|
258
|
-
"""获取新的通道"""
|
|
259
|
-
|
|
260
|
-
|
|
267
|
+
"""获取新的通道 - 使用更安全的方式避免pika并发Bug"""
|
|
268
|
+
max_attempts = 2 # 减少重试次数避免过度重试
|
|
269
|
+
|
|
270
|
+
for attempt in range(max_attempts):
|
|
271
|
+
try:
|
|
272
|
+
# 每次都创建新连接,避免连接复用导致的并发问题
|
|
273
|
+
if attempt > 0:
|
|
274
|
+
# 强制清理并等待
|
|
275
|
+
with RabbitMQConnectionPool._connection_lock:
|
|
276
|
+
try:
|
|
277
|
+
if self._mq_key in RabbitMQConnectionPool._connections:
|
|
278
|
+
RabbitMQConnectionPool._cleanup_connection(self._mq_key)
|
|
279
|
+
RabbitMQConnectionPool._connections[self._mq_key] = None
|
|
280
|
+
except:
|
|
281
|
+
pass
|
|
282
|
+
|
|
283
|
+
# 等待稍长时间让连接完全清理
|
|
284
|
+
time.sleep(0.5)
|
|
285
|
+
|
|
286
|
+
connection = RabbitMQConnectionPool.get_connection(self._mq_key)
|
|
287
|
+
if not connection:
|
|
288
|
+
raise Exception("Failed to get connection")
|
|
289
|
+
|
|
290
|
+
# 简化状态检查,避免触发pika内部Bug
|
|
291
|
+
if hasattr(connection, 'is_open') and not connection.is_open:
|
|
292
|
+
raise Exception("Connection is not open")
|
|
293
|
+
|
|
294
|
+
# 直接创建channel,不做额外的心跳检查
|
|
295
|
+
channel = connection.channel()
|
|
296
|
+
if not channel:
|
|
297
|
+
raise Exception("Failed to create channel")
|
|
298
|
+
|
|
299
|
+
if hasattr(channel, 'is_open') and not channel.is_open:
|
|
300
|
+
raise Exception("Channel is not open")
|
|
301
|
+
|
|
302
|
+
return channel
|
|
303
|
+
|
|
304
|
+
except Exception as e:
|
|
305
|
+
self.logger.warning(f"Failed to get channel (attempt {attempt + 1}/{max_attempts}): {e}")
|
|
306
|
+
if attempt == max_attempts - 1:
|
|
307
|
+
# 最后一次失败,创建一个临时连接
|
|
308
|
+
try:
|
|
309
|
+
return self._create_emergency_channel()
|
|
310
|
+
except Exception as emergency_e:
|
|
311
|
+
raise Exception(f"All channel attempts failed. Last error: {e}, Emergency error: {emergency_e}")
|
|
312
|
+
|
|
313
|
+
raise Exception("Unexpected error in channel creation")
|
|
314
|
+
|
|
315
|
+
def _create_emergency_channel(self) -> Any:
|
|
316
|
+
"""创建紧急通道 - 直接连接,不使用连接池"""
|
|
317
|
+
try:
|
|
318
|
+
mq_config = configs[const.MQ_CONFIG_ITEM][self._mq_key]
|
|
319
|
+
|
|
320
|
+
credentials = pika.PlainCredentials(
|
|
321
|
+
mq_config[const.MQ_USER],
|
|
322
|
+
mq_config[const.MQ_PWD]
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
# 使用最简单的连接参数
|
|
326
|
+
connection_params = pika.ConnectionParameters(
|
|
327
|
+
host=mq_config[const.MQ_ADDR],
|
|
328
|
+
port=int(mq_config[const.MQ_PORT]),
|
|
329
|
+
virtual_host=mq_config[const.MQ_VHOST],
|
|
330
|
+
credentials=credentials,
|
|
331
|
+
heartbeat=0, # 禁用心跳
|
|
332
|
+
socket_timeout=10
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
connection = pika.BlockingConnection(connection_params)
|
|
336
|
+
channel = connection.channel()
|
|
337
|
+
|
|
338
|
+
self.logger.info("Created emergency channel")
|
|
339
|
+
return channel
|
|
340
|
+
|
|
341
|
+
except Exception as e:
|
|
342
|
+
self.logger.error(f"Failed to create emergency channel: {e}")
|
|
343
|
+
raise
|
|
261
344
|
|
|
262
345
|
def _with_retry(self, operation: Callable, *args, **kwargs) -> Any:
|
|
263
|
-
"""带重试机制执行操作"""
|
|
346
|
+
"""带重试机制执行操作 - 优化版本"""
|
|
264
347
|
last_exception = None
|
|
265
348
|
|
|
266
349
|
for attempt in range(self._max_retries + 1):
|
|
@@ -273,8 +356,24 @@ class MessageQueueBase:
|
|
|
273
356
|
break
|
|
274
357
|
else:
|
|
275
358
|
self.logger.warning(f"Attempt {attempt + 1} failed: {e}. Retrying...")
|
|
276
|
-
|
|
277
|
-
|
|
359
|
+
|
|
360
|
+
# 只在特定错误类型时清理连接
|
|
361
|
+
error_str = str(e).lower()
|
|
362
|
+
if any(keyword in error_str for keyword in [
|
|
363
|
+
'connection', 'channel', 'socket', 'transport',
|
|
364
|
+
'callback', 'deque', 'abort', 'closed', 'asynctransport'
|
|
365
|
+
]):
|
|
366
|
+
# 安全地清理连接
|
|
367
|
+
with RabbitMQConnectionPool._connection_lock:
|
|
368
|
+
try:
|
|
369
|
+
if self._mq_key in RabbitMQConnectionPool._connections:
|
|
370
|
+
RabbitMQConnectionPool._cleanup_connection(self._mq_key)
|
|
371
|
+
RabbitMQConnectionPool._connections[self._mq_key] = None
|
|
372
|
+
except Exception as cleanup_e:
|
|
373
|
+
self.logger.debug(f"Error during connection cleanup: {cleanup_e}")
|
|
374
|
+
|
|
375
|
+
# 递增延迟重试,但不要太长
|
|
376
|
+
time.sleep(min(0.1 * (2 ** attempt), 2.0))
|
|
278
377
|
|
|
279
378
|
raise last_exception
|
|
280
379
|
|
|
@@ -302,7 +401,7 @@ class MessageQueueBase:
|
|
|
302
401
|
|
|
303
402
|
# 如果有队列名,声明并绑定队列
|
|
304
403
|
if self._queue_name:
|
|
305
|
-
channel.queue_declare(queue=self._queue_name)
|
|
404
|
+
channel.queue_declare(queue=self._queue_name, durable=True)
|
|
306
405
|
channel.queue_bind(
|
|
307
406
|
exchange=self._exchange,
|
|
308
407
|
queue=self._queue_name,
|
|
@@ -322,8 +421,11 @@ class MessageQueueBase:
|
|
|
322
421
|
f'Published message to exchange:{self._exchange}, routing_key:{actual_routing_key}')
|
|
323
422
|
|
|
324
423
|
finally:
|
|
325
|
-
if channel and channel.is_open:
|
|
326
|
-
|
|
424
|
+
if channel and hasattr(channel, 'is_open') and channel.is_open:
|
|
425
|
+
try:
|
|
426
|
+
channel.close()
|
|
427
|
+
except:
|
|
428
|
+
pass # 忽略关闭时的错误
|
|
327
429
|
|
|
328
430
|
self._with_retry(_publish_operation)
|
|
329
431
|
|
|
@@ -373,11 +475,17 @@ class MessageQueueBase:
|
|
|
373
475
|
|
|
374
476
|
except KeyboardInterrupt:
|
|
375
477
|
self.logger.info("Received interrupt signal, stopping consumption")
|
|
376
|
-
if channel and channel.is_open:
|
|
377
|
-
|
|
478
|
+
if channel and hasattr(channel, 'is_open') and channel.is_open:
|
|
479
|
+
try:
|
|
480
|
+
channel.stop_consuming()
|
|
481
|
+
except:
|
|
482
|
+
pass
|
|
378
483
|
finally:
|
|
379
|
-
if channel and channel.is_open:
|
|
380
|
-
|
|
484
|
+
if channel and hasattr(channel, 'is_open') and channel.is_open:
|
|
485
|
+
try:
|
|
486
|
+
channel.close()
|
|
487
|
+
except:
|
|
488
|
+
pass
|
|
381
489
|
|
|
382
490
|
self._with_retry(_consume_operation)
|
|
383
491
|
|
|
@@ -498,5 +606,5 @@ if __name__ == "__main__":
|
|
|
498
606
|
|
|
499
607
|
# 示例3:向后兼容的使用方式
|
|
500
608
|
print("=== 向后兼容使用 ===")
|
|
501
|
-
with MessageQueueBase('test_exchange', 'direct', routing_key='test.key') as old_mq:
|
|
609
|
+
with MessageQueueBase('test_exchange', 'direct', routing_key='test.key', queue_name='task_test_queue') as old_mq:
|
|
502
610
|
old_mq.publish_message("Hello from old interface!")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|