forked from BLC/AyposWeb
247 lines
7.9 KiB
TypeScript
247 lines
7.9 KiB
TypeScript
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
import { VMDetails, GainBeforeData, MigrationAdviceData } from './types';
|
|
import { config } from '../../config/env';
|
|
|
|
const API_BASE_URL = config.apiUrl;
|
|
const REFRESH_INTERVAL = 30000; // 30 seconds
|
|
|
|
interface GainAfterData {
|
|
past_power: number;
|
|
cur_power: number;
|
|
prop_power: number;
|
|
prop_ratio: number;
|
|
actual_ratio: number;
|
|
val_ratio: number;
|
|
val_difference: number;
|
|
}
|
|
|
|
// Smart polling hook
|
|
export const useSmartPolling = <T>(
|
|
fetchFunction: () => Promise<T>,
|
|
initialState: T | null,
|
|
minInterval: number = 5000,
|
|
maxInterval: number = 30000
|
|
) => {
|
|
const [data, setData] = useState<T | null>(initialState);
|
|
const [pollingInterval, setPollingInterval] = useState(minInterval);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [isInitialLoad, setIsInitialLoad] = useState(true);
|
|
const lastDataRef = useRef<string>('');
|
|
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
|
|
|
const poll = useCallback(async () => {
|
|
try {
|
|
if (isInitialLoad) {
|
|
setIsLoading(true);
|
|
}
|
|
const newData = await fetchFunction();
|
|
const newDataString = JSON.stringify(newData);
|
|
|
|
// Only update if data actually changed
|
|
if (lastDataRef.current !== newDataString) {
|
|
console.log('Data changed, resetting polling interval to', minInterval, 'ms');
|
|
setData(newData);
|
|
setPollingInterval(minInterval);
|
|
lastDataRef.current = newDataString;
|
|
} else {
|
|
// Gradually increase interval when data is stable
|
|
setPollingInterval(prevInterval => {
|
|
const newInterval = Math.min(prevInterval * 1.5, maxInterval);
|
|
if (newInterval !== prevInterval) {
|
|
console.log('Data stable, increasing polling interval to', newInterval, 'ms');
|
|
}
|
|
return newInterval;
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('Error in smart polling:', error);
|
|
// On error, keep current interval
|
|
} finally {
|
|
setIsLoading(false);
|
|
setIsInitialLoad(false);
|
|
}
|
|
}, [fetchFunction, minInterval, maxInterval, isInitialLoad]);
|
|
|
|
useEffect(() => {
|
|
// Initial fetch
|
|
poll();
|
|
|
|
// Clear any existing interval
|
|
if (intervalRef.current) {
|
|
clearInterval(intervalRef.current);
|
|
}
|
|
|
|
// Set new interval
|
|
intervalRef.current = setInterval(poll, pollingInterval);
|
|
|
|
return () => {
|
|
if (intervalRef.current) {
|
|
clearInterval(intervalRef.current);
|
|
intervalRef.current = null;
|
|
}
|
|
};
|
|
}, [poll, pollingInterval]);
|
|
|
|
return { data, isLoading, pollingInterval };
|
|
};
|
|
|
|
export const useMigrationData = () => {
|
|
const [gainBeforeData, setGainBeforeData] = useState<GainBeforeData | null>(null);
|
|
const [migrationAdviceData, setMigrationAdviceData] = useState<MigrationAdviceData | null>(null);
|
|
const [isLoadingGainData, setIsLoadingGainData] = useState(false);
|
|
|
|
const fetchMigrationData = async () => {
|
|
try {
|
|
setIsLoadingGainData(true);
|
|
|
|
// Log the request start
|
|
console.log('Fetching migration data...');
|
|
|
|
const [gainResponse, migrationResponse] = await Promise.all([
|
|
fetch(`${API_BASE_URL}/prom/get_chart_data/gain_before`),
|
|
fetch(`${API_BASE_URL}/prom/get_chart_data/migration`)
|
|
]);
|
|
|
|
// Log the response status
|
|
console.log('Gain Response Status:', gainResponse.status);
|
|
console.log('Migration Response Status:', migrationResponse.status);
|
|
|
|
if (!gainResponse.ok || !migrationResponse.ok) {
|
|
throw new Error(`Failed to fetch migration data: Gain Status ${gainResponse.status}, Migration Status ${migrationResponse.status}`);
|
|
}
|
|
|
|
const [gainData, migrationData] = await Promise.all([
|
|
gainResponse.json(),
|
|
migrationResponse.json()
|
|
]);
|
|
|
|
// Log the received data
|
|
console.log('Received Gain Data:', gainData);
|
|
console.log('Received Migration Data:', migrationData);
|
|
|
|
if (!gainData || typeof gainData.cur_power === 'undefined') {
|
|
console.error('Invalid gain data format:', gainData);
|
|
throw new Error('Invalid gain data format');
|
|
}
|
|
|
|
if (!migrationData || Object.keys(migrationData).length === 0) {
|
|
console.warn('No migration advice available:', migrationData);
|
|
}
|
|
|
|
setGainBeforeData(gainData);
|
|
setMigrationAdviceData(migrationData);
|
|
} catch (error) {
|
|
console.error('Error fetching migration data:', error);
|
|
// Clear the data on error to prevent showing stale data
|
|
setGainBeforeData(null);
|
|
setMigrationAdviceData(null);
|
|
} finally {
|
|
setIsLoadingGainData(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchMigrationData();
|
|
const interval = setInterval(fetchMigrationData, REFRESH_INTERVAL);
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
return { gainBeforeData, migrationAdviceData, isLoadingGainData, fetchMigrationData };
|
|
};
|
|
|
|
export const useMonitoringData = () => {
|
|
const [monitoringData, setMonitoringData] = useState<any[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [stableHosts, setStableHosts] = useState<string[]>([]);
|
|
const [computeCount, setComputeCount] = useState<number>(0);
|
|
const [vmCount, setVmCount] = useState<number>(0);
|
|
|
|
const fetchMonitoringData = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const response = await fetch(`${API_BASE_URL}/prom/monitoring`);
|
|
if (!response.ok) {
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
const result = await response.json();
|
|
|
|
if (result?.data) {
|
|
setMonitoringData(result.data);
|
|
const filteredData = result.data.filter((pm: any) => pm.virtual_machines?.length > 0);
|
|
setComputeCount(filteredData.length);
|
|
setVmCount(filteredData.reduce((acc: number, pm: any) => acc + pm.virtual_machines.length, 0));
|
|
|
|
const newHosts = result.data.map((pm: any) => pm.host);
|
|
setStableHosts(prevHosts => {
|
|
const allHosts = Array.from(new Set([...prevHosts, ...newHosts]));
|
|
return allHosts.filter(host => newHosts.includes(host));
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching monitoring data:', error);
|
|
setMonitoringData([]);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchMonitoringData();
|
|
const interval = setInterval(fetchMonitoringData, REFRESH_INTERVAL);
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
return { monitoringData, loading, stableHosts, computeCount, vmCount };
|
|
};
|
|
|
|
export const useVMDetails = () => {
|
|
const [vmDetails, setVmDetails] = useState<VMDetails | null>(null);
|
|
const [expandedVMs, setExpandedVMs] = useState<Record<string, boolean>>({});
|
|
|
|
const toggleVMDetails = (vmId: string) => {
|
|
setExpandedVMs(prev => ({
|
|
...prev,
|
|
[vmId]: !prev[vmId]
|
|
}));
|
|
};
|
|
|
|
const fetchVMDetails = async (vmName: string) => {
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}/prom/vm_details/${vmName}`);
|
|
if (!response.ok) {
|
|
throw new Error('Failed to fetch VM details');
|
|
}
|
|
const data = await response.json();
|
|
setVmDetails(data);
|
|
} catch (error) {
|
|
console.error('Error fetching VM details:', error);
|
|
setVmDetails(null);
|
|
}
|
|
};
|
|
|
|
return { vmDetails, expandedVMs, toggleVMDetails, fetchVMDetails };
|
|
};
|
|
|
|
export const useGainAfterData = () => {
|
|
const [gainAfterData, setGainAfterData] = useState<GainAfterData | null>(null);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
const fetchGainAfterData = async () => {
|
|
try {
|
|
setIsLoading(true);
|
|
const response = await fetch(`${API_BASE_URL}/prom/get_chart_data/gain_after`);
|
|
if (!response.ok) {
|
|
throw new Error('Failed to fetch gain after data');
|
|
}
|
|
const data = await response.json();
|
|
setGainAfterData(data);
|
|
} catch (error) {
|
|
console.error('Error fetching gain after data:', error);
|
|
setGainAfterData(null);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
return { gainAfterData, isLoading: isLoading, fetchGainAfterData };
|
|
};
|