pybioos 0.0.15__py3-none-any.whl → 0.0.19__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.
@@ -0,0 +1,806 @@
1
+ from datetime import datetime
2
+ from typing import Optional, Dict, Any, List
3
+
4
+ import pandas as pd
5
+ from cachetools import TTLCache, cached
6
+ from pandas import DataFrame
7
+
8
+ from bioos.config import Config
9
+ from bioos.errors import ConflictError, NotFoundError, ParameterError
10
+ from bioos.utils.common_tools import SingletonType, dict_str
11
+
12
+
13
+ class WebInstanceApp(metaclass=SingletonType):
14
+ """表示一个Web实例应用程序。
15
+
16
+ 这个类封装了所有与Web实例应用程序相关的信息和操作,
17
+ 包括其元数据、状态和创建能力。
18
+ """
19
+
20
+ def __init__(self,
21
+ name: str,
22
+ workspace_id: str,
23
+ check: bool = False):
24
+ """初始化Web实例应用程序实例。
25
+
26
+ Args:
27
+ name: Web实例应用程序的名称
28
+ workspace_id: 包含此Web实例应用程序的工作空间ID
29
+ check: 是否立即检查Web实例应用程序的存在性
30
+ """
31
+ self.name = name
32
+ self.workspace_id = workspace_id
33
+ self._id: str = ""
34
+ self._description: str = ""
35
+ self._status: str = ""
36
+ self._create_time: int = 0
37
+ self._update_time: int = 0
38
+ self._owner_name: str = ""
39
+ self._endpoint: str = ""
40
+ self._port: int = 0
41
+ self._app_type: str = ""
42
+ self._user_id: int = 0
43
+ self._scope: str = ""
44
+ self._resource_size: str = ""
45
+ self._storage_capacity: int = 0
46
+ self._ssh_info: Dict[str, Any] = {}
47
+ self._image: str = ""
48
+ self._termination: Dict[str, Any] = {}
49
+ self._tos_mounts: List[Dict[str, Any]] = []
50
+ self._status_detail: Dict[str, Any] = {}
51
+ self._access_urls: Dict[str, str] = {}
52
+ self._running_duration: int = 0
53
+ self._start_time: int = 0
54
+
55
+ if check:
56
+ self.sync()
57
+
58
+ def __repr__(self):
59
+ """返回Web实例应用程序的字符串表示。"""
60
+ info_dict = dict_str({
61
+ "id": self.id,
62
+ "name": self.name,
63
+ "description": self.description,
64
+ "status": self.status_detail.get('State', 'Unknown'),
65
+ "user_id": self.user_id,
66
+ "scope": self.scope,
67
+ "resource_size": self.resource_size,
68
+ "storage_capacity": self.storage_capacity,
69
+ "image": self.image,
70
+ "running_duration": self.get_running_time_formatted(),
71
+ "is_running": self.is_running(),
72
+ "ssh_info": self.get_ssh_connection_info(),
73
+ "access_urls": self.access_urls,
74
+ "create_time": self.create_time,
75
+ "update_time": self.update_time
76
+ })
77
+ return f"WebInstanceAppInfo:\n{info_dict}"
78
+
79
+ @property
80
+ @cached(cache=TTLCache(maxsize=100, ttl=1))
81
+ def id(self) -> str:
82
+ """获取Web实例应用程序的ID
83
+
84
+ :return: Web实例应用程序的ID
85
+ :rtype: str
86
+ """
87
+ res = WebInstanceAppResource(self.workspace_id).list().query(f"Name=='{self.name}'")
88
+ if res.empty:
89
+ raise ParameterError("name")
90
+ return res["ID"].iloc[0]
91
+
92
+ @property
93
+ def description(self) -> str:
94
+ """获取Web实例应用程序描述。"""
95
+ if not self._description:
96
+ self.sync()
97
+ return self._description
98
+
99
+ @property
100
+ def status(self) -> str:
101
+ """获取Web实例应用程序状态。"""
102
+ if not self._status:
103
+ self.sync()
104
+ return self._status
105
+
106
+ @property
107
+ def create_time(self) -> int:
108
+ """获取Web实例应用程序创建时间戳。"""
109
+ if not self._create_time:
110
+ self.sync()
111
+ return self._create_time
112
+
113
+ @property
114
+ def update_time(self) -> int:
115
+ """获取Web实例应用程序最后更新时间戳。"""
116
+ if not self._update_time:
117
+ self.sync()
118
+ return self._update_time
119
+
120
+ @property
121
+ def owner_name(self) -> str:
122
+ """获取Web实例应用程序所有者姓名。"""
123
+ if not self._owner_name:
124
+ self.sync()
125
+ return self._owner_name
126
+
127
+ @property
128
+ def endpoint(self) -> str:
129
+ """获取Web实例应用程序访问端点。"""
130
+ if not self._endpoint:
131
+ self.sync()
132
+ return self._endpoint
133
+
134
+ @property
135
+ def port(self) -> int:
136
+ """获取Web实例应用程序端口。"""
137
+ if not self._port:
138
+ self.sync()
139
+ return self._port
140
+
141
+ @property
142
+ def app_type(self) -> str:
143
+ """获取Web实例应用程序类型。"""
144
+ if not self._app_type:
145
+ self.sync()
146
+ return self._app_type
147
+
148
+ @property
149
+ def user_id(self) -> int:
150
+ """获取用户ID。"""
151
+ if not self._user_id:
152
+ self.sync()
153
+ return self._user_id
154
+
155
+ @property
156
+ def scope(self) -> str:
157
+ """获取实例范围。"""
158
+ if not self._scope:
159
+ self.sync()
160
+ return self._scope
161
+
162
+ @property
163
+ def resource_size(self) -> str:
164
+ """获取资源规格。"""
165
+ if not self._resource_size:
166
+ self.sync()
167
+ return self._resource_size
168
+
169
+ @property
170
+ def storage_capacity(self) -> int:
171
+ """获取存储容量。"""
172
+ if not self._storage_capacity:
173
+ self.sync()
174
+ return self._storage_capacity
175
+
176
+ @property
177
+ def ssh_info(self) -> Dict[str, Any]:
178
+ """获取SSH连接信息。"""
179
+ if not self._ssh_info:
180
+ self.sync()
181
+ return self._ssh_info
182
+
183
+ @property
184
+ def image(self) -> str:
185
+ """获取镜像地址。"""
186
+ if not self._image:
187
+ self.sync()
188
+ return self._image
189
+
190
+ @property
191
+ def termination(self) -> Dict[str, Any]:
192
+ """获取终止策略配置。"""
193
+ if not self._termination:
194
+ self.sync()
195
+ return self._termination
196
+
197
+ @property
198
+ def tos_mounts(self) -> List[Dict[str, Any]]:
199
+ """获取TOS挂载配置。"""
200
+ if not self._tos_mounts:
201
+ self.sync()
202
+ return self._tos_mounts
203
+
204
+ @property
205
+ def status_detail(self) -> Dict[str, Any]:
206
+ """获取详细状态信息。"""
207
+ if not self._status_detail:
208
+ self.sync()
209
+ return self._status_detail
210
+
211
+ @property
212
+ def access_urls(self) -> Dict[str, str]:
213
+ """获取访问URL。"""
214
+ if not self._access_urls:
215
+ self.sync()
216
+ return self._access_urls
217
+
218
+ @property
219
+ def running_duration(self) -> int:
220
+ """获取运行持续时间(秒)。"""
221
+ self.sync() # 运行时间需要实时获取
222
+ return self._running_duration
223
+
224
+ @property
225
+ def start_time(self) -> int:
226
+ """获取启动时间戳。"""
227
+ if not self._start_time:
228
+ self.sync()
229
+ return self._start_time
230
+
231
+ @cached(cache=TTLCache(maxsize=100, ttl=1))
232
+ def sync(self):
233
+ """与远程服务同步Web实例应用程序信息。"""
234
+ res = WebInstanceAppResource(self.workspace_id).list().query(f"Name=='{self.name}'")
235
+ if res.empty:
236
+ raise ParameterError("name")
237
+
238
+ # 获取详细的Web实例应用程序信息
239
+ params = {
240
+ 'Filter': {
241
+ 'Application': 'ies',
242
+ 'WorkspaceID': self.workspace_id,
243
+ 'IDs': [res["ID"].iloc[0]]
244
+ }
245
+ }
246
+ webinstanceapps = Config.service().list_webinstance_apps(params).get('Items')
247
+ if len(webinstanceapps) != 1:
248
+ raise NotFoundError("webinstanceapp", self.name)
249
+
250
+ detail = webinstanceapps[0]
251
+
252
+ # 更新所有属性
253
+ self._id = detail.get("ID", "")
254
+ self._description = detail.get("Description", "")
255
+ self._status = detail.get("Status", "")
256
+ self._create_time = detail.get("CreateTime", 0)
257
+ self._update_time = detail.get("UpdateTime", 0)
258
+ self._owner_name = detail.get("OwnerName", "")
259
+ self._endpoint = detail.get("Endpoint", "")
260
+ self._port = detail.get("Port", 0)
261
+ self._app_type = detail.get("AppType", "")
262
+
263
+ # 新增的属性
264
+ self._user_id = detail.get("UserID", 0)
265
+ self._scope = detail.get("Scope", "")
266
+ self._resource_size = detail.get("ResourceSize", "")
267
+ self._storage_capacity = detail.get("StorageCapacity", 0)
268
+ self._ssh_info = detail.get("SSH", {})
269
+ self._image = detail.get("Image", "")
270
+ self._termination = detail.get("Termination", {})
271
+ self._tos_mounts = detail.get("TOSMounts", [])
272
+
273
+ # 状态详情处理
274
+ status_detail = detail.get("Status", {})
275
+ if isinstance(status_detail, dict):
276
+ self._status_detail = status_detail
277
+ self._access_urls = status_detail.get("AccessURLs", {})
278
+ self._running_duration = status_detail.get("RunningDuration", 0)
279
+ self._start_time = status_detail.get("StartTime", 0)
280
+ else:
281
+ self._status_detail = {"State": str(status_detail)}
282
+ self._access_urls = {}
283
+ self._running_duration = 0
284
+ self._start_time = 0
285
+
286
+ def check_name_exists(self) -> bool:
287
+ """检查当前Web实例应用程序名称是否已存在。
288
+
289
+ *Example*:
290
+ ::
291
+
292
+ ws = bioos.workspace("foo")
293
+ app = ws.webinstanceapp("my-ies-instance")
294
+ exists = app.check_name_exists()
295
+ if exists:
296
+ print("实例名称已存在")
297
+ else:
298
+ print("实例名称可用")
299
+
300
+ :return: 名称是否已存在
301
+ :rtype: bool
302
+ """
303
+ params = {
304
+ "WorkspaceID": self.workspace_id,
305
+ "Name": self.name,
306
+ "Application": "ies"
307
+ }
308
+
309
+ result = Config.service().check_webinstance_app(params)
310
+ return result.get("IsNameExist", False)
311
+
312
+ def get_ssh_connection_info(self) -> Dict[str, str]:
313
+ """获取SSH连接信息的便捷方法。
314
+
315
+ *Example*:
316
+ ::
317
+
318
+ ws = bioos.workspace("foo")
319
+ app = ws.webinstanceapp("my-ies-instance")
320
+ ssh_info = app.get_ssh_connection_info()
321
+ print(f"SSH地址: {ssh_info['ip']}:{ssh_info['port']}")
322
+ print(f"用户名: {ssh_info['username']}")
323
+ print(f"密码: {ssh_info['password']}")
324
+
325
+ :return: SSH连接信息
326
+ :rtype: Dict[str, str]
327
+ """
328
+ ssh_data = self.ssh_info
329
+ return {
330
+ 'ip': ssh_data.get('IP', ''),
331
+ 'port': ssh_data.get('Port', ''),
332
+ 'username': ssh_data.get('Username', ''),
333
+ 'password': ssh_data.get('Passwd', '')
334
+ }
335
+
336
+ def get_notebook_url(self) -> str:
337
+ """获取Jupyter Notebook访问URL。
338
+
339
+ :return: Notebook访问URL
340
+ :rtype: str
341
+ """
342
+ return self.access_urls.get('notebook', '')
343
+
344
+ def get_rstudio_url(self) -> str:
345
+ """获取RStudio访问URL。
346
+
347
+ :return: RStudio访问URL
348
+ :rtype: str
349
+ """
350
+ return self.access_urls.get('rstudio', '')
351
+
352
+ def get_vscode_url(self) -> str:
353
+ """获取VSCode访问URL。
354
+
355
+ :return: VSCode访问URL
356
+ :rtype: str
357
+ """
358
+ return self.access_urls.get('vscode', '')
359
+
360
+ def is_running(self) -> bool:
361
+ """检查实例是否正在运行。
362
+
363
+ :return: 是否正在运行
364
+ :rtype: bool
365
+ """
366
+ status_detail = self.status_detail
367
+ return status_detail.get('State', '') == 'Running'
368
+
369
+ def get_running_time_formatted(self) -> str:
370
+ """获取格式化的运行时间。
371
+
372
+ :return: 格式化的运行时间(如"1小时23分钟")
373
+ :rtype: str
374
+ """
375
+ duration = self.running_duration
376
+ if duration < 60:
377
+ return f"{duration}秒"
378
+ elif duration < 3600:
379
+ minutes = duration // 60
380
+ seconds = duration % 60
381
+ return f"{minutes}分钟{seconds}秒"
382
+ else:
383
+ hours = duration // 3600
384
+ minutes = (duration % 3600) // 60
385
+ return f"{hours}小时{minutes}分钟"
386
+
387
+ def get_events(self,
388
+ start_time: Optional[int] = None,
389
+ end_time: Optional[int] = None,
390
+ levels: Optional[List[str]] = None) -> List[Dict[str, Any]]:
391
+ """获取Web实例应用程序的事件日志。
392
+
393
+ *Example*:
394
+ ::
395
+
396
+ ws = bioos.workspace("foo")
397
+ app = ws.webinstanceapp("my-ies-instance")
398
+
399
+ # 获取所有事件
400
+ events = app.get_events()
401
+
402
+ # 获取指定时间范围的事件
403
+ events = app.get_events(start_time=1640995200, end_time=1641081600)
404
+
405
+ # 获取指定级别的事件
406
+ events = app.get_events(levels=["Info", "Urgency"])
407
+
408
+ :param start_time: 起始时间戳(可选)
409
+ :type start_time: Optional[int]
410
+ :param end_time: 结束时间戳(可选)
411
+ :type end_time: Optional[int]
412
+ :param levels: 事件级别列表,可选值: ["Info", "Urgency", "Priority"]
413
+ :type levels: Optional[List[str]]
414
+ :return: 事件列表
415
+ :rtype: List[Dict[str, Any]]
416
+ """
417
+ params = {
418
+ "ID": self.id,
419
+ "WorkspaceID": self.workspace_id,
420
+ "Filter": {}
421
+ }
422
+
423
+ # 构建过滤条件
424
+ filter_params = {}
425
+ if start_time is not None:
426
+ filter_params["StartTime"] = start_time
427
+ if end_time is not None:
428
+ filter_params["EndTime"] = end_time
429
+ if levels is not None:
430
+ filter_params["Level"] = levels
431
+
432
+ if filter_params:
433
+ params["Filter"] = filter_params
434
+
435
+ result = Config.service().list_webinstance_events(params)
436
+ return result.get("Items", [])
437
+
438
+ def commit_image(self,
439
+ image_name: str,
440
+ description: str = "",
441
+ replace_image: bool = False) -> dict:
442
+ """将当前实例状态保存为镜像。
443
+
444
+ *Example*:
445
+ ::
446
+
447
+ ws = bioos.workspace("foo")
448
+ app = ws.webinstanceapp("my-ies-instance")
449
+
450
+ # 保存为新镜像
451
+ result = app.commit_image(
452
+ image_name="my-custom-image:v1.0",
453
+ description="我的自定义开发环境",
454
+ replace_image=False
455
+ )
456
+
457
+ :param image_name: 镜像名称
458
+ :type image_name: str
459
+ :param description: 镜像描述
460
+ :type description: str
461
+ :param replace_image: 是否自动替换当前实例的镜像
462
+ :type replace_image: bool
463
+ :return: 提交结果
464
+ :rtype: dict
465
+ """
466
+ if not image_name:
467
+ raise ParameterError("image_name", "image_name cannot be empty")
468
+
469
+ params = {
470
+ "WorkspaceID": self.workspace_id,
471
+ "WebappInstanceID": self.id,
472
+ "Image": image_name,
473
+ "Description": description,
474
+ "ImageReplace": replace_image
475
+ }
476
+
477
+ return Config.service().commit_ies_image(params)
478
+
479
+
480
+ class WebInstanceAppResource(metaclass=SingletonType):
481
+ """Web实例应用程序资源管理类。"""
482
+
483
+ def __init__(self, workspace_id: str):
484
+ self.workspace_id = workspace_id
485
+
486
+ def __repr__(self):
487
+ return f"WebInstanceAppInfo:\n{self.list()}"
488
+
489
+ def list(self) -> DataFrame:
490
+ """列出所有Web实例应用程序信息。
491
+
492
+ *Example*:
493
+ ::
494
+
495
+ ws = bioos.workspace("foo")
496
+ ws.webinstanceapps.list()
497
+
498
+ :return: 所有Web实例应用程序信息
499
+ :rtype: DataFrame
500
+ """
501
+ content = Config.service().list_webinstance_apps({
502
+ 'Filter': {
503
+ 'Application': 'ies',
504
+ 'WorkspaceID': self.workspace_id
505
+ }
506
+ })
507
+ res_df = pd.DataFrame.from_records(content['Items'])
508
+ if res_df.empty:
509
+ return res_df
510
+ res_df['CreateTime'] = pd.to_datetime(
511
+ res_df['CreateTime'], unit='ms', origin=pd.Timestamp('2018-07-01'))
512
+ res_df['UpdateTime'] = pd.to_datetime(
513
+ res_df['UpdateTime'], unit='ms', origin=pd.Timestamp('2018-07-01'))
514
+
515
+ return res_df
516
+
517
+ def create_new_instance(self,
518
+ name: str,
519
+ description: str,
520
+ resource_size: str = "1c-2gib",
521
+ storage_capacity: int = 21474836480,
522
+ image: str = "",
523
+ ssh_enabled: bool = True,
524
+ running_time_limit_seconds: int = 10800, # 3小时 = 3*60*60
525
+ idle_timeout_seconds: int = 10800, # 3小时 = 3*60*60
526
+ auto_start: bool = True) -> dict:
527
+ """创建一个新的IES Web实例应用程序。
528
+
529
+ *Example*:
530
+ ::
531
+
532
+ ws = bioos.workspace("foo")
533
+ ws.webinstanceapps.create_new_instance(
534
+ name="my-ies-instance",
535
+ description="我的IES实例",
536
+ resource_size="1c-2gib",
537
+ storage_capacity=21474836480,
538
+ image="your-image-url",
539
+ ssh_enabled=True,
540
+ running_time_limit_seconds=10800,
541
+ idle_timeout_seconds=10800,
542
+ auto_start=True
543
+ )
544
+
545
+ :param name: Web实例应用程序名称
546
+ :type name: str
547
+ :param description: Web实例应用程序描述
548
+ :type description: str
549
+ :param resource_size: 资源规格(如"1c-2gib"表示1核2GB内存)
550
+ :type resource_size: str
551
+ :param storage_capacity: 存储容量(字节,默认20GB)
552
+ :type storage_capacity: int
553
+ :param image: 镜像地址
554
+ :type image: str
555
+ :param ssh_enabled: 是否启用SSH
556
+ :type ssh_enabled: bool
557
+ :param running_time_limit_seconds: 运行时长限制(秒,5分钟-24小时,默认3小时)
558
+ :type running_time_limit_seconds: int
559
+ :param idle_timeout_seconds: 空闲超时时间(秒,5分钟-24小时,默认3小时)
560
+ :type idle_timeout_seconds: int
561
+ :param auto_start: 是否自动启动
562
+ :type auto_start: bool
563
+ :return: 创建结果
564
+ :rtype: dict
565
+ """
566
+ if not name:
567
+ raise ParameterError("name", "name cannot be empty")
568
+
569
+ if not image:
570
+ raise ParameterError("image", "image cannot be empty")
571
+
572
+ # 检查名称是否已存在
573
+ if self.check_name_exists(name):
574
+ raise ConflictError("name", f"{name} already exists")
575
+
576
+ # 验证时间限制范围(5分钟到24小时)
577
+ min_time = 300 # 5分钟
578
+ max_time = 86400 # 24小时
579
+
580
+ if not (min_time <= running_time_limit_seconds <= max_time):
581
+ raise ParameterError("running_time_limit_seconds",
582
+ f"must be between {min_time} and {max_time} seconds")
583
+
584
+ if not (min_time <= idle_timeout_seconds <= max_time):
585
+ raise ParameterError("idle_timeout_seconds",
586
+ f"must be between {min_time} and {max_time} seconds")
587
+
588
+ params = {
589
+ "WorkspaceID": self.workspace_id,
590
+ "Name": name,
591
+ "Scope": "Private",
592
+ "Description": description,
593
+ "Application": "ies",
594
+ "ResourceSize": resource_size,
595
+ "StorageCapacity": storage_capacity,
596
+ "SSHEnabled": ssh_enabled,
597
+ "Image": image,
598
+ "Termination": {
599
+ "RunningTimeLimitSeconds": running_time_limit_seconds,
600
+ "IdleTimeoutSeconds": idle_timeout_seconds
601
+ },
602
+ "TOSMounts": [
603
+ {
604
+ "BucketName": f"bioos-{self.workspace_id}",
605
+ "MountPath": f"/home/ies/bioos-{self.workspace_id}",
606
+ "ReadOnly": False
607
+ }
608
+ ],
609
+ "AutoStart": auto_start
610
+ }
611
+
612
+ return Config.service().create_webinstance_app(params)
613
+
614
+ def delete(self, target: str):
615
+ """从工作空间删除一个Web实例应用程序。
616
+
617
+ *Example*:
618
+ ::
619
+
620
+ ws = bioos.workspace("foo")
621
+ ws.webinstanceapps.delete(target="bar")
622
+
623
+ :param target: Web实例应用程序名称
624
+ :type target: str
625
+ """
626
+ res = self.list().query(f"Name=='{target}'")
627
+ if res.empty:
628
+ raise ParameterError("target")
629
+
630
+ Config.service().delete_webinstance_app({
631
+ "ID": res["ID"].iloc[0],
632
+ "WorkspaceID": self.workspace_id
633
+ })
634
+
635
+ def start(self, target: str):
636
+ """启动一个Web实例应用程序。
637
+
638
+ *Example*:
639
+ ::
640
+
641
+ ws = bioos.workspace("foo")
642
+ ws.webinstanceapps.start(target="bar")
643
+
644
+ :param target: Web实例应用程序名称
645
+ :type target: str
646
+ """
647
+ res = self.list().query(f"Name=='{target}'")
648
+ if res.empty:
649
+ raise ParameterError("target")
650
+
651
+ Config.service().start_webinstance_app({
652
+ "ID": res["ID"].iloc[0],
653
+ "WorkspaceID": self.workspace_id
654
+ })
655
+
656
+ def stop(self, target: str):
657
+ """停止一个Web实例应用程序。
658
+
659
+ *Example*:
660
+ ::
661
+
662
+ ws = bioos.workspace("foo")
663
+ ws.webinstanceapps.stop(target="bar")
664
+
665
+ :param target: Web实例应用程序名称
666
+ :type target: str
667
+ """
668
+ res = self.list().query(f"Name=='{target}'")
669
+ if res.empty:
670
+ raise ParameterError("target")
671
+
672
+ Config.service().stop_webinstance_app({
673
+ "ID": res["ID"].iloc[0],
674
+ "WorkspaceID": self.workspace_id
675
+ })
676
+
677
+ def get_events(self,
678
+ target: str,
679
+ start_time: Optional[int] = None,
680
+ end_time: Optional[int] = None,
681
+ levels: Optional[List[str]] = None) -> List[Dict[str, Any]]:
682
+ """获取指定Web实例应用程序的事件日志。
683
+
684
+ *Example*:
685
+ ::
686
+
687
+ ws = bioos.workspace("foo")
688
+
689
+ # 获取所有事件
690
+ events = ws.webinstanceapps.get_events("my-ies-instance")
691
+
692
+ # 获取指定级别的事件
693
+ events = ws.webinstanceapps.get_events("my-ies-instance", levels=["Info", "Urgency"])
694
+
695
+ :param target: Web实例应用程序名称
696
+ :type target: str
697
+ :param start_time: 起始时间戳(可选)
698
+ :type start_time: Optional[int]
699
+ :param end_time: 结束时间戳(可选)
700
+ :type end_time: Optional[int]
701
+ :param levels: 事件级别列表
702
+ :type levels: Optional[List[str]]
703
+ :return: 事件列表
704
+ :rtype: List[Dict[str, Any]]
705
+ """
706
+ res = self.list().query(f"Name=='{target}'")
707
+ if res.empty:
708
+ raise ParameterError("target")
709
+
710
+ params = {
711
+ "ID": res["ID"].iloc[0],
712
+ "WorkspaceID": self.workspace_id
713
+ }
714
+
715
+ # 构建过滤条件
716
+ filter_params = {}
717
+ if start_time is not None:
718
+ filter_params["StartTime"] = start_time
719
+ if end_time is not None:
720
+ filter_params["EndTime"] = end_time
721
+ if levels is not None:
722
+ filter_params["Level"] = levels
723
+
724
+ if filter_params:
725
+ params["Filter"] = filter_params
726
+
727
+ result = Config.service().list_webinstance_events(params)
728
+ return result.get("Items", [])
729
+
730
+ def commit_image(self,
731
+ target: str,
732
+ image_name: str,
733
+ description: str = "",
734
+ replace_image: bool = False) -> dict:
735
+ """将指定实例状态保存为镜像。
736
+
737
+ *Example*:
738
+ ::
739
+
740
+ ws = bioos.workspace("foo")
741
+
742
+ # 保存为新镜像
743
+ result = ws.webinstanceapps.commit_image(
744
+ target="my-ies-instance",
745
+ image_name="my-custom-image:v1.0",
746
+ description="我的自定义开发环境",
747
+ replace_image=False
748
+ )
749
+
750
+ :param target: Web实例应用程序名称
751
+ :type target: str
752
+ :param image_name: 镜像名称
753
+ :type image_name: str
754
+ :param description: 镜像描述
755
+ :type description: str
756
+ :param replace_image: 是否自动替换当前实例的镜像
757
+ :type replace_image: bool
758
+ :return: 提交结果
759
+ :rtype: dict
760
+ """
761
+ if not image_name:
762
+ raise ParameterError("image_name", "image_name cannot be empty")
763
+
764
+ res = self.list().query(f"Name=='{target}'")
765
+ if res.empty:
766
+ raise ParameterError("target")
767
+
768
+ params = {
769
+ "WorkspaceID": self.workspace_id,
770
+ "WebappInstanceID": res["ID"].iloc[0],
771
+ "Image": image_name,
772
+ "Description": description,
773
+ "ImageReplace": replace_image
774
+ }
775
+
776
+ return Config.service().commit_ies_image(params)
777
+
778
+ def check_name_exists(self, name: str) -> bool:
779
+ """检查Web实例应用程序名称是否已存在。
780
+
781
+ *Example*:
782
+ ::
783
+
784
+ ws = bioos.workspace("foo")
785
+ exists = ws.webinstanceapps.check_name_exists("my-ies-instance")
786
+ if exists:
787
+ print("实例名称已存在")
788
+ else:
789
+ print("实例名称可用")
790
+
791
+ :param name: Web实例应用程序名称
792
+ :type name: str
793
+ :return: 名称是否已存在
794
+ :rtype: bool
795
+ """
796
+ if not name:
797
+ raise ParameterError("name", "name cannot be empty")
798
+
799
+ params = {
800
+ "WorkspaceID": self.workspace_id,
801
+ "Name": name,
802
+ "Application": "ies"
803
+ }
804
+
805
+ result = Config.service().check_webinstance_app(params)
806
+ return result.get("IsNameExist", False)