diff --git a/AppImage/components/network-metrics.tsx b/AppImage/components/network-metrics.tsx index b984a63..59e156f 100644 --- a/AppImage/components/network-metrics.tsx +++ b/AppImage/components/network-metrics.tsx @@ -153,7 +153,6 @@ export function NetworkMetrics() { const [selectedInterface, setSelectedInterface] = useState(null) const [timeframe, setTimeframe] = useState<"hour" | "day" | "week" | "month" | "year">("day") - const [interfaceTimeframe, setInterfaceTimeframe] = useState<"hour" | "day" | "week" | "month" | "year">("day") const [networkTotals, setNetworkTotals] = useState<{ received: number; sent: number }>({ received: 0, sent: 0 }) const { data: interfaceHistoricalData } = useSWR(`/api/node/metrics?timeframe=${timeframe}`, fetcher, { @@ -161,15 +160,6 @@ export function NetworkMetrics() { revalidateOnFocus: false, }) - const { data: selectedInterfaceData, isLoading: interfaceDataLoading } = useSWR( - selectedInterface ? `/api/network/${selectedInterface.name}/history?timeframe=${interfaceTimeframe}` : null, - fetcher, - { - refreshInterval: 30000, - revalidateOnFocus: false, - }, - ) - if (isLoading) { return (
@@ -652,22 +642,6 @@ export function NetworkMetrics() { {selectedInterface && (
- {/* Timeframe Selector */} -
- -
- {/* Basic Information */}

Basic Information

@@ -799,39 +773,6 @@ export function NetworkMetrics() {
)} - {/* Network Traffic Chart */} - {selectedInterfaceData && ( -
-

- Network Traffic -{" "} - {interfaceTimeframe === "hour" - ? "1 Hour" - : interfaceTimeframe === "day" - ? "24 Hours" - : interfaceTimeframe === "week" - ? "7 Days" - : interfaceTimeframe === "month" - ? "30 Days" - : "1 Year"} -

- {interfaceDataLoading ? ( -
-
Loading historical data...
-
- ) : ( -
- { - // Optional: update totals if needed - }} - /> -
- )} -
- )} - {/* Traffic Statistics */}

Traffic since last boot

diff --git a/AppImage/components/network-traffic-chart.tsx b/AppImage/components/network-traffic-chart.tsx index 98fd89e..d0c0c65 100644 --- a/AppImage/components/network-traffic-chart.tsx +++ b/AppImage/components/network-traffic-chart.tsx @@ -13,7 +13,6 @@ interface NetworkMetricsData { interface NetworkTrafficChartProps { timeframe: string - interfaceName?: string // Added optional interfaceName prop for specific interface data onTotalsCalculated?: (totals: { received: number; sent: number }) => void } @@ -37,7 +36,7 @@ const CustomNetworkTooltip = ({ active, payload, label }: any) => { return null } -export function NetworkTrafficChart({ timeframe, interfaceName, onTotalsCalculated }: NetworkTrafficChartProps) { +export function NetworkTrafficChart({ timeframe, onTotalsCalculated }: NetworkTrafficChartProps) { const [data, setData] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) @@ -48,7 +47,7 @@ export function NetworkTrafficChart({ timeframe, interfaceName, onTotalsCalculat useEffect(() => { fetchMetrics() - }, [timeframe, interfaceName]) + }, [timeframe]) const fetchMetrics = async () => { setLoading(true) @@ -57,15 +56,12 @@ export function NetworkTrafficChart({ timeframe, interfaceName, onTotalsCalculat try { const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : "" - - const apiUrl = interfaceName - ? `${baseUrl}/api/network/${interfaceName}/history?timeframe=${timeframe}` - : `${baseUrl}/api/node/metrics?timeframe=${timeframe}` + const apiUrl = `${baseUrl}/api/node/metrics?timeframe=${timeframe}` const response = await fetch(apiUrl) if (!response.ok) { - throw new Error(`Failed to fetch network metrics: ${response.status}`) + throw new Error(`Failed to fetch node metrics: ${response.status}`) } const result = await response.json() diff --git a/AppImage/scripts/flask_server.py b/AppImage/scripts/flask_server.py index a422956..ce1db39 100644 --- a/AppImage/scripts/flask_server.py +++ b/AppImage/scripts/flask_server.py @@ -2653,7 +2653,7 @@ def get_detailed_gpu_info(gpu): mem_clock = clocks['GFX_MCLK'] if 'value' in mem_clock: detailed_info['clock_memory'] = f"{mem_clock['value']} MHz" - print(f"[v0] Memory Clock: {detailed_info['clock_memory']} MHz", flush=True) + print(f"[v0] Memory Clock: {detailed_info['clock_memory']}", flush=True) data_retrieved = True # Parse GPU activity (gpu_activity.GFX) @@ -3711,6 +3711,92 @@ def api_network(): """Get network information""" return jsonify(get_network_info()) +@app.route('/api/network//metrics', methods=['GET']) +def api_network_interface_metrics(interface_name): + """Get historical metrics (RRD data) for a specific network interface""" + try: + timeframe = request.args.get('timeframe', 'day') # hour, day, week, month, year + + print(f"[v0] ===== NETWORK INTERFACE METRICS REQUEST =====") + print(f"[v0] Interface: {interface_name}") + print(f"[v0] Timeframe: {timeframe}") + + # Validate timeframe + valid_timeframes = ['hour', 'day', 'week', 'month', 'year'] + if timeframe not in valid_timeframes: + print(f"[v0] ERROR: Invalid timeframe: {timeframe}") + return jsonify({'error': f'Invalid timeframe. Must be one of: {", ".join(valid_timeframes)}'}), 400 + + # Get local node name + local_node = socket.gethostname() + print(f"[v0] Local node: {local_node}") + + # Determine interface type and get appropriate RRD data + interface_type = get_interface_type(interface_name) + print(f"[v0] Interface type: {interface_type}") + + rrd_data = [] + + if interface_type == 'vm_lxc': + # For VM/LXC interfaces, get data from the VM/LXC RRD + vmid, vm_type = extract_vmid_from_interface(interface_name) + if vmid: + print(f"[v0] Fetching RRD data for {vm_type} {vmid}...") + rrd_result = subprocess.run(['pvesh', 'get', f'/nodes/{local_node}/{vm_type}/{vmid}/rrddata', + '--timeframe', timeframe, '--output-format', 'json'], + capture_output=True, text=True, timeout=10) + + if rrd_result.returncode == 0: + all_data = json.loads(rrd_result.stdout) + # Filter to only network-related fields + for point in all_data: + filtered_point = {'time': point.get('time')} + # Add network fields if they exist + for key in ['netin', 'netout', 'diskread', 'diskwrite']: + if key in point: + filtered_point[key] = point[key] + rrd_data.append(filtered_point) + print(f"[v0] RRD data points: {len(rrd_data)}") + else: + print(f"[v0] ERROR: Failed to get RRD data for VM/LXC") + print(f"[v0] stderr: {rrd_result.stderr}") + else: + # For physical/bridge interfaces, get data from node RRD + print(f"[v0] Fetching RRD data for node {local_node}...") + rrd_result = subprocess.run(['pvesh', 'get', f'/nodes/{local_node}/rrddata', + '--timeframe', timeframe, '--output-format', 'json'], + capture_output=True, text=True, timeout=10) + + if rrd_result.returncode == 0: + all_data = json.loads(rrd_result.stdout) + # Filter to only network-related fields for this interface + for point in all_data: + filtered_point = {'time': point.get('time')} + # Add network fields if they exist + for key in ['netin', 'netout']: + if key in point: + filtered_point[key] = point[key] + rrd_data.append(filtered_point) + print(f"[v0] RRD data points: {len(rrd_data)}") + else: + print(f"[v0] ERROR: Failed to get RRD data for node") + print(f"[v0] stderr: {rrd_result.stderr}") + + print(f"[v0] ===== NETWORK INTERFACE METRICS REQUEST SUCCESS =====") + return jsonify({ + 'interface': interface_name, + 'type': interface_type, + 'timeframe': timeframe, + 'data': rrd_data + }) + + except Exception as e: + print(f"[v0] EXCEPTION in api_network_interface_metrics: {e}") + print(f"[v0] ===== NETWORK INTERFACE METRICS REQUEST EXCEPTION =====") + return jsonify({'error': str(e)}), 500 + +# ... existing code ... + @app.route('/api/vms', methods=['GET']) def api_vms(): """Get virtual machine information"""