gpustack-runtime 0.2.4.post3__tar.gz → 0.2.5.post1__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.
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/PKG-INFO +2 -2
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/_version.py +2 -2
- gpustack_runtime-0.2.5.post1/gpustack_runtime/_version_appendix.py +1 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/cdi/ascend.py +130 -12
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/docker.py +6 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/amd.py +65 -32
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/iluvatar.py +33 -12
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/nvidia.py +30 -2
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/thead.py +6 -1
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/envs.py +8 -1
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/pyproject.toml +3 -3
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/specs/2026-08-14-detector-alignment-and-workload-exit-status.md +1 -1
- gpustack_runtime-0.2.5.post1/tests/gpustack_runtime/deployer/test_ascend_ranktable.py +224 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_amd.py +147 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_iluvatar.py +86 -2
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_nvidia.py +138 -1
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_thead.py +61 -1
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/uv.lock +57 -43
- gpustack_runtime-0.2.4.post3/gpustack_runtime/_version_appendix.py +0 -1
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/.codespelldict +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/.codespellrc +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/.dockerignore +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/.gitattributes +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/.gitignore +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/.pre-commit-config.yaml +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/.python-version +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/LICENSE +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/Makefile +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/README.md +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/deploy/manifests/docker-compose.yaml +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/deploy/manifests/kubernetes.yaml +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/docs/index.md +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/docs/modules/gpustack_runtime.deployer.md +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/docs/modules/gpustack_runtime.detector.md +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/docs/modules/gpustack_runtime.md +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/__main__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/_version.pyi +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/cmds/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/cmds/__types__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/cmds/deployer.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/cmds/detector.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/cmds/images.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/__patches__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/__types__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/__utils__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/cdi/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/cdi/__types__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/cdi/__utils__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/cdi/amd.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/cdi/hygon.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/cdi/iluvatar.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/cdi/metax.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/cdi/thead.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/k8s/devicemanager/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/kuberentes.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/podman.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/__types__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/__utils__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/ascend.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/cambricon.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/hygon.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/metax.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/mthreads.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/pyamdgpu/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/pyamdsmi/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/pycndev/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/pydcmi/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/pydmi/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/pyhgml/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/pyhgml/libhgml.so +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/pyhgml/libuki.so +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/pyhsa/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/pyixml/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/pymtml/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/pymxsml/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/pynvml/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/pyrocmsmi/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/logging.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/hatch.toml +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/mkdocs.yml +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/pack/Dockerfile +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/pack/Dockerfile.dummy +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/pytest.ini +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/ruff.toml +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/fixtures/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/fixtures/test_compare_versions.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/fixtures/test_correct_runner_image.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_multiple_jsons.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_multiple_yamls.yaml +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_single_json.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_single_yaml.yaml +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/fixtures/test_nginx_entrypoint.sh +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/test_docker_status.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/test_kubernetes_status.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/test_podman_status.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/test_privileged.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/test_resource_injection_policy.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/test_runtime_class.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/test_termination_grace_period.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/test_utils.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/test_visible_devices_ordering.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/deployer/test_workload_status.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/fixtures/__init__.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/README.md +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_amd_mi300x.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_amd_mi308x.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_amd_rx7800xt.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_ascend_310p3.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_ascend_910b2.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_hygon_k100ai.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_metax_c500.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_gb10.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h100.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h100_mig.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h200.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4080super.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4090_48g.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4090d.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx5090d.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/detect_output_thead_ppu.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_amd_mi300x.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_amd_mi308x.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_amd_rx7800xt.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_ascend_310p3.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_ascend_910b2.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_hygon_k100ai.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_metax_c500.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_mthreads_s5000.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h100.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h100_mig.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h200.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4080super.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4090_48g.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4090d.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx5090d.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/samples/topology_output_thead_ppu.json +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_ascend.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_cambricon.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_detector_cli.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_detector_types.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_detector_utils.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_hygon.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_metax.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_mig_devices.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_mthreads.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_pycndev.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_pydmi.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/tests/gpustack_runtime/detector/test_samples.py +0 -0
- {gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/uv.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: gpustack-runtime
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5.post1
|
|
4
4
|
Summary: GPUStack Runtime is library for detecting GPU resources and launching GPU workloads.
|
|
5
5
|
Project-URL: Homepage, https://github.com/gpustack/runtime
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/gpustack/gpustack/issues
|
|
@@ -16,7 +16,7 @@ Requires-Python: >=3.10
|
|
|
16
16
|
Requires-Dist: argcomplete>=3.6.3
|
|
17
17
|
Requires-Dist: cachetools>=5.5.2
|
|
18
18
|
Requires-Dist: docker>=7.1.0
|
|
19
|
-
Requires-Dist: gpustack-runner>=0.1.
|
|
19
|
+
Requires-Dist: gpustack-runner>=0.1.29
|
|
20
20
|
Requires-Dist: kubernetes>=33.1.0
|
|
21
21
|
Requires-Dist: mthreads-ml-py>=2.2.10
|
|
22
22
|
Requires-Dist: nvidia-ml-py>=13.580.65
|
|
@@ -27,8 +27,8 @@ version_tuple: VERSION_TUPLE
|
|
|
27
27
|
__commit_id__: COMMIT_ID
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
|
|
30
|
-
__version__ = version = '0.2.
|
|
31
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
30
|
+
__version__ = version = '0.2.5.post1'
|
|
31
|
+
__version_tuple__ = version_tuple = (0, 2, 5, 'post1')
|
|
32
32
|
try:
|
|
33
33
|
from ._version_appendix import git_commit
|
|
34
34
|
__commit_id__ = commit_id = git_commit
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
git_commit = "55bbd00"
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations as __future_annotations__
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
import logging
|
|
5
|
+
from functools import cache
|
|
6
|
+
from pathlib import Path
|
|
4
7
|
|
|
5
8
|
from ...detector import (
|
|
6
9
|
Devices,
|
|
@@ -32,6 +35,28 @@ The CANN variant of the A5 generation, whose UB fabric replaces what the
|
|
|
32
35
|
earlier generations reach through the shared memory device.
|
|
33
36
|
"""
|
|
34
37
|
|
|
38
|
+
_HCCL_RANKTABLE_PATH = "/etc/hccl_rootinfo.json"
|
|
39
|
+
"""
|
|
40
|
+
The host ranktable ("rootinfo") file. Optional, user-maintained state: it
|
|
41
|
+
belongs to no driver package, and the vendor documents it as generated by
|
|
42
|
+
mindcluster-tools and mounted only when present, see
|
|
43
|
+
https://gitcode.com/Ascend/mind-cluster/blob/master/docs/en/scheduling/references/appendix.md.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
_A5_RANKTABLE_VERSION = "2.0"
|
|
47
|
+
"""
|
|
48
|
+
The ranktable version the A5 generation requires.
|
|
49
|
+
|
|
50
|
+
The format is bound to the chip generation -- 1.0 for A2, 1.2 for A3, 2.0 for
|
|
51
|
+
A5, see ascend-operator/pkg/ranktable/common/common.go:37-38 and
|
|
52
|
+
ranktable/v2dot0/ranktable.go:27. A5 loads libhccl_v2.so, which rejects an
|
|
53
|
+
older table outright rather than ignoring it.
|
|
54
|
+
|
|
55
|
+
Only the A5 row of that mapping has a measured failure behind it, so only A5 is
|
|
56
|
+
acted on. An A3 host carrying a 1.2 table is presumed correct rather than
|
|
57
|
+
warned about, because nothing here established otherwise.
|
|
58
|
+
"""
|
|
59
|
+
|
|
35
60
|
_A5_UB_MOUNT_PATTERNS = [
|
|
36
61
|
"/usr/lib64/libummu*",
|
|
37
62
|
"/usr/lib64/liburma*",
|
|
@@ -124,30 +149,51 @@ class AscendGenerator(Generator):
|
|
|
124
149
|
if not common_device_nodes:
|
|
125
150
|
return None
|
|
126
151
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
152
|
+
# Device.appendix defaults to None and callers pass their own devices
|
|
153
|
+
# in, so every read goes through `or {}`.
|
|
154
|
+
is_a5 = any(
|
|
155
|
+
get_ascend_cann_variant((dev.appendix or {}).get("arch_family"))
|
|
156
|
+
== _A5_CANN_VARIANT
|
|
157
|
+
for dev in devices
|
|
158
|
+
if dev
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
mount_paths = [
|
|
162
|
+
_HCCL_RANKTABLE_PATH,
|
|
130
163
|
"/usr/local/Ascend/driver/topo",
|
|
131
164
|
"/usr/local/Ascend/driver/lib64",
|
|
132
165
|
"/usr/local/Ascend/driver/include",
|
|
133
166
|
"/usr/local/dcmi",
|
|
134
167
|
"/usr/local/bin/npu-smi",
|
|
135
168
|
"/var/queue_schedule",
|
|
136
|
-
]
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
if is_a5:
|
|
172
|
+
# A5 loads libhccl_v2.so, which accepts a 2.0 ranktable only. A host
|
|
173
|
+
# file left over from an A2 fleet is 1.0, and HCCL refuses it with
|
|
174
|
+
# Config_Error_Ranktable(EI0014) rather than falling back, so every
|
|
175
|
+
# multi-card init dies. Nothing here writes the file, so the only
|
|
176
|
+
# lever is not mounting it -- verified by isolation: mounting only
|
|
177
|
+
# this file into an otherwise-working container reproduces EI0014,
|
|
178
|
+
# mounting only driver/topo does not.
|
|
179
|
+
#
|
|
180
|
+
# This is single-node evidence. A multi-node deployment does need a
|
|
181
|
+
# ranktable; when that lands it has to be a 2.0 one, not this.
|
|
182
|
+
#
|
|
183
|
+
# Only GPUStack's own mount list is covered here. Under the Env
|
|
184
|
+
# policy ascend-docker-runtime mounts the file itself, which
|
|
185
|
+
# `warn_incompatible_ranktable` reports and cannot prevent.
|
|
186
|
+
mount_paths.remove(_HCCL_RANKTABLE_PATH)
|
|
187
|
+
|
|
188
|
+
common_mounts = []
|
|
189
|
+
for p in mount_paths:
|
|
137
190
|
cm = path_to_cdi_mount(
|
|
138
191
|
path=p,
|
|
139
192
|
)
|
|
140
193
|
if cm:
|
|
141
194
|
common_mounts.append(cm)
|
|
142
195
|
|
|
143
|
-
|
|
144
|
-
# in, so every read goes through `or {}`.
|
|
145
|
-
if any(
|
|
146
|
-
get_ascend_cann_variant((dev.appendix or {}).get("arch_family"))
|
|
147
|
-
== _A5_CANN_VARIANT
|
|
148
|
-
for dev in devices
|
|
149
|
-
if dev
|
|
150
|
-
):
|
|
196
|
+
if is_a5:
|
|
151
197
|
for pattern in _A5_UB_MOUNT_PATTERNS:
|
|
152
198
|
ub_mounts = glob_to_cdi_mounts(pattern=pattern)
|
|
153
199
|
if not ub_mounts:
|
|
@@ -225,3 +271,75 @@ class AscendGenerator(Generator):
|
|
|
225
271
|
mounts=common_mounts,
|
|
226
272
|
),
|
|
227
273
|
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def read_ranktable_version(path: str = _HCCL_RANKTABLE_PATH) -> str | None:
|
|
277
|
+
"""
|
|
278
|
+
Read the ``version`` of the host ranktable file.
|
|
279
|
+
|
|
280
|
+
The path is resolved as this process sees it, exactly as the mount list
|
|
281
|
+
above resolves the paths it emits. Both therefore agree on whether the file
|
|
282
|
+
exists.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
path:
|
|
286
|
+
The ranktable path to read.
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
The declared version, or None if the file is absent, unreadable or not
|
|
290
|
+
a JSON object carrying a string version.
|
|
291
|
+
|
|
292
|
+
"""
|
|
293
|
+
try:
|
|
294
|
+
content = json.loads(Path(path).read_text(encoding="utf-8"))
|
|
295
|
+
except (OSError, ValueError):
|
|
296
|
+
return None
|
|
297
|
+
|
|
298
|
+
if not isinstance(content, dict):
|
|
299
|
+
return None
|
|
300
|
+
|
|
301
|
+
version = content.get("version")
|
|
302
|
+
return version if isinstance(version, str) else None
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
@cache
|
|
306
|
+
def warn_incompatible_ranktable() -> str | None:
|
|
307
|
+
"""
|
|
308
|
+
Warn once if this A5 host carries a ranktable A5's HCCL will reject.
|
|
309
|
+
|
|
310
|
+
ascend-docker-runtime mounts the host ranktable whenever the file is there
|
|
311
|
+
-- its `addUBMount` is a bare `os.Stat` with no version check -- so under
|
|
312
|
+
the default Env policy GPUStack cannot prevent the mount. Reporting is the
|
|
313
|
+
only available action: the operator has to remove the file or replace it
|
|
314
|
+
with a 2.0 one.
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
The warning message emitted, or None if there is nothing to warn about.
|
|
318
|
+
|
|
319
|
+
"""
|
|
320
|
+
# Ordered so a host without the file pays nothing: the file is absent on
|
|
321
|
+
# almost every non-Ascend host, and detection is the expensive half.
|
|
322
|
+
version = read_ranktable_version()
|
|
323
|
+
if version is None or version == _A5_RANKTABLE_VERSION:
|
|
324
|
+
return None
|
|
325
|
+
|
|
326
|
+
devices = detect_devices(manufacturer=ManufacturerEnum.ASCEND)
|
|
327
|
+
if not devices or not any(
|
|
328
|
+
get_ascend_cann_variant((dev.appendix or {}).get("arch_family"))
|
|
329
|
+
== _A5_CANN_VARIANT
|
|
330
|
+
for dev in devices
|
|
331
|
+
if dev
|
|
332
|
+
):
|
|
333
|
+
# An older generation's own ranktable is correct for it.
|
|
334
|
+
return None
|
|
335
|
+
|
|
336
|
+
msg = (
|
|
337
|
+
f"Host ranktable {_HCCL_RANKTABLE_PATH} declares version {version!r}, "
|
|
338
|
+
f"but this A5 (Ascend 950) host needs {_A5_RANKTABLE_VERSION!r}. "
|
|
339
|
+
f"Under the default Env policy ascend-docker-runtime mounts it on "
|
|
340
|
+
f"presence and HCCL refuses it with Config_Error_Ranktable(EI0014), "
|
|
341
|
+
f"failing every multi-card workload. Remove the file or replace it "
|
|
342
|
+
f"with a {_A5_RANKTABLE_VERSION} table."
|
|
343
|
+
)
|
|
344
|
+
logger.warning(msg)
|
|
345
|
+
return msg
|
{gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/deployer/docker.py
RENAMED
|
@@ -58,6 +58,9 @@ from .__utils__ import (
|
|
|
58
58
|
sensitive_env_var,
|
|
59
59
|
)
|
|
60
60
|
from .cdi import dump_config as cdi_dump_config
|
|
61
|
+
from .cdi.ascend import (
|
|
62
|
+
warn_incompatible_ranktable as warn_incompatible_ascend_ranktable,
|
|
63
|
+
)
|
|
61
64
|
|
|
62
65
|
if TYPE_CHECKING:
|
|
63
66
|
from collections.abc import Callable, Generator
|
|
@@ -1568,6 +1571,9 @@ class DockerDeployer(EndoscopicDeployer):
|
|
|
1568
1571
|
raise TypeError(msg)
|
|
1569
1572
|
|
|
1570
1573
|
self._prepare_mirrored_deployment()
|
|
1574
|
+
# ascend-docker-runtime mounts the host ranktable itself, so this can
|
|
1575
|
+
# only report, not prevent. Cached, so it costs one check per process.
|
|
1576
|
+
warn_incompatible_ascend_ranktable()
|
|
1571
1577
|
|
|
1572
1578
|
if isinstance(workload, WorkloadPlan):
|
|
1573
1579
|
workload = DockerWorkloadPlan(**workload.__dict__)
|
{gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/amd.py
RENAMED
|
@@ -186,28 +186,14 @@ class AMDDetector(Detector):
|
|
|
186
186
|
dev_asic_family_id = dev_gpudev_info.family_id
|
|
187
187
|
|
|
188
188
|
dev_mem = 0
|
|
189
|
-
dev_mem_status = DeviceMemoryStatusEnum.HEALTHY
|
|
190
189
|
try:
|
|
191
190
|
dev_gpu_vram_usage = pyamdsmi.amdsmi_get_gpu_vram_usage(dev)
|
|
192
191
|
dev_mem = dev_gpu_vram_usage.get("vram_total")
|
|
193
|
-
if not envs.GPUSTACK_RUNTIME_DETECT_NO_HEALTH_CHECK:
|
|
194
|
-
dev_ecc_count = pyamdsmi.amdsmi_get_gpu_ecc_count(
|
|
195
|
-
dev,
|
|
196
|
-
pyamdsmi.AmdSmiGpuBlock.UMC,
|
|
197
|
-
)
|
|
198
|
-
if dev_ecc_count.get("uncorrectable_count", 0) > 0:
|
|
199
|
-
dev_mem_status = DeviceMemoryStatusEnum.UNHEALTHY
|
|
200
192
|
except pyamdsmi.AmdSmiException:
|
|
201
193
|
dev_mem = byte_to_mebibyte( # byte to MiB
|
|
202
194
|
pyrocmsmi.rsmi_dev_memory_total_get(dev_idx),
|
|
203
195
|
)
|
|
204
|
-
|
|
205
|
-
with contextlib.suppress(pyrocmsmi.ROCMSMIError):
|
|
206
|
-
dev_ecc_count = pyrocmsmi.rsmi_dev_ecc_count_get(
|
|
207
|
-
dev_idx,
|
|
208
|
-
)
|
|
209
|
-
if dev_ecc_count.uncorrectable_err > 0:
|
|
210
|
-
dev_mem_status = DeviceMemoryStatusEnum.UNHEALTHY
|
|
196
|
+
dev_mem_status = _get_memory_status(dev, dev_idx)
|
|
211
197
|
|
|
212
198
|
# The power limit is inventory, so it stays here, while the used
|
|
213
199
|
# power the same call carries belongs to the usage query.
|
|
@@ -331,20 +317,10 @@ class AMDDetector(Detector):
|
|
|
331
317
|
|
|
332
318
|
dev_mem = 0
|
|
333
319
|
dev_mem_used = 0
|
|
334
|
-
# Health is reported by both queries, as the operator reports it
|
|
335
|
-
# from DetectAccelerator and MonitorAccelerator alike.
|
|
336
|
-
dev_mem_status = DeviceMemoryStatusEnum.HEALTHY
|
|
337
320
|
try:
|
|
338
321
|
dev_gpu_vram_usage = pyamdsmi.amdsmi_get_gpu_vram_usage(dev)
|
|
339
322
|
dev_mem = dev_gpu_vram_usage.get("vram_total")
|
|
340
323
|
dev_mem_used = dev_gpu_vram_usage.get("vram_used")
|
|
341
|
-
if not envs.GPUSTACK_RUNTIME_DETECT_NO_HEALTH_CHECK:
|
|
342
|
-
dev_ecc_count = pyamdsmi.amdsmi_get_gpu_ecc_count(
|
|
343
|
-
dev,
|
|
344
|
-
pyamdsmi.AmdSmiGpuBlock.UMC,
|
|
345
|
-
)
|
|
346
|
-
if dev_ecc_count.get("uncorrectable_count", 0) > 0:
|
|
347
|
-
dev_mem_status = DeviceMemoryStatusEnum.UNHEALTHY
|
|
348
324
|
except pyamdsmi.AmdSmiException:
|
|
349
325
|
dev_mem = byte_to_mebibyte( # byte to MiB
|
|
350
326
|
pyrocmsmi.rsmi_dev_memory_total_get(dev_idx),
|
|
@@ -352,13 +328,9 @@ class AMDDetector(Detector):
|
|
|
352
328
|
dev_mem_used = byte_to_mebibyte( # byte to MiB
|
|
353
329
|
pyrocmsmi.rsmi_dev_memory_usage_get(dev_idx),
|
|
354
330
|
)
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
dev_idx,
|
|
359
|
-
)
|
|
360
|
-
if dev_ecc_count.uncorrectable_err > 0:
|
|
361
|
-
dev_mem_status = DeviceMemoryStatusEnum.UNHEALTHY
|
|
331
|
+
# Health is reported by both queries, as the operator reports it
|
|
332
|
+
# from DetectAccelerator and MonitorAccelerator alike.
|
|
333
|
+
dev_mem_status = _get_memory_status(dev, dev_idx)
|
|
362
334
|
|
|
363
335
|
# A sentinel reading means the same as a failed call -- AMD SMI
|
|
364
336
|
# cannot tell the used power -- so both route to ROCm SMI.
|
|
@@ -523,6 +495,67 @@ class AMDDetector(Detector):
|
|
|
523
495
|
return ret
|
|
524
496
|
|
|
525
497
|
|
|
498
|
+
def _get_memory_status(dev, dev_idx: int) -> DeviceMemoryStatusEnum:
|
|
499
|
+
"""
|
|
500
|
+
Get a device's memory health from its uncorrected ECC error count.
|
|
501
|
+
|
|
502
|
+
Both queries produce it, mirroring the operator, which reports `Unhealthy`
|
|
503
|
+
from `DetectAccelerator` and `MonitorAccelerator` alike.
|
|
504
|
+
|
|
505
|
+
Args:
|
|
506
|
+
dev:
|
|
507
|
+
The AMD SMI device handle.
|
|
508
|
+
dev_idx:
|
|
509
|
+
The device index, for the ROCm SMI fallback.
|
|
510
|
+
|
|
511
|
+
Returns:
|
|
512
|
+
The memory status.
|
|
513
|
+
|
|
514
|
+
"""
|
|
515
|
+
if envs.GPUSTACK_RUNTIME_DETECT_NO_HEALTH_CHECK:
|
|
516
|
+
return DeviceMemoryStatusEnum.HEALTHY
|
|
517
|
+
|
|
518
|
+
try:
|
|
519
|
+
dev_ecc_count = pyamdsmi.amdsmi_get_gpu_ecc_count(
|
|
520
|
+
dev,
|
|
521
|
+
pyamdsmi.AmdSmiGpuBlock.UMC,
|
|
522
|
+
)
|
|
523
|
+
except pyamdsmi.AmdSmiException as e:
|
|
524
|
+
# Fail closed: a genuine driver error marks the device unhealthy, while
|
|
525
|
+
# a card without the counter cannot be judged. The stub binding raises
|
|
526
|
+
# a codeless error when amdsmi is absent, in which case ROCm SMI is
|
|
527
|
+
# the fallback, as it is for the memory read.
|
|
528
|
+
err_code = getattr(e, "err_code", None)
|
|
529
|
+
if err_code is not None:
|
|
530
|
+
if err_code != getattr(pyamdsmi, "AMDSMI_STATUS_NOT_SUPPORTED", None):
|
|
531
|
+
return DeviceMemoryStatusEnum.UNHEALTHY
|
|
532
|
+
return DeviceMemoryStatusEnum.HEALTHY
|
|
533
|
+
else:
|
|
534
|
+
if dev_ecc_count.get("uncorrectable_count", 0) > 0:
|
|
535
|
+
return DeviceMemoryStatusEnum.UNHEALTHY
|
|
536
|
+
return DeviceMemoryStatusEnum.HEALTHY
|
|
537
|
+
|
|
538
|
+
try:
|
|
539
|
+
dev_ecc_count = pyrocmsmi.rsmi_dev_ecc_count_get(dev_idx)
|
|
540
|
+
if dev_ecc_count.uncorrectable_err > 0:
|
|
541
|
+
return DeviceMemoryStatusEnum.UNHEALTHY
|
|
542
|
+
except pyrocmsmi.ROCMSMIError as e:
|
|
543
|
+
# rsmi_status_t comes from the ROCm-installed rsmiBindings, so it is
|
|
544
|
+
# looked up late: without ROCm the module has no such attribute.
|
|
545
|
+
rsmi_not_supported = getattr(
|
|
546
|
+
getattr(pyrocmsmi, "rsmi_status_t", None),
|
|
547
|
+
"RSMI_STATUS_NOT_SUPPORTED",
|
|
548
|
+
None,
|
|
549
|
+
)
|
|
550
|
+
if e.value not in (
|
|
551
|
+
pyrocmsmi.ROCMSMI_ERROR_FUNCTION_NOT_FOUND,
|
|
552
|
+
rsmi_not_supported,
|
|
553
|
+
):
|
|
554
|
+
return DeviceMemoryStatusEnum.UNHEALTHY
|
|
555
|
+
|
|
556
|
+
return DeviceMemoryStatusEnum.HEALTHY
|
|
557
|
+
|
|
558
|
+
|
|
526
559
|
def _get_reading(dev_info: dict, key: str, default: Any = None) -> Any:
|
|
527
560
|
"""
|
|
528
561
|
Read one value out of an AMD SMI answer, treating its sentinel as absent.
|
{gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/iluvatar.py
RENAMED
|
@@ -141,7 +141,6 @@ class IluvatarDetector(Detector):
|
|
|
141
141
|
dev_cores = pyixml.nvmlDeviceGetNumGpuCores(dev)
|
|
142
142
|
|
|
143
143
|
dev_mem = 0
|
|
144
|
-
dev_mem_status = DeviceMemoryStatusEnum.HEALTHY
|
|
145
144
|
with contextlib.suppress(pyixml.NVMLError):
|
|
146
145
|
# Prefer the v2 memory structure, falling back to v1 --
|
|
147
146
|
# mirrors the operator's GetMemoryInfoV, which drops the
|
|
@@ -150,11 +149,7 @@ class IluvatarDetector(Detector):
|
|
|
150
149
|
dev_mem = byte_to_mebibyte( # byte to MiB
|
|
151
150
|
dev_mem_info.total,
|
|
152
151
|
)
|
|
153
|
-
|
|
154
|
-
with contextlib.suppress(pyixml.NVMLError):
|
|
155
|
-
dev_health = pyixml.ixmlDeviceGetHealth(dev)
|
|
156
|
-
if dev_health != pyixml.IXML_HEALTH_OK:
|
|
157
|
-
dev_mem_status = DeviceMemoryStatusEnum.UNHEALTHY
|
|
152
|
+
dev_mem_status = _get_memory_status(dev)
|
|
158
153
|
|
|
159
154
|
dev_power = None
|
|
160
155
|
with contextlib.suppress(pyixml.NVMLError):
|
|
@@ -251,7 +246,6 @@ class IluvatarDetector(Detector):
|
|
|
251
246
|
|
|
252
247
|
dev_mem = 0
|
|
253
248
|
dev_mem_used = 0
|
|
254
|
-
dev_mem_status = DeviceMemoryStatusEnum.HEALTHY
|
|
255
249
|
with contextlib.suppress(pyixml.NVMLError):
|
|
256
250
|
# Same v2-then-v1 fallback as detect_info: the operator's
|
|
257
251
|
# MonitorAccelerator re-reads the memory info rather than
|
|
@@ -263,11 +257,7 @@ class IluvatarDetector(Detector):
|
|
|
263
257
|
dev_mem_used = byte_to_mebibyte( # byte to MiB
|
|
264
258
|
dev_mem_info.used,
|
|
265
259
|
)
|
|
266
|
-
|
|
267
|
-
with contextlib.suppress(pyixml.NVMLError):
|
|
268
|
-
dev_health = pyixml.ixmlDeviceGetHealth(dev)
|
|
269
|
-
if dev_health != pyixml.IXML_HEALTH_OK:
|
|
270
|
-
dev_mem_status = DeviceMemoryStatusEnum.UNHEALTHY
|
|
260
|
+
dev_mem_status = _get_memory_status(dev)
|
|
271
261
|
|
|
272
262
|
dev_cores_util = None
|
|
273
263
|
with contextlib.suppress(pyixml.NVMLError):
|
|
@@ -405,3 +395,34 @@ def _get_memory_info(dev):
|
|
|
405
395
|
return pyixml.nvmlDeviceGetMemoryInfo(dev, version=pyixml.nvmlMemory_v2)
|
|
406
396
|
except pyixml.NVMLError:
|
|
407
397
|
return pyixml.nvmlDeviceGetMemoryInfo(dev)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def _get_memory_status(dev) -> DeviceMemoryStatusEnum:
|
|
401
|
+
"""
|
|
402
|
+
Get a device's health from its dedicated health query.
|
|
403
|
+
|
|
404
|
+
Both queries produce it, mirroring the operator, which reports `Unhealthy`
|
|
405
|
+
from `DetectAccelerator` and `MonitorAccelerator` alike.
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
dev:
|
|
409
|
+
The device handle.
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
The memory status.
|
|
413
|
+
|
|
414
|
+
"""
|
|
415
|
+
if envs.GPUSTACK_RUNTIME_DETECT_NO_HEALTH_CHECK:
|
|
416
|
+
return DeviceMemoryStatusEnum.HEALTHY
|
|
417
|
+
|
|
418
|
+
try:
|
|
419
|
+
dev_health = pyixml.ixmlDeviceGetHealth(dev)
|
|
420
|
+
if dev_health != pyixml.IXML_HEALTH_OK:
|
|
421
|
+
return DeviceMemoryStatusEnum.UNHEALTHY
|
|
422
|
+
except pyixml.NVMLError as e:
|
|
423
|
+
# Fail closed: a query the driver errors on marks the device
|
|
424
|
+
# unhealthy, while an unsupported query means it cannot be judged.
|
|
425
|
+
if e.value != pyixml.NVML_ERROR_NOT_SUPPORTED:
|
|
426
|
+
return DeviceMemoryStatusEnum.UNHEALTHY
|
|
427
|
+
|
|
428
|
+
return DeviceMemoryStatusEnum.HEALTHY
|
{gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/nvidia.py
RENAMED
|
@@ -529,7 +529,12 @@ def _get_memory_status(
|
|
|
529
529
|
memory_location: int,
|
|
530
530
|
) -> DeviceMemoryStatusEnum:
|
|
531
531
|
"""
|
|
532
|
-
Get a device's memory health
|
|
532
|
+
Get a device's memory health.
|
|
533
|
+
|
|
534
|
+
The verdict is the uncorrected ECC error counter plus the driver's
|
|
535
|
+
recovery state: a GSP failure (Xid 119/154) leaves the ECC counters
|
|
536
|
+
readable at zero, so the recovery action and reset status fields are
|
|
537
|
+
probed as well.
|
|
533
538
|
|
|
534
539
|
Both queries produce it, mirroring the operator, which reports `Unhealthy`
|
|
535
540
|
from `DetectAccelerator` and `MonitorAccelerator` alike.
|
|
@@ -549,7 +554,7 @@ def _get_memory_status(
|
|
|
549
554
|
if envs.GPUSTACK_RUNTIME_DETECT_NO_HEALTH_CHECK:
|
|
550
555
|
return DeviceMemoryStatusEnum.HEALTHY
|
|
551
556
|
|
|
552
|
-
|
|
557
|
+
try:
|
|
553
558
|
dev_mem_ecc_errors = pynvml.nvmlDeviceGetMemoryErrorCounter(
|
|
554
559
|
dev,
|
|
555
560
|
pynvml.NVML_MEMORY_ERROR_TYPE_UNCORRECTED,
|
|
@@ -558,6 +563,29 @@ def _get_memory_status(
|
|
|
558
563
|
)
|
|
559
564
|
if dev_mem_ecc_errors > 0:
|
|
560
565
|
return DeviceMemoryStatusEnum.UNHEALTHY
|
|
566
|
+
except pynvml.NVMLError as e:
|
|
567
|
+
# Fail closed: a query the driver errors on (a wedged GSP answers
|
|
568
|
+
# NVML_ERROR_UNKNOWN after its RPC timeout) marks the card unhealthy,
|
|
569
|
+
# while an unsupported counter means the card cannot be judged.
|
|
570
|
+
if e.value != pynvml.NVML_ERROR_NOT_SUPPORTED:
|
|
571
|
+
return DeviceMemoryStatusEnum.UNHEALTHY
|
|
572
|
+
|
|
573
|
+
try:
|
|
574
|
+
dev_fields = pynvml.nvmlDeviceGetFieldValues(
|
|
575
|
+
dev,
|
|
576
|
+
fieldIds=[
|
|
577
|
+
pynvml.NVML_FI_DEV_GET_GPU_RECOVERY_ACTION,
|
|
578
|
+
pynvml.NVML_FI_DEV_RESET_STATUS,
|
|
579
|
+
],
|
|
580
|
+
)
|
|
581
|
+
except pynvml.NVMLError as e:
|
|
582
|
+
if e.value != pynvml.NVML_ERROR_NOT_SUPPORTED:
|
|
583
|
+
return DeviceMemoryStatusEnum.UNHEALTHY
|
|
584
|
+
return DeviceMemoryStatusEnum.HEALTHY
|
|
585
|
+
for dev_field in dev_fields:
|
|
586
|
+
# A per-field error extracts to None: cannot judge, keep the verdict.
|
|
587
|
+
if _extract_field_value(dev_field):
|
|
588
|
+
return DeviceMemoryStatusEnum.UNHEALTHY
|
|
561
589
|
|
|
562
590
|
return DeviceMemoryStatusEnum.HEALTHY
|
|
563
591
|
|
{gpustack_runtime-0.2.4.post3 → gpustack_runtime-0.2.5.post1}/gpustack_runtime/detector/thead.py
RENAMED
|
@@ -508,7 +508,7 @@ def _get_memory_status(
|
|
|
508
508
|
if envs.GPUSTACK_RUNTIME_DETECT_NO_HEALTH_CHECK:
|
|
509
509
|
return DeviceMemoryStatusEnum.HEALTHY
|
|
510
510
|
|
|
511
|
-
|
|
511
|
+
try:
|
|
512
512
|
dev_mem_ecc_errors = pyhgml.hgmlDeviceGetMemoryErrorCounter(
|
|
513
513
|
dev,
|
|
514
514
|
pyhgml.HGML_MEMORY_ERROR_TYPE_UNCORRECTED,
|
|
@@ -517,6 +517,11 @@ def _get_memory_status(
|
|
|
517
517
|
)
|
|
518
518
|
if dev_mem_ecc_errors > 0:
|
|
519
519
|
return DeviceMemoryStatusEnum.UNHEALTHY
|
|
520
|
+
except pyhgml.HGMLError as e:
|
|
521
|
+
# Fail closed: a query the driver errors on marks the device
|
|
522
|
+
# unhealthy, while an unsupported counter means it cannot be judged.
|
|
523
|
+
if e.value != pyhgml.HGML_ERROR_NOT_SUPPORTED:
|
|
524
|
+
return DeviceMemoryStatusEnum.UNHEALTHY
|
|
520
525
|
|
|
521
526
|
return DeviceMemoryStatusEnum.HEALTHY
|
|
522
527
|
|
|
@@ -46,8 +46,15 @@ if TYPE_CHECKING:
|
|
|
46
46
|
"""
|
|
47
47
|
GPUSTACK_RUNTIME_DETECT_NO_HEALTH_CHECK: bool = True
|
|
48
48
|
"""
|
|
49
|
-
Set true to disable
|
|
49
|
+
Set true to disable the health check during detection,
|
|
50
50
|
which is used to determine the health status of the device.
|
|
51
|
+
|
|
52
|
+
When enabled (set false), the check reads the uncorrected ECC error
|
|
53
|
+
counter and, on NVIDIA, the driver's recovery state (GPU reset required,
|
|
54
|
+
e.g. after a GSP failure); a query the driver errors on reports the device
|
|
55
|
+
unhealthy. Each query is a driver call per device, and against a wedged
|
|
56
|
+
device it can block until the driver's RPC timeout (up to 45s with GSP
|
|
57
|
+
firmware).
|
|
51
58
|
"""
|
|
52
59
|
GPUSTACK_RUNTIME_DETECT_BACKEND_MAP_RESOURCE_KEY: dict[str, str] | None = None
|
|
53
60
|
"""
|
|
@@ -23,7 +23,7 @@ dependencies = [
|
|
|
23
23
|
# NB(thxCode): Pin to this Podman version for a custom patch. See gpustack_runtime/deployer/__patches__.py.
|
|
24
24
|
"podman==5.6.0",
|
|
25
25
|
"pyyaml",
|
|
26
|
-
"gpustack-runner>=0.1.
|
|
26
|
+
"gpustack-runner>=0.1.29",
|
|
27
27
|
"nvidia-ml-py>=13.580.65",
|
|
28
28
|
"mthreads-ml-py>=2.2.10",
|
|
29
29
|
"argcomplete>=3.6.3",
|
|
@@ -43,13 +43,13 @@ gpustack-runtime = "gpustack_runtime.__main__:main"
|
|
|
43
43
|
dev=[
|
|
44
44
|
"pyclean==3.4.0",
|
|
45
45
|
"pre-commit==4.5.0",
|
|
46
|
-
"pytest
|
|
46
|
+
"pytest>=9.0.3,<10",
|
|
47
47
|
"hatchling",
|
|
48
48
|
"hatch-vcs",
|
|
49
49
|
"mkdocs>=1.6.1",
|
|
50
50
|
"mkdocstrings-python>=1.18.0",
|
|
51
51
|
"mkdocs-material>=9.6.18",
|
|
52
|
-
"pymdown-extensions>=
|
|
52
|
+
"pymdown-extensions>=11.0.1",
|
|
53
53
|
"dockerpty>=0.4.1",
|
|
54
54
|
"amdsmi>=7.0.2",
|
|
55
55
|
]
|
|
@@ -128,7 +128,7 @@ covered by F3). Known gaps, each to be closed or documented as a deliberate dive
|
|
|
128
128
|
| Cambricon | Not a binding at all: a `cnmon info -e -m -u -j` shell-out parsing `cnmon_info.json`, with a `TODO(thxCode)` where the sample output should be. No driver version, no Neuware version, no cores, no power, no BDF, no NUMA, no health/ECC, no PCIe bus id. | New hand-written `pycndev` ctypes binding (following `pydcmi`/`pymxsml`) and a rewritten `cambricon.py` calling `GetDeviceCount`, `GetDeviceHandleByIndex`, `GetUUID`, `GetPCIeInfoV`, `GetCardName`, `GetMemoryInfoV`, `GetCardHealthStateV`, `GetVersionInfo`, `GetUtilizationInfo`, `GetTemperatureInfo`, `GetPowerInfo`, plus the Neuware version from `/usr/local/neuware/version.txt`. `cndev.h` in the operator's `binding/cndev` is the header of record. Four decisions taken while building `T3`, all recorded rather than assumed: **cores and the power limit stay unreported** — `pycndev` binds no core-count entry point and the operator reads neither for this vendor, so closing those two needs a binding addition of its own; **`driver_version` carries `major.minor.build`** where the operator formats only `major.minor`, since it is free precision from the same query and every other vendor here reports the driver's full string; **health comes from `cndevGetCardHealthStateV2().health` alone, with no ECC read**, because that is literally what the operator does (`memoryUnhealthy = healthInfo.Health == 0`); and **memory needs no unit conversion** — the header says MB but the operator, `cnmon` and this repo's Ascend detector all treat the value as MiB, so converting would under-report ~4.8 % against the operator on the same host, which is the very discrepancy Story 1 exists to remove. `C2` confirms the memory figure against `cnmon`. |
|
|
129
129
|
| Cambricon | Per-device failure policy diverges from every sibling **on purpose**: `T3` follows the operator and `continue`s past a card whose required reads fail, where the other eight detectors let the error propagate. | A single faulty card therefore costs one device on Cambricon and **all** devices on the other eight. Graceful degradation is very likely the right behaviour everywhere and the other eight should move toward it, but that is a repo-wide behaviour change deserving its own task — not something to slip in per vendor. |
|
|
130
130
|
| THead | Enumerates MIG-style GPU/compute instances, which the operator does not. | Keep — this is the appendix mechanism F2 preserves. |
|
|
131
|
-
| **All vendors** | `GPUSTACK_RUNTIME_DETECT_NO_HEALTH_CHECK` **defaults to true**, so a default run never reads an ECC counter and every `memory_status` comes back `healthy`. The operator reads the uncorrected-ECC counter unconditionally. | Keep the default (the check costs a driver call per card per pass), but record it as a deliberate divergence: on the same host the runtime can report `healthy` where the operator reports `Unhealthy`, which is a code-path difference of exactly the kind `Story 1` exists to eliminate. `C2` must therefore compare health with the flag switched **off**, or it compares nothing. Found while building `T5`. |
|
|
131
|
+
| **All vendors** | `GPUSTACK_RUNTIME_DETECT_NO_HEALTH_CHECK` **defaults to true**, so a default run never reads an ECC counter and every `memory_status` comes back `healthy`. The operator reads the uncorrected-ECC counter unconditionally. | Keep the default (the check costs a driver call per card per pass), but record it as a deliberate divergence: on the same host the runtime can report `healthy` where the operator reports `Unhealthy`, which is a code-path difference of exactly the kind `Story 1` exists to eliminate. `C2` must therefore compare health with the flag switched **off**, or it compares nothing. Found while building `T5`. With the flag off, the NVIDIA check additionally probes the driver's recovery state (`NVML_FI_DEV_GET_GPU_RECOVERY_ACTION` / `NVML_FI_DEV_RESET_STATUS`, i.e. the Xid 154 GPU-reset-required state) and treats an errored query as `unhealthy` rather than swallowing it; against a wedged card such a query can block until the driver's GSP RPC timeout (up to 45s). |
|
|
132
132
|
|
|
133
133
|
Acceptance criteria:
|
|
134
134
|
- ~~A written parity table lives in the repo (module docstring or `docs/`) listing, per vendor, the
|