dts-dance 0.2.4__py3-none-any.whl → 0.2.6__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.
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dts-dance
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: dts dance lib
5
5
  Keywords: observation,tools
6
6
  Requires-Python: >=3.12
7
- Requires-Dist: boto3<2.0.0,>=1.42.30
7
+ Requires-Dist: boto3>=1.42.35
8
8
  Requires-Dist: loguru<0.8.0,>=0.7.3
9
9
  Requires-Dist: pyyaml<7.0.0,>=6.0.3
10
10
  Requires-Dist: requests-toolbelt==1.0.0
@@ -0,0 +1,15 @@
1
+ dtsdance/__init__.py,sha256=Yl_jEZ5weYfcrklnDvwB4wSgCOvMBLRRgWx0gHs3qfM,49
2
+ dtsdance/bytecloud.py,sha256=LZ-uw5NMmapyNpt89bMKhCgv4U2cGR-XjLzFFFEXGxM,5899
3
+ dtsdance/ddutil.py,sha256=1ZUmW11nw8WW1NfAIRbjuKy8zeQVTFvoXl6QY3o4YJ0,1534
4
+ dtsdance/dflow.py,sha256=jwsngCqgHUSAUFh-IUgq-fnQs8lAvHtj7uLLs_tupqI,7718
5
+ dtsdance/dsyncer.py,sha256=PCatQIFXgi1l2qT60zNN-ALAqtv7_sg-D2oNMB_t8qw,9906
6
+ dtsdance/feishu_base.py,sha256=dYYNnsCjGZYN2I6inR9FRQoUiDLB7j40rzydw50N38o,11825
7
+ dtsdance/feishu_table.py,sha256=JleCjQJ-rcbC15v2UAc5BK-YioB2SMGsdGztQ9BaT4k,10779
8
+ dtsdance/metrics_fe.py,sha256=hzIl5BJmuCrkqJOHELVzXm3YAqrPttbyVkKBglS4mgQ,18978
9
+ dtsdance/s3.py,sha256=Bh-cwLksfO5PewNtIzE_Md3rRLDLI1DUVoOD7Pou5T8,1294
10
+ dtsdance/spacex.py,sha256=W_YGt_Wo_Cy4eDhgXhNfskayPHcZ1kg2Mil2ghI_VLE,4791
11
+ dtsdance/tcc_inner.py,sha256=QqVyhwP0kCdp1HFHFgtme9o0im-eQNn1HVGdLt3Xr8g,1627
12
+ dtsdance/tcc_open.py,sha256=JIfVhL3yfm0ftS_vzONE2QNzfqcZuX1mPTvjovAtwmE,6893
13
+ dts_dance-0.2.6.dist-info/METADATA,sha256=cUaSHfyZVfa3EzpzyvyIJuiot0s1e166G3Omzzk3vMU,826
14
+ dts_dance-0.2.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
15
+ dts_dance-0.2.6.dist-info/RECORD,,
dtsdance/bytecloud.py CHANGED
@@ -145,18 +145,23 @@ class ByteCloudClient:
145
145
  site_config = self.get_site_config(site)
146
146
  return self._acquire_jwt_token(site_config.endpoint, site_config.svc_secret)
147
147
 
148
- def build_request_headers(self, site: str) -> dict[str, str]:
148
+ def build_request_headers(self, site: str, vregion: str | None = None) -> dict[str, str]:
149
149
  """
150
150
  构建请求头
151
151
 
152
152
  Args:
153
153
  site: 站点名称
154
-
154
+ vregion: 默认为 None
155
155
  Returns:
156
156
  dict[str, str]: 请求头字典
157
157
  """
158
158
  jwt_token = self.get_jwt_token(site)
159
- return {"Content-Type": "application/json", "x-jwt-token": jwt_token}
159
+ headers = {"Content-Type": "application/json", "x-jwt-token": jwt_token}
160
+ if vregion:
161
+ headers["x-bcgw-vregion"] = vregion
162
+ headers["get-svc"] = "1" # response header 中显示实际请求的机房
163
+
164
+ return headers
160
165
 
161
166
  def get_site_config(self, site: str) -> SiteConfig:
162
167
  """
dtsdance/ddutil.py ADDED
@@ -0,0 +1,52 @@
1
+ from typing import Any, Optional
2
+ from loguru import logger
3
+ import requests
4
+ import json
5
+
6
+
7
+ def make_request(method: str, url: str, headers: dict[str, str], payload: Optional[dict] = None) -> dict[str, Any]:
8
+ """
9
+ 发送 HTTP 请求的通用方法
10
+
11
+ Args:
12
+ method: HTTP 方法 (GET/POST)
13
+ url: 请求 URL
14
+ headers: 请求头
15
+ payload: POST 请求的 JSON 数据
16
+
17
+ Returns:
18
+ dict[str, Any]: 解析后的 JSON 响应
19
+ """
20
+ response = None
21
+ try:
22
+ if method.upper() == "GET":
23
+ response = requests.get(url, headers=headers)
24
+ elif method.upper() == "POST":
25
+ response = requests.post(url, json=payload, headers=headers)
26
+ else:
27
+ raise ValueError(f"不支持的 HTTP 方法: {method}")
28
+
29
+ # 检查响应状态码
30
+ # response.raise_for_status()
31
+
32
+ # 解析 JSON 响应
33
+ return response.json()
34
+
35
+ except Exception as e:
36
+ error_msg = f"make_request occur error, error: {e}"
37
+ if response is not None:
38
+ error_msg += f", response.text: {response.text}"
39
+ logger.warning(error_msg)
40
+ raise
41
+
42
+
43
+ def output_curl(url: str, headers: dict[str, str], payload: dict) -> str:
44
+ """
45
+ 生成等效的 curl 命令字符串
46
+ """
47
+ curl_headers = " \\\n ".join([f"-H '{k}: {v}'" for k, v in headers.items()])
48
+ curl_payload = json.dumps(payload, ensure_ascii=False)
49
+ curl_cmd = f"""curl -X POST '{url}' \\
50
+ {curl_headers} \\
51
+ -d '{curl_payload}'"""
52
+ return curl_cmd
dtsdance/dflow.py CHANGED
@@ -1,7 +1,9 @@
1
- from typing import Any, cast, Optional
1
+ import collections
2
+ from typing import Any
2
3
  from loguru import logger
4
+
5
+ from dtsdance.ddutil import make_request, output_curl
3
6
  from .bytecloud import ByteCloudClient
4
- import requests
5
7
 
6
8
 
7
9
  class TaskNotFound(Exception):
@@ -17,42 +19,7 @@ class DFlowClient:
17
19
  def __init__(self, bytecloud_client: ByteCloudClient) -> None:
18
20
  self.bytecloud_client = bytecloud_client
19
21
 
20
- def _make_request(self, method: str, url: str, headers: dict[str, str], json_data: Optional[dict] = None) -> dict[str, Any]:
21
- """
22
- 发送 HTTP 请求的通用方法
23
-
24
- Args:
25
- method: HTTP 方法 (GET/POST)
26
- url: 请求 URL
27
- headers: 请求头
28
- json_data: POST 请求的 JSON 数据
29
-
30
- Returns:
31
- dict[str, Any]: 解析后的 JSON 响应
32
- """
33
- response = None
34
- try:
35
- if method.upper() == "GET":
36
- response = requests.get(url, headers=headers)
37
- elif method.upper() == "POST":
38
- response = requests.post(url, json=json_data, headers=headers)
39
- else:
40
- raise ValueError(f"不支持的 HTTP 方法: {method}")
41
-
42
- # 检查响应状态码
43
- response.raise_for_status()
44
-
45
- # 解析 JSON 响应
46
- return response.json()
47
-
48
- except Exception as e:
49
- error_msg = f"_make_request occur error, error: {e}"
50
- if response is not None:
51
- error_msg += f", response.text: {response.text}"
52
- logger.warning(error_msg)
53
- raise
54
-
55
- def get_task_info(self, site: str, task_id: str) -> dict[str, Any]:
22
+ def get_task_info(self, site: str, task_id: str, vregion: str | None = None) -> dict[str, Any]:
56
23
  """
57
24
  获取 DFlow 任务信息
58
25
 
@@ -68,9 +35,9 @@ class DFlowClient:
68
35
  url = f"{site_info.endpoint}/api/v1/bytedts/api/bytedts/v3/DescribeTaskInfo"
69
36
 
70
37
  # 构建请求数据
71
- json_data = {"id": int(task_id)}
38
+ payload = {"id": int(task_id)}
72
39
 
73
- response_data = self._make_request("POST", url, self.bytecloud_client.build_request_headers(site), json_data)
40
+ response_data = make_request("POST", url, self.bytecloud_client.build_request_headers(site, vregion), payload)
74
41
 
75
42
  message = response_data.get("message")
76
43
  # logger.debug(f"get_task_info {site} {task_id}, message: {message}")
@@ -79,8 +46,7 @@ class DFlowClient:
79
46
  raise TaskNotFound(f"获取 DFlow 任务信息失败,站点: {site}, 任务 ID: {task_id} 不存在")
80
47
 
81
48
  try:
82
- data = cast(dict, response_data.get("data", {}))
83
- task = cast(dict, data.get("task", {}))
49
+ task = response_data.get("data", {}).get("task", {})
84
50
  # 提取核心信息
85
51
  filtered_data = {
86
52
  "task_id": task.get("id", ""),
@@ -94,7 +60,7 @@ class DFlowClient:
94
60
  except (KeyError, AttributeError, Exception) as e:
95
61
  raise Exception(f"无法从响应中提取 DFlow 任务信息数据: {str(e)}")
96
62
 
97
- def get_dflow_info(self, site: str, dflow_id: str) -> dict[str, Any]:
63
+ def get_dflow_info(self, site: str, dflow_id: str, vregion: str | None = None) -> dict[str, Any]:
98
64
  """
99
65
  获取 DFlow 进程信息
100
66
 
@@ -110,9 +76,9 @@ class DFlowClient:
110
76
  url = f"{site_info.endpoint}/api/v1/bytedts/api/bytedts/v3/DescribeDFlowDetail"
111
77
 
112
78
  # 构建请求数据
113
- json_data = {"dflow_id": int(dflow_id)}
79
+ payload = {"dflow_id": int(dflow_id)}
114
80
 
115
- response_data = self._make_request("POST", url, self.bytecloud_client.build_request_headers(site), json_data)
81
+ response_data = make_request("POST", url, self.bytecloud_client.build_request_headers(site, vregion), payload)
116
82
 
117
83
  message = response_data.get("message", "")
118
84
  # logger.debug(f"get_dflow_info {site} {dflow_id}, message: {message}")
@@ -121,8 +87,7 @@ class DFlowClient:
121
87
  raise DFlowNotFound(f"获取 DFlow 进程信息失败,站点: {site}, 进程 ID: {dflow_id} 不存在")
122
88
 
123
89
  try:
124
- data = cast(dict, response_data.get("data", {}))
125
- dflow = cast(dict, data.get("dflow", {}))
90
+ dflow = response_data.get("data", {}).get("dflow", {})
126
91
  # 提取核心信息
127
92
  filtered_data = {
128
93
  "dflow_id": dflow.get("id", ""),
@@ -168,23 +133,23 @@ class DFlowClient:
168
133
  url = f"{site_info.endpoint}/api/v1/bytedts/api/bytedts/v3/InitSystemResource"
169
134
 
170
135
  # 构建请求数据
171
- json_data = {"ctrl_env": ctrl_env}
136
+ payload = {"ctrl_env": ctrl_env}
172
137
 
173
- response_data = self._make_request("POST", url, self.bytecloud_client.build_request_headers(site), json_data)
138
+ response_data = make_request("POST", url, self.bytecloud_client.build_request_headers(site, None), payload)
174
139
 
175
140
  message = response_data.get("message")
176
141
  logger.info(f"int_resources {site} {ctrl_env}, message: {message}")
177
142
 
178
143
  return message == "ok"
179
144
 
180
- def list_resources(self, site: str, ctrl_env: str) -> list[str]:
145
+ def list_resources(self, site: str, ctrl_env: str, vregion: str | None = None) -> list[str]:
181
146
  """
182
147
  列举 CTRL 环境资源列表,连同 agent 列表
183
148
 
184
149
  Args:
185
150
  site: 站点名称
186
151
  ctrl_env: 控制环境
187
-
152
+ vregion: 虚拟区域
188
153
  Returns:
189
154
  list[str]: CTRL 环境资源列表
190
155
  """
@@ -193,13 +158,44 @@ class DFlowClient:
193
158
  url = f"{site_info.endpoint}/api/v1/bytedts/api/bytedts/v3/DescribeResources"
194
159
 
195
160
  # 构建请求数据
196
- json_data = {"offset": 0, "limit": 10, "ctrl_env": ctrl_env}
197
-
198
- response_data = self._make_request("POST", url, self.bytecloud_client.build_request_headers(site), json_data)
161
+ headers = self.bytecloud_client.build_request_headers(site, vregion)
162
+ payload = {"offset": 0, "limit": 10, "ctrl_env": ctrl_env}
163
+ response_data = make_request("POST", url, headers, payload)
199
164
 
165
+ # curl_cmd = output_curl(url, headers, payload)
166
+ # print(f"Equivalent curl:\n{curl_cmd}")
167
+
200
168
  try:
201
169
  data = response_data.get("data", {})
202
170
  items = data.get("items", [])
203
171
  return [item["name"] for item in items]
204
172
  except (KeyError, AttributeError, Exception) as e:
205
173
  raise Exception(f"无法从响应中提取 CTRL 环境资源列表数据: {str(e)}")
174
+
175
+ def get_all_ctrl_envs(self, site: str) -> dict[str, list[str]]:
176
+ """获取所有可用的控制环境列表,返回以domain为键,ctrl_env列表为值的字典"""
177
+ site_info = self.bytecloud_client.get_site_config(site)
178
+ url = f"{site_info.endpoint}/api/v1/bytedts/api/bytedts/v3/DescribeRegions"
179
+ resp_json = make_request("POST", url, self.bytecloud_client.build_request_headers(site, None), {})
180
+ if resp_json["code"] != 0:
181
+ raise Exception(resp_json["message"])
182
+
183
+ # print(f"Fetched regions info: {json.dumps(resp_json["data"], ensure_ascii=False, indent=2)}")
184
+
185
+ # 提取所有控制环境列表,格式为以domain为键,ctrl_env列表为值的字典
186
+ all_ctrl_envs_by_domain = collections.defaultdict(list[str])
187
+
188
+ # 从ops_platform_region中提取ctrl_env_list
189
+ if "bytedts_env" in resp_json["data"]:
190
+ for bytedts_env in resp_json["data"]["bytedts_env"]:
191
+ if "ctrl_env_list" in bytedts_env and "domain" in bytedts_env:
192
+ env = bytedts_env["env"]
193
+ region = bytedts_env["include_region"][0] if bytedts_env["include_region"] else "Unknown"
194
+ env_region_key = env + "|" + region
195
+ for ctrl_env_item in bytedts_env["ctrl_env_list"]:
196
+ if "ctrl_env" in ctrl_env_item:
197
+ ctrl_env = ctrl_env_item["ctrl_env"]
198
+ # 将ctrl_env添加到domain对应的列表中
199
+ all_ctrl_envs_by_domain[env_region_key].append(ctrl_env)
200
+
201
+ return dict(all_ctrl_envs_by_domain)
dtsdance/dsyncer.py CHANGED
@@ -1,7 +1,7 @@
1
+ from dtsdance.ddutil import make_request
1
2
  from .bytecloud import ByteCloudClient
2
3
  from typing import Any, NamedTuple, Tuple, cast, Optional
3
4
  from loguru import logger
4
- import requests
5
5
  import re
6
6
  from datetime import datetime
7
7
 
@@ -21,7 +21,7 @@ class DSyncerClient:
21
21
  self.envs = envs
22
22
  self.bytecloud_client = bytecloud_client
23
23
 
24
- def _build_headers(self, env: str, secret_api: bool = False) -> dict[str, str]:
24
+ def _build_headers(self, site: str, secret_api: bool = False) -> dict[str, str]:
25
25
  """
26
26
  构建请求头
27
27
 
@@ -32,51 +32,15 @@ class DSyncerClient:
32
32
  Returns:
33
33
  dict[str, str]: 请求头字典
34
34
  """
35
- jwt_token = self.bytecloud_client.get_jwt_token(env)
36
- headers = {"X-Jwt-Token": jwt_token, "x-bcgw-vregion": env}
35
+ jwt_token = self.bytecloud_client.get_jwt_token(site)
36
+ headers = {"X-Jwt-Token": jwt_token}
37
37
 
38
38
  if secret_api:
39
- token = self.envs[env].token
39
+ token = self.envs[site].token
40
40
  headers["Authorization"] = f"Token {token}"
41
41
 
42
42
  return headers
43
43
 
44
- def _make_request(self, method: str, url: str, headers: dict[str, str], data_raw: Optional[dict] = None) -> dict[str, Any]:
45
- """
46
- 发送 HTTP 请求的通用方法
47
-
48
- Args:
49
- method: HTTP 方法 (GET/POST)
50
- url: 请求 URL
51
- headers: 请求头
52
- json_data: POST 请求的 JSON 数据
53
- operation_name: 操作名称,用于日志记录
54
-
55
- Returns:
56
- dict[str, Any]: 解析后的 JSON 响应
57
- """
58
- response = None
59
- try:
60
- if method.upper() == "GET":
61
- response = requests.get(url, headers=headers)
62
- elif method.upper() == "POST":
63
- response = requests.post(url, json=data_raw, headers=headers)
64
- else:
65
- raise ValueError(f"不支持的 HTTP 方法: {method}")
66
-
67
- # 检查响应状态码
68
- # response.raise_for_status()
69
-
70
- # 解析 JSON 响应
71
- return response.json()
72
-
73
- except Exception as e:
74
- error_msg = f"_make_request occur error, error: {e}"
75
- if response is not None:
76
- error_msg += f", response.text: {response.text}"
77
- logger.warning(error_msg)
78
- raise
79
-
80
44
  def _acquire_task_info(self, site: str, task_id: str) -> dict[str, str]:
81
45
  """
82
46
  获取 DSyncer 任务信息
@@ -95,7 +59,7 @@ class DSyncerClient:
95
59
  # 准备请求头
96
60
  headers = self._build_headers(site)
97
61
 
98
- return self._make_request("GET", url, headers)
62
+ return make_request("GET", url, headers)
99
63
 
100
64
  def get_dflow_task_info(self, site: str, task_id: str) -> tuple[str, str]:
101
65
  """
@@ -108,7 +72,7 @@ class DSyncerClient:
108
72
  # 准备请求头
109
73
  headers = self._build_headers(site)
110
74
 
111
- json_data = self._make_request("GET", url, headers)
75
+ json_data = make_request("GET", url, headers)
112
76
 
113
77
  message = json_data.get("message", "")
114
78
  logger.debug(f"get task migrate info {site} {task_id}, message: {message}")
@@ -214,9 +178,9 @@ class DSyncerClient:
214
178
  # 准备请求头
215
179
  headers = self._build_headers(site, secret_api=True)
216
180
 
217
- data_raw = {"task_id": task_id}
181
+ payload = {"task_id": task_id}
218
182
 
219
- response_data = self._make_request("POST", url, headers, data_raw)
183
+ response_data = make_request("POST", url, headers, payload)
220
184
 
221
185
  message = response_data.get("message", "")
222
186
  logger.debug(f"get task migrate status {site} {task_id}, message: {message}")
@@ -236,9 +200,9 @@ class DSyncerClient:
236
200
  # 准备请求头
237
201
  headers = self._build_headers(site, secret_api=True)
238
202
 
239
- data_raw = {"task_id_list": [task_id]}
203
+ payload = {"task_id_list": [task_id]}
240
204
 
241
- response_data = self._make_request("POST", url, headers, data_raw)
205
+ response_data = make_request("POST", url, headers, payload)
242
206
 
243
207
  logger.debug(f"migration_mark_rollback return {site} {task_id}, json_data: {response_data}")
244
208
  success_task = response_data.get("data", {}).get("success_task", [])
@@ -255,9 +219,9 @@ class DSyncerClient:
255
219
  # 准备请求头
256
220
  headers = self._build_headers(site, secret_api=True)
257
221
 
258
- data_raw = {"task_id": task_id}
222
+ payload = {"task_id": task_id}
259
223
 
260
- response_data = self._make_request("POST", url, headers, data_raw)
224
+ response_data = make_request("POST", url, headers, payload)
261
225
 
262
226
  logger.debug(f"migration_mark_success return {site} {task_id}, json_data: {response_data}")
263
227
  success_task = response_data.get("data", {}).get("success_task", [])
@@ -277,7 +241,7 @@ class DSyncerClient:
277
241
  # 准备请求头
278
242
  headers = self._build_headers(site, secret_api=True)
279
243
 
280
- data_raw = {
244
+ payload = {
281
245
  "task_id": task_id,
282
246
  "delay_threshold": "20s",
283
247
  "dsyncer_task_pause_threshold": "180s",
@@ -289,7 +253,7 @@ class DSyncerClient:
289
253
  "app_parallel": app_parallel,
290
254
  }
291
255
 
292
- response_data = self._make_request("POST", url, headers, data_raw)
256
+ response_data = make_request("POST", url, headers, payload)
293
257
 
294
258
  logger.debug(f"migrate_task return {site} {task_id}, json_data: {response_data}")
295
259
  message = response_data.get("message")
dtsdance/feishu_base.py CHANGED
@@ -100,13 +100,15 @@ class FeishuBase:
100
100
  headers["Authorization"] = f"Bearer {token}"
101
101
  kwargs["headers"] = headers
102
102
 
103
+ response: requests.Response | None = None
103
104
  try:
104
105
  response = requests.request(method, url, **kwargs)
105
- logger.debug(f"response_json: {response.json()}")
106
+ # logger.debug(f"response_json: {response.json()}")
106
107
  response.raise_for_status()
107
108
  return response
108
109
  except requests.RequestException as e:
109
- logger.warning(f"API请求失败: {method} {url}, error: {e}")
110
+ error_detail = response.json() if response is not None else "无响应"
111
+ logger.warning(f"API请求失败: {method} {url}, error: {e}, response_json: {error_detail}")
110
112
  raise
111
113
 
112
114
  def upload_image(self, image_path: str) -> str | None:
@@ -265,7 +267,7 @@ class FeishuBase:
265
267
  logger.warning(f"回复消息失败: {e}")
266
268
  raise
267
269
 
268
- def batch_get_user_ids(self, emails: list[str]) -> list[str]:
270
+ def batch_get_user_ids(self, emails: list[str]) -> dict[str, None | str]:
269
271
  """
270
272
  批量获取用户ID
271
273
 
@@ -273,7 +275,7 @@ class FeishuBase:
273
275
  emails: 用户邮箱列表
274
276
 
275
277
  Returns:
276
- 批量获取结果(用户ID列表)
278
+ 批量获取结果(email -> user_id 的字典映射)
277
279
 
278
280
  Raises:
279
281
  Exception: 获取失败
@@ -281,16 +283,51 @@ class FeishuBase:
281
283
  payload = {"emails": emails, "include_resigned": False}
282
284
 
283
285
  try:
284
- response = self.make_request("POST", f"/contact/v3/users/batch_get_id?user_id_type=user_id", json=payload)
286
+ response = self.make_request("POST", "/contact/v3/users/batch_get_id?user_id_type=user_id", json=payload)
285
287
  result = response.json()
286
288
  if result.get("code") != 0:
287
289
  raise Exception(f"请求失败: {result.get('msg')}")
288
290
 
289
- user_ids = [user.get("user_id") for user in result.get("data", {}).get("user_list", [])]
291
+ user_dict = {user.get("email"): user.get("user_id", None) for user in result.get("data", {}).get("user_list", []) if user.get("email")}
290
292
 
291
293
  logger.debug(f"发送响应logid: {response.headers.get('X-Tt-Logid')}")
292
294
 
293
- return user_ids
295
+ return user_dict
294
296
  except Exception as e:
295
297
  logger.warning(f"批量获取用户ID失败: {e}")
296
298
  raise
299
+
300
+ def batch_get_all_user_ids(self, emails: list[str]) -> dict[str, None | str]:
301
+ """
302
+ 批量获取所有用户ID,自动分批处理(每批最多50个)
303
+
304
+ Args:
305
+ emails: 用户邮箱列表
306
+
307
+ Returns:
308
+ 所有用户ID字典(email -> user_id 的映射)
309
+
310
+ Raises:
311
+ Exception: 获取失败
312
+ """
313
+ if not emails:
314
+ return {}
315
+
316
+ all_user_dict = {}
317
+ batch_size = 50
318
+
319
+ # 分批处理
320
+ for i in range(0, len(emails), batch_size):
321
+ batch_emails = emails[i : i + batch_size]
322
+ logger.info(f"正在获取第 {i // batch_size + 1} 批用户ID,数量: {len(batch_emails)}")
323
+
324
+ try:
325
+ batch_user_dict = self.batch_get_user_ids(batch_emails)
326
+ all_user_dict.update(batch_user_dict)
327
+ except Exception as e:
328
+ logger.error(f"获取第 {i // batch_size + 1} 批用户ID失败: {e}")
329
+ # 继续处理下一批,不中断整个流程
330
+ continue
331
+
332
+ logger.info(f"共获取到 {len(all_user_dict)} 个用户ID")
333
+ return all_user_dict
@@ -40,11 +40,11 @@ class SpaceXClient:
40
40
  logger.warning(f"do quest queryServerMeta exception: {e}")
41
41
  raise
42
42
 
43
- def register_resource(self, site: str, data_raw: dict[str, Any]) -> bool:
43
+ def register_resource(self, site: str, payload: dict[str, Any]) -> bool:
44
44
  site_info = self.bytecloud_client.get_site_config(site)
45
45
  url = f"{site_info.endpoint_bytedts_spacex}/resource/v1/registerResource"
46
46
  try:
47
- response = requests.post(url, json=data_raw, headers=self.bytecloud_client.build_request_headers(site))
47
+ response = requests.post(url, json=payload, headers=self.bytecloud_client.build_request_headers(site))
48
48
  # print(f"response status code: {response.status_code}, response text: {response.text}")
49
49
 
50
50
  response.raise_for_status()
@@ -56,11 +56,11 @@ class SpaceXClient:
56
56
  logger.warning(f"do quest registerResource exception: {e}")
57
57
  raise
58
58
 
59
- def list_resources(self, site: str, data_raw: dict[str, Any]) -> list[str]:
59
+ def list_resources(self, site: str, payload: dict[str, Any]) -> list[str]:
60
60
  site_info = self.bytecloud_client.get_site_config(site)
61
61
  url = f"{site_info.endpoint_bytedts_spacex}/resource/v1/listResource"
62
62
  try:
63
- response = requests.post(url, json=data_raw, headers=self.bytecloud_client.build_request_headers(site))
63
+ response = requests.post(url, json=payload, headers=self.bytecloud_client.build_request_headers(site))
64
64
  # print(f"response status code: {response.status_code}, response text: {response.text}")
65
65
 
66
66
  response.raise_for_status()
@@ -78,7 +78,7 @@ class SpaceXClient:
78
78
  def register_gateway(self, site: str, gateway_info: GatewayInfo) -> bool:
79
79
  site_info = self.bytecloud_client.get_site_config(site)
80
80
  url = f"{site_info.endpoint_bytedts_spacex}/bytedts/v1/registryGateway"
81
- data_raw = {
81
+ payload = {
82
82
  "region": gateway_info.mgr_name,
83
83
  "server_region": gateway_info.mgr_name,
84
84
  "cluster_region": gateway_info.ctrl_name,
@@ -97,7 +97,7 @@ class SpaceXClient:
97
97
  "unified_tao_service": 1,
98
98
  }
99
99
  try:
100
- response = requests.post(url, json=data_raw, headers=self.bytecloud_client.build_request_headers(site))
100
+ response = requests.post(url, json=payload, headers=self.bytecloud_client.build_request_headers(site))
101
101
  response.raise_for_status()
102
102
 
103
103
  result = response.json()
@@ -107,11 +107,11 @@ class SpaceXClient:
107
107
  logger.warning(f"do quest registryGateway exception: {e}")
108
108
  raise
109
109
 
110
- def gateway_operation(self, site: str, operation: str, data_raw: dict[str, Any]) -> dict[str, Any]:
110
+ def gateway_operation(self, site: str, operation: str, payload: dict[str, Any]) -> dict[str, Any]:
111
111
  site_info = self.bytecloud_client.get_site_config(site)
112
112
  url = f"{site_info.endpoint_bytedts_spacex}/bytedts/v1/{operation}"
113
113
  try:
114
- response = requests.post(url, json=data_raw, headers=self.bytecloud_client.build_request_headers(site))
114
+ response = requests.post(url, json=payload, headers=self.bytecloud_client.build_request_headers(site))
115
115
  # print(f"response status code: {response.status_code}, response text: {response.text}")
116
116
 
117
117
  response.raise_for_status()
dtsdance/tcc_inner.py CHANGED
@@ -20,14 +20,14 @@ class TCCInnerClient:
20
20
  region: str,
21
21
  dir: str,
22
22
  conf_name: str,
23
- ) -> dict[str, Any]:
23
+ ) -> list[dict[str, Any]]:
24
24
  """
25
25
  List TCC configurations.
26
26
  """
27
27
  site_info = self.bytecloud_client.get_site_config(site)
28
28
  url = f"{site_info.endpoint}/api/v3/tcc/bcc/config/list_v2"
29
29
 
30
- data_raw = {
30
+ payload = {
31
31
  "ns_name": ns_name,
32
32
  "region": region,
33
33
  "dir_path": dir,
@@ -40,7 +40,7 @@ class TCCInnerClient:
40
40
  }
41
41
 
42
42
  try:
43
- response = requests.post(url, headers=self.bytecloud_client.build_request_headers(site), json=data_raw, timeout=30)
43
+ response = requests.post(url, headers=self.bytecloud_client.build_request_headers(site), json=payload, timeout=30)
44
44
  response.raise_for_status()
45
45
  result = response.json()
46
46
 
@@ -49,7 +49,7 @@ class TCCInnerClient:
49
49
  if base_resp.get("error_code", 0) != 0:
50
50
  raise TCCError(f"TCC API error: {base_resp.get('error_message', 'Unknown error')}")
51
51
 
52
- return result.get("data", {})
52
+ return result.get("data", [])
53
53
 
54
54
  except requests.RequestException as e:
55
55
  raise TCCError(f"Failed to get TCC config: {str(e)}") from e
dtsdance/tcc_open.py CHANGED
@@ -40,9 +40,9 @@ class TCCClient:
40
40
  self,
41
41
  ns_name: str,
42
42
  region: str,
43
+ dir: str,
43
44
  conf_name: str,
44
45
  value: str,
45
- dir: str,
46
46
  description: str,
47
47
  data_type: str,
48
48
  ) -> dict[str, Any]:
@@ -1,14 +0,0 @@
1
- dtsdance/__init__.py,sha256=Yl_jEZ5weYfcrklnDvwB4wSgCOvMBLRRgWx0gHs3qfM,49
2
- dtsdance/bytecloud.py,sha256=s1suo-YexMH6gUHYwC6P5k6AeyOm7Q_acYh_gHCDVjI,5654
3
- dtsdance/dflow.py,sha256=ggwNB2x-LgnpI9nzNTTnYnGfYyGH5rYcHHBhC4pOlfg,7059
4
- dtsdance/dsyncer.py,sha256=3Oj1Ko5FuB_sUm9Hj-hBy95wee3b6TH2FXKoPFGsakM,11193
5
- dtsdance/feishu_base.py,sha256=Dsl8CZZ3wyZf3TEiiUAs3_WpX1pinCuWFwqpm29ROVc,10414
6
- dtsdance/feishu_table.py,sha256=JleCjQJ-rcbC15v2UAc5BK-YioB2SMGsdGztQ9BaT4k,10779
7
- dtsdance/metrics_fe.py,sha256=hzIl5BJmuCrkqJOHELVzXm3YAqrPttbyVkKBglS4mgQ,18978
8
- dtsdance/s3.py,sha256=Bh-cwLksfO5PewNtIzE_Md3rRLDLI1DUVoOD7Pou5T8,1294
9
- dtsdance/spacex_bytedts.py,sha256=oVnHXTj-cuq2vcsUJq33_aaq0TQp7A52kldvspKueI4,4799
10
- dtsdance/tcc_inner.py,sha256=6Tuq_r2nNCK_HC4hqEGh7UVAa8kDtuIcU95WBu4zuFQ,1623
11
- dtsdance/tcc_open.py,sha256=Qb_ue3xH0CSsoReItdFKuvW7JsDTfFkDNIB_4zjzSQI,6893
12
- dts_dance-0.2.4.dist-info/METADATA,sha256=LY_7WWh06QNmhr_1vyuMTtKDrx88N0P1BjGEURYAl6U,833
13
- dts_dance-0.2.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
- dts_dance-0.2.4.dist-info/RECORD,,