/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
import { EuiInMemoryTable, EuiSpacer, EuiLink, EuiFlyout, EuiButton, EuiEmptyPrompt, EuiHealth } from "@elastic/eui";
import _ from "lodash";
import React, { useEffect, useContext, useState, useMemo } from "react";
import { SnapshotManagementService } from "../../../../services";
import { CoreServicesContext } from "../../../../components/core_services";
import { getToasts } from "../../helper"
import { Toast, ModifiedStages, IndexItem } from "../../../../models/interfaces"
import { GetIndexRecoveryResponse } from "../../../../../server/models/interfaces";
import { BREADCRUMBS, restoreIndicesCols } from "../../../../utils/constants";
import { ContentPanel } from "../../../../components/ContentPanel";
import IndexList from "../IndexList";
interface RestoreActivitiesPanelProps {
snapshotManagementService: SnapshotManagementService;
onOpenError: () => void;
sendError: (error: object) => void;
sendToasts: (toasts: Toast[]) => void;
snapshotId: string;
restoreStartRef: number;
indicesToRestore: string[];
}
const intervalIds: ReturnType[] = [];
export const RestoreActivitiesPanel = (
{
onOpenError,
sendError,
sendToasts,
snapshotManagementService,
snapshotId,
restoreStartRef,
indicesToRestore
}: RestoreActivitiesPanelProps) => {
const context = useContext(CoreServicesContext);
const [startTime, setStartTime] = useState("");
const [stopTime, setStopTime] = useState("");
const [stage, setStage] = useState("");
const [indices, setIndices] = useState([]);
const [flyout, setFlyout] = useState(false);
const [statusOk, setStatusOk] = useState(true)
const restoreCount = indicesToRestore.length;
useEffect(() => {
context?.chrome.setBreadcrumbs([BREADCRUMBS.SNAPSHOT_MANAGEMENT, BREADCRUMBS.SNAPSHOTS, BREADCRUMBS.SNAPSHOT_RESTORE]);
if (statusOk && stage !== "Completed (100%)") {
intervalIds.push(setInterval(() => {
getRestoreStatus();
}, 2000))
return () => {
intervalIds.forEach((id) => {
clearInterval(id);
});
}
}
}, [stage]);
const getRestoreStatus = async () => {
const percent = stage.slice(stage.indexOf("("));
const failedStage = `Failed ${percent}`
if (!restoreStartRef) {
return;
}
try {
const res = await snapshotManagementService.getIndexRecovery();
if (res.ok) {
const response: GetIndexRecoveryResponse = res.response;
setRestoreStatus(response);
} else {
const toasts = getToasts(
"error_restore_toast",
"",
snapshotId,
onOpenError
);
res.error = res.error.concat(`, please check your connection`);
setStage(failedStage);
sendError(res);
sendToasts(toasts)
intervalIds.forEach((id) => {
clearInterval(id);
});
return;
}
} catch (err) {
const toasts = getToasts(
"error_restore_toast",
"",
snapshotId,
onOpenError
);
setStage(failedStage);
setStatusOk(false);
sendError(err);
sendToasts(toasts)
}
};
const onIndexesClick = async (e: React.MouseEvent) => {
e.preventDefault();
await getRestoreStatus();
setFlyout(true);
};
const onCloseFlyout = () => {
setFlyout(false);
};
const setRestoreStatus = (response: GetIndexRecoveryResponse) => {
let minStartTime: number = 0;
let maxStopTime: number = 0;
let stageIndex: number = 6;
let doneCount: number = 0;
const indexes: IndexItem[] = [];
const stages: string[] = ["START", "INIT", "INDEX", "VERIFY_INDEX", "TRANSLOG", "FINALIZE", "DONE"];
const modifiedStages: ModifiedStages = {
START: "Starting",
INIT: "Initializing",
INDEX: "Copying",
VERIFY_INDEX: "Verifying",
TRANSLOG: "Replaying translog",
FINALIZE: "Cleaning up",
DONE: "Completed"
}
const lastStage = stages.length - 1;
// Loop through indices in response, filter out kibana index,
// gather progress info then use it to create progress field values.
for (let item in response) {
const responseItem = item as keyof GetIndexRecoveryResponse;
if (
item.indexOf("kibana") < 0 &&
response[responseItem].shards &&
response[responseItem].shards[0].start_time_in_millis >= restoreStartRef
) {
const info = response[responseItem].shards[0];
const stage = stages.indexOf(info.stage);
const time = {
start_time: info.start_time_in_millis,
stop_time: info.stop_time_in_millis ? info.stop_time_in_millis : Date.now()
};
doneCount = stage === lastStage ? doneCount + 1 : doneCount;
stageIndex = stage < stageIndex ? stage : stageIndex;
maxStopTime = maxStopTime && maxStopTime > time.stop_time ? maxStopTime : time.stop_time;
const indexStatus: string = modifiedStages[info.stage as keyof ModifiedStages];
if (info.source.index && info.source.snapshot === snapshotId) {
minStartTime = minStartTime && minStartTime < time.start_time ? minStartTime : time.start_time;
indexes.push({ index: info.source.index, "restore_status": indexStatus });
}
}
}
const updatedIndices: IndexItem[] = [...indexes];
const indicesStarted = indexes.map((index) => index.index);
for (let index of indicesToRestore) {
if (indicesStarted.indexOf(index) < 0) {
updatedIndices.push({ index, restore_status: "Pending" })
}
}
const sortedUpdatedIndices = updatedIndices.sort((a, b) => {
return a.index - b.index;
});
const percent = Math.floor((doneCount / restoreCount) * 100);
setIndices(sortedUpdatedIndices);
setStopTime(new Date(maxStopTime).toLocaleString().replace(",", " "));
setStartTime(new Date(minStartTime).toLocaleString().replace(",", " "))
if (stages[stageIndex]) {
stageIndex = (stageIndex === lastStage && doneCount < restoreCount) ? 2 : stageIndex;
setStage(`${modifiedStages[stages[stageIndex] as keyof ModifiedStages]} (${percent}%)`);
}
};
const actions = useMemo(() => (
[
Refresh
,
]
), []);
const currentStage = stage.slice(0, stage.indexOf(" "))
const color = currentStage === "Completed" ? "success" : currentStage === "Failed" ? "failure" : "warning";
const indexText = `${restoreCount === 1 ? "1 Index" : `${restoreCount} Indices`}`
const restoreStatus = [
{
start_time: startTime,
stop_time: stopTime,
snapshot: snapshotId,
status: stage,
indexes: indexText,
},
];
const columns = [
{
field: "start_time",
name: "Start time",
},
{
field: "stop_time",
name: "Completion time",
},
{
field: "snapshot",
name: "Snapshot name",
},
{
field: "status",
name: "Status",
render: (text: string) => {text}
},
{
field: "indexes",
name: "Indices being restored",
render: (text: string) => {text},
},
];
const message = (There are no restore activities.
} titleSize="s">)
return (
<>
{flyout &&
}
>
);
};