This is an automated email from the ASF dual-hosted git repository.
arshad pushed a commit to branch frontend-refactor
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/frontend-refactor by this push:
new d5db621320 AMBARI-26391: Ambari Web React: Implement upgrade options
layout modal & upgrade checks (#4067)
d5db621320 is described below
commit d5db621320dc063209f944077886fd1fac550386
Author: Sandeep Kumar <[email protected]>
AuthorDate: Sun Sep 14 21:46:50 2025 +0530
AMBARI-26391: Ambari Web React: Implement upgrade options layout modal &
upgrade checks (#4067)
* AMBARI-26391: Ambari Web React: Implement upgrade options layout modal
* AMBARI-26392: Ambari Web React: implement upgrade checks.
---
ambari-web/latest/src/api/AssignMastersApi.ts | 2 +-
ambari-web/latest/src/api/VersionsApi.ts | 26 +-
.../ClusterAdmin/StackAndVersions/ListVersion.tsx | 702 ++++++++++++++++++++-
.../screens/ClusterAdmin/StackAndVersions/types.ts | 41 ++
4 files changed, 757 insertions(+), 14 deletions(-)
diff --git a/ambari-web/latest/src/api/AssignMastersApi.ts
b/ambari-web/latest/src/api/AssignMastersApi.ts
index 3b7bfd213b..aa3b246b3c 100644
--- a/ambari-web/latest/src/api/AssignMastersApi.ts
+++ b/ambari-web/latest/src/api/AssignMastersApi.ts
@@ -21,7 +21,7 @@ import { ambariApi } from "./config/axiosConfig";
const AssignMastersApi = {
getCpuInfo: async function (HOSTS: any) {
const hostsParams = HOSTS.join(",");
- const url =
`/hosts?Hosts/host_name.in(${hostsParams})&fields=Hosts/cpu_count,Hosts/disk_info,Hosts/total_mem,Hosts/ip,Hosts/os_type,Hosts/os_arch,Hosts/public_host_name&minimal_response=true&_=1731567268225`;
+ const url =
`/hosts?Hosts/host_name.in(${hostsParams})&fields=Hosts/cpu_count,Hosts/disk_info,Hosts/total_mem,Hosts/ip,Hosts/os_type,Hosts/os_arch,Hosts/public_host_name&minimal_response=true`;
const response = await ambariApi.request({
url: url,
method: "GET",
diff --git a/ambari-web/latest/src/api/VersionsApi.ts
b/ambari-web/latest/src/api/VersionsApi.ts
index b911562307..78dc68a462 100644
--- a/ambari-web/latest/src/api/VersionsApi.ts
+++ b/ambari-web/latest/src/api/VersionsApi.ts
@@ -33,7 +33,31 @@ const VersionsApi = {
method: "GET",
});
return response.data;
- },
+ },
+ get_supported_upgradeTypes: async function (
+ stackName: string,
+ stackVersion: string,
+ toVersion: string
+ ) {
+ const url =
`/stacks/${stackName}/versions/${stackVersion}/compatible_repository_versions?CompatibleRepositoryVersions/repository_version=${toVersion}`;
+ const response = await ambariApi.request({
+ url: url,
+ method: "GET",
+ });
+ return response.data;
+ },
+ runPreUpgradeCheck: async function (
+ clusterName: string,
+ toId: string,
+ upgradeType: string
+ ) {
+ const url =
`/clusters/${clusterName}/rolling_upgrades_check?fields=*&UpgradeChecks/repository_version_id=${toId}&UpgradeChecks/upgrade_type=${upgradeType}`;
+ const response = await ambariApi.request({
+ url: url,
+ method: "GET",
+ });
+ return response.data;
+ },
};
export default VersionsApi;
diff --git
a/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/ListVersion.tsx
b/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/ListVersion.tsx
index 3dca8a5e22..0b200b9069 100644
---
a/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/ListVersion.tsx
+++
b/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/ListVersion.tsx
@@ -16,16 +16,25 @@
* limitations under the License.
*/
-import { useContext, useEffect, useRef, useState } from "react";
+import { JSX, useContext, useEffect, useRef, useState } from "react";
import {
Button,
ButtonGroup,
Dropdown,
+ Form,
OverlayTrigger,
Tooltip,
} from "react-bootstrap";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faEdit, faExternalLink } from "@fortawesome/free-solid-svg-icons";
+import {
+ faBolt,
+ faDashboard,
+ faEdit,
+ faExternalLink,
+ faTimes,
+ faWarning,
+ IconDefinition,
+} from "@fortawesome/free-solid-svg-icons";
import VersionsApi from "../../../api/VersionsApi";
import toast from "react-hot-toast";
import Spinner from "../../../components/Spinner";
@@ -38,7 +47,8 @@ import { Link } from "react-router-dom";
import { AppContext } from "../../../store/context";
import { RequestApi } from "../../../api/requestApi";
import OperationsProgress from "../../../components/OperationProgress";
-import { StackVersion } from "./types";
+import { ClusterCheckPopupData, Item, Response, StackVersion } from "./types";
+import { translate, translateWithVariables } from "../../../Utils/Utility";
const initialOptions = [
{ key: "ALL", values: ["ALL"], count: 0 },
@@ -58,6 +68,69 @@ const initialOptions = [
{ key: "READY TO FINALIZE", values: ["Ready to Finalize"], count: 0 },
];
+const initialUpgradeMethods = [
+ {
+ displayName: translate(
+ "admin.stackVersions.version.upgrade.upgradeOptions.RU.title"
+ ),
+ type: "ROLLING",
+ icon: "faDashboard",
+ description: translate(
+ "admin.stackVersions.version.upgrade.upgradeOptions.RU.description"
+ ),
+ selected: false,
+ allowed: true,
+ isCheckComplete: false,
+ isCheckRequestInProgress: false,
+ precheckResultsMessage: "",
+ preCheckResultsModalContent: null as JSX.Element | null,
+ precheckResultsTitle: "",
+ action: "",
+ isWizardRestricted: false,
+ },
+ {
+ displayName: translate(
+ "admin.stackVersions.version.upgrade.upgradeOptions.EU.title"
+ ),
+ type: "NON_ROLLING",
+ icon: "faBolt",
+ description: translate(
+ "admin.stackVersions.version.upgrade.upgradeOptions.EU.description"
+ ),
+ selected: false,
+ allowed: true,
+ isCheckComplete: false,
+ isCheckRequestInProgress: false,
+ precheckResultsMessage: "",
+ preCheckResultsModalContent: null as JSX.Element | null,
+ precheckResultsTitle: "",
+ action: "",
+ isWizardRestricted: false,
+ },
+ {
+ displayName: translate(
+ "admin.stackVersions.version.upgrade.upgradeOptions.HOU.title"
+ ),
+ type: "HOST_ORDERED",
+ icon: "faBolt",
+ description: "",
+ selected: false,
+ allowed: false,
+ isCheckComplete: false,
+ isCheckRequestInProgress: false,
+ precheckResultsMessage: "",
+ preCheckResultsModalContent: null as JSX.Element | null,
+ precheckResultsTitle: "",
+ action: "",
+ cantBeStarted: true,
+ },
+];
+
+const iconMapping: { [key: string]: IconDefinition } = {
+ faDashboard: faDashboard,
+ faBolt: faBolt,
+};
+
export default function Versions() {
const [loading, setLoading] = useState(false);
const [options, setOptions] = useState(initialOptions);
@@ -65,6 +138,9 @@ export default function Versions() {
const [services, setServices] = useState<string[]>([]);
const [originalStacks, setOriginalStacks] = useState<StackVersion[]>([]);
const [stacks, setStacks] = useState<StackVersion[]>([]);
+ const [upgradeMethods, setUpgradeMethods] = useState(initialUpgradeMethods);
+ const [methodType, setMethodType] = useState("");
+ const [currentUpgradeTypes, setCurrentUpgradeTypes] = useState<string[]>([]);
const { clusterName } = useContext(AppContext);
const [versionModal, setVersionModal] = useState(false);
@@ -72,6 +148,10 @@ export default function Versions() {
const [manageVersionModal, setManageVersionsModal] = useState(false);
const [repoModal, setRepoModal] = useState(false);
const [hostModal, setHostModal] = useState(false);
+ const [upgradeModal, setUpgradeModal] = useState(false);
+ const [upgradeCheckModal, setUpgradeCheckModal] = useState(false);
+ const [upgradeConfirmationModal, setUpgradeConfirmationModal] =
+ useState(false);
const [, setCompletionStatus] = useState(false);
const [selectedStack, setSelectedStack] = useState<
@@ -82,8 +162,13 @@ export default function Versions() {
const hostModalContent = useRef("");
const hostModalTitle = useRef("");
+ const upgradeModalContent = useRef<JSX.Element | null>(null);
+ const upgradeMethodsRef = useRef(initialUpgradeMethods);
+ const upgradeOkButton = useRef<Boolean>(true);
+ const showRerunButton = useRef<Boolean>(true);
+ const showUpgradeProceedButton = useRef<boolean>(true);
- const {} = usePolling(fetchServices, 1000);
+ const {} = usePolling(fetchServices, 6000);
useEffect(() => {
if (selectedOption.key !== "ALL") {
@@ -157,12 +242,111 @@ export default function Versions() {
fetchServices();
}, []);
- const handleSelectChange = (selected: any) => {
- const option = options.find((o) => o.key === selected.value);
- if (option) {
- setSelectedOption(option);
+ useEffect(() => {
+ setUpgradeMethods(upgradeMethodsRef.current);
+ }, [upgradeMethodsRef.current]);
+
+ useEffect(() => {
+ upgradeMethodsRef.current = upgradeMethods;
+
+ // Update the upgrade modal content when upgrade methods change
+ if (upgradeModal && selectedStack) {
+ const modalContent = (
+ <div className="upgrade-modal">
+ <div>
+ You are about to perform an upgrade to{" "}
+ <strong>
+ {selectedStack?.repository_versions[0]?.RepositoryVersions
+ ?.display_name || "Unknown"}
+ </strong>
+ </div>
+ <div className="pt-1">Choose the upgrade method:</div>
+
+ <div className="upgrade-options-container">
+ {upgradeMethods
+ .filter(
+ (method) =>
+ method.allowed && currentUpgradeTypes.includes(method.type)
+ )
+ .map((method) => (
+ <div
+ key={method.type}
+ className={`upgrade-method ${
+ methodType === method.type ? "selected" : ""
+ }`}
+ onClick={() => {
+ setMethodType(method.type);
+ if (method.precheckResultsMessage.includes("Required")) {
+ upgradeOkButton.current = true;
+ } else {
+ upgradeOkButton.current = false;
+ }
+ }}
+ >
+ <FontAwesomeIcon
+ className="upgrade-method-icon"
+ icon={iconMapping[method.icon]}
+ />
+ <div className="upgrade-method-title">
+ {method.displayName}
+ </div>
+ <div className="upgrade-method-description">
+ {method.description}
+ </div>
+ <div
+ className="upgrade-method-checks"
+ onClick={(e) => {
+ e.stopPropagation();
+ showRerunButton.current = true;
+ setUpgradeCheckModal(true);
+ setMethodType(method.type);
+ }}
+ >
+ {method.precheckResultsMessage.includes("Required") ? (
+ <FontAwesomeIcon
+ className="text-danger mx-1"
+ icon={faTimes}
+ />
+ ) : (
+ <FontAwesomeIcon
+ className="text-warning mx-1"
+ icon={faWarning}
+ />
+ )}
+ Checks: {method.precheckResultsMessage}
+ </div>
+ </div>
+ ))}
+ </div>
+
+ <div className="upgrade-failure-tolerance">
+ <div>Select optional upgrade failure tolerance:</div>
+ <Form className="pt-2">
+ <Form.Group controlId="serviceCheck">
+ <Form.Check
+ type="checkbox"
+ label="Skip all Service Check failures"
+ />
+ </Form.Group>
+ <Form.Group controlId="slaveComponentFailures">
+ <Form.Check
+ type="checkbox"
+ label="Skip all Slave Component failures"
+ />
+ </Form.Group>
+ </Form>
+ </div>
+
+ <div className="upgrade-warning">
+ Cluster alerts will still be visible and recorded in Ambari but
+ notifications (such as Email and SNMP) will be suppressed during
the
+ upgrade.
+ </div>
+ </div>
+ );
+ upgradeModalContent.current = modalContent;
}
- };
+ }, [upgradeMethods, upgradeModal, methodType]);
if (loading) {
return <Spinner />;
@@ -192,6 +376,13 @@ export default function Versions() {
}),
];
+ const handleSelectChange = (selected: any) => {
+ const option = options.find((o) => o.key === selected.value);
+ if (option) {
+ setSelectedOption(option);
+ }
+ };
+
function getStackHeader(stackData: StackVersion) {
return (
<div>
@@ -231,7 +422,13 @@ export default function Versions() {
</Link>
) : getButtonName(stackData) === "UPGRADE" ? (
<Dropdown as={ButtonGroup}>
- <Button variant="success">Upgrade</Button>
+ <Button
+ variant="success"
+ className="text-uppercase"
+ onClick={() => handleUpgradeButton(stackData)}
+ >
+ Upgrade
+ </Button>
<Dropdown.Toggle split variant="success" id="dropdown" />
<Dropdown.Menu>
<Dropdown.Item
@@ -239,7 +436,18 @@ export default function Versions() {
>
Re-install
</Dropdown.Item>
- <Dropdown.Item>Pre-upgrade check</Dropdown.Item>
+ <Dropdown.Item
+ onClick={() => {
+ showUpgradeProceedButton.current = false;
+ if (selectedStack) {
+ handleUpgradeClick(selectedStack);
+ } else {
+ toast.error("No stack selected for upgrade.");
+ }
+ }}
+ >
+ Pre-upgrade check
+ </Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
) : (
@@ -278,7 +486,8 @@ export default function Versions() {
case "CURRENT":
return;
case "UPGRADE":
- return "show upgrade modal";
+ handleUpgradeClick(stackData);
+ return;
default:
installPackagesPayloadSet(stackData);
}
@@ -485,6 +694,415 @@ export default function Versions() {
setHostModal(true);
}
+ async function handleUpgradeClick(stack: StackVersion) {
+ const response = await VersionsApi.get_supported_upgradeTypes(
+ stack.ClusterStackVersions.stack,
+ stack.ClusterStackVersions.version,
+ stack.repository_versions[0].RepositoryVersions.repository_version
+ );
+ const upgradeTypes =
+ response.items[0].CompatibleRepositoryVersions.upgrade_types;
+
+ // Store upgrade types in state instead of in the stack object
+ setCurrentUpgradeTypes(upgradeTypes);
+
+ // run upgrade method checks.
+ const updateMethods = upgradeMethodsRef.current.map((method) => ({
+ ...method,
+ isCheckRequestInProgress: true,
+ isCheckComplete: false,
+ }));
+ upgradeMethodsRef.current = updateMethods;
+ setUpgradeMethods(updateMethods);
+
+ // Initial modal content - will be updated when pre-check results are
available
+ const modalContent = (
+ <div className="upgrade-modal">
+ <div>
+ You are about to perform an upgrade to{" "}
+ <strong>
+ {stack.repository_versions[0].RepositoryVersions.display_name}
+ </strong>
+ </div>
+ <div className="pt-1">Choose the upgrade method:</div>
+
+ <div className="upgrade-options-container">
+ {upgradeMethods
+ .filter(
+ (method) => method.allowed && upgradeTypes.includes(method.type)
+ )
+ .map((method) => (
+ <div
+ key={method.type}
+ className={`upgrade-method ${
+ methodType === method.type ? "selected" : ""
+ }`}
+ onClick={() => {
+ setMethodType(method.type);
+ if (method.precheckResultsMessage.includes("Required")) {
+ upgradeOkButton.current = true;
+ } else {
+ upgradeOkButton.current = false;
+ }
+ }}
+ >
+ <FontAwesomeIcon
+ className="upgrade-method-icon"
+ icon={iconMapping[method.icon]}
+ />
+ <div
className="upgrade-method-title">{method.displayName}</div>
+ <div className="upgrade-method-description">
+ {method.description}
+ </div>
+ <div
+ className="upgrade-method-checks"
+ onClick={(e) => {
+ e.stopPropagation();
+ showRerunButton.current = true;
+ setUpgradeCheckModal(true);
+ setMethodType(method.type);
+ }}
+ >
+ {method.isCheckRequestInProgress ? (
+ <span>Loading checks...</span>
+ ) : (
+ <>
+ {method.precheckResultsMessage.includes("Required") ? (
+ <FontAwesomeIcon
+ className="text-danger mx-1"
+ icon={faTimes}
+ />
+ ) : (
+ <FontAwesomeIcon
+ className="text-warning mx-1"
+ icon={faWarning}
+ />
+ )}
+ Checks: {method.precheckResultsMessage}
+ </>
+ )}
+ </div>
+ </div>
+ ))}
+ </div>
+
+ <div className="upgrade-failure-tolerance">
+ <div>Select optional upgrade failure tolerance:</div>
+ <Form className="pt-2">
+ <Form.Group controlId="serviceCheck">
+ <Form.Check
+ type="checkbox"
+ label="Skip all Service Check failures"
+ />
+ </Form.Group>
+ <Form.Group controlId="slaveComponentFailures">
+ <Form.Check
+ type="checkbox"
+ label="Skip all Slave Component failures"
+ />
+ </Form.Group>
+ </Form>
+ </div>
+
+ <div className="upgrade-warning">
+ Cluster alerts will still be visible and recorded in Ambari but
+ notifications (such as Email and SNMP) will be suppressed during the
+ upgrade.
+ </div>
+ </div>
+ );
+ upgradeModalContent.current = modalContent;
+ setUpgradeModal(true);
+
+ // Run the pre-upgrade checks after setting the initial modal content
+ runUpgradeMethodCheck(stack);
+ }
+
+ async function runPreUpgradeCheckOnly(data: any) {
+ const methodIndex = upgradeMethodsRef.current.findIndex(
+ (method) => method.displayName === data.type
+ );
+ if (methodIndex !== -1) {
+ const updatedMethods = [...upgradeMethodsRef.current];
+ updatedMethods[methodIndex] = {
+ ...updatedMethods[methodIndex],
+ isCheckComplete: false,
+ isCheckRequestInProgress: true,
+ action: "",
+ };
+ upgradeMethodsRef.current = updatedMethods;
+ setUpgradeMethods(updatedMethods);
+ }
+
+ // run pre upgrade check
+ const response = await VersionsApi.runPreUpgradeCheck(
+ clusterName,
+ data.id,
+ data.type
+ );
+
+ let failTitle = translate("popup.clusterCheck.Upgrade.fail.title");
+ let failAlert = translate("popup.clusterCheck.Upgrade.fail.alert");
+ const bypassedFailures =
+ response?.items?.filter(
+ (item: Item) => item.UpgradeChecks?.status === "BYPASS"
+ ).length > 0;
+ if (
+ response.items.filter(
+ (item: Item) => item.UpgradeChecks.status === "ERROR"
+ ).length === 0 &&
+ bypassedFailures
+ ) {
+ failTitle = translate(
+ "popup.clusterCheck.Upgrade.bypassed-failures.title"
+ );
+ failAlert = translate(
+ "popup.clusterCheck.Upgrade.bypassed-failures.alert"
+ );
+ }
+ const header = translateWithVariables("popup.clusterCheck.Upgrade.header",
{
+ "0":
+ selectedStack?.repository_versions[0]?.RepositoryVersions
+ ?.display_name || "Unknown",
+ });
+
+ const warningTitle = translate("popup.clusterCheck.Upgrade.warning.title");
+ const warningAlert = translate("popup.clusterCheck.Upgrade.warning.alert");
+ const configsMergeWarning = response.items.find(
+ (item: Item) => item.UpgradeChecks.id === "CONFIG_MERGE"
+ );
+ const popupData = {
+ href: "",
+ items: response.items.filter(
+ (item: Item) => item.UpgradeChecks.id !== "CONFIG_MERGE"
+ ),
+ };
+ const configs = getConfigsWarnings(configsMergeWarning);
+
+ showClusterCheckPopup(
+ popupData,
+ {
+ header: header,
+ failTitle: failTitle,
+ failAlert: failAlert,
+ warningTitle: warningTitle,
+ warningAlert: warningAlert,
+ primary: translate(
+ "admin.stackVersions.version.upgrade.upgradeOptions.preCheck.rerun"
+ ),
+ secondary: translate("common.cancel"),
+ bypassedFailures: bypassedFailures,
+ },
+ configs,
+ data.type
+ );
+ }
+
+ function showClusterCheckPopup(
+ data: Response,
+ popup: ClusterCheckPopupData,
+ configs: any[],
+ methodType: string
+ ) {
+ const fails = data.items.filter(
+ (item: Item) => item.UpgradeChecks.status === "FAIL"
+ ),
+ warnings = data.items.filter(
+ (item: Item) => item.UpgradeChecks.status === "WARNING"
+ ),
+ bypass = data.items.filter(
+ (item: Item) => item.UpgradeChecks.status === "BYPASS"
+ ),
+ configsMergeConflicts = configs?.filter(
+ (config) => config.wasModified === false
+ ),
+ configsRecommendations = configs?.filter(
+ (config) => config.wasModified === true
+ );
+
+ const upgradeCheckModalContent1 = (
+ <>
+ {fails.length > 0 && (
+ <div>
+ <h2>{popup.failTitle}</h2>
+ <div className="alert alert-warning">{popup.failAlert}</div>
+ <div>{mapUpgradeChecks(fails)}</div>
+ </div>
+ )}
+ {warnings.length > 0 && (
+ <div>
+ <h2>{popup.warningTitle}</h2>
+ <div className="alert alert-warning">{popup.warningAlert}</div>
+ <div>{mapUpgradeChecks(warnings)}</div>
+ </div>
+ )}
+ {bypass.length > 0 && (
+ <div>
+ <h5>{popup.failTitle}</h5>
+ <p>{popup.failAlert}</p>
+ <div>{mapUpgradeChecks(bypass)}</div>
+ </div>
+ )}
+ {configsMergeConflicts?.length > 0 && (
+ <div>
+ <h5>Configuration Merge Conflicts</h5>
+ <ul>
+ {configsMergeConflicts.map((config, index) => (
+ <li key={index}>
+ {config.name}: {config.currentValue} (current) vs{" "}
+ {config.recommendedValue} (recommended)
+ </li>
+ ))}
+ </ul>
+ <p>Resolve the conflicts before proceeding with the upgrade.</p>
+ </div>
+ )}
+ {configsRecommendations?.length > 0 && (
+ <div>
+ <h5>Configuration Recommendations</h5>
+ <ul>
+ {configsRecommendations.map((config, index) => (
+ <li key={index}>
+ {config.name}: {config.currentValue} (current) vs{" "}
+ {config.recommendedValue} (recommended)
+ </li>
+ ))}
+ </ul>
+ <p>
+ Review the recommendations before proceeding with the upgrade.
+ </p>
+ </div>
+ )}
+ </>
+ );
+
+ let upgradeCheckResult1 =
+ fails.length > 0 || warnings.length > 0
+ ? `${fails.length > 0 ? `${fails.length} Required` : ""}` +
+ `${
+ warnings.length > 0
+ ? (fails.length > 0 ? ", " : "") + `${warnings.length} Warning`
+ : ""
+ }`
+ : "Passed";
+
+ // set the content of modal & message in the method.
+ const methodIndex = upgradeMethodsRef.current.findIndex(
+ (method) => method.type === methodType
+ );
+
+ if (methodIndex !== -1) {
+ const updatedMethods = [...upgradeMethodsRef.current];
+ updatedMethods[methodIndex] = {
+ ...updatedMethods[methodIndex],
+ isCheckComplete: true,
+ isCheckRequestInProgress: false,
+ precheckResultsMessage: upgradeCheckResult1,
+ preCheckResultsModalContent: upgradeCheckModalContent1,
+ };
+ upgradeMethodsRef.current = updatedMethods;
+ setUpgradeMethods(updatedMethods);
+ }
+ }
+
+ function mapUpgradeChecks(items: Item[]) {
+ return items.map((item, index) => {
+ const { failed_on, reason, check } = item.UpgradeChecks || {};
+ return (
+ <>
+ <ul>
+ <li key={index}>
+ <div className="text-dark mb-2">
+ <FontAwesomeIcon icon={faTimes} className="text-danger" />{" "}
+ {check}
+ </div>
+ <pre className="scrollable-container py-2">
+ <pre className="border-none">Reason: {reason}</pre>
+ <br />
+ <span>Failed on: {failed_on}</span>
+ </pre>
+ </li>
+ </ul>
+ </>
+ );
+ });
+ }
+
+ function getConfigsWarnings(configsMergeWarning: any) {
+ let configs = [];
+ if (
+ configsMergeWarning &&
+ configsMergeWarning.UpgradeChecks?.status === "WARNING"
+ ) {
+ const configsMergeCheckData =
+ configsMergeWarning.UpgradeChecks?.failed_detail;
+ if (configsMergeCheckData && Array.isArray(configsMergeCheckData)) {
+ configs = configsMergeCheckData.reduce((allConfigs, item) => {
+ const isDeprecated = item.new_stack_value == null;
+ const willBeRemoved = item.result_value == null;
+
+ return allConfigs.concat({
+ type: item.type,
+ name: item.property,
+ wasModified:
+ !isDeprecated &&
+ !willBeRemoved &&
+ item.current === item.result_value,
+ currentValue: item.current,
+ recommendedValue: isDeprecated
+ ? "Deprecated"
+ : item.new_stack_value,
+ isDeprecated: isDeprecated,
+ resultingValue: willBeRemoved
+ ? "Will be removed"
+ : item.result_value,
+ willBeRemoved: willBeRemoved,
+ });
+ }, []);
+ }
+ }
+ return configs;
+ }
+
+ function runUpgradeMethodCheck(stack: StackVersion) {
+ const updateMethods = upgradeMethodsRef.current.map((method) => {
+ if (method.allowed) {
+ runPreUpgradeCheckOnly({
+ id: stack.ClusterStackVersions.id,
+ label: stack.repository_versions[0].RepositoryVersions.display_name,
+ type: method.type,
+ });
+ } else {
+ return {
+ ...method,
+ isCheckRequestInProgress: false,
+ isCheckComplete: false,
+ action: "",
+ };
+ }
+ return method;
+ });
+ upgradeMethodsRef.current = updateMethods;
+ setUpgradeMethods(updateMethods);
+ }
+
+ function getPreCheckResultsModalContent() {
+ const method = upgradeMethodsRef.current.find(
+ (method) => method.type === methodType
+ );
+ return get(method, "preCheckResultsModalContent", null);
+ }
+
+ function getPreCheckModalTitle() {
+ return `Upgrade to
${selectedStack?.repository_versions[0].RepositoryVersions.display_name}`;
+ }
+
+ function getUpgradConfirmationModalBody() {
+ const stackVersion =
+ selectedStack?.repository_versions[0].RepositoryVersions.display_name;
+ return `You are about to perform an ${methodType} Upgrade to
${stackVersion}. This will incur cluster downtime. Are you sure you want to
proceed?`;
+ }
+
return (
<>
<div className="mt-4">
@@ -584,6 +1202,66 @@ export default function Versions() {
}}
/>
)}
+
+ <Modal
+ isOpen={upgradeModal}
+ onClose={() => setUpgradeModal(false)}
+ modalTitle="Upgrade Options"
+ modalBody={upgradeModalContent.current}
+ options={{
+ cancelableViaBtn: true,
+ cancelableViaIcon: true,
+ okButtonText: "PROCEED",
+ cancelButtonText: "CANCEL",
+ okButtonDisabled: !!upgradeOkButton.current,
+ }}
+ successCallback={() => {
+ if (showUpgradeProceedButton) {
+ setUpgradeModal(false);
+ setUpgradeConfirmationModal(true);
+ }
+ }}
+ />
+ {upgradeCheckModal ? (
+ <Modal
+ isOpen={upgradeCheckModal}
+ onClose={() => setUpgradeCheckModal(false)}
+ modalTitle={getPreCheckModalTitle()}
+ modalBody={getPreCheckResultsModalContent()}
+ options={{
+ modalSize: "modal-lg",
+ cancelableViaBtn: true,
+ cancelableViaIcon: true,
+ okButtonText: showRerunButton.current
+ ? "RERUN PRE-UPGRADE CHECKS"
+ : "Proceed anyway",
+ cancelButtonText: "CANCEL",
+ modalBodyClassName: "scrollable",
+ }}
+ successCallback={async () => {
+ setUpgradeCheckModal(false);
+ }}
+ />
+ ) : null}
+ {upgradeConfirmationModal && (
+ <Modal
+ modalTitle="Confirmation"
+ isOpen={upgradeConfirmationModal}
+ onClose={() => setUpgradeConfirmationModal(false)}
+ modalBody={getUpgradConfirmationModalBody()}
+ options={{
+ cancelableViaBtn: true,
+ cancelableViaIcon: true,
+ okButtonText: "YES",
+ cancelButtonText: "CANCEL",
+ }}
+ successCallback={() => {
+ setUpgradeConfirmationModal(false);
+ showRerunButton.current = false;
+ setUpgradeCheckModal(true);
+ }}
+ />
+ )}
</>
);
}
diff --git
a/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/types.ts
b/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/types.ts
index a03939ddde..d04e08d4ed 100644
--- a/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/types.ts
+++ b/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/types.ts
@@ -125,6 +125,43 @@ type StackVersion = {
repository_versions: RepositoryVersion[];
}
+type UpgradeCheck = {
+ check: string;
+ check_type: string;
+ cluster_name: string;
+ failed_detail: Array<{
+ state: string;
+ label: string;
+ host_name: string;
+ }>;
+ failed_on: string[];
+ id: string;
+ reason: string;
+ repository_version_id: number;
+ status: string;
+ upgrade_type: string;
+}
+
+type Item = {
+ href: string;
+ UpgradeChecks: UpgradeCheck;
+}
+
+type Response = {
+ href: string;
+ items: Item[];
+}
+type ClusterCheckPopupData = {
+ header: any;
+ failTitle: any;
+ failAlert: any;
+ warningTitle: any;
+ warningAlert: any;
+ primary: any;
+ secondary: any;
+ bypassedFailures: boolean;
+}
+
export type {
RepositoryVersion,
Service,
@@ -134,4 +171,8 @@ export type {
Repository,
ClusterStackVersion,
StackVersion,
+ UpgradeCheck,
+ Item,
+ Response,
+ ClusterCheckPopupData
};
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]