openbayes-cli 0.26.0__tar.gz → 0.26.1__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.
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/PKG-INFO +1 -1
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/gear_client.py +5 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/commands/gear.py +16 -7
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/commands/hpc.py +14 -7
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/gear_usecase.py +49 -1
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/pyproject.toml +1 -1
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/README.md +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/__init__.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/__init__.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/base.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/dataset_client.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/dataset_list_client.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/dataset_upload_client.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/dataset_version_client.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/job_run_client.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/minio_storage_client.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/org_client.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/resource_client.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/runtime_client.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/ssh_client.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/status_client.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/client/user_client.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/commands/__init__.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/commands/data.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/commands/org.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/commands/ssh.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/error.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/model/__init__.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/model/dataset.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/model/dataset_version.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/model/file/__init__.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/model/file/bayes_file.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/model/file/data_bindings.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/model/file/openbayes_gear.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/model/file/openbayes_ignore.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/model/file/openbayes_yaml.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/model/file/settings.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/model/party.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/model/resource.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/model/runtime.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/root.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/templates/__init__.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/templates/openbayes_zh-Hans.yaml +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/templates/openbayesignore.yaml +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/__init__.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/archive_usecase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/auth_usecase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/dataset_list_usecase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/dataset_usecase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/dataset_version_usecase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/disk_usecase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/gear_download_usecese.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/gear_logs_usecase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/gear_run_usecase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/minio_storage_usecase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/open_usecase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/org_usecase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/resource_usecase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/runtime_usecase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/ssh_usecase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/switch_usercase.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/usercases/upload_state_manager.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/utils/__init__.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/utils/add_global_param.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/bayes/utils.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/openbayestool/__init__.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/openbayestool/config.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/openbayestool/http_client.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/openbayestool/jwt_client.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/openbayestool/tracker.py +0 -0
- {openbayes_cli-0.26.0 → openbayes_cli-0.26.1}/openbayestool/updater.py +0 -0
|
@@ -106,6 +106,11 @@ class Job(BaseModel):
|
|
|
106
106
|
def is_finished(self) -> bool:
|
|
107
107
|
return self.status in {"SUCCEEDED", "CANCELLED", "FAILED"}
|
|
108
108
|
|
|
109
|
+
def is_workspace(self) -> bool:
|
|
110
|
+
# API 返回的 mode 形如 "WORKSPACE" / "BATCH_WORKSPACE" / "TASK" / "BATCH_TASK"。
|
|
111
|
+
# workspace 进入 RUNNING 后会持续保持运行,是 follow 的合理终点;其它模式会跑完后进入 finished。
|
|
112
|
+
return bool(self.mode) and "WORKSPACE" in self.mode.upper()
|
|
113
|
+
|
|
109
114
|
def get_job_dataBindings(self) -> List[str]:
|
|
110
115
|
datasets = []
|
|
111
116
|
for binding in self.dataBindings or []:
|
|
@@ -124,7 +124,7 @@ def run(
|
|
|
124
124
|
data: List[str] = typer.Option([], "-d", "--data", help="绑定数据"),
|
|
125
125
|
env: str = typer.Option("", "-e", "--env", help="选择镜像"),
|
|
126
126
|
resource: str = typer.Option("", "-r", "--resource", help="选择算力"),
|
|
127
|
-
follow: bool = typer.Option(False, "-f", "--follow", help="
|
|
127
|
+
follow: bool = typer.Option(False, "-f", "--follow", help="跟踪容器运行;task 模式会自动打印实时日志直到任务结束,workspace 进入运行后退出"),
|
|
128
128
|
message: str = typer.Option("", "-m", "--message", help="执行描述"),
|
|
129
129
|
open_browser: bool = typer.Option(False, "-o", "--open", help="成功创建容器后,在浏览器打开"),
|
|
130
130
|
node: int = typer.Option(-1, "-n", "--node", help="指定运行节点数量"),
|
|
@@ -142,7 +142,7 @@ def run(
|
|
|
142
142
|
|
|
143
143
|
-e, --env string 选择镜像
|
|
144
144
|
|
|
145
|
-
-f, --follow [可选]
|
|
145
|
+
-f, --follow [可选] 跟踪容器运行;task 模式会自动打印实时日志直到任务结束,workspace 进入运行后退出
|
|
146
146
|
|
|
147
147
|
-h, --help 查看 run 的帮助
|
|
148
148
|
|
|
@@ -249,7 +249,12 @@ def run(
|
|
|
249
249
|
gear_settings.update_jid(str(current_path), job_id)
|
|
250
250
|
|
|
251
251
|
if follow and mode != ModeEnum.hypertuning:
|
|
252
|
-
|
|
252
|
+
if mode == ModeEnum.workspace:
|
|
253
|
+
# workspace 进入 RUNNING 即视为就绪
|
|
254
|
+
gear_usecase.follow_status(job_id, party_name, stop_with_running=True)
|
|
255
|
+
else:
|
|
256
|
+
# task 等到 RUNNING 后切换为日志流,跟随直到任务结束
|
|
257
|
+
gear_usecase.follow_logs_until_finished(job_id, party_name)
|
|
253
258
|
gear_usecase.print_last_status(job_id, party_name)
|
|
254
259
|
|
|
255
260
|
|
|
@@ -304,7 +309,7 @@ def restart(ctx: typer.Context,
|
|
|
304
309
|
data: List[str] = typer.Option([], "-d", "--data", help="绑定数据"),
|
|
305
310
|
env: str = typer.Option("", "-e", "--env", help="选择镜像"),
|
|
306
311
|
resource: str = typer.Option("", "-r", "--resource", help="选择算力"),
|
|
307
|
-
follow: bool = typer.Option(False, "-f", "--follow", help="
|
|
312
|
+
follow: bool = typer.Option(False, "-f", "--follow", help="跟踪容器运行;task 模式会自动打印实时日志直到任务结束,workspace 进入运行后退出"),
|
|
308
313
|
message: str = typer.Option("", "-m", "--message", help="执行描述"),
|
|
309
314
|
open_browser: bool = typer.Option(False, "-o", "--open", help="成功创建容器后,在浏览器打开"),
|
|
310
315
|
node: int = typer.Option(-1, "-n", "--node", help="指定运行节点数量"),
|
|
@@ -322,7 +327,7 @@ def restart(ctx: typer.Context,
|
|
|
322
327
|
|
|
323
328
|
-e, --env string 选择镜像
|
|
324
329
|
|
|
325
|
-
-f, --follow [可选]
|
|
330
|
+
-f, --follow [可选] 跟踪容器运行;task 模式会自动打印实时日志直到任务结束,workspace 进入运行后退出
|
|
326
331
|
|
|
327
332
|
-h, --help 查看 restart 的帮助
|
|
328
333
|
|
|
@@ -385,7 +390,10 @@ def restart(ctx: typer.Context,
|
|
|
385
390
|
open_usecase.open_browser(new_open_url)
|
|
386
391
|
|
|
387
392
|
if follow:
|
|
388
|
-
|
|
393
|
+
if job.is_workspace():
|
|
394
|
+
gear_usecase.follow_status(job_id, party_name, stop_with_running=True)
|
|
395
|
+
else:
|
|
396
|
+
gear_usecase.follow_logs_until_finished(job_id, party_name)
|
|
389
397
|
gear_usecase.print_last_status(job_id, party_name)
|
|
390
398
|
|
|
391
399
|
|
|
@@ -448,7 +456,8 @@ def stop(ctx: typer.Context,
|
|
|
448
456
|
open_usecase.open_browser(link_value)
|
|
449
457
|
|
|
450
458
|
if follow:
|
|
451
|
-
|
|
459
|
+
# 等待容器真正进入终止态(CANCELLED / FAILED / SUCCEEDED),否则中途的 RUNNING 会让跟踪过早退出。
|
|
460
|
+
gear_usecase.follow_status(id, party_name, False)
|
|
452
461
|
gear_usecase.print_last_status(id, party_name)
|
|
453
462
|
|
|
454
463
|
|
|
@@ -125,7 +125,7 @@ def run(
|
|
|
125
125
|
data: List[str] = typer.Option([], "-d", "--data", help="绑定数据"),
|
|
126
126
|
env: str = typer.Option("", "-e", "--env", help="选择镜像"),
|
|
127
127
|
resource: str = typer.Option("", "-r", "--resource", help="选择算力"),
|
|
128
|
-
follow: bool = typer.Option(False, "-f", "--follow", help="
|
|
128
|
+
follow: bool = typer.Option(False, "-f", "--follow", help="跟踪容器运行;task 模式会自动打印实时日志直到任务结束,workspace 进入运行后退出"),
|
|
129
129
|
message: str = typer.Option("", "-m", "--message", help="执行描述"),
|
|
130
130
|
open_browser: bool = typer.Option(False, "-o", "--open", help="成功创建容器后,在浏览器打开"),
|
|
131
131
|
node: int = typer.Option(-1, "-n", "--node", help="指定运行节点数量"),
|
|
@@ -143,7 +143,7 @@ def run(
|
|
|
143
143
|
|
|
144
144
|
-e, --env string 选择镜像
|
|
145
145
|
|
|
146
|
-
-f, --follow [可选]
|
|
146
|
+
-f, --follow [可选] 跟踪容器运行;task 模式会自动打印实时日志直到任务结束,workspace 进入运行后退出
|
|
147
147
|
|
|
148
148
|
-h, --help 查看 run 的帮助
|
|
149
149
|
|
|
@@ -252,7 +252,10 @@ def run(
|
|
|
252
252
|
gear_settings.update_jid(str(current_path), job_id)
|
|
253
253
|
|
|
254
254
|
if follow and mode != ModeEnum.hypertuning:
|
|
255
|
-
|
|
255
|
+
if mode == ModeEnum.workspace:
|
|
256
|
+
gear_usecase.follow_status(job_id, party_name, stop_with_running=True)
|
|
257
|
+
else:
|
|
258
|
+
gear_usecase.follow_logs_until_finished(job_id, party_name)
|
|
256
259
|
gear_usecase.print_last_status(job_id, party_name)
|
|
257
260
|
|
|
258
261
|
|
|
@@ -307,7 +310,7 @@ def restart(ctx: typer.Context,
|
|
|
307
310
|
data: List[str] = typer.Option([], "-d", "--data", help="绑定数据"),
|
|
308
311
|
env: str = typer.Option("", "-e", "--env", help="选择镜像"),
|
|
309
312
|
resource: str = typer.Option("", "-r", "--resource", help="选择算力"),
|
|
310
|
-
follow: bool = typer.Option(False, "-f", "--follow", help="
|
|
313
|
+
follow: bool = typer.Option(False, "-f", "--follow", help="跟踪容器运行;task 模式会自动打印实时日志直到任务结束,workspace 进入运行后退出"),
|
|
311
314
|
message: str = typer.Option("", "-m", "--message", help="执行描述"),
|
|
312
315
|
open_browser: bool = typer.Option(False, "-o", "--open", help="成功创建容器后,在浏览器打开"),
|
|
313
316
|
node: int = typer.Option(-1, "-n", "--node", help="指定运行节点数量"),
|
|
@@ -325,7 +328,7 @@ def restart(ctx: typer.Context,
|
|
|
325
328
|
|
|
326
329
|
-e, --env string 选择镜像
|
|
327
330
|
|
|
328
|
-
-f, --follow [可选]
|
|
331
|
+
-f, --follow [可选] 跟踪容器运行;task 模式会自动打印实时日志直到任务结束,workspace 进入运行后退出
|
|
329
332
|
|
|
330
333
|
-h, --help 查看 restart 的帮助
|
|
331
334
|
|
|
@@ -388,7 +391,10 @@ def restart(ctx: typer.Context,
|
|
|
388
391
|
open_usecase.open_browser(new_open_url)
|
|
389
392
|
|
|
390
393
|
if follow:
|
|
391
|
-
|
|
394
|
+
if job.is_workspace():
|
|
395
|
+
gear_usecase.follow_status(job_id, party_name, stop_with_running=True)
|
|
396
|
+
else:
|
|
397
|
+
gear_usecase.follow_logs_until_finished(job_id, party_name)
|
|
392
398
|
gear_usecase.print_last_status(job_id, party_name)
|
|
393
399
|
|
|
394
400
|
|
|
@@ -452,7 +458,8 @@ def stop(ctx: typer.Context,
|
|
|
452
458
|
open_usecase.open_browser(link_value)
|
|
453
459
|
|
|
454
460
|
if follow:
|
|
455
|
-
|
|
461
|
+
# 等待容器真正进入终止态(CANCELLED / FAILED / SUCCEEDED),否则中途的 RUNNING 会让跟踪过早退出。
|
|
462
|
+
gear_usecase.follow_status(id, party_name, False)
|
|
456
463
|
gear_usecase.print_last_status(id, party_name)
|
|
457
464
|
|
|
458
465
|
|
|
@@ -26,7 +26,7 @@ from bayes.model.file.openbayes_gear import (
|
|
|
26
26
|
)
|
|
27
27
|
from bayes.model.file.openbayes_ignore import OpenBayesIgnoreSettings
|
|
28
28
|
from bayes.model.file.settings import BayesEnvConfig, BayesSettings
|
|
29
|
-
from bayes.usercases import dataset_version_usecase
|
|
29
|
+
from bayes.usercases import dataset_version_usecase, gear_logs_usecase
|
|
30
30
|
from bayes.usercases.org_usecase import display_table
|
|
31
31
|
from bayes.utils import Utils
|
|
32
32
|
|
|
@@ -194,6 +194,54 @@ def follow_status(id, party_name, stop_with_running):
|
|
|
194
194
|
time.sleep(0.1)
|
|
195
195
|
|
|
196
196
|
|
|
197
|
+
def follow_logs_until_finished(id, party_name):
|
|
198
|
+
"""先等容器进入 RUNNING(或直接 finished),再切换到日志流跟随直到容器结束。
|
|
199
|
+
|
|
200
|
+
日志流不可用时降级为单纯的状态轮询,保证 -f 至少跟到 finished。
|
|
201
|
+
"""
|
|
202
|
+
follow_status(id, party_name, stop_with_running=True)
|
|
203
|
+
|
|
204
|
+
default_env: Optional[BayesEnvConfig] = BayesSettings().default_env
|
|
205
|
+
gql_client = BayesGQLClient(default_env.graphQL, default_env.token)
|
|
206
|
+
job = gear_client.get_job_by_id(gql_client, id, party_name)
|
|
207
|
+
if job is None or job.is_finished():
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
historical = gear_logs_usecase.get_logs(id, party_name)
|
|
211
|
+
if historical:
|
|
212
|
+
print(historical)
|
|
213
|
+
|
|
214
|
+
ws, error = gear_logs_usecase.get_logs_follow(id, party_name)
|
|
215
|
+
if error is not None or ws is None:
|
|
216
|
+
# 拿不到日志流就退化成轮询状态,仍然跟到任务结束
|
|
217
|
+
follow_status(id, party_name, stop_with_running=False)
|
|
218
|
+
return
|
|
219
|
+
|
|
220
|
+
print("已连接到日志流,按 Ctrl+C 停止跟踪...")
|
|
221
|
+
log_stream_closed = False
|
|
222
|
+
try:
|
|
223
|
+
while True:
|
|
224
|
+
data, err = gear_logs_usecase.receive_logs(ws, id)
|
|
225
|
+
if err is not None:
|
|
226
|
+
print(err)
|
|
227
|
+
log_stream_closed = True
|
|
228
|
+
break
|
|
229
|
+
if data:
|
|
230
|
+
print(data)
|
|
231
|
+
except KeyboardInterrupt:
|
|
232
|
+
print("\n已停止日志跟踪")
|
|
233
|
+
finally:
|
|
234
|
+
try:
|
|
235
|
+
ws.close()
|
|
236
|
+
except Exception:
|
|
237
|
+
pass
|
|
238
|
+
|
|
239
|
+
# 日志流自然结束后,job 状态需要几秒才上报为 finished;这里轮询一下,
|
|
240
|
+
# 让调用方的 print_last_status 拿到正确的最终状态。Ctrl+C 提前退出则跳过。
|
|
241
|
+
if log_stream_closed:
|
|
242
|
+
follow_status(id, party_name, stop_with_running=False)
|
|
243
|
+
|
|
244
|
+
|
|
197
245
|
def print_last_status(id, party_name):
|
|
198
246
|
default_env: Optional[BayesEnvConfig] = BayesSettings().default_env
|
|
199
247
|
gql_client = BayesGQLClient(default_env.graphQL, default_env.token)
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|