huaweicloud-encryption-sdk-python 1.2.1__py3-none-any.whl
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.
- examples/file_encryption_example.py +57 -0
- examples/key_capsule_encryption_example.py +240 -0
- examples/kms_discovery_encryption_example.py +56 -0
- examples/local_aes_encryption_example.py +59 -0
- examples/test_rsa.py +42 -0
- examples/test_sm2.py +38 -0
- examples/test_sm4_cbc.py +37 -0
- examples/test_sm4_gcm.py +37 -0
- huaweicloud_encryption_sdk_python-1.2.1.dist-info/METADATA +18 -0
- huaweicloud_encryption_sdk_python-1.2.1.dist-info/RECORD +174 -0
- huaweicloud_encryption_sdk_python-1.2.1.dist-info/WHEEL +5 -0
- huaweicloud_encryption_sdk_python-1.2.1.dist-info/top_level.txt +3 -0
- huaweicloud_encryptionsdk/__init__.py +5 -0
- huaweicloud_encryptionsdk/_apig_sdk/__init__.py +1 -0
- huaweicloud_encryptionsdk/_apig_sdk/signer.py +278 -0
- huaweicloud_encryptionsdk/_apig_sdk/signer_v11.py +70 -0
- huaweicloud_encryptionsdk/_apig_sdk/sm3_hash.py +141 -0
- huaweicloud_encryptionsdk/_lts_sdk/auth/__init__.py +0 -0
- huaweicloud_encryptionsdk/_lts_sdk/auth/signer.py +174 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/__init__.py +0 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/client/__init__.py +0 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/client/consumer_client_adapter.py +59 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/client/http_client.py +211 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/core/__init__.py +0 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/core/client_consumer_worker.py +106 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/core/consumer_heartbeat.py +73 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/core/log_shard_consumer.py +179 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/interface/__init__.py +0 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/interface/consumer_check_point_tracker.py +120 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/interface/consumer_processor.py +18 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/interface/consumer_processor_factory.py +3 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/model/__init__.py +0 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/model/check_point.py +20 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/model/consumer_config.py +27 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/model/log_data.py +18 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/model/task_result.py +40 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/task/__init__.py +0 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/task/consumer_fetch_task.py +53 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/task/initial_task.py +55 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/task/process_task.py +25 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer/task/shutdown_task.py +24 -0
- huaweicloud_encryptionsdk/_lts_sdk/consumer_main.py +88 -0
- huaweicloud_encryptionsdk/_lts_sdk/parallel_main.py +64 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/__init__.py +0 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/callback/__init__.py +0 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/callback/call_back.py +7 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/core/__init__.py +0 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/core/accumulator.py +135 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/core/io_thread_pool.py +107 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/core/io_worker.py +124 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/core/mover.py +69 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/core/producer.py +98 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/lts_http/__init__.py +0 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/lts_http/http_client.py +63 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/lts_http/simple_request.py +14 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/model/__init__.py +0 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/model/config.py +50 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/model/error.py +18 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/model/log.py +87 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/model/log_batch.py +154 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/model/lts_store.py +21 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/model/result.py +17 -0
- huaweicloud_encryptionsdk/_lts_sdk/producer/model/retry_queue.py +39 -0
- huaweicloud_encryptionsdk/_lts_sdk/serial_main.py +77 -0
- huaweicloud_encryptionsdk/_lts_sdk/utils/__init__.py +0 -0
- huaweicloud_encryptionsdk/_lts_sdk/utils/common.py +57 -0
- huaweicloud_encryptionsdk/_lts_sdk/utils/lts_threading.py +32 -0
- huaweicloud_encryptionsdk/cache/__init__.py +0 -0
- huaweicloud_encryptionsdk/cache/data_key_cache.py +79 -0
- huaweicloud_encryptionsdk/cache/local_data_key_cache.py +161 -0
- huaweicloud_encryptionsdk/common/__init__.py +0 -0
- huaweicloud_encryptionsdk/common/constants.py +15 -0
- huaweicloud_encryptionsdk/common/counting_output_stream.py +18 -0
- huaweicloud_encryptionsdk/common/crypto_factory.py +48 -0
- huaweicloud_encryptionsdk/common/ec_private_key_loader.py +43 -0
- huaweicloud_encryptionsdk/common/ecs_metadata_service.py +42 -0
- huaweicloud_encryptionsdk/common/file_path_for_example_constants.py +4 -0
- huaweicloud_encryptionsdk/common/utils.py +162 -0
- huaweicloud_encryptionsdk/exception/__init__.py +0 -0
- huaweicloud_encryptionsdk/exception/errors.py +109 -0
- huaweicloud_encryptionsdk/handler/__init__.py +0 -0
- huaweicloud_encryptionsdk/handler/cipher_handler.py +294 -0
- huaweicloud_encryptionsdk/handler/crypto_handler.py +24 -0
- huaweicloud_encryptionsdk/handler/default_crypto_handler.py +148 -0
- huaweicloud_encryptionsdk/handler/default_serialize_handler.py +45 -0
- huaweicloud_encryptionsdk/handler/file_decrypt_handler.py +101 -0
- huaweicloud_encryptionsdk/handler/file_encrypt_handler.py +30 -0
- huaweicloud_encryptionsdk/handler/serialize_handler.py +14 -0
- huaweicloud_encryptionsdk/huawei_config.py +49 -0
- huaweicloud_encryptionsdk/huawei_crypto.py +311 -0
- huaweicloud_encryptionsdk/keyrings/__init__.py +0 -0
- huaweicloud_encryptionsdk/keyrings/keyring.py +14 -0
- huaweicloud_encryptionsdk/keyrings/keyring_factory.py +10 -0
- huaweicloud_encryptionsdk/keyrings/kms_keyring_factory.py +21 -0
- huaweicloud_encryptionsdk/keyrings/kmskeyring/__init__.py +0 -0
- huaweicloud_encryptionsdk/keyrings/kmskeyring/kms_discovery_keyring.py +51 -0
- huaweicloud_encryptionsdk/keyrings/kmskeyring/kms_key_capsule_keyring.py +100 -0
- huaweicloud_encryptionsdk/keyrings/kmskeyring/kms_keyring.py +155 -0
- huaweicloud_encryptionsdk/keyrings/kmskeyring/kms_multi_region_keyring.py +8 -0
- huaweicloud_encryptionsdk/keyrings/raw_keyring_factory.py +27 -0
- huaweicloud_encryptionsdk/keyrings/rawkeyring/__init__.py +0 -0
- huaweicloud_encryptionsdk/keyrings/rawkeyring/raw_aes_keyring.py +63 -0
- huaweicloud_encryptionsdk/keyrings/rawkeyring/raw_keyring.py +112 -0
- huaweicloud_encryptionsdk/keyrings/rawkeyring/raw_rsa_keyring.py +55 -0
- huaweicloud_encryptionsdk/keyrings/rawkeyring/raw_sm2_keyring.py +75 -0
- huaweicloud_encryptionsdk/keyrings/rawkeyring/raw_sm4_cbc_keyring.py +66 -0
- huaweicloud_encryptionsdk/keyrings/rawkeyring/raw_sm4_keyring.py +70 -0
- huaweicloud_encryptionsdk/kms/__init__.py +0 -0
- huaweicloud_encryptionsdk/kms/create_key_capsule_request.py +35 -0
- huaweicloud_encryptionsdk/kms/create_key_capsule_response.py +19 -0
- huaweicloud_encryptionsdk/kms/decrypt_key_capsule_request.py +32 -0
- huaweicloud_encryptionsdk/kms/decrypt_key_capsule_response.py +19 -0
- huaweicloud_encryptionsdk/kms/kms_http_client.py +116 -0
- huaweicloud_encryptionsdk/log/__init__.py +0 -0
- huaweicloud_encryptionsdk/log/log_config.py +30 -0
- huaweicloud_encryptionsdk/log/log_file_backup.py +144 -0
- huaweicloud_encryptionsdk/log/log_metrics.py +132 -0
- huaweicloud_encryptionsdk/log/log_reporter_manager.py +163 -0
- huaweicloud_encryptionsdk/log/lts_sdk_producer.py +386 -0
- huaweicloud_encryptionsdk/log/lts_struct_log_builder.py +106 -0
- huaweicloud_encryptionsdk/meterialmanager/__init__.py +0 -0
- huaweicloud_encryptionsdk/meterialmanager/cache_crypto_meterial_manager.py +221 -0
- huaweicloud_encryptionsdk/meterialmanager/crypto_meterial_manager.py +19 -0
- huaweicloud_encryptionsdk/meterialmanager/data_key_generate.py +10 -0
- huaweicloud_encryptionsdk/meterialmanager/data_key_generate_factory.py +20 -0
- huaweicloud_encryptionsdk/meterialmanager/default_crypto_meterials_manager.py +71 -0
- huaweicloud_encryptionsdk/meterialmanager/key_capsule_data_key_generate.py +63 -0
- huaweicloud_encryptionsdk/meterialmanager/kms_data_key_generate.py +65 -0
- huaweicloud_encryptionsdk/meterialmanager/local_data_key_generate.py +13 -0
- huaweicloud_encryptionsdk/model/__init__.py +0 -0
- huaweicloud_encryptionsdk/model/access_point_config.py +9 -0
- huaweicloud_encryptionsdk/model/cipher_body.py +81 -0
- huaweicloud_encryptionsdk/model/cipher_header.py +405 -0
- huaweicloud_encryptionsdk/model/ciphertext_data_key.py +166 -0
- huaweicloud_encryptionsdk/model/crypto_result.py +11 -0
- huaweicloud_encryptionsdk/model/data_key_materials.py +17 -0
- huaweicloud_encryptionsdk/model/data_materials.py +11 -0
- huaweicloud_encryptionsdk/model/enums/__init__.py +0 -0
- huaweicloud_encryptionsdk/model/enums/access_point_type.py +28 -0
- huaweicloud_encryptionsdk/model/enums/crypto_algorithm.py +75 -0
- huaweicloud_encryptionsdk/model/enums/data_key_generate_type.py +20 -0
- huaweicloud_encryptionsdk/model/enums/key_provider_enum.py +19 -0
- huaweicloud_encryptionsdk/model/enums/keyring_type_enum.py +27 -0
- huaweicloud_encryptionsdk/model/key_capsule.py +10 -0
- huaweicloud_encryptionsdk/model/key_capsule_config.py +12 -0
- huaweicloud_encryptionsdk/model/kms_config.py +10 -0
- huaweicloud_encryptionsdk/model/request/__init__.py +0 -0
- huaweicloud_encryptionsdk/model/request/encrypt_request.py +10 -0
- huaweicloud_encryptionsdk/model/request/sign_request.py +2 -0
- huaweicloud_encryptionsdk/model/request/verify_request.py +2 -0
- huaweicloud_encryptionsdk/proof/__init__.py +0 -0
- huaweicloud_encryptionsdk/proof/attestation_document.py +36 -0
- huaweicloud_encryptionsdk/proof/collector/__init__.py +0 -0
- huaweicloud_encryptionsdk/proof/collector/cce_proof_collector.py +24 -0
- huaweicloud_encryptionsdk/proof/collector/ecs_proof_collector.py +11 -0
- huaweicloud_encryptionsdk/proof/collector/key_pair_proof_collector.py +55 -0
- huaweicloud_encryptionsdk/proof/collector/ma_proof_collector.py +51 -0
- huaweicloud_encryptionsdk/proof/private_key_decrypt_callback.py +7 -0
- huaweicloud_encryptionsdk/proof/proof_collector.py +11 -0
- huaweicloud_encryptionsdk/proof/proof_collector_factory.py +53 -0
- huaweicloud_encryptionsdk/proof/proof_info.py +10 -0
- huaweicloud_encryptionsdk/util/__init__.py +0 -0
- huaweicloud_encryptionsdk/util/common_utils.py +12 -0
- tests/__init__.py +0 -0
- tests/test_enums.py +84 -0
- tests/test_exceptions.py +38 -0
- tests/test_factories.py +71 -0
- tests/test_huawei_crypto.py +67 -0
- tests/test_imports.py +46 -0
- tests/test_kms_models.py +95 -0
- tests/test_log_file_backup.py +45 -0
- tests/test_lts_struct_log_builder.py +97 -0
- tests/test_models.py +142 -0
- tests/test_utils.py +111 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import re
|
|
3
|
+
import time
|
|
4
|
+
import logging
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
from urllib.parse import urlencode
|
|
7
|
+
|
|
8
|
+
from huaweicloud_encryptionsdk.log.log_config import LogConfig
|
|
9
|
+
from huaweicloud_encryptionsdk.log.log_metrics import LogMetrics
|
|
10
|
+
from huaweicloud_encryptionsdk.log.log_file_backup import LogFileBackup
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
_CONTROL_CHARS_PATTERN = re.compile(r'[\r\n\t\x00-\x1F\x7F-\x9F]')
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def sanitize(input_str: Optional[str]) -> str:
|
|
18
|
+
if input_str is None:
|
|
19
|
+
return ""
|
|
20
|
+
return _CONTROL_CHARS_PATTERN.sub("", input_str)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class _LtsLogCallBack:
|
|
24
|
+
|
|
25
|
+
def __init__(self, producer, logs):
|
|
26
|
+
self._producer = producer
|
|
27
|
+
self._logs = logs
|
|
28
|
+
|
|
29
|
+
def invoke_on_success(self, result):
|
|
30
|
+
try:
|
|
31
|
+
self._producer._handle_success(self._logs)
|
|
32
|
+
except Exception as e:
|
|
33
|
+
logger.error("Error in invoke_on_success callback: %s", str(e))
|
|
34
|
+
|
|
35
|
+
def invoke_on_failed(self, result):
|
|
36
|
+
last_attempt = result.attempt_list[-1] if result.attempt_list else None
|
|
37
|
+
if last_attempt:
|
|
38
|
+
error_message = "ErrorCode: {}, ErrorMessage: {}".format(
|
|
39
|
+
last_attempt.error_code, last_attempt.error_msg)
|
|
40
|
+
logger.error("LTS log report failed, httpCode=%s, %s",
|
|
41
|
+
last_attempt.http_code, error_message)
|
|
42
|
+
else:
|
|
43
|
+
error_message = "LTS log report failed, no attempt info available"
|
|
44
|
+
logger.error(error_message)
|
|
45
|
+
try:
|
|
46
|
+
self._producer._handle_failure(self._logs, error_message)
|
|
47
|
+
except Exception as e:
|
|
48
|
+
logger.error("Error in invoke_on_failed callback: %s", str(e))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class LTSSDKProducer:
|
|
52
|
+
|
|
53
|
+
def __init__(self, ak: str, sk: str, project_id: str, region: str, log_config: LogConfig, security_token: str = ""):
|
|
54
|
+
self._ak = ak
|
|
55
|
+
self._sk = sk
|
|
56
|
+
self._project_id = project_id
|
|
57
|
+
self._region = region
|
|
58
|
+
self._log_config = log_config
|
|
59
|
+
self._security_token = security_token
|
|
60
|
+
self._log_file_backup = LogFileBackup(log_config.backup_dir, log_config.fail_on_backup_dir_missing)
|
|
61
|
+
self._resolve_log_group_stream_ids()
|
|
62
|
+
self._producer = self._create_producer(ak, sk, project_id, region, log_config)
|
|
63
|
+
self._initialized = True
|
|
64
|
+
self._metrics = LogMetrics()
|
|
65
|
+
self._recover_failed_logs_on_startup()
|
|
66
|
+
|
|
67
|
+
def _resolve_log_group_stream_ids(self) -> None:
|
|
68
|
+
if self._log_config.log_group_id and self._log_config.log_stream_id:
|
|
69
|
+
logger.info("Using logGroupId and logStreamId from config: groupId=%s, streamId=%s",
|
|
70
|
+
self._log_config.log_group_id, self._log_config.log_stream_id)
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
log_group_name = self._log_config.log_group_name
|
|
74
|
+
log_stream_name = self._log_config.log_stream_name
|
|
75
|
+
if not log_group_name or not log_stream_name:
|
|
76
|
+
logger.warning("Both logGroupId/logStreamId and logGroupName/logStreamName are not configured")
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
logger.info("logGroupId or logStreamId is empty, resolving from names: groupName=%s, streamName=%s",
|
|
80
|
+
log_group_name, log_stream_name)
|
|
81
|
+
try:
|
|
82
|
+
group_id, stream_id = self._query_log_group_stream_info_by_name(
|
|
83
|
+
self._project_id, log_group_name, log_stream_name
|
|
84
|
+
)
|
|
85
|
+
if group_id and stream_id:
|
|
86
|
+
self._log_config.log_group_id = group_id
|
|
87
|
+
self._log_config.log_stream_id = stream_id
|
|
88
|
+
logger.info("Resolved logGroupId=%s, logStreamId=%s from names", group_id, stream_id)
|
|
89
|
+
else:
|
|
90
|
+
logger.error("Failed to resolve logGroupId/logStreamId from names, response is empty")
|
|
91
|
+
except Exception as e:
|
|
92
|
+
logger.error("Failed to resolve logGroupId/logStreamId from names: %s", str(e))
|
|
93
|
+
|
|
94
|
+
def _query_log_group_stream_info_by_name(
|
|
95
|
+
self, project_id: str, log_group_name: str, log_stream_name: str
|
|
96
|
+
) -> tuple:
|
|
97
|
+
import requests as req_lib
|
|
98
|
+
from urllib.parse import urlparse
|
|
99
|
+
from huaweicloud_encryptionsdk._lts_sdk.auth.signer import Signer as LtsSigner
|
|
100
|
+
from huaweicloud_encryptionsdk._lts_sdk.producer.lts_http.simple_request import LtsRequest
|
|
101
|
+
|
|
102
|
+
endpoint = self._log_config.lts_endpoint or ""
|
|
103
|
+
if not endpoint:
|
|
104
|
+
raise ValueError("lts_endpoint is required to resolve log group/stream IDs by name")
|
|
105
|
+
|
|
106
|
+
url = "{}/v2/{}/log-group-stream-info".format(endpoint.rstrip("/"), project_id)
|
|
107
|
+
params = {
|
|
108
|
+
"log_group_name": log_group_name,
|
|
109
|
+
"log_stream_name": log_stream_name,
|
|
110
|
+
}
|
|
111
|
+
full_url = "{}?{}".format(url, urlencode(params))
|
|
112
|
+
|
|
113
|
+
parsed = urlparse(full_url)
|
|
114
|
+
host = parsed.netloc
|
|
115
|
+
interface = parsed.path
|
|
116
|
+
query_params = dict(p.split("=", 1) for p in parsed.query.split("&") if "=" in p)
|
|
117
|
+
|
|
118
|
+
lts_request = LtsRequest(
|
|
119
|
+
method="GET",
|
|
120
|
+
host="{}://{}".format(parsed.scheme, host),
|
|
121
|
+
headers={
|
|
122
|
+
"host": host,
|
|
123
|
+
"Content-Type": "application/json",
|
|
124
|
+
},
|
|
125
|
+
body="",
|
|
126
|
+
region=self._region,
|
|
127
|
+
interface=interface,
|
|
128
|
+
ak=self._ak,
|
|
129
|
+
sk=self._sk,
|
|
130
|
+
security_token=self._security_token,
|
|
131
|
+
**query_params,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
signer = LtsSigner()
|
|
135
|
+
signed_req = signer.sign(lts_request)
|
|
136
|
+
|
|
137
|
+
prepared = signed_req.prepare()
|
|
138
|
+
resp = req_lib.request(
|
|
139
|
+
method=prepared.method,
|
|
140
|
+
url=prepared.url,
|
|
141
|
+
headers=prepared.headers,
|
|
142
|
+
data=prepared.body,
|
|
143
|
+
timeout=(10, 10),
|
|
144
|
+
verify=False,
|
|
145
|
+
)
|
|
146
|
+
logger.info("Query log group stream info response: status=%s, body=%s",
|
|
147
|
+
resp.status_code, resp.text[:500])
|
|
148
|
+
|
|
149
|
+
if resp.status_code == 200:
|
|
150
|
+
data = resp.json()
|
|
151
|
+
info_list = data if isinstance(data, list) else data.get("logGroupStreamInfoList", data.get("results", []))
|
|
152
|
+
if info_list and len(info_list) > 0:
|
|
153
|
+
info = info_list[0]
|
|
154
|
+
return info.get("log_group_id", ""), info.get("log_stream_id", "")
|
|
155
|
+
return "", ""
|
|
156
|
+
|
|
157
|
+
def _create_producer(self, ak: str, sk: str, project_id: str, region: str, config: LogConfig):
|
|
158
|
+
try:
|
|
159
|
+
from huaweicloud_encryptionsdk._lts_sdk.producer.core.producer import Producer
|
|
160
|
+
from huaweicloud_encryptionsdk._lts_sdk.producer.model.config import Config
|
|
161
|
+
|
|
162
|
+
lts_config = Config()
|
|
163
|
+
lts_config.access_key = ak
|
|
164
|
+
lts_config.access_secret = sk
|
|
165
|
+
lts_config.region_id = region
|
|
166
|
+
lts_config.project_id = project_id
|
|
167
|
+
lts_config.endpoint = config.lts_endpoint if hasattr(config, 'lts_endpoint') and config.lts_endpoint else ""
|
|
168
|
+
lts_config.security_token = self._security_token
|
|
169
|
+
lts_config.logGroup = config.log_group_id
|
|
170
|
+
lts_config.logStream = config.log_stream_id
|
|
171
|
+
|
|
172
|
+
producer = Producer.init_producer(lts_config)
|
|
173
|
+
producer.start_producer()
|
|
174
|
+
logger.info("LTS Producer created with config:")
|
|
175
|
+
logger.info(" logGroupId: %s", config.log_group_id)
|
|
176
|
+
logger.info(" logStreamId: %s", config.log_stream_id)
|
|
177
|
+
logger.info(" logGroupName: %s", config.log_group_name)
|
|
178
|
+
logger.info(" logStreamName: %s", config.log_stream_name)
|
|
179
|
+
logger.info(" endpoint: %s", config.lts_endpoint)
|
|
180
|
+
logger.info(" region: %s", region)
|
|
181
|
+
logger.info(" backupDir: %s", config.backup_dir)
|
|
182
|
+
return producer
|
|
183
|
+
except Exception as e:
|
|
184
|
+
logger.warning("Failed to create LTS Producer, LTS logging will be disabled: %s", str(e))
|
|
185
|
+
return None
|
|
186
|
+
|
|
187
|
+
def send_struct_log(self, struct_log: dict) -> None:
|
|
188
|
+
if not self._initialized:
|
|
189
|
+
return
|
|
190
|
+
sanitized = self._sanitize_log(struct_log)
|
|
191
|
+
try:
|
|
192
|
+
if self._producer is not None:
|
|
193
|
+
self._do_send(sanitized)
|
|
194
|
+
self._metrics.add_buffer_log_count(1)
|
|
195
|
+
logger.debug("Sent log to buffer, buffer count: %s", self._metrics.buffer_log_count)
|
|
196
|
+
except Exception as e:
|
|
197
|
+
self._handle_exception(e, [sanitized])
|
|
198
|
+
|
|
199
|
+
def send_batch_struct_log(self, struct_logs: List[dict]) -> None:
|
|
200
|
+
if not self._initialized or struct_logs is None or len(struct_logs) == 0:
|
|
201
|
+
return
|
|
202
|
+
sanitized_logs = [self._sanitize_log(log) for log in struct_logs]
|
|
203
|
+
try:
|
|
204
|
+
if self._producer is not None:
|
|
205
|
+
for log_item in sanitized_logs:
|
|
206
|
+
self._do_send(log_item)
|
|
207
|
+
self._metrics.add_buffer_log_count(len(sanitized_logs))
|
|
208
|
+
logger.debug("Sent batch %d logs to buffer, total buffer count: %s",
|
|
209
|
+
len(sanitized_logs), self._metrics.buffer_log_count)
|
|
210
|
+
except Exception as e:
|
|
211
|
+
self._handle_exception(e, sanitized_logs)
|
|
212
|
+
|
|
213
|
+
def _sanitize_log(self, struct_log: dict) -> dict:
|
|
214
|
+
sanitized = {}
|
|
215
|
+
for key, value in struct_log.items():
|
|
216
|
+
if isinstance(value, str):
|
|
217
|
+
sanitized[key] = sanitize(value)
|
|
218
|
+
else:
|
|
219
|
+
sanitized[key] = value
|
|
220
|
+
return sanitized
|
|
221
|
+
|
|
222
|
+
def _do_send(self, struct_log: dict) -> None:
|
|
223
|
+
try:
|
|
224
|
+
from huaweicloud_encryptionsdk._lts_sdk.producer.model import lts_store
|
|
225
|
+
|
|
226
|
+
log_group_id = self._log_config.log_group_id
|
|
227
|
+
log_stream_id = self._log_config.log_stream_id
|
|
228
|
+
|
|
229
|
+
if not log_group_id or not log_stream_id:
|
|
230
|
+
logger.warning("logGroupId or logStreamId is not configured, skipping log send")
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
content = json.dumps(struct_log, ensure_ascii=False)
|
|
234
|
+
labels = {"source": "EncryptionSDK_PYTHON"}
|
|
235
|
+
log_p = lts_store.generate_log([content], labels)
|
|
236
|
+
|
|
237
|
+
self._producer.send_log_with_callback(log_group_id, log_stream_id, log_p, _LtsLogCallBack(self, [struct_log]))
|
|
238
|
+
except Exception as e:
|
|
239
|
+
self._handle_failure([struct_log], sanitize(str(e)))
|
|
240
|
+
|
|
241
|
+
def _handle_success(self, logs: list) -> None:
|
|
242
|
+
self._metrics.last_report_time = int(time.time() * 1000)
|
|
243
|
+
self._metrics.increment_success_count()
|
|
244
|
+
self._metrics.add_success_log_count(len(logs))
|
|
245
|
+
self._metrics.add_buffer_log_count(-len(logs))
|
|
246
|
+
|
|
247
|
+
def _handle_failure(self, logs: list, error_message: str) -> None:
|
|
248
|
+
self._metrics.increment_failure_count()
|
|
249
|
+
self._metrics.add_failure_log_count(len(logs))
|
|
250
|
+
self._metrics.last_error_message = error_message
|
|
251
|
+
self._metrics.last_error_time = int(time.time() * 1000)
|
|
252
|
+
logger.warn("Log report failed: %d logs, error: %s, saving to backup dir", len(logs), error_message)
|
|
253
|
+
try:
|
|
254
|
+
backup_path = self._log_file_backup.save_failed_logs(logs, int(time.time() * 1000))
|
|
255
|
+
if backup_path:
|
|
256
|
+
logger.info("Failed logs backed up to: %s", backup_path)
|
|
257
|
+
else:
|
|
258
|
+
logger.warn("Failed to save failed logs to backup directory")
|
|
259
|
+
except Exception as e:
|
|
260
|
+
logger.error("Error saving failed logs to backup: %s", str(e))
|
|
261
|
+
|
|
262
|
+
def _handle_exception(self, e: Exception, logs: list) -> None:
|
|
263
|
+
error_message = sanitize(str(e))
|
|
264
|
+
self._metrics.increment_failure_count()
|
|
265
|
+
self._metrics.add_failure_log_count(len(logs))
|
|
266
|
+
self._metrics.last_error_message = error_message
|
|
267
|
+
self._metrics.last_error_time = int(time.time() * 1000)
|
|
268
|
+
logger.warn("Log report exception: %d logs, error: %s", len(logs), error_message, exc_info=e)
|
|
269
|
+
try:
|
|
270
|
+
backup_path = self._log_file_backup.save_failed_logs(logs, int(time.time() * 1000))
|
|
271
|
+
if backup_path:
|
|
272
|
+
logger.info("Failed logs backed up to: %s", backup_path)
|
|
273
|
+
else:
|
|
274
|
+
logger.warn("Failed to save failed logs to backup directory")
|
|
275
|
+
except Exception as ex:
|
|
276
|
+
logger.error("Error saving failed logs to backup: %s", str(ex))
|
|
277
|
+
|
|
278
|
+
def _recover_failed_logs_on_startup(self) -> None:
|
|
279
|
+
if not self._log_config.enable_recovery_on_startup:
|
|
280
|
+
return
|
|
281
|
+
recovered_logs = self._log_file_backup.recover_failed_logs()
|
|
282
|
+
if recovered_logs:
|
|
283
|
+
batch_size = max(1, self._log_config.recovery_batch_size)
|
|
284
|
+
batch_interval = self._log_config.recovery_batch_interval / 1000.0
|
|
285
|
+
for i in range(0, len(recovered_logs), batch_size):
|
|
286
|
+
batch = recovered_logs[i:i + batch_size]
|
|
287
|
+
self.send_batch_struct_log(batch)
|
|
288
|
+
if i + batch_size < len(recovered_logs):
|
|
289
|
+
time.sleep(batch_interval)
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def metrics(self) -> LogMetrics:
|
|
293
|
+
return self._metrics
|
|
294
|
+
|
|
295
|
+
def reset_metrics(self) -> None:
|
|
296
|
+
self._metrics.reset()
|
|
297
|
+
|
|
298
|
+
def close(self) -> None:
|
|
299
|
+
if not self._initialized:
|
|
300
|
+
return
|
|
301
|
+
try:
|
|
302
|
+
if self._producer is not None:
|
|
303
|
+
logger.info("LTS Producer closing, flushing remaining logs...")
|
|
304
|
+
self._flush_remaining_logs()
|
|
305
|
+
self._wait_send_complete()
|
|
306
|
+
self._wait_submit_pool_and_save_failed()
|
|
307
|
+
self._producer = None
|
|
308
|
+
logger.info("LTS Producer closed")
|
|
309
|
+
except Exception as e:
|
|
310
|
+
logger.error("Failed to close LTS Producer: %s", str(e))
|
|
311
|
+
finally:
|
|
312
|
+
self._initialized = False
|
|
313
|
+
|
|
314
|
+
def _flush_remaining_logs(self) -> None:
|
|
315
|
+
try:
|
|
316
|
+
accumulator = self._producer.accumulator
|
|
317
|
+
io_thread_pool = self._producer.io_thread_pool
|
|
318
|
+
if accumulator is None or io_thread_pool is None:
|
|
319
|
+
return
|
|
320
|
+
accumulator.accumulator_lock.acquire_write()
|
|
321
|
+
try:
|
|
322
|
+
if accumulator.log_batch_map:
|
|
323
|
+
for key, batch in accumulator.log_batch_map.items():
|
|
324
|
+
io_thread_pool.add_task(batch)
|
|
325
|
+
logger.info("Flushed %d remaining log batches to LTS", len(accumulator.log_batch_map))
|
|
326
|
+
accumulator.log_batch_map = {}
|
|
327
|
+
finally:
|
|
328
|
+
accumulator.accumulator_lock.release_write()
|
|
329
|
+
except Exception as e:
|
|
330
|
+
logger.error("Failed to flush remaining logs: %s", str(e))
|
|
331
|
+
|
|
332
|
+
def _wait_send_complete(self, timeout: int = 30) -> None:
|
|
333
|
+
try:
|
|
334
|
+
io_thread_pool = self._producer.io_thread_pool
|
|
335
|
+
if io_thread_pool is None:
|
|
336
|
+
return
|
|
337
|
+
start = time.time()
|
|
338
|
+
while time.time() - start < timeout:
|
|
339
|
+
queue_size = io_thread_pool.task_queue_size()
|
|
340
|
+
if queue_size <= 0:
|
|
341
|
+
time.sleep(1)
|
|
342
|
+
logger.info("All LTS log tasks completed")
|
|
343
|
+
return
|
|
344
|
+
time.sleep(0.5)
|
|
345
|
+
logger.warning("LTS log tasks not completed within %s seconds, force closing", timeout)
|
|
346
|
+
except Exception as e:
|
|
347
|
+
logger.error("Failed to wait for send complete: %s", str(e))
|
|
348
|
+
|
|
349
|
+
def _wait_submit_pool_and_save_failed(self) -> None:
|
|
350
|
+
try:
|
|
351
|
+
io_thread_pool = self._producer.io_thread_pool
|
|
352
|
+
if io_thread_pool is not None and hasattr(io_thread_pool, '_submit_thread_pool'):
|
|
353
|
+
submit_pool = io_thread_pool._submit_thread_pool
|
|
354
|
+
if submit_pool is not None:
|
|
355
|
+
logger.info("Waiting for in-flight LTS requests to complete...")
|
|
356
|
+
submit_pool.shutdown(wait=True)
|
|
357
|
+
logger.info("All in-flight LTS requests completed")
|
|
358
|
+
except Exception as e:
|
|
359
|
+
logger.error("Failed to wait for submit thread pool: %s", str(e))
|
|
360
|
+
|
|
361
|
+
try:
|
|
362
|
+
retry_queue = None
|
|
363
|
+
io_thread_pool = self._producer.io_thread_pool
|
|
364
|
+
if io_thread_pool is not None and io_thread_pool.io_worker is not None:
|
|
365
|
+
retry_queue = io_thread_pool.io_worker._retry_queue
|
|
366
|
+
|
|
367
|
+
if retry_queue is not None:
|
|
368
|
+
saved_count = 0
|
|
369
|
+
while not retry_queue.empty():
|
|
370
|
+
try:
|
|
371
|
+
batch = retry_queue.get_nowait()
|
|
372
|
+
for call_back in batch.call_back_list:
|
|
373
|
+
if isinstance(call_back, _LtsLogCallBack):
|
|
374
|
+
self._log_file_backup.save_failed_logs(
|
|
375
|
+
call_back._logs, int(time.time() * 1000))
|
|
376
|
+
saved_count += len(call_back._logs)
|
|
377
|
+
except Exception:
|
|
378
|
+
break
|
|
379
|
+
if saved_count > 0:
|
|
380
|
+
logger.info("Saved %d failed logs from retry queue to backup", saved_count)
|
|
381
|
+
except Exception as e:
|
|
382
|
+
logger.error("Failed to save inflight failed logs: %s", str(e))
|
|
383
|
+
|
|
384
|
+
@property
|
|
385
|
+
def producer(self):
|
|
386
|
+
return self._producer
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import socket
|
|
3
|
+
import time
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
_CONTROL_CHARS_PATTERN = re.compile(r'[\r\n\t\x00-\x1F\x7F-\x9F]')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def sanitize(input_str: Optional[str]) -> str:
|
|
12
|
+
if input_str is None:
|
|
13
|
+
return ""
|
|
14
|
+
return _CONTROL_CHARS_PATTERN.sub("", input_str)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class LtsStructLogBuilder:
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def build_encrypt_struct_log(
|
|
21
|
+
encrypt_file_name: Optional[str],
|
|
22
|
+
encrypt_data_bytes: Optional[int],
|
|
23
|
+
encrypt_output_file_name: Optional[str],
|
|
24
|
+
encrypt_output_bytes: Optional[int],
|
|
25
|
+
key_id: Optional[str],
|
|
26
|
+
access_point_type: Optional[str],
|
|
27
|
+
instance_id: Optional[str],
|
|
28
|
+
) -> dict:
|
|
29
|
+
return LtsStructLogBuilder.build_struct_log(
|
|
30
|
+
"encrypt", encrypt_file_name, encrypt_data_bytes,
|
|
31
|
+
encrypt_output_file_name, encrypt_output_bytes,
|
|
32
|
+
key_id, access_point_type, instance_id,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def build_decrypt_struct_log(
|
|
37
|
+
decrypt_file_name: Optional[str],
|
|
38
|
+
decrypt_data_bytes: Optional[int],
|
|
39
|
+
decrypt_output_file_name: Optional[str],
|
|
40
|
+
decrypt_output_bytes: Optional[int],
|
|
41
|
+
key_id: Optional[str],
|
|
42
|
+
access_point_type: Optional[str],
|
|
43
|
+
instance_id: Optional[str],
|
|
44
|
+
) -> dict:
|
|
45
|
+
return LtsStructLogBuilder.build_struct_log(
|
|
46
|
+
"decrypt", decrypt_file_name, decrypt_data_bytes,
|
|
47
|
+
decrypt_output_file_name, decrypt_output_bytes,
|
|
48
|
+
key_id, access_point_type, instance_id,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def build_struct_log(
|
|
53
|
+
operation: str,
|
|
54
|
+
file_name: Optional[str],
|
|
55
|
+
data_bytes: Optional[int],
|
|
56
|
+
output_file_name: Optional[str],
|
|
57
|
+
output_bytes: Optional[int],
|
|
58
|
+
key_id: Optional[str],
|
|
59
|
+
access_point_type: Optional[str],
|
|
60
|
+
instance_id: Optional[str],
|
|
61
|
+
) -> dict:
|
|
62
|
+
struct_log = {}
|
|
63
|
+
ts = int(time.time() * 1000)
|
|
64
|
+
struct_log["timestamp"] = str(ts)
|
|
65
|
+
struct_log["utcTime"] = datetime.fromtimestamp(ts / 1000, tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
|
|
66
|
+
struct_log["operation"] = sanitize(operation)
|
|
67
|
+
|
|
68
|
+
field_prefix = operation
|
|
69
|
+
if file_name:
|
|
70
|
+
struct_log[field_prefix + "FileName"] = sanitize(file_name)
|
|
71
|
+
if data_bytes is not None and data_bytes > 0:
|
|
72
|
+
struct_log[field_prefix + "DataBytes"] = str(data_bytes)
|
|
73
|
+
if output_file_name:
|
|
74
|
+
struct_log[field_prefix + "OutputFileName"] = sanitize(output_file_name)
|
|
75
|
+
if output_bytes is not None and output_bytes > 0:
|
|
76
|
+
struct_log[field_prefix + "OutputBytes"] = str(output_bytes)
|
|
77
|
+
if key_id:
|
|
78
|
+
struct_log["keyId"] = sanitize(key_id)
|
|
79
|
+
if access_point_type:
|
|
80
|
+
struct_log["accessPointType"] = sanitize(access_point_type)
|
|
81
|
+
if instance_id:
|
|
82
|
+
struct_log["instanceId"] = sanitize(instance_id)
|
|
83
|
+
|
|
84
|
+
ip = LtsStructLogBuilder.get_local_ip()
|
|
85
|
+
if ip:
|
|
86
|
+
struct_log["ipAddress"] = ip
|
|
87
|
+
return struct_log
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def get_local_ip() -> Optional[str]:
|
|
91
|
+
try:
|
|
92
|
+
for info in socket.getaddrinfo(socket.gethostname(), None, socket.AF_INET):
|
|
93
|
+
addr = info[4][0]
|
|
94
|
+
if addr and not addr.startswith("127."):
|
|
95
|
+
return addr
|
|
96
|
+
except Exception:
|
|
97
|
+
pass
|
|
98
|
+
try:
|
|
99
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
100
|
+
s.connect(("8.8.8.8", 80))
|
|
101
|
+
ip = s.getsockname()[0]
|
|
102
|
+
s.close()
|
|
103
|
+
return ip
|
|
104
|
+
except Exception:
|
|
105
|
+
pass
|
|
106
|
+
return None
|
|
File without changes
|