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]

Reply via email to