Skip to content

Commit

Permalink
Rebalance list
Browse files Browse the repository at this point in the history
Signed-off-by: hemahg <[email protected]>
  • Loading branch information
hemahg committed Sep 27, 2024
1 parent ee5061f commit 3f6312a
Show file tree
Hide file tree
Showing 19 changed files with 425 additions and 39 deletions.
79 changes: 79 additions & 0 deletions ui/api/Rebalance/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { logger } from "@/utils/logger";
import {
RebalanceResponse,
RebalanceResponseSchema,
RebalanceSchema,
RebalancesResponse,
RebalanceStatus,
} from "./schema";
import { filterUndefinedFromObj } from "@/utils/filterUndefinedFromObj";
import { getHeaders } from "@/api/api";

const log = logger.child({ module: "rebalance-api" });

export async function getRebalancesList(
kafkaId: string,
params: {
name?: string;
mode?: string;
status?: RebalanceStatus[];
pageSize?: number;
pageCursor?: string;
sort?: string;
sortDir?: string;
},
): Promise<RebalancesResponse> {
const sp = new URLSearchParams(
filterUndefinedFromObj({
"fields[kafkaRebalances]": "name,namespace,creationTimestamp,status",
"filter[name]": params.name ? `like,*${params.name}*` : undefined,
"filter[status]":
params.status && params.status.length > 0
? `in,${params.status.join(",")}`
: undefined,
"filter[mode]": params.mode ? `like,*${params.mode}*` : undefined,
"page[size]": params.pageSize,
"page[after]": params.pageCursor,
sort: params.sort
? (params.sortDir !== "asc" ? "-" : "") + params.sort
: undefined,
}),
);
const rebalanceQuery = sp.toString();
const url = `${process.env.BACKEND_URL}/api/kafkas/${kafkaId}/rebalances?${rebalanceQuery}`;
const res = await fetch(url, {
headers: await getHeaders(),
next: {
tags: ["rebalances"],
},
});

log.debug({ url }, "getRebalanceList");
const rawData = await res.json();
log.trace({ url, rawData }, "getRebalanceList response");
return RebalanceResponseSchema.parse(rawData);
}

export async function getRebalanceDetails(
kafkaId: string,
rebalanceId: string,
): Promise<RebalanceResponse> {
const url = `${process.env.BACKEND_URL}/api/kafkas/${kafkaId}/rebalances/${rebalanceId}`;
const body = {
data: {
type: "kafkaRebalances",
id: rebalanceId,
meta: {},
attributes: {},
},
};
const res = await fetch(url, {
headers: await getHeaders(),
method: "PATCH",
body: JSON.stringify(body),
});
const rawData = await res.json();
console.log(rawData);
log.debug({ url, rawData }, "get Optimal proposal response");
return RebalanceSchema.parse(rawData);
}
56 changes: 48 additions & 8 deletions ui/api/Rebalance/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,42 @@ const RebalanceStatusSchema = z.union([
z.literal("ReconciliationPaused"),
]);

const RebalanceSchema = z.object({
id: z.string(),
const OptimizationResultSchema = z.object({
numIntraBrokerReplicaMovements: z.number(),
numReplicaMovements: z.number(),
onDemandBalancednessScoreAfter: z.number(),
afterBeforeLoadConfigMap: z.string(),
intraBrokerDataToMoveMB: z.number(),
monitoredPartitionsPercentage: z.number(),
provisionRecommendation: z.string(),
excludedBrokersForReplicaMove: z.array(z.string()),
excludedBrokersForLeadership: z.array(z.string()),
provisionStatus: z.string(),
onDemandBalancednessScoreBefore: z.number(),
recentWindows: z.number(),
dataToMoveMB: z.number(),
excludedTopics: z.array(z.string()),
numLeaderMovements: z.number(),
});

export const RebalanceSchema = z.object({
type: z.literal("kafkaRebalances"),
id: z.string(),
meta: z
.object({
managed: z.boolean().optional(),
autoApproval: z.boolean().optional(),
allowedActions: z.array(z.string()),
})
.optional(),
attributes: z.object({
name: z.string(),
namespace: z.string(),
creationTimestamp: z.string(),
name: z.string().optional(),
namespace: z.string().optional(),
creationTimestamp: z.string().optional(),
status: RebalanceStatusSchema,
mode: z.string(),
brokers: z.array(z.number()),
mode: z.string().optional(),
brokers: z.array(z.number()).optional(),
sessionId: z.string().optional(),
optimizationResult: OptimizationResultSchema,
}),
});

Expand All @@ -45,8 +66,27 @@ const RebalancesListSchema = z.object({
mode: true,
brokers: true,
}),
autoApproval: RebalanceSchema.shape.meta.unwrap().shape.autoApproval,
});

