mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-11-17 19:16:25 +00:00
Merge branch 'MacRimi:main' into main
This commit is contained in:
81
.github/workflows/build-appimage-manual.yml
vendored
Normal file
81
.github/workflows/build-appimage-manual.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
name: Build ProxMenux Monitor AppImage
|
||||||
|
|
||||||
|
on:
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
working-directory: AppImage
|
||||||
|
run: npm install --legacy-peer-deps
|
||||||
|
|
||||||
|
- name: Build Next.js app
|
||||||
|
working-directory: AppImage
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Install Python dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y python3 python3-pip python3-venv
|
||||||
|
|
||||||
|
- name: Make build script executable
|
||||||
|
working-directory: AppImage
|
||||||
|
run: chmod +x scripts/build_appimage.sh
|
||||||
|
|
||||||
|
- name: Build AppImage
|
||||||
|
working-directory: AppImage
|
||||||
|
run: ./scripts/build_appimage.sh
|
||||||
|
|
||||||
|
- name: Get version from package.json
|
||||||
|
id: version
|
||||||
|
working-directory: AppImage
|
||||||
|
run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Upload AppImage artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ProxMenux-${{ steps.version.outputs.VERSION }}-AppImage
|
||||||
|
path: AppImage/dist/*.AppImage
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Generate SHA256 checksum
|
||||||
|
run: |
|
||||||
|
cd AppImage/dist
|
||||||
|
sha256sum *.AppImage > ProxMenux-Monitor.AppImage.sha256
|
||||||
|
echo "Generated SHA256:"
|
||||||
|
cat ProxMenux-Monitor.AppImage.sha256
|
||||||
|
|
||||||
|
- name: Upload AppImage and checksum to /AppImage folder in main
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
git config --global user.name "github-actions[bot]"
|
||||||
|
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
git fetch origin main
|
||||||
|
git checkout main
|
||||||
|
|
||||||
|
rm -f AppImage/*.AppImage AppImage/*.sha256 || true
|
||||||
|
|
||||||
|
# Copy new files
|
||||||
|
cp AppImage/dist/*.AppImage AppImage/
|
||||||
|
cp AppImage/dist/ProxMenux-Monitor.AppImage.sha256 AppImage/
|
||||||
|
|
||||||
|
git add AppImage/*.AppImage AppImage/*.sha256
|
||||||
|
git commit -m "Update AppImage build ($(date +'%Y-%m-%d %H:%M:%S'))" || echo "No changes to commit"
|
||||||
|
git push origin main
|
||||||
32
.github/workflows/build-appimage.yml
vendored
32
.github/workflows/build-appimage.yml
vendored
@@ -8,10 +8,7 @@ on:
|
|||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
paths: [ 'AppImage/**' ]
|
paths: [ 'AppImage/**' ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
@@ -57,30 +54,3 @@ jobs:
|
|||||||
name: ProxMenux-${{ steps.version.outputs.VERSION }}-AppImage
|
name: ProxMenux-${{ steps.version.outputs.VERSION }}-AppImage
|
||||||
path: AppImage/dist/*.AppImage
|
path: AppImage/dist/*.AppImage
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
- name: Generate SHA256 checksum
|
|
||||||
run: |
|
|
||||||
cd AppImage/dist
|
|
||||||
sha256sum *.AppImage > ProxMenux-Monitor.AppImage.sha256
|
|
||||||
echo "Generated SHA256:"
|
|
||||||
cat ProxMenux-Monitor.AppImage.sha256
|
|
||||||
|
|
||||||
- name: Upload AppImage and checksum to /AppImage folder in main
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
git config --global user.name "github-actions[bot]"
|
|
||||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
|
|
||||||
git fetch origin main
|
|
||||||
git checkout main
|
|
||||||
|
|
||||||
rm -f AppImage/*.AppImage AppImage/*.sha256 || true
|
|
||||||
|
|
||||||
# Copy new files
|
|
||||||
cp AppImage/dist/*.AppImage AppImage/
|
|
||||||
cp AppImage/dist/ProxMenux-Monitor.AppImage.sha256 AppImage/
|
|
||||||
|
|
||||||
git add AppImage/*.AppImage AppImage/*.sha256
|
|
||||||
git commit -m "Update AppImage build ($(date +'%Y-%m-%d %H:%M:%S'))" || echo "No changes to commit"
|
|
||||||
git push origin main
|
|
||||||
|
|||||||
@@ -188,7 +188,12 @@ export default function Hardware() {
|
|||||||
|
|
||||||
const fetchRealtimeData = async () => {
|
const fetchRealtimeData = async () => {
|
||||||
try {
|
try {
|
||||||
const apiUrl = `http://${window.location.hostname}:8008/api/gpu/${fullSlot}/realtime`
|
const { protocol, hostname, port } = window.location
|
||||||
|
const isStandardPort = port === "" || port === "80" || port === "443"
|
||||||
|
|
||||||
|
const apiUrl = isStandardPort
|
||||||
|
? `/api/gpu/${fullSlot}/realtime`
|
||||||
|
: `${protocol}//${hostname}:8008/api/gpu/${fullSlot}/realtime`
|
||||||
|
|
||||||
const response = await fetch(apiUrl, {
|
const response = await fetch(apiUrl, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@@ -1783,13 +1788,15 @@ export default function Hardware() {
|
|||||||
{selectedDisk.rotation_rate !== undefined && selectedDisk.rotation_rate !== null && (
|
{selectedDisk.rotation_rate !== undefined && selectedDisk.rotation_rate !== null && (
|
||||||
<div className="flex justify-between border-b border-border/50 pb-2">
|
<div className="flex justify-between border-b border-border/50 pb-2">
|
||||||
<span className="text-sm font-medium text-muted-foreground">Rotation Rate</span>
|
<span className="text-sm font-medium text-muted-foreground">Rotation Rate</span>
|
||||||
<span className="text-sm">
|
<div className="text-sm">
|
||||||
{typeof selectedDisk.rotation_rate === "number" && selectedDisk.rotation_rate > 0
|
{typeof selectedDisk.rotation_rate === "number" && selectedDisk.rotation_rate === -1
|
||||||
? `${selectedDisk.rotation_rate} rpm`
|
? "N/A"
|
||||||
: typeof selectedDisk.rotation_rate === "string"
|
: typeof selectedDisk.rotation_rate === "number" && selectedDisk.rotation_rate > 0
|
||||||
? selectedDisk.rotation_rate
|
? `${selectedDisk.rotation_rate} rpm`
|
||||||
: "Solid State Device"}
|
: typeof selectedDisk.rotation_rate === "string"
|
||||||
</span>
|
? selectedDisk.rotation_rate
|
||||||
|
: "Solid State Device"}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -118,8 +118,11 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps)
|
|||||||
setError(null)
|
setError(null)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const baseUrl =
|
const { protocol, hostname, port } = window.location
|
||||||
typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
const isStandardPort = port === "" || port === "80" || port === "443"
|
||||||
|
|
||||||
|
const baseUrl = isStandardPort ? "" : `${protocol}//${hostname}:8008`
|
||||||
|
|
||||||
const apiUrl = `${baseUrl}/api/vms/${vmid}/metrics?timeframe=${timeframe}`
|
const apiUrl = `${baseUrl}/api/vms/${vmid}/metrics?timeframe=${timeframe}`
|
||||||
|
|
||||||
const response = await fetch(apiUrl)
|
const response = await fetch(apiUrl)
|
||||||
|
|||||||
@@ -75,8 +75,10 @@ export function NetworkTrafficChart({
|
|||||||
setError(null)
|
setError(null)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const baseUrl =
|
const { protocol, hostname, port } = window.location
|
||||||
typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
const isStandardPort = port === "" || port === "80" || port === "443"
|
||||||
|
|
||||||
|
const baseUrl = isStandardPort ? "" : `${protocol}//${hostname}:8008`
|
||||||
|
|
||||||
const apiUrl = interfaceName
|
const apiUrl = interfaceName
|
||||||
? `${baseUrl}/api/network/${interfaceName}/metrics?timeframe=${timeframe}`
|
? `${baseUrl}/api/network/${interfaceName}/metrics?timeframe=${timeframe}`
|
||||||
|
|||||||
@@ -86,8 +86,11 @@ export function NodeMetricsCharts() {
|
|||||||
setError(null)
|
setError(null)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const baseUrl =
|
const { protocol, hostname, port } = window.location
|
||||||
typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
const isStandardPort = port === "" || port === "80" || port === "443"
|
||||||
|
|
||||||
|
const baseUrl = isStandardPort ? "" : `${protocol}//${hostname}:8008`
|
||||||
|
|
||||||
const apiUrl = `${baseUrl}/api/node/metrics?timeframe=${timeframe}`
|
const apiUrl = `${baseUrl}/api/node/metrics?timeframe=${timeframe}`
|
||||||
|
|
||||||
console.log("[v0] Fetching node metrics from:", apiUrl)
|
console.log("[v0] Fetching node metrics from:", apiUrl)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { VirtualMachines } from "./virtual-machines"
|
|||||||
import Hardware from "./hardware"
|
import Hardware from "./hardware"
|
||||||
import { SystemLogs } from "./system-logs"
|
import { SystemLogs } from "./system-logs"
|
||||||
import { OnboardingCarousel } from "./onboarding-carousel"
|
import { OnboardingCarousel } from "./onboarding-carousel"
|
||||||
|
import { getApiUrl } from "../lib/api-config"
|
||||||
import {
|
import {
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
@@ -67,8 +68,7 @@ export function ProxmoxDashboard() {
|
|||||||
console.log("[v0] Fetching system data from Flask server...")
|
console.log("[v0] Fetching system data from Flask server...")
|
||||||
console.log("[v0] Current window location:", window.location.href)
|
console.log("[v0] Current window location:", window.location.href)
|
||||||
|
|
||||||
const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
const apiUrl = getApiUrl("/api/system")
|
||||||
const apiUrl = `${baseUrl}/api/system`
|
|
||||||
|
|
||||||
console.log("[v0] API URL:", apiUrl)
|
console.log("[v0] API URL:", apiUrl)
|
||||||
|
|
||||||
@@ -235,13 +235,8 @@ export function ProxmoxDashboard() {
|
|||||||
<p>• The ProxMenux server should start automatically on port 8008</p>
|
<p>• The ProxMenux server should start automatically on port 8008</p>
|
||||||
<p>
|
<p>
|
||||||
• Try accessing:{" "}
|
• Try accessing:{" "}
|
||||||
<a
|
<a href={getApiUrl("/api/health")} target="_blank" rel="noopener noreferrer" className="underline">
|
||||||
href={`http://${typeof window !== "undefined" ? window.location.host : "localhost:8008"}/api/health`}
|
{getApiUrl("/api/health")}
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="underline"
|
|
||||||
>
|
|
||||||
http://{typeof window !== "undefined" ? window.location.host : "localhost:8008"}/api/health
|
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -211,6 +211,12 @@ export function StorageOverview() {
|
|||||||
if (diskName.startsWith("nvme")) {
|
if (diskName.startsWith("nvme")) {
|
||||||
return "NVMe"
|
return "NVMe"
|
||||||
}
|
}
|
||||||
|
// rotation_rate = -1 means HDD but RPM is unknown (detected via kernel rotational flag)
|
||||||
|
// rotation_rate = 0 or undefined means SSD
|
||||||
|
// rotation_rate > 0 means HDD with known RPM
|
||||||
|
if (rotationRate === -1) {
|
||||||
|
return "HDD"
|
||||||
|
}
|
||||||
if (!rotationRate || rotationRate === 0) {
|
if (!rotationRate || rotationRate === 0) {
|
||||||
return "SSD"
|
return "SSD"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,7 +125,14 @@ export function SystemLogs() {
|
|||||||
|
|
||||||
const getApiUrl = (endpoint: string) => {
|
const getApiUrl = (endpoint: string) => {
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
return `${window.location.protocol}//${window.location.hostname}:8008${endpoint}`
|
const { protocol, hostname, port } = window.location
|
||||||
|
const isStandardPort = port === "" || port === "80" || port === "443"
|
||||||
|
|
||||||
|
if (isStandardPort) {
|
||||||
|
return endpoint
|
||||||
|
} else {
|
||||||
|
return `${protocol}//${hostname}:8008${endpoint}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return `http://localhost:8008${endpoint}`
|
return `http://localhost:8008${endpoint}`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Cpu, MemoryStick, Thermometer, Server, Zap, AlertCircle, HardDrive, Net
|
|||||||
import { NodeMetricsCharts } from "./node-metrics-charts"
|
import { NodeMetricsCharts } from "./node-metrics-charts"
|
||||||
import { NetworkTrafficChart } from "./network-traffic-chart"
|
import { NetworkTrafficChart } from "./network-traffic-chart"
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"
|
||||||
|
import { getApiUrl } from "../lib/api-config"
|
||||||
|
|
||||||
interface SystemData {
|
interface SystemData {
|
||||||
cpu_usage: number
|
cpu_usage: number
|
||||||
@@ -97,8 +98,7 @@ interface ProxmoxStorageData {
|
|||||||
|
|
||||||
const fetchSystemData = async (): Promise<SystemData | null> => {
|
const fetchSystemData = async (): Promise<SystemData | null> => {
|
||||||
try {
|
try {
|
||||||
const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
const apiUrl = getApiUrl("/api/system")
|
||||||
const apiUrl = `${baseUrl}/api/system`
|
|
||||||
|
|
||||||
const response = await fetch(apiUrl, {
|
const response = await fetch(apiUrl, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@@ -122,8 +122,7 @@ const fetchSystemData = async (): Promise<SystemData | null> => {
|
|||||||
|
|
||||||
const fetchVMData = async (): Promise<VMData[]> => {
|
const fetchVMData = async (): Promise<VMData[]> => {
|
||||||
try {
|
try {
|
||||||
const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
const apiUrl = getApiUrl("/api/vms")
|
||||||
const apiUrl = `${baseUrl}/api/vms`
|
|
||||||
|
|
||||||
const response = await fetch(apiUrl, {
|
const response = await fetch(apiUrl, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@@ -147,8 +146,7 @@ const fetchVMData = async (): Promise<VMData[]> => {
|
|||||||
|
|
||||||
const fetchStorageData = async (): Promise<StorageData | null> => {
|
const fetchStorageData = async (): Promise<StorageData | null> => {
|
||||||
try {
|
try {
|
||||||
const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
const apiUrl = getApiUrl("/api/storage/summary")
|
||||||
const apiUrl = `${baseUrl}/api/storage/summary`
|
|
||||||
|
|
||||||
const response = await fetch(apiUrl, {
|
const response = await fetch(apiUrl, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@@ -173,8 +171,7 @@ const fetchStorageData = async (): Promise<StorageData | null> => {
|
|||||||
|
|
||||||
const fetchNetworkData = async (): Promise<NetworkData | null> => {
|
const fetchNetworkData = async (): Promise<NetworkData | null> => {
|
||||||
try {
|
try {
|
||||||
const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
const apiUrl = getApiUrl("/api/network/summary")
|
||||||
const apiUrl = `${baseUrl}/api/network/summary`
|
|
||||||
|
|
||||||
const response = await fetch(apiUrl, {
|
const response = await fetch(apiUrl, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@@ -199,8 +196,7 @@ const fetchNetworkData = async (): Promise<NetworkData | null> => {
|
|||||||
|
|
||||||
const fetchProxmoxStorageData = async (): Promise<ProxmoxStorageData | null> => {
|
const fetchProxmoxStorageData = async (): Promise<ProxmoxStorageData | null> => {
|
||||||
try {
|
try {
|
||||||
const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
const apiUrl = getApiUrl("/api/proxmox-storage")
|
||||||
const apiUrl = `${baseUrl}/api/proxmox-storage`
|
|
||||||
|
|
||||||
const response = await fetch(apiUrl, {
|
const response = await fetch(apiUrl, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
|||||||
71
AppImage/lib/api-config.ts
Normal file
71
AppImage/lib/api-config.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* API Configuration for ProxMenux Monitor
|
||||||
|
* Handles API URL generation with automatic proxy detection
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the base URL for API calls
|
||||||
|
* Automatically detects if running behind a proxy by checking if we're on a standard port
|
||||||
|
*
|
||||||
|
* @returns Base URL for API endpoints
|
||||||
|
*/
|
||||||
|
export function getApiBaseUrl(): string {
|
||||||
|
if (typeof window === "undefined") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const { protocol, hostname, port } = window.location
|
||||||
|
|
||||||
|
// If accessing via standard ports (80/443) or no port, assume we're behind a proxy
|
||||||
|
// In this case, use relative URLs so the proxy handles routing
|
||||||
|
const isStandardPort = port === "" || port === "80" || port === "443"
|
||||||
|
|
||||||
|
if (isStandardPort) {
|
||||||
|
// Behind a proxy - use relative URL
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
// Direct access - use explicit port 8008
|
||||||
|
return `${protocol}//${hostname}:8008`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a full API URL
|
||||||
|
*
|
||||||
|
* @param endpoint - API endpoint path (e.g., '/api/system')
|
||||||
|
* @returns Full API URL
|
||||||
|
*/
|
||||||
|
export function getApiUrl(endpoint: string): string {
|
||||||
|
const baseUrl = getApiBaseUrl()
|
||||||
|
|
||||||
|
// Ensure endpoint starts with /
|
||||||
|
const normalizedEndpoint = endpoint.startsWith("/") ? endpoint : `/${endpoint}`
|
||||||
|
|
||||||
|
return `${baseUrl}${normalizedEndpoint}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches data from an API endpoint with error handling
|
||||||
|
*
|
||||||
|
* @param endpoint - API endpoint path
|
||||||
|
* @param options - Fetch options
|
||||||
|
* @returns Promise with the response data
|
||||||
|
*/
|
||||||
|
export async function fetchApi<T>(endpoint: string, options?: RequestInit): Promise<T> {
|
||||||
|
const url = getApiUrl(endpoint)
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options?.headers,
|
||||||
|
},
|
||||||
|
cache: "no-store",
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`API request failed: ${response.status} ${response.statusText}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
}
|
||||||
@@ -1387,6 +1387,23 @@ def get_smart_data(disk_name):
|
|||||||
smart_data['health'] = 'warning'
|
smart_data['health'] = 'warning'
|
||||||
# print(f"[v0] Health: WARNING (temperature {smart_data['temperature']}°C)")
|
# print(f"[v0] Health: WARNING (temperature {smart_data['temperature']}°C)")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# CHANGE: Use -1 to indicate HDD with unknown RPM instead of inventing 7200 RPM
|
||||||
|
# Fallback: Check kernel's rotational flag if smartctl didn't provide rotation_rate
|
||||||
|
# This fixes detection for older disks that don't report RPM via smartctl
|
||||||
|
if smart_data['rotation_rate'] == 0:
|
||||||
|
try:
|
||||||
|
rotational_path = f"/sys/block/{disk_name}/queue/rotational"
|
||||||
|
if os.path.exists(rotational_path):
|
||||||
|
with open(rotational_path, 'r') as f:
|
||||||
|
rotational = int(f.read().strip())
|
||||||
|
if rotational == 1:
|
||||||
|
# Disk is rotational (HDD), use -1 to indicate "HDD but RPM unknown"
|
||||||
|
smart_data['rotation_rate'] = -1
|
||||||
|
# If rotational == 0, it's an SSD, keep rotation_rate as 0
|
||||||
|
except Exception as e:
|
||||||
|
pass # If we can't read the file, leave rotation_rate as is
|
||||||
|
|
||||||
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
# print(f"[v0] ERROR: smartctl not found - install smartmontools for disk monitoring.")
|
# print(f"[v0] ERROR: smartctl not found - install smartmontools for disk monitoring.")
|
||||||
|
|||||||
@@ -2671,7 +2671,7 @@ enable_vfio_iommu() {
|
|||||||
msg_info "$(translate "Checking conflicting drivers blacklist...")"
|
msg_info "$(translate "Checking conflicting drivers blacklist...")"
|
||||||
touch "$blacklist_file"
|
touch "$blacklist_file"
|
||||||
|
|
||||||
local blacklist_drivers=("nouveau" "lbm-nouveau" "amdgpu" "radeon" "nvidia" "nvidiafb")
|
local blacklist_drivers=("nouveau" "lbm-nouveau" "radeon" "nvidia" "nvidiafb")
|
||||||
for driver in "${blacklist_drivers[@]}"; do
|
for driver in "${blacklist_drivers[@]}"; do
|
||||||
if ! grep -q "^blacklist $driver" "$blacklist_file"; then
|
if ! grep -q "^blacklist $driver" "$blacklist_file"; then
|
||||||
echo "blacklist $driver" >> "$blacklist_file"
|
echo "blacklist $driver" >> "$blacklist_file"
|
||||||
|
|||||||
Reference in New Issue
Block a user