simstack 2.0.1__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.
- simstack/__init__.py +0 -0
- simstack/_version.py +24 -0
- simstack/core/__init__.py +0 -0
- simstack/core/artifacts.py +406 -0
- simstack/core/asnyc_helper.py +14 -0
- simstack/core/config_file.py +60 -0
- simstack/core/context.py +283 -0
- simstack/core/definitions.py +22 -0
- simstack/core/engine.py +115 -0
- simstack/core/find_simstack_modules.py +41 -0
- simstack/core/hash.py +179 -0
- simstack/core/node.py +1137 -0
- simstack/core/node_claim.py +31 -0
- simstack/core/node_runner.py +362 -0
- simstack/core/process_results.py +102 -0
- simstack/core/recompute_artifacts.py +117 -0
- simstack/core/reset_db.py +12 -0
- simstack/core/resource_assignment.py +222 -0
- simstack/core/resources.py +90 -0
- simstack/core/route_table.py +24 -0
- simstack/core/run_docker.py +109 -0
- simstack/core/run_node.py +71 -0
- simstack/core/runner.py +169 -0
- simstack/core/services/__init__.py +11 -0
- simstack/core/services/base_service.py +191 -0
- simstack/core/services/file_transfer_service.py +117 -0
- simstack/core/services/git_restart_service.py +12 -0
- simstack/core/services/git_uv_update_service.py +105 -0
- simstack/core/services/node_execution_service.py +182 -0
- simstack/core/services/resource_branch_monitor_service.py +71 -0
- simstack/core/services/runner_cleanup_service.py +149 -0
- simstack/core/services/runner_manager.py +163 -0
- simstack/core/services/runner_status_service.py +10 -0
- simstack/core/services/slurm_status_service.py +67 -0
- simstack/core/services/stop_check_service.py +27 -0
- simstack/core/services/timeout_restart_service.py +26 -0
- simstack/core/simstack_result.py +39 -0
- simstack/core/submit_node.py +217 -0
- simstack/core/task_id.py +22 -0
- simstack/methods/__init__.py +0 -0
- simstack/methods/archive_files.py +292 -0
- simstack/methods/fire_and_forget_runner.py +113 -0
- simstack/methods/generate_test.py +125 -0
- simstack/methods/get_file.py +26 -0
- simstack/methods/mass_runner.py +154 -0
- simstack/methods/multiple_executor.py +307 -0
- simstack/methods/switch_git.py +16 -0
- simstack/methods/upload_helpers.py +81 -0
- simstack/models/__init__.py +74 -0
- simstack/models/array_list.py +21 -0
- simstack/models/array_storage.py +83 -0
- simstack/models/artifact_models.py +127 -0
- simstack/models/base_lists.py +51 -0
- simstack/models/base_types.py +209 -0
- simstack/models/charts_artifact.py +690 -0
- simstack/models/dataset.py +451 -0
- simstack/models/dataset_metadata.py +390 -0
- simstack/models/datasettuple.py +587 -0
- simstack/models/file_instance.py +150 -0
- simstack/models/file_list.py +45 -0
- simstack/models/files.py +424 -0
- simstack/models/fire_and_forget_result.py +19 -0
- simstack/models/images2d.py +113 -0
- simstack/models/log_entry_model.py +37 -0
- simstack/models/models.py +52 -0
- simstack/models/named_data_reference.py +36 -0
- simstack/models/node_registry.py +96 -0
- simstack/models/pandas_model.py +239 -0
- simstack/models/parameters.py +402 -0
- simstack/models/pickle_models.py +300 -0
- simstack/models/project.py +13 -0
- simstack/models/resource_assignment.py +170 -0
- simstack/models/resource_definition.py +125 -0
- simstack/models/runner_model.py +39 -0
- simstack/models/simple_table.py +79 -0
- simstack/models/simstack_model.py +97 -0
- simstack/models/slurm_info.py +17 -0
- simstack/models/table_artifact.py +87 -0
- simstack/models/tag.py +12 -0
- simstack/models/unused_test_file.py +5 -0
- simstack/simstack_marker.com +1 -0
- simstack/static/docs/.buildinfo +4 -0
- simstack/static/docs/_images/GUI Concept.webp +0 -0
- simstack/static/docs/_modules/index.html +434 -0
- simstack/static/docs/_modules/simstack/core/artifacts.html +743 -0
- simstack/static/docs/_modules/simstack/core/asnyc_helper.html +330 -0
- simstack/static/docs/_modules/simstack/core/config_file.html +379 -0
- simstack/static/docs/_modules/simstack/core/definitions.html +341 -0
- simstack/static/docs/_modules/simstack/core/engine.html +437 -0
- simstack/static/docs/_modules/simstack/core/find_simstack_modules.html +360 -0
- simstack/static/docs/_modules/simstack/core/hash.html +528 -0
- simstack/static/docs/_modules/simstack/core/node.html +1392 -0
- simstack/static/docs/_modules/simstack/core/node_claim.html +347 -0
- simstack/static/docs/_modules/simstack/core/node_runner.html +711 -0
- simstack/static/docs/_modules/simstack/core/process_results.html +418 -0
- simstack/static/docs/_modules/simstack/core/recompute_artifacts.html +433 -0
- simstack/static/docs/_modules/simstack/core/reset_db.html +328 -0
- simstack/static/docs/_modules/simstack/core/resource_assignment.html +553 -0
- simstack/static/docs/_modules/simstack/core/resources.html +424 -0
- simstack/static/docs/_modules/simstack/core/route_table.html +349 -0
- simstack/static/docs/_modules/simstack/core/run_docker.html +425 -0
- simstack/static/docs/_modules/simstack/core/run_node.html +390 -0
- simstack/static/docs/_modules/simstack/core/runner.html +491 -0
- simstack/static/docs/_modules/simstack/core/services/base_service.html +522 -0
- simstack/static/docs/_modules/simstack/core/services/file_transfer_service.html +436 -0
- simstack/static/docs/_modules/simstack/core/services/git_restart_service.html +331 -0
- simstack/static/docs/_modules/simstack/core/services/git_uv_update_service.html +427 -0
- simstack/static/docs/_modules/simstack/core/services/node_execution_service.html +507 -0
- simstack/static/docs/_modules/simstack/core/services/resource_branch_monitor_service.html +390 -0
- simstack/static/docs/_modules/simstack/core/services/runner_cleanup_service.html +468 -0
- simstack/static/docs/_modules/simstack/core/services/runner_manager.html +485 -0
- simstack/static/docs/_modules/simstack/core/services/runner_status_service.html +329 -0
- simstack/static/docs/_modules/simstack/core/services/slurm_status_service.html +386 -0
- simstack/static/docs/_modules/simstack/core/services/stop_check_service.html +346 -0
- simstack/static/docs/_modules/simstack/core/services/timeout_restart_service.html +345 -0
- simstack/static/docs/_modules/simstack/core/simstack_result.html +355 -0
- simstack/static/docs/_modules/simstack/core/submit_node.html +536 -0
- simstack/static/docs/_modules/simstack/core/task_id.html +344 -0
- simstack/static/docs/_modules/simstack/methods/archive_files.html +598 -0
- simstack/static/docs/_modules/simstack/methods/fire_and_forget_runner.html +432 -0
- simstack/static/docs/_modules/simstack/methods/generate_test.html +443 -0
- simstack/static/docs/_modules/simstack/methods/get_file.html +342 -0
- simstack/static/docs/_modules/simstack/methods/mass_runner.html +476 -0
- simstack/static/docs/_modules/simstack/methods/multiple_executor.html +674 -0
- simstack/static/docs/_modules/simstack/methods/switch_git.html +332 -0
- simstack/static/docs/_modules/simstack/methods/upload_helpers.html +403 -0
- simstack/static/docs/_modules/simstack/models/array_list.html +337 -0
- simstack/static/docs/_modules/simstack/models/array_storage.html +417 -0
- simstack/static/docs/_modules/simstack/models/base_lists.html +374 -0
- simstack/static/docs/_modules/simstack/models/base_types.html +588 -0
- simstack/static/docs/_modules/simstack/models/charts_artifact.html +1035 -0
- simstack/static/docs/_modules/simstack/models/dataset.html +866 -0
- simstack/static/docs/_modules/simstack/models/dataset_metadata.html +711 -0
- simstack/static/docs/_modules/simstack/models/datasettuple.html +1005 -0
- simstack/static/docs/_modules/simstack/models/file_instance.html +472 -0
- simstack/static/docs/_modules/simstack/models/file_list.html +367 -0
- simstack/static/docs/_modules/simstack/models/files.html +773 -0
- simstack/static/docs/_modules/simstack/models/fire_and_forget_result.html +335 -0
- simstack/static/docs/_modules/simstack/models/images2d.html +447 -0
- simstack/static/docs/_modules/simstack/models/log_entry_model.html +356 -0
- simstack/static/docs/_modules/simstack/models/models.html +374 -0
- simstack/static/docs/_modules/simstack/models/named_data_reference.html +355 -0
- simstack/static/docs/_modules/simstack/models/node_registry.html +415 -0
- simstack/static/docs/_modules/simstack/models/pandas_model.html +570 -0
- simstack/static/docs/_modules/simstack/models/parameters.html +739 -0
- simstack/static/docs/_modules/simstack/models/pickle_models.html +631 -0
- simstack/static/docs/_modules/simstack/models/project.html +329 -0
- simstack/static/docs/_modules/simstack/models/resource_assignment.html +501 -0
- simstack/static/docs/_modules/simstack/models/resource_definition.html +477 -0
- simstack/static/docs/_modules/simstack/models/runner_model.html +361 -0
- simstack/static/docs/_modules/simstack/models/simple_table.html +413 -0
- simstack/static/docs/_modules/simstack/models/simstack_model.html +416 -0
- simstack/static/docs/_modules/simstack/models/slurm_info.html +333 -0
- simstack/static/docs/_modules/simstack/models/table_artifact.html +406 -0
- simstack/static/docs/_modules/simstack/models/tag.html +328 -0
- simstack/static/docs/_modules/simstack/tables/model_table.html +519 -0
- simstack/static/docs/_modules/simstack/tables/node_children.html +538 -0
- simstack/static/docs/_modules/simstack/tables/node_table.html +854 -0
- simstack/static/docs/_modules/simstack/tables/table_builder.html +642 -0
- simstack/static/docs/_modules/simstack/util/async_zip_utils.html +337 -0
- simstack/static/docs/_modules/simstack/util/b64mixin.html +339 -0
- simstack/static/docs/_modules/simstack/util/cleaned_json_schema.html +336 -0
- simstack/static/docs/_modules/simstack/util/config_reader.html +498 -0
- simstack/static/docs/_modules/simstack/util/custom_model_dump.html +391 -0
- simstack/static/docs/_modules/simstack/util/database_information.html +488 -0
- simstack/static/docs/_modules/simstack/util/db.html +1392 -0
- simstack/static/docs/_modules/simstack/util/db_logger.html +448 -0
- simstack/static/docs/_modules/simstack/util/default_from_dict.html +376 -0
- simstack/static/docs/_modules/simstack/util/directory_iterator.html +553 -0
- simstack/static/docs/_modules/simstack/util/docstring_parser.html +526 -0
- simstack/static/docs/_modules/simstack/util/file_hashing.html +394 -0
- simstack/static/docs/_modules/simstack/util/file_transfer_client.html +791 -0
- simstack/static/docs/_modules/simstack/util/files.html +733 -0
- simstack/static/docs/_modules/simstack/util/find_python_files.html +349 -0
- simstack/static/docs/_modules/simstack/util/generate_ui_schema.html +396 -0
- simstack/static/docs/_modules/simstack/util/generic_list_mixin.html +487 -0
- simstack/static/docs/_modules/simstack/util/get_module_path.html +336 -0
- simstack/static/docs/_modules/simstack/util/git_pull.html +381 -0
- simstack/static/docs/_modules/simstack/util/git_repository_status.html +411 -0
- simstack/static/docs/_modules/simstack/util/import_module.html +377 -0
- simstack/static/docs/_modules/simstack/util/importer.html +637 -0
- simstack/static/docs/_modules/simstack/util/init_data_source.html +357 -0
- simstack/static/docs/_modules/simstack/util/make_graph.html +423 -0
- simstack/static/docs/_modules/simstack/util/make_table.html +1171 -0
- simstack/static/docs/_modules/simstack/util/mappings.html +378 -0
- simstack/static/docs/_modules/simstack/util/minimal_route_finder.html +388 -0
- simstack/static/docs/_modules/simstack/util/mock_db.html +472 -0
- simstack/static/docs/_modules/simstack/util/module_path_checker.html +354 -0
- simstack/static/docs/_modules/simstack/util/object_list_mixin.html +649 -0
- simstack/static/docs/_modules/simstack/util/path_manager.html +566 -0
- simstack/static/docs/_modules/simstack/util/project_root_finder.html +357 -0
- simstack/static/docs/_modules/simstack/util/queue_watcher.html +593 -0
- simstack/static/docs/_modules/simstack/util/resource_config.html +532 -0
- simstack/static/docs/_modules/simstack/util/route_finder.html +390 -0
- simstack/static/docs/_modules/simstack/util/runner_util.html +487 -0
- simstack/static/docs/_modules/simstack/util/runner_utils.html +468 -0
- simstack/static/docs/_modules/simstack/util/safe_code_executor.html +510 -0
- simstack/static/docs/_modules/simstack/util/sample_config.html +414 -0
- simstack/static/docs/_modules/simstack/util/sensitive_content_filter.html +364 -0
- simstack/static/docs/_modules/simstack/util/setup_logging.html +365 -0
- simstack/static/docs/_modules/simstack/util/submit_to_watchdog.html +425 -0
- simstack/static/docs/_modules/simstack/util/toml_reader.html +482 -0
- simstack/static/docs/_modules/simstack/util/transform_file_name.html +357 -0
- simstack/static/docs/_modules/simstack/util/ui_tools.html +541 -0
- simstack/static/docs/_sources/architecture.rst.txt +25 -0
- simstack/static/docs/_sources/array_lists.rst.txt +60 -0
- simstack/static/docs/_sources/ci_pipeline.rst.txt +34 -0
- simstack/static/docs/_sources/data.rst.txt +175 -0
- simstack/static/docs/_sources/database_migrations.rst.txt +310 -0
- simstack/static/docs/_sources/datasets.rst.txt +93 -0
- simstack/static/docs/_sources/developer_guide.rst.txt +25 -0
- simstack/static/docs/_sources/docker_nodes.rst.txt +149 -0
- simstack/static/docs/_sources/files.rst.txt +159 -0
- simstack/static/docs/_sources/fire_and_forget_runner.rst.txt +67 -0
- simstack/static/docs/_sources/index.rst.txt +25 -0
- simstack/static/docs/_sources/installation.rst.txt +213 -0
- simstack/static/docs/_sources/lists.rst.txt +111 -0
- simstack/static/docs/_sources/mass_runner.rst.txt +91 -0
- simstack/static/docs/_sources/misc.rst.txt +106 -0
- simstack/static/docs/_sources/overview.rst.txt +91 -0
- simstack/static/docs/_sources/recovery.rst.txt +38 -0
- simstack/static/docs/_sources/resource_config.rst.txt +141 -0
- simstack/static/docs/_sources/simstack/modules/modules.rst.txt +7 -0
- simstack/static/docs/_sources/simstack/modules/simstack.core.rst.txt +205 -0
- simstack/static/docs/_sources/simstack/modules/simstack.core.services.rst.txt +109 -0
- simstack/static/docs/_sources/simstack/modules/simstack.methods.rst.txt +77 -0
- simstack/static/docs/_sources/simstack/modules/simstack.models.rst.txt +261 -0
- simstack/static/docs/_sources/simstack/modules/simstack.rst.txt +22 -0
- simstack/static/docs/_sources/simstack/modules/simstack.tables.rst.txt +45 -0
- simstack/static/docs/_sources/simstack/modules/simstack.util.rst.txt +381 -0
- simstack/static/docs/_sources/submit.rst.txt +141 -0
- simstack/static/docs/_sources/tables.rst.txt +50 -0
- simstack/static/docs/_sources/testing_complex_nodes.rst.txt +92 -0
- simstack/static/docs/_sources/ui_documentation.rst.txt +272 -0
- simstack/static/docs/_sources/workflows.rst.txt +446 -0
- simstack/static/docs/_static/base-stemmer.js +476 -0
- simstack/static/docs/_static/basic.css +906 -0
- simstack/static/docs/_static/cobalt2-theme.css +1 -0
- simstack/static/docs/_static/debug.css +69 -0
- simstack/static/docs/_static/docs-build-badge.css +51 -0
- simstack/static/docs/_static/docs-build-badge.js +51 -0
- simstack/static/docs/_static/doctools.js +150 -0
- simstack/static/docs/_static/documentation_options.js +13 -0
- simstack/static/docs/_static/english-stemmer.js +1066 -0
- simstack/static/docs/_static/file.png +0 -0
- simstack/static/docs/_static/language_data.js +13 -0
- simstack/static/docs/_static/minus.png +0 -0
- simstack/static/docs/_static/plus.png +0 -0
- simstack/static/docs/_static/pygments.css +250 -0
- simstack/static/docs/_static/scripts/furo-extensions.js +0 -0
- simstack/static/docs/_static/scripts/furo.js +3 -0
- simstack/static/docs/_static/scripts/furo.js.LICENSE.txt +7 -0
- simstack/static/docs/_static/scripts/furo.js.map +1 -0
- simstack/static/docs/_static/searchtools.js +693 -0
- simstack/static/docs/_static/skeleton.css +296 -0
- simstack/static/docs/_static/sphinx_highlight.js +159 -0
- simstack/static/docs/_static/styles/furo-extensions.css +2 -0
- simstack/static/docs/_static/styles/furo-extensions.css.map +1 -0
- simstack/static/docs/_static/styles/furo.css +2 -0
- simstack/static/docs/_static/styles/furo.css.map +1 -0
- simstack/static/docs/architecture.html +340 -0
- simstack/static/docs/array_lists.html +678 -0
- simstack/static/docs/ci_pipeline.html +385 -0
- simstack/static/docs/data.html +545 -0
- simstack/static/docs/database_migrations.html +652 -0
- simstack/static/docs/datasets.html +1268 -0
- simstack/static/docs/developer_guide.html +359 -0
- simstack/static/docs/docker_nodes.html +503 -0
- simstack/static/docs/files.html +718 -0
- simstack/static/docs/fire_and_forget_runner.html +641 -0
- simstack/static/docs/genindex.html +6390 -0
- simstack/static/docs/index.html +516 -0
- simstack/static/docs/installation.html +553 -0
- simstack/static/docs/lists.html +478 -0
- simstack/static/docs/mass_runner.html +481 -0
- simstack/static/docs/misc.html +454 -0
- simstack/static/docs/objects.inv +0 -0
- simstack/static/docs/overview.html +469 -0
- simstack/static/docs/py-modindex.html +1111 -0
- simstack/static/docs/recovery.html +371 -0
- simstack/static/docs/resource_config.html +590 -0
- simstack/static/docs/search.html +327 -0
- simstack/static/docs/searchindex.js +1 -0
- simstack/static/docs/simstack/modules/modules.html +489 -0
- simstack/static/docs/simstack/modules/simstack.core.html +1883 -0
- simstack/static/docs/simstack/modules/simstack.core.services.html +698 -0
- simstack/static/docs/simstack/modules/simstack.html +3060 -0
- simstack/static/docs/simstack/modules/simstack.methods.html +1150 -0
- simstack/static/docs/simstack/modules/simstack.models.html +13113 -0
- simstack/static/docs/simstack/modules/simstack.tables.html +635 -0
- simstack/static/docs/simstack/modules/simstack.util.html +3320 -0
- simstack/static/docs/submit.html +487 -0
- simstack/static/docs/tables.html +539 -0
- simstack/static/docs/testing_complex_nodes.html +458 -0
- simstack/static/docs/ui_documentation.html +788 -0
- simstack/static/docs/workflows.html +766 -0
- simstack/tables/__init__.py +0 -0
- simstack/tables/model_table.py +191 -0
- simstack/tables/node_children.py +219 -0
- simstack/tables/node_table.py +517 -0
- simstack/tables/table_builder.py +314 -0
- simstack/util/__init__.py +0 -0
- simstack/util/async_zip_utils.py +18 -0
- simstack/util/b64mixin.py +23 -0
- simstack/util/cleaned_json_schema.py +20 -0
- simstack/util/config_reader.py +179 -0
- simstack/util/custom_model_dump.py +75 -0
- simstack/util/database_information.py +160 -0
- simstack/util/db.py +1013 -0
- simstack/util/db_logger.py +117 -0
- simstack/util/default_from_dict.py +57 -0
- simstack/util/directory_iterator.py +216 -0
- simstack/util/docstring_parser.py +186 -0
- simstack/util/file_hashing.py +72 -0
- simstack/util/file_transfer_client.py +427 -0
- simstack/util/files.py +396 -0
- simstack/util/find_python_files.py +33 -0
- simstack/util/generate_ui_schema.py +80 -0
- simstack/util/generic_list_mixin.py +120 -0
- simstack/util/geometry_utils.py +118 -0
- simstack/util/get_module_path.py +20 -0
- simstack/util/git_pull.py +65 -0
- simstack/util/git_repository_status.py +95 -0
- simstack/util/import_module.py +63 -0
- simstack/util/importer.py +312 -0
- simstack/util/init_data_source.py +41 -0
- simstack/util/make_graph.py +107 -0
- simstack/util/make_table.py +837 -0
- simstack/util/mappings.py +41 -0
- simstack/util/minimal_route_finder.py +72 -0
- simstack/util/mock_db.py +144 -0
- simstack/util/module_path_checker.py +38 -0
- simstack/util/object_list_mixin.py +282 -0
- simstack/util/path_manager.py +223 -0
- simstack/util/project_root_finder.py +41 -0
- simstack/util/queue_watcher.py +262 -0
- simstack/util/resource_config.py +198 -0
- simstack/util/route_finder.py +74 -0
- simstack/util/runner_util.py +166 -0
- simstack/util/runner_utils.py +143 -0
- simstack/util/safe_code_executor.py +194 -0
- simstack/util/sample_config.py +98 -0
- simstack/util/sensitive_content_filter.py +48 -0
- simstack/util/setup_logging.py +49 -0
- simstack/util/submit_to_watchdog.py +106 -0
- simstack/util/toml_reader.py +142 -0
- simstack/util/transform_file_name.py +41 -0
- simstack/util/ui_tools.py +213 -0
- simstack-2.0.1.dist-info/METADATA +454 -0
- simstack-2.0.1.dist-info/RECORD +354 -0
- simstack-2.0.1.dist-info/WHEEL +5 -0
- simstack-2.0.1.dist-info/entry_points.txt +12 -0
- simstack-2.0.1.dist-info/licenses/LICENSE +21 -0
- simstack-2.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
from datetime import datetime, timedelta
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from simstack.core.context import context
|
|
8
|
+
from simstack.models.files import FileStack
|
|
9
|
+
from simstack.models.parameters import Resource
|
|
10
|
+
from simstack.models.runner_model import RunnerEvent, RunnerType
|
|
11
|
+
from simstack.core.services.base_service import BaseService
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger("NodeRunner")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _resource_to_str(resource: Any) -> str:
|
|
17
|
+
if hasattr(resource, "resource_str"):
|
|
18
|
+
return str(getattr(resource, "resource_str"))
|
|
19
|
+
raw = getattr(resource, "__dict__", {}).get("value")
|
|
20
|
+
if raw is not None:
|
|
21
|
+
return str(raw)
|
|
22
|
+
return str(resource)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _cache_ttl_days() -> int:
|
|
26
|
+
raw = os.environ.get("SIMSTACK_FILE_CACHE_TTL_DAYS")
|
|
27
|
+
if raw is None:
|
|
28
|
+
return 14
|
|
29
|
+
try:
|
|
30
|
+
value = int(raw)
|
|
31
|
+
except ValueError:
|
|
32
|
+
return 14
|
|
33
|
+
return max(value, 0)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _cache_cleanup_enabled() -> bool:
|
|
37
|
+
return os.environ.get("SIMSTACK_FILE_CACHE_CLEANUP_ENABLED", "1").lower() not in {
|
|
38
|
+
"0",
|
|
39
|
+
"false",
|
|
40
|
+
"no",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _as_naive_datetime(value: datetime | None) -> datetime | None:
|
|
45
|
+
if value is None:
|
|
46
|
+
return None
|
|
47
|
+
if value.tzinfo is None:
|
|
48
|
+
return value
|
|
49
|
+
return value.astimezone().replace(tzinfo=None)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class RunnerCleanupService(BaseService):
|
|
53
|
+
"""
|
|
54
|
+
Service that cleans up old RunnerEvent logs and expired local file cache
|
|
55
|
+
copies for the current resource.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(self, resource: Resource, interval: int = 300) -> None:
|
|
59
|
+
# Default interval 5 minutes
|
|
60
|
+
super().__init__("RunnerCleanup", resource, interval)
|
|
61
|
+
|
|
62
|
+
async def execute(self) -> None:
|
|
63
|
+
cutoff_time = datetime.now() - timedelta(minutes=30)
|
|
64
|
+
|
|
65
|
+
# Find and delete events matching the criteria
|
|
66
|
+
old_events = await context.db.find(
|
|
67
|
+
RunnerEvent,
|
|
68
|
+
(RunnerEvent.runner_type == RunnerType.RESOURCE_RUNNER)
|
|
69
|
+
& (RunnerEvent.resource == self._resource)
|
|
70
|
+
& (RunnerEvent.timestamp < cutoff_time),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if old_events:
|
|
74
|
+
logger.info(
|
|
75
|
+
f"Cleaning up {len(old_events)} old RunnerEvent logs for resource {self._resource}"
|
|
76
|
+
)
|
|
77
|
+
for event in old_events:
|
|
78
|
+
await context.db.delete(event)
|
|
79
|
+
|
|
80
|
+
if _cache_cleanup_enabled():
|
|
81
|
+
await self._cleanup_file_cache()
|
|
82
|
+
|
|
83
|
+
async def _cleanup_file_cache(self) -> None:
|
|
84
|
+
ttl_days = _cache_ttl_days()
|
|
85
|
+
cutoff_time = datetime.now() - timedelta(days=ttl_days)
|
|
86
|
+
resource_name = _resource_to_str(self._resource)
|
|
87
|
+
workdir = Path(context.config.workdir).resolve()
|
|
88
|
+
|
|
89
|
+
file_stacks = await context.db.find_all(FileStack)
|
|
90
|
+
updated_count = 0
|
|
91
|
+
deleted_count = 0
|
|
92
|
+
|
|
93
|
+
for file_stack in file_stacks:
|
|
94
|
+
changed = False
|
|
95
|
+
for location in file_stack.locations or []:
|
|
96
|
+
if _resource_to_str(location.resource) != resource_name:
|
|
97
|
+
continue
|
|
98
|
+
if getattr(location, "location_type", "local_path") != "local_path":
|
|
99
|
+
continue
|
|
100
|
+
if getattr(location, "status", "available") != "available":
|
|
101
|
+
continue
|
|
102
|
+
if not getattr(location, "is_cached", False):
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
accessed_at = _as_naive_datetime(
|
|
106
|
+
getattr(location, "last_accessed_at", None) or location.created_at
|
|
107
|
+
)
|
|
108
|
+
if accessed_at and accessed_at > cutoff_time:
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
path = Path(location.path)
|
|
112
|
+
resolved_path = path if path.is_absolute() else workdir / path
|
|
113
|
+
try:
|
|
114
|
+
resolved_path = resolved_path.resolve()
|
|
115
|
+
if (
|
|
116
|
+
workdir != resolved_path
|
|
117
|
+
and workdir not in resolved_path.parents
|
|
118
|
+
):
|
|
119
|
+
logger.warning(
|
|
120
|
+
"Skipping cached FileInstance outside workdir: file_stack=%s path=%s",
|
|
121
|
+
file_stack.id,
|
|
122
|
+
resolved_path,
|
|
123
|
+
)
|
|
124
|
+
continue
|
|
125
|
+
if resolved_path.exists():
|
|
126
|
+
resolved_path.unlink()
|
|
127
|
+
deleted_count += 1
|
|
128
|
+
location.status = "deleted"
|
|
129
|
+
location.expires_at = datetime.now()
|
|
130
|
+
changed = True
|
|
131
|
+
except Exception as exc:
|
|
132
|
+
logger.warning(
|
|
133
|
+
"Failed to clean cached FileInstance file_stack=%s path=%s: %s",
|
|
134
|
+
file_stack.id,
|
|
135
|
+
resolved_path,
|
|
136
|
+
exc,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if changed:
|
|
140
|
+
await context.db.save(file_stack)
|
|
141
|
+
updated_count += 1
|
|
142
|
+
|
|
143
|
+
if updated_count:
|
|
144
|
+
logger.info(
|
|
145
|
+
"Cleaned file cache for resource %s: marked %s FileStack(s), deleted %s file(s)",
|
|
146
|
+
resource_name,
|
|
147
|
+
updated_count,
|
|
148
|
+
deleted_count,
|
|
149
|
+
)
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from simstack.core.services.base_service import BaseService
|
|
8
|
+
from simstack.core.context import context
|
|
9
|
+
from simstack.models.parameters import Resource
|
|
10
|
+
from simstack.core.services.node_execution_service import NodeExecutionService
|
|
11
|
+
from simstack.core.services.runner_status_service import RunnerStatusService
|
|
12
|
+
from simstack.core.services.runner_cleanup_service import RunnerCleanupService
|
|
13
|
+
from simstack.core.services.file_transfer_service import FileTransferService
|
|
14
|
+
from simstack.core.services.slurm_status_service import SlurmStatusService
|
|
15
|
+
from simstack.core.services.resource_branch_monitor_service import (
|
|
16
|
+
ResourceBranchMonitorService,
|
|
17
|
+
)
|
|
18
|
+
from simstack.core.services.stop_check_service import StopCheckService
|
|
19
|
+
from simstack.core.services.git_uv_update_service import GitUvUpdateService
|
|
20
|
+
from simstack.core.services.timeout_restart_service import TimeoutRestartService
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger("NodeRunner")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class RunnerManager:
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
resource: Resource,
|
|
29
|
+
detach: bool = True,
|
|
30
|
+
no_pull: bool = False,
|
|
31
|
+
is_default: bool = False,
|
|
32
|
+
with_file_transfer: bool = True,
|
|
33
|
+
) -> None:
|
|
34
|
+
self._resource = resource
|
|
35
|
+
self._detach = detach
|
|
36
|
+
self._no_pull = no_pull
|
|
37
|
+
self._pid = os.getpid()
|
|
38
|
+
self._services: list[BaseService] = []
|
|
39
|
+
self._shutdown_event: asyncio.Event = asyncio.Event()
|
|
40
|
+
self._pid_file: Path = Path(context.config.workdir) / f"runner_{resource}.pid"
|
|
41
|
+
self._is_default = is_default
|
|
42
|
+
self._with_file_transfer = with_file_transfer
|
|
43
|
+
|
|
44
|
+
def _is_process_running(self, pid: int) -> bool:
|
|
45
|
+
"""Check if a process with given PID is running and is a simstack_runner process"""
|
|
46
|
+
try:
|
|
47
|
+
if platform.system() == "Windows":
|
|
48
|
+
# On Windows, os.kill with signal 0 doesn't work, use tasklist
|
|
49
|
+
import subprocess
|
|
50
|
+
|
|
51
|
+
result = subprocess.run(
|
|
52
|
+
["tasklist", "/FI", f"PID eq {pid}", "/V", "/FO", "CSV"],
|
|
53
|
+
capture_output=True,
|
|
54
|
+
text=False, # keep bytes; avoid UnicodeDecodeError from console code pages
|
|
55
|
+
)
|
|
56
|
+
# Check if process exists and command line contains simstack_runner
|
|
57
|
+
stdout = result.stdout.decode("utf-8", errors="ignore")
|
|
58
|
+
return str(pid) in stdout and "simstack_runner" in stdout.lower()
|
|
59
|
+
else:
|
|
60
|
+
# Check if process exists
|
|
61
|
+
os.kill(pid, 0)
|
|
62
|
+
# Verify it's a simstack_runner process by checking command line
|
|
63
|
+
try:
|
|
64
|
+
with open(f"/proc/{pid}/cmdline", "r") as f:
|
|
65
|
+
cmdline = f.read()
|
|
66
|
+
return "simstack_runner" in cmdline
|
|
67
|
+
except (FileNotFoundError, PermissionError):
|
|
68
|
+
# If we can't read cmdline, fall back to just checking if process exists
|
|
69
|
+
return True
|
|
70
|
+
except (OSError, ProcessLookupError):
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
def _check_existing_runner(self) -> None:
|
|
74
|
+
"""Check if another runner for this resource is already running on this host"""
|
|
75
|
+
if self._pid_file.exists():
|
|
76
|
+
try:
|
|
77
|
+
existing_pid = int(self._pid_file.read_text().strip())
|
|
78
|
+
if existing_pid != self._pid and self._is_process_running(existing_pid):
|
|
79
|
+
error_msg = (
|
|
80
|
+
f"Another runner for resource '{self._resource}' is already running "
|
|
81
|
+
f"on this host with PID {existing_pid}. Exiting."
|
|
82
|
+
)
|
|
83
|
+
logger.error(error_msg)
|
|
84
|
+
raise RuntimeError(error_msg)
|
|
85
|
+
else:
|
|
86
|
+
logger.info(
|
|
87
|
+
f"Stale PID file found for resource '{self._resource}'. "
|
|
88
|
+
f"Overwriting with current PID {self._pid}."
|
|
89
|
+
)
|
|
90
|
+
except (ValueError, OSError) as e:
|
|
91
|
+
logger.warning(
|
|
92
|
+
f"Could not read PID file: {e}. Proceeding with startup."
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
async def stop_all_services(self) -> None:
|
|
96
|
+
"""Gracefully stop all registered services"""
|
|
97
|
+
logger.info("Stopping all services...")
|
|
98
|
+
await asyncio.gather(
|
|
99
|
+
*(s.stop() for s in self._services), return_exceptions=True
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
async def run_nodes_for_resource(
|
|
103
|
+
self,
|
|
104
|
+
polling_interval: int = 5,
|
|
105
|
+
max_concurrent: int = 10,
|
|
106
|
+
timeout: int | None = None,
|
|
107
|
+
) -> None:
|
|
108
|
+
"""Orchestrates multiple independent services"""
|
|
109
|
+
# Check if another runner is already running for this resource
|
|
110
|
+
self._check_existing_runner()
|
|
111
|
+
|
|
112
|
+
# Save PID on startup
|
|
113
|
+
self._pid_file.write_text(str(self._pid))
|
|
114
|
+
|
|
115
|
+
self._services = [
|
|
116
|
+
NodeExecutionService(
|
|
117
|
+
self._resource,
|
|
118
|
+
polling_interval,
|
|
119
|
+
max_concurrent,
|
|
120
|
+
self._shutdown_event,
|
|
121
|
+
detach=self._detach,
|
|
122
|
+
is_default=self._is_default,
|
|
123
|
+
),
|
|
124
|
+
RunnerStatusService(self._resource, interval=60),
|
|
125
|
+
RunnerCleanupService(self._resource, interval=300),
|
|
126
|
+
SlurmStatusService(self._resource, interval=60),
|
|
127
|
+
StopCheckService(
|
|
128
|
+
self._resource, interval=10, shutdown_event=self._shutdown_event
|
|
129
|
+
),
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
if self._with_file_transfer:
|
|
133
|
+
self._services.append(
|
|
134
|
+
FileTransferService(
|
|
135
|
+
self._resource,
|
|
136
|
+
interval=10,
|
|
137
|
+
max_concurrent=2,
|
|
138
|
+
shutdown_event=None,
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
if not self._no_pull:
|
|
143
|
+
self._services.append(GitUvUpdateService(self._resource, interval=60))
|
|
144
|
+
self._services.append(
|
|
145
|
+
ResourceBranchMonitorService(self._resource, interval=60)
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Add timeout restart service if timeout is specified
|
|
149
|
+
if timeout is not None:
|
|
150
|
+
self._services.append(TimeoutRestartService(self._resource, timeout))
|
|
151
|
+
|
|
152
|
+
# Start all services
|
|
153
|
+
service_tasks = [service.start() for service in self._services]
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
# Wait for either a service task to finish or the shutdown event to be set
|
|
157
|
+
shutdown_task = asyncio.create_task(self._shutdown_event.wait())
|
|
158
|
+
tasks_to_wait = [*service_tasks, shutdown_task]
|
|
159
|
+
done, pending = await asyncio.wait(
|
|
160
|
+
tasks_to_wait, return_when=asyncio.FIRST_COMPLETED
|
|
161
|
+
)
|
|
162
|
+
finally:
|
|
163
|
+
await self.stop_all_services()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from simstack.models.parameters import Resource
|
|
2
|
+
from simstack.models.runner_model import RunnerEventEnum
|
|
3
|
+
from simstack.core.services.base_service import BaseService
|
|
4
|
+
|
|
5
|
+
class RunnerStatusService(BaseService):
|
|
6
|
+
def __init__(self, resource: Resource, interval):
|
|
7
|
+
super().__init__("RunnerStatus", resource, interval)
|
|
8
|
+
|
|
9
|
+
async def execute(self):
|
|
10
|
+
await self.write_resource_event(RunnerEventEnum.ALIVE)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
from simstack.core.context import context
|
|
5
|
+
from simstack.core.definitions import TaskStatus
|
|
6
|
+
from simstack.models import NodeRegistry
|
|
7
|
+
from simstack.models.parameters import Resource
|
|
8
|
+
from simstack.models.slurm_info import SlurmInfo
|
|
9
|
+
from simstack.util.runner_utils import get_job_info, clean_slurm_info
|
|
10
|
+
from simstack.core.services.base_service import BaseService
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger("NodeRunner")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SlurmStatusService(BaseService):
|
|
16
|
+
def __init__(self, resource: Resource, interval: int) -> None:
|
|
17
|
+
super().__init__("SlurmStatus", resource, interval)
|
|
18
|
+
self._resource_name = str(resource)
|
|
19
|
+
|
|
20
|
+
async def execute(self) -> None:
|
|
21
|
+
try:
|
|
22
|
+
# logger.info(f"Running clean_slurm_info for {self._resource} and user {self._username}")
|
|
23
|
+
await clean_slurm_info(self._resource, user=self._username)
|
|
24
|
+
|
|
25
|
+
running_tasks = await context.db.find(
|
|
26
|
+
NodeRegistry,
|
|
27
|
+
(NodeRegistry.status == TaskStatus.RUNNING)
|
|
28
|
+
& (NodeRegistry.parameters.resource == self._resource),
|
|
29
|
+
)
|
|
30
|
+
queued_tasks = await context.db.find(
|
|
31
|
+
NodeRegistry,
|
|
32
|
+
(NodeRegistry.status == TaskStatus.SLURM_QUEUED)
|
|
33
|
+
& (NodeRegistry.parameters.resource == self._resource),
|
|
34
|
+
)
|
|
35
|
+
# logger.info(f"Checking Slurm status for {len(running_tasks)} running jobs on resource {self._resource}")
|
|
36
|
+
|
|
37
|
+
for task in list(running_tasks) + list(queued_tasks):
|
|
38
|
+
if task.job_id is not None:
|
|
39
|
+
slurm_info = get_job_info(
|
|
40
|
+
task.job_id, task.id, Resource(value=self._resource_name)
|
|
41
|
+
)
|
|
42
|
+
slurm_entry = await context.db.find_one(
|
|
43
|
+
SlurmInfo, SlurmInfo.job_id == task.job_id
|
|
44
|
+
)
|
|
45
|
+
if slurm_info:
|
|
46
|
+
if slurm_entry:
|
|
47
|
+
slurm_entry.code = slurm_info.code
|
|
48
|
+
slurm_entry.time = slurm_info.time
|
|
49
|
+
slurm_entry.updated = datetime.now()
|
|
50
|
+
await context.db.save(slurm_entry)
|
|
51
|
+
else:
|
|
52
|
+
await context.db.save(slurm_info)
|
|
53
|
+
else:
|
|
54
|
+
# Logic for finished/timed out jobs
|
|
55
|
+
check_job = await context.db.find_one(
|
|
56
|
+
NodeRegistry, NodeRegistry.id == task.id
|
|
57
|
+
)
|
|
58
|
+
if slurm_entry:
|
|
59
|
+
await context.db.delete(slurm_entry)
|
|
60
|
+
if check_job.status == TaskStatus.RUNNING:
|
|
61
|
+
task.job_id = None
|
|
62
|
+
task.status = TaskStatus.TIME_OUT
|
|
63
|
+
await context.db.save(task)
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
logger.exception(f"Error checking Slurm status: {e}")
|
|
67
|
+
raise e
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from simstack.core.context import context
|
|
6
|
+
from simstack.models.parameters import Resource
|
|
7
|
+
from simstack.models.runner_model import RunnerEventEnum
|
|
8
|
+
from simstack.core.services.base_service import BaseService
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger("NodeRunner")
|
|
11
|
+
|
|
12
|
+
class StopCheckService(BaseService):
|
|
13
|
+
"""Service that checks for STOP file and triggers shutdown"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, resource: Resource, interval: int, shutdown_event: asyncio.Event):
|
|
16
|
+
super().__init__("StopCheck", resource, interval, shutdown_event=shutdown_event)
|
|
17
|
+
|
|
18
|
+
async def execute(self):
|
|
19
|
+
# Check for STOP file
|
|
20
|
+
for path in [context.config.workdir, context.config.project_root]:
|
|
21
|
+
stop_file = Path(path) / "STOP"
|
|
22
|
+
if stop_file.exists():
|
|
23
|
+
await self.write_resource_event(RunnerEventEnum.SHUTDOWN, message="STOP file found")
|
|
24
|
+
logger.info(f"STOP file found at {stop_file}, signaling shutdown...")
|
|
25
|
+
if self._shutdown_event:
|
|
26
|
+
self._shutdown_event.set()
|
|
27
|
+
return
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from simstack.core.context import context
|
|
4
|
+
from simstack.models.parameters import Resource
|
|
5
|
+
from simstack.models.runner_model import RunnerEventEnum
|
|
6
|
+
from simstack.core.services.base_service import RestartService
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger("NodeRunner")
|
|
9
|
+
|
|
10
|
+
class TimeoutRestartService(RestartService):
|
|
11
|
+
"""Service that restarts the runner after a specified timeout"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, resource: Resource, timeout_minutes: int):
|
|
14
|
+
super().__init__("TimeoutRestart", resource, interval=timeout_minutes * 60)
|
|
15
|
+
self._project_dir = context.config.project_root.resolve(strict=True)
|
|
16
|
+
self._timeout_minutes = timeout_minutes
|
|
17
|
+
self._executed = False
|
|
18
|
+
|
|
19
|
+
async def execute(self):
|
|
20
|
+
# Only execute once after the timeout interval
|
|
21
|
+
if not self._executed:
|
|
22
|
+
self._executed = True
|
|
23
|
+
logger.info(f"Timeout of {self._timeout_minutes} minutes reached. Triggering restart...")
|
|
24
|
+
await self.write_resource_event(RunnerEventEnum.SHUTDOWN,
|
|
25
|
+
message=f"Timeout restart after {self._timeout_minutes} minutes")
|
|
26
|
+
await self.trigger_restart()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from typing import Optional, List
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field, ConfigDict
|
|
4
|
+
|
|
5
|
+
from simstack.core.definitions import TaskStatus
|
|
6
|
+
from simstack.models.files import FileStack
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SimstackResult(BaseModel):
|
|
10
|
+
model_config = ConfigDict(extra="allow")
|
|
11
|
+
"""
|
|
12
|
+
Represents the results from a Simstack operation.
|
|
13
|
+
|
|
14
|
+
This class serves to encapsulate the status, messages, and files resulting
|
|
15
|
+
from a Simstack operation. It provides structured attributes for the error
|
|
16
|
+
message, operation message, and categorized files, ensuring clarity in the
|
|
17
|
+
representation of these aspects. It also supports additional fields through
|
|
18
|
+
flexible configuration.
|
|
19
|
+
|
|
20
|
+
:ivar status: Indicates the status of the task, defaulting to `TaskStatus.COMPLETED`.
|
|
21
|
+
:type status: TaskStatus
|
|
22
|
+
:ivar error_message: Optionally stores an error message if the task encountered issues.
|
|
23
|
+
:type error_message: Optional[str]
|
|
24
|
+
:ivar message: An optional general message providing additional information about the task.
|
|
25
|
+
:type message: Optional[str]
|
|
26
|
+
:ivar files: A list of `FileStack` objects that represent the primary files involved in
|
|
27
|
+
the operation. Defaults to an empty list.
|
|
28
|
+
:type files: List[FileStack]
|
|
29
|
+
:ivar info_files: A list of `FileStack` objects that provide additional or informational
|
|
30
|
+
files pertaining to the operation. Defaults to an empty list. These files are shown in the results
|
|
31
|
+
but not passed to the calling function
|
|
32
|
+
:type info_files: List[FileStack]
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
status: TaskStatus = TaskStatus.RETRIEVED
|
|
36
|
+
error_message: Optional[str] = None
|
|
37
|
+
message: Optional[str] = None
|
|
38
|
+
files: List[FileStack] = Field(default_factory=list)
|
|
39
|
+
info_files: List[FileStack] = Field(default_factory=list)
|