dts-dance 0.2.6__py3-none-any.whl → 0.2.8__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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dts-dance
3
- Version: 0.2.6
3
+ Version: 0.2.8
4
4
  Summary: dts dance lib
5
5
  Keywords: observation,tools
6
6
  Requires-Python: >=3.12
@@ -1,7 +1,7 @@
1
1
  dtsdance/__init__.py,sha256=Yl_jEZ5weYfcrklnDvwB4wSgCOvMBLRRgWx0gHs3qfM,49
2
2
  dtsdance/bytecloud.py,sha256=LZ-uw5NMmapyNpt89bMKhCgv4U2cGR-XjLzFFFEXGxM,5899
3
3
  dtsdance/ddutil.py,sha256=1ZUmW11nw8WW1NfAIRbjuKy8zeQVTFvoXl6QY3o4YJ0,1534
4
- dtsdance/dflow.py,sha256=jwsngCqgHUSAUFh-IUgq-fnQs8lAvHtj7uLLs_tupqI,7718
4
+ dtsdance/dflow.py,sha256=v6tQv9O6-xjXhNuU14vys0lIxi4hKKv27nuBjLgRk6Y,7747
5
5
  dtsdance/dsyncer.py,sha256=PCatQIFXgi1l2qT60zNN-ALAqtv7_sg-D2oNMB_t8qw,9906
6
6
  dtsdance/feishu_base.py,sha256=dYYNnsCjGZYN2I6inR9FRQoUiDLB7j40rzydw50N38o,11825
7
7
  dtsdance/feishu_table.py,sha256=JleCjQJ-rcbC15v2UAc5BK-YioB2SMGsdGztQ9BaT4k,10779
@@ -10,6 +10,7 @@ dtsdance/s3.py,sha256=Bh-cwLksfO5PewNtIzE_Md3rRLDLI1DUVoOD7Pou5T8,1294
10
10
  dtsdance/spacex.py,sha256=W_YGt_Wo_Cy4eDhgXhNfskayPHcZ1kg2Mil2ghI_VLE,4791
11
11
  dtsdance/tcc_inner.py,sha256=QqVyhwP0kCdp1HFHFgtme9o0im-eQNn1HVGdLt3Xr8g,1627
12
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,,
13
+ dtsdance/tce.py,sha256=QwqPimZu0GKeP--sdbMP9h1e22nGuZL2FHqBUykXDOw,11563
14
+ dts_dance-0.2.8.dist-info/METADATA,sha256=YjipvJ24oD--koIvidnlXrhll5UzI5Efpl-Zog_kQZc,826
15
+ dts_dance-0.2.8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
+ dts_dance-0.2.8.dist-info/RECORD,,
dtsdance/dflow.py CHANGED
@@ -117,12 +117,13 @@ class DFlowClient:
117
117
  site_info = self.bytecloud_client.get_site_config(site)
118
118
  return f"{site_info.endpoint}/bytedts/datasync/detail/{task_id}"
119
119
 
120
- def init_resources(self, site: str, ctrl_env: str) -> bool:
120
+ def init_resources(self, site: str, vregion: str, ctrl_env: str) -> bool:
121
121
  """
122
122
  初始化 CTRL 环境资源
123
123
 
124
124
  Args:
125
125
  site: 站点名称
126
+ vregion: 虚拟区域
126
127
  ctrl_env: 控制环境
127
128
 
128
129
  Returns:
@@ -135,21 +136,21 @@ class DFlowClient:
135
136
  # 构建请求数据
136
137
  payload = {"ctrl_env": ctrl_env}
137
138
 
138
- response_data = make_request("POST", url, self.bytecloud_client.build_request_headers(site, None), payload)
139
+ response_data = make_request("POST", url, self.bytecloud_client.build_request_headers(site, vregion), payload)
139
140
 
140
141
  message = response_data.get("message")
141
142
  logger.info(f"int_resources {site} {ctrl_env}, message: {message}")
142
143
 
143
144
  return message == "ok"
144
145
 
145
- def list_resources(self, site: str, ctrl_env: str, vregion: str | None = None) -> list[str]:
146
+ def list_resources(self, site: str, vregion: str, ctrl_env: str) -> list[str]:
146
147
  """
147
148
  列举 CTRL 环境资源列表,连同 agent 列表
148
149
 
149
150
  Args:
150
151
  site: 站点名称
151
- ctrl_env: 控制环境
152
152
  vregion: 虚拟区域
153
+ ctrl_env: 控制环境
153
154
  Returns:
154
155
  list[str]: CTRL 环境资源列表
155
156
  """
@@ -164,7 +165,7 @@ class DFlowClient:
164
165
 
165
166
  # curl_cmd = output_curl(url, headers, payload)
166
167
  # print(f"Equivalent curl:\n{curl_cmd}")
167
-
168
+
168
169
  try:
169
170
  data = response_data.get("data", {})
170
171
  items = data.get("items", [])
dtsdance/tce.py ADDED
@@ -0,0 +1,289 @@
1
+ from typing import Any
2
+ import requests
3
+
4
+ from .bytecloud import ByteCloudClient
5
+
6
+
7
+ class TCEError(Exception):
8
+ pass
9
+
10
+
11
+ class TCEErrorServiceExist(Exception):
12
+ pass
13
+
14
+
15
+ class TCEErrorClusterExist(Exception):
16
+ pass
17
+
18
+
19
+ class TCEClient:
20
+ """Client for TCE deployment API."""
21
+
22
+ def __init__(self, bytecloud_client: ByteCloudClient) -> None:
23
+ """
24
+ Initialize TCE client.
25
+
26
+ Args:
27
+ base_url: TCE API base URL
28
+ jwt_token: JWT token for authentication (can be fetched automatically)
29
+ """
30
+ self.bytecloud_client = bytecloud_client
31
+
32
+ def create_service(self, site: str, ctrl_name: str, bytetree_parent_id: int) -> int | None:
33
+ site_info = self.bytecloud_client.get_site_config(site)
34
+ url = f"{site_info.endpoint}/api/v1/tce/open-apis/v1/deployment/service/create/"
35
+
36
+ service_info = {
37
+ "meta": {"name": f"bytedts.cm.{ctrl_name}", "psm": f"bytedts.cm.{ctrl_name}", "env": "prod"},
38
+ "auth": {
39
+ "galaxy_info": {"parent_id": bytetree_parent_id},
40
+ "approval_controls": ["reset"],
41
+ "iam_info": {"owners": [{"username": site_info.svc_account}, {"username": "mahang"}]},
42
+ "owner_name": site_info.svc_account,
43
+ },
44
+ "extra_features": {
45
+ "service_token": {
46
+ "tech_stack": {"language": "go", "framework": "custom", "platform_types_v_2": {"tce": ["http"]}},
47
+ "deploy": {"type": "stateless_compute", "scale": {"enable": True}, "descale": {"enable": True}},
48
+ "level": "P2",
49
+ "purpose": "convention",
50
+ "contain_user_personal_info": False,
51
+ "data_sensitivity_level": "L2",
52
+ }
53
+ },
54
+ "build": {
55
+ "dependency_mode": "default",
56
+ "base_image": "debian.bookworm.tce_service:latest",
57
+ "install_package_info": [],
58
+ "scm_repo_info": [
59
+ {"name": "bytedts/dflow/dtscm", "remote_id": 387073, "main_repo": True, "path": "dts-cm"},
60
+ {"name": "toutiao/load", "remote_id": 667, "path": "toutiao/load"},
61
+ {"name": "toutiao/runtime", "remote_id": 631, "path": "toutiao/runtime"},
62
+ ],
63
+ "language": "go",
64
+ "framework_support": "none",
65
+ "tech_stack": "custom",
66
+ },
67
+ "runtime": {
68
+ "umount_ss_conf": False,
69
+ "new_ports": [
70
+ {"intent": "primary", "port": 8088, "is_primary": True, "type": "tcp"},
71
+ {"intent": "other", "port": 3033, "is_primary": False, "type": "tcp"},
72
+ ],
73
+ "is_runtime": False,
74
+ "app_path": "dts-cm/bootstrap.sh",
75
+ "enable_iast": False,
76
+ },
77
+ }
78
+
79
+ payload = {"service_info": service_info}
80
+
81
+ try:
82
+ response = requests.post(url, json=payload, headers=self.bytecloud_client.build_request_headers(site), timeout=60)
83
+ if response.status_code != 200:
84
+ if "already exists" in response.text:
85
+ raise TCEErrorServiceExist("TCE cluster with the same name already exists")
86
+
87
+ raise TCEError(f"TCE API request failed with status {response.status_code}\n" f"Response: {response.text}")
88
+
89
+ result = response.json()
90
+ if result.get("code", 0) == -1:
91
+ raise TCEError(f"TCE API error: {result.get('error')}")
92
+
93
+ return result.get("data", {}).get("service", 0)
94
+
95
+ except requests.RequestException as e:
96
+ raise TCEError(f"Failed to create TCE cluster: {str(e)}") from e
97
+
98
+ def create_cluster(self, site: str, cluster_info: dict[str, Any], service: int) -> int | None:
99
+ """
100
+ Create TCE cluster.
101
+
102
+ Args:
103
+ cluster_info: Cluster configuration (meta, resource, runtime)
104
+ service: Service ID
105
+
106
+ Returns:
107
+ The created cluster ID, or None if creation failed
108
+
109
+ Raises:
110
+ TCEError: If API call fails
111
+ """
112
+ site_info = self.bytecloud_client.get_site_config(site)
113
+ url = f"{site_info.endpoint}/api/v1/tce/open-apis/v1/deployment/cluster/create/"
114
+ payload = {"cluster_info": cluster_info, "service": service}
115
+
116
+ try:
117
+ response = requests.post(url, json=payload, headers=self.bytecloud_client.build_request_headers(site), timeout=60)
118
+ if response.status_code != 200:
119
+ if "a same named TCE cluster already exists" in response.text:
120
+ raise TCEErrorClusterExist("TCE cluster with the same name already exists")
121
+
122
+ raise TCEError(f"TCE API request failed with status {response.status_code}\n" f"Response: {response.text}")
123
+
124
+ result = response.json()
125
+
126
+ # Check for errors in response
127
+ if not isinstance(result, dict):
128
+ raise TCEError(f"Unexpected response format: {result}")
129
+
130
+ # TCE API may return error information in different formats
131
+ if result.get("code", 0) == -1:
132
+ raise TCEError(f"TCE API error: {result.get('error')}")
133
+
134
+ data = result.get("data", {})
135
+ create_success = data.get("deployment_type", "") == "create" and data.get("status", "") == "pending"
136
+ if not create_success:
137
+ raise TCEError(f"TCE cluster creation failed: {result}")
138
+
139
+ cluster_id = data.get("cluster_info", {}).get("meta", {}).get("id", 0)
140
+ # Ensure cluster_id is an integer
141
+ return int(cluster_id) if cluster_id else 0
142
+
143
+ except requests.RequestException as e:
144
+ raise TCEError(f"Failed to create TCE cluster: {str(e)}") from e
145
+
146
+ def delete_cluster(self, site: str, cluster_id: int, service_id: int, cluster_name: str) -> int | None:
147
+ """
148
+ Delete TCE cluster (reset/destroy).
149
+
150
+ Args:
151
+ site: Site identifier
152
+ cluster_id: Cluster ID to delete
153
+ service_id: Service ID
154
+ cluster_name: Cluster name
155
+
156
+ Returns:
157
+ Response data
158
+
159
+ Raises:
160
+ TCEError: If API call fails
161
+ """
162
+ site_info = self.bytecloud_client.get_site_config(site)
163
+ url = f"{site_info.endpoint}/api/v1/tce/open-apis/v1/deployment/cluster/reset/"
164
+ payload = {"cluster": cluster_id, "service": service_id}
165
+
166
+ # print(f"Deleting TCE cluster {cluster_id} for service {service_id}")
167
+ try:
168
+ response = requests.post(url, json=payload, headers=self.bytecloud_client.build_request_headers(site), timeout=60)
169
+ if response.status_code != 200:
170
+ raise TCEError(f"TCE API request failed with status {response.status_code}\n" f"Response: {response.text}")
171
+
172
+ result = response.json()
173
+ if result.get("code", 0) == -1:
174
+ error_msg = result.get("error", "")
175
+ if "do not exist" in error_msg:
176
+ print(f"cluster {cluster_name} not exist, maybe already deleted")
177
+ return None
178
+
179
+ raise TCEError(f"TCE API error: {error_msg}")
180
+
181
+ data = result.get("data", {})
182
+ # 查找 "人工确认" step 的 id
183
+ steps = data.get("steps", [])
184
+ for step in steps:
185
+ if step.get("step_type") == "ConfirmStep":
186
+ return step.get("id")
187
+
188
+ return None
189
+
190
+ except requests.RequestException as e:
191
+ raise TCEError(f"Failed to delete TCE cluster: {cluster_name}, msg: {str(e)}") from e
192
+
193
+ def get_cluster_info(self, site: str, cluster_id: int) -> dict[str, Any]:
194
+ site_info = self.bytecloud_client.get_site_config(site)
195
+ url = f"{site_info.endpoint}/api/v1/tce/open-apis/v1/clusters/{cluster_id}/"
196
+
197
+ try:
198
+ response = requests.get(url, headers=self.bytecloud_client.build_request_headers(site), timeout=60)
199
+ if response.status_code != 200:
200
+ raise TCEError(f"TCE API request failed with status {response.status_code}\n" f"Response: {response.text}")
201
+
202
+ # print(f"get_cluster_info response.text: {response.text}")
203
+ result = response.json()
204
+ if result.get("code", 0) == -1:
205
+ raise TCEError(f"TCE API error: {result.get('error')}")
206
+
207
+ return result.get("data", {})
208
+
209
+ except requests.RequestException as e:
210
+ raise TCEError(f"Failed to create TCE cluster: {str(e)}") from e
211
+
212
+ def is_cluster_running(self, site: str, cluster_id: int | None) -> bool:
213
+ if cluster_id is None:
214
+ return False
215
+
216
+ cluster_info = self.get_cluster_info(site, cluster_id)
217
+ return cluster_info.get("meta", {}).get("status", "unknown") == "running"
218
+
219
+ def restart_cluster(self, site: str, service_id: int, cluster_id: int) -> list[int]:
220
+ site_info = self.bytecloud_client.get_site_config(site)
221
+ url = f"{site_info.endpoint}/api/v1/tce/open-apis/v1/deployment/cluster/pod_exec/"
222
+
223
+ payload = {
224
+ "service": service_id,
225
+ "cluster": cluster_id,
226
+ "exec_command": "rebuild",
227
+ "exec_params": {
228
+ "surge_percent": 25,
229
+ "ready_seconds": 10,
230
+ "timeout": 120,
231
+ "tolerance_num": 3,
232
+ "failed_policy": "remain",
233
+ "need_check": True,
234
+ },
235
+ "exec_type": "rebuild",
236
+ "parallel_mode": True,
237
+ }
238
+
239
+ try:
240
+ response = requests.post(url, json=payload, headers=self.bytecloud_client.build_request_headers(site), timeout=60)
241
+ if response.status_code != 200:
242
+ raise TCEError(f"TCE API request failed with status {response.status_code}\n" f"Response: {response.text}")
243
+
244
+ result = response.json()
245
+ if result.get("code", 0) == -1:
246
+ raise TCEError(f"TCE API error: {result.get('error')}")
247
+
248
+ steps = result.get("data", {}).get("steps", [])
249
+ step_ids = [step.get("id", 0) for step in steps]
250
+ return step_ids
251
+
252
+ except requests.RequestException as e:
253
+ raise TCEError(f"Failed to create TCE cluster: {str(e)}") from e
254
+
255
+ def confirm_step(self, site: str, step_id: int, action: str = "confirm") -> bool:
256
+ """
257
+ Confirm a step in TCE cluster deployment.
258
+
259
+ Args:
260
+ step_id: Step ID
261
+
262
+ Returns:
263
+ Response data
264
+
265
+ Raises:
266
+ TCEError: If API call fails
267
+ """
268
+ site_info = self.bytecloud_client.get_site_config(site)
269
+ url = f"{site_info.endpoint}/api/v1/tce/open-apis/v1/deployment_steps/{step_id}/actions/"
270
+ payload = {"action": action}
271
+
272
+ try:
273
+ response = requests.post(url, json=payload, headers=self.bytecloud_client.build_request_headers(site), timeout=60)
274
+ # print(f"Confirm step response: {response.text}")
275
+
276
+ response.raise_for_status()
277
+ result = response.json()
278
+
279
+ # Check for errors in response
280
+ if not isinstance(result, dict):
281
+ raise TCEError(f"Unexpected response format: {result}")
282
+
283
+ if result.get("code", 0) == -1:
284
+ raise TCEError(f"TCE API error: {result.get('error')}")
285
+
286
+ return True
287
+
288
+ except requests.RequestException as e:
289
+ raise TCEError(f"Failed to confirm ticket, msg: {str(e)}") from e