-
Notifications
You must be signed in to change notification settings - Fork 2
feat(roles): restore NVIDIA vGPU host state across reboots #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -211,6 +211,155 @@ jobs: | |
| - name: Test host LVM global_filter rendering and effectiveness | ||
| run: ansible-playbook tests/test-lvm-global-filter.yml | ||
|
|
||
| nvidia-vgpu-host: | ||
| name: NVIDIA vGPU host restore unit | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v7 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🛡️ Analyzed with Security Review | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: sed -n '1,280p' .github/workflows/test.ymlRepository: cozystack/ansible-cozystack Length of output: 9142 🏁 Script executed: printf '%s\n' '--- permission and trigger declarations ---'
rg -n -C 2 '^(on:| (push|pull_request|pull_request_target|workflow_call):|permissions:|[[:space:]]+permissions:)' .github/workflows/test.yml
printf '%s\n' '--- remaining workflow ---'
sed -n '280,380p' .github/workflows/test.ymlRepository: cozystack/ansible-cozystack Length of output: 4442 🌐 Web query:
💡 Result: In the GitHub Actions Citations:
Sensitive Data Exposure Reachability: External Restrict checkout credentials in pull-request jobs. This workflow executes pull-request content after checkout. Disable credential persistence on each checkout and set an explicit read-only token scope. permissions:
contents: read
# On each checkout step:
with:
persist-credentials: false🧰 Tools🪛 zizmor (1.29.0)[warning] 218-219: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false (artipacked) [warning] 2-517: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block (excessive-permissions) [warning] 214-361: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block (excessive-permissions) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v7.0.0 | ||
| with: | ||
| python-version: "3.14" | ||
|
|
||
| - name: Install Ansible | ||
| run: pip install ansible-core | ||
|
|
||
| - name: Build and install collection | ||
| run: | | ||
| ansible-galaxy collection build | ||
| ansible-galaxy collection install cozystack-installer-*.tar.gz --force | ||
|
|
||
| # The runner has no NVIDIA GPU, which is the point: this asserts | ||
| # the artifacts render correctly, the unit is skipped by its | ||
| # condition rather than failing, and every path the script can | ||
| # take on hardware it is not meant for exits the way it should. | ||
| # shellcheck and systemd-analyze both run inside the playbook. | ||
| - name: Test the vGPU host restore unit and its no-op paths | ||
| run: >- | ||
| sudo env "PATH=$PATH" "HOME=$HOME" ansible-playbook | ||
| tests/test-nvidia-vgpu-host.yml | ||
|
|
||
| - name: Test re-application (second run) | ||
| run: >- | ||
| sudo env "PATH=$PATH" "HOME=$HOME" ansible-playbook | ||
| tests/test-nvidia-vgpu-host.yml | ||
|
|
||
| # The refusal paths above are everything the script does without a | ||
| # GPU. This runs the stages themselves against a fake nvidia-smi, | ||
| # a fake sriov-manage and a fake PCI tree, so the code that writes | ||
| # to hardware is executed rather than only rendered. | ||
| - name: Test the restore stages against a fake GPU | ||
| run: >- | ||
| sudo env "PATH=$PATH" "HOME=$HOME" ansible-playbook | ||
| tests/test-nvidia-vgpu-host-stages.yml | ||
|
|
||
| # Two spellings of one card, or of one virtual function, collapse to | ||
| # a single key at render time and one of the two profiles is | ||
| # silently discarded. The role refuses rather than pick a winner. | ||
| - name: Test that duplicate device addresses are rejected | ||
| run: | | ||
| set +e | ||
| output="$(sudo env "PATH=$PATH" "HOME=$HOME" ansible-playbook \ | ||
| tests/test-nvidia-vgpu-host-idempotency.yml \ | ||
| --extra-vars '{"cozystack_nvidia_vgpu_devices":[{"address":"0000:41:00.0","sriov":true},{"address":"41:00.0","sriov":true}]}' 2>&1)" | ||
| status=$? | ||
| set -e | ||
|
|
||
| if [ "$status" -eq 0 ]; then | ||
| echo "ERROR: two spellings of one card were accepted" | ||
| exit 1 | ||
| fi | ||
|
|
||
| if ! grep -q "names the same GPU more than" <<< "$output"; then | ||
| echo "ERROR: rejected, but not by the duplicate-address check" | ||
| echo "$output" | tail -30 | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "OK: duplicate device addresses correctly rejected" | ||
|
|
||
| # A card and a virtual function are different levels and had | ||
| # different rules; the VF check deleted the PCI domain, so two | ||
| # functions on different segments looked like one. | ||
| - name: Test that duplicate virtual-function addresses are rejected | ||
| run: | | ||
| set +e | ||
| output="$(sudo env "PATH=$PATH" "HOME=$HOME" ansible-playbook \ | ||
| tests/test-nvidia-vgpu-host-idempotency.yml \ | ||
| --extra-vars '{"cozystack_nvidia_vgpu_devices":[{"address":"0000:41:00.0","vgpu_profiles":{"0000:41:00.5":1155,"41:00.5":1160}}]}' 2>&1)" | ||
| status=$? | ||
| set -e | ||
|
|
||
| if [ "$status" -eq 0 ]; then | ||
| echo "ERROR: two spellings of one virtual function were accepted" | ||
| exit 1 | ||
| fi | ||
|
|
||
| if ! grep -q "names the same virtual function more than" <<< "$output"; then | ||
| echo "ERROR: rejected, but not by the duplicate-VF check" | ||
| echo "$output" | tail -30 | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "OK: duplicate virtual-function addresses correctly rejected" | ||
|
|
||
| # The other direction, which is the one that regressed: cards and | ||
| # functions on different PCI segments are distinct and must be | ||
| # accepted, not refused as duplicates. | ||
| - name: Test that a multi-segment configuration is accepted | ||
| run: | | ||
| set -euo pipefail | ||
| devices='{"cozystack_nvidia_vgpu_devices":[ | ||
| {"address":"0000:41:00.0","vgpu_profiles":{"0000:41:00.5":1155}}, | ||
| {"address":"0001:41:00.0","vgpu_profiles":{"0001:41:00.5":1160}}]}' | ||
| sudo env "PATH=$PATH" "HOME=$HOME" ansible-playbook \ | ||
| tests/test-nvidia-vgpu-host-idempotency.yml --extra-vars "$devices" | ||
| echo "OK: cards on different PCI segments accepted" | ||
|
|
||
| # Writing 0 to a function's current_vgpu_type does not set a | ||
| # profile, so a profile id of 0 is a configuration error rather | ||
| # than a value to pass through. | ||
| - name: Test that a zero profile id is rejected | ||
| run: | | ||
| set +e | ||
| for v in '{"cozystack_nvidia_vgpu_devices":[{"address":"0000:41:00.0","vgpu_profile":0}]}' \ | ||
| '{"cozystack_nvidia_vgpu_devices":[{"address":"0000:41:00.0","vgpu_profiles":{"0000:41:00.5":0}}]}' \ | ||
| '{"cozystack_nvidia_vgpu_devices":[{"address":"0000:41:00.0","mig":false}]}'; do | ||
| output="$(sudo env "PATH=$PATH" "HOME=$HOME" ansible-playbook \ | ||
| tests/test-nvidia-vgpu-host-idempotency.yml --extra-vars "$v" 2>&1)" | ||
| status=$? | ||
| if [ "$status" -eq 0 ]; then | ||
| echo "ERROR: accepted $v" | ||
| exit 1 | ||
| fi | ||
| if ! grep -qE "Invalid entry in|Invalid per-VF" <<< "$output"; then | ||
| echo "ERROR: rejected for the wrong reason: $v" | ||
| echo "$output" | tail -20 | ||
| exit 1 | ||
| fi | ||
| done | ||
| set -e | ||
|
Comment on lines
+326
to
+343
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Check the exit status directly to clear the actionlint error. actionlint reports SC2181 as an error on this step, so the lint job fails. Test the command in the ♻️ Proposed fix- set +e
for v in '{"cozystack_nvidia_vgpu_devices":[{"address":"0000:41:00.0","vgpu_profile":0}]}' \
'{"cozystack_nvidia_vgpu_devices":[{"address":"0000:41:00.0","vgpu_profiles":{"0000:41:00.5":0}}]}' \
'{"cozystack_nvidia_vgpu_devices":[{"address":"0000:41:00.0","mig":false}]}'; do
- output="$(sudo env "PATH=$PATH" "HOME=$HOME" ansible-playbook \
- tests/test-nvidia-vgpu-host-idempotency.yml --extra-vars "$v" 2>&1)"
- if [ $? -eq 0 ]; then
+ if output="$(sudo env "PATH=$PATH" "HOME=$HOME" ansible-playbook \
+ tests/test-nvidia-vgpu-host-idempotency.yml --extra-vars "$v" 2>&1)"; then
echo "ERROR: accepted $v"
exit 1
fi
if ! grep -qE "Invalid entry in|Invalid per-VF" <<< "$output"; then
echo "ERROR: rejected for the wrong reason: $v"
echo "$output" | tail -20
exit 1
fi
done
- set -e
echo "OK: zero profile ids and entries that ask for nothing are rejected"🧰 Tools🪛 zizmor (1.29.0)[warning] 2-516: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block (excessive-permissions) [warning] 214-360: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block (excessive-permissions) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| echo "OK: zero profile ids and entries that ask for nothing are rejected" | ||
|
|
||
| - name: Apply the role on its own | ||
| run: >- | ||
| sudo env "PATH=$PATH" "HOME=$HOME" ansible-playbook | ||
| tests/test-nvidia-vgpu-host-idempotency.yml | ||
|
|
||
| - name: Test idempotency (second run reports no change) | ||
| run: | | ||
| set -euo pipefail | ||
| output="$(sudo env "PATH=$PATH" "HOME=$HOME" ansible-playbook \ | ||
| tests/test-nvidia-vgpu-host-idempotency.yml)" | ||
| echo "$output" | ||
| if ! grep -q "changed=0" <<< "$output"; then | ||
| echo "ERROR: re-applying the role reported changes" | ||
| exit 1 | ||
| fi | ||
| echo "OK: role is idempotent" | ||
|
|
||
| e2e: | ||
| name: E2E | ||
| runs-on: ubuntu-latest | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🛡️ Analyzed with Security Review | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: cozystack/ansible-cozystack
Length of output: 10939
🤖 get_repo_knowledge executed:
get_repo_knowledge cozystack/ansible-cozystack /tmp/coderabbit-repo-knowledge/cozystack-ansible-cozystack-db272816/conventions /tmp/coderabbit-repo-knowledge/cozystack-ansible-cozystack-db272816/learningsLength of output: 9098
🏁 Script executed:
Repository: cozystack/ansible-cozystack
Length of output: 10837
🤖 get_repo_knowledge executed:
get_repo_knowledge cozystack/ansible-cozystack /tmp/coderabbit-repo-knowledge/cozystack-ansible-cozystack-db272816/conventions /tmp/coderabbit-repo-knowledge/cozystack-ansible-cozystack-db272816/learningsLength of output: 5686
🏁 Script executed:
Repository: cozystack/ansible-cozystack
Length of output: 10308
Security Misconfiguration
Reachability: External
CWE: CWE-250
Restrict the
GITHUB_TOKENpermissions.This workflow executes checked-out pull-request code without a
permissionsblock. Addpermissions: contents: readat the workflow or job level. Keep broader scopes only for steps that require them.🧰 Tools
🪛 zizmor (1.29.0)
[warning] 2-517: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
[warning] 214-361: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
🤖 Prompt for AI Agents
Source: Linters/SAST tools