export const RebalanceResponseSchema = z.object({
meta: z.object({
page: z.object({
total: z.number(),
pageNumber: z.number().optional(),
}),
}),
links: z.object({
first: z.string().nullable(),
prev: z.string().nullable(),
next: z.string().nullable(),
last: z.string().nullable(),
}),
data: z.array(RebalancesListSchema),
});
export type RebalanceList = z.infer<typeof RebalancesListSchema>;
export type RebalancesResponse = z.infer<typeof RebalanceResponseSchema>;

export type RebalanceResponse = z.infer<typeof RebalanceSchema>;

export type RebalanceStatus = z.infer<typeof RebalanceStatusSchema>;
2 changes: 1 addition & 1 deletion ui/api/kafka/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export async function getKafkaCluster(
): Promise<ClusterDetail | null> {
const sp = new URLSearchParams({
"fields[kafkas]":
"name,namespace,creationTimestamp,status,kafkaVersion,nodes,controller,authorizedOperations,listeners,conditions,nodePools",
"name,namespace,creationTimestamp,status,kafkaVersion,nodes,controller,authorizedOperations,listeners,conditions,nodePools,cruiseControlEnabled",
});
const kafkaClusterQuery = sp.toString();
const url = `${process.env.BACKEND_URL}/api/kafkas/${clusterId}?${kafkaClusterQuery}`;
Expand Down
1 change: 1 addition & 0 deletions ui/api/kafka/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const ClusterDetailSchema = z.object({
nodes: z.array(NodeSchema),
controller: NodeSchema,
authorizedOperations: z.array(z.string()),
cruiseControlEnabled: z.boolean().optional(),
listeners: z
.array(
z.object({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,49 @@ import { getKafkaCluster } from "@/api/kafka/actions";
import { KafkaParams } from "@/app/[locale]/(authorized)/kafka/[kafkaId]/kafka.params";
import { AppHeader } from "@/components/AppHeader";
import { Number } from "@/components/Format/Number";
import { Label, Spinner, Split, SplitItem } from "@/libs/patternfly/react-core";
import { NavItemLink } from "@/components/Navigation/NavItemLink";
import {
Label,
Nav,
NavList,
PageNavigation,
Spinner,
Split,
SplitItem,
} from "@/libs/patternfly/react-core";
import { CheckCircleIcon } from "@/libs/patternfly/react-icons";
import { Suspense } from "react";

export default function NodesHeader({ params }: { params: KafkaParams }) {
return (
<Suspense fallback={<Header />}>
<Suspense
fallback={<Header kafkaId={undefined} cruiseControlEnable={false} />}
>
<ConnectedHeader params={params} />
</Suspense>
);
}

async function ConnectedHeader({ params }: { params: KafkaParams }) {
const cluster = await getKafkaCluster(params.kafkaId);
return <Header total={cluster?.attributes.nodes.length || 0} />;
return (
<Header
total={cluster?.attributes.nodes.length || 0}
kafkaId={cluster?.id}
cruiseControlEnable={cluster?.attributes.cruiseControlEnabled || false}
/>
);
}

function Header({ total }: { total?: number }) {
function Header({
total,
kafkaId,
cruiseControlEnable,
}: {
total?: number;
kafkaId: string | undefined;
cruiseControlEnable: boolean;
}) {
return (
<AppHeader
title={
Expand All @@ -41,6 +66,22 @@ function Header({ total }: { total?: number }) {
</SplitItem>
</Split>
}
navigation={
<PageNavigation>
<Nav aria-label="Node navigation" variant="tertiary">
<NavList>
<NavItemLink url={`/kafka/${kafkaId}/nodes`}>
Overview
</NavItemLink>
{cruiseControlEnable && (
<NavItemLink url={`/kafka/${kafkaId}/nodes/rebalances`}>
Rebalance
</NavItemLink>
)}
</NavList>
</Nav>
</PageNavigation>
}
/>
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"use client";
import { RebalanceList, RebalanceStatus } from "@/api/Rebalance/schema";
import { useRouter } from "@/navigation";
import { useFilterParams } from "@/utils/useFilterParams";
import { useOptimistic, useState, useTransition } from "react";
Expand All @@ -18,6 +17,7 @@ import {
} from "@/libs/patternfly/react-core";
import { useTranslations } from "next-intl";
import { RebalancesCountCard } from "./RebalancesCountCard";
import { RebalanceList, RebalanceStatus } from "@/api/rebalance/schema";

export type ConnectedReabalancesTableProps = {
rebalances: RebalanceList[] | undefined;
Expand Down Expand Up @@ -56,6 +56,7 @@ export function ConnectedReabalancesTable({
nextPageCursor,
prevPageCursor,
mode,
baseurl,
}: ConnectedReabalancesTableProps) {
const t = useTranslations("Rebalancing");

Expand Down Expand Up @@ -221,6 +222,7 @@ export function ConnectedReabalancesTable({
onClearAllFilters={clearFilters}
onApprove={() => {}}
onStop={() => {}}
baseurl={baseurl}
/>
</GridItem>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { TableView, TableViewProps } from "@/components/Table";
import { useTranslations } from "next-intl";
import { RebalanceList, RebalanceStatus } from "@/api/Rebalance/schema";
import { RebalanceList, RebalanceStatus } from "@/api/rebalance/schema";
import {
Flex,
FlexItem,
Icon,
TextContent,
Text,
DescriptionList,
DescriptionListGroup,
DescriptionListTerm,
DescriptionListDescription,
Level,
LevelItem,
Badge,
} from "@/libs/patternfly/react-core";
import {
CheckIcon,
Expand All @@ -22,6 +19,7 @@ import {
ResourcesFullIcon,
} from "@/libs/patternfly/react-icons";
import { ReactNode } from "react";
import { Link } from "@/navigation";

export const RebalanceTableColumns = ["name", "status", "createdAt"] as const;

Expand Down Expand Up @@ -95,6 +93,7 @@ const StatusLabel: Record<RebalanceStatus, ReactNode> = {
};

export type RebalanceTabelProps = {
baseurl: string;
rebalanceList: RebalanceList[] | undefined;
rebalancesCount: number;
page: number;
Expand All @@ -112,6 +111,7 @@ export type RebalanceTabelProps = {
"isColumnSortable" | "onPageChange" | "onClearAllFilters"
>;
export function RebalanceTable({
baseurl,
isColumnSortable,
rebalanceList,
rebalancesCount,
Expand Down Expand Up @@ -168,8 +168,10 @@ export function RebalanceTable({
switch (column) {
case "name":
return (
<Td key={key} dataLabel={"Topic"} width={10}>
{row.attributes.name}
<Td key={key} dataLabel={"Rebalance Name"} width={10}>
<Link href={`${baseurl}/${row.id}`}>
<Badge key={key}>{t("cr_badge")}</Badge> {row.attributes.name}
</Link>
</Td>
);
case "status":
Expand All @@ -180,7 +182,7 @@ export function RebalanceTable({
);
case "createdAt":
return (
<Td key={key} dataLabel={"Status"}>
<Td key={key} dataLabel={"Created At"}>
{row.attributes.creationTimestamp}
</Td>
);
Expand Down Expand Up @@ -222,15 +224,15 @@ export function RebalanceTable({
{t("auto_approval_enabled")}
</DescriptionListTerm>
<DescriptionListDescription>
{row.attributes.mode}
{row.autoApproval}
</DescriptionListDescription>
</DescriptionListGroup>
</FlexItem>
<FlexItem className={"pf-v5-u-ml-auto-on-xl"}>
<DescriptionListGroup>
<DescriptionListTerm>{t("brokers")}</DescriptionListTerm>
<DescriptionListDescription>
{row.attributes.brokers.join(",")}
{row.attributes.brokers?.join(",")}
</DescriptionListDescription>
</DescriptionListGroup>
</FlexItem>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { KafkaParams } from "@/app/[locale]/(authorized)/kafka/[kafkaId]/kafka.params";

export type KafkaRebalanceParams = KafkaParams & { rebalanceId: string };
Loading

0 comments on commit 3f6312a

Please sign in to comment.