diff --git a/code/dashboard-2/src/App.tsx b/code/dashboard-2/src/App.tsx index 3b816cf9..b0b743c7 100644 --- a/code/dashboard-2/src/App.tsx +++ b/code/dashboard-2/src/App.tsx @@ -28,7 +28,9 @@ const router = createBrowserRouter( }/> }/> }/> + }/> }/> + }/> }/> ) diff --git a/code/dashboard-2/src/Constants.ts b/code/dashboard-2/src/Constants.ts index 5e158ae7..f4ccad8b 100644 --- a/code/dashboard-2/src/Constants.ts +++ b/code/dashboard-2/src/Constants.ts @@ -47,7 +47,7 @@ export const PAGE_TITLE_SUFFIX = ' | ' + APP_NAME // URLs and API Endpoints to be moved to .env files once dev and prod environments are set up export const APP_URL = 'https://naic-monitor.uio.no' export const API_ENDPOINT = import.meta.env.VITE_API_ENDPOINT -export const JOB_QUERY_API_ENDPOINT = import.meta.env.VITE_JOB_QUERY_API_ENDPOINT +export const QUERY_API_ENDPOINT = import.meta.env.VITE_QUERY_API_ENDPOINT // The representation of "true" is a hack, but it's determined by the server, so live with it. export const TRUE_VAL = 'xxxxxtruexxxxx' @@ -134,8 +134,8 @@ export const CLUSTER_INFO: Record = { canonical: 'mlx.hpc.uio.no', subclusters: [{name: 'nvidia', nodes: 'ml[1-3,6-9]'}], uptime: true, - violators: false, - deadweight: false, + violators: true, + deadweight: true, defaultQuery: '*', hasDowntime: true, name: 'ML nodes', @@ -154,7 +154,7 @@ export const CLUSTER_INFO: Record = { ], uptime: true, violators: false, - deadweight: false, + deadweight: true, defaultQuery: 'login* or int*', name: 'Fox', hasDowntime: true, diff --git a/code/dashboard-2/src/hooks/useExportJobQuery.ts b/code/dashboard-2/src/hooks/useExportJobQuery.ts index 4c0d9082..fe1d0d2a 100644 --- a/code/dashboard-2/src/hooks/useExportJobQuery.ts +++ b/code/dashboard-2/src/hooks/useExportJobQuery.ts @@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query' import { AxiosInstance } from 'axios' import useAxios from './useAxios.ts' -import { JOB_QUERY_API_ENDPOINT, QueryKeys } from '../Constants.ts' +import { QUERY_API_ENDPOINT, QueryKeys } from '../Constants.ts' import { prepareJobQueryString } from '../util/query/QueryUtils.ts' import { ExportOptions, JobQueryValues } from '../types' @@ -16,7 +16,7 @@ const exportJobQuery = async (axios: AxiosInstance, jobQueryValues: JobQueryValu } export const useExportJobQuery = (jobQueryValues: JobQueryValues, exportOptions: ExportOptions) => { - const axios = useAxios(JOB_QUERY_API_ENDPOINT) + const axios = useAxios(QUERY_API_ENDPOINT) return useQuery( { enabled: false, diff --git a/code/dashboard-2/src/hooks/useFetchDeadWeight.ts b/code/dashboard-2/src/hooks/useFetchDeadWeight.ts index 43c3c849..bdc0a7e2 100644 --- a/code/dashboard-2/src/hooks/useFetchDeadWeight.ts +++ b/code/dashboard-2/src/hooks/useFetchDeadWeight.ts @@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query' import { AxiosInstance, AxiosResponse } from 'axios' import useAxios from './useAxios.ts' -import { QueryKeys } from '../Constants.ts' +import { QUERY_API_ENDPOINT, QueryKeys } from '../Constants.ts' import { prepareShareableJobQueryLink } from '../util/query/QueryUtils.ts' import { Cluster, @@ -16,19 +16,19 @@ interface Filter { hostname: string | null } -const fetchDeadWeight = async (axios: AxiosInstance, canonical: string, clusterName: string) => { - const endpoint = `${canonical}/${clusterName}-deadweight-report.json` +const fetchDeadWeight = async (axios: AxiosInstance, clusterName: string) => { + const endpoint = `/report?cluster=${clusterName}&report-name=${clusterName}-deadweight-report.json` const response: AxiosResponse = await axios.get(endpoint) return response.data } export const useFetchDeadWeight = (cluster: Cluster, filter: Filter | null = null, enabled: boolean = true) => { - const axios = useAxios() + const axios = useAxios(QUERY_API_ENDPOINT) return useQuery( { enabled, queryKey: [QueryKeys.DEAD_WEIGHT, cluster.cluster], - queryFn: () => fetchDeadWeight(axios, cluster.canonical, cluster.cluster), + queryFn: () => fetchDeadWeight(axios, cluster.cluster), select: data => { let deadWeights: DeadWeight[] = data.map((d: FetchedDeadWeight) => { const commonJobQueryValues = { diff --git a/code/dashboard-2/src/hooks/useFetchJobQuery.ts b/code/dashboard-2/src/hooks/useFetchJobQuery.ts index c6caf2f5..4c01d0e7 100644 --- a/code/dashboard-2/src/hooks/useFetchJobQuery.ts +++ b/code/dashboard-2/src/hooks/useFetchJobQuery.ts @@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query' import { AxiosInstance } from 'axios' import useAxios from './useAxios.ts' -import { JOB_QUERY_API_ENDPOINT, QueryKeys } from '../Constants.ts' +import { QUERY_API_ENDPOINT, QueryKeys } from '../Constants.ts' import { FetchedJobQueryResultItem, JobQueryJobId, @@ -22,7 +22,7 @@ const fetchJobQuery = async (axios: AxiosInstance, jobQueryValues: JobQueryValue } export const useFetchJobQuery = (jobQueryValues: JobQueryValues, fields: string[]) => { - const axios = useAxios(JOB_QUERY_API_ENDPOINT) + const axios = useAxios(QUERY_API_ENDPOINT) return useQuery( { enabled: false, diff --git a/code/dashboard-2/src/hooks/useFetchViolations.ts b/code/dashboard-2/src/hooks/useFetchViolations.ts index 3bd0f97e..6dbaa12f 100644 --- a/code/dashboard-2/src/hooks/useFetchViolations.ts +++ b/code/dashboard-2/src/hooks/useFetchViolations.ts @@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query' import { AxiosInstance, AxiosResponse } from 'axios' import useAxios from './useAxios.ts' -import { QueryKeys } from '../Constants.ts' +import { QUERY_API_ENDPOINT, QueryKeys } from '../Constants.ts' import { Cluster, FetchedViolatingJob, @@ -17,19 +17,19 @@ interface Filter { hostname: string | null } -const fetchViolations = async (axios: AxiosInstance, canonical: string, clusterName: string) => { - const endpoint = `${canonical}/${clusterName}-violator-report.json` +const fetchViolations = async (axios: AxiosInstance, clusterName: string) => { + const endpoint = `/report?cluster=${clusterName}&report-name=${clusterName}-violator-report.json` const response: AxiosResponse = await axios.get(endpoint) return response.data } export const useFetchViolations = (cluster: Cluster, filter: Filter | null = null, enabled: boolean = true) => { - const axios = useAxios() + const axios = useAxios(QUERY_API_ENDPOINT) return useQuery( { enabled, queryKey: [QueryKeys.VIOLATIONS, cluster.cluster], - queryFn: () => fetchViolations(axios, cluster.canonical, cluster.cluster), + queryFn: () => fetchViolations(axios, cluster.cluster), select: (data) => { let violatingJobs: ViolatingJob[] = data.map((d) => { diff --git a/code/dashboard-2/src/hooks/useFetchViolator.ts b/code/dashboard-2/src/hooks/useFetchViolator.ts index 1992e886..703bea57 100644 --- a/code/dashboard-2/src/hooks/useFetchViolator.ts +++ b/code/dashboard-2/src/hooks/useFetchViolator.ts @@ -2,21 +2,21 @@ import { useQuery } from '@tanstack/react-query' import { AxiosInstance, AxiosResponse } from 'axios' import useAxios from './useAxios.ts' -import { POLICIES, QueryKeys } from '../Constants.ts' +import { QUERY_API_ENDPOINT, POLICIES, QueryKeys } from '../Constants.ts' import { Cluster, FetchedViolatingJob } from '../types' -const fetchViolator = async (axios: AxiosInstance, canonical: string, clusterName: string) => { - const endpoint = `${canonical}/${clusterName}-violator-report.json` +const fetchViolator = async (axios: AxiosInstance, clusterName: string) => { + const endpoint = `/report?cluster=${clusterName}&report-name=${clusterName}-violator-report.json` const response: AxiosResponse = await axios.get(endpoint) return response.data } export const useFetchViolator = (cluster: Cluster, violator: string) => { - const axios = useAxios() + const axios = useAxios(QUERY_API_ENDPOINT) return useQuery( { queryKey: [QueryKeys.VIOLATOR, cluster.cluster, violator], - queryFn: () => fetchViolator(axios, cluster.canonical, cluster.cluster), + queryFn: () => fetchViolator(axios, cluster.cluster), select: (data) => { const jobsOfUser = data.filter(job => job.user === violator) diff --git a/code/dashboard-2/src/pages/DeadWeightPage.tsx b/code/dashboard-2/src/pages/DeadWeightPage.tsx index 8b004351..65eaf3d8 100644 --- a/code/dashboard-2/src/pages/DeadWeightPage.tsx +++ b/code/dashboard-2/src/pages/DeadWeightPage.tsx @@ -23,7 +23,7 @@ import { useFetchDeadWeight } from '../hooks/useFetchDeadWeight.ts' export default function DeadWeightPage() { - const {clusterName} = useParams() + const {clusterName, hostname} = useParams() const cluster = findCluster(clusterName) @@ -33,7 +33,12 @@ export default function DeadWeightPage() { ) } - const {data, isFetched} = useFetchDeadWeight(cluster) + const filter = { + afterDate: null, + hostname: hostname || null + } + + const {data, isFetched} = useFetchDeadWeight(cluster, filter) const deadWeightJobTableColumns = useMemo(() => getDeadWeightTableColumns(), [cluster]) const [sorting, setSorting] = useState([]) @@ -49,13 +54,15 @@ export default function DeadWeightPage() { } }) + const pageTitle = `${cluster.name} Deadweight${hostname ? ` - ${hostname}` : ''}` + return ( <> - + - {cluster.name} dead weight + {pageTitle} The following processes and jobs are zombies or defuncts or diff --git a/code/dashboard-2/src/pages/HostDetailsPage.tsx b/code/dashboard-2/src/pages/HostDetailsPage.tsx index fd286a43..b7751aef 100644 --- a/code/dashboard-2/src/pages/HostDetailsPage.tsx +++ b/code/dashboard-2/src/pages/HostDetailsPage.tsx @@ -1,34 +1,26 @@ -import { useMemo, useState } from 'react' +import { useState } from 'react' import { - Box, + Box, Button, ButtonGroup, Checkbox, Divider, Heading, HStack, - Link as ChakraLink, Select, - SlideFade, Spacer, Text, VStack } from '@chakra-ui/react' import { Link as ReactRouterLink, Navigate, useParams } from 'react-router-dom' -import { getCoreRowModel, getSortedRowModel, SortingState, useReactTable } from '@tanstack/react-table' -import { EMPTY_ARRAY, FETCH_FREQUENCIES } from '../Constants.ts' +import { FETCH_FREQUENCIES } from '../Constants.ts' import { useFetchHostnames } from '../hooks/useFetchHosts.ts' import { useFetchHostDetails } from '../hooks/useFetchHostDetails.ts' -import { useFetchViolations } from '../hooks/useFetchViolations.ts' -import { useFetchDeadWeight } from '../hooks/useFetchDeadWeight.ts' import { findCluster } from '../util' -import { - getDeadWeightTableColumns, - getViolatingJobTableColumns, - getViolatingUserTableColumns -} from '../util/TableUtils.ts' import { NavigateBackButton, PageTitle } from '../components' -import { ViolatingUserTable, ViolatingJobTable, DeadWeightTable } from '../components/table' import { MachineDetailsChart } from '../components/chart/MachineDetailsChart' +import { IoSearchOutline } from 'react-icons/io5' +import { GiShamblingZombie } from 'react-icons/gi' +import { PiGavel } from 'react-icons/pi' export default function HostDetailsPage() { @@ -57,64 +49,9 @@ export default function HostDetailsPage() { data: hostDetails } = useFetchHostDetails(selectedCluster.canonical, hostname!, selectedFrequency.value, isShowData, isShowDowntime, isValidHostname) - const now = new Date() - const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000) - - const filter = { - afterDate: thirtyDaysAgo, - hostname: hostname, - } - - const { - data: violations, - isFetched: isFetchedViolations, - } = useFetchViolations(selectedCluster, filter, isValidHostname) - - const violatingUserTableColumns = useMemo(() => getViolatingUserTableColumns(), [selectedCluster]) - const [violatingUserTableSorting, setViolatingUserTableSorting] = useState([]) - - const violatingUserTable = useReactTable({ - columns: violatingUserTableColumns, - data: violations?.byUser || EMPTY_ARRAY, - getCoreRowModel: getCoreRowModel(), - onSortingChange: setViolatingUserTableSorting, - getSortedRowModel: getSortedRowModel(), - state: { - sorting: violatingUserTableSorting, - } - }) - - const violatingJobTableColumns = useMemo(() => getViolatingJobTableColumns(), [selectedCluster]) - const [violatingJobTableSorting, setViolatingJobTableSorting] = useState([]) - - const violatingJobTable = useReactTable({ - columns: violatingJobTableColumns, - data: violations?.byJob || EMPTY_ARRAY, - getCoreRowModel: getCoreRowModel(), - onSortingChange: setViolatingJobTableSorting, - getSortedRowModel: getSortedRowModel(), - state: { - sorting: violatingJobTableSorting, - } - }) - - const {data: deadweights, isFetched: isFetchedDeadweights} = useFetchDeadWeight(selectedCluster, filter, isValidHostname) - - const deadWeightJobTableColumns = useMemo(() => getDeadWeightTableColumns(), [clusterName]) - const [sorting, setSorting] = useState([]) - - const deadWeightTable = useReactTable({ - columns: deadWeightJobTableColumns, - data: deadweights || EMPTY_ARRAY, - getCoreRowModel: getCoreRowModel(), - onSortingChange: setSorting, - getSortedRowModel: getSortedRowModel(), - state: { - sorting: sorting, - } - }) - const jobQueryLink = `/jobquery?cluster=${clusterName}&host=${hostname}` + const violatorsLink = `/${clusterName}/${hostname}/violators` + const deadWeightLink = `/${clusterName}/${hostname}/deadweight` return ( <> @@ -128,10 +65,22 @@ export default function HostDetailsPage() { Description :{'\t'}{hostDetails?.system.description} - - Job Query for this host - + + + {selectedCluster.violators && + + } + {selectedCluster.deadweight && + + } + Machine Load @@ -195,48 +144,6 @@ export default function HostDetailsPage() { measurement is the sum of the sizes of the jobs' private memories. - {selectedCluster.violators && - <> - - Violators last 30 days - - - - The following jobs have violated usage policy and are probably not appropriate to run on this cluster. The - list - is recomputed at noon and midnight and goes back four weeks. - - - By user - - - - - - By job and time - - - - - - } - - {selectedCluster.deadweight && - <> - - Deadweight processes last 30 days - - - The following processes and jobs are zombies or defuncts or - otherwise dead and may be bogging down the system. The list is - recomputed at noon and midnight and goes back four weeks. - - - - - - - } ) diff --git a/code/dashboard-2/src/pages/ViolatorPage.tsx b/code/dashboard-2/src/pages/ViolatorPage.tsx index e7ffc65e..87dac563 100644 --- a/code/dashboard-2/src/pages/ViolatorPage.tsx +++ b/code/dashboard-2/src/pages/ViolatorPage.tsx @@ -41,7 +41,7 @@ export default function ViolatorPage() { ) } - const {data: allJobsOfUser, isFetched} = useFetchViolator(clusterName!, violator) + const {data: allJobsOfUser, isFetched} = useFetchViolator(cluster, violator) const violatingJobTableColumns = useMemo(() => getUserViolatingJobTableColumns(), [cluster]) const [violatingJobTableSorting, setViolatingJobTableSorting] = useState([]) diff --git a/code/dashboard-2/src/pages/ViolatorsPage.tsx b/code/dashboard-2/src/pages/ViolatorsPage.tsx index afe78fee..be4c0bf1 100644 --- a/code/dashboard-2/src/pages/ViolatorsPage.tsx +++ b/code/dashboard-2/src/pages/ViolatorsPage.tsx @@ -16,7 +16,7 @@ import { ViolatingUserTable, ViolatingJobTable } from '../components/table' import { NavigateBackButton, PageTitle } from '../components' export default function ViolatorsPage() { - const {clusterName} = useParams() + const {clusterName, hostname} = useParams() const cluster = findCluster(clusterName) @@ -26,7 +26,12 @@ export default function ViolatorsPage() { ) } - const {data, isFetched} = useFetchViolations(cluster) + const filter = { + afterDate: null, + hostname: hostname || null + } + + const {data, isFetched} = useFetchViolations(cluster, filter) const violatingUserTableColumns = useMemo(() => getViolatingUserTableColumns(), [cluster]) const [violatingUserTableSorting, setViolatingUserTableSorting] = useState([]) @@ -56,13 +61,15 @@ export default function ViolatorsPage() { } }) + const pageTitle = `${cluster.name} Policy Violators${hostname ? ` - ${hostname}` : ''}` + return ( <> - + - {cluster.name} policy violators + {pageTitle} The following users and jobs have been running significantly outside of policy and are probably not appropriate to run on this cluster. The list is recomputed at noon and midnight