forked from BLC/sgeUpdated
342 lines
12 KiB
JavaScript
342 lines
12 KiB
JavaScript
import React, { useState, useEffect } from "react";
|
|
import { useSelector, useDispatch } from "react-redux";
|
|
import { Card, CardHeader, CardTitle, Button } from "reactstrap";
|
|
import DataTable from "react-data-table-component";
|
|
import { ChevronDown, RefreshCw, Server, Monitor } from "react-feather";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useSnackbar } from "notistack";
|
|
import { getDataCenters } from "../redux/actions/dataCenter";
|
|
import SpinnerComponent from "../@core/components/spinner/Fallback-spinner";
|
|
|
|
const DataCenter = () => {
|
|
const { t } = useTranslation();
|
|
const { enqueueSnackbar } = useSnackbar();
|
|
const dispatch = useDispatch();
|
|
|
|
// Redux state
|
|
const dataCenterStore = useSelector((state) => state.dataCenter);
|
|
const [refreshInterval, setRefreshInterval] = useState(null);
|
|
|
|
const getAllPhysicalMachines = (dataCenter) => {
|
|
// Physical machines are directly in the dataCenter object, not in projects
|
|
const pms = dataCenter.physicalMachines || [];
|
|
return pms;
|
|
};
|
|
|
|
// Table columns following your pattern
|
|
const initialColumns = [
|
|
{
|
|
name: "External ID",
|
|
selector: (row) => row.externalId,
|
|
sortable: true,
|
|
minWidth: "100px",
|
|
},
|
|
{
|
|
name: "Data Centers",
|
|
selector: (row) => row.dataCenter,
|
|
sortable: true,
|
|
minWidth: "200px",
|
|
},
|
|
// Projects - Based on API response, this field might not exist or be structured differently
|
|
// {
|
|
// name: "Projects",
|
|
// selector: (row) => row.projects?.length || 0,
|
|
// sortable: true,
|
|
// minWidth: "200px",
|
|
// cell: (row) => (
|
|
// <div>
|
|
// {row.projects && row.projects.length > 0 ? (
|
|
// <div className="d-flex flex-column">
|
|
// {row.projects.map((project, index) => (
|
|
// <div
|
|
// key={project.id}
|
|
// className={`badge badge-light-primary ${
|
|
// index > 0 ? "mt-1" : ""
|
|
// }`}
|
|
// >
|
|
// {project.name}
|
|
// </div>
|
|
// ))}
|
|
// </div>
|
|
// ) : (
|
|
// <span className="text-muted">-</span>
|
|
// )}
|
|
// </div>
|
|
// ),
|
|
// },
|
|
// Physical Machines
|
|
{
|
|
name: "Physical Machines",
|
|
selector: (row) => getAllPhysicalMachines(row),
|
|
sortable: false,
|
|
minWidth: "200px",
|
|
cell: (row) => {
|
|
const pms = getAllPhysicalMachines(row);
|
|
return (
|
|
<div className="d-flex align-items-center">
|
|
<Server size={16} className="mr-1" />
|
|
<span>{pms.length} machines</span>
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
name: "Virtual Machines",
|
|
selector: (row) => {
|
|
const pms = getAllPhysicalMachines(row);
|
|
const vms = pms.reduce(
|
|
(acc, pm) => {
|
|
if (!pm.vms) return acc;
|
|
return {
|
|
active:
|
|
acc.active +
|
|
pm.vms.filter((vm) => vm.state?.toLowerCase() === "active")
|
|
.length,
|
|
total: acc.total + pm.vms.length,
|
|
};
|
|
},
|
|
{ active: 0, total: 0 }
|
|
);
|
|
return vms.total;
|
|
},
|
|
sortable: true,
|
|
minWidth: "200px",
|
|
cell: (row) => {
|
|
const pms = getAllPhysicalMachines(row);
|
|
const vms = pms.reduce(
|
|
(acc, pm) => {
|
|
if (!pm.vms) return acc;
|
|
return {
|
|
active:
|
|
acc.active +
|
|
pm.vms.filter((vm) => vm.state?.toLowerCase() === "active")
|
|
.length,
|
|
total: acc.total + pm.vms.length,
|
|
};
|
|
},
|
|
{ active: 0, total: 0 }
|
|
);
|
|
|
|
return (
|
|
<div className="d-flex align-items-center">
|
|
<Monitor size={16} className="mr-2" />
|
|
<div>
|
|
<div className="font-weight-bold">{vms.total} Total</div>
|
|
<div className="small">
|
|
<span className="text-success">{vms.active} Active</span>
|
|
<span className="text-muted mx-1">•</span>
|
|
<span className="text-warning">
|
|
{vms.total - vms.active} Inactive
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
];
|
|
|
|
// Fetch data on component mount
|
|
useEffect(() => {
|
|
dispatch(getDataCenters());
|
|
|
|
// Set up auto-refresh every 2 minutes
|
|
const interval = setInterval(() => {
|
|
dispatch(getDataCenters());
|
|
}, 2 * 60 * 1000);
|
|
|
|
setRefreshInterval(interval);
|
|
|
|
return () => {
|
|
if (interval) clearInterval(interval);
|
|
};
|
|
}, [dispatch]);
|
|
|
|
// Handle errors
|
|
useEffect(() => {
|
|
if (dataCenterStore.error) {
|
|
enqueueSnackbar(dataCenterStore.error, {
|
|
variant: "error",
|
|
preventDuplicate: true,
|
|
});
|
|
}
|
|
}, [dataCenterStore.error, enqueueSnackbar]);
|
|
|
|
const handleRefresh = () => {
|
|
dispatch(getDataCenters());
|
|
};
|
|
|
|
// Expandable component for showing physical machines and VMs
|
|
const ExpandableComponent = ({ data }) => {
|
|
const physicalMachines = getAllPhysicalMachines(data);
|
|
console.log("▶️ Expandable Data", physicalMachines);
|
|
return (
|
|
<div className="expandable-content p-3">
|
|
<h6 className="mb-3">Physical Machines:</h6>
|
|
{physicalMachines.length === 0 && (
|
|
<p className="text-muted ml-3">No physical machines</p>
|
|
)}
|
|
{physicalMachines.map((pm) => (
|
|
<div key={pm.id} className="mb-3 border rounded p-3">
|
|
<h6 className="text-primary">{pm.name}</h6>
|
|
|
|
{/* All VMs */}
|
|
<div className="mb-2 d-flex justify-content-between align-items-center">
|
|
<h6 className="mb-0">
|
|
<Monitor size={16} className="mr-1" />
|
|
Virtual Machines ({pm.vms?.length || 0})
|
|
</h6>
|
|
</div>
|
|
{pm.vms?.length > 0 ? (
|
|
<div className="table-responsive mt-2">
|
|
<table className="table table-bordered table-hover">
|
|
<thead className="thead-light">
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Status</th>
|
|
<th>Power (W)</th>
|
|
<th>Configuration</th>
|
|
<th>Host</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{pm.vms.map((vm) => {
|
|
const isActive =
|
|
vm.state && ["ACTIVE", "active"].includes(vm.state);
|
|
return (
|
|
<tr key={vm.id}>
|
|
<td>
|
|
<span className="font-weight-bold">
|
|
{vm.vmName || vm.vm_name}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<div
|
|
className={`d-inline-block px-2 py-1 rounded-pill ${
|
|
isActive
|
|
? "bg-light-success text-success"
|
|
: "bg-light-warning text-warning"
|
|
}`}
|
|
>
|
|
{vm.state}
|
|
</div>
|
|
</td>
|
|
<td>
|
|
{vm.power ? (
|
|
<span>{vm.power.toFixed(2)}</span>
|
|
) : (
|
|
<span className="text-muted">-</span>
|
|
)}
|
|
</td>
|
|
<td>
|
|
<div className="d-flex align-items-center">
|
|
<div className="mr-3">
|
|
<small className="text-muted d-block">
|
|
CPU
|
|
</small>
|
|
<span>
|
|
{vm.config?.cpu ||
|
|
(vm.confg && vm.confg[1]) ||
|
|
"-"}
|
|
</span>
|
|
</div>
|
|
<div className="mr-3">
|
|
<small className="text-muted d-block">
|
|
RAM
|
|
</small>
|
|
<span>
|
|
{vm.config?.ram ||
|
|
(vm.confg && vm.confg[2]) ||
|
|
"-"}{" "}
|
|
GB
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<small className="text-muted d-block">
|
|
Disk
|
|
</small>
|
|
<span>
|
|
{vm.config?.disk ||
|
|
(vm.confg && vm.confg[3]) ||
|
|
"-"}{" "}
|
|
GB
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div className="d-flex align-items-center">
|
|
<Server size={14} className="mr-1" />
|
|
<span>
|
|
{vm.host ||
|
|
vm.hostingPm ||
|
|
(vm.confg && vm.confg[4]) ||
|
|
"-"}
|
|
</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
);
|
|
})}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
) : (
|
|
<div className="text-center p-3 bg-light-secondary rounded">
|
|
<Monitor size={24} className="text-muted mb-1" />
|
|
<p className="text-muted mb-0">No virtual machines found</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div style={{ marginTop: "2%" }}>
|
|
<Card>
|
|
<CardHeader className="border-bottom">
|
|
<CardTitle tag="h4">Test</CardTitle>
|
|
<Button
|
|
className="ml-2"
|
|
color="primary"
|
|
onClick={handleRefresh}
|
|
disabled={dataCenterStore.loading}
|
|
>
|
|
<RefreshCw size={15} />
|
|
<span className="align-middle ml-50">
|
|
{dataCenterStore.loading ? "Refreshing..." : "Refresh"}
|
|
</span>
|
|
</Button>
|
|
</CardHeader>
|
|
|
|
<DataTable
|
|
noHeader
|
|
columns={initialColumns}
|
|
data={dataCenterStore.dataCenters}
|
|
sortIcon={<ChevronDown size={10} />}
|
|
expandableRows
|
|
expandableRowsComponent={ExpandableComponent}
|
|
progressPending={dataCenterStore.loading}
|
|
progressComponent={
|
|
<div className="d-flex justify-content-center p-3">
|
|
<SpinnerComponent />
|
|
</div>
|
|
}
|
|
noDataComponent={
|
|
<div className="d-flex flex-column align-items-center p-3">
|
|
<Server size={48} className="text-muted mb-2" />
|
|
|
|
<p className="text-muted">No data centers found</p>
|
|
</div>
|
|
}
|
|
className="react-dataTable"
|
|
/>
|
|
</Card>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DataCenter;
|