import { useState, useEffect } from 'react'; import { Box, Paper, Grid, Typography, Button, useTheme, Card, CardContent, IconButton, CircularProgress, Collapse, Dialog, DialogTitle, DialogContent, DialogActions, LinearProgress, } from '@mui/material'; import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew'; import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; import TerminalIcon from '@mui/icons-material/Terminal'; import VisibilityIcon from '@mui/icons-material/Visibility'; import LockIcon from '@mui/icons-material/Lock'; import SummaryStats from '../components/Migration/SummaryStats'; import ResourceDistributionChart from '../components/Migration/ResourceDistributionChart'; import MigrationAdviceCard from '../components/Migration/MigrationAdviceCard'; import VerifiedMigration from '../components/Migration/VerifiedMigration'; import { useMigrationData, useGainAfterData } from '../components/Migration/hooks'; // Constants const API_BASE_URL = 'http://141.196.83.136:8003'; const REFRESH_INTERVAL = 30000; // 30 seconds interface VMPlacementData { data_center: string; id: number; physical_machines: Array<{ status: 'blocked' | 'open'; name: string; power_consumption: number; vms: { active: Array<{ status: 'blocked' | 'open'; name: string; power: number; confg: { cpu: number; ram: number; disk: number; }; }>; inactive: Array<{ status: 'blocked' | 'open'; name: string; power: number; confg: { cpu: number; ram: number; disk: number; }; }>; }; }>; } interface VMCardProps { vm: { name: string; power: number; status: 'blocked' | 'open'; confg: { cpu: number; ram: number; disk: number; }; }; vmId: string; isActive: boolean; expandedVMs: Record; toggleVMDetails: (vmId: string) => void; theme: any; } const VMCard = ({ vm, vmId, isActive, expandedVMs, toggleVMDetails, theme }: VMCardProps) => ( {vm.name} {vm.status === 'blocked' && ( )} {vm.power.toFixed(2)}W { e.stopPropagation(); toggleVMDetails(vmId); }} > Status {vm.status === 'blocked' ? ( <> Blocked ) : ( 'Open' )} CPU Cores {vm.confg.cpu} RAM {vm.confg.ram} GB Disk Size {vm.confg.disk} GB Power {vm.power.toFixed(2)}W ); const getMessageColor = (message: string, theme: any): string => { if (message.includes('Error') || message.includes('BadRequestException')) { return theme.palette.error.light; } else if (message.includes('DEBUG')) { return theme.palette.info.light; } else if (message.includes('Attempting')) { return theme.palette.warning.light; } else if (message.includes('completed') || message.includes('Migration completed')) { return theme.palette.success.light; } return 'white'; }; const MigrationProgress = ({ open, progress, onClose }: { open: boolean; progress: string[]; onClose: () => void; }) => { const theme = useTheme(); return ( Migration Progress {progress.length > 0 && ( )} {progress.length > 0 ? ( progress.map((message, index) => ( {message} )) ) : ( Starting migration process... )} ); }; const Migration = () => { const theme = useTheme(); // Essential states const [vmPlacementData, setVmPlacementData] = useState(null); const [isLoadingVmPlacement, setIsLoadingVmPlacement] = useState(false); const [expandedVMs, setExpandedVMs] = useState>({}); const [showVerifiedSection, setShowVerifiedSection] = useState(false); const [isCardExpanded, setIsCardExpanded] = useState(false); const [migrationMode] = useState<'auto' | 'semiauto'>('auto'); const [isProcessing, setIsProcessing] = useState(false); const [migrationProgress, setMigrationProgress] = useState([]); const [showProgress, setShowProgress] = useState(false); const [hasProgress, setHasProgress] = useState(false); // Hooks for migration functionality const { gainBeforeData, migrationAdviceData, isLoadingGainData, fetchMigrationData } = useMigrationData(); const { gainAfterData, isLoading: isLoadingGainAfter, fetchGainAfterData } = useGainAfterData(); // Essential functions const toggleVMDetails = (vmId: string) => { setExpandedVMs(prev => ({ ...prev, [vmId]: !prev[vmId] })); }; const fetchVmPlacementData = async () => { try { setIsLoadingVmPlacement(true); const response = await fetch(`${API_BASE_URL}/prom/get_chart_data/vm_placement`); if (!response.ok) { throw new Error(`Failed to fetch VM placement data: ${response.status}`); } const data = await response.json(); console.log('Raw API response:', data); // Debug log setVmPlacementData(data); // Use the data directly since it already has the correct structure } catch (error) { console.error('Error fetching VM placement data:', error); } finally { setIsLoadingVmPlacement(false); } }; const handleApproveMigration = async () => { try { setIsProcessing(true); setMigrationProgress([]); setHasProgress(true); // First, send the POST request for migration approval const approvalResponse = await fetch('http://141.196.83.136:8003/prom/migration/decisions4?run_migration=true', { method: 'POST', headers: { 'accept': 'application/json' } }); if (!approvalResponse.ok) { throw new Error('Failed to approve migration'); } const reader = approvalResponse.body?.getReader(); const decoder = new TextDecoder(); if (reader) { while (true) { const { value, done } = await reader.read(); if (done) break; const text = decoder.decode(value); const lines = text.split('\n').filter(line => line.trim()); setMigrationProgress(prev => [...prev, ...lines]); } } // If approval is successful, show verified section and fetch gain after data setShowVerifiedSection(true); await fetchGainAfterData(); } catch (error) { console.error('Error during migration approval:', error); setMigrationProgress(prev => [...prev, `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`]); } finally { setIsProcessing(false); } }; const handleDeclineMigration = async () => { try { setIsProcessing(true); const response = await fetch('http://141.196.83.136:8003/prom/migration/decisions4?run_migration=false', { method: 'POST', headers: { 'accept': 'application/json' } }); if (!response.ok) { throw new Error('Failed to decline migration'); } // Hide verified section if it was shown setShowVerifiedSection(false); } catch (error) { console.error('Error declining migration:', error); } finally { setIsProcessing(false); } }; // Data fetching effect useEffect(() => { console.log('Initial data fetch'); fetchVmPlacementData(); const intervalId = setInterval(() => { console.log('Interval data fetch'); fetchVmPlacementData(); }, REFRESH_INTERVAL); return () => clearInterval(intervalId); }, []); // Add effect to monitor vmPlacementData changes useEffect(() => { if (vmPlacementData) { const blockedPMs = vmPlacementData.physical_machines.filter(pm => pm.status === 'blocked').length; const blockedVMs = vmPlacementData.physical_machines.reduce((acc, pm) => { const activeBlocked = pm.vms.active.filter(vm => vm.status === 'blocked').length; const inactiveBlocked = pm.vms.inactive.filter(vm => vm.status === 'blocked').length; return acc + activeBlocked + inactiveBlocked; }, 0); console.log('VM Placement Data updated:', { timestamp: new Date().toISOString(), pmCount: vmPlacementData.physical_machines.length, blockedPMs, totalVMs: vmPlacementData.physical_machines.reduce((acc, pm) => acc + pm.vms.active.length + pm.vms.inactive.length, 0 ), blockedVMs }); } }, [vmPlacementData]); return ( { e.stopPropagation(); fetchMigrationData(); }} /> {/* Migration Action Buttons */} {hasProgress && ( )} {/* Verified Migration Section */} {showVerifiedSection && ( )} {/* PM & VM Monitoring Section */} PMs & VMs Monitoring {isLoadingVmPlacement ? ( ) : vmPlacementData?.physical_machines ? ( {vmPlacementData.physical_machines.map((pm) => ( {pm.name} {pm.status === 'blocked' && ( )} {pm.power_consumption.toFixed(2)}W {/* Active VMs */} {pm.vms.active.map((vm, index) => ( ))} {/* Inactive VMs */} {pm.vms.inactive.map((vm, index) => ( ))} {pm.vms.active.length === 0 && pm.vms.inactive.length === 0 && ( No VMs running )} ))} ) : ( No monitoring data available )} setShowProgress(false)} /> ); }; export default Migration;