Make deb-installer for PVEMods (#168)
* first deb-installer workflow for PVE-mods --------- Co-authored-by: Meliox <na> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
afe7e8643c
commit
73b9cece16
84
.github/workflows/pr-create.yml
vendored
Normal file
84
.github/workflows/pr-create.yml
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
name: Open / Update Version Bump PR
|
||||||
|
|
||||||
|
# Triggered by any push to main where src/ files changed.
|
||||||
|
# This happens when a feature PR is merged to main.
|
||||||
|
# Creates a dedicated chore/version-bump branch and opens a PR from it.
|
||||||
|
# When creating a new PR it assigns the default "bump:patch" label.
|
||||||
|
# When updating an existing PR the label is NOT changed, preserving any
|
||||||
|
# manual label change (e.g. bump:minor, bump:major) made by a reviewer.
|
||||||
|
# The bot guard prevents this from firing on changelog commits.
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'src/**'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pr:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.actor != 'github-actions[bot]'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Collect src/ commits from merge
|
||||||
|
id: commits
|
||||||
|
run: |
|
||||||
|
LOG=$(git log "HEAD~1..HEAD" --oneline -- src/ | head -50)
|
||||||
|
EOF=$(openssl rand -hex 16)
|
||||||
|
echo "log<<$EOF" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "$LOG" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "$EOF" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Create or reset version bump branch
|
||||||
|
run: |
|
||||||
|
git checkout -B chore/version-bump
|
||||||
|
git push origin chore/version-bump --force
|
||||||
|
|
||||||
|
- name: Create or update version bump PR
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
COMMITS: ${{ steps.commits.outputs.log }}
|
||||||
|
run: |
|
||||||
|
BUMP_BRANCH="chore/version-bump"
|
||||||
|
VERSION=$(grep -m1 '(' debian/changelog | sed 's/.*(\(.*\)).*/\1/')
|
||||||
|
TITLE="chore: bump pve-mod to v${VERSION}"
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "## Changes merged to main"
|
||||||
|
echo ""
|
||||||
|
echo "${COMMITS:-No src/ changes detected.}"
|
||||||
|
echo ""
|
||||||
|
echo "---"
|
||||||
|
echo "*Apply a bump:minor or bump:major label before merging if needed.*"
|
||||||
|
echo "*Default label is bump:patch.*"
|
||||||
|
} > /tmp/pr-body.md
|
||||||
|
|
||||||
|
EXISTING=$(gh pr list \
|
||||||
|
--head "$BUMP_BRANCH" \
|
||||||
|
--base main \
|
||||||
|
--json number --jq '.[0].number' 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [[ -n "$EXISTING" ]]; then
|
||||||
|
gh pr edit "$EXISTING" \
|
||||||
|
--title "$TITLE" \
|
||||||
|
--body-file /tmp/pr-body.md
|
||||||
|
echo "Updated PR #${EXISTING} body and title (label unchanged)"
|
||||||
|
else
|
||||||
|
gh pr create \
|
||||||
|
--title "$TITLE" \
|
||||||
|
--body-file /tmp/pr-body.md \
|
||||||
|
--base main \
|
||||||
|
--head "$BUMP_BRANCH" \
|
||||||
|
--label "bump:patch"
|
||||||
|
echo "Opened new version bump PR with label bump:patch"
|
||||||
|
fi
|
||||||
85
.github/workflows/release.yml
vendored
Normal file
85
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
name: Build and Release
|
||||||
|
|
||||||
|
# Triggered when debian/changelog changes on main — meaning version-bump.yml
|
||||||
|
# just committed a new version. Builds the .deb and publishes a GitHub Release.
|
||||||
|
# The actor guard ensures this only fires for the bot's commit, not manual edits.
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'debian/changelog'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.actor == 'github-actions[bot]'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract version from changelog
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
VERSION=$(grep -m1 '(' debian/changelog | sed 's/.*(\(.*\)).*/\1/')
|
||||||
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Install build dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y debhelper dpkg-dev
|
||||||
|
|
||||||
|
- name: Build deb package
|
||||||
|
run: |
|
||||||
|
dpkg-buildpackage -us -uc -b
|
||||||
|
ls -lh ../pve-mod_*.deb
|
||||||
|
|
||||||
|
- name: Collect release assets
|
||||||
|
id: assets
|
||||||
|
run: |
|
||||||
|
DEB=$(ls ../pve-mods_${{ steps.version.outputs.version }}.deb)
|
||||||
|
echo "deb=$DEB" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Extract release notes from changelog
|
||||||
|
id: notes
|
||||||
|
run: |
|
||||||
|
# Pull the top changelog entry (lines between first and second "^pve-mod (")
|
||||||
|
NOTES=$(awk '
|
||||||
|
/^pve-mod \(/ { if (found) exit; found=1; next }
|
||||||
|
found && /^ --/ { exit }
|
||||||
|
found { print }
|
||||||
|
' debian/changelog | grep '^\s*\*' | sed 's/^\s*\* /- /')
|
||||||
|
EOF=$(dd if=/dev/urandom bs=15 count=1 2>/dev/null | base64)
|
||||||
|
echo "notes<<$EOF" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "$NOTES" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "$EOF" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Create Git tag
|
||||||
|
env:
|
||||||
|
TAG: ${{ steps.version.outputs.tag }}
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
|
git tag "$TAG"
|
||||||
|
git push origin "$TAG"
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
TAG: ${{ steps.version.outputs.tag }}
|
||||||
|
NOTES: ${{ steps.notes.outputs.notes }}
|
||||||
|
DEB: ${{ steps.assets.outputs.deb }}
|
||||||
|
run: |
|
||||||
|
gh release create "$TAG" \
|
||||||
|
--title "pve-mod $TAG" \
|
||||||
|
--notes "$NOTES" \
|
||||||
|
"$DEB" \
|
||||||
|
"install.sh"
|
||||||
52
.github/workflows/test-release.yml
vendored
Normal file
52
.github/workflows/test-release.yml
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
name: Test Release Build
|
||||||
|
|
||||||
|
# Runs on every push, manual dispatch, or when the 'make-test-build' label
|
||||||
|
# is assigned to a pull request.
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
pull_request:
|
||||||
|
types: [labeled]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-release-build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name != 'pull_request' || github.event.label.name == 'make-test-build'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
|
- name: Install build deps
|
||||||
|
run: sudo apt-get install -y devscripts debhelper build-essential
|
||||||
|
|
||||||
|
- name: Build package
|
||||||
|
run: dpkg-buildpackage -us -uc -b
|
||||||
|
|
||||||
|
- name: Verify package contents
|
||||||
|
run: |
|
||||||
|
echo "=== Built files ==="
|
||||||
|
ls -la ../*.deb
|
||||||
|
echo ""
|
||||||
|
echo "=== Package info ==="
|
||||||
|
dpkg-deb --info ../*.deb
|
||||||
|
echo ""
|
||||||
|
echo "=== Package contents ==="
|
||||||
|
dpkg-deb --contents ../*.deb
|
||||||
|
|
||||||
|
- name: Collect deb artifacts
|
||||||
|
id: collect
|
||||||
|
run: |
|
||||||
|
mkdir -p artifacts
|
||||||
|
cp ../*.deb artifacts/
|
||||||
|
SHORT_SHA=$(git rev-parse --short HEAD)
|
||||||
|
echo "short_sha=$SHORT_SHA" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Upload deb as artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: pve-mods-test-build-${{ steps.collect.outputs.short_sha }}
|
||||||
|
path: artifacts/*.deb
|
||||||
|
retention-days: 3
|
||||||
84
.github/workflows/version-bump.yml
vendored
Normal file
84
.github/workflows/version-bump.yml
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
name: Version Bump
|
||||||
|
|
||||||
|
# Triggered when src/ changes land on main (i.e. a PR just merged).
|
||||||
|
# Increments the patch version in debian/changelog and commits it.
|
||||||
|
# The changelog commit only touches debian/changelog (not src/),
|
||||||
|
# so it does not re-trigger this workflow.
|
||||||
|
# release.yml picks up the changelog change and builds/publishes the release.
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'src/**'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
bump:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Skip commits made by this workflow itself
|
||||||
|
if: github.actor != 'github-actions[bot]'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract and bump version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
CURRENT=$(grep -m1 '(' debian/changelog | sed 's/.*(\(.*\)).*/\1/')
|
||||||
|
MAJOR=$(echo "$CURRENT" | cut -d. -f1)
|
||||||
|
MINOR=$(echo "$CURRENT" | cut -d. -f2)
|
||||||
|
PATCH=$(echo "$CURRENT" | cut -d. -f3)
|
||||||
|
NEW="${MAJOR}.${MINOR}.$((PATCH + 1))"
|
||||||
|
echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "new=$NEW" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Collect src/ commits since last tag
|
||||||
|
id: commits
|
||||||
|
run: |
|
||||||
|
LAST_TAG=$(git tag -l 'v*' | sort -V | tail -n1)
|
||||||
|
if [[ -n "$LAST_TAG" ]]; then
|
||||||
|
LOG=$(git log "${LAST_TAG}..HEAD" --oneline -- src/ | head -50)
|
||||||
|
else
|
||||||
|
LOG=$(git log --oneline -- src/ | head -50)
|
||||||
|
fi
|
||||||
|
EOF=$(dd if=/dev/urandom bs=15 count=1 2>/dev/null | base64)
|
||||||
|
echo "log<<$EOF" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "$LOG" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "$EOF" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Prepend new changelog entry
|
||||||
|
env:
|
||||||
|
NEW_VERSION: ${{ steps.version.outputs.new }}
|
||||||
|
COMMITS: ${{ steps.commits.outputs.log }}
|
||||||
|
run: |
|
||||||
|
DATE=$(date -R)
|
||||||
|
{
|
||||||
|
echo "pve-mod ($NEW_VERSION) stable; urgency=low"
|
||||||
|
echo ""
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[[ -n "$line" ]] && echo " * $line"
|
||||||
|
done <<< "$COMMITS"
|
||||||
|
[[ -z "$COMMITS" ]] && echo " * Automated version bump."
|
||||||
|
echo ""
|
||||||
|
echo " -- github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> $DATE"
|
||||||
|
echo ""
|
||||||
|
cat debian/changelog
|
||||||
|
} > debian/changelog.new
|
||||||
|
mv debian/changelog.new debian/changelog
|
||||||
|
|
||||||
|
- name: Commit and push changelog
|
||||||
|
env:
|
||||||
|
NEW_VERSION: ${{ steps.version.outputs.new }}
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add debian/changelog
|
||||||
|
git commit -m "Release v${NEW_VERSION}"
|
||||||
|
git push
|
||||||
70
debian/changelog
vendored
Normal file
70
debian/changelog
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
pve-mod (0.1.5) stable; urgency=low
|
||||||
|
|
||||||
|
* cfeccc2 align with main
|
||||||
|
* 463dd66 fix startup_message
|
||||||
|
* a6b2560 ..
|
||||||
|
* a7d8c3b add debug to pve-mod-configure
|
||||||
|
* 342a5b5 add debug mode to pve-mod-configure and config
|
||||||
|
* 54dd209 test again
|
||||||
|
* a7cef62 rework hook implementation to work on dpkg triggered
|
||||||
|
* d9ff050 first draft for testing
|
||||||
|
* 73567f1 Restructure git repo for installer
|
||||||
|
* bfb078a PVE Temp mod version 2.0 (#152)
|
||||||
|
|
||||||
|
-- github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Sun, 07 Jun 2026 14:12:32 +0000
|
||||||
|
|
||||||
|
pve-mod (0.1.4) stable; urgency=low
|
||||||
|
|
||||||
|
* 463dd66 fix startup_message
|
||||||
|
* a6b2560 ..
|
||||||
|
* a7d8c3b add debug to pve-mod-configure
|
||||||
|
* 342a5b5 add debug mode to pve-mod-configure and config
|
||||||
|
* 54dd209 test again
|
||||||
|
* a7cef62 rework hook implementation to work on dpkg triggered
|
||||||
|
* d9ff050 first draft for testing
|
||||||
|
* 73567f1 Restructure git repo for installer
|
||||||
|
* bfb078a PVE Temp mod version 2.0 (#152)
|
||||||
|
|
||||||
|
-- github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Sun, 07 Jun 2026 14:05:47 +0000
|
||||||
|
|
||||||
|
pve-mod (0.1.3) stable; urgency=low
|
||||||
|
|
||||||
|
* a6b2560 ..
|
||||||
|
* a7d8c3b add debug to pve-mod-configure
|
||||||
|
* 342a5b5 add debug mode to pve-mod-configure and config
|
||||||
|
* 54dd209 test again
|
||||||
|
* a7cef62 rework hook implementation to work on dpkg triggered
|
||||||
|
* d9ff050 first draft for testing
|
||||||
|
* 73567f1 Restructure git repo for installer
|
||||||
|
* bfb078a PVE Temp mod version 2.0 (#152)
|
||||||
|
|
||||||
|
-- github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Sun, 07 Jun 2026 13:51:51 +0000
|
||||||
|
|
||||||
|
pve-mod (0.1.2) stable; urgency=low
|
||||||
|
|
||||||
|
* a7d8c3b add debug to pve-mod-configure
|
||||||
|
* 342a5b5 add debug mode to pve-mod-configure and config
|
||||||
|
* 54dd209 test again
|
||||||
|
* a7cef62 rework hook implementation to work on dpkg triggered
|
||||||
|
* d9ff050 first draft for testing
|
||||||
|
* 73567f1 Restructure git repo for installer
|
||||||
|
* bfb078a PVE Temp mod version 2.0 (#152)
|
||||||
|
|
||||||
|
-- github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Sun, 07 Jun 2026 13:48:24 +0000
|
||||||
|
|
||||||
|
pve-mod (0.1.1) stable; urgency=low
|
||||||
|
|
||||||
|
* 342a5b5 add debug mode to pve-mod-configure and config
|
||||||
|
* 54dd209 test again
|
||||||
|
* a7cef62 rework hook implementation to work on dpkg triggered
|
||||||
|
* d9ff050 first draft for testing
|
||||||
|
* 73567f1 Restructure git repo for installer
|
||||||
|
* bfb078a PVE Temp mod version 2.0 (#152)
|
||||||
|
|
||||||
|
-- github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Sun, 07 Jun 2026 13:04:56 +0000
|
||||||
|
|
||||||
|
pve-mod (0.1.0) beta; urgency=low
|
||||||
|
|
||||||
|
* Initial release.
|
||||||
|
|
||||||
|
-- Meliox <meliox@users.noreply.github.com> Sat, 07 Jun 2026 00:00:00 +0000
|
||||||
21
debian/control
vendored
Normal file
21
debian/control
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
Source: pve-mod
|
||||||
|
Section: misc
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Meliox <meliox@users.noreply.github.com>
|
||||||
|
Build-Depends: debhelper-compat (= 13)
|
||||||
|
Standards-Version: 4.6.2
|
||||||
|
Homepage: https://github.com/Meliox/PVE-mods
|
||||||
|
|
||||||
|
Package: pve-mod
|
||||||
|
Architecture: all
|
||||||
|
Depends: ${misc:Depends}, perl, librrds-perl
|
||||||
|
Recommends: lm-sensors, nut-client
|
||||||
|
Suggests: igt-gpu-tools
|
||||||
|
Description: Proxmox VE UI modifications and sensor monitoring
|
||||||
|
Extends the Proxmox VE web interface with sensor readings including
|
||||||
|
CPU, GPU, NVMe/HDD/SSD temperatures, fan speeds, and RAM temperatures
|
||||||
|
via lm-sensors. Optionally adds UPS monitoring via Network UPS Tools,
|
||||||
|
system/motherboard information via dmidecode, GPU historical data graphs,
|
||||||
|
and removes the subscription nag screen.
|
||||||
|
.
|
||||||
|
After installation, run: pve-mod-configure
|
||||||
1
debian/pve-mod.conffiles
vendored
Normal file
1
debian/pve-mod.conffiles
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/etc/pve-mod/pve-mod.conf
|
||||||
86
debian/pve-mod.postinst
vendored
Normal file
86
debian/pve-mod.postinst
vendored
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
NODES_PM="/usr/share/perl5/PVE/API2/Nodes.pm"
|
||||||
|
DEFAULT_CONF="/usr/share/pve-mod/pve-mod.conf.default"
|
||||||
|
USER_CONF="/etc/pve-mod/pve-mod.conf"
|
||||||
|
|
||||||
|
# Extracts "section.key" pairs from an INI file, one per line.
|
||||||
|
_extract_conf_keys() {
|
||||||
|
local file="$1" section="" line key
|
||||||
|
while IFS= read -r line; do
|
||||||
|
case "$line" in
|
||||||
|
'#'*|'') continue ;;
|
||||||
|
'['*']') section="${line#[}"; section="${section%]}"; continue ;;
|
||||||
|
*'='*) key="${line%%=*}"; echo "${section}.${key}" ;;
|
||||||
|
esac
|
||||||
|
done < "$file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Warns about keys present in the default config but missing from the user's config.
|
||||||
|
_check_new_config_keys() {
|
||||||
|
[ -f "$DEFAULT_CONF" ] || return 0
|
||||||
|
[ -f "$USER_CONF" ] || return 0
|
||||||
|
|
||||||
|
local new_keys="" section="" key default_val
|
||||||
|
while IFS= read -r line; do
|
||||||
|
case "$line" in
|
||||||
|
'#'*|'') continue ;;
|
||||||
|
'['*']') section="${line#[}"; section="${section%]}"; continue ;;
|
||||||
|
*'='*)
|
||||||
|
key="${line%%=*}"
|
||||||
|
if ! _extract_conf_keys "$USER_CONF" | grep -qF "${section}.${key}"; then
|
||||||
|
new_keys="${new_keys} [${section}] ${line}\n"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < "$DEFAULT_CONF"
|
||||||
|
|
||||||
|
if [ -n "$new_keys" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "pve-mod: New configuration options are available since your last install:"
|
||||||
|
printf "%b" "$new_keys"
|
||||||
|
echo "pve-mod: Run 'pve-mod-configure' to configure them, or add them manually to $USER_CONF"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
configure)
|
||||||
|
# Apply patches for any enabled modules. Fails loudly on error.
|
||||||
|
/usr/lib/pve-mod/apply-patches.sh
|
||||||
|
|
||||||
|
if [ -z "$2" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "pve-mod installed successfully."
|
||||||
|
echo "Run 'pve-mod-configure' to enable and configure modules."
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
# Upgrading from a previous version — warn about any new config keys.
|
||||||
|
_check_new_config_keys
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
triggered)
|
||||||
|
# Fired by dpkg when pve-manager is upgraded. Only re-apply patches
|
||||||
|
# if the user has opted in via 'pve-mod-configure'.
|
||||||
|
TRIGGER_ENABLED=$(awk -F= '/^\[pve_trigger\]/{s=1} s && /^enabled=/{print $2; exit}' \
|
||||||
|
/etc/pve-mod/pve-mod.conf 2>/dev/null || echo 0)
|
||||||
|
if [ "${TRIGGER_ENABLED}" = "1" ]; then
|
||||||
|
echo "pve-mod: pve-manager upgrade detected — re-applying patches."
|
||||||
|
if ! /usr/lib/pve-mod/apply-patches.sh; then
|
||||||
|
echo "pve-mod: WARNING: Failed to re-apply patches. Run 'pve-mod-configure' to fix." >&2
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "pve-mod: WARNING: pve-manager was upgraded but auto re-patching is disabled." >&2
|
||||||
|
echo "pve-mod: The Proxmox UI modifications may no longer be active." >&2
|
||||||
|
echo "pve-mod: Run 'pve-mod-configure' to re-apply patches." >&2
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
abort-upgrade|abort-remove|abort-deconfigure)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
#DEBHELPER#
|
||||||
|
exit 0
|
||||||
18
debian/pve-mod.postrm
vendored
Normal file
18
debian/pve-mod.postrm
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
remove)
|
||||||
|
# Remove apt hook if it was installed via pve-mod-configure.
|
||||||
|
rm -f /etc/apt/apt.conf.d/99-pve-mod
|
||||||
|
;;
|
||||||
|
|
||||||
|
purge)
|
||||||
|
rm -f /etc/apt/apt.conf.d/99-pve-mod
|
||||||
|
rm -rf /etc/pve-mod
|
||||||
|
rm -rf /var/lib/pve-mod
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
#DEBHELPER#
|
||||||
|
exit 0
|
||||||
33
debian/pve-mod.prerm
vendored
Normal file
33
debian/pve-mod.prerm
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
_revert_patches() {
|
||||||
|
if [ -x /usr/lib/pve-mod/revert-patches.sh ]; then
|
||||||
|
/usr/lib/pve-mod/revert-patches.sh 2>&1 || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
remove|deconfigure)
|
||||||
|
# 1. Revert patches before files are removed.
|
||||||
|
_revert_patches
|
||||||
|
|
||||||
|
# 2. Ask whether to keep the config (only in interactive sessions).
|
||||||
|
if [ -t 0 ] && [ -f /etc/pve-mod/pve-mod.conf ]; then
|
||||||
|
printf '\npve-mod: Keep /etc/pve-mod/pve-mod.conf for re-use after reinstall? [Y/n] '
|
||||||
|
read -r _keep
|
||||||
|
case "$_keep" in
|
||||||
|
[nN]*) rm -f /etc/pve-mod/pve-mod.conf
|
||||||
|
echo "pve-mod: Configuration removed." ;;
|
||||||
|
*) echo "pve-mod: Configuration kept at /etc/pve-mod/pve-mod.conf" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
upgrade)
|
||||||
|
# On upgrade, leave patches in place; postinst will re-apply them.
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
#DEBHELPER#
|
||||||
|
exit 0
|
||||||
1
debian/pve-mod.triggers
vendored
Normal file
1
debian/pve-mod.triggers
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
interest pve-manager
|
||||||
47
debian/rules
vendored
Normal file
47
debian/rules
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
override_dh_install:
|
||||||
|
# PVE API2 facade
|
||||||
|
install -Dm644 src/PVENodeInfo/PveMod_SensorInfo.pm \
|
||||||
|
debian/pve-mod/usr/share/perl5/PVE/API2/PVEMod_SensorInfo.pm
|
||||||
|
# PVEMod core modules (strip PVEMod_ prefix)
|
||||||
|
install -Dm644 src/PVENodeInfo/PVEMod_Config.pm \
|
||||||
|
debian/pve-mod/usr/share/perl5/PVE/PVEMod/Config.pm
|
||||||
|
install -Dm644 src/PVENodeInfo/PVEMod_Utils.pm \
|
||||||
|
debian/pve-mod/usr/share/perl5/PVE/PVEMod/Utils.pm
|
||||||
|
install -Dm644 src/PVENodeInfo/PVEMod_Store.pm \
|
||||||
|
debian/pve-mod/usr/share/perl5/PVE/PVEMod/Store.pm
|
||||||
|
install -Dm644 src/PVENodeInfo/PVEMod_ProcessManager.pm \
|
||||||
|
debian/pve-mod/usr/share/perl5/PVE/PVEMod/ProcessManager.pm
|
||||||
|
# Collector plugins
|
||||||
|
install -Dm644 src/PVENodeInfo/PVEMod_Collectors/Intel.pm \
|
||||||
|
debian/pve-mod/usr/share/perl5/PVE/PVEMod/Collector/Intel.pm
|
||||||
|
install -Dm644 src/PVENodeInfo/PVEMod_Collectors/Nvidia.pm \
|
||||||
|
debian/pve-mod/usr/share/perl5/PVE/PVEMod/Collector/Nvidia.pm
|
||||||
|
install -Dm644 src/PVENodeInfo/PVEMod_Collectors/Amd.pm \
|
||||||
|
debian/pve-mod/usr/share/perl5/PVE/PVEMod/Collector/Amd.pm
|
||||||
|
install -Dm644 src/PVENodeInfo/PVEMod_Collectors/LmSensors.pm \
|
||||||
|
debian/pve-mod/usr/share/perl5/PVE/PVEMod/Collector/LmSensors.pm
|
||||||
|
install -Dm644 src/PVENodeInfo/PVEMod_Collectors/Ups.pm \
|
||||||
|
debian/pve-mod/usr/share/perl5/PVE/PVEMod/Collector/Ups.pm
|
||||||
|
install -Dm644 src/PVENodeInfo/PVEMod_Collectors/systemInformation.pm \
|
||||||
|
debian/pve-mod/usr/share/perl5/PVE/PVEMod/Collector/SystemInformation.pm
|
||||||
|
# JS module (rename to match loader reference)
|
||||||
|
install -Dm644 src/PVENodeInfo/PveMod_pvemanagerlib.js \
|
||||||
|
debian/pve-mod/usr/share/pve-manager/js/PveMod_PveNodeStatusView.js
|
||||||
|
# Patch helpers
|
||||||
|
install -Dm755 src/PVENodeInfo/apply-patches.sh \
|
||||||
|
debian/pve-mod/usr/lib/pve-mod/apply-patches.sh
|
||||||
|
install -Dm755 src/PVENodeInfo/revert-patches.sh \
|
||||||
|
debian/pve-mod/usr/lib/pve-mod/revert-patches.sh
|
||||||
|
# Configure tool
|
||||||
|
install -Dm755 src/Scripts/pve-mod-configure \
|
||||||
|
debian/pve-mod/usr/sbin/pve-mod-configure
|
||||||
|
# Default config (conffile for user edits)
|
||||||
|
install -Dm644 src/pve-mod.conf \
|
||||||
|
debian/pve-mod/etc/pve-mod/pve-mod.conf
|
||||||
|
# Reference copy for upgrade key-diff (not a conffile — always updated)
|
||||||
|
install -Dm644 src/pve-mod.conf \
|
||||||
|
debian/pve-mod/usr/share/pve-mod/pve-mod.conf.default
|
||||||
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
3.0 (native)
|
||||||
61
install.sh
Normal file
61
install.sh
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# install.sh — Bootstrap installer for pve-mod
|
||||||
|
# Usage: curl -sL https://github.com/Meliox/PVE-mods/releases/latest/download/install.sh | bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO="Meliox/PVE-mods"
|
||||||
|
API_URL="https://api.github.com/repos/${REPO}/releases/latest"
|
||||||
|
|
||||||
|
#region helpers
|
||||||
|
info() { echo -e "\e[0;32m[pve-mod] ${1}\e[0m"; }
|
||||||
|
err() { echo -e "\e[0;31m[pve-mod] ERROR: ${1}\e[0m" >&2; exit 1; }
|
||||||
|
#endregion helpers
|
||||||
|
|
||||||
|
# ── Prerequisite checks ───────────────────────────────────────────────────────
|
||||||
|
[[ $EUID -eq 0 ]] || err "This installer must be run as root."
|
||||||
|
|
||||||
|
dpkg -l proxmox-ve &>/dev/null 2>&1 || \
|
||||||
|
err "This system does not appear to be running Proxmox VE."
|
||||||
|
|
||||||
|
for cmd in curl dpkg; do
|
||||||
|
command -v "$cmd" &>/dev/null || err "Required command not found: $cmd"
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Fetch latest release metadata ─────────────────────────────────────────────
|
||||||
|
info "Fetching latest release information..."
|
||||||
|
RELEASE_JSON=$(curl -sL "$API_URL") || err "Failed to contact GitHub API."
|
||||||
|
|
||||||
|
# Extract .deb download URL (no jq dependency)
|
||||||
|
DEB_URL=$(echo "$RELEASE_JSON" \
|
||||||
|
| grep '"browser_download_url"' \
|
||||||
|
| grep '\.deb"' \
|
||||||
|
| sed 's/.*"browser_download_url": "\([^"]*\)".*/\1/' \
|
||||||
|
| head -n1)
|
||||||
|
|
||||||
|
[[ -n "$DEB_URL" ]] || err "No .deb package found in the latest release."
|
||||||
|
|
||||||
|
VERSION=$(echo "$RELEASE_JSON" \
|
||||||
|
| grep '"tag_name"' \
|
||||||
|
| sed 's/.*"tag_name": "\([^"]*\)".*/\1/' \
|
||||||
|
| head -n1)
|
||||||
|
|
||||||
|
info "Installing pve-mod ${VERSION}..."
|
||||||
|
|
||||||
|
# ── Download and install ───────────────────────────────────────────────────────
|
||||||
|
TMP=$(mktemp /tmp/pve-mod-XXXXXX.deb)
|
||||||
|
trap 'rm -f "$TMP"' EXIT
|
||||||
|
|
||||||
|
curl -sL -o "$TMP" "$DEB_URL" || err "Failed to download package from $DEB_URL"
|
||||||
|
|
||||||
|
dpkg -i "$TMP" || {
|
||||||
|
info "Resolving missing dependencies..."
|
||||||
|
apt-get install -f -y
|
||||||
|
dpkg -i "$TMP"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Done ──────────────────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
info "pve-mod ${VERSION} installed successfully."
|
||||||
|
info "Run 'pve-mod-configure' to enable and configure modules."
|
||||||
|
echo ""
|
||||||
@ -30,38 +30,47 @@ our $process_type = 'main'; # 'main', 'worker', or 'collector'
|
|||||||
|
|
||||||
our %config = (
|
our %config = (
|
||||||
gpu => {
|
gpu => {
|
||||||
intel_enabled => 1,
|
intel_enabled => 0,
|
||||||
amd_enabled => 0,
|
amd_enabled => 0,
|
||||||
nvidia_enabled => 0,
|
nvidia_enabled => 0,
|
||||||
|
gpu_history => 0,
|
||||||
},
|
},
|
||||||
debug => {
|
debug => {
|
||||||
log_enabled => 0,
|
log_enabled => 0,
|
||||||
log_file => '/tmp/pve-mod-debug.log',
|
log_file => '/tmp/pve-mod-debug.log',
|
||||||
nvidia_mode => 1,
|
|
||||||
nvidia_devices_file => '/tmp/nvidia-smi-devices.csv',
|
|
||||||
nvidia_output_file => '/tmp/nvidia-smi-output.csv',
|
|
||||||
intel_mode => 0,
|
|
||||||
intel_devices_file => '/tmp/intel-gpu-devices.json',
|
|
||||||
amd_mode => 0,
|
|
||||||
amd_devices_file => '/tmp/amd-gpu-devices.json',
|
|
||||||
ups_mode => 0,
|
|
||||||
ups_output_file => '/tmp/ups-output.json',
|
|
||||||
lm_sensors_mode => 0,
|
lm_sensors_mode => 0,
|
||||||
lm_sensors_output_file => '/tmp/sensors-output.json',
|
lm_sensors_output_file => '/tmp/sensors-output.json',
|
||||||
|
intel_mode => 0,
|
||||||
|
intel_devices_file => '/tmp/intel-gpu-devices.json',
|
||||||
|
nvidia_mode => 0,
|
||||||
|
nvidia_devices_file => '/tmp/nvidia-smi-devices.csv',
|
||||||
|
nvidia_output_file => '/tmp/nvidia-smi-output.csv',
|
||||||
|
amd_mode => 0,
|
||||||
|
amd_devices_file => '/tmp/amd-gpu-devices.json',
|
||||||
|
ups_mode => 0,
|
||||||
|
ups_output_file => '/tmp/ups-output.json',
|
||||||
},
|
},
|
||||||
intervals => {
|
intervals => {
|
||||||
data_pull => 1, # seconds between data pulls
|
data_pull => 1, # seconds between data pulls
|
||||||
collector_timeout => 10, # stop collectors after N seconds of inactivity
|
collector_timeout => 10, # stop collectors after N seconds of inactivity
|
||||||
},
|
},
|
||||||
lm_sensors => {
|
lm_sensors => {
|
||||||
enabled => 1,
|
enabled => 0,
|
||||||
|
enable_cpu => 0,
|
||||||
|
cpu_temp_target => 'Core',
|
||||||
|
enable_ram_temp => 0,
|
||||||
|
enable_hdd_temp => 0,
|
||||||
|
enable_nvme_temp => 0,
|
||||||
|
enable_fan_speed => 0,
|
||||||
|
display_zero_speed_fans => 0,
|
||||||
|
temp_unit => 'C',
|
||||||
},
|
},
|
||||||
ups => {
|
ups => {
|
||||||
enabled => 1,
|
enabled => 0,
|
||||||
device_name => 'ups@192.168.3.2',
|
device_name => 'ups@localhost',
|
||||||
},
|
},
|
||||||
system_info => {
|
system_info => {
|
||||||
enabled => 1,
|
enabled => 0,
|
||||||
type => 1, # 1 = System (dmidecode -t 1), 2 = Baseboard/Motherboard (dmidecode -t 2)
|
type => 1, # 1 = System (dmidecode -t 1), 2 = Baseboard/Motherboard (dmidecode -t 2)
|
||||||
},
|
},
|
||||||
paths => {
|
paths => {
|
||||||
@ -88,4 +97,55 @@ our $startup_lock = "$pve_mod_working_dir/startup.lock";
|
|||||||
our $RRD_SOCKET = '/var/run/rrdcached.sock';
|
our $RRD_SOCKET = '/var/run/rrdcached.sock';
|
||||||
our $RRD_BASE = '/var/lib/rrdcached/db/pve-mod-gpu';
|
our $RRD_BASE = '/var/lib/rrdcached/db/pve-mod-gpu';
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Load configuration from /etc/pve-mod/pve-mod.conf (INI format).
|
||||||
|
# Merges file values into %config, overriding compiled-in defaults.
|
||||||
|
# Safe to call multiple times; silently skips missing file or unknown keys.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
sub _load_ini_file {
|
||||||
|
my $path = '/etc/pve-mod/pve-mod.conf';
|
||||||
|
return unless -f $path;
|
||||||
|
|
||||||
|
open my $fh, '<', $path or return;
|
||||||
|
my $section = '';
|
||||||
|
|
||||||
|
while (my $line = <$fh>) {
|
||||||
|
chomp $line;
|
||||||
|
$line =~ s/#.*//; # strip inline comments
|
||||||
|
$line =~ s/^\s+|\s+$//g; # trim whitespace
|
||||||
|
next unless length $line;
|
||||||
|
|
||||||
|
if ($line =~ /^\[([^\]]+)\]$/) {
|
||||||
|
$section = $1;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($line =~ /^([^=]+)=(.*)$/) {
|
||||||
|
my ($key, $val) = ($1, $2);
|
||||||
|
$key =~ s/^\s+|\s+$//g;
|
||||||
|
$val =~ s/^\s+|\s+$//g;
|
||||||
|
|
||||||
|
if ($section eq 'gpu' && exists $config{gpu}{$key}) {
|
||||||
|
$config{gpu}{$key} = $val;
|
||||||
|
}
|
||||||
|
elsif ($section eq 'lm_sensors' && exists $config{lm_sensors}{$key}) {
|
||||||
|
$config{lm_sensors}{$key} = $val;
|
||||||
|
}
|
||||||
|
elsif ($section eq 'ups' && exists $config{ups}{$key}) {
|
||||||
|
$config{ups}{$key} = $val;
|
||||||
|
}
|
||||||
|
elsif ($section eq 'system_info' && exists $config{system_info}{$key}) {
|
||||||
|
$config{system_info}{$key} = $val;
|
||||||
|
}
|
||||||
|
elsif ($section eq 'debug' && exists $config{debug}{$key}) {
|
||||||
|
$config{debug}{$key} = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close $fh;
|
||||||
|
}
|
||||||
|
|
||||||
|
_load_ini_file();
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|||||||
260
src/PVENodeInfo/apply-patches.sh
Normal file
260
src/PVENodeInfo/apply-patches.sh
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# /usr/lib/pve-mod/apply-patches.sh
|
||||||
|
#
|
||||||
|
# Applies pve-mod patches to Proxmox VE system files.
|
||||||
|
# Reads /etc/pve-mod/pve-mod.conf to determine which modules are enabled.
|
||||||
|
# Idempotent: safe to call multiple times (e.g. from apt hook after PVE upgrade).
|
||||||
|
|
||||||
|
CONF_FILE="/etc/pve-mod/pve-mod.conf"
|
||||||
|
BACKUP_DIR="/var/lib/pve-mod/backup"
|
||||||
|
NODES_PM="/usr/share/perl5/PVE/API2/Nodes.pm"
|
||||||
|
PVE_MANAGER_JS="/usr/share/pve-manager/js/pvemanagerlib.js"
|
||||||
|
PVE_MOD_JS="/usr/share/pve-manager/js/PveMod_PveNodeStatusView.js"
|
||||||
|
PROXMOXLIB_JS="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
|
||||||
|
PROXMOXLIB_MIN_JS="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js"
|
||||||
|
GPU_RRD_DIR="/var/lib/rrdcached/db/pve-mod-gpu"
|
||||||
|
|
||||||
|
info() { echo "[pve-mod] $*"; }
|
||||||
|
warn() { echo "[pve-mod] WARNING: $*" >&2; }
|
||||||
|
|
||||||
|
# Read one value from the INI config file; prints $default if not found.
|
||||||
|
read_conf() {
|
||||||
|
local section="$1" key="$2" default="${3:-0}"
|
||||||
|
if [[ ! -f "$CONF_FILE" ]]; then
|
||||||
|
echo "$default"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
local val
|
||||||
|
val=$(awk -F= -v sec="[$section]" -v k="$key" '
|
||||||
|
/^\[/ { in_sec = ($0 == sec) }
|
||||||
|
in_sec && /^[^#=]+=/ {
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1)
|
||||||
|
if ($1 == k) {
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2)
|
||||||
|
print $2; exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$CONF_FILE")
|
||||||
|
echo "${val:-$default}"
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_file() {
|
||||||
|
local src="$1"
|
||||||
|
[[ -f "$src" ]] || return 0
|
||||||
|
local name ts
|
||||||
|
name=$(basename "$src")
|
||||||
|
ts=$(date +%Y%m%d_%H%M%S)
|
||||||
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
cp "$src" "$BACKUP_DIR/${name}.${ts}"
|
||||||
|
info "Backed up $(basename "$src") → $BACKUP_DIR/${name}.${ts}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Read enabled modules ──────────────────────────────────────────────────────
|
||||||
|
NODE_INFO=$(read_conf modules node_info 0)
|
||||||
|
NAG_SCREEN=$(read_conf modules nag_screen 0)
|
||||||
|
GPU_HISTORY=$(read_conf gpu gpu_history 0)
|
||||||
|
|
||||||
|
CHANGED=false
|
||||||
|
|
||||||
|
# ── node-info: Nodes.pm ───────────────────────────────────────────────────────
|
||||||
|
if [[ "$NODE_INFO" == "1" ]]; then
|
||||||
|
if ! grep -qF "use PVE::API2::PVEMod_SensorInfo" "$NODES_PM" 2>/dev/null; then
|
||||||
|
backup_file "$NODES_PM"
|
||||||
|
python3 - "$NODES_PM" <<'PYEOF'
|
||||||
|
import sys, re
|
||||||
|
|
||||||
|
path = sys.argv[1]
|
||||||
|
content = open(path).read()
|
||||||
|
|
||||||
|
if 'use PVE::API2::PVEMod_SensorInfo' in content:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
m = re.search(r'^([ \t]*)my \$dinfo = df\(\'\/\', 1\);', content, re.MULTILINE)
|
||||||
|
if not m:
|
||||||
|
print("ERROR: Anchor 'my $dinfo = df' not found in Nodes.pm", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
indent = m.group(1)
|
||||||
|
insertion = (
|
||||||
|
f"{indent}# Collect sensor data from PveMod_SensorInfo\n"
|
||||||
|
f"{indent}use PVE::API2::PVEMod_SensorInfo;\n"
|
||||||
|
f"{indent}$res->{{PveMod_JsonSensorInfo}} = PVE::API2::PVEMod_SensorInfo::get_sensors_info();\n"
|
||||||
|
f"{indent}$res->{{PveMod_graphicsInfo}} = PVE::API2::PVEMod_SensorInfo::get_pve_mod_version();\n"
|
||||||
|
f"{indent}$res->{{PveMod_upsInfo}} = PVE::API2::PVEMod_SensorInfo::get_ups_info();\n"
|
||||||
|
f"{indent}$res->{{pveMod_sensorInfo_systemInfo}} = PVE::API2::PVEMod_SensorInfo::get_system_information();\n"
|
||||||
|
)
|
||||||
|
content = content[:m.start()] + insertion + content[m.start():]
|
||||||
|
open(path, 'w').write(content)
|
||||||
|
PYEOF
|
||||||
|
info "Patched Nodes.pm"
|
||||||
|
CHANGED=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── node-info: pvemanagerlib.js ───────────────────────────────────────────
|
||||||
|
if ! grep -qF "PveMod_PveNodeStatusView.js" "$PVE_MANAGER_JS" 2>/dev/null; then
|
||||||
|
backup_file "$PVE_MANAGER_JS"
|
||||||
|
python3 - "$PVE_MANAGER_JS" <<'PYEOF'
|
||||||
|
import sys, re
|
||||||
|
|
||||||
|
path = sys.argv[1]
|
||||||
|
content = open(path).read()
|
||||||
|
|
||||||
|
if 'PveMod_PveNodeStatusView.js' in content:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Comment out original StatusView definition
|
||||||
|
content = re.sub(
|
||||||
|
r"(?m)^(Ext\.define\('PVE\.node\.StatusView',.*?^}\);)",
|
||||||
|
lambda m: '\n'.join('// ' + line for line in m.group(1).split('\n')),
|
||||||
|
content, flags=re.DOTALL
|
||||||
|
)
|
||||||
|
|
||||||
|
# Comment out original Summary definition
|
||||||
|
content = re.sub(
|
||||||
|
r"(?m)^(Ext\.define\('PVE\.node\.Summary',.*?^}\);)",
|
||||||
|
lambda m: '\n'.join('// ' + line for line in m.group(1).split('\n')),
|
||||||
|
content, flags=re.DOTALL
|
||||||
|
)
|
||||||
|
|
||||||
|
# Insert dynamic loader before the now-commented StatusView block
|
||||||
|
loader = (
|
||||||
|
"// Load custom PVE.node.StatusView from external module\n"
|
||||||
|
"Ext.Loader.loadScript({\n"
|
||||||
|
" url: '/pve2/js/PveMod_PveNodeStatusView.js',\n"
|
||||||
|
" onLoad: function() { },\n"
|
||||||
|
" onError: function() { console.error('Failed to load PveMod_PveNodeStatusView.js'); }\n"
|
||||||
|
"});\n"
|
||||||
|
)
|
||||||
|
content = re.sub(
|
||||||
|
r"(// Ext\.define\('PVE\.node\.StatusView',)",
|
||||||
|
loader + r'\1',
|
||||||
|
content, count=1
|
||||||
|
)
|
||||||
|
open(path, 'w').write(content)
|
||||||
|
PYEOF
|
||||||
|
info "Patched pvemanagerlib.js"
|
||||||
|
CHANGED=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── node-info: GPU RRD history ────────────────────────────────────────────────
|
||||||
|
if [[ "$NODE_INFO" == "1" && "$GPU_HISTORY" == "1" ]]; then
|
||||||
|
if ! grep -qF "gpurrddata" "$NODES_PM" 2>/dev/null; then
|
||||||
|
# Register method in the node sub-path list
|
||||||
|
sed -i "s/{ name => 'rrddata' },/{ name => 'rrddata' },\n { name => 'gpurrddata' },/" "$NODES_PM"
|
||||||
|
|
||||||
|
# Append gpurrddata method definition after the rrddata code block
|
||||||
|
python3 - "$NODES_PM" <<'PYEOF'
|
||||||
|
import sys, re
|
||||||
|
|
||||||
|
path = sys.argv[1]
|
||||||
|
content = open(path).read()
|
||||||
|
|
||||||
|
if 'gpurrddata' in content:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
method = r"""
|
||||||
|
__PACKAGE__->register_method({
|
||||||
|
name => 'gpurrddata',
|
||||||
|
path => 'gpurrddata',
|
||||||
|
method => 'GET',
|
||||||
|
protected => 1,
|
||||||
|
proxyto => 'node',
|
||||||
|
permissions => {
|
||||||
|
check => ['perm', '/nodes/{node}', ['Sys.Audit']],
|
||||||
|
},
|
||||||
|
description => "Read GPU RRD statistics",
|
||||||
|
parameters => {
|
||||||
|
additionalProperties => 0,
|
||||||
|
properties => {
|
||||||
|
node => get_standard_option('pve-node'),
|
||||||
|
card => {
|
||||||
|
description => "The GPU card identifier (e.g. card0, nvidia0).",
|
||||||
|
type => 'string',
|
||||||
|
pattern => '[a-zA-Z0-9]+',
|
||||||
|
},
|
||||||
|
timeframe => {
|
||||||
|
description => "Specify the time frame you are interested in.",
|
||||||
|
type => 'string',
|
||||||
|
enum => ['hour', 'day', 'week', 'month', 'year', 'decade'],
|
||||||
|
},
|
||||||
|
cf => {
|
||||||
|
description => "The RRD consolidation function",
|
||||||
|
type => 'string',
|
||||||
|
enum => ['AVERAGE', 'MAX'],
|
||||||
|
optional => 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
returns => {
|
||||||
|
type => "array",
|
||||||
|
items => {
|
||||||
|
type => "object",
|
||||||
|
properties => {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
code => sub {
|
||||||
|
my ($param) = @_;
|
||||||
|
my $nodename = PVE::INotify::nodename();
|
||||||
|
my $card = $param->{card};
|
||||||
|
die "invalid card name\n" unless $card =~ /^[a-zA-Z0-9]+$/;
|
||||||
|
return PVE::RRD::create_rrd_data(
|
||||||
|
"pve-mod-gpu/$nodename/$card", $param->{timeframe}, $param->{cf},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Insert before the final 1; at end of file
|
||||||
|
content = re.sub(r'\n1;\s*$', method + '\n1;\n', content)
|
||||||
|
open(path, 'w').write(content)
|
||||||
|
PYEOF
|
||||||
|
|
||||||
|
mkdir -p "$GPU_RRD_DIR"
|
||||||
|
chown www-data:www-data "$GPU_RRD_DIR" 2>/dev/null || true
|
||||||
|
info "Installed gpurrddata API endpoint"
|
||||||
|
CHANGED=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── nag-screen: proxmoxlib.js ─────────────────────────────────────────────────
|
||||||
|
if [[ "$NAG_SCREEN" == "1" ]]; then
|
||||||
|
if ! grep -qF "// disable subscription nag screen" "$PROXMOXLIB_JS" 2>/dev/null; then
|
||||||
|
backup_file "$PROXMOXLIB_JS"
|
||||||
|
python3 - "$PROXMOXLIB_JS" <<'PYEOF'
|
||||||
|
import sys, re
|
||||||
|
|
||||||
|
path = sys.argv[1]
|
||||||
|
content = open(path).read()
|
||||||
|
|
||||||
|
if '// disable subscription nag screen' in content:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
m = re.search(r'(checked_command:\s*function\s*\(orig_cmd\)\s*\{)', content)
|
||||||
|
if not m:
|
||||||
|
print("ERROR: checked_command pattern not found in proxmoxlib.js", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
insert = "\n\t\t\t// disable subscription nag screen\n\t\t\torig_cmd();\n\t\t\treturn;"
|
||||||
|
pos = m.end()
|
||||||
|
content = content[:pos] + insert + content[pos:]
|
||||||
|
open(path, 'w').write(content)
|
||||||
|
PYEOF
|
||||||
|
info "Patched proxmoxlib.js (nag screen)"
|
||||||
|
CHANGED=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -L "$PROXMOXLIB_MIN_JS" ]]; then
|
||||||
|
backup_file "$PROXMOXLIB_MIN_JS"
|
||||||
|
mv "$PROXMOXLIB_MIN_JS" "$BACKUP_DIR/proxmoxlib.min.js.$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true
|
||||||
|
ln -sf "$PROXMOXLIB_JS" "$PROXMOXLIB_MIN_JS"
|
||||||
|
info "Symlinked proxmoxlib.min.js → proxmoxlib.js"
|
||||||
|
CHANGED=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── restart pveproxy if anything changed ──────────────────────────────────────
|
||||||
|
if [[ "$CHANGED" == "true" ]]; then
|
||||||
|
info "Restarting pveproxy..."
|
||||||
|
systemctl restart pveproxy 2>/dev/null || true
|
||||||
|
fi
|
||||||
69
src/PVENodeInfo/revert-patches.sh
Normal file
69
src/PVENodeInfo/revert-patches.sh
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# /usr/lib/pve-mod/revert-patches.sh
|
||||||
|
#
|
||||||
|
# Reverts all patches applied by apply-patches.sh.
|
||||||
|
# Restores PVE system files from backups in /var/lib/pve-mod/backup/.
|
||||||
|
# Called by prerm before package files are removed.
|
||||||
|
|
||||||
|
BACKUP_DIR="/var/lib/pve-mod/backup"
|
||||||
|
NODES_PM="/usr/share/perl5/PVE/API2/Nodes.pm"
|
||||||
|
PVE_MANAGER_JS="/usr/share/pve-manager/js/pvemanagerlib.js"
|
||||||
|
PVE_MOD_JS="/usr/share/pve-manager/js/PveMod_PveNodeStatusView.js"
|
||||||
|
PROXMOXLIB_JS="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
|
||||||
|
PROXMOXLIB_MIN_JS="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js"
|
||||||
|
|
||||||
|
info() { echo "[pve-mod] $*"; }
|
||||||
|
warn() { echo "[pve-mod] WARNING: $*" >&2; }
|
||||||
|
|
||||||
|
restore_latest() {
|
||||||
|
local name="$1" target="$2"
|
||||||
|
local latest
|
||||||
|
latest=$(find "$BACKUP_DIR" -name "${name}.*" -type f -printf '%T+ %p\n' 2>/dev/null \
|
||||||
|
| sort -r | head -n1 | awk '{print $2}')
|
||||||
|
if [[ -n "$latest" ]]; then
|
||||||
|
cp "$latest" "$target"
|
||||||
|
info "Restored $(basename "$target") from backup"
|
||||||
|
else
|
||||||
|
warn "No backup found for ${name}; $target not restored"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
CHANGED=false
|
||||||
|
|
||||||
|
# ── node-info: Nodes.pm ───────────────────────────────────────────────────────
|
||||||
|
if grep -qF "use PVE::API2::PVEMod_SensorInfo" "$NODES_PM" 2>/dev/null; then
|
||||||
|
restore_latest "Nodes.pm" "$NODES_PM"
|
||||||
|
CHANGED=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── node-info: pvemanagerlib.js ───────────────────────────────────────────────
|
||||||
|
if grep -qF "PveMod_PveNodeStatusView.js" "$PVE_MANAGER_JS" 2>/dev/null; then
|
||||||
|
restore_latest "pvemanagerlib.js" "$PVE_MANAGER_JS"
|
||||||
|
CHANGED=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── node-info: JS module file ─────────────────────────────────────────────────
|
||||||
|
if [[ -f "$PVE_MOD_JS" ]]; then
|
||||||
|
rm -f "$PVE_MOD_JS"
|
||||||
|
info "Removed PveMod_PveNodeStatusView.js"
|
||||||
|
CHANGED=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── nag-screen: proxmoxlib.min.js symlink ────────────────────────────────────
|
||||||
|
if [[ -L "$PROXMOXLIB_MIN_JS" ]]; then
|
||||||
|
rm -f "$PROXMOXLIB_MIN_JS"
|
||||||
|
restore_latest "proxmoxlib.min.js" "$PROXMOXLIB_MIN_JS"
|
||||||
|
CHANGED=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── nag-screen: proxmoxlib.js ────────────────────────────────────────────────
|
||||||
|
if grep -qF "// disable subscription nag screen" "$PROXMOXLIB_JS" 2>/dev/null; then
|
||||||
|
restore_latest "proxmoxlib.js" "$PROXMOXLIB_JS"
|
||||||
|
CHANGED=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── restart pveproxy if anything changed ─────────────────────────────────────
|
||||||
|
if [[ "$CHANGED" == "true" ]]; then
|
||||||
|
info "Restarting pveproxy..."
|
||||||
|
systemctl restart pveproxy 2>/dev/null || true
|
||||||
|
fi
|
||||||
584
src/Scripts/pve-mod-configure
Normal file
584
src/Scripts/pve-mod-configure
Normal file
@ -0,0 +1,584 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# pve-mod-configure - Interactive configuration tool for pve-mod
|
||||||
|
# Detects hardware, asks the user which features to enable, writes
|
||||||
|
# /etc/pve-mod/pve-mod.conf, and applies patches to PVE system files.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CONF_FILE="/etc/pve-mod/pve-mod.conf"
|
||||||
|
APPLY_PATCHES="/usr/lib/pve-mod/apply-patches.sh"
|
||||||
|
NODES_PM="/usr/share/perl5/PVE/API2/Nodes.pm"
|
||||||
|
|
||||||
|
KNOWN_CPU_SENSORS=("coretemp-isa-" "k10temp-pci-")
|
||||||
|
|
||||||
|
#region helpers
|
||||||
|
msgb() { echo -e "\e[1m${1}\e[0m"; }
|
||||||
|
info() { echo -e "\e[0;32m[info] ${1}\e[0m"; }
|
||||||
|
warn() { echo -e "\e[0;33m[warning] ${1}\e[0m"; }
|
||||||
|
err() { echo -e "\e[0;31m[error] ${1}\e[0m"; exit 1; }
|
||||||
|
ask() {
|
||||||
|
local prompt="$1" response
|
||||||
|
read -r -p $'\n\e[1;36m'"${prompt}:"$'\e[0m ' response
|
||||||
|
echo "$response"
|
||||||
|
}
|
||||||
|
bool() { [[ "$1" == true ]] && echo 1 || echo 0; }
|
||||||
|
|
||||||
|
_load_conf_debug() {
|
||||||
|
[[ -f "$CONF_FILE" ]] || return 0
|
||||||
|
local in_debug=0 line key val
|
||||||
|
while IFS= read -r line; do
|
||||||
|
case "$line" in
|
||||||
|
'[debug]') in_debug=1; continue ;;
|
||||||
|
'['*']') in_debug=0; continue ;;
|
||||||
|
'#'*|'') continue ;;
|
||||||
|
esac
|
||||||
|
[[ "$in_debug" -eq 0 ]] && continue
|
||||||
|
key="${line%%=*}"; val="${line#*=}"
|
||||||
|
case "$key" in
|
||||||
|
lm_sensors_mode) DEBUG_LM_SENSORS="$val" ;;
|
||||||
|
lm_sensors_output_file) DEBUG_LM_SENSORS_FILE="$val" ;;
|
||||||
|
intel_mode) DEBUG_INTEL="$val" ;;
|
||||||
|
intel_devices_file) DEBUG_INTEL_FILE="$val" ;;
|
||||||
|
nvidia_mode) DEBUG_NVIDIA="$val" ;;
|
||||||
|
nvidia_devices_file) DEBUG_NVIDIA_DEVICES_FILE="$val" ;;
|
||||||
|
nvidia_output_file) DEBUG_NVIDIA_OUTPUT_FILE="$val" ;;
|
||||||
|
amd_mode) DEBUG_AMD="$val" ;;
|
||||||
|
amd_devices_file) DEBUG_AMD_FILE="$val" ;;
|
||||||
|
ups_mode) DEBUG_UPS="$val" ;;
|
||||||
|
ups_output_file) DEBUG_UPS_FILE="$val" ;;
|
||||||
|
log_enabled) DEBUG_LOG="$val" ;;
|
||||||
|
log_file) DEBUG_LOG_FILE="$val" ;;
|
||||||
|
esac
|
||||||
|
done < "$CONF_FILE"
|
||||||
|
}
|
||||||
|
#endregion helpers
|
||||||
|
|
||||||
|
sanitize_sensors_output() {
|
||||||
|
local input="$1"
|
||||||
|
echo "$input" | perl -0777 -pe '
|
||||||
|
s/ERROR:.+\s(\w+):\s(.+)/"$1": 0.000,/g;
|
||||||
|
s/ERROR:.+\s(\w+)!/"$1": 0.000,/g;
|
||||||
|
s/,\s*(\})/$1/g;
|
||||||
|
s/\bNaN\b/null/g;
|
||||||
|
s/"SODIMM"\s*:\s*\{\s*"temp(\d+)_input"/"SODIMM $1": {\n "temp$1_input"/g;
|
||||||
|
s/"([^"]*Fan[^"]*)"\s*:\s*\{\s*"fan(\d+)_input"/"$1 $2": {\n "fan$2_input"/g;
|
||||||
|
' | python3 -m json.tool 2>/dev/null || echo "$input"
|
||||||
|
}
|
||||||
|
|
||||||
|
_check_or_install_tool() {
|
||||||
|
local cmd="$1" pkg="$2" description="$3"
|
||||||
|
if command -v "$cmd" &>/dev/null; then
|
||||||
|
info "$description is installed."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
local choice
|
||||||
|
choice=$(ask "$description is not installed. Install it now? (y/N)")
|
||||||
|
case "$choice" in
|
||||||
|
[yY])
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y "$pkg"
|
||||||
|
command -v "$cmd" &>/dev/null && { info "$description installed."; return 0; } || \
|
||||||
|
{ warn "$description installation failed. Section will be skipped."; return 1; }
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
info "Skipping $description."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
_check_nvidia_tool() {
|
||||||
|
if command -v nvidia-smi &>/dev/null; then
|
||||||
|
info "nvidia-smi is installed."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
warn "nvidia-smi not found. NVIDIA monitoring requires NVIDIA drivers (not installable via apt)."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#region node-info wizard
|
||||||
|
configure_node_info() {
|
||||||
|
# Initialize all variables to off
|
||||||
|
LM_SENSORS_ENABLED=0
|
||||||
|
ENABLE_CPU=0; CPU_TEMP_TARGET="Core"
|
||||||
|
ENABLE_RAM_TEMP=0; ENABLE_HDD_TEMP=0; ENABLE_NVME_TEMP=0
|
||||||
|
ENABLE_FAN_SPEED=0; DISPLAY_ZERO_SPEED_FANS=0; TEMP_UNIT="C"
|
||||||
|
ENABLE_INTEL_GPU_INFO=0; ENABLE_NVIDIA_GPU_INFO=0; ENABLE_AMD_GPU_INFO=0
|
||||||
|
ENABLE_GPU_HISTORY=0
|
||||||
|
ENABLE_UPS=0; UPS_DEVICE_NAME="ups@localhost"
|
||||||
|
ENABLE_SYSTEM_INFO=0; SYSTEM_INFO_TYPE=1
|
||||||
|
|
||||||
|
local lm_sensors_ok=false
|
||||||
|
local sensors_detected=false
|
||||||
|
|
||||||
|
if [[ "$DEBUG_LM_SENSORS" -eq 1 && -f "$DEBUG_LM_SENSORS_FILE" ]]; then
|
||||||
|
info "[debug] Using sensor data from $DEBUG_LM_SENSORS_FILE"
|
||||||
|
lm_sensors_ok=true; LM_SENSORS_ENABLED=1
|
||||||
|
else
|
||||||
|
_check_or_install_tool sensors lm-sensors "lm-sensors" && lm_sensors_ok=true && LM_SENSORS_ENABLED=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$lm_sensors_ok" == true ]]; then
|
||||||
|
local sensorsOutput sanitisedSensorsOutput
|
||||||
|
if [[ "$DEBUG_LM_SENSORS" -eq 1 ]]; then
|
||||||
|
sensorsOutput=$(cat "$DEBUG_LM_SENSORS_FILE")
|
||||||
|
else
|
||||||
|
sensorsOutput=$(sensors -j 2>/dev/null)
|
||||||
|
fi
|
||||||
|
sanitisedSensorsOutput=$(sanitize_sensors_output "$sensorsOutput")
|
||||||
|
|
||||||
|
#region CPU
|
||||||
|
msgb "\n=== Detecting CPU temperature sensors ==="
|
||||||
|
local cpuList="" cpuCount=0
|
||||||
|
for pattern in "${KNOWN_CPU_SENSORS[@]}"; do
|
||||||
|
local found_cpus
|
||||||
|
found_cpus=$(echo "$sanitisedSensorsOutput" | grep -o "\"${pattern}[^\"]*\"" || true | sed 's/"//g')
|
||||||
|
if [[ -n "$found_cpus" ]]; then
|
||||||
|
while read -r sensor; do
|
||||||
|
[[ -z "$sensor" ]] && continue
|
||||||
|
cpuCount=$((cpuCount + 1))
|
||||||
|
cpuList="${cpuList:+$cpuList,}$sensor"
|
||||||
|
ENABLE_CPU=1
|
||||||
|
done <<< "$found_cpus"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ "$ENABLE_CPU" -eq 1 ]]; then
|
||||||
|
info "Detected CPU sensors ($cpuCount): $cpuList"
|
||||||
|
sensors_detected=true
|
||||||
|
while true; do
|
||||||
|
local choice
|
||||||
|
choice=$(ask "Display temperatures for all cores [C] or average per CPU [a]? (C/a)")
|
||||||
|
case "$choice" in
|
||||||
|
[cC]|"") CPU_TEMP_TARGET="Core"; info "Showing per-core temperatures."; break ;;
|
||||||
|
[aA]) CPU_TEMP_TARGET="Package"; info "Showing average per-CPU temperature."; break ;;
|
||||||
|
*) warn "Invalid input, choose C or a." ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
else
|
||||||
|
warn "No CPU temperature sensors found."
|
||||||
|
fi
|
||||||
|
#endregion CPU
|
||||||
|
|
||||||
|
#region RAM
|
||||||
|
msgb "\n=== Detecting RAM temperature sensors ==="
|
||||||
|
local ramCount
|
||||||
|
ramCount=$(grep -c '"SODIMM[^"]*"' <<<"$sanitisedSensorsOutput" || true)
|
||||||
|
if [[ "$ramCount" -gt 0 ]]; then
|
||||||
|
info "Detected $ramCount RAM sensor(s)."
|
||||||
|
ENABLE_RAM_TEMP=1; sensors_detected=true
|
||||||
|
else
|
||||||
|
warn "No RAM temperature sensors found."
|
||||||
|
fi
|
||||||
|
#endregion RAM
|
||||||
|
|
||||||
|
#region HDD/SSD
|
||||||
|
msgb "\n=== Detecting HDD/SSD temperature sensors ==="
|
||||||
|
local hddList
|
||||||
|
hddList=$(echo "$sanitisedSensorsOutput" | grep -o '"drivetemp-scsi[^"]*"' | sed 's/"//g' | wc -l || true)
|
||||||
|
if [[ "$hddList" -gt 0 ]]; then
|
||||||
|
info "Detected $hddList HDD/SSD sensor(s)."
|
||||||
|
ENABLE_HDD_TEMP=1; sensors_detected=true
|
||||||
|
else
|
||||||
|
warn "No HDD/SSD temperature sensors found. (Requires kernel module 'drivetemp'.)"
|
||||||
|
fi
|
||||||
|
#endregion HDD/SSD
|
||||||
|
|
||||||
|
#region NVMe
|
||||||
|
msgb "\n=== Detecting NVMe temperature sensors ==="
|
||||||
|
local nvmeCount
|
||||||
|
nvmeCount=$(echo "$sanitisedSensorsOutput" | grep -c '"nvme[^"]*"' || true)
|
||||||
|
if [[ "$nvmeCount" -gt 0 ]]; then
|
||||||
|
info "Detected $nvmeCount NVMe sensor(s)."
|
||||||
|
ENABLE_NVME_TEMP=1; sensors_detected=true
|
||||||
|
else
|
||||||
|
warn "No NVMe temperature sensors found."
|
||||||
|
fi
|
||||||
|
#endregion NVMe
|
||||||
|
|
||||||
|
#region Fans
|
||||||
|
msgb "\n=== Detecting fan speed sensors ==="
|
||||||
|
local fanCount
|
||||||
|
fanCount=$(grep -c 'fan[0-9]\+_input' <<<"$sanitisedSensorsOutput" || true)
|
||||||
|
if [[ "$fanCount" -gt 0 ]]; then
|
||||||
|
info "Detected $fanCount fan speed reading(s)."
|
||||||
|
ENABLE_FAN_SPEED=1; sensors_detected=true
|
||||||
|
local choice
|
||||||
|
choice=$(ask "Display fans reporting zero speed? (Y/n)")
|
||||||
|
case "$choice" in
|
||||||
|
[nN]) DISPLAY_ZERO_SPEED_FANS=0; info "Zero-speed fans will be hidden." ;;
|
||||||
|
*) DISPLAY_ZERO_SPEED_FANS=1; info "Zero-speed fans will be shown." ;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
warn "No fan speed sensors found."
|
||||||
|
fi
|
||||||
|
#endregion Fans
|
||||||
|
|
||||||
|
#region Temperature unit
|
||||||
|
if [[ "$sensors_detected" == true ]]; then
|
||||||
|
msgb "\n=== Temperature unit ==="
|
||||||
|
local unit
|
||||||
|
unit=$(ask "Display temperatures in Celsius [C] or Fahrenheit [f]? (C/f)")
|
||||||
|
case "$unit" in
|
||||||
|
[fF]) TEMP_UNIT="F"; info "Using Fahrenheit." ;;
|
||||||
|
*) TEMP_UNIT="C"; info "Using Celsius." ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
#endregion Temperature unit
|
||||||
|
fi
|
||||||
|
|
||||||
|
#region Intel GPU
|
||||||
|
msgb "\n=== Detecting Intel GPU ==="
|
||||||
|
local intelCards=""
|
||||||
|
if [[ "$DEBUG_INTEL" -eq 1 && -f "$DEBUG_INTEL_FILE" ]]; then
|
||||||
|
info "[debug] Using Intel GPU data from $DEBUG_INTEL_FILE"
|
||||||
|
intelCards=$(cat "$DEBUG_INTEL_FILE")
|
||||||
|
if [[ -n "$intelCards" ]]; then
|
||||||
|
info "Intel GPU(s) detected (debug):"
|
||||||
|
echo "$intelCards" | while IFS= read -r line; do echo " $line"; done
|
||||||
|
ENABLE_INTEL_GPU_INFO=1
|
||||||
|
else
|
||||||
|
warn "No Intel GPUs in debug file."
|
||||||
|
fi
|
||||||
|
elif _check_or_install_tool intel_gpu_top igt-gpu-tools "Intel GPU tools (igt-gpu-tools)"; then
|
||||||
|
intelCards=$(intel_gpu_top -L 2>/dev/null | grep -E '^card[0-9]+' || true)
|
||||||
|
if [[ -n "$intelCards" ]]; then
|
||||||
|
info "Intel GPU(s) detected:"
|
||||||
|
echo "$intelCards" | while IFS= read -r line; do echo " $line"; done
|
||||||
|
ENABLE_INTEL_GPU_INFO=1
|
||||||
|
else
|
||||||
|
warn "No Intel GPUs detected by intel_gpu_top."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
#endregion Intel GPU
|
||||||
|
|
||||||
|
#region NVIDIA GPU
|
||||||
|
msgb "\n=== Detecting NVIDIA GPU ==="
|
||||||
|
if [[ "$DEBUG_NVIDIA" -eq 1 && -f "$DEBUG_NVIDIA_DEVICES_FILE" ]]; then
|
||||||
|
info "[debug] Using NVIDIA GPU data from $DEBUG_NVIDIA_DEVICES_FILE"
|
||||||
|
local nvidiaCards
|
||||||
|
nvidiaCards=$(cat "$DEBUG_NVIDIA_DEVICES_FILE")
|
||||||
|
if [[ -n "$nvidiaCards" ]]; then
|
||||||
|
info "NVIDIA GPU(s) detected (debug):"
|
||||||
|
echo "$nvidiaCards" | while IFS= read -r line; do echo " $line"; done
|
||||||
|
ENABLE_NVIDIA_GPU_INFO=1
|
||||||
|
else
|
||||||
|
warn "No NVIDIA GPUs in debug file."
|
||||||
|
fi
|
||||||
|
elif _check_nvidia_tool; then
|
||||||
|
local nvidiaCards
|
||||||
|
nvidiaCards=$(nvidia-smi -L 2>/dev/null || true)
|
||||||
|
if [[ -n "$nvidiaCards" ]]; then
|
||||||
|
info "NVIDIA GPU(s) detected:"
|
||||||
|
echo "$nvidiaCards" | while IFS= read -r line; do echo " $line"; done
|
||||||
|
ENABLE_NVIDIA_GPU_INFO=1
|
||||||
|
else
|
||||||
|
warn "No NVIDIA GPUs detected by nvidia-smi."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
#endregion NVIDIA GPU
|
||||||
|
|
||||||
|
#region AMD GPU (placeholder)
|
||||||
|
ENABLE_AMD_GPU_INFO=0
|
||||||
|
#endregion AMD GPU
|
||||||
|
|
||||||
|
#region GPU history
|
||||||
|
if [[ "$ENABLE_INTEL_GPU_INFO" -eq 1 || "$ENABLE_NVIDIA_GPU_INFO" -eq 1 ]]; then
|
||||||
|
msgb "\n=== GPU Historical Data ==="
|
||||||
|
local choice
|
||||||
|
choice=$(ask "Store historical GPU data for graphs? (y/N)")
|
||||||
|
case "$choice" in
|
||||||
|
[yY]) ENABLE_GPU_HISTORY=1; info "Historical GPU data will be stored." ;;
|
||||||
|
*) info "Historical GPU data disabled." ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
#endregion GPU history
|
||||||
|
|
||||||
|
#region UPS
|
||||||
|
msgb "\n=== UPS Information ==="
|
||||||
|
local choiceUPS
|
||||||
|
choiceUPS=$(ask "Enable UPS information? (y/N)")
|
||||||
|
case "$choiceUPS" in
|
||||||
|
[yY])
|
||||||
|
local upsConn modelName upsOutput
|
||||||
|
upsConn=$(ask "Enter UPS connection string (e.g. upsname@hostname[:port])")
|
||||||
|
if [[ "$DEBUG_UPS" -eq 1 && -f "$DEBUG_UPS_FILE" ]]; then
|
||||||
|
info "[debug] Using UPS data from $DEBUG_UPS_FILE"
|
||||||
|
upsOutput=$(cat "$DEBUG_UPS_FILE")
|
||||||
|
else
|
||||||
|
if ! command -v upsc &>/dev/null; then
|
||||||
|
err "'upsc' is not available. Install 'nut-client' first."
|
||||||
|
fi
|
||||||
|
upsOutput=$(upsc "$upsConn" 2>&1)
|
||||||
|
fi
|
||||||
|
if echo "$upsOutput" | grep -q "device.model:"; then
|
||||||
|
modelName=$(echo "$upsOutput" | grep "device.model:" | cut -d: -f2- | xargs)
|
||||||
|
ENABLE_UPS=1
|
||||||
|
UPS_DEVICE_NAME="$upsConn"
|
||||||
|
info "Connected to UPS: $modelName at $upsConn"
|
||||||
|
else
|
||||||
|
warn "Could not connect to UPS at '$upsConn'. UPS info will be disabled."
|
||||||
|
ENABLE_UPS=0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*) info "UPS information disabled." ;;
|
||||||
|
esac
|
||||||
|
#endregion UPS
|
||||||
|
|
||||||
|
#region System info
|
||||||
|
msgb "\n=== System Information ==="
|
||||||
|
echo " type 1) System information (manufacturer, product, serial)"
|
||||||
|
dmidecode -t 1 2>/dev/null | awk -F': ' '/Manufacturer|Product Name|Serial Number/ {print " "$0}' || true
|
||||||
|
echo " type 2) Baseboard/Motherboard information"
|
||||||
|
dmidecode -t 2 2>/dev/null | awk -F': ' '/Manufacturer|Product Name|Serial Number/ {print " "$0}' || true
|
||||||
|
|
||||||
|
local choiceSys
|
||||||
|
choiceSys=$(ask "Enable system information? (1/2/n)")
|
||||||
|
case "$choiceSys" in
|
||||||
|
1|"") ENABLE_SYSTEM_INFO=1; SYSTEM_INFO_TYPE=1; info "System information (type 1) will be shown." ;;
|
||||||
|
2) ENABLE_SYSTEM_INFO=1; SYSTEM_INFO_TYPE=2; info "Baseboard information (type 2) will be shown." ;;
|
||||||
|
[nN]) info "System information disabled." ;;
|
||||||
|
*) warn "Invalid selection. Defaulting to type 1."; ENABLE_SYSTEM_INFO=1; SYSTEM_INFO_TYPE=1 ;;
|
||||||
|
esac
|
||||||
|
#endregion System info
|
||||||
|
}
|
||||||
|
#endregion node-info wizard
|
||||||
|
|
||||||
|
#region write config
|
||||||
|
write_config() {
|
||||||
|
mkdir -p "$(dirname "$CONF_FILE")"
|
||||||
|
cat > "$CONF_FILE" <<EOF
|
||||||
|
# pve-mod configuration file
|
||||||
|
# Managed by pve-mod-configure. Re-run to update.
|
||||||
|
|
||||||
|
[modules]
|
||||||
|
node_info=${MOD_NODE_INFO}
|
||||||
|
nag_screen=${MOD_NAG_SCREEN}
|
||||||
|
|
||||||
|
[gpu]
|
||||||
|
intel_enabled=${ENABLE_INTEL_GPU_INFO}
|
||||||
|
nvidia_enabled=${ENABLE_NVIDIA_GPU_INFO}
|
||||||
|
amd_enabled=${ENABLE_AMD_GPU_INFO}
|
||||||
|
gpu_history=${ENABLE_GPU_HISTORY}
|
||||||
|
|
||||||
|
[lm_sensors]
|
||||||
|
enabled=${LM_SENSORS_ENABLED}
|
||||||
|
enable_cpu=${ENABLE_CPU}
|
||||||
|
cpu_temp_target=${CPU_TEMP_TARGET}
|
||||||
|
enable_ram_temp=${ENABLE_RAM_TEMP}
|
||||||
|
enable_hdd_temp=${ENABLE_HDD_TEMP}
|
||||||
|
enable_nvme_temp=${ENABLE_NVME_TEMP}
|
||||||
|
enable_fan_speed=${ENABLE_FAN_SPEED}
|
||||||
|
display_zero_speed_fans=${DISPLAY_ZERO_SPEED_FANS}
|
||||||
|
temp_unit=${TEMP_UNIT}
|
||||||
|
|
||||||
|
[ups]
|
||||||
|
enabled=${ENABLE_UPS}
|
||||||
|
device_name=${UPS_DEVICE_NAME}
|
||||||
|
|
||||||
|
[system_info]
|
||||||
|
enabled=${ENABLE_SYSTEM_INFO}
|
||||||
|
type=${SYSTEM_INFO_TYPE}
|
||||||
|
|
||||||
|
[pve_trigger]
|
||||||
|
enabled=${PVE_TRIGGER_ENABLED}
|
||||||
|
|
||||||
|
[service]
|
||||||
|
mode=embedded
|
||||||
|
|
||||||
|
# Debug mode: when a collector's mode is 1, the real tool is not required.
|
||||||
|
# Data is read from the file path instead. Useful for development/testing.
|
||||||
|
[debug]
|
||||||
|
lm_sensors_mode=${DEBUG_LM_SENSORS}
|
||||||
|
lm_sensors_output_file=${DEBUG_LM_SENSORS_FILE}
|
||||||
|
intel_mode=${DEBUG_INTEL}
|
||||||
|
intel_devices_file=${DEBUG_INTEL_FILE}
|
||||||
|
nvidia_mode=${DEBUG_NVIDIA}
|
||||||
|
nvidia_devices_file=${DEBUG_NVIDIA_DEVICES_FILE}
|
||||||
|
nvidia_output_file=${DEBUG_NVIDIA_OUTPUT_FILE}
|
||||||
|
amd_mode=${DEBUG_AMD}
|
||||||
|
amd_devices_file=${DEBUG_AMD_FILE}
|
||||||
|
ups_mode=${DEBUG_UPS}
|
||||||
|
ups_output_file=${DEBUG_UPS_FILE}
|
||||||
|
log_enabled=${DEBUG_LOG}
|
||||||
|
log_file=${DEBUG_LOG_FILE}
|
||||||
|
EOF
|
||||||
|
info "Configuration saved to $CONF_FILE"
|
||||||
|
}
|
||||||
|
#endregion write config
|
||||||
|
|
||||||
|
#region debug wizard
|
||||||
|
configure_debug() {
|
||||||
|
msgb "\n=== Debug / Development Mode ==="
|
||||||
|
echo "Debug mode substitutes real tools with pre-captured data files."
|
||||||
|
echo "When a collector's mode is enabled the tool (sensors/intel_gpu_top/etc.) is not required."
|
||||||
|
local choice
|
||||||
|
choice=$(ask "Configure debug file overrides? (y/N)")
|
||||||
|
[[ "$choice" != [yY] ]] && return
|
||||||
|
|
||||||
|
local newpath
|
||||||
|
|
||||||
|
choice=$(ask "Enable LM-Sensors debug? (y/N)")
|
||||||
|
if [[ "$choice" == [yY] ]]; then
|
||||||
|
DEBUG_LM_SENSORS=1
|
||||||
|
newpath=$(ask "Path to sensors JSON file [${DEBUG_LM_SENSORS_FILE}]")
|
||||||
|
DEBUG_LM_SENSORS_FILE="${newpath:-$DEBUG_LM_SENSORS_FILE}"
|
||||||
|
info "LM-Sensors debug: ${DEBUG_LM_SENSORS_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
choice=$(ask "Enable Intel GPU debug? (y/N)")
|
||||||
|
if [[ "$choice" == [yY] ]]; then
|
||||||
|
DEBUG_INTEL=1
|
||||||
|
newpath=$(ask "Path to Intel GPU devices JSON [${DEBUG_INTEL_FILE}]")
|
||||||
|
DEBUG_INTEL_FILE="${newpath:-$DEBUG_INTEL_FILE}"
|
||||||
|
info "Intel GPU debug: ${DEBUG_INTEL_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
choice=$(ask "Enable NVIDIA GPU debug? (y/N)")
|
||||||
|
if [[ "$choice" == [yY] ]]; then
|
||||||
|
DEBUG_NVIDIA=1
|
||||||
|
newpath=$(ask "Path to nvidia-smi output CSV [${DEBUG_NVIDIA_OUTPUT_FILE}]")
|
||||||
|
DEBUG_NVIDIA_OUTPUT_FILE="${newpath:-$DEBUG_NVIDIA_OUTPUT_FILE}"
|
||||||
|
newpath=$(ask "Path to nvidia-smi devices CSV [${DEBUG_NVIDIA_DEVICES_FILE}]")
|
||||||
|
DEBUG_NVIDIA_DEVICES_FILE="${newpath:-$DEBUG_NVIDIA_DEVICES_FILE}"
|
||||||
|
info "NVIDIA GPU debug: output=${DEBUG_NVIDIA_OUTPUT_FILE} devices=${DEBUG_NVIDIA_DEVICES_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
choice=$(ask "Enable AMD GPU debug? (y/N)")
|
||||||
|
if [[ "$choice" == [yY] ]]; then
|
||||||
|
DEBUG_AMD=1
|
||||||
|
newpath=$(ask "Path to AMD GPU devices JSON [${DEBUG_AMD_FILE}]")
|
||||||
|
DEBUG_AMD_FILE="${newpath:-$DEBUG_AMD_FILE}"
|
||||||
|
info "AMD GPU debug: ${DEBUG_AMD_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
choice=$(ask "Enable UPS debug? (y/N)")
|
||||||
|
if [[ "$choice" == [yY] ]]; then
|
||||||
|
DEBUG_UPS=1
|
||||||
|
newpath=$(ask "Path to UPS output JSON [${DEBUG_UPS_FILE}]")
|
||||||
|
DEBUG_UPS_FILE="${newpath:-$DEBUG_UPS_FILE}"
|
||||||
|
info "UPS debug: ${DEBUG_UPS_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
choice=$(ask "Enable debug logging? (y/N)")
|
||||||
|
if [[ "$choice" == [yY] ]]; then
|
||||||
|
DEBUG_LOG=1
|
||||||
|
newpath=$(ask "Log file path [${DEBUG_LOG_FILE}]")
|
||||||
|
DEBUG_LOG_FILE="${newpath:-$DEBUG_LOG_FILE}"
|
||||||
|
info "Debug log: ${DEBUG_LOG_FILE}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
#endregion debug wizard
|
||||||
|
|
||||||
|
main() {
|
||||||
|
# ── Welcome banner ────────────────────────────────────────────────────────
|
||||||
|
local _version
|
||||||
|
_version=$(dpkg-query -W -f='${Version}' pve-mod 2>/dev/null || echo "unknown")
|
||||||
|
echo -e "\e[1;34m"
|
||||||
|
echo " ██████╗ ██╗ ██╗███████╗ ███╗ ███╗ ██████╗ ██████╗ ███████╗"
|
||||||
|
echo " ██╔══██╗██║ ██║██╔════╝ ████╗ ████║██╔═══██╗██╔══██╗██╔════╝"
|
||||||
|
echo " ██████╔╝██║ ██║█████╗ ██╔████╔██║██║ ██║██║ ██║███████╗"
|
||||||
|
echo " ██╔═══╝ ╚██╗ ██╔╝██╔══╝ ██║╚██╔╝██║██║ ██║██║ ██║╚════██║"
|
||||||
|
echo " ██║ ╚████╔╝ ███████╗ ██║ ╚═╝ ██║╚██████╔╝██████╔╝███████║"
|
||||||
|
echo " ╚═╝ ╚═══╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝"
|
||||||
|
echo -e "\e[0m"
|
||||||
|
echo -e "\e[1m PVE-mods Configurator \e[0;36mv${_version}\e[0m"
|
||||||
|
echo -e " Proxmox VE enhancement suite — hardware monitoring, GPU info & more\n"
|
||||||
|
|
||||||
|
# ── Root check ────────────────────────────────────────────────────────────
|
||||||
|
[[ $EUID -eq 0 ]] || err "This script must be run as root."
|
||||||
|
|
||||||
|
# ── Prerequisite check ────────────────────────────────────────────────────
|
||||||
|
if [[ ! -x "$APPLY_PATCHES" ]]; then
|
||||||
|
err "pve-mod is not installed (cannot find $APPLY_PATCHES).\nInstall it first: dpkg -i pve-mod_*.deb"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Legacy installation check ─────────────────────────────────────────────
|
||||||
|
if grep -qF '$res->{sensorsJSONOutput}' "$NODES_PM" 2>/dev/null || \
|
||||||
|
grep -qF '$res->{systemInfo}' "$NODES_PM" 2>/dev/null; then
|
||||||
|
err "A legacy pve-mod bash-script installation was detected in Nodes.pm.\nRemove it first:\n bash /path/to/pve-mod-gui-sensors.sh uninstall"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Existing config notice ────────────────────────────────────────────────
|
||||||
|
if [[ -f "$CONF_FILE" ]]; then
|
||||||
|
local choice
|
||||||
|
choice=$(ask "Existing configuration found at $CONF_FILE — Reconfigure? (Y/n)")
|
||||||
|
case "$choice" in
|
||||||
|
[nN]) info "Keeping existing configuration."; exit 0 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Initialize all config variables with safe defaults ────────────────────
|
||||||
|
MOD_NODE_INFO=0; MOD_NAG_SCREEN=0
|
||||||
|
LM_SENSORS_ENABLED=0
|
||||||
|
ENABLE_CPU=0; CPU_TEMP_TARGET="Core"
|
||||||
|
ENABLE_RAM_TEMP=0; ENABLE_HDD_TEMP=0; ENABLE_NVME_TEMP=0
|
||||||
|
ENABLE_FAN_SPEED=0; DISPLAY_ZERO_SPEED_FANS=0; TEMP_UNIT="C"
|
||||||
|
ENABLE_INTEL_GPU_INFO=0; ENABLE_NVIDIA_GPU_INFO=0; ENABLE_AMD_GPU_INFO=0
|
||||||
|
ENABLE_GPU_HISTORY=0
|
||||||
|
ENABLE_UPS=0; UPS_DEVICE_NAME="ups@localhost"
|
||||||
|
ENABLE_SYSTEM_INFO=0; SYSTEM_INFO_TYPE=1
|
||||||
|
PVE_TRIGGER_ENABLED=0
|
||||||
|
DEBUG_LM_SENSORS=0; DEBUG_LM_SENSORS_FILE="/tmp/sensors-output.json"
|
||||||
|
DEBUG_INTEL=0; DEBUG_INTEL_FILE="/tmp/intel-gpu-devices.json"
|
||||||
|
DEBUG_NVIDIA=0; DEBUG_NVIDIA_OUTPUT_FILE="/tmp/nvidia-smi-output.csv"
|
||||||
|
DEBUG_NVIDIA_DEVICES_FILE="/tmp/nvidia-smi-devices.csv"
|
||||||
|
DEBUG_AMD=0; DEBUG_AMD_FILE="/tmp/amd-gpu-devices.json"
|
||||||
|
DEBUG_UPS=0; DEBUG_UPS_FILE="/tmp/ups-output.json"
|
||||||
|
DEBUG_LOG=0; DEBUG_LOG_FILE="/tmp/pve-mod-debug.log"
|
||||||
|
_load_conf_debug
|
||||||
|
|
||||||
|
# ── Module selection ──────────────────────────────────────────────────────
|
||||||
|
msgb "\n=== pve-mod Module Selection ==="
|
||||||
|
echo "Available modules:"
|
||||||
|
echo " [1] Node Info — sensor readings, GPU stats, UPS, system information"
|
||||||
|
echo " [2] Nag Screen — remove Proxmox subscription nag screen"
|
||||||
|
echo " [3] Both"
|
||||||
|
echo " [n] None / cancel"
|
||||||
|
local modChoice
|
||||||
|
modChoice=$(ask "Select modules to enable (1/2/3/n)")
|
||||||
|
case "$modChoice" in
|
||||||
|
1) MOD_NODE_INFO=1 ;;
|
||||||
|
2) MOD_NAG_SCREEN=1 ;;
|
||||||
|
3) MOD_NODE_INFO=1; MOD_NAG_SCREEN=1 ;;
|
||||||
|
[nN]) info "No modules selected. Exiting."; exit 0 ;;
|
||||||
|
*) warn "Invalid selection. Defaulting to Node Info only."; MOD_NODE_INFO=1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# ── Per-module wizards ────────────────────────────────────────────────────
|
||||||
|
if [[ "$MOD_NODE_INFO" -eq 1 ]]; then
|
||||||
|
msgb "\n=== Node Info Configuration ==="
|
||||||
|
configure_node_info
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$MOD_NAG_SCREEN" -eq 1 ]]; then
|
||||||
|
msgb "\n=== Nag Screen ==="
|
||||||
|
info "Subscription nag screen removal will be applied."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Debug mode ────────────────────────────────────────────────────────────
|
||||||
|
configure_debug
|
||||||
|
|
||||||
|
# ── Re-patch on PVE upgrade ───────────────────────────────────────────────
|
||||||
|
msgb "\n=== Auto Re-patching on PVE Upgrade ==="
|
||||||
|
echo "pve-mod registers a dpkg trigger on pve-manager. When pve-manager is"
|
||||||
|
echo "upgraded, the trigger fires and can automatically re-apply patches."
|
||||||
|
local triggerChoice
|
||||||
|
triggerChoice=$(ask "Re-apply patches automatically when pve-manager upgrades? (y/N)")
|
||||||
|
case "$triggerChoice" in
|
||||||
|
[yY]) PVE_TRIGGER_ENABLED=1; info "Auto re-patching enabled." ;;
|
||||||
|
*) PVE_TRIGGER_ENABLED=0; info "Auto re-patching disabled. Run 'pve-mod-configure' after a PVE upgrade." ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# ── Write config and apply ────────────────────────────────────────────────
|
||||||
|
write_config
|
||||||
|
|
||||||
|
msgb "\n=== Applying patches ==="
|
||||||
|
"$APPLY_PATCHES"
|
||||||
|
|
||||||
|
msgb "\n=== Done ==="
|
||||||
|
info "pve-mod is configured and active."
|
||||||
|
info "Clear your browser cache to see the changes."
|
||||||
|
}
|
||||||
|
|
||||||
|
main
|
||||||
55
src/pve-mod.conf
Normal file
55
src/pve-mod.conf
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# pve-mod configuration file
|
||||||
|
# Run 'pve-mod-configure' to set values interactively.
|
||||||
|
# All flags are 0 (disabled) by default; pve-mod-configure enables them.
|
||||||
|
|
||||||
|
[modules]
|
||||||
|
node_info=0
|
||||||
|
nag_screen=0
|
||||||
|
|
||||||
|
[gpu]
|
||||||
|
intel_enabled=0
|
||||||
|
nvidia_enabled=0
|
||||||
|
amd_enabled=0
|
||||||
|
gpu_history=0
|
||||||
|
|
||||||
|
[lm_sensors]
|
||||||
|
enabled=0
|
||||||
|
enable_cpu=0
|
||||||
|
cpu_temp_target=Core
|
||||||
|
enable_ram_temp=0
|
||||||
|
enable_hdd_temp=0
|
||||||
|
enable_nvme_temp=0
|
||||||
|
enable_fan_speed=0
|
||||||
|
display_zero_speed_fans=0
|
||||||
|
temp_unit=C
|
||||||
|
|
||||||
|
[ups]
|
||||||
|
enabled=0
|
||||||
|
device_name=ups@localhost
|
||||||
|
|
||||||
|
[system_info]
|
||||||
|
enabled=0
|
||||||
|
type=1
|
||||||
|
|
||||||
|
[pve_trigger]
|
||||||
|
enabled=0
|
||||||
|
|
||||||
|
[service]
|
||||||
|
mode=embedded
|
||||||
|
|
||||||
|
# Debug mode: when a mode is 1, the real tool is not required.
|
||||||
|
# Data is read from the file path instead. Useful for development/testing.
|
||||||
|
[debug]
|
||||||
|
lm_sensors_mode=0
|
||||||
|
lm_sensors_output_file=/tmp/sensors-output.json
|
||||||
|
intel_mode=0
|
||||||
|
intel_devices_file=/tmp/intel-gpu-devices.json
|
||||||
|
nvidia_mode=0
|
||||||
|
nvidia_devices_file=/tmp/nvidia-smi-devices.csv
|
||||||
|
nvidia_output_file=/tmp/nvidia-smi-output.csv
|
||||||
|
amd_mode=0
|
||||||
|
amd_devices_file=/tmp/amd-gpu-devices.json
|
||||||
|
ups_mode=0
|
||||||
|
ups_output_file=/tmp/ups-output.json
|
||||||
|
log_enabled=0
|
||||||
|
log_file=/tmp/pve-mod-debug.log
|
||||||
1
src/test.yml
Normal file
1
src/test.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
trigger workflow test 1
|
||||||
Loading…
Reference in New Issue
Block a user