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 5c5b80c830 AMBARI-26549 : Ambari Web React: Add Host Wizard (#4072)
5c5b80c830 is described below
commit 5c5b80c8306267d2b535f6f07a7b103d09482350
Author: Himanshu Maurya <[email protected]>
AuthorDate: Mon Sep 15 00:17:34 2025 +0530
AMBARI-26549 : Ambari Web React: Add Host Wizard (#4072)
---
ambari-web/latest/src/api/VersionsApi.ts | 8 +
.../latest/src/screens/ClusterWizard/constants.ts | 23 ++
.../Hosts/AddHostWizard/AddHostConfigurations.tsx | 196 +++++++++++
.../screens/Hosts/AddHostWizard/Step2Wrapper.tsx | 27 ++
.../Hosts/AddHostWizard/addHostWizardSteps.tsx | 119 +++++++
.../AddHostWizard/wizardDataStore/context.tsx | 359 +++++++++++++++++++++
.../Hosts/AddHostWizard/wizardDataStore/reducer.ts | 44 +++
.../Hosts/AddHostWizard/wizardDataStore/types.ts | 32 ++
ambari-web/latest/src/screens/Hosts/utils.tsx | 24 +-
9 files changed, 831 insertions(+), 1 deletion(-)
diff --git a/ambari-web/latest/src/api/VersionsApi.ts
b/ambari-web/latest/src/api/VersionsApi.ts
index 791a145301..9a3e2ed10e 100644
--- a/ambari-web/latest/src/api/VersionsApi.ts
+++ b/ambari-web/latest/src/api/VersionsApi.ts
@@ -182,6 +182,14 @@ const VersionsApi = {
});
return response.data;
},
+ getRepoDetails: async (stack: string, version: string) => {
+ const url =
`/stacks/${stack}/versions?fields=repository_versions/operating_systems/repositories/*,repository_versions/operating_systems/OperatingSystems/*,repository_versions/RepositoryVersions/*&repository_versions/RepositoryVersions/repository_version=${version}`;
+ const response = await ambariApi.request({
+ url: url,
+ method: "GET",
+ });
+ return response.data;
+ },
};
export default VersionsApi;
diff --git a/ambari-web/latest/src/screens/ClusterWizard/constants.ts
b/ambari-web/latest/src/screens/ClusterWizard/constants.ts
new file mode 100644
index 0000000000..4b35585d80
--- /dev/null
+++ b/ambari-web/latest/src/screens/ClusterWizard/constants.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export const excludeServicesOnDisplay: string[] = [
+ "KERBEROS",
+ "GANGLIA",
+ "MAPREDUCE2",
+];
\ No newline at end of file
diff --git
a/ambari-web/latest/src/screens/Hosts/AddHostWizard/AddHostConfigurations.tsx
b/ambari-web/latest/src/screens/Hosts/AddHostWizard/AddHostConfigurations.tsx
new file mode 100644
index 0000000000..1b6f3fe8bc
--- /dev/null
+++
b/ambari-web/latest/src/screens/Hosts/AddHostWizard/AddHostConfigurations.tsx
@@ -0,0 +1,196 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useContext, useEffect, useState } from "react";
+import { AppContext } from "../../../store/context";
+import ConfigGroupApi from "../../../api/configGroupApi";
+import { cloneDeep, forEach, get, isEmpty, set } from "lodash";
+import Table from "../../../components/Table";
+import { Form } from "react-bootstrap";
+import WizardFooter from "../../../components/StepWizard/WizardFooter";
+import { ContextWrapper } from "../../ClusterWizard";
+import { ActionTypes } from "./wizardDataStore/types";
+import { translate } from "../../../Utils/Utility";
+
+export default function AddHostConfigurations() {
+ const { clusterName, services } = useContext(AppContext);
+ const { Context } = useContext(ContextWrapper);
+ const {
+ dispatch,
+ state,
+ flushStateToDb,
+ stepWizardUtilities: {
+ currentStep,
+ handleNextImperitive,
+ jumpToStep,
+ prevStepNumber,
+ },
+ }: any = useContext(Context);
+
+ const initialConfigs = get(
+ state,
+ `addHostSteps.CONFIGURATIONS.data.configurations`,
+ []
+ );
+
+ const [configGroups, setConfigGroups] = useState({});
+ const [formData, setFormData] = useState(initialConfigs);
+
+ useEffect(() => {
+ getConfigGroups();
+ }, []);
+
+ useEffect(() => {
+ if (!isEmpty(configGroups)) {
+ let data: any = [];
+ forEach(services, (service: any) => {
+ const serviceName = get(service, "ServiceInfo.service_name");
+ data.push({
+ serviceName: serviceName,
+ configGroups: get(configGroups, "items", [])
+ .filter(
+ (configGroup: any) =>
+ serviceName === get(configGroup, "ConfigGroup.tag")
+ )
+ .map((configGroup: any) => {
+ return {
+ ...get(configGroup, "ConfigGroup"),
+ isSelected: false,
+ };
+ }),
+ });
+ });
+ forEach(data, (service) => {
+ service?.configGroups.unshift({
+ cluster_name: clusterName,
+ description: "",
+ desired_configs: [],
+ group_name: "Default",
+ hosts: [],
+ tag: service?.serviceName,
+ isSelected: true,
+ });
+ });
+ setFormData(data);
+ }
+ }, [configGroups]);
+
+ const getConfigGroups = async () => {
+ const response = await ConfigGroupApi.getConfigGroups(clusterName, "*");
+ setConfigGroups(response);
+ };
+
+ const getSelectedConfigGroupName = (configGroups: any[]) => {
+ return configGroups.filter((cg: any) => cg.isSelected)?.[0]?.group_name;
+ };
+
+ const setSelectedConfigGroup = (
+ configGroupName: string,
+ serviceName: string
+ ) => {
+ const formDataCopy = cloneDeep(formData);
+ formDataCopy.forEach((service: any) => {
+ if (get(service, "serviceName") === serviceName) {
+ get(service, "configGroups", []).forEach((cg: any) => {
+ if (get(cg, "group_name") === configGroupName) {
+ set(cg, "isSelected", true);
+ } else {
+ set(cg, "isSelected", false);
+ }
+ });
+ }
+ });
+ setFormData(formDataCopy);
+ };
+
+ const columnInTable = [
+ {
+ header: translate("common.service"),
+ id: "service",
+ width: "40%",
+ cell: (info: any) => {
+ return get(info, "row.original.serviceName");
+ },
+ },
+ {
+ header: translate("common.conf.group"),
+ id: "configGroup",
+ width: "60%",
+ cell: (info: any) => {
+ const serviceName = get(info, "row.original.serviceName");
+ const configGroups = get(info, "row.original.configGroups");
+ return (
+ <Form.Select
+ className="custom-form-control fs-12 w-50"
+ value={getSelectedConfigGroupName(configGroups)}
+ onChange={(e) => {
+ setSelectedConfigGroup(e.target.value, serviceName);
+ }}
+ >
+ {configGroups.map((configGroup: any) => {
+ return (
+ <option
+ key={get(configGroup, "group_name")}
+ value={get(configGroup, "group_name")}
+ >
+ {get(configGroup, "group_name")}
+ </option>
+ );
+ })}
+ </Form.Select>
+ );
+ },
+ },
+ ];
+
+ const moveToNextStep = () => {
+ dispatch({
+ type: ActionTypes.STORE_INFORMATION,
+ payload: {
+ step: currentStep.name,
+ data: { configurations: formData },
+ },
+ });
+ flushStateToDb("next");
+ handleNextImperitive();
+ };
+
+ return (
+ <div>
+ <h2 className="step-title">{translate("addHost.step4.header")}</h2>
+ <p className="make-all-grey step-description">
+ {translate("addHost.step4.title")}
+ </p>
+ <Table data={formData} columns={columnInTable} />
+ <WizardFooter
+ isNextEnabled={true}
+ step={currentStep}
+ onNext={() => {
+ moveToNextStep();
+ }}
+ onCancel={() => {
+ flushStateToDb("cancel");
+ }}
+ onBack={() => {
+ flushStateToDb("back");
+ jumpToStep(prevStepNumber);
+ }}
+ />
+ </div>
+ );
+}
diff --git a/ambari-web/latest/src/screens/Hosts/AddHostWizard/Step2Wrapper.tsx
b/ambari-web/latest/src/screens/Hosts/AddHostWizard/Step2Wrapper.tsx
new file mode 100644
index 0000000000..81155a57fd
--- /dev/null
+++ b/ambari-web/latest/src/screens/Hosts/AddHostWizard/Step2Wrapper.tsx
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useContext } from "react";
+import Step2 from "../../ClusterWizard/Step2";
+import { AddHostContext } from "./wizardDataStore/context";
+
+export default function Step2Wrapper() {
+ const { installedHosts } = useContext(AddHostContext);
+
+ return <Step2 wizardName={"addHost"} installedHosts={installedHosts} />;
+}
diff --git
a/ambari-web/latest/src/screens/Hosts/AddHostWizard/addHostWizardSteps.tsx
b/ambari-web/latest/src/screens/Hosts/AddHostWizard/addHostWizardSteps.tsx
new file mode 100644
index 0000000000..2ac4789f79
--- /dev/null
+++ b/ambari-web/latest/src/screens/Hosts/AddHostWizard/addHostWizardSteps.tsx
@@ -0,0 +1,119 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// import Step10 from "../../ClusterWizard/Step10";
+// import Step3 from "../../ClusterWizard/Step3";
+// import Step8 from "../../ClusterWizard/Step8";
+// import Step9 from "../../ClusterWizard/Step9";
+import AddHostConfigurations from "./AddHostConfigurations";
+import Step2Wrapper from "./Step2Wrapper";
+// import Step6 from "../../ClusterWizard/Step6";
+import { translate } from "../../../Utils/Utility";
+
+function ComponentInProgress() {
+ return <h1>Component In Progress</h1>;
+}
+
+export default {
+ 1: {
+ label: translate("installer.step2.header"),
+ completed: false,
+ Component: <Step2Wrapper />,
+ canGoBack: false,
+ isNextEnabled: false,
+ nextLabel: String(
+ translate("installer.step2.registerAndConfirm")
+ ).toUpperCase(),
+ name: "HOSTS",
+ keysToRemove: [
+ "HOST_STATUS",
+ "SLAVES_AND_CLIENTS",
+ "CONFIGURATIONS",
+ "REVIEW",
+ "INSTALL_START_TEST",
+ ],
+ },
+ 2: {
+ label: translate("installer.step3.header"),
+ completed: false,
+ Component: <ComponentInProgress />,
+ // Component: <Step3 wizardName="addHost" />,
+ canGoBack: true,
+ isNextEnabled: true,
+ onNext: () => {
+ return new Promise((resolve) => resolve("Something"));
+ },
+ name: "HOST_STATUS",
+ keysToRemove: [
+ "SLAVES_AND_CLIENTS",
+ "CONFIGURATIONS",
+ "REVIEW",
+ "INSTALL_START_TEST",
+ ],
+ },
+ 3: {
+ label: translate("installer.step6.header"),
+ completed: false,
+ Component: <ComponentInProgress />,
+ // Component: <Step6 wizardName="addHost" />,
+ canGoBack: true,
+ isNextEnabled: false,
+ name: "SLAVES_AND_CLIENTS",
+ keysToRemove: ["CONFIGURATIONS", "REVIEW", "INSTALL_START_TEST"],
+ },
+ 4: {
+ label: translate("addHost.step4.header"),
+ completed: false,
+ Component: <AddHostConfigurations />,
+ canGoBack: true,
+ isNextEnabled: false,
+ nextLabel: String(translate("common.next")).toUpperCase(),
+ name: "CONFIGURATIONS",
+ keysToRemove: ["REVIEW", "INSTALL_START_TEST"],
+ },
+ 5: {
+ label: translate("common.review"),
+ completed: false,
+ Component: <ComponentInProgress />,
+ // Component: <Step8 wizardName="addHost" />,
+ canGoBack: true,
+ nextLabel: String(translate("common.deploy")).toUpperCase(),
+ isNextEnabled: false,
+ name: "REVIEW",
+ keysToRemove: ["INSTALL_START_TEST"],
+ },
+ 6: {
+ label: translate("installer.step9.header"),
+ completed: false,
+ Component: <ComponentInProgress />,
+ // Component: <Step9 wizardName="addHost" />,
+ canGoBack: false,
+ isNextEnabled: false,
+ name: "INSTALL_START_TEST",
+ keyToRemove: [],
+ },
+ 7: {
+ label: translate("installer.step10.header"),
+ completed: false,
+ Component: <ComponentInProgress />,
+ // Component: <Step10 wizardName="addHost" />,
+ canGoBack: false,
+ isNextEnabled: false,
+ keyToRemove: [],
+ },
+};
diff --git
a/ambari-web/latest/src/screens/Hosts/AddHostWizard/wizardDataStore/context.tsx
b/ambari-web/latest/src/screens/Hosts/AddHostWizard/wizardDataStore/context.tsx
new file mode 100644
index 0000000000..a61e784f61
--- /dev/null
+++
b/ambari-web/latest/src/screens/Hosts/AddHostWizard/wizardDataStore/context.tsx
@@ -0,0 +1,359 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React, {
+ createContext,
+ Dispatch,
+ useContext,
+ useEffect,
+ useReducer,
+ useRef,
+ useState,
+} from "react";
+import { State, Action, ActionTypes } from "./types";
+import { reducer, initialState } from "./reducer";
+import ClusterApi from "../../../../api/clusterApi";
+import { AppContext } from "../../../../store/context";
+import { forEach, get, isEmpty } from "lodash";
+import { excludeServicesOnDisplay } from "../../../ClusterWizard/constants";
+import VersionsApi from "../../../../api/VersionsApi";
+import { HostsApi } from "../../../../api/hostsApi";
+import { getAllComponents } from "../../utils";
+import { ClusterProgressStatus } from "../../../../constants";
+
+interface AddHostContextProps {
+ state: State;
+ dispatch: Dispatch<Action>;
+ stepWizardUtilities?: any;
+ flushStateToDb?: any;
+ installedHosts: string[];
+}
+
+export const AddHostContext = createContext<AddHostContextProps>({
+ state: initialState,
+ dispatch: () => undefined,
+ flushStateToDb: () => undefined,
+ installedHosts: [],
+});
+
+export const AddHostProvider: React.FC<{
+ stepWizardUtilities: any;
+ children: React.ReactNode;
+}> = ({ stepWizardUtilities, children }) => {
+ const [state, dispatch] = useReducer(reducer, initialState);
+ const [currStepData, setCurrStepData] = useState({});
+ const [installedHosts, setInstalledHosts] = useState([]);
+ const { clusterName, services, serviceComponentInfo } =
+ useContext(AppContext);
+
+ const isDataPersisted = useRef(false);
+
+ const getInstalledServices = () => {
+ const serviceNames = services.map((service: any) =>
+ get(service, "ServiceInfo.service_name", "")
+ );
+ let installedServicesData: any = {};
+ forEach(serviceComponentInfo.items, (service: any) => {
+ if (serviceNames.includes(get(service, "StackServices.service_name"))) {
+ installedServicesData[service.StackServices.service_name] = {
+ displayName: service.StackServices.display_name,
+ serviceName: service.StackServices.service_name,
+ serviceType: service.StackServices.service_type,
+ version: service.StackServices.service_version,
+ comments: service.StackServices.comments,
+ selected: true,
+ required: service.StackServices.required_services,
+ isIgnored: false,
+ isHiddenOnDisplay: excludeServicesOnDisplay.includes(
+ service.StackServices.service_name
+ ),
+ };
+ }
+ });
+ dispatch({
+ type: ActionTypes.STORE_INFORMATION,
+ payload: {
+ step: "SERVICES",
+ data: { services: installedServicesData },
+ },
+ });
+ };
+
+ const setClusterName = () => {
+ dispatch({
+ type: ActionTypes.STORE_INFORMATION,
+ payload: {
+ step: "NAME",
+ data: { clusterName },
+ },
+ });
+ };
+
+ const setStackAndVersion = async () => {
+ const response = await VersionsApi.getServices(clusterName);
+ const repoVersionId = get(
+ response,
+ "items.[0].ClusterStackVersions.repository_version",
+ ""
+ );
+ const repo = get(response, "items.[0].repository_versions", []).find(
+ (version: any) => get(version, "RepositoryVersions.id") === repoVersionId
+ )?.RepositoryVersions;
+ const repoVersion = get(repo, "repository_version", "");
+ const repoDisplayName = get(repo, "display_name", "");
+ const stackName = get(response, "items.[0].ClusterStackVersions.stack",
"");
+ const stackVersion = get(
+ response,
+ "items.[0].ClusterStackVersions.version",
+ ""
+ );
+ const repoData = await VersionsApi.getRepoDetails(stackName, repoVersion);
+ const os = get(
+ repoData,
+ "items.[0].repository_versions.[0].operating_systems.[0]",
+ {}
+ );
+ const repos = get(os, "repositories", []).map((repo: any) => {
+ return {
+ id: get(repo, "Repositories.repo_id"),
+ defaultId: get(repo, "Repositories.repo_id"),
+ baseUrl: get(repo, "Repositories.base_url"),
+ name: get(repo, "Repositories.repo_name"),
+ defaultUrl: get(repo, "Repositories.default_base_url"),
+ };
+ });
+ const data = {
+ selectedVersion: {
+ id: repoDisplayName,
+ stack_name: stackName,
+ stack_version: stackVersion,
+ },
+ selectedStack: {
+ id: repoDisplayName,
+ stack_name: stackName,
+ stack_version: stackVersion,
+ },
+ operatingSystems: {
+ [repoDisplayName]: [
+ {
+ os: get(os, "OperatingSystems.os_type", ""),
+ isAdded: true,
+ repos: repos,
+ },
+ ],
+ },
+ };
+ dispatch({
+ type: ActionTypes.STORE_INFORMATION,
+ payload: {
+ step: "VERSION",
+ data: data,
+ },
+ });
+ };
+
+ useEffect(() => {
+ syncUserPersistedData();
+ getHostComponents();
+ }, []);
+
+ useEffect(() => {
+ if (isDataPersisted.current) {
+ flushCurrentData();
+ }
+ }, [state.addHostSteps, currStepData]);
+
+ useEffect(() => {
+ if (!isEmpty(services) && !isEmpty(serviceComponentInfo)) {
+ getInstalledServices();
+ }
+ }, [services, serviceComponentInfo]);
+
+ const getHostComponents = async () => {
+ const response = await HostsApi.getHostComponentsDetails(
+ clusterName,
+ "fields=host_components/HostRoles/state&minimal_response=true"
+ );
+ const hostsList = get(response, "items", []).map((item: any) =>
+ get(item, "Hosts.host_name")
+ );
+ setInstalledHosts(hostsList);
+ let mastersData: any[] = [];
+ const allComponents = getAllComponents(serviceComponentInfo).filter(
+ (c) => get(c, "HostRoles.is_master") === true
+ );
+ forEach(response.items, (host: any, idx: number) => {
+ let masterServicesData: any[] = [];
+ forEach(get(host, "host_components", []), (component) => {
+ const componentName = get(component, "HostRoles.component_name");
+ const componentData = allComponents.find(
+ (c: any) =>
+ get(c, "HostRoles.component_name") ===
+ get(component, "HostRoles.component_name")
+ );
+ if (!isEmpty(componentData)) {
+ masterServicesData.push({
+ display_name: get(componentData, "HostRoles.display_name"),
+ component: componentName,
+ serviceId: get(componentData, "HostRoles.service_name"),
+ isInstalled: true,
+ host_id: idx + 1,
+ hostName: get(host, "Hosts.host_name"),
+ });
+ }
+ });
+ const data = {
+ host_name: get(host, "Hosts.host_name"),
+ masterServices: masterServicesData,
+ };
+ mastersData.push(data);
+ });
+ dispatch({
+ type: ActionTypes.STORE_INFORMATION,
+ payload: {
+ step: "MASTERS",
+ data: { mastersData },
+ },
+ });
+ };
+
+ async function syncUserPersistedData() {
+ try {
+ const persistedData = await ClusterApi.getPersistData("ADD_HOST");
+ if (!isEmpty(get(persistedData, "addHostSteps", {}))) {
+ dispatch({
+ type: ActionTypes.SYNC_STATE,
+ payload: persistedData,
+ });
+ }
+ if (get(persistedData, "activeStep", "")) {
+ try {
+ const activeStepName = get(persistedData, "activeStep");
+ setCurrStepData({
+ progressStatus: ClusterProgressStatus.ADDING_HOST,
+ stepName: activeStepName,
+ });
+ let activeStepNumber = Object.keys(
+ stepWizardUtilities.wizardSteps
+ ).find((stepName) => {
+ return (
+ stepWizardUtilities.wizardSteps?.[stepName]?.name ===
+ activeStepName
+ );
+ });
+ stepWizardUtilities.jumpToStep(Number(activeStepNumber), true);
+ } catch (err) {
+ console.error("Error while jumping to step", err);
+ }
+ } else {
+ getInstalledServices();
+ setClusterName();
+ setStackAndVersion();
+ stepWizardUtilities.jumpToStep(1, true);
+ }
+ } finally {
+ isDataPersisted.current = true;
+ }
+ }
+
+ async function flushCurrentData() {
+ await ClusterApi.postPersistData(
+ JSON.stringify({
+ ADD_HOST: JSON.stringify({
+ ...state,
+ activeStep: get(currStepData, "stepName", ""),
+ }),
+ CLUSTER_STATE: JSON.stringify(currStepData),
+ })
+ );
+ }
+
+ function flushOnCancel() {
+ ClusterApi.postPersistData(
+ JSON.stringify({
+ ADD_HOST: JSON.stringify(initialState),
+ CLUSTER_STATE: JSON.stringify({}),
+ })
+ );
+ window.location.href = "/#/main/hosts";
+ }
+
+ async function flushOnStepChange(nextStep: number) {
+ if (nextStep >= 1) {
+ let nextStepDetails = stepWizardUtilities.wizardSteps?.[nextStep];
+ if (nextStepDetails?.keysToRemove) {
+ nextStepDetails.keysToRemove.forEach((key: string) => {
+ if (state?.addHostSteps?.[key]) {
+ dispatch({
+ type: ActionTypes.REMOVE_KEY,
+ payload: { key },
+ });
+ }
+ });
+ }
+ setCurrStepData({
+ progressStatus: ClusterProgressStatus.ADDING_HOST,
+ stepName: stepWizardUtilities?.wizardSteps?.[nextStep]?.name,
+ });
+ }
+ }
+
+ function flushStateToDb(
+ operation: string = "default",
+ jumpStep: number = -1
+ ) {
+ let activeStep = Object.keys(stepWizardUtilities.wizardSteps).find(
+ (stepName) => {
+ return (
+ stepWizardUtilities.wizardSteps?.[stepName]?.name ===
+ stepWizardUtilities.currentStep.name
+ );
+ }
+ );
+ switch (operation) {
+ case "cancel":
+ flushOnCancel();
+ break;
+ case "back":
+ flushOnStepChange(Number(activeStep) - 1);
+ break;
+ case "next":
+ flushOnStepChange(Number(activeStep) + 1);
+ break;
+ case "jump":
+ flushOnStepChange(jumpStep);
+ break;
+ default:
+ flushCurrentData();
+ }
+ }
+
+ return (
+ <AddHostContext.Provider
+ value={{
+ state,
+ dispatch,
+ stepWizardUtilities,
+ flushStateToDb,
+ installedHosts,
+ }}
+ >
+ {children}
+ </AddHostContext.Provider>
+ );
+};
diff --git
a/ambari-web/latest/src/screens/Hosts/AddHostWizard/wizardDataStore/reducer.ts
b/ambari-web/latest/src/screens/Hosts/AddHostWizard/wizardDataStore/reducer.ts
new file mode 100644
index 0000000000..f5acb46222
--- /dev/null
+++
b/ambari-web/latest/src/screens/Hosts/AddHostWizard/wizardDataStore/reducer.ts
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { cloneDeep } from "lodash";
+import { Action, ActionTypes, State } from "./types";
+
+export const initialState: State = { addHostSteps: {} };
+
+export const reducer = (state: State, action: Action): State => {
+ switch (action.type) {
+ case ActionTypes.STORE_INFORMATION:
+ const stateCopy = cloneDeep(state);
+ if(!stateCopy.addHostSteps) {
+ stateCopy.addHostSteps = {};
+ }
+ const addHostSteps = cloneDeep(stateCopy.addHostSteps);
+ addHostSteps[action.payload.step] = action.payload;
+ stateCopy.addHostSteps = addHostSteps;
+ return stateCopy;
+ case ActionTypes.SYNC_STATE:
+ return { ...action.payload };
+ case ActionTypes.REMOVE_KEY:
+ const updatedSteps = { ...state.addHostSteps };
+ delete updatedSteps[action.payload.key];
+ return { ...state, addHostSteps: updatedSteps };
+ default:
+ return state;
+ }
+};
diff --git
a/ambari-web/latest/src/screens/Hosts/AddHostWizard/wizardDataStore/types.ts
b/ambari-web/latest/src/screens/Hosts/AddHostWizard/wizardDataStore/types.ts
new file mode 100644
index 0000000000..0588835b16
--- /dev/null
+++ b/ambari-web/latest/src/screens/Hosts/AddHostWizard/wizardDataStore/types.ts
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export interface State {
+ addHostSteps: any;
+}
+
+export enum ActionTypes {
+ STORE_INFORMATION = "STORE INFORMATION",
+ SYNC_STATE = "SYNC STATE",
+ REMOVE_KEY = "REMOVE KEY",
+}
+
+export type Action =
+ | { type: ActionTypes.STORE_INFORMATION; payload: any }
+ | { type: ActionTypes.SYNC_STATE; payload: any }
+ | { type: ActionTypes.REMOVE_KEY; payload: any };
diff --git a/ambari-web/latest/src/screens/Hosts/utils.tsx
b/ambari-web/latest/src/screens/Hosts/utils.tsx
index 3dfca4f1bc..1ff1f4665e 100644
--- a/ambari-web/latest/src/screens/Hosts/utils.tsx
+++ b/ambari-web/latest/src/screens/Hosts/utils.tsx
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-import { get } from "lodash";
+import { get, isEmpty } from "lodash";
import { ComponentType } from "./enums";
import { translate, translateWithVariables } from "../../Utils/Utility";
@@ -68,3 +68,25 @@ export const validateInteger = (
}
return "";
};
+
+export const getAllComponents = (serviceComponentInfo: any) => {
+ if (!isEmpty(serviceComponentInfo)) {
+ let allComponentsCopy: any[] = [];
+ get(serviceComponentInfo, "items", []).forEach((service: any) => {
+ allComponentsCopy = allComponentsCopy.concat(
+ get(service, "components", []).map((component: any) => {
+ return {
+ HostRoles: {
+ ...get(component, "StackServiceComponents"),
+ dependencies: get(component, "dependencies", []).map(
+ (d: any) => d.Dependencies.component_name
+ ),
+ },
+ };
+ })
+ );
+ });
+ return allComponentsCopy;
+ }
+ return [];
+};
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]