dts-dance 0.2.5__tar.gz → 0.2.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {dts_dance-0.2.5 → dts_dance-0.2.6}/PKG-INFO +2 -2
  2. {dts_dance-0.2.5 → dts_dance-0.2.6}/dtsdance/bytecloud.py +8 -3
  3. {dts_dance-0.2.5 → dts_dance-0.2.6}/dtsdance/ddutil.py +13 -0
  4. {dts_dance-0.2.5 → dts_dance-0.2.6}/dtsdance/dflow.py +15 -20
  5. {dts_dance-0.2.5 → dts_dance-0.2.6}/dtsdance/dsyncer.py +4 -4
  6. {dts_dance-0.2.5 → dts_dance-0.2.6}/dtsdance/spacex.py +8 -8
  7. {dts_dance-0.2.5 → dts_dance-0.2.6}/dtsdance/tcc_inner.py +2 -2
  8. {dts_dance-0.2.5 → dts_dance-0.2.6}/pyproject.toml +2 -2
  9. {dts_dance-0.2.5 → dts_dance-0.2.6}/tests/test_dflow.py +8 -15
  10. {dts_dance-0.2.5 → dts_dance-0.2.6}/uv.lock +7 -7
  11. {dts_dance-0.2.5 → dts_dance-0.2.6}/.gitignore +0 -0
  12. {dts_dance-0.2.5 → dts_dance-0.2.6}/.python-version +0 -0
  13. {dts_dance-0.2.5 → dts_dance-0.2.6}/CLAUDE.md +0 -0
  14. {dts_dance-0.2.5 → dts_dance-0.2.6}/DEV.md +0 -0
  15. {dts_dance-0.2.5 → dts_dance-0.2.6}/README.md +0 -0
  16. {dts_dance-0.2.5 → dts_dance-0.2.6}/config.yaml +0 -0
  17. {dts_dance-0.2.5 → dts_dance-0.2.6}/dtsdance/__init__.py +0 -0
  18. {dts_dance-0.2.5 → dts_dance-0.2.6}/dtsdance/feishu_base.py +0 -0
  19. {dts_dance-0.2.5 → dts_dance-0.2.6}/dtsdance/feishu_table.py +0 -0
  20. {dts_dance-0.2.5 → dts_dance-0.2.6}/dtsdance/metrics_fe.py +0 -0
  21. {dts_dance-0.2.5 → dts_dance-0.2.6}/dtsdance/s3.py +0 -0
  22. {dts_dance-0.2.5 → dts_dance-0.2.6}/dtsdance/tcc_open.py +0 -0
  23. {dts_dance-0.2.5 → dts_dance-0.2.6}/tests/config.py +0 -0
  24. {dts_dance-0.2.5 → dts_dance-0.2.6}/tests/test_spacex.py +0 -0
  25. {dts_dance-0.2.5 → dts_dance-0.2.6}/tests/test_tcc.py +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dts-dance
3
- Version: 0.2.5
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>=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
@@ -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
  """
@@ -1,6 +1,7 @@
1
1
  from typing import Any, Optional
2
2
  from loguru import logger
3
3
  import requests
4
+ import json
4
5
 
5
6
 
6
7
  def make_request(method: str, url: str, headers: dict[str, str], payload: Optional[dict] = None) -> dict[str, Any]:
@@ -37,3 +38,15 @@ def make_request(method: str, url: str, headers: dict[str, str], payload: Option
37
38
  error_msg += f", response.text: {response.text}"
38
39
  logger.warning(error_msg)
39
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
@@ -1,8 +1,8 @@
1
1
  import collections
2
- from typing import Any, cast
2
+ from typing import Any
3
3
  from loguru import logger
4
4
 
5
- from dtsdance.ddutil import make_request
5
+ from dtsdance.ddutil import make_request, output_curl
6
6
  from .bytecloud import ByteCloudClient
7
7
 
8
8
 
@@ -19,12 +19,6 @@ class DFlowClient:
19
19
  def __init__(self, bytecloud_client: ByteCloudClient) -> None:
20
20
  self.bytecloud_client = bytecloud_client
21
21
 
22
- def build_request_headers(self, site: str, vregion: str | None) -> dict[str, str]:
23
- headers = self.bytecloud_client.build_request_headers(site)
24
- if vregion:
25
- headers["x-bcgw-vregion"] = vregion
26
- return headers
27
-
28
22
  def get_task_info(self, site: str, task_id: str, vregion: str | None = None) -> dict[str, Any]:
29
23
  """
30
24
  获取 DFlow 任务信息
@@ -43,7 +37,7 @@ class DFlowClient:
43
37
  # 构建请求数据
44
38
  payload = {"id": int(task_id)}
45
39
 
46
- response_data = make_request("POST", url, self.build_request_headers(site, vregion), payload)
40
+ response_data = make_request("POST", url, self.bytecloud_client.build_request_headers(site, vregion), payload)
47
41
 
48
42
  message = response_data.get("message")
49
43
  # logger.debug(f"get_task_info {site} {task_id}, message: {message}")
@@ -52,8 +46,7 @@ class DFlowClient:
52
46
  raise TaskNotFound(f"获取 DFlow 任务信息失败,站点: {site}, 任务 ID: {task_id} 不存在")
53
47
 
54
48
  try:
55
- data = cast(dict, response_data.get("data", {}))
56
- task = cast(dict, data.get("task", {}))
49
+ task = response_data.get("data", {}).get("task", {})
57
50
  # 提取核心信息
58
51
  filtered_data = {
59
52
  "task_id": task.get("id", ""),
@@ -85,7 +78,7 @@ class DFlowClient:
85
78
  # 构建请求数据
86
79
  payload = {"dflow_id": int(dflow_id)}
87
80
 
88
- response_data = make_request("POST", url, self.build_request_headers(site, vregion), payload)
81
+ response_data = make_request("POST", url, self.bytecloud_client.build_request_headers(site, vregion), payload)
89
82
 
90
83
  message = response_data.get("message", "")
91
84
  # logger.debug(f"get_dflow_info {site} {dflow_id}, message: {message}")
@@ -94,8 +87,7 @@ class DFlowClient:
94
87
  raise DFlowNotFound(f"获取 DFlow 进程信息失败,站点: {site}, 进程 ID: {dflow_id} 不存在")
95
88
 
96
89
  try:
97
- data = cast(dict, response_data.get("data", {}))
98
- dflow = cast(dict, data.get("dflow", {}))
90
+ dflow = response_data.get("data", {}).get("dflow", {})
99
91
  # 提取核心信息
100
92
  filtered_data = {
101
93
  "dflow_id": dflow.get("id", ""),
@@ -143,21 +135,21 @@ class DFlowClient:
143
135
  # 构建请求数据
144
136
  payload = {"ctrl_env": ctrl_env}
145
137
 
146
- response_data = make_request("POST", url, self.build_request_headers(site, None), payload)
138
+ response_data = make_request("POST", url, self.bytecloud_client.build_request_headers(site, None), payload)
147
139
 
148
140
  message = response_data.get("message")
149
141
  logger.info(f"int_resources {site} {ctrl_env}, message: {message}")
150
142
 
151
143
  return message == "ok"
152
144
 
153
- 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]:
154
146
  """
155
147
  列举 CTRL 环境资源列表,连同 agent 列表
156
148
 
157
149
  Args:
158
150
  site: 站点名称
159
151
  ctrl_env: 控制环境
160
-
152
+ vregion: 虚拟区域
161
153
  Returns:
162
154
  list[str]: CTRL 环境资源列表
163
155
  """
@@ -166,10 +158,13 @@ class DFlowClient:
166
158
  url = f"{site_info.endpoint}/api/v1/bytedts/api/bytedts/v3/DescribeResources"
167
159
 
168
160
  # 构建请求数据
161
+ headers = self.bytecloud_client.build_request_headers(site, vregion)
169
162
  payload = {"offset": 0, "limit": 10, "ctrl_env": ctrl_env}
163
+ response_data = make_request("POST", url, headers, payload)
170
164
 
171
- response_data = make_request("POST", url, self.build_request_headers(site, None), payload)
172
-
165
+ # curl_cmd = output_curl(url, headers, payload)
166
+ # print(f"Equivalent curl:\n{curl_cmd}")
167
+
173
168
  try:
174
169
  data = response_data.get("data", {})
175
170
  items = data.get("items", [])
@@ -181,7 +176,7 @@ class DFlowClient:
181
176
  """获取所有可用的控制环境列表,返回以domain为键,ctrl_env列表为值的字典"""
182
177
  site_info = self.bytecloud_client.get_site_config(site)
183
178
  url = f"{site_info.endpoint}/api/v1/bytedts/api/bytedts/v3/DescribeRegions"
184
- resp_json = make_request("POST", url, self.build_request_headers(site, None), {})
179
+ resp_json = make_request("POST", url, self.bytecloud_client.build_request_headers(site, None), {})
185
180
  if resp_json["code"] != 0:
186
181
  raise Exception(resp_json["message"])
187
182
 
@@ -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,11 +32,11 @@ 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
@@ -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()
@@ -27,7 +27,7 @@ class TCCInnerClient:
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
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "dts-dance"
7
- version = "0.2.5"
7
+ version = "0.2.6"
8
8
  description = "dts dance lib"
9
9
  readme = "README.md"
10
10
  keywords = ["tools", "observation"]
@@ -12,7 +12,7 @@ requires-python = ">=3.12"
12
12
  dependencies = [
13
13
  "requests>=2.32.5,<3.0.0",
14
14
  "loguru>=0.7.3,<0.8.0",
15
- "boto3>=1.42.30",
15
+ "boto3>=1.42.35",
16
16
  "pyyaml>=6.0.3,<7.0.0",
17
17
  "requests_toolbelt==1.0.0",
18
18
  ]
@@ -10,23 +10,16 @@ bytecloud_client = ByteCloudClient(site_configs)
10
10
  dflow_client = DFlowClient(bytecloud_client)
11
11
 
12
12
 
13
- # pytest tests/test_dflow.py::test_get_task_info1 -s
14
- def test_get_task_info1():
15
- info = dflow_client.get_task_info("cn", "106037095986690")
16
- print(f"DFlow Info: {info}")
17
-
18
-
19
- # pytest tests/test_dflow.py::test_get_task_info2 -s
20
- def test_get_task_info2():
21
- info = dflow_client.get_task_info("cn", "99868429582521")
22
- print(f"DFlow Info: {info}")
23
-
24
-
25
- # pytest tests/test_dflow.py::test_get_dflow_info -s
26
- def test_get_dflow_info():
27
- info = dflow_client.get_dflow_info("cn", "106029334786818")
13
+ # pytest tests/test_dflow.py::test_get_task_info -s
14
+ def test_get_task_info():
15
+ # info = dflow_client.get_task_info("cn", "106037095986690")
16
+ info = dflow_client.get_task_info("cn", "10457709471247")
28
17
  print(f"DFlow Info: {info}")
29
18
 
19
+ # pytest tests/test_dflow.py::test_list_resources -s
20
+ def test_list_resources():
21
+ resources = dflow_client.list_resources("cn", "cn_east", vregion = "China-East")
22
+ print(f"list_resources: {json.dumps(resources, indent=2)}")
30
23
 
31
24
  # pytest tests/test_dflow.py::test_get_all_ctrl_envs -s
32
25
  def test_get_all_ctrl_envs():
@@ -4,30 +4,30 @@ requires-python = ">=3.12"
4
4
 
5
5
  [[package]]
6
6
  name = "boto3"
7
- version = "1.42.30"
7
+ version = "1.42.36"
8
8
  source = { registry = "https://pypi.org/simple" }
9
9
  dependencies = [
10
10
  { name = "botocore" },
11
11
  { name = "jmespath" },
12
12
  { name = "s3transfer" },
13
13
  ]
14
- sdist = { url = "https://files.pythonhosted.org/packages/42/79/2dac8b7cb075cfa43908ee9af3f8ee06880d84b86013854c5cca8945afac/boto3-1.42.30.tar.gz", hash = "sha256:ba9cd2f7819637d15bfbeb63af4c567fcc8a7dcd7b93dd12734ec58601169538", size = 112809, upload-time = "2026-01-16T20:37:23.636Z" }
14
+ sdist = { url = "https://files.pythonhosted.org/packages/54/06/50ff808cf4f40efada8edc20f9d563ab287864423c874dfb94f755a60c52/boto3-1.42.36.tar.gz", hash = "sha256:a4eb51105c8c5d7b2bc2a9e2316e69baf69a55611275b9f189c0cf59f1aae171", size = 112839, upload-time = "2026-01-27T20:38:26.992Z" }
15
15
  wheels = [
16
- { url = "https://files.pythonhosted.org/packages/52/b3/2c0d828c9f668292e277ca5232e6160dd5b4b660a3f076f20dd5378baa1e/boto3-1.42.30-py3-none-any.whl", hash = "sha256:d7e548bea65e0ae2c465c77de937bc686b591aee6a352d5a19a16bc751e591c1", size = 140573, upload-time = "2026-01-16T20:37:22.089Z" },
16
+ { url = "https://files.pythonhosted.org/packages/8d/d1/35d12f04a7792e2f5e9ddff3c7b60493a32027761380dee7f24ee8ae80cc/boto3-1.42.36-py3-none-any.whl", hash = "sha256:e0ff6f2747bfdec63405b35ea185a7aea35239c3f4fe99e4d29368a6de9c4a84", size = 140604, upload-time = "2026-01-27T20:38:25.349Z" },
17
17
  ]
18
18
 
19
19
  [[package]]
20
20
  name = "botocore"
21
- version = "1.42.30"
21
+ version = "1.42.36"
22
22
  source = { registry = "https://pypi.org/simple" }
23
23
  dependencies = [
24
24
  { name = "jmespath" },
25
25
  { name = "python-dateutil" },
26
26
  { name = "urllib3" },
27
27
  ]
28
- sdist = { url = "https://files.pythonhosted.org/packages/44/38/23862628a0eb044c8b8b3d7a9ad1920b3bfd6bce6d746d5a871e8382c7e4/botocore-1.42.30.tar.gz", hash = "sha256:9bf1662b8273d5cc3828a49f71ca85abf4e021011c1f0a71f41a2ea5769a5116", size = 14891439, upload-time = "2026-01-16T20:37:13.77Z" }
28
+ sdist = { url = "https://files.pythonhosted.org/packages/66/4e/b24089cf7a77d38886ac4fbae300a3c4c6d68c1b9ccb66af03cb07b6c35c/botocore-1.42.36.tar.gz", hash = "sha256:2ebd89cc75927944e2cee51b7adce749f38e0cb269a758a6464a27f8bcca65fb", size = 14909073, upload-time = "2026-01-27T20:38:16.621Z" }
29
29
  wheels = [
30
- { url = "https://files.pythonhosted.org/packages/3d/8d/6d7b016383b1f74dd93611b1c5078bbaddaca901553ab886dcda87cae365/botocore-1.42.30-py3-none-any.whl", hash = "sha256:97070a438cac92430bb7b65f8ebd7075224f4a289719da4ee293d22d1e98db02", size = 14566340, upload-time = "2026-01-16T20:37:10.94Z" },
30
+ { url = "https://files.pythonhosted.org/packages/e6/e8/f14d25bd768187424b385bc6a44e2ed5e96964e461ba019add03e48713c7/botocore-1.42.36-py3-none-any.whl", hash = "sha256:2cfae4c482e5e87bd835ab4289b711490c161ba57e852c06b65a03e7c25e08eb", size = 14583066, upload-time = "2026-01-27T20:38:14.02Z" },
31
31
  ]
32
32
 
33
33
  [[package]]
@@ -119,7 +119,7 @@ dependencies = [
119
119
 
120
120
  [package.metadata]
121
121
  requires-dist = [
122
- { name = "boto3", specifier = ">=1.42.30" },
122
+ { name = "boto3", specifier = ">=1.42.35" },
123
123
  { name = "loguru", specifier = ">=0.7.3,<0.8.0" },
124
124
  { name = "pyyaml", specifier = ">=6.0.3,<7.0.0" },
125
125
  { name = "requests", specifier = ">=2.32.5,<3.0.0" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes