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.
Files changed (174) hide show
  1. examples/file_encryption_example.py +57 -0
  2. examples/key_capsule_encryption_example.py +240 -0
  3. examples/kms_discovery_encryption_example.py +56 -0
  4. examples/local_aes_encryption_example.py +59 -0
  5. examples/test_rsa.py +42 -0
  6. examples/test_sm2.py +38 -0
  7. examples/test_sm4_cbc.py +37 -0
  8. examples/test_sm4_gcm.py +37 -0
  9. huaweicloud_encryption_sdk_python-1.2.1.dist-info/METADATA +18 -0
  10. huaweicloud_encryption_sdk_python-1.2.1.dist-info/RECORD +174 -0
  11. huaweicloud_encryption_sdk_python-1.2.1.dist-info/WHEEL +5 -0
  12. huaweicloud_encryption_sdk_python-1.2.1.dist-info/top_level.txt +3 -0
  13. huaweicloud_encryptionsdk/__init__.py +5 -0
  14. huaweicloud_encryptionsdk/_apig_sdk/__init__.py +1 -0
  15. huaweicloud_encryptionsdk/_apig_sdk/signer.py +278 -0
  16. huaweicloud_encryptionsdk/_apig_sdk/signer_v11.py +70 -0
  17. huaweicloud_encryptionsdk/_apig_sdk/sm3_hash.py +141 -0
  18. huaweicloud_encryptionsdk/_lts_sdk/auth/__init__.py +0 -0
  19. huaweicloud_encryptionsdk/_lts_sdk/auth/signer.py +174 -0
  20. huaweicloud_encryptionsdk/_lts_sdk/consumer/__init__.py +0 -0
  21. huaweicloud_encryptionsdk/_lts_sdk/consumer/client/__init__.py +0 -0
  22. huaweicloud_encryptionsdk/_lts_sdk/consumer/client/consumer_client_adapter.py +59 -0
  23. huaweicloud_encryptionsdk/_lts_sdk/consumer/client/http_client.py +211 -0
  24. huaweicloud_encryptionsdk/_lts_sdk/consumer/core/__init__.py +0 -0
  25. huaweicloud_encryptionsdk/_lts_sdk/consumer/core/client_consumer_worker.py +106 -0
  26. huaweicloud_encryptionsdk/_lts_sdk/consumer/core/consumer_heartbeat.py +73 -0
  27. huaweicloud_encryptionsdk/_lts_sdk/consumer/core/log_shard_consumer.py +179 -0
  28. huaweicloud_encryptionsdk/_lts_sdk/consumer/interface/__init__.py +0 -0
  29. huaweicloud_encryptionsdk/_lts_sdk/consumer/interface/consumer_check_point_tracker.py +120 -0
  30. huaweicloud_encryptionsdk/_lts_sdk/consumer/interface/consumer_processor.py +18 -0
  31. huaweicloud_encryptionsdk/_lts_sdk/consumer/interface/consumer_processor_factory.py +3 -0
  32. huaweicloud_encryptionsdk/_lts_sdk/consumer/model/__init__.py +0 -0
  33. huaweicloud_encryptionsdk/_lts_sdk/consumer/model/check_point.py +20 -0
  34. huaweicloud_encryptionsdk/_lts_sdk/consumer/model/consumer_config.py +27 -0
  35. huaweicloud_encryptionsdk/_lts_sdk/consumer/model/log_data.py +18 -0
  36. huaweicloud_encryptionsdk/_lts_sdk/consumer/model/task_result.py +40 -0
  37. huaweicloud_encryptionsdk/_lts_sdk/consumer/task/__init__.py +0 -0
  38. huaweicloud_encryptionsdk/_lts_sdk/consumer/task/consumer_fetch_task.py +53 -0
  39. huaweicloud_encryptionsdk/_lts_sdk/consumer/task/initial_task.py +55 -0
  40. huaweicloud_encryptionsdk/_lts_sdk/consumer/task/process_task.py +25 -0
  41. huaweicloud_encryptionsdk/_lts_sdk/consumer/task/shutdown_task.py +24 -0
  42. huaweicloud_encryptionsdk/_lts_sdk/consumer_main.py +88 -0
  43. huaweicloud_encryptionsdk/_lts_sdk/parallel_main.py +64 -0
  44. huaweicloud_encryptionsdk/_lts_sdk/producer/__init__.py +0 -0
  45. huaweicloud_encryptionsdk/_lts_sdk/producer/callback/__init__.py +0 -0
  46. huaweicloud_encryptionsdk/_lts_sdk/producer/callback/call_back.py +7 -0
  47. huaweicloud_encryptionsdk/_lts_sdk/producer/core/__init__.py +0 -0
  48. huaweicloud_encryptionsdk/_lts_sdk/producer/core/accumulator.py +135 -0
  49. huaweicloud_encryptionsdk/_lts_sdk/producer/core/io_thread_pool.py +107 -0
  50. huaweicloud_encryptionsdk/_lts_sdk/producer/core/io_worker.py +124 -0
  51. huaweicloud_encryptionsdk/_lts_sdk/producer/core/mover.py +69 -0
  52. huaweicloud_encryptionsdk/_lts_sdk/producer/core/producer.py +98 -0
  53. huaweicloud_encryptionsdk/_lts_sdk/producer/lts_http/__init__.py +0 -0
  54. huaweicloud_encryptionsdk/_lts_sdk/producer/lts_http/http_client.py +63 -0
  55. huaweicloud_encryptionsdk/_lts_sdk/producer/lts_http/simple_request.py +14 -0
  56. huaweicloud_encryptionsdk/_lts_sdk/producer/model/__init__.py +0 -0
  57. huaweicloud_encryptionsdk/_lts_sdk/producer/model/config.py +50 -0
  58. huaweicloud_encryptionsdk/_lts_sdk/producer/model/error.py +18 -0
  59. huaweicloud_encryptionsdk/_lts_sdk/producer/model/log.py +87 -0
  60. huaweicloud_encryptionsdk/_lts_sdk/producer/model/log_batch.py +154 -0
  61. huaweicloud_encryptionsdk/_lts_sdk/producer/model/lts_store.py +21 -0
  62. huaweicloud_encryptionsdk/_lts_sdk/producer/model/result.py +17 -0
  63. huaweicloud_encryptionsdk/_lts_sdk/producer/model/retry_queue.py +39 -0
  64. huaweicloud_encryptionsdk/_lts_sdk/serial_main.py +77 -0
  65. huaweicloud_encryptionsdk/_lts_sdk/utils/__init__.py +0 -0
  66. huaweicloud_encryptionsdk/_lts_sdk/utils/common.py +57 -0
  67. huaweicloud_encryptionsdk/_lts_sdk/utils/lts_threading.py +32 -0
  68. huaweicloud_encryptionsdk/cache/__init__.py +0 -0
  69. huaweicloud_encryptionsdk/cache/data_key_cache.py +79 -0
  70. huaweicloud_encryptionsdk/cache/local_data_key_cache.py +161 -0
  71. huaweicloud_encryptionsdk/common/__init__.py +0 -0
  72. huaweicloud_encryptionsdk/common/constants.py +15 -0
  73. huaweicloud_encryptionsdk/common/counting_output_stream.py +18 -0
  74. huaweicloud_encryptionsdk/common/crypto_factory.py +48 -0
  75. huaweicloud_encryptionsdk/common/ec_private_key_loader.py +43 -0
  76. huaweicloud_encryptionsdk/common/ecs_metadata_service.py +42 -0
  77. huaweicloud_encryptionsdk/common/file_path_for_example_constants.py +4 -0
  78. huaweicloud_encryptionsdk/common/utils.py +162 -0
  79. huaweicloud_encryptionsdk/exception/__init__.py +0 -0
  80. huaweicloud_encryptionsdk/exception/errors.py +109 -0
  81. huaweicloud_encryptionsdk/handler/__init__.py +0 -0
  82. huaweicloud_encryptionsdk/handler/cipher_handler.py +294 -0
  83. huaweicloud_encryptionsdk/handler/crypto_handler.py +24 -0
  84. huaweicloud_encryptionsdk/handler/default_crypto_handler.py +148 -0
  85. huaweicloud_encryptionsdk/handler/default_serialize_handler.py +45 -0
  86. huaweicloud_encryptionsdk/handler/file_decrypt_handler.py +101 -0
  87. huaweicloud_encryptionsdk/handler/file_encrypt_handler.py +30 -0
  88. huaweicloud_encryptionsdk/handler/serialize_handler.py +14 -0
  89. huaweicloud_encryptionsdk/huawei_config.py +49 -0
  90. huaweicloud_encryptionsdk/huawei_crypto.py +311 -0
  91. huaweicloud_encryptionsdk/keyrings/__init__.py +0 -0
  92. huaweicloud_encryptionsdk/keyrings/keyring.py +14 -0
  93. huaweicloud_encryptionsdk/keyrings/keyring_factory.py +10 -0
  94. huaweicloud_encryptionsdk/keyrings/kms_keyring_factory.py +21 -0
  95. huaweicloud_encryptionsdk/keyrings/kmskeyring/__init__.py +0 -0
  96. huaweicloud_encryptionsdk/keyrings/kmskeyring/kms_discovery_keyring.py +51 -0
  97. huaweicloud_encryptionsdk/keyrings/kmskeyring/kms_key_capsule_keyring.py +100 -0
  98. huaweicloud_encryptionsdk/keyrings/kmskeyring/kms_keyring.py +155 -0
  99. huaweicloud_encryptionsdk/keyrings/kmskeyring/kms_multi_region_keyring.py +8 -0
  100. huaweicloud_encryptionsdk/keyrings/raw_keyring_factory.py +27 -0
  101. huaweicloud_encryptionsdk/keyrings/rawkeyring/__init__.py +0 -0
  102. huaweicloud_encryptionsdk/keyrings/rawkeyring/raw_aes_keyring.py +63 -0
  103. huaweicloud_encryptionsdk/keyrings/rawkeyring/raw_keyring.py +112 -0
  104. huaweicloud_encryptionsdk/keyrings/rawkeyring/raw_rsa_keyring.py +55 -0
  105. huaweicloud_encryptionsdk/keyrings/rawkeyring/raw_sm2_keyring.py +75 -0
  106. huaweicloud_encryptionsdk/keyrings/rawkeyring/raw_sm4_cbc_keyring.py +66 -0
  107. huaweicloud_encryptionsdk/keyrings/rawkeyring/raw_sm4_keyring.py +70 -0
  108. huaweicloud_encryptionsdk/kms/__init__.py +0 -0
  109. huaweicloud_encryptionsdk/kms/create_key_capsule_request.py +35 -0
  110. huaweicloud_encryptionsdk/kms/create_key_capsule_response.py +19 -0
  111. huaweicloud_encryptionsdk/kms/decrypt_key_capsule_request.py +32 -0
  112. huaweicloud_encryptionsdk/kms/decrypt_key_capsule_response.py +19 -0
  113. huaweicloud_encryptionsdk/kms/kms_http_client.py +116 -0
  114. huaweicloud_encryptionsdk/log/__init__.py +0 -0
  115. huaweicloud_encryptionsdk/log/log_config.py +30 -0
  116. huaweicloud_encryptionsdk/log/log_file_backup.py +144 -0
  117. huaweicloud_encryptionsdk/log/log_metrics.py +132 -0
  118. huaweicloud_encryptionsdk/log/log_reporter_manager.py +163 -0
  119. huaweicloud_encryptionsdk/log/lts_sdk_producer.py +386 -0
  120. huaweicloud_encryptionsdk/log/lts_struct_log_builder.py +106 -0
  121. huaweicloud_encryptionsdk/meterialmanager/__init__.py +0 -0
  122. huaweicloud_encryptionsdk/meterialmanager/cache_crypto_meterial_manager.py +221 -0
  123. huaweicloud_encryptionsdk/meterialmanager/crypto_meterial_manager.py +19 -0
  124. huaweicloud_encryptionsdk/meterialmanager/data_key_generate.py +10 -0
  125. huaweicloud_encryptionsdk/meterialmanager/data_key_generate_factory.py +20 -0
  126. huaweicloud_encryptionsdk/meterialmanager/default_crypto_meterials_manager.py +71 -0
  127. huaweicloud_encryptionsdk/meterialmanager/key_capsule_data_key_generate.py +63 -0
  128. huaweicloud_encryptionsdk/meterialmanager/kms_data_key_generate.py +65 -0
  129. huaweicloud_encryptionsdk/meterialmanager/local_data_key_generate.py +13 -0
  130. huaweicloud_encryptionsdk/model/__init__.py +0 -0
  131. huaweicloud_encryptionsdk/model/access_point_config.py +9 -0
  132. huaweicloud_encryptionsdk/model/cipher_body.py +81 -0
  133. huaweicloud_encryptionsdk/model/cipher_header.py +405 -0
  134. huaweicloud_encryptionsdk/model/ciphertext_data_key.py +166 -0
  135. huaweicloud_encryptionsdk/model/crypto_result.py +11 -0
  136. huaweicloud_encryptionsdk/model/data_key_materials.py +17 -0
  137. huaweicloud_encryptionsdk/model/data_materials.py +11 -0
  138. huaweicloud_encryptionsdk/model/enums/__init__.py +0 -0
  139. huaweicloud_encryptionsdk/model/enums/access_point_type.py +28 -0
  140. huaweicloud_encryptionsdk/model/enums/crypto_algorithm.py +75 -0
  141. huaweicloud_encryptionsdk/model/enums/data_key_generate_type.py +20 -0
  142. huaweicloud_encryptionsdk/model/enums/key_provider_enum.py +19 -0
  143. huaweicloud_encryptionsdk/model/enums/keyring_type_enum.py +27 -0
  144. huaweicloud_encryptionsdk/model/key_capsule.py +10 -0
  145. huaweicloud_encryptionsdk/model/key_capsule_config.py +12 -0
  146. huaweicloud_encryptionsdk/model/kms_config.py +10 -0
  147. huaweicloud_encryptionsdk/model/request/__init__.py +0 -0
  148. huaweicloud_encryptionsdk/model/request/encrypt_request.py +10 -0
  149. huaweicloud_encryptionsdk/model/request/sign_request.py +2 -0
  150. huaweicloud_encryptionsdk/model/request/verify_request.py +2 -0
  151. huaweicloud_encryptionsdk/proof/__init__.py +0 -0
  152. huaweicloud_encryptionsdk/proof/attestation_document.py +36 -0
  153. huaweicloud_encryptionsdk/proof/collector/__init__.py +0 -0
  154. huaweicloud_encryptionsdk/proof/collector/cce_proof_collector.py +24 -0
  155. huaweicloud_encryptionsdk/proof/collector/ecs_proof_collector.py +11 -0
  156. huaweicloud_encryptionsdk/proof/collector/key_pair_proof_collector.py +55 -0
  157. huaweicloud_encryptionsdk/proof/collector/ma_proof_collector.py +51 -0
  158. huaweicloud_encryptionsdk/proof/private_key_decrypt_callback.py +7 -0
  159. huaweicloud_encryptionsdk/proof/proof_collector.py +11 -0
  160. huaweicloud_encryptionsdk/proof/proof_collector_factory.py +53 -0
  161. huaweicloud_encryptionsdk/proof/proof_info.py +10 -0
  162. huaweicloud_encryptionsdk/util/__init__.py +0 -0
  163. huaweicloud_encryptionsdk/util/common_utils.py +12 -0
  164. tests/__init__.py +0 -0
  165. tests/test_enums.py +84 -0
  166. tests/test_exceptions.py +38 -0
  167. tests/test_factories.py +71 -0
  168. tests/test_huawei_crypto.py +67 -0
  169. tests/test_imports.py +46 -0
  170. tests/test_kms_models.py +95 -0
  171. tests/test_log_file_backup.py +45 -0
  172. tests/test_lts_struct_log_builder.py +97 -0
  173. tests/test_models.py +142 -0
  174. 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