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,116 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
from huaweicloud_encryptionsdk.exception.errors import HuaweicloudException, ErrorMessage
|
|
7
|
+
from huaweicloud_encryptionsdk.model.kms_config import KMSConfig
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
SIGNATURE_ALGORITHM = "SDK-HMAC-SHA256"
|
|
12
|
+
CONTENT_TYPE_JSON = "application/json"
|
|
13
|
+
CONNECT_TIMEOUT_SECONDS = 10
|
|
14
|
+
SOCKET_TIMEOUT_SECONDS = 10
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class KmsHttpClient:
|
|
18
|
+
|
|
19
|
+
def __init__(self, ak: str, sk: str, kms_config: KMSConfig, security_token: str = ""):
|
|
20
|
+
self._ak = ak
|
|
21
|
+
self._sk = sk
|
|
22
|
+
self._kms_config = kms_config
|
|
23
|
+
self._security_token = security_token
|
|
24
|
+
self._signer = self._create_signer()
|
|
25
|
+
|
|
26
|
+
def _create_signer(self):
|
|
27
|
+
from huaweicloud_encryptionsdk._apig_sdk import signer
|
|
28
|
+
sig = signer.Signer()
|
|
29
|
+
sig.Key = self._ak
|
|
30
|
+
sig.Secret = self._sk
|
|
31
|
+
return sig
|
|
32
|
+
|
|
33
|
+
def post(self, uri, body, response_type=None):
|
|
34
|
+
return self._execute("POST", uri, body, response_type)
|
|
35
|
+
|
|
36
|
+
def get(self, uri, response_type=None):
|
|
37
|
+
return self._execute("GET", uri, None, response_type)
|
|
38
|
+
|
|
39
|
+
def put(self, uri, body, response_type=None):
|
|
40
|
+
return self._execute("PUT", uri, body, response_type)
|
|
41
|
+
|
|
42
|
+
def delete(self, uri, body, response_type=None):
|
|
43
|
+
return self._execute("DELETE", uri, body, response_type)
|
|
44
|
+
|
|
45
|
+
def _execute(self, method, uri, body, response_type):
|
|
46
|
+
from huaweicloud_encryptionsdk._apig_sdk.signer import HttpRequest
|
|
47
|
+
url = self._build_url(uri)
|
|
48
|
+
json_body = json.dumps(body, default=self._json_default) if body is not None else None
|
|
49
|
+
try:
|
|
50
|
+
r = HttpRequest(method, url)
|
|
51
|
+
r.headers["Content-Type"] = CONTENT_TYPE_JSON
|
|
52
|
+
if self._security_token:
|
|
53
|
+
r.headers["X-Security-Token"] = self._security_token
|
|
54
|
+
if json_body is not None:
|
|
55
|
+
r.body = json_body
|
|
56
|
+
|
|
57
|
+
self._signer.Sign(r)
|
|
58
|
+
|
|
59
|
+
logger.info("[EncryptionSDK] KMS Request: %s %s", method, url)
|
|
60
|
+
if json_body is not None:
|
|
61
|
+
logger.debug("[EncryptionSDK] KMS Request Body: %s", json_body)
|
|
62
|
+
|
|
63
|
+
resp = requests.request(
|
|
64
|
+
method=r.method,
|
|
65
|
+
url=r.scheme + "://" + r.host + r.uri,
|
|
66
|
+
headers=r.headers,
|
|
67
|
+
data=r.body,
|
|
68
|
+
timeout=(CONNECT_TIMEOUT_SECONDS, SOCKET_TIMEOUT_SECONDS)
|
|
69
|
+
)
|
|
70
|
+
request_id = resp.headers.get("X-Request-Id", "")
|
|
71
|
+
logger.info("[EncryptionSDK] X-Request-Id is %s", request_id)
|
|
72
|
+
logger.info("[EncryptionSDK] KMS Response Status: %s", resp.status_code)
|
|
73
|
+
logger.debug("[EncryptionSDK] KMS Response Body: %s", resp.text[:1024] if resp.text else "")
|
|
74
|
+
if resp.status_code != 200:
|
|
75
|
+
raise HuaweicloudException(
|
|
76
|
+
"KMS API call failed, status=" + str(resp.status_code)
|
|
77
|
+
+ ", body=" + resp.text
|
|
78
|
+
)
|
|
79
|
+
response_data = resp.json()
|
|
80
|
+
if response_type is not None:
|
|
81
|
+
return self._deserialize_response(response_data, response_type)
|
|
82
|
+
return response_data
|
|
83
|
+
except HuaweicloudException:
|
|
84
|
+
raise
|
|
85
|
+
except Exception as e:
|
|
86
|
+
raise HuaweicloudException(ErrorMessage.KMS_CREATE_KEY_EXCEPTION.message, e)
|
|
87
|
+
|
|
88
|
+
def _build_url(self, uri):
|
|
89
|
+
endpoint = self._kms_config.end_point
|
|
90
|
+
if not endpoint:
|
|
91
|
+
raise HuaweicloudException(ErrorMessage.END_POINT_NOT_NULL.message)
|
|
92
|
+
if not endpoint.startswith("http://") and not endpoint.startswith("https://"):
|
|
93
|
+
endpoint = "https://" + endpoint
|
|
94
|
+
return endpoint + "/v1.0/" + self._kms_config.project_id + uri
|
|
95
|
+
|
|
96
|
+
@staticmethod
|
|
97
|
+
def _deserialize_response(data, response_type):
|
|
98
|
+
if hasattr(response_type, 'from_dict'):
|
|
99
|
+
return response_type.from_dict(data)
|
|
100
|
+
obj = response_type()
|
|
101
|
+
for key, value in data.items():
|
|
102
|
+
attr_name = key.replace("-", "_").replace(" ", "_")
|
|
103
|
+
if hasattr(obj, attr_name):
|
|
104
|
+
setattr(obj, attr_name, value)
|
|
105
|
+
return obj
|
|
106
|
+
|
|
107
|
+
@staticmethod
|
|
108
|
+
def _json_default(obj):
|
|
109
|
+
if hasattr(obj, '__dict__'):
|
|
110
|
+
result = {}
|
|
111
|
+
for key, value in obj.__dict__.items():
|
|
112
|
+
if value is not None:
|
|
113
|
+
json_key = key.lstrip('_')
|
|
114
|
+
result[json_key] = value
|
|
115
|
+
return result
|
|
116
|
+
return str(obj)
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class LogConfig:
|
|
8
|
+
log_group_id: str = ""
|
|
9
|
+
log_stream_id: str = ""
|
|
10
|
+
log_group_name: str = "DSC"
|
|
11
|
+
log_stream_name: str = "crypto_operation_log"
|
|
12
|
+
region: Optional[str] = None
|
|
13
|
+
project_id: Optional[str] = None
|
|
14
|
+
lts_endpoint: Optional[str] = None
|
|
15
|
+
batch_count_threshold: int = 1000
|
|
16
|
+
batch_size_threshold_in_bytes: int = 524288
|
|
17
|
+
linger_ms: int = 30000
|
|
18
|
+
total_size_in_bytes: int = 104857600
|
|
19
|
+
max_block_ms: int = 0
|
|
20
|
+
io_thread_count: int = field(default_factory=lambda: os.cpu_count() or 1)
|
|
21
|
+
retries: int = 5
|
|
22
|
+
base_retry_backoff_ms: int = 100
|
|
23
|
+
max_retry_backoff_ms: int = 500
|
|
24
|
+
enable_local_test: bool = False
|
|
25
|
+
backup_dir: str = field(default_factory=lambda: os.path.join(os.path.expanduser("~"), "lts_log_backup"))
|
|
26
|
+
max_backup_file_size: int = 10 * 1024 * 1024
|
|
27
|
+
enable_recovery_on_startup: bool = True
|
|
28
|
+
recovery_batch_size: int = 1000
|
|
29
|
+
recovery_batch_interval: int = 1000
|
|
30
|
+
fail_on_backup_dir_missing: bool = False
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
import threading
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
|
|
7
|
+
from huaweicloud_encryptionsdk.exception.errors import HuaweicloudException
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class LogFileBackup:
|
|
11
|
+
|
|
12
|
+
DEFAULT_BACKUP_DIR = "lts_log_backup"
|
|
13
|
+
BACKUP_FILE_PREFIX = "lts_backup_"
|
|
14
|
+
BACKUP_FILE_SUFFIX = ".json"
|
|
15
|
+
MAX_BACKUP_FILE_SIZE = 10 * 1024 * 1024
|
|
16
|
+
_save_lock = threading.Lock()
|
|
17
|
+
|
|
18
|
+
def __init__(self, backup_dir_path: Optional[str] = None, fail_on_missing: bool = False):
|
|
19
|
+
if backup_dir_path is None or backup_dir_path.strip() == "":
|
|
20
|
+
backup_dir_path = os.path.join(os.path.expanduser("~"), self.DEFAULT_BACKUP_DIR)
|
|
21
|
+
self._backup_dir_path = backup_dir_path.strip()
|
|
22
|
+
self._fail_on_missing = fail_on_missing
|
|
23
|
+
self._init_backup_dir()
|
|
24
|
+
|
|
25
|
+
def _init_backup_dir(self) -> None:
|
|
26
|
+
backup_dir = self._backup_dir_path
|
|
27
|
+
if os.path.exists(backup_dir):
|
|
28
|
+
if not os.path.isdir(backup_dir):
|
|
29
|
+
if self._fail_on_missing:
|
|
30
|
+
raise HuaweicloudException(f"Backup path exists but is not a directory: {backup_dir}")
|
|
31
|
+
return
|
|
32
|
+
if not os.access(backup_dir, os.W_OK):
|
|
33
|
+
if self._fail_on_missing:
|
|
34
|
+
raise HuaweicloudException(f"No write permission for backup directory: {backup_dir}")
|
|
35
|
+
return
|
|
36
|
+
test_file = os.path.join(backup_dir, f".write_test_{int(time.time() * 1000)}")
|
|
37
|
+
try:
|
|
38
|
+
with open(test_file, "w") as f:
|
|
39
|
+
f.write("test")
|
|
40
|
+
os.remove(test_file)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
if self._fail_on_missing:
|
|
43
|
+
raise HuaweicloudException(f"Cannot write to backup directory: {backup_dir}", e)
|
|
44
|
+
return
|
|
45
|
+
else:
|
|
46
|
+
try:
|
|
47
|
+
os.makedirs(backup_dir, exist_ok=True)
|
|
48
|
+
except Exception:
|
|
49
|
+
if self._fail_on_missing:
|
|
50
|
+
raise HuaweicloudException(f"Failed to create backup directory: {backup_dir}")
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
def save_failed_logs(self, logs: List[dict], timestamp: int) -> Optional[str]:
|
|
54
|
+
if logs is None or len(logs) == 0:
|
|
55
|
+
return None
|
|
56
|
+
try:
|
|
57
|
+
date_str = time.strftime("%Y%m%d", time.localtime(timestamp / 1000))
|
|
58
|
+
file_name = f"{self.BACKUP_FILE_PREFIX}{date_str}{self.BACKUP_FILE_SUFFIX}"
|
|
59
|
+
backup_file = os.path.join(self._backup_dir_path, file_name)
|
|
60
|
+
|
|
61
|
+
with self._save_lock:
|
|
62
|
+
existing_logs = []
|
|
63
|
+
if os.path.exists(backup_file):
|
|
64
|
+
try:
|
|
65
|
+
if os.path.getsize(backup_file) < self.MAX_BACKUP_FILE_SIZE:
|
|
66
|
+
with open(backup_file, "r", encoding="utf-8") as f:
|
|
67
|
+
existing_logs = json.loads(f.read())
|
|
68
|
+
else:
|
|
69
|
+
seq = 1
|
|
70
|
+
while True:
|
|
71
|
+
file_name = f"{self.BACKUP_FILE_PREFIX}{date_str}_{seq}{self.BACKUP_FILE_SUFFIX}"
|
|
72
|
+
backup_file = os.path.join(self._backup_dir_path, file_name)
|
|
73
|
+
if not os.path.exists(backup_file) or os.path.getsize(backup_file) < self.MAX_BACKUP_FILE_SIZE:
|
|
74
|
+
if os.path.exists(backup_file):
|
|
75
|
+
with open(backup_file, "r", encoding="utf-8") as f:
|
|
76
|
+
existing_logs = json.loads(f.read())
|
|
77
|
+
break
|
|
78
|
+
seq += 1
|
|
79
|
+
except Exception:
|
|
80
|
+
existing_logs = []
|
|
81
|
+
|
|
82
|
+
existing_logs.extend(logs)
|
|
83
|
+
json_content = json.dumps(existing_logs, ensure_ascii=False, default=str)
|
|
84
|
+
with open(backup_file, "w", encoding="utf-8") as f:
|
|
85
|
+
f.write(json_content)
|
|
86
|
+
|
|
87
|
+
return os.path.abspath(backup_file)
|
|
88
|
+
except Exception:
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
def recover_failed_logs(self) -> List[dict]:
|
|
92
|
+
recovered_logs = []
|
|
93
|
+
try:
|
|
94
|
+
if not os.path.exists(self._backup_dir_path) or not os.path.isdir(self._backup_dir_path):
|
|
95
|
+
return recovered_logs
|
|
96
|
+
backup_files = []
|
|
97
|
+
for name in os.listdir(self._backup_dir_path):
|
|
98
|
+
if name.startswith(self.BACKUP_FILE_PREFIX) and name.endswith(self.BACKUP_FILE_SUFFIX):
|
|
99
|
+
backup_files.append(name)
|
|
100
|
+
if not backup_files:
|
|
101
|
+
return recovered_logs
|
|
102
|
+
backup_files.sort(key=lambda f: self._extract_timestamp_from_file(f))
|
|
103
|
+
for file_name in backup_files:
|
|
104
|
+
backup_file = os.path.join(self._backup_dir_path, file_name)
|
|
105
|
+
try:
|
|
106
|
+
file_size = os.path.getsize(backup_file)
|
|
107
|
+
if file_size > self.MAX_BACKUP_FILE_SIZE:
|
|
108
|
+
continue
|
|
109
|
+
with open(backup_file, "r", encoding="utf-8") as f:
|
|
110
|
+
content = f.read()
|
|
111
|
+
logs = json.loads(content)
|
|
112
|
+
if logs and len(logs) > 0:
|
|
113
|
+
recovered_logs.extend(logs)
|
|
114
|
+
try:
|
|
115
|
+
os.remove(backup_file)
|
|
116
|
+
except Exception:
|
|
117
|
+
pass
|
|
118
|
+
except Exception:
|
|
119
|
+
pass
|
|
120
|
+
except Exception:
|
|
121
|
+
pass
|
|
122
|
+
return recovered_logs
|
|
123
|
+
|
|
124
|
+
def _extract_timestamp_from_file(self, file_name: str) -> int:
|
|
125
|
+
try:
|
|
126
|
+
ts_str = file_name[len(self.BACKUP_FILE_PREFIX):-len(self.BACKUP_FILE_SUFFIX)]
|
|
127
|
+
ts_str = ts_str.split("_")[0]
|
|
128
|
+
return int(ts_str)
|
|
129
|
+
except Exception:
|
|
130
|
+
return 0
|
|
131
|
+
|
|
132
|
+
def clear_all_backup_files(self) -> None:
|
|
133
|
+
if not os.path.exists(self._backup_dir_path):
|
|
134
|
+
return
|
|
135
|
+
for name in os.listdir(self._backup_dir_path):
|
|
136
|
+
if name.startswith(self.BACKUP_FILE_PREFIX) and name.endswith(self.BACKUP_FILE_SUFFIX):
|
|
137
|
+
try:
|
|
138
|
+
os.remove(os.path.join(self._backup_dir_path, name))
|
|
139
|
+
except Exception:
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def backup_dir_path(self) -> str:
|
|
144
|
+
return self._backup_dir_path
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class LogMetrics:
|
|
6
|
+
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self._lock = threading.Lock()
|
|
9
|
+
self._last_report_time: int = 0
|
|
10
|
+
self._success_count: int = 0
|
|
11
|
+
self._failure_count: int = 0
|
|
12
|
+
self._success_log_count: int = 0
|
|
13
|
+
self._failure_log_count: int = 0
|
|
14
|
+
self._buffer_log_count: int = 0
|
|
15
|
+
self._last_error_message: Optional[str] = None
|
|
16
|
+
self._last_error_time: int = 0
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def last_report_time(self) -> int:
|
|
20
|
+
with self._lock:
|
|
21
|
+
return self._last_report_time
|
|
22
|
+
|
|
23
|
+
@last_report_time.setter
|
|
24
|
+
def last_report_time(self, value: int) -> None:
|
|
25
|
+
with self._lock:
|
|
26
|
+
self._last_report_time = value
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def success_count(self) -> int:
|
|
30
|
+
with self._lock:
|
|
31
|
+
return self._success_count
|
|
32
|
+
|
|
33
|
+
@success_count.setter
|
|
34
|
+
def success_count(self, value: int) -> None:
|
|
35
|
+
with self._lock:
|
|
36
|
+
self._success_count = value
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def failure_count(self) -> int:
|
|
40
|
+
with self._lock:
|
|
41
|
+
return self._failure_count
|
|
42
|
+
|
|
43
|
+
@failure_count.setter
|
|
44
|
+
def failure_count(self, value: int) -> None:
|
|
45
|
+
with self._lock:
|
|
46
|
+
self._failure_count = value
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def success_log_count(self) -> int:
|
|
50
|
+
with self._lock:
|
|
51
|
+
return self._success_log_count
|
|
52
|
+
|
|
53
|
+
@success_log_count.setter
|
|
54
|
+
def success_log_count(self, value: int) -> None:
|
|
55
|
+
with self._lock:
|
|
56
|
+
self._success_log_count = value
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def failure_log_count(self) -> int:
|
|
60
|
+
with self._lock:
|
|
61
|
+
return self._failure_log_count
|
|
62
|
+
|
|
63
|
+
@failure_log_count.setter
|
|
64
|
+
def failure_log_count(self, value: int) -> None:
|
|
65
|
+
with self._lock:
|
|
66
|
+
self._failure_log_count = value
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def buffer_log_count(self) -> int:
|
|
70
|
+
with self._lock:
|
|
71
|
+
return self._buffer_log_count
|
|
72
|
+
|
|
73
|
+
@buffer_log_count.setter
|
|
74
|
+
def buffer_log_count(self, value: int) -> None:
|
|
75
|
+
with self._lock:
|
|
76
|
+
self._buffer_log_count = value
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def last_error_message(self) -> Optional[str]:
|
|
80
|
+
with self._lock:
|
|
81
|
+
return self._last_error_message
|
|
82
|
+
|
|
83
|
+
@last_error_message.setter
|
|
84
|
+
def last_error_message(self, value: Optional[str]) -> None:
|
|
85
|
+
with self._lock:
|
|
86
|
+
self._last_error_message = value
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def last_error_time(self) -> int:
|
|
90
|
+
with self._lock:
|
|
91
|
+
return self._last_error_time
|
|
92
|
+
|
|
93
|
+
@last_error_time.setter
|
|
94
|
+
def last_error_time(self, value: int) -> None:
|
|
95
|
+
with self._lock:
|
|
96
|
+
self._last_error_time = value
|
|
97
|
+
|
|
98
|
+
def increment_success_count(self) -> int:
|
|
99
|
+
with self._lock:
|
|
100
|
+
self._success_count += 1
|
|
101
|
+
return self._success_count
|
|
102
|
+
|
|
103
|
+
def increment_failure_count(self) -> int:
|
|
104
|
+
with self._lock:
|
|
105
|
+
self._failure_count += 1
|
|
106
|
+
return self._failure_count
|
|
107
|
+
|
|
108
|
+
def add_success_log_count(self, delta: int) -> int:
|
|
109
|
+
with self._lock:
|
|
110
|
+
self._success_log_count += delta
|
|
111
|
+
return self._success_log_count
|
|
112
|
+
|
|
113
|
+
def add_failure_log_count(self, delta: int) -> int:
|
|
114
|
+
with self._lock:
|
|
115
|
+
self._failure_log_count += delta
|
|
116
|
+
return self._failure_log_count
|
|
117
|
+
|
|
118
|
+
def add_buffer_log_count(self, delta: int) -> int:
|
|
119
|
+
with self._lock:
|
|
120
|
+
self._buffer_log_count += delta
|
|
121
|
+
return self._buffer_log_count
|
|
122
|
+
|
|
123
|
+
def reset(self) -> None:
|
|
124
|
+
with self._lock:
|
|
125
|
+
self._last_report_time = 0
|
|
126
|
+
self._success_count = 0
|
|
127
|
+
self._failure_count = 0
|
|
128
|
+
self._success_log_count = 0
|
|
129
|
+
self._failure_log_count = 0
|
|
130
|
+
self._buffer_log_count = 0
|
|
131
|
+
self._last_error_message = None
|
|
132
|
+
self._last_error_time = 0
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import threading
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from huaweicloud_encryptionsdk.log.log_config import LogConfig
|
|
6
|
+
from huaweicloud_encryptionsdk.log.lts_sdk_producer import LTSSDKProducer
|
|
7
|
+
from huaweicloud_encryptionsdk.log.lts_struct_log_builder import LtsStructLogBuilder
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
MAX_INIT_RETRY_COUNT = 3
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def desensitize(value: Optional[str]) -> str:
|
|
15
|
+
if value is None or len(value) <= 4:
|
|
16
|
+
return "****"
|
|
17
|
+
return value[:2] + "****" + value[-2:]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LogReporterManager:
|
|
21
|
+
|
|
22
|
+
def __init__(self):
|
|
23
|
+
self._lts_producer: Optional[LTSSDKProducer] = None
|
|
24
|
+
self._log_config: Optional[LogConfig] = None
|
|
25
|
+
self._local_log_only: bool = False
|
|
26
|
+
self._initialized: bool = False
|
|
27
|
+
self._init_retry_count: int = 0
|
|
28
|
+
self._lock = threading.Lock()
|
|
29
|
+
|
|
30
|
+
def init(self, config) -> None:
|
|
31
|
+
with self._lock:
|
|
32
|
+
if self._initialized:
|
|
33
|
+
logger.debug("LogReporterManager is already initialized")
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
if self._init_retry_count >= MAX_INIT_RETRY_COUNT:
|
|
37
|
+
logger.warn("LogReporterManager init has exceeded max retry count (%d), giving up", MAX_INIT_RETRY_COUNT)
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
if config.log_config is None and not config.local_log_only:
|
|
41
|
+
logger.info("Log config is not provided and localLogOnly is false, logging is disabled")
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
self._log_config = config.log_config if config.log_config is not None else LogConfig()
|
|
45
|
+
|
|
46
|
+
if config.local_log_only:
|
|
47
|
+
self._local_log_only = True
|
|
48
|
+
self._initialized = True
|
|
49
|
+
logger.info("LogReporterManager initialized with local log only mode")
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
if self._log_config.project_id is None:
|
|
54
|
+
if config.kms_config_list and len(config.kms_config_list) > 0:
|
|
55
|
+
self._log_config.project_id = config.kms_config_list[0].project_id
|
|
56
|
+
else:
|
|
57
|
+
self._log_config.project_id = "default"
|
|
58
|
+
self._lts_producer = LTSSDKProducer(
|
|
59
|
+
config.get_ak(),
|
|
60
|
+
config.get_sk(),
|
|
61
|
+
self._log_config.project_id,
|
|
62
|
+
self._log_config.region,
|
|
63
|
+
self._log_config,
|
|
64
|
+
security_token=config.security_token or "",
|
|
65
|
+
)
|
|
66
|
+
self._initialized = True
|
|
67
|
+
logger.info("LogReporterManager initialized successfully with LTS mode")
|
|
68
|
+
logger.info("Log config: batchCountThreshold=%s, lingerMs=%s, ioThreadCount=%s",
|
|
69
|
+
self._log_config.batch_count_threshold,
|
|
70
|
+
self._log_config.linger_ms,
|
|
71
|
+
self._log_config.io_thread_count)
|
|
72
|
+
except Exception as e:
|
|
73
|
+
self._init_retry_count += 1
|
|
74
|
+
logger.error("Failed to initialize LogReporterManager (attempt %d/%d): %s",
|
|
75
|
+
self._init_retry_count, MAX_INIT_RETRY_COUNT, str(e), exc_info=e)
|
|
76
|
+
if self._log_config.fail_on_backup_dir_missing:
|
|
77
|
+
raise
|
|
78
|
+
|
|
79
|
+
def record_encrypt_log(
|
|
80
|
+
self,
|
|
81
|
+
encrypt_file_name: Optional[str],
|
|
82
|
+
encrypt_data_bytes: Optional[int],
|
|
83
|
+
encrypt_output_file_name: Optional[str],
|
|
84
|
+
encrypt_output_bytes: Optional[int],
|
|
85
|
+
key_id: Optional[str],
|
|
86
|
+
access_point_type: Optional[str],
|
|
87
|
+
instance_id: Optional[str],
|
|
88
|
+
) -> None:
|
|
89
|
+
self._record_log(
|
|
90
|
+
"encrypt", encrypt_file_name, encrypt_data_bytes,
|
|
91
|
+
encrypt_output_file_name, encrypt_output_bytes,
|
|
92
|
+
key_id, access_point_type, instance_id,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def record_decrypt_log(
|
|
96
|
+
self,
|
|
97
|
+
decrypt_file_name: Optional[str],
|
|
98
|
+
decrypt_data_bytes: Optional[int],
|
|
99
|
+
decrypt_output_file_name: Optional[str],
|
|
100
|
+
decrypt_output_bytes: Optional[int],
|
|
101
|
+
key_id: Optional[str],
|
|
102
|
+
access_point_type: Optional[str],
|
|
103
|
+
instance_id: Optional[str],
|
|
104
|
+
) -> None:
|
|
105
|
+
self._record_log(
|
|
106
|
+
"decrypt", decrypt_file_name, decrypt_data_bytes,
|
|
107
|
+
decrypt_output_file_name, decrypt_output_bytes,
|
|
108
|
+
key_id, access_point_type, instance_id,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
def _record_log(
|
|
112
|
+
self,
|
|
113
|
+
operation: str,
|
|
114
|
+
file_name: Optional[str],
|
|
115
|
+
data_bytes: Optional[int],
|
|
116
|
+
output_file_name: Optional[str],
|
|
117
|
+
output_bytes: Optional[int],
|
|
118
|
+
key_id: Optional[str],
|
|
119
|
+
access_point_type: Optional[str],
|
|
120
|
+
instance_id: Optional[str],
|
|
121
|
+
) -> None:
|
|
122
|
+
if not self._initialized:
|
|
123
|
+
return
|
|
124
|
+
try:
|
|
125
|
+
struct_log = LtsStructLogBuilder.build_struct_log(
|
|
126
|
+
operation, file_name, data_bytes,
|
|
127
|
+
output_file_name, output_bytes,
|
|
128
|
+
key_id, access_point_type, instance_id,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if self._local_log_only:
|
|
132
|
+
logger.info("[LOCAL_LOG] %s", struct_log)
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
if self._lts_producer is not None:
|
|
136
|
+
self._lts_producer.send_struct_log(struct_log)
|
|
137
|
+
logger.info("Recorded %s log for keyId: %s, dataBytes: %s, accessPointType: %s",
|
|
138
|
+
operation, desensitize(key_id), data_bytes, access_point_type)
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logger.error("Failed to record %s operation log: %s", operation, str(e))
|
|
141
|
+
|
|
142
|
+
def shutdown(self) -> None:
|
|
143
|
+
with self._lock:
|
|
144
|
+
if not self._initialized:
|
|
145
|
+
return
|
|
146
|
+
if self._lts_producer is not None:
|
|
147
|
+
try:
|
|
148
|
+
self._lts_producer.close()
|
|
149
|
+
except Exception as e:
|
|
150
|
+
logger.error("Failed to close LTS producer during shutdown, will retry on next shutdown call: %s", str(e))
|
|
151
|
+
return
|
|
152
|
+
self._lts_producer = None
|
|
153
|
+
self._initialized = False
|
|
154
|
+
self._init_retry_count = 0
|
|
155
|
+
logger.info("LogReporterManager shutdown successfully")
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def is_initialized(self) -> bool:
|
|
159
|
+
return self._initialized
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def lts_producer(self) -> Optional[LTSSDKProducer]:
|
|
163
|
+
return self._lts_producer
|