From 6ab684abdd85f1aa8e9572171888b31a1dd69e6e Mon Sep 17 00:00:00 2001 From: shaur97 Date: Tue, 24 Feb 2026 01:37:32 +0530 Subject: [PATCH] AMBARI-26590: Models and Updater hooks for services --- .../hooks/useAmbariMetricsConfigUpdater.ts | 290 ++++ .../latest/src/hooks/useHDFSConfigUpdater.ts | 1213 +++++++++++++++++ .../latest/src/hooks/useHbaseConfigUpdater.ts | 530 +++++++ .../latest/src/hooks/useHiveConfigUpdater.ts | 296 ++++ .../src/hooks/useKerberosConfigUpdater.ts | 131 ++ .../src/hooks/useKyuubiConfigUpdater.ts | 180 +++ .../src/hooks/useMapReduce2ConfigUpdater.ts | 210 +++ .../latest/src/hooks/usePinotConfigUpdater.ts | 223 +++ .../src/hooks/useRangerConfigUpdater.ts | 416 ++++++ .../src/hooks/useRangerKMSConfigUpdater.ts | 172 +++ .../latest/src/hooks/useSSMConfigUpdater.ts | 251 ++++ .../src/hooks/useSpark3ConfigUpdater.ts | 301 ++++ .../latest/src/hooks/useTezConfigUpdater.ts | 134 ++ .../src/hooks/useTrinoGatewayConfigUpdater.ts | 214 +++ .../latest/src/hooks/useYarnConfigUpdater.ts | 821 +++++++++++ .../latest/src/hooks/useZkConfigUpdater.ts | 177 +++ ambari-web/latest/src/models/Tez.ts | 49 + .../latest/src/models/ambari_metrics.ts | 63 + ambari-web/latest/src/models/hbase.ts | 169 +++ ambari-web/latest/src/models/hdfs.ts | 367 +++++ ambari-web/latest/src/models/hive.ts | 52 + ambari-web/latest/src/models/kerberos.ts | 44 + ambari-web/latest/src/models/kyuubi.ts | 40 + ambari-web/latest/src/models/mapreduce2.ts | 55 + ambari-web/latest/src/models/pinot.ts | 88 ++ ambari-web/latest/src/models/ranger.ts | 72 + ambari-web/latest/src/models/ranger_kms.ts | 38 + ambari-web/latest/src/models/spark3.ts | 81 ++ ambari-web/latest/src/models/sqoop.ts | 51 + ambari-web/latest/src/models/ssm.ts | 60 + ambari-web/latest/src/models/trino.ts | 67 + ambari-web/latest/src/models/trinogateway.ts | 49 + ambari-web/latest/src/models/yarn.ts | 291 ++++ ambari-web/latest/src/models/zookeeper.ts | 52 + 34 files changed, 7247 insertions(+) create mode 100644 ambari-web/latest/src/hooks/useAmbariMetricsConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/useHDFSConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/useHbaseConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/useHiveConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/useKerberosConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/useKyuubiConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/useMapReduce2ConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/usePinotConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/useRangerConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/useRangerKMSConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/useSSMConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/useSpark3ConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/useTezConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/useTrinoGatewayConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/useYarnConfigUpdater.ts create mode 100644 ambari-web/latest/src/hooks/useZkConfigUpdater.ts create mode 100644 ambari-web/latest/src/models/Tez.ts create mode 100644 ambari-web/latest/src/models/ambari_metrics.ts create mode 100644 ambari-web/latest/src/models/hbase.ts create mode 100644 ambari-web/latest/src/models/hdfs.ts create mode 100644 ambari-web/latest/src/models/hive.ts create mode 100644 ambari-web/latest/src/models/kerberos.ts create mode 100644 ambari-web/latest/src/models/kyuubi.ts create mode 100644 ambari-web/latest/src/models/mapreduce2.ts create mode 100644 ambari-web/latest/src/models/pinot.ts create mode 100644 ambari-web/latest/src/models/ranger.ts create mode 100644 ambari-web/latest/src/models/ranger_kms.ts create mode 100644 ambari-web/latest/src/models/spark3.ts create mode 100644 ambari-web/latest/src/models/sqoop.ts create mode 100644 ambari-web/latest/src/models/ssm.ts create mode 100644 ambari-web/latest/src/models/trino.ts create mode 100644 ambari-web/latest/src/models/trinogateway.ts create mode 100644 ambari-web/latest/src/models/yarn.ts create mode 100644 ambari-web/latest/src/models/zookeeper.ts diff --git a/ambari-web/latest/src/hooks/useAmbariMetricsConfigUpdater.ts b/ambari-web/latest/src/hooks/useAmbariMetricsConfigUpdater.ts new file mode 100644 index 00000000000..8aba7d5c3ac --- /dev/null +++ b/ambari-web/latest/src/hooks/useAmbariMetricsConfigUpdater.ts @@ -0,0 +1,290 @@ +import { useContext, useEffect } from "react"; +import { cloneDeep, find, get, isEmpty, isEqual } from "lodash"; +import { ServiceApi } from "../api/ServiceApi.ts"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { updateServiceAlertsAndStateFromCentralizedApi } from "../Utils/centralizedServiceStateUtils.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums.ts"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import { + componentFinishStates, + maintenanceStates, +} from "../screens/Hosts/constants.ts"; +import { Categories } from "../enums/Categories.ts"; + +export const useAmbariMetricsConfigUpdater = () => { + // @ts-ignore + const { services, clusterName, parsedSocketMessages } = useContext(AppContext); + + // Early return if AMBARI_METRICS service is not installed + const isAmbariMetricsInstalled = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "AMBARI_METRICS"); + + if (!isAmbariMetricsInstalled) { + return; + } + + const { polledHostComponentsData, masterSlaveClientsData, serviceStatesData } = + useContext(ServiceContext); + //@ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + //const vdpStackVersion = get(cluster, "version", "").split("-")[1]; + + const fetchAmbariMetricsMasterSlaveClientsData = async () => { + // 🚀 OPTIMIZATION: Try centralized cache first, fallback to masterSlaveClientsData + + let ambariMetricsComponentsData = cachedServiceApi.getServiceComponentData("AMBARI_METRICS"); + + if (!ambariMetricsComponentsData) { + ambariMetricsComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "AMBARI_METRICS" + ); + } else { + } + + return ambariMetricsComponentsData; + }; + + const updateComponentObjectForSelectMaster = (componentData: any) => { + let masterComponent = find( + //@ts-ignore + polledHostComponentsData?.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "AMBARI_METRICS" && + get(item, "ServiceComponentInfo.component_name") === + componentData.componentName + ); + + if (masterComponent) { + const hostComponents = get(masterComponent, "host_components"); + if (hostComponents && hostComponents.length > 0) { + hostComponents.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + componentData.componentName + ) { + componentData.hostComponents.forEach((host: any) => { + const polledHostComponentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const componentDataHostName = get(host, "HostRoles.host_name"); + if (componentDataHostName === polledHostComponentHostName) { + host.state = get(hostComponent, "HostRoles.state"); + host.passiveState = get( + hostComponent, + "HostRoles.maintenance_state" + ); + } + }); + } + }); + } + } + return componentData; + }; + + const findMasterSlaveComponents = async () => { + const items = await fetchAmbariMetricsMasterSlaveClientsData(); + + if (!allServiceModels["ambari_metrics"]) { + return; + } + + const currentConfig = cloneDeep(allServiceModels["ambari_metrics"]); + let masterComponents: any[] = []; + let slaveComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.MASTER) { + if ( + componentData.componentName === "METRICS_COLLECTOR" || + componentData.componentName === "METRICS_GRAFANA" + ) { + const masterComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + masterComponents.push(masterComponentDataWithState); + } + } else if (componentData.category === Categories.SLAVE) { + slaveComponents.push(componentData); + } + }); + + currentConfig[ + ServiceComponentMetricsEnums.AMBARI_METRICS.masterComponents + ] = masterComponents; + currentConfig[ServiceComponentMetricsEnums.AMBARI_METRICS.slaveComponents] = + slaveComponents; + + if (!isEqual(allServiceModels["ambari_metrics"], currentConfig)) { + allServiceModels["ambari_metrics"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + //check for updating metrics monitor + //@ts-ignore + const updateAmbariMetricsHostComponentsData = async () => { + let updatedConfig = cloneDeep(allServiceModels["ambari_metrics"]); + const serviceName = "AMBARI_METRICS"; // Replace with the desired service name + const fields = `ServiceComponentInfo/service_name,ServiceComponentInfo/category,ServiceComponentInfo/installed_count,ServiceComponentInfo/started_count,ServiceComponentInfo/init_count,ServiceComponentInfo/install_failed_count,ServiceComponentInfo/unknown_count,ServiceComponentInfo/total_count,ServiceComponentInfo/display_name,host_components/HostRoles/host_name&minimal_response=true`; + await ServiceApi.getAllServiceComponentsListAndInitialMetrics( + clusterName, + `${fields}&ServiceComponentInfo/service_name=${serviceName}` + ); + + const components = [{ name: "METRICS_MONITOR", metric: "metricsMonitors" }]; + + components.forEach((component) => { + // const hostComponents = findHostComponentItems("RANGER", component.name, response); + const hostComponents = { ServiceComponentInfo: {} as any }; + + if (!hostComponents || !hostComponents.ServiceComponentInfo) { + return; // Skip if hostComponents is undefined or doesn't have ServiceComponentInfo + } + + const installedCount = + hostComponents.ServiceComponentInfo.installed_count; + const startedCount = hostComponents.ServiceComponentInfo.started_count; + const totalCount = hostComponents.ServiceComponentInfo.total_count; + updatedConfig[ + ServiceComponentMetricsEnums.AMBARI_METRICS[ + `${component.metric}Started` as keyof typeof ServiceComponentMetricsEnums.AMBARI_METRICS + ] as any + ] = startedCount; + updatedConfig[ + ServiceComponentMetricsEnums.AMBARI_METRICS[ + `${component.metric}Installed` as keyof typeof ServiceComponentMetricsEnums.AMBARI_METRICS + ] as any + ] = installedCount; + updatedConfig[ + ServiceComponentMetricsEnums.AMBARI_METRICS[ + `${component.metric}Total` as keyof typeof ServiceComponentMetricsEnums.AMBARI_METRICS + ] as any + ] = totalCount; + }); + + if (!isEqual(allServiceModels["ambari_metrics"], updatedConfig)) { + allServiceModels["ambari_metrics"].updateConfig(updatedConfig); + updateRegistry(allServiceModels); + } + }; + + const parseWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.destination === "/events/hostcomponents" + ); + } + + if ( + latestHostOperationMessage && + latestHostOperationMessage.hostComponents + ) { + for (const hostComponent of latestHostOperationMessage.hostComponents) { + if (hostComponent.currentState in componentFinishStates) { + //await updateRangerHostComponentsData(); + await findMasterSlaveComponents(); + } + } + } + }; + + + const updateGrafanaComponent = async () => { + //@ts-ignore + if (!polledHostComponentsData?.items) return; + + const currentConfig = cloneDeep(allServiceModels["ambari_metrics"]); + + let grafana = find( + //@ts-ignore + polledHostComponentsData.items, + (item: any) => + get(item, "ServiceComponentInfo.service_name") === "AMBARI_METRICS" && + get(item, "ServiceComponentInfo.component_name") === "METRICS_GRAFANA" + ); + grafana.state = get(grafana, "HostRoles.state"); + currentConfig[ServiceComponentMetricsEnums.AMBARI_METRICS.grafana] = + grafana; + + // Only update if we have changes + if ( + !isEqual(allServiceModels["ambari_metrics"], currentConfig) && + !isEmpty(currentConfig) + ) { + allServiceModels["ambari_metrics"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep(allServiceModels["ambari_metrics"]); + configToBeUpdated.isInPassiveForService = + maintenanceState === maintenanceStates[0]; //signifies ON, assigns ON/PFF + + if (!isEqual(allServiceModels["ambari_metrics"], configToBeUpdated)) { + allServiceModels["ambari_metrics"].updateConfig(configToBeUpdated); + updateRegistry(allServiceModels); + } + }; + + const updateAlertsAndServiceStateData = async () => { + // Use centralized service state API instead of individual call + updateServiceAlertsAndStateFromCentralizedApi("AMBARI_METRICS", "ambari_metrics", allServiceModels, updateRegistry); + }; + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === "AMBARI_METRICS" + ); + } + if ( + latestHostOperationMessage && + (componentFinishStates.includes(latestHostOperationMessage.state) || + (latestHostOperationMessage.maintenance_state && maintenanceStates.includes(latestHostOperationMessage.maintenance_state))) + ) { + await updateServiceMaintenanceState( + latestHostOperationMessage.maintenance_state + ); + await updateAlertsAndServiceStateData(); + } + }; + + //usePolling(pollServiceComponentInfoApi, 1000); + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + findMasterSlaveComponents(); + updateGrafanaComponent(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + //pollServiceComponentInfoApi(); + findMasterSlaveComponents(); + updateAmbariMetricsHostComponentsData(); + }, []); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); + + useEffect(() => { + parseWebSocketMessages(); + parseAlertsWebSocketMessages(); + }, [parsedSocketMessages]); +}; diff --git a/ambari-web/latest/src/hooks/useHDFSConfigUpdater.ts b/ambari-web/latest/src/hooks/useHDFSConfigUpdater.ts new file mode 100644 index 00000000000..e2f3f288d9a --- /dev/null +++ b/ambari-web/latest/src/hooks/useHDFSConfigUpdater.ts @@ -0,0 +1,1213 @@ +import { useContext, useEffect, useRef, useState } from "react"; +import { cloneDeep, find, get, isEmpty, isEqual, map, set } from "lodash"; +import { ServiceApi } from "../api/ServiceApi"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { centralizedServiceStateApi } from "../api/CentralizedServiceStateApi.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import { + componentFinishStates, + maintenanceStates, +} from "../screens/Hosts/constants"; +import { Categories } from "../enums/Categories"; +import useHDFSConfigsTags from "./useConfigsTags.ts"; + +//START GENAI +export const useHDFSConfigUpdater = () => { + const { + polledHostComponentsData, + masterSlaveClientsData, + serviceStatesData, + } = useContext(ServiceContext); + +//END GENAI + // @ts-ignore + const { services } = useContext(AppContext); + + // Early return if HDFS service is not installed + const isHDFSInstalled = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "HDFS"); + + if (!isHDFSInstalled) { + return; + } + + const { configsData } = useHDFSConfigsTags(); + + // @ts-ignore + const { clusterName } = useContext(AppContext); + // @ts-ignore + const { parsedSocketMessages } = useContext(AppContext); + + // @ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + const [serverClockTime, setServerClockTime] = useState(); + //const vdpStackVersion = get(cluster, "version", "").split("-")[1]; + const hasNameNodeHAEnabledUseEffectRunOnce = useRef(false); + const [isHAEnabledForNamenode, setIsHAEnabledForNamenode] = useState(false); + + // useEffect(() => { + // if (allServiceModels?.["hdfs"]) { + // console.log("namespace obj ", inferNamespace()); + // inferNamespace(); + // } + // }, [JSON.stringify(allServiceModels?.["hdfs"]?.masterComponents)]); + + useEffect(() => { + if (allServiceModels?.["hdfs"]) { + inferNamespace(); + } + }, [allServiceModels["hdfs"]?.isNameNodeHaEnabled]); + + // Update masterComponentGroups when configs change (e.g., after federation is enabled) + useEffect(() => { + if (allServiceModels?.["hdfs"] && configsData?.items) { + inferNamespace(); + } + }, [JSON.stringify(configsData?.items)]); + + const updateWorkStatusValues = () => { + if (!allServiceModels["hdfs"]) { + return; + } + const masterComponents = allServiceModels["hdfs"].masterComponents; + const slaveComponents = allServiceModels["hdfs"].slaveComponents; + const clientComponents = allServiceModels["hdfs"].clientComponents; + const currentConfig = cloneDeep(allServiceModels["hdfs"]); + //create a host state map + const hostStateMap: { [key: string]: { healthStatus: any; state: any } } = + {}; + masterComponents.forEach((masterComponent: { hostComponents: any[] }) => { + masterComponent.hostComponents.forEach((host: any) => { + if (hostStateMap[host.HostRoles.host_name]) { + return; + } + const healthStatusMappedValue = host.healthStatusMappedValue; + const hostName = host.HostRoles.host_name; + const hostState = host.state; + hostStateMap[hostName] = { + healthStatus: healthStatusMappedValue, + state: hostState, + }; + }); + }); + slaveComponents.forEach((slaveComponent: any) => { + slaveComponent.hostComponents.forEach((host: any) => { + if (hostStateMap[host.HostRoles.host_name]) { + return; + } + const healthStatusMappedValue = host.healthStatusMappedValue; + const hostName = host.HostRoles.host_name; + const hostState = host.state; + hostStateMap[hostName] = { + healthStatus: healthStatusMappedValue, + state: hostState, + }; + }); + }); + clientComponents.forEach((clientComponent: any) => { + clientComponent.hostComponents.forEach((host: any) => { + if (hostStateMap[host.HostRoles.host_name]) { + return; + } + const healthStatusMappedValue = host.healthStatusMappedValue; + const hostName = host.HostRoles.host_name; + const hostState = host.state; + // if (!hostStateMap[hostName]) { + hostStateMap[hostName] = { + healthStatus: healthStatusMappedValue, + state: hostState, + }; + }); + }); + currentConfig.workStatusValues = hostStateMap; + if (!isEqual(allServiceModels["hdfs"], currentConfig)) { + allServiceModels["hdfs"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + const fetchServerCLockTime = async () => { + const fields = "?fields=RootServiceComponents/server_clock"; + const responseData = await ServiceApi.ambariService(fields); + const serverClock = get( + responseData, + "RootServiceComponents.server_clock", + null + ); + setServerClockTime(serverClock); + }; + + function inferNamespace() { + const hdfsModel = cloneDeep(allServiceModels["hdfs"]); + const isHAEnabled = hdfsModel?.isNameNodeHaEnabled; + if (isHAEnabled) { + const hdfsSiteConfigs = find(configsData?.items, ["type", "hdfs-site"]); + if (hdfsSiteConfigs) { + const properties = get(hdfsSiteConfigs, "properties", {}); + if (properties) { + const nameSpaceProperty = properties["dfs.nameservices"]; + if (nameSpaceProperty) { + const nameSpaces = nameSpaceProperty + .split(",") + .map((nameSpace: any) => { + const nameNodeIdsProperty = + properties[`dfs.ha.namenodes.${nameSpace}`]; + if (nameNodeIdsProperty) { + const nameNodeIds = nameNodeIdsProperty.split(","), + hostNames = nameNodeIds.map((id: any) => { + const propertyValue = + properties[ + `dfs.namenode.http-address.${nameSpace}.${id}` + ], + matches = + propertyValue && + propertyValue.match(/([\D\d]+)\:\d+$/), + hostName = matches && matches[1]; + return hostName; + }); + return { + nameSpace, + hostNames, + }; + } + }); + const componentsCopy = cloneDeep( + get(hdfsModel, "masterComponents") + ); + const allNameNodes = map( + componentsCopy.find( + (component: any) => component.componentName === "NAMENODE" + )?.hostComponents, + "HostRoles" + ); + + allNameNodes.forEach((component) => { + const nameSpaceObject = nameSpaces.find( + (ns: any) => + ns && + ns.hostNames && + ns.hostNames.includes(component.host_name) + ); + if (nameSpaceObject) { + set(component, "haNameSpace", nameSpaceObject.nameSpace); + } + }); + + // Create masterComponentGroups for federation detection + const masterComponentGroups = nameSpaces + .filter((ns: any) => ns && ns.nameSpace && ns.hostNames) + .map((ns: any) => ({ + name: ns.nameSpace, + title: ns.nameSpace, + hosts: ns.hostNames.filter((host: string) => host), // Filter out null/undefined hosts + components: ["NAMENODE", "ZKFC"], + clusterId: "default" + })); + + hdfsModel.updateConfig({ + namespaces: nameSpaces, + isNamespaceLoaded: true, + masterComponents: componentsCopy, + federationNamespaces: masterComponentGroups, + }); + } + } + } + } else { + // For non-HA HDFS, create a single default namespace group + const defaultMasterComponentGroups = [{ + name: "default", + title: "default", + hosts: hdfsModel.masterComponents + ?.find((comp: any) => comp.componentName === "NAMENODE") + ?.hostComponents?.map((hc: any) => hc.HostRoles.host_name) || [], + components: ["NAMENODE"], + clusterId: "default" + }]; + + hdfsModel.updateConfig({ + isNamespaceLoaded: true, + federationNamespaces: defaultMasterComponentGroups, + }); + } + updateRegistry({ ...allServiceModels, ...{ hdfs: hdfsModel as any } }); + } + + const fetchHDFSMasterSlaveClientsData = async () => { + if (isHAEnabledForNamenode) + allServiceModels["hdfs"].isNameNodeHaEnabled = true; + + return Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "HDFS" + ); + }; + const updateComponentObjectForSelectMaster = (componentData: any) => { + let masterComponent = find( + //@ts-ignore + polledHostComponentsData?.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "HDFS" && + get(item, "ServiceComponentInfo.component_name") === + componentData.componentName + ); + + if (masterComponent) { + const hostComponents = get(masterComponent, "host_components"); + if (hostComponents && hostComponents.length > 0) { + hostComponents.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + componentData.componentName + ) { + componentData.hostComponents.forEach((host: any) => { + const polledHostComponentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const componentDataHostName = get(host, "HostRoles.host_name"); + if (componentDataHostName === polledHostComponentHostName) { + host.state = get(hostComponent, "HostRoles.state"); + host.passiveState = get( + hostComponent, + "HostRoles.maintenance_state" + ); + const hdfsServiceObject = + allServiceModels["hdfs"].getServiceObject(); + host.healthStatusMappedValue = + hdfsServiceObject.findHealthStatusMapValueForSingleHost( + host.state + ); + if (componentData.componentName === "NAMENODE") { + //check if ha is enabled + if (allServiceModels["hdfs"].isNameNodeHaEnabled) { + host.haStatus = get( + hostComponent, + "metrics.dfs.FSNamesystem.HAState" + ); + } + } + } + }); + } + }); + } + } + return componentData; + }; + const findMasterSlaveClientComponents = async () => { + const items = await fetchHDFSMasterSlaveClientsData(); + //const isHAEnabled = allServiceModels["hdfs"]?.isNameNodeHaEnabled; + + if (!allServiceModels["hdfs"]) { + return; + } + const isHAEnabled = allServiceModels["hdfs"]?.isNameNodeHaEnabled; + + const currentConfig = cloneDeep(allServiceModels["hdfs"]); + let masterComponents: any[] = []; + let slaveComponents: any[] = []; + let clientComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.MASTER) { + if ( + isHAEnabled && + componentData.componentName === "SECONDARY_NAMENODE" + ) { + return; + } + const masterComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + masterComponents.push(masterComponentDataWithState); + } else if (componentData.category === Categories.SLAVE) { + //For ZKFC state is updated via polled Data + if (componentData.componentName === "ZKFC") { + const slaveComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + slaveComponents.push(slaveComponentDataWithState); + } + //for these slaves the state is not updated via polled data + else { + slaveComponents.push(componentData); + } + } else { + clientComponents.push(componentData); + } + }); + + currentConfig[ServiceComponentMetricsEnums.HDFS.masterComponents] = + masterComponents; + currentConfig[ServiceComponentMetricsEnums.HDFS.slaveComponents] = + slaveComponents; + currentConfig[ServiceComponentMetricsEnums.HDFS.clientComponents] = + clientComponents; + + // Update masterComponentGroups when master components change + if (isHAEnabled && configsData?.items) { + const hdfsSiteConfigs = find(configsData?.items, ["type", "hdfs-site"]); + if (hdfsSiteConfigs) { + const properties = get(hdfsSiteConfigs, "properties", {}); + const nameSpaceProperty = properties["dfs.nameservices"]; + if (nameSpaceProperty) { + const nameSpaces = nameSpaceProperty.split(","); + const masterComponentGroups = nameSpaces.map((nameSpace: string) => { + const nameNodeIdsProperty = properties[`dfs.ha.namenodes.${nameSpace}`]; + let hosts: string[] = []; + if (nameNodeIdsProperty) { + const nameNodeIds = nameNodeIdsProperty.split(","); + hosts = nameNodeIds.map((id: any) => { + const propertyValue = properties[`dfs.namenode.http-address.${nameSpace}.${id}`]; + const matches = propertyValue && propertyValue.match(/([\D\d]+)\:\d+$/); + return matches && matches[1]; + }).filter((host: string) => host); // Filter out null/undefined hosts + } + return { + name: nameSpace, + title: nameSpace, + hosts: hosts, + components: ["NAMENODE", "ZKFC"], + clusterId: "default" + }; + }); + currentConfig.federationNamespaces = masterComponentGroups; + } + } + } else { + // For non-HA HDFS, create a single default namespace group + const nameNodeComponent = masterComponents.find((comp: any) => comp.componentName === "NAMENODE"); + const defaultMasterComponentGroups = [{ + name: "default", + title: "default", + hosts: nameNodeComponent?.hostComponents?.map((hc: any) => hc.HostRoles.host_name) || [], + components: ["NAMENODE"], + clusterId: "default" + }]; + currentConfig.federationNamespaces = defaultMasterComponentGroups; + } + + if (!isEqual(allServiceModels["hdfs"], currentConfig)) { + allServiceModels["hdfs"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + const updateHDFSHostComponentsData = async () => { + let updatedConfig = cloneDeep(allServiceModels["hdfs"]); + + let hdfsComponentsData = cachedServiceApi.getServiceComponentData("HDFS"); + + if (!hdfsComponentsData) { + hdfsComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "HDFS" + ); + + if (!hdfsComponentsData || hdfsComponentsData.length === 0) { + return; + } + } + + let datanodes = [] as any; + + const components = [ + { name: "DATANODE", metric: "dataNodes" }, + { name: "ROUTER", metric: "routers" }, + { name: "NFS_GATEWAY", metric: "nfsGateways" }, + { name: "JOURNALNODE", metric: "journalNodes" }, + ]; + + components.forEach((component) => { + // Find component in cached data instead of making API call + const hostComponents = hdfsComponentsData.find((item: any) => + item.ServiceComponentInfo?.component_name === component.name + ); + + if (hostComponents && hostComponents.ServiceComponentInfo) { + const installedCount = hostComponents.ServiceComponentInfo.installed_count; + const startedCount = hostComponents.ServiceComponentInfo.started_count; + const totalCount = hostComponents.ServiceComponentInfo.total_count; + + updatedConfig[ + ServiceComponentMetricsEnums.HDFS[ + `${component.metric}Started` as keyof typeof ServiceComponentMetricsEnums.HDFS + ] as any + ] = startedCount; + updatedConfig[ + ServiceComponentMetricsEnums.HDFS[ + `${component.metric}Installed` as keyof typeof ServiceComponentMetricsEnums.HDFS + ] as any + ] = installedCount; + updatedConfig[ + ServiceComponentMetricsEnums.HDFS[ + `${component.metric}Total` as keyof typeof ServiceComponentMetricsEnums.HDFS + ] as any + ] = totalCount; + } + }); + + // Find datanode in cached data + const datanode = hdfsComponentsData.find((item: any) => + item.ServiceComponentInfo?.component_name === "DATANODE" + ); + + if (datanode && datanode.host_components) { + datanode.host_components.forEach((hostComponent: any) => { + if (get(hostComponent, "HostRoles.component_name") === "DATANODE") { + const hostComponentData = { + componentName: "DATANODE", + hostName: get(hostComponent, "HostRoles.host_name"), + state: get(hostComponent, "HostRoles.state"), + }; + datanodes.push(hostComponentData); + } + }); + + if (datanodes.length > 0) { + updatedConfig[ServiceComponentMetricsEnums.HDFS.datanodes] = datanodes; + } + } + + if (!isEqual(allServiceModels["hdfs"], updatedConfig)) { + allServiceModels["hdfs"].updateConfig(updatedConfig); + updateRegistry(allServiceModels); + } + }; + + const parseWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.destination === "/events/hostcomponents" + ); + } + + if ( + latestHostOperationMessage && + latestHostOperationMessage.hostComponents + ) { + for (const hostComponent of latestHostOperationMessage.hostComponents) { + if (hostComponent.currentState in componentFinishStates) { + await updateHDFSHostComponentsData(); + await findMasterSlaveClientComponents(); + } + } + } + }; + + const calclateNamenodeUptime = (startTime: number) => { + const currentConfig = cloneDeep(allServiceModels["hdfs"]); + const hdfsServiceObj = currentConfig.getServiceObject(); + let clientClock = Date.now(); + + let serverClock = serverClockTime; + serverClock = serverClock.toString(); + serverClock = serverClock.length < 13 ? serverClock + "000" : serverClock; + const clockDistance = serverClock - clientClock; + const uptime = startTime; + if (uptime && uptime > 0) { + const appDateTime = Date.now() + clockDistance; + let diff = appDateTime - uptime; + if (diff < 0) { + diff = 0; + } + const formatted = hdfsServiceObj.timingFormat(diff); + return formatted.toString(); + } + }; + + const calcDiskUsagePartandPercent = () => { + const currentConfig = cloneDeep(allServiceModels["hdfs"]); + const hdfsServiceObj = currentConfig?.getServiceObject(); + const capacity = hdfsServiceObj?.capacityUsed; + const capacityTotal = hdfsServiceObj?.capacityTotal; + + if (!hdfsServiceObj) return; + + const updates = [ + { + key: "percentDFSUsed", + value: hdfsServiceObj?.findCapacityPercentage(capacity, capacityTotal), + }, + { + key: "diskPartDFSUsed", + value: hdfsServiceObj?.diskPart(capacity, capacityTotal), + }, + { + key: "percentNonDFSUsed", + value: hdfsServiceObj?.findCapacityPercentage( + hdfsServiceObj.capacityNonDfsUsed, + capacityTotal + ), + }, + { + key: "diskPartNonDFSUsed", + value: hdfsServiceObj?.diskPart( + hdfsServiceObj.capacityNonDfsUsed, + capacityTotal + ), + }, + { + key: "percentDFSRemaining", + value: hdfsServiceObj?.findCapacityPercentage( + hdfsServiceObj.capacityRemaining, + capacityTotal + ), + }, + { + key: "diskPartDFSRemaining", + value: hdfsServiceObj?.diskPart( + hdfsServiceObj.capacityRemaining, + capacityTotal + ), + }, + { + key: "diskPartNamenodeHeap", + value: hdfsServiceObj?.diskPart( + hdfsServiceObj.jvmMemoryHeapUsedValues, + hdfsServiceObj.jvmMemoryHeapMaxValues + ), + }, + { + key: "percentNamenodeHeap", + value: hdfsServiceObj?.findCapacityPercentage( + hdfsServiceObj.jvmMemoryHeapUsedValues, + hdfsServiceObj.jvmMemoryHeapMaxValues + ), + }, + ]; + + updates.forEach(({ key, value }) => { + if (currentConfig[key] && currentConfig[key] !== value) { + currentConfig[key] = value.toString(); + } + }); + + if (!isEqual(allServiceModels["hdfs"], currentConfig)) { + allServiceModels["hdfs"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + + const updateHDFSMasterComponents = async () => { + //@ts-ignore + if (!polledHostComponentsData?.items || !allServiceModels["hdfs"]) return; + + //console.log("updating HDFS master components"); + const updates = cloneDeep(allServiceModels["hdfs"]); + //console.log("hdfs updated conf = ", updates); + + let activeNameNodes = [] as any; + let standbyNameNodes = [] as any; + let zookeeperFailOverControllers = [] as any; + let nonActiveStandbyNamenodes = [] as any; + + let isNameNodeHaEnabled = allServiceModels["hdfs"].isNameNodeHaEnabled; + //let isNameNodeHaEnabled = true; + //let isNameNodeHaEnabled = isNnHAEnabled(); + //console.log("is namenode ha enabled = ", isNameNodeHaEnabled); + let nameNode = find( + //@ts-ignore + polledHostComponentsData.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "HDFS" && + get(item, "ServiceComponentInfo.component_name") === "NAMENODE" + ); + + if (nameNode && isNameNodeHaEnabled) { + nameNode.host_components.forEach((hostComponent: any) => { + if (get(hostComponent, "HostRoles.component_name") === "NAMENODE") { + const hostComponentData = { + componentName: "NAMENODE", + hostName: get(hostComponent, "HostRoles.host_name"), + haStatus: get(hostComponent, "metrics.dfs.FSNamesystem.HAState"), + state: get(hostComponent, "HostRoles.state"), + }; + + if ( + hostComponentData.state === componentFinishStates[1] && + hostComponentData.haStatus === "active" + ) { + //updates[ServiceComponentMetricsEnums.HDFS["nameNode"]] = hostComponentData; + activeNameNodes.push(hostComponentData); + return; + } else if ( + hostComponentData.state === componentFinishStates[1] && + hostComponentData.haStatus === "standby" + ) { + standbyNameNodes.push(hostComponentData); + return; + } + nonActiveStandbyNamenodes.push(hostComponentData); + } + }); + } else if (nameNode && !isNameNodeHaEnabled) { + nameNode.host_components.forEach((hostComponent: any) => { + if (get(hostComponent, "HostRoles.component_name") === "NAMENODE") { + const hostComponentData = { + componentName: "NAMENODE", + hostName: get(hostComponent, "HostRoles.host_name"), + state: get(hostComponent, "HostRoles.state"), + }; + updates[ServiceComponentMetricsEnums.HDFS["nameNode"]] = + hostComponentData; + nonActiveStandbyNamenodes.push(hostComponentData); + } + }); + } + updates[ServiceComponentMetricsEnums.HDFS["activeNameNodes"]] = + activeNameNodes; + updates[ServiceComponentMetricsEnums.HDFS["standbyNameNodes"]] = + standbyNameNodes; + updates[ServiceComponentMetricsEnums.HDFS["nonActiveStandbyNamenodes"]] = + nonActiveStandbyNamenodes; + + let snameNode = find( + //@ts-ignore + polledHostComponentsData.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "HDFS" && + get(item, "ServiceComponentInfo.component_name") === + "SECONDARY_NAMENODE" + ); + + if (snameNode) { + const sNameNodeData = snameNode.host_components.map( + (hostComponent: any) => ({ + componentName: "SECONDARY_NAMENODE", + hostName: get(hostComponent, "metrics.dfs.FSNamesystem.HAState"), + state: get(hostComponent, "HostRoles.state"), + }) + )[0]; + + if (sNameNodeData) { + updates[ServiceComponentMetricsEnums.HDFS["snameNode"]] = sNameNodeData; + } + } + + let zookeeperFC = find( + //@ts-ignore + polledHostComponentsData.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "HDFS" && + get(item, "ServiceComponentInfo.component_name") === "ZKFC" + ); + + if (zookeeperFC) { + zookeeperFC.host_components.forEach((hostComponent: any) => { + if (get(hostComponent, "HostRoles.component_name") === "ZKFC") { + const hostComponentData = { + componentName: "ZKFC", + hostName: get(hostComponent, "HostRoles.host_name"), + state: get(hostComponent, "HostRoles.state"), + }; + zookeeperFailOverControllers.push(hostComponentData); + } + }); + } + if (zookeeperFailOverControllers.length > 0) { + updates[ + ServiceComponentMetricsEnums.HDFS["zookeeperFailoverControllers"] + ] = zookeeperFailOverControllers; + } + + // Only update if we have changes + if (!isEqual(allServiceModels["hdfs"], updates)) { + allServiceModels["hdfs"].updateConfig(updates); + updateRegistry(allServiceModels); + } + }; + + const isNnHAEnabled = async () => { + if (!allServiceModels["hdfs"]) return; + + const fields = `ServiceComponentInfo/service_name,ServiceComponentInfo/category,ServiceComponentInfo/installed_count,ServiceComponentInfo/started_count,ServiceComponentInfo/init_count,ServiceComponentInfo/install_failed_count,ServiceComponentInfo/unknown_count,ServiceComponentInfo/total_count,ServiceComponentInfo/display_name,host_components/HostRoles/host_name&minimal_response=true`; + const response = + await ServiceApi.getAllServiceComponentsListAndInitialMetrics( + clusterName, + fields + ); + + const secondaryNn = find(response.data.items, (item) => { + return ( + get(item, "ServiceComponentInfo.service_name") === "HDFS" && + get(item, "ServiceComponentInfo.component_name") === + "SECONDARY_NAMENODE" + ); + }); + const nameNode = find(response.data.items, (item) => { + return ( + get(item, "ServiceComponentInfo.service_name") === "HDFS" && + get(item, "ServiceComponentInfo.component_name") === "NAMENODE" + ); + }); + + const currentConfig = cloneDeep(allServiceModels["hdfs"]); + if ( + (!allServiceModels["hdfs"].snameNode || + secondaryNn.host_components.length === 0) && + nameNode.host_components.length > 1 + ) { + currentConfig[ServiceComponentMetricsEnums.HDFS.isNameNodeHaEnabled] = + true; + currentConfig[ServiceComponentMetricsEnums.HDFS.snameNode] = null; + setIsHAEnabledForNamenode(true); + // const updates = { + // [ServiceComponentMetricsEnums.HDFS.isNameNodeHaEnabled]: true, + // [ServiceComponentMetricsEnums.HDFS.snameNode]: null, + // }; + // allServiceModels["hdfs"].isNameNodeHaEnabled = true; + + if (!isEqual(currentConfig, allServiceModels["hdfs"])) { + allServiceModels["hdfs"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + //isHdfsConfigUpdated = true; + } + } + }; + + const updateHDFSData = () => { + const findMetrics = (data: any, metricParams: any) => { + let dfsMetrics = new Map(); + let nameNodeMetricsByHost = new Map(); // Store metrics by host for federation + let nameNodeMetricsByNamespace = new Map(); // Store metrics by namespace for federation + + const item = find( + data.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "HDFS" && + get(item, "ServiceComponentInfo.component_name") === "NAMENODE" + ); + + if (item) { + const isNameNodeHAEnabled = + allServiceModels["hdfs"].isNameNodeHaEnabled; + + // For federation, collect metrics from all NameNodes, not just active one + const isFederated = allServiceModels["hdfs"].federationNamespaces && + allServiceModels["hdfs"].federationNamespaces.length > 1; + + if (isFederated) { + // Collect metrics from all NameNodes for federation + item.host_components.forEach((hostComponent: any) => { + if (get(hostComponent, "HostRoles.component_name") === "NAMENODE") { + const hostName = get(hostComponent, "HostRoles.host_name"); + const hostMetrics = new Map(); + + // Find which namespace this host belongs to + const namespace = allServiceModels["hdfs"].federationNamespaces?.find((ns: any) => + ns.hosts && ns.hosts.includes(hostName) + ); + const namespaceName = namespace?.name || hostName; + + metricParams.forEach((metricParam: any) => { + let metricParamValue = get( + hostComponent, + `metrics.dfs.FSNamesystem.${metricParam}`, + null + ); + + if (metricParamValue !== null && metricParamValue !== undefined) { + hostMetrics.set(metricParam, metricParamValue); + } else { + metricParamValue = get( + hostComponent, + `metrics.jvm.${metricParam}`, + null + ); + if (metricParamValue !== null && metricParamValue !== undefined) { + hostMetrics.set(metricParam, metricParamValue); + } else { + metricParamValue = get( + hostComponent, + `metrics.runtime.${metricParam}`, + null + ); + if (metricParam === "StartTime") { + if (metricParamValue !== null) { + metricParamValue = calclateNamenodeUptime(metricParamValue); + } + hostMetrics.set(metricParam, metricParamValue); + } else { + metricParamValue = get( + hostComponent, + `metrics.dfs.namenode.${metricParam}`, + null + ); + if (metricParam === "Safemode") { + metricParamValue = + allServiceModels["hdfs"]?.findSafeModeStatus( + metricParamValue + ); + } else if (metricParam === "UpgradeFinalized") { + const healthStatus = + allServiceModels["hdfs"]?.workStatusValues[ + hostName as any + ]?.healthStatus; + metricParamValue = allServiceModels[ + "hdfs" + ].findUpgradeStatus(metricParamValue, healthStatus); + } else if ( + metricParam === "LiveNodes" || + metricParam === "DeadNodes" || + metricParam === "DecomNodes" + ) { + const dataNodesStatusObj = get( + hostComponent, + `metrics.dfs.namenode.${metricParam}`, + null + ); + metricParamValue = + allServiceModels["hdfs"]?.countKeysMatchingPattern( + dataNodesStatusObj + ); + } + hostMetrics.set(metricParam, metricParamValue); + } + } + } + }); + + // Store metrics by host and namespace + nameNodeMetricsByHost.set(hostName, hostMetrics); + nameNodeMetricsByNamespace.set(namespaceName, hostMetrics); + } + }); + + // For federation, also set the active NameNode metrics as the main metrics (for backward compatibility) + const activeHostComponent = find(item.host_components, (hostComponent) => { + return ( + get(hostComponent, "HostRoles.component_name") === "NAMENODE" && + get(hostComponent, "HostRoles.state") === "STARTED" && + get(hostComponent, "metrics.dfs.FSNamesystem.HAState") === "active" + ); + }); + + if (activeHostComponent) { + const activeHostName = get(activeHostComponent, "HostRoles.host_name"); + const activeMetrics = nameNodeMetricsByHost.get(activeHostName); + if (activeMetrics) { + dfsMetrics = activeMetrics; + } + } + } else { + // Non-federation logic (preserve existing behavior) + let hostComponent = {}; + if (isNameNodeHAEnabled) { + hostComponent = find(item.host_components, (hostComponent) => { + return ( + get(hostComponent, "HostRoles.component_name") === "NAMENODE" && + get(hostComponent, "HostRoles.state") === "STARTED" && + get(hostComponent, "metrics.dfs.FSNamesystem.HAState") === + "active" + ); + }); + } else { + hostComponent = find(item.host_components, (hostComponent) => { + return ( + get(hostComponent, "HostRoles.component_name") === "NAMENODE" && + get(hostComponent, "HostRoles.state") === "STARTED" + ); + }); + } + + if (hostComponent) { + metricParams.forEach((metricParam: any) => { + let metricParamValue = get( + hostComponent, + `metrics.dfs.FSNamesystem.${metricParam}`, + null + ); + if (metricParamValue !== null && metricParamValue !== undefined) { + dfsMetrics.set(metricParam, metricParamValue); + return; + } else { + metricParamValue = get( + hostComponent, + `metrics.jvm.${metricParam}`, + null + ); + if (metricParamValue !== null && metricParamValue !== undefined) { + dfsMetrics.set(metricParam, metricParamValue); + return; + } else { + metricParamValue = get( + hostComponent, + `metrics.runtime.${metricParam}`, + null + ); + if (metricParam === "StartTime") { + if (metricParamValue !== null) { + metricParamValue = calclateNamenodeUptime(metricParamValue); + } + dfsMetrics.set(metricParam, metricParamValue); + return; + } else { + metricParamValue = get( + hostComponent, + `metrics.dfs.namenode.${metricParam}`, + null + ); + if (metricParam === "Safemode") { + metricParamValue = + allServiceModels["hdfs"]?.findSafeModeStatus( + metricParamValue + ); + } else if (metricParam === "UpgradeFinalized") { + const currentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const healthStatus = + allServiceModels["hdfs"]?.workStatusValues[ + currentHostName as any + ]?.healthStatus; + metricParamValue = allServiceModels[ + "hdfs" + ].findUpgradeStatus(metricParamValue, healthStatus); + } else if ( + metricParam === "LiveNodes" || + metricParam === "DeadNodes" || + metricParam === "DecomNodes" + ) { + const dataNodesStatusObj = get( + hostComponent, + `metrics.dfs.namenode.${metricParam}`, + null + ); + metricParamValue = + allServiceModels["hdfs"]?.countKeysMatchingPattern( + dataNodesStatusObj + ); + } + dfsMetrics.set(metricParam, metricParamValue); + return; + } + } + } + }); + } + } + } + return { dfsMetrics, nameNodeMetricsByHost, nameNodeMetricsByNamespace }; + }; + + const fetchComponentsData = async () => { + if (!allServiceModels["hdfs"]) return; + + let updatedConfig = cloneDeep(allServiceModels["hdfs"]); + + if (isEmpty(polledHostComponentsData)) { + return; + } + + // Simulating the fetching of HDFS metric keys and finding metrics + const hdfsMetricKeys = Object.keys( + ServiceComponentMetricsEnums.HDFS.metrics + ); + const metricsResult = findMetrics(polledHostComponentsData, hdfsMetricKeys); + const { dfsMetrics, nameNodeMetricsByHost, nameNodeMetricsByNamespace } = metricsResult; + const currentMetrics = {}; + const newMetrics = {}; + + if (!isEmpty(dfsMetrics)) { + hdfsMetricKeys.forEach((key) => { + const metricKey = + ServiceComponentMetricsEnums.HDFS.metrics[ + key as keyof typeof ServiceComponentMetricsEnums.HDFS.metrics + ]; + const metricValue = dfsMetrics.get(key); + if (metricValue || metricValue >= 0) { + //@ts-ignore + currentMetrics[metricKey as string] = + allServiceModels["hdfs"][metricKey as string]; + //@ts-ignore + newMetrics[metricKey as string] = metricValue; + updatedConfig[metricKey] = metricValue; + } + }); + + // Store federation-specific data + if (nameNodeMetricsByHost && nameNodeMetricsByHost.size > 0) { + // Convert host-based metrics to objects for easier access + const hostMetricsObj: { [key: string]: any } = {}; + const namespaceMetricsObj: { [key: string]: any } = {}; + + nameNodeMetricsByHost.forEach((metrics, hostName) => { + const hostData: { [key: string]: any } = {}; + metrics.forEach((value:any, key:string) => { + const metricKey = ServiceComponentMetricsEnums.HDFS.metrics[ + key as keyof typeof ServiceComponentMetricsEnums.HDFS.metrics + ]; + if (metricKey) { + hostData[metricKey] = value; + } + }); + hostMetricsObj[hostName] = hostData; + }); + + nameNodeMetricsByNamespace.forEach((metrics, namespaceName) => { + const namespaceData: { [key: string]: any } = {}; + metrics.forEach((value:any, key:string) => { + const metricKey = ServiceComponentMetricsEnums.HDFS.metrics[ + key as keyof typeof ServiceComponentMetricsEnums.HDFS.metrics + ]; + if (metricKey) { + namespaceData[metricKey] = value; + } + }); + namespaceMetricsObj[namespaceName] = namespaceData; + }); + + // Store the federation-specific metrics + updatedConfig.nameNodeMetricsByHost = hostMetricsObj; + updatedConfig.nameNodeMetricsByNamespace = namespaceMetricsObj; + } + + if (!isEqual(updatedConfig, allServiceModels["hdfs"])) { + // Preserve the existing masterComponentGroups before updating + const existingMasterComponentGroups = allServiceModels["hdfs"].masterComponentGroups; + allServiceModels["hdfs"].updateConfig(updatedConfig); + // Restore the masterComponentGroups after update + if (existingMasterComponentGroups && existingMasterComponentGroups.length > 0) { + allServiceModels["hdfs"].setMasterComponentGroups(existingMasterComponentGroups); + } + updateRegistry(allServiceModels); + //console.log("✅ Batch update complete"); + } + } + }; + fetchComponentsData(); + }; + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep(allServiceModels["hdfs"]); + configToBeUpdated.isInPassiveForService = + maintenanceState === maintenanceStates[0]; //signifies ON, assigns ON/PFF + + if (!isEqual(allServiceModels["hdfs"], configToBeUpdated)) { + allServiceModels["hdfs"].updateConfig(configToBeUpdated); + updateRegistry(allServiceModels); + } + }; + + const updateAlertsAndServiceStateData = async () => { + const currentConfig = cloneDeep(allServiceModels["hdfs"]); + const serviceName = "HDFS"; + + // Use centralized service state API instead of individual call + const serviceStateData = centralizedServiceStateApi.getServiceStateData(serviceName); + + if (!serviceStateData) return; + + const { alertsCount, hasCriticalAlerts, state } = serviceStateData; + + if (!alertsCount && alertsCount !== 0) return; + + currentConfig[ServiceComponentMetricsEnums.AMBARI_METRICS.hasCriticalAlerts] = hasCriticalAlerts; + currentConfig[ServiceComponentMetricsEnums.HDFS.alertsCount] = alertsCount; + currentConfig[ServiceComponentMetricsEnums.HDFS.state] = state; + + if (!isEqual(allServiceModels["hdfs"], currentConfig)) { + allServiceModels["hdfs"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === "HDFS" + ); + } + if ( + (latestHostOperationMessage && + componentFinishStates.includes(latestHostOperationMessage.state)) || + (latestHostOperationMessage.maintenance_state && maintenanceStates.includes(latestHostOperationMessage.maintenance_state)) + ) { + await updateServiceMaintenanceState( + latestHostOperationMessage.maintenance_state + ); + await updateAlertsAndServiceStateData(); + } + }; + + const resetIsNameNodeHaEnabledAttr = () => { + if (isHAEnabledForNamenode) { + allServiceModels["hdfs"].isNameNodeHaEnabled = true; + } + }; + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + updateHDFSData(); + updateHDFSMasterComponents(); + findMasterSlaveClientComponents(); + calcDiskUsagePartandPercent(); + fetchServerCLockTime(); + } + }, [polledHostComponentsData]); + + // useEffect(() => { + // updateQuicklinksData(); + // }, [JSON.stringify(allServiceModels["hdfs"])]); + + useEffect(() => { + if (isEmpty(masterSlaveClientsData) && clusterName) { + cachedServiceApi.fetchAllServiceComponents(clusterName, true); + } + findMasterSlaveClientComponents(); + }, [masterSlaveClientsData, clusterName]); + + useEffect(() => { + fetchServerCLockTime(); + //findMasterSlaveClientComponents(); + updateWorkStatusValues(); + }, []); + + useEffect(() => { + if ( + hasNameNodeHAEnabledUseEffectRunOnce.current || + !allServiceModels["hdfs"] || + isHAEnabledForNamenode + ) { + return; + } + isNnHAEnabled(); + hasNameNodeHAEnabledUseEffectRunOnce.current = true; + }, [allServiceModels]); + // useEffect(() => { + // if ( + // allServiceModels && + // allServiceModels["hdfs"] && + // allServiceModels["hdfs"].isNameNodeHaEnabled === false + // ) { + // isNnHAEnabled(); + // } + // }, [allServiceModels]); + + useEffect(() => { + resetIsNameNodeHaEnabledAttr(); + }, [isHAEnabledForNamenode]); + + useEffect(() => { + updateWorkStatusValues(); + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); + + useEffect(() => { + parseWebSocketMessages(); + parseAlertsWebSocketMessages(); + }, [parsedSocketMessages]); +}; diff --git a/ambari-web/latest/src/hooks/useHbaseConfigUpdater.ts b/ambari-web/latest/src/hooks/useHbaseConfigUpdater.ts new file mode 100644 index 00000000000..f35d82fdbc6 --- /dev/null +++ b/ambari-web/latest/src/hooks/useHbaseConfigUpdater.ts @@ -0,0 +1,530 @@ +import { useContext, useEffect, useState } from "react"; +import { cloneDeep, find, get, isEmpty, isEqual } from "lodash"; +import { ServiceApi } from "../api/ServiceApi.ts"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { updateServiceAlertsAndStateFromCentralizedApi } from "../Utils/centralizedServiceStateUtils.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums.ts"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import { + componentFinishStates, + maintenanceStates, +} from "../screens/Hosts/constants.ts"; +import { Categories } from "../enums/Categories"; + +export const useHbaseConfigUpdater = () => { + const { + polledHostComponentsData, + masterSlaveClientsData, + serviceStatesData, + } = useContext(ServiceContext); + + // @ts-ignore + const { services, clusterName, parsedSocketMessages } = useContext(AppContext); + + // Early return if HBASE service is not installed + const isHbaseInstalled = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "HBASE"); + + if (!isHbaseInstalled) { + return; + } + + //@ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + const [serverClockTime, setServerClockTime] = useState(); + + const fetchServerCLockTime = async () => { + const fields = "?fields=RootServiceComponents/server_clock"; + const responseData = await ServiceApi.ambariService(fields); + const serverClock = get( + responseData, + "RootServiceComponents.server_clock", + null + ); + setServerClockTime(serverClock); + }; + + const fetchHbaseMasterSlaveClientsData = async () => { + let hbaseComponentsData = cachedServiceApi.getServiceComponentData("HBASE"); + + if (!hbaseComponentsData) { + hbaseComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "HBASE" + ); + } + + return hbaseComponentsData; + }; + + const updateComponentObjectForSelectMaster = (componentData: any) => { + let masterComponent = find( + //@ts-ignore + polledHostComponentsData?.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "HBASE" && + get(item, "ServiceComponentInfo.component_name") === + componentData.componentName + ); + + if (masterComponent) { + const hostComponents = get(masterComponent, "host_components"); + if (hostComponents && hostComponents.length > 0) { + hostComponents.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + componentData.componentName + ) { + componentData.hostComponents.forEach((host: any) => { + const polledHostComponentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const componentDataHostName = get(host, "HostRoles.host_name"); + if (componentDataHostName === polledHostComponentHostName) { + host.state = get(hostComponent, "HostRoles.state"); + host.passiveState = get( + hostComponent, + "HostRoles.maintenance_state" + ); + if (componentData.componentName === "HBASE_MASTER") { + host.isActiveMaster = get( + hostComponent, + "metrics.hbase.master.IsActiveMaster" + ); + } else { + host.isActiveMaster = false; + } + } + }); + } + }); + } + } + return componentData; + }; + + const findMasterSlaveClientComponents = async () => { + const items = await fetchHbaseMasterSlaveClientsData(); + + if (!allServiceModels["hbase"]) { + return; + } + + const currentConfig = cloneDeep(allServiceModels["hbase"]); + //const hdfsServiceObj = currentConfig.getServiceObject(); + let masterComponents: any[] = []; + let slaveComponents: any[] = []; + let clientComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.MASTER) { + const masterComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + masterComponents.push(masterComponentDataWithState); + } else if (componentData.category === Categories.SLAVE) { + slaveComponents.push(componentData); + } else { + clientComponents.push(componentData); + } + }); + currentConfig[ServiceComponentMetricsEnums.HDFS.masterComponents] = + masterComponents; + currentConfig[ServiceComponentMetricsEnums.HDFS.slaveComponents] = + slaveComponents; + currentConfig[ServiceComponentMetricsEnums.HDFS.clientComponents] = + clientComponents; + + if (!isEqual(allServiceModels["hbase"], currentConfig)) { + allServiceModels["hbase"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + const updateHbaseHostComponentsData = async () => { + let updatedConfig = cloneDeep(allServiceModels["hbase"]); + + const serviceName = "HBASE"; // Replace with the desired service name + const fields = `ServiceComponentInfo/service_name,ServiceComponentInfo/category,ServiceComponentInfo/installed_count,ServiceComponentInfo/started_count,ServiceComponentInfo/init_count,ServiceComponentInfo/install_failed_count,ServiceComponentInfo/unknown_count,ServiceComponentInfo/total_count,ServiceComponentInfo/display_name,host_components/HostRoles/host_name&minimal_response=true`; + const response = + await ServiceApi.getAllServiceComponentsListAndInitialMetrics( + clusterName, + `${fields}&ServiceComponentInfo/service_name=${serviceName}` + ); + + const components = [ + { name: "HBASE_REGIONSERVER", metric: "hbaseRegionServers" }, + { name: "PHOENIX_QUERY_SERVER", metric: "phoenixQueryServers" }, + ]; + + components.forEach((component) => { + const hostComponents = findHostComponentItems( + "HBASE", + component.name, + response + ); + + if (!hostComponents || !hostComponents.ServiceComponentInfo) { + return; // Skip if hostComponents is undefined or doesn't have ServiceComponentInfo + } + + const installedCount = + hostComponents.ServiceComponentInfo.installed_count; + const startedCount = hostComponents.ServiceComponentInfo.started_count; + const totalCount = hostComponents.ServiceComponentInfo.total_count; + updatedConfig[ + ServiceComponentMetricsEnums.HBASE[ + `${component.metric}Started ` as keyof typeof ServiceComponentMetricsEnums.HBASE + ] as any + ] = startedCount; + updatedConfig[ + ServiceComponentMetricsEnums.HBASE[ + `${component.metric}Installed` as keyof typeof ServiceComponentMetricsEnums.HBASE + ] as any + ] = installedCount; + updatedConfig[ + ServiceComponentMetricsEnums.HBASE[ + `${component.metric}Total` as keyof typeof ServiceComponentMetricsEnums.HBASE + ] as any + ] = totalCount; + }); + + if (!isEqual(allServiceModels["hbase"], updatedConfig)) { + allServiceModels["hbase"].updateConfig(updatedConfig); + updateRegistry(allServiceModels); + } + }; + + const parseWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.destination === "/events/hostcomponents" + ); + } + if ( + latestHostOperationMessage && + latestHostOperationMessage.hostComponents + ) { + for (const hostComponent of latestHostOperationMessage.hostComponents) { + if (hostComponent.currentState in componentFinishStates) { + await updateHbaseHostComponentsData(); + await findMasterSlaveClientComponents(); + } + } + } + }; + + const calculateHbaseMasterUptime = (startOrActiveTime: number) => { + const currentConfig = cloneDeep(allServiceModels["hbase"]); + const hbaseServiceObj = currentConfig?.getServiceObject(); + let clientClock = Date.now(); + + let serverClock = serverClockTime; + if (!serverClock) { + return null; // Return null if serverClockTime is not available + } + + serverClock = serverClock.toString(); + serverClock = serverClock.length < 13 ? serverClock + "000" : serverClock; + const clockDistance = serverClock - clientClock; + const uptime = startOrActiveTime; + if (uptime && uptime > 0) { + const appDateTime = Date.now() + clockDistance; + let diff = appDateTime - uptime; + if (diff < 0) { + diff = 0; + } + const formatted = hbaseServiceObj.timingFormat(diff); + return formatted.toString(); + } + }; + const calcDiskUsagePartandPercent = () => { + const currentConfig = cloneDeep(allServiceModels["hbase"]); + const hbaseServiceObj = currentConfig?.getServiceObject(); + + if (!hbaseServiceObj) { + return; + } + + const updates = [ + { + key: "diskPartHbaseMasterHeap", + value: hbaseServiceObj.diskPart( + hbaseServiceObj.heapMemoryUsed, + hbaseServiceObj.heapMemoryMax + ), + }, + { + key: "percentHbaseMasterHeap", + value: hbaseServiceObj.findCapacityPercentage( + hbaseServiceObj.heapMemoryUsed, + hbaseServiceObj.heapMemoryMax + ), + }, + ]; + + updates.forEach(({ key, value }) => { + if (currentConfig[key] && currentConfig[key] !== value) { + currentConfig[key] = value.toString(); + } + }); + + if (!isEqual(allServiceModels["hbase"], currentConfig)) { + allServiceModels["hbase"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + + const updateHbaseMasterComponents = async () => { + //@ts-ignore + if (!polledHostComponentsData?.items) return; + + const updates = cloneDeep(allServiceModels["hbase"]); + let activeHbaseMasters = [] as any; + let standbyHbaseMasters = [] as any; + let nonActiveStandbyHbaseMasters = [] as any; + + let hbaseMaster = find( + //@ts-ignore + polledHostComponentsData.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "HBASE" && + get(item, "ServiceComponentInfo.component_name") === "HBASE_MASTER" + ); + + if (hbaseMaster) { + hbaseMaster.host_components.forEach((hostComponent: any) => { + if (get(hostComponent, "HostRoles.component_name") === "HBASE_MASTER") { + const hostComponentData = { + componentName: "HBASE_MASTER", + hostName: get(hostComponent, "HostRoles.host_name"), + isActiveMaster: get( + hostComponent, + "metrics.hbase.master.IsActiveMaster" + ), + state: get(hostComponent, "HostRoles.state"), + }; + + if ( + hostComponentData.state === componentFinishStates[1] && //denotes started + hostComponentData.isActiveMaster === "true" + ) { + //updates[ServiceComponentMetricsEnums.HDFS["nameNode"]] = hostComponentData; + activeHbaseMasters.push(hostComponentData); + return; + } else if ( + hostComponentData.state === componentFinishStates[1] && + hostComponentData.isActiveMaster === "false" + ) { + standbyHbaseMasters.push(hostComponentData); + return; + } + nonActiveStandbyHbaseMasters.push(hostComponentData); + } + }); + } + updates[ServiceComponentMetricsEnums.HBASE["activeHbaseMasters"]] = + activeHbaseMasters; + updates[ServiceComponentMetricsEnums.HBASE["standbyHbaseMasters"]] = + standbyHbaseMasters; + updates[ + ServiceComponentMetricsEnums.HBASE["nonActiveStandbyHbaseMasters"] + ] = nonActiveStandbyHbaseMasters; + + // Only update if we have changes + if (!isEqual(allServiceModels["hbase"], updates)) { + allServiceModels["hbase"].updateConfig(updates); + updateRegistry(allServiceModels); + } + }; + + const findHostComponentItems = ( + serviceName: string, + componentName: any, + response: any + ) => { + const item = find(response.data.items, (item) => { + return ( + get(item, "ServiceComponentInfo.service_name") === serviceName && + get(item, "ServiceComponentInfo.component_name") === componentName + ); + }); + return item; + }; + + const updateHbaseData = () => { + const findMetrics = (data: any, metricParams: any) => { + let hbaseMetrics = new Map(); + const item = find( + data.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "HBASE" && + get(item, "ServiceComponentInfo.component_name") === "HBASE_MASTER" + ); + if (item) { + const hostComponent = find(item.host_components, (hostComponent) => { + return ( + get(hostComponent, "HostRoles.component_name") === "HBASE_MASTER" && + get(hostComponent, "HostRoles.state") === "STARTED" && + get(hostComponent, "metrics.hbase.master.IsActiveMaster") === "true" + ); + }); + if (hostComponent) { + metricParams.forEach((metricParam: any) => { + //check for hbase master main metrics + let metricParamValue = get( + hostComponent, + `metrics.hbase.master.${metricParam}`, + null + ); + if (metricParam === "AverageLoad" && metricParamValue !== null) { + metricParamValue = Number.isInteger(metricParamValue) + ? metricParamValue + " regions per RegionServer" + : metricParamValue.toFixed(2) + " regions per RegionServer"; + } else if ( + metricParam === "MasterStartTime" || + metricParam === "MasterActiveTime" + ) { + metricParamValue = calculateHbaseMasterUptime(metricParamValue); + } + + //check for jvm metrics + if (metricParamValue == null) { + metricParamValue = get( + hostComponent, + `metrics.jvm.${metricParam}`, + null + ); + } + //check for regions in transition + if (metricParamValue == null) { + metricParamValue = get( + hostComponent, + `metrics.master.AssignmentManager.${metricParam}`, + null + ); + } + hbaseMetrics.set(metricParam, metricParamValue); + }); + return hbaseMetrics; + } + } + return null; + }; + + const fetchComponentsData = async () => { + let updatedConfig = cloneDeep(allServiceModels["hbase"]); + + // Simulating the fetching of host components data + if (isEmpty(polledHostComponentsData)) { + return; + } + + // Simulating the fetching of HDFS metric keys and finding metrics + const hbaseMetricKeys = Object.keys( + ServiceComponentMetricsEnums.HBASE.metrics + ); + const metricsMap = findMetrics(polledHostComponentsData, hbaseMetricKeys); + const currentMetrics = {}; + const newMetrics = {}; + + if (metricsMap) { + hbaseMetricKeys.forEach((key) => { + const metricKey = + ServiceComponentMetricsEnums.HBASE.metrics[ + key as keyof typeof ServiceComponentMetricsEnums.HBASE.metrics + ]; + const metricValue = metricsMap.get(key); + if (metricValue || metricValue >= 0) { + //@ts-ignore + currentMetrics[metricKey as string] = + allServiceModels["hbase"][metricKey as string]; + //@ts-ignore + newMetrics[metricKey as string] = metricValue; + updatedConfig[metricKey] = metricValue; + } + + if (!isEqual(updatedConfig, allServiceModels["hbase"])) { + allServiceModels["hbase"].updateConfig(updatedConfig); + updateRegistry(allServiceModels); + } + }); + } + }; + fetchComponentsData(); + }; + + //usePolling(pollServiceComponentInfoApi, 3000); + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep(allServiceModels["hbase"]); + configToBeUpdated.isInPassiveForService = + maintenanceState === maintenanceStates[0]; //signifies ON, assigns ON/PFF + + if (!isEqual(allServiceModels["hbase"], configToBeUpdated)) { + allServiceModels["hbase"].updateConfig(configToBeUpdated); + updateRegistry(allServiceModels); + } + }; + + const updateAlertsAndServiceStateData = async () => { + // Use centralized service state API instead of individual call + updateServiceAlertsAndStateFromCentralizedApi("HBASE", "hbase", allServiceModels, updateRegistry); + }; + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === "HBASE" + ); + } + if ( + (latestHostOperationMessage && + componentFinishStates.includes(latestHostOperationMessage.state)) || + (latestHostOperationMessage.maintenance_state && maintenanceStates.includes(latestHostOperationMessage.maintenance_state)) + ) { + await updateServiceMaintenanceState( + latestHostOperationMessage.maintenance_state + ); + await updateAlertsAndServiceStateData(); + } + }; + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + updateHbaseData(); + updateHbaseMasterComponents(); + findMasterSlaveClientComponents(); + calcDiskUsagePartandPercent(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + updateHbaseHostComponentsData(); + fetchServerCLockTime(); + findMasterSlaveClientComponents(); + parseAlertsWebSocketMessages(); + }, []); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); + + useEffect(() => { + parseWebSocketMessages(); + parseAlertsWebSocketMessages(); + }, [parsedSocketMessages]); +}; diff --git a/ambari-web/latest/src/hooks/useHiveConfigUpdater.ts b/ambari-web/latest/src/hooks/useHiveConfigUpdater.ts new file mode 100644 index 00000000000..3fee772a5bd --- /dev/null +++ b/ambari-web/latest/src/hooks/useHiveConfigUpdater.ts @@ -0,0 +1,296 @@ +import { useContext, useEffect, useState } from "react"; +import { cloneDeep, find, get, isEmpty, isEqual } from "lodash"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { updateServiceAlertsAndStateFromCentralizedApi } from "../Utils/centralizedServiceStateUtils.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums.ts"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import { Categories } from "../enums/Categories.ts"; +import {componentFinishStates, maintenanceStates} from "../screens/Hosts/constants.ts"; +import ConfigsApi from "../api/ConfigsApi.tsx"; + +export const useHiveConfigUpdater = () => { + // @ts-ignore + const { services, parsedSocketMessages, clusterName } = useContext(AppContext); + + // Early return if HIVE service is not installed + const isHiveInstalled = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "HIVE"); + + if (!isHiveInstalled) { + return; + } + + const { polledHostComponentsData, masterSlaveClientsData, serviceStatesData } = + useContext(ServiceContext); + //@ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + const [hiveJDBCURL, setHiveJDBCURL] = useState(""); + + + const fetchHiveMasterSlaveClientsData = async () => { + if (allServiceModels["hive"].hiveServer2JDBCURL === "") { + allServiceModels["hive"].hiveServer2JDBCURL = hiveJDBCURL; + } + + let hiveComponentsData = cachedServiceApi.getServiceComponentData("HIVE"); + + if (!hiveComponentsData) { + hiveComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "HIVE" + ); + } + + return hiveComponentsData; + }; + + const updateComponentObjectForSelectMaster = (componentData: any) => { + let masterComponent = find( + //@ts-ignore + polledHostComponentsData?.items, + (item: any) => + get(item, "ServiceComponentInfo.service_name") === "HIVE" && + get(item, "ServiceComponentInfo.component_name") === + componentData.componentName + ); + + if (masterComponent) { + const hostComponents = get(masterComponent, "host_components"); + if (hostComponents && hostComponents.length > 0) { + hostComponents.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + componentData.componentName + ) { + componentData.hostComponents.forEach((host: any) => { + const polledHostComponentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const componentDataHostName = get(host, "HostRoles.host_name"); + if (componentDataHostName === polledHostComponentHostName) { + host.state = get(hostComponent, "HostRoles.state"); + host.passiveState = get( + hostComponent, + "HostRoles.maintenance_state" + ); + } + }); + } + }); + } + } + return componentData; + }; + + const findHiveMasterClientComponents = async () => { + const items = await fetchHiveMasterSlaveClientsData(); + + if (!allServiceModels["hive"]) { + return; + } + + const currentConfig = cloneDeep(allServiceModels["hive"]); + let masterComponents: any[] = []; + let clientComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.MASTER) { + const masterComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + masterComponents.push(masterComponentDataWithState); + } else { + const hiveClientsInstalled = componentData.installedCount; + currentConfig[ServiceComponentMetricsEnums.HIVE.hiveClients] = + hiveClientsInstalled; + clientComponents.push(componentData); + } + }); + + currentConfig[ServiceComponentMetricsEnums.HIVE.masterComponents] = + masterComponents; + currentConfig[ServiceComponentMetricsEnums.HIVE.clientComponents] = + clientComponents; + + if (!isEqual(allServiceModels["hive"], currentConfig)) { + allServiceModels["hive"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep(allServiceModels["hive"]); + configToBeUpdated.isInPassiveForService = maintenanceState === maintenanceStates[0]; //signifies ON, assigns ON/PFF + + if (!isEqual(allServiceModels["hive"], configToBeUpdated)) { + allServiceModels["hive"].updateConfig(configToBeUpdated); + updateRegistry(allServiceModels); + } + } + + const updateAlertsAndServiceStateData = async () => { + // Use centralized service state API instead of individual call + updateServiceAlertsAndStateFromCentralizedApi("HIVE", "hive", allServiceModels, updateRegistry); + }; + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === "HIVE" + ); + } + if ( + latestHostOperationMessage && + componentFinishStates.includes(latestHostOperationMessage.state) + || (latestHostOperationMessage.maintenance_state && maintenanceStates.includes(latestHostOperationMessage.maintenance_state)) + ) { + await updateServiceMaintenanceState(latestHostOperationMessage.maintenance_state); + await updateAlertsAndServiceStateData(); + } + }; + + const updateHiveServer2JDBCURL = async () => { + if ( + allServiceModels["hive"] && + !isEmpty(allServiceModels["hive"].hiveServer2JDBCURL) + ) { + return; + } + + try { + // Following Ember.js setHiveEndPointsValue logic - fetch configurations by sites + const sites = ['hive-site', 'hive-interactive-site']; + + // First get the current config tags (following Ember.js pattern) + const configTagsResponse = await ConfigsApi.loadConfigTags(clusterName); + + if (!configTagsResponse?.Clusters?.desired_configs) { + return; + } + + const desiredConfigs = configTagsResponse.Clusters.desired_configs; + + // Build URL params for the sites we need (following Ember.js pattern) + const urlParams = sites + .filter(site => desiredConfigs[site]) // Only include sites that exist + .map(site => `(type=${site}&tag=${desiredConfigs[site].tag})`) + .join('|'); + + if (!urlParams) { + return; + } + + // Fetch configurations by tags (following Ember.js reassign.load_configs pattern) + const configsResponse = await ConfigsApi.reassignLoadConfigs(clusterName, urlParams); + + if (!configsResponse?.items) { + return; + } + + // Convert to the format expected by our logic + const configs = configsResponse.items.filter((config: any) => config && config.properties); + + if (configs.length === 0) { + return; + } + + // Following Ember.js logic - ensure hive-site is first + const hiveSiteIndex = configs.findIndex((config: any) => config.type === 'hive-site'); + if (hiveSiteIndex > 0) { + // Move hive-site to first position + const hiveSiteConfig = configs.splice(hiveSiteIndex, 1)[0]; + configs.unshift(hiveSiteConfig); + } + + // Initialize with hive-site values (following Ember.js pattern) + let hiveSiteDynamicDiscovery = configs[0].properties['hive.server2.support.dynamic.service.discovery']; + let hiveSiteZkQuorom = configs[0].properties['hive.zookeeper.quorum']; + let hiveSiteServiceDiscoveryMode = 'zooKeeper'; + let hiveSiteZkNameSpace = configs[0].properties['hive.server2.zookeeper.namespace']; + + // Process each config (following Ember.js forEach logic) + configs.forEach((_config: any) => { + if (_config.type === 'hive-interactive-site') { + // Override with hive-interactive-site values if available (Ember.js fallback pattern) + hiveSiteDynamicDiscovery = _config.properties['hive.server2.support.dynamic.service.discovery'] || hiveSiteDynamicDiscovery; + hiveSiteZkQuorom = _config.properties['hive.zookeeper.quorum'] || hiveSiteZkQuorom; + + // For namespace, only override if it's not the interactive-specific namespace + // This ensures we use the standard hiveserver2 namespace, not hiveserver2-interactive + const interactiveNamespace = _config.properties['hive.server2.zookeeper.namespace']; + if (interactiveNamespace && interactiveNamespace !== 'hiveserver2-interactive') { + hiveSiteZkNameSpace = interactiveNamespace; + } + + // Check for HIVE_SERVER_INTERACTIVE HA mode (following Ember.js HA detection) + // Only use HA namespace if it exists and is not a placeholder value + const haNamespace = _config.properties['hive.server2.active.passive.ha.registry.namespace']; + if (haNamespace && haNamespace !== 'hs2ActivePassiveHA') { + hiveSiteServiceDiscoveryMode = 'zooKeeperHA'; + hiveSiteZkNameSpace = haNamespace; + } + } + }); + + // Check if required properties exist after processing all configs + if (!hiveSiteZkQuorom || !hiveSiteZkNameSpace) { + return; + } + + // Construct JDBC URL following Ember.js format pattern + const hiveServer2JDBCURL = `jdbc:hive2://${hiveSiteZkQuorom}/${hiveSiteZkNameSpace};serviceDiscoveryMode=${hiveSiteServiceDiscoveryMode};zooKeeperNamespace=${hiveSiteZkNameSpace}`; + + if (!allServiceModels["hive"]) { + return; + } + + const currentConfig = cloneDeep(allServiceModels["hive"]); + currentConfig[ServiceComponentMetricsEnums.HIVE.hiveServer2JDBCURL] = hiveServer2JDBCURL; + setHiveJDBCURL(hiveServer2JDBCURL); + + if (!isEqual(allServiceModels["hive"], currentConfig)) { + allServiceModels["hive"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + } catch (error) { + // Graceful error handling - JDBC URL will remain empty if config is not available + } + }; + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + findHiveMasterClientComponents(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + if (!isEmpty(parsedSocketMessages)) { + parseAlertsWebSocketMessages(); + } + }, [parsedSocketMessages]); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); + + useEffect(() => { + if ( + allServiceModels && + allServiceModels["hive"] && + allServiceModels["hive"].hiveServer2JDBCURL === "" + ) { + updateHiveServer2JDBCURL(); + } + }, [allServiceModels]); +}; diff --git a/ambari-web/latest/src/hooks/useKerberosConfigUpdater.ts b/ambari-web/latest/src/hooks/useKerberosConfigUpdater.ts new file mode 100644 index 00000000000..fe667b046c2 --- /dev/null +++ b/ambari-web/latest/src/hooks/useKerberosConfigUpdater.ts @@ -0,0 +1,131 @@ +import { useContext, useEffect } from "react"; +import { cloneDeep, get, isEqual } from "lodash"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { updateServiceAlertsAndStateFromCentralizedApi } from "../Utils/centralizedServiceStateUtils.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums.ts"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import { Categories } from "../enums/Categories.ts"; +import {componentFinishStates, maintenanceStates} from "../screens/Hosts/constants.ts"; + +export const useKerberosConfigUpdater = () => { + // @ts-ignore + const { services, parsedSocketMessages, clusterName } = useContext(AppContext); + + // Early return if KERBEROS service is not installed + const isKerberosInstalled = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "KERBEROS"); + + if (!isKerberosInstalled) { + return; + } + + const { polledHostComponentsData, masterSlaveClientsData, serviceStatesData } = + useContext(ServiceContext); + //@ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + + const fetchKerberosMasterSlaveClientsData = async () => { + let kerberosComponentsData = cachedServiceApi.getServiceComponentData("KERBEROS"); + + if (!kerberosComponentsData) { + kerberosComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "KERBEROS" + ); + } + + return kerberosComponentsData; + }; + + const findKerberosClientComponents = async () => { + const items = await fetchKerberosMasterSlaveClientsData(); + + if (!allServiceModels["kerberos"]) { + return; + } + + const currentConfig = cloneDeep(allServiceModels["kerberos"]); + let clientComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.CLIENT) { + const kerberosClientsInstalled = componentData.installedCount; + currentConfig[ + ServiceComponentMetricsEnums.KERBEROS.kerberosClientsInstalled + ] = kerberosClientsInstalled; + clientComponents.push(componentData); + } + }); + + currentConfig[ServiceComponentMetricsEnums.KERBEROS.clientComponents] = + clientComponents; + + if (!isEqual(allServiceModels["kerberos"], currentConfig)) { + allServiceModels["kerberos"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + //usePolling(pollServiceComponentInfoApi, 3000); + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep(allServiceModels["zk"]); + configToBeUpdated.isInPassiveForService = maintenanceState === maintenanceStates[0]; //signifies ON, assigns ON/PFF + + if (!isEqual(allServiceModels["zk"], configToBeUpdated)) { + allServiceModels["zk"].updateConfig(configToBeUpdated); + updateRegistry(allServiceModels); + } + } + + const updateAlertsAndServiceStateData = async () => { + // Use centralized service state API instead of individual call + updateServiceAlertsAndStateFromCentralizedApi("KERBEROS", "kerberos", allServiceModels, updateRegistry); + }; + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === "KERBEROS" + ); + } + if ( + latestHostOperationMessage && + componentFinishStates.includes(latestHostOperationMessage.state) + || maintenanceStates.includes(latestHostOperationMessage.maintenance_state) + ) { + await updateServiceMaintenanceState(latestHostOperationMessage.maintenance_state); + await updateAlertsAndServiceStateData(); + } + }; + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + findKerberosClientComponents(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + parseAlertsWebSocketMessages(); + }, [parsedSocketMessages]); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); + + useEffect(() => { + //pollServiceComponentInfoApi(); + findKerberosClientComponents(); + }, []); +}; diff --git a/ambari-web/latest/src/hooks/useKyuubiConfigUpdater.ts b/ambari-web/latest/src/hooks/useKyuubiConfigUpdater.ts new file mode 100644 index 00000000000..5d674251da5 --- /dev/null +++ b/ambari-web/latest/src/hooks/useKyuubiConfigUpdater.ts @@ -0,0 +1,180 @@ +import { useContext, useEffect } from "react"; +import { cloneDeep, find, get, isEmpty, isEqual } from "lodash"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { updateServiceAlertsAndStateFromCentralizedApi } from "../Utils/centralizedServiceStateUtils.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums.ts"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import { Categories } from "../enums/Categories.ts"; +import { + componentFinishStates, + maintenanceStates, +} from "../screens/Hosts/constants.ts"; + +export const useKyuubiConfigUpdater = () => { + const { polledHostComponentsData, masterSlaveClientsData, serviceStatesData } = + useContext(ServiceContext); + const { services } = useContext(AppContext); + //@ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + const { parsedSocketMessages } = useContext(AppContext); + const serviceNameForServiceModel = "kyuubi"; + const serviceNameForPolledApi = "KYUUBI"; + + const isKyuubiInstalled = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "KYUUBI"); + + if (!isKyuubiInstalled) { + return; + } + + const fetchKyuubiMasterSlaveClientsData = async () => { + // 🚀 OPTIMIZATION: Try centralized cache first, fallback to masterSlaveClientsData + + let kyuubiComponentsData = cachedServiceApi.getServiceComponentData("KYUUBI"); + + if (!kyuubiComponentsData) { + kyuubiComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === serviceNameForPolledApi + ); + } else { + } + + return kyuubiComponentsData; + }; + + const updateComponentObjectForSelectMaster = (componentData: any) => { + let masterComponent = find( + //@ts-ignore + polledHostComponentsData?.items, + (item: any) => + get(item, "ServiceComponentInfo.service_name") === + serviceNameForPolledApi && + get(item, "ServiceComponentInfo.component_name") === + componentData.componentName + ); + + if (masterComponent) { + const hostComponents = get(masterComponent, "host_components"); + if (hostComponents && hostComponents.length > 0) { + hostComponents.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + componentData.componentName + ) { + componentData.hostComponents.forEach((host: any) => { + const polledHostComponentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const componentDataHostName = get(host, "HostRoles.host_name"); + if (componentDataHostName === polledHostComponentHostName) { + host.state = get(hostComponent, "HostRoles.state"); + host.passiveState = get( + hostComponent, + "HostRoles.maintenance_state" + ); + } + }); + } + }); + } + } + return componentData; + }; + const findKyuubiMasterComponents = async () => { + const items = await fetchKyuubiMasterSlaveClientsData(); + if (!allServiceModels[serviceNameForServiceModel]) { + return; + } + + const currentConfig = cloneDeep( + allServiceModels[serviceNameForServiceModel] + ); + let masterComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.MASTER) { + const masterComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + masterComponents.push(masterComponentDataWithState); + } + }); + + currentConfig[ServiceComponentMetricsEnums.KYUUBI.masterComponents] = + masterComponents; + + if (!isEqual(allServiceModels[serviceNameForServiceModel], currentConfig)) { + allServiceModels[serviceNameForServiceModel].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep( + allServiceModels[serviceNameForServiceModel] + ); + configToBeUpdated.isInPassiveForService = + maintenanceState === maintenanceStates[0]; + + if ( + !isEqual(allServiceModels[serviceNameForServiceModel], configToBeUpdated) + ) { + allServiceModels[serviceNameForServiceModel].updateConfig( + configToBeUpdated + ); + updateRegistry(allServiceModels); + } + }; + + const updateAlertsAndServiceStateData = async () => { + // Use centralized service state API instead of individual call + updateServiceAlertsAndStateFromCentralizedApi("KYUUBI", "kyuubi", allServiceModels, updateRegistry); + }; + + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === serviceNameForPolledApi + ); + } + if ( + (latestHostOperationMessage && + componentFinishStates.includes(latestHostOperationMessage.state)) || + maintenanceStates.includes(latestHostOperationMessage.maintenance_state) + ) { + await updateServiceMaintenanceState( + latestHostOperationMessage.maintenance_state + ); + await updateAlertsAndServiceStateData(); + } + }; + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + findKyuubiMasterComponents(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); + + useEffect(() => { + if (!isEmpty(parsedSocketMessages)) { + parseAlertsWebSocketMessages(); + } + }, [parsedSocketMessages]); +}; diff --git a/ambari-web/latest/src/hooks/useMapReduce2ConfigUpdater.ts b/ambari-web/latest/src/hooks/useMapReduce2ConfigUpdater.ts new file mode 100644 index 00000000000..6e5348a36cb --- /dev/null +++ b/ambari-web/latest/src/hooks/useMapReduce2ConfigUpdater.ts @@ -0,0 +1,210 @@ +import { useContext, useEffect } from "react"; +import { cloneDeep, find, get, isEmpty, isEqual } from "lodash"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { updateServiceAlertsAndStateFromCentralizedApi } from "../Utils/centralizedServiceStateUtils.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums.ts"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import { Categories } from "../enums/Categories.ts"; +import {componentFinishStates, maintenanceStates} from "../screens/Hosts/constants.ts"; + +export const useMapReduce2ConfigUpdater = () => { + // @ts-ignore + const { services, parsedSocketMessages, clusterName } = useContext(AppContext); + + // Early return if MAPREDUCE2 service is not installed + const isMapReduce2Installed = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "MAPREDUCE2"); + + if (!isMapReduce2Installed) { + return; + } + + const { + polledHostComponentsData, + masterSlaveClientsData, + serviceStatesData, + } = useContext(ServiceContext); + //@ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + //const vdpStackVersion = get(cluster, "version", "").split("-")[1]; + + const fetchMR2MasterSlaveClientsData = async () => { + let mr2ComponentsData = cachedServiceApi.getServiceComponentData("MAPREDUCE2"); + + if (!mr2ComponentsData) { + mr2ComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "MAPREDUCE2" + ); + } + + return mr2ComponentsData; + }; + + const updateComponentObjectForSelectMaster = (componentData: any) => { + let masterComponent = find( + //@ts-ignore + polledHostComponentsData?.items, + (item: any) => + get(item, "ServiceComponentInfo.service_name") === "MAPREDUCE2" && + get(item, "ServiceComponentInfo.component_name") === + componentData.componentName + ); + + if (masterComponent) { + const hostComponents = get(masterComponent, "host_components"); + if (hostComponents && hostComponents.length > 0) { + hostComponents.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + componentData.componentName + ) { + componentData.hostComponents.forEach((host: any) => { + const polledHostComponentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const componentDataHostName = get(host, "HostRoles.host_name"); + if (componentDataHostName === polledHostComponentHostName) { + host.state = get(hostComponent, "HostRoles.state"); + host.passiveState = get( + hostComponent, + "HostRoles.maintenance_state" + ); + } + }); + } + }); + } + } + return componentData; + }; + + + const updateJobHistoryServerComponent = async () => { + //@ts-ignore + if (!polledHostComponentsData?.items) return; + + const currentConfig = cloneDeep(allServiceModels["mapreduce2"]); + + let jobHistoryServer = find( + //@ts-ignore + polledHostComponentsData.items, + (item: any) => + get(item, "ServiceComponentInfo.service_name") === "MAPREDUCE2" && + get(item, "ServiceComponentInfo.component_name") === "HISTORYSERVER" + ); + jobHistoryServer.state = get(jobHistoryServer, "HostRoles.state"); + currentConfig[ServiceComponentMetricsEnums.MAPREDUCE2.jobHistoryServer] = + jobHistoryServer; + + // Only update if we have changes + if ( + !isEqual(allServiceModels["mapreduce2"], currentConfig) && + !isEmpty(currentConfig) + ) { + allServiceModels["mapreduce2"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + const findMR2MasterClientComponents = async () => { + const items = await fetchMR2MasterSlaveClientsData(); + + if (!allServiceModels["mapreduce2"]) { + return; + } + + const currentConfig = cloneDeep(allServiceModels["mapreduce2"]); + let masterComponents: any[] = []; + let clientComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.MASTER) { + const masterComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + masterComponents.push(masterComponentDataWithState); + } else { + const mr2ClientsInstalled = componentData.installedCount; + currentConfig[ + ServiceComponentMetricsEnums.MAPREDUCE2.mapReduce2Clients + ] = mr2ClientsInstalled; + clientComponents.push(componentData); + } + }); + + currentConfig[ServiceComponentMetricsEnums.MAPREDUCE2.masterComponents] = + masterComponents; + currentConfig[ServiceComponentMetricsEnums.MAPREDUCE2.clientComponents] = + clientComponents; + + if (!isEqual(allServiceModels["mapreduce2"], currentConfig)) { + allServiceModels["mapreduce2"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + //usePolling(pollServiceComponentInfoApi, 3000); + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep(allServiceModels["mapreduce2"]); + configToBeUpdated.isInPassiveForService = maintenanceState === maintenanceStates[0]; //signifies ON, assigns ON/PFF + + if (!isEqual(allServiceModels["mapreduce2"], configToBeUpdated)) { + allServiceModels["mapreduce2"].updateConfig(configToBeUpdated); + updateRegistry(allServiceModels); + } + } + + const updateAlertsAndServiceStateData = async () => { + // Use centralized service state API instead of individual call + updateServiceAlertsAndStateFromCentralizedApi("MAPREDUCE2", "mapreduce2", allServiceModels, updateRegistry); + }; + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === "MAPREDUCE2" + ); + } + if ( + latestHostOperationMessage && + (componentFinishStates.includes(latestHostOperationMessage.state) + || (latestHostOperationMessage.maintenance_state && maintenanceStates.includes(latestHostOperationMessage.maintenance_state))) + ) { + await updateServiceMaintenanceState(latestHostOperationMessage.maintenance_state); + await updateAlertsAndServiceStateData(); + } + }; + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + findMR2MasterClientComponents(); + updateJobHistoryServerComponent(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + parseAlertsWebSocketMessages(); + }, [parsedSocketMessages]); + + useEffect(() => { + //pollServiceComponentInfoApi(); + findMR2MasterClientComponents(); + }, []); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); +}; diff --git a/ambari-web/latest/src/hooks/usePinotConfigUpdater.ts b/ambari-web/latest/src/hooks/usePinotConfigUpdater.ts new file mode 100644 index 00000000000..7966bfc9d03 --- /dev/null +++ b/ambari-web/latest/src/hooks/usePinotConfigUpdater.ts @@ -0,0 +1,223 @@ +import { useContext, useEffect} from "react"; +import { cloneDeep, find, get, isEqual } from "lodash"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { centralizedServiceStateApi } from "../api/CentralizedServiceStateApi.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import { componentFinishStates, maintenanceStates } from "../screens/Hosts/constants"; +import { Categories } from "../enums/Categories"; + +export const usePinotConfigUpdater = () => { + const { + polledHostComponentsData, + masterSlaveClientsData, + serviceStatesData, + } = useContext(ServiceContext); + + // @ts-ignore + const { services, clusterName, parsedSocketMessages } = useContext(AppContext); + + // Early return if PINOT service is not installed + const isPinotInstalled = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "PINOT"); + + if (!isPinotInstalled) { + return; + } + + // @ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + + const fetchPinotMasterSlaveClientsData = async () => { + let pinotComponentsData = cachedServiceApi.getServiceComponentData("PINOT"); + + if (!pinotComponentsData) { + pinotComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "PINOT" + ); + } + + return pinotComponentsData; + }; + + const updateComponentObjectForSelectMaster = (componentData: any) => { + let masterComponent = find( + //@ts-ignore + polledHostComponentsData?.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "PINOT" && + get(item, "ServiceComponentInfo.component_name") === + componentData.componentName + ); + + if (masterComponent) { + const hostComponents = get(masterComponent, "host_components"); + if (hostComponents && hostComponents.length > 0) { + hostComponents.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + componentData.componentName + ) { + componentData.hostComponents.forEach((host: any) => { + const polledHostComponentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const componentDataHostName = get(host, "HostRoles.host_name"); + if (componentDataHostName === polledHostComponentHostName) { + host.state = get(hostComponent, "HostRoles.state"); + host.passiveState = get( + hostComponent, + "HostRoles.maintenance_state" + ); + } + }); + } + }); + } + } + return componentData; + }; + + const findMasterSlaveClientComponents = async () => { + const items = await fetchPinotMasterSlaveClientsData(); + + if (!allServiceModels["pinot"]) { + return; + } + + const currentConfig = cloneDeep(allServiceModels["pinot"]); + let masterComponents: any[] = []; + let slaveComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.MASTER) { + const masterComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + masterComponents.push(masterComponentDataWithState); + } else if (componentData.category === Categories.SLAVE) { + slaveComponents.push(componentData); + + // Update specific component counts + if (componentData.componentName === "PINOT_BROKER") { + currentConfig[ServiceComponentMetricsEnums.PINOT.pinotBrokerStartedCount] = componentData.startedCount; + currentConfig[ServiceComponentMetricsEnums.PINOT.pinotBrokerInstalledCount] = componentData.installedCount; + currentConfig[ServiceComponentMetricsEnums.PINOT.pinotBrokerTotalCount] = componentData.totalCount; + } else if (componentData.componentName === "PINOT_MINION") { + currentConfig[ServiceComponentMetricsEnums.PINOT.pinotMinionStartedCount] = componentData.startedCount; + currentConfig[ServiceComponentMetricsEnums.PINOT.pinotMinionInstalledCount] = componentData.installedCount; + currentConfig[ServiceComponentMetricsEnums.PINOT.pinotMinionTotalCount] = componentData.totalCount; + } else if (componentData.componentName === "PINOT_SERVER") { + currentConfig[ServiceComponentMetricsEnums.PINOT.pinotServerStartedCount] = componentData.startedCount; + currentConfig[ServiceComponentMetricsEnums.PINOT.pinotServerInstalledCount] = componentData.installedCount; + currentConfig[ServiceComponentMetricsEnums.PINOT.pinotServerTotalCount] = componentData.totalCount; + } + } + }); + + currentConfig[ServiceComponentMetricsEnums.PINOT.masterComponents] = masterComponents; + currentConfig[ServiceComponentMetricsEnums.PINOT.slaveComponents] = slaveComponents; + + if (!isEqual(allServiceModels["pinot"], currentConfig)) { + allServiceModels["pinot"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + const parseWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.destination === "/events/hostcomponents" + ); + } + + if ( + latestHostOperationMessage && + latestHostOperationMessage.hostComponents + ) { + for (const hostComponent of latestHostOperationMessage.hostComponents) { + if (hostComponent.currentState in componentFinishStates) { + await findMasterSlaveClientComponents(); + } + } + } + }; + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep(allServiceModels["pinot"]); + configToBeUpdated.isInPassiveForService = maintenanceState === maintenanceStates[0]; //signifies ON, assigns ON/OFF + + if (!isEqual(allServiceModels["pinot"], configToBeUpdated)) { + allServiceModels["pinot"].updateConfig(configToBeUpdated); + updateRegistry(allServiceModels); + } + } + + const updateAlertsAndServiceStateData = async () => { + const currentConfig = cloneDeep(allServiceModels["pinot"]); + const serviceName = "PINOT"; + + // Use centralized service state API instead of individual call + const serviceStateData = centralizedServiceStateApi.getServiceStateData(serviceName); + + if (!serviceStateData) return; + + const { alertsCount, hasCriticalAlerts, state } = serviceStateData; + + if (!alertsCount && alertsCount !== 0) return; + + currentConfig[ServiceComponentMetricsEnums.PINOT.hasCriticalAlerts] = hasCriticalAlerts; + currentConfig[ServiceComponentMetricsEnums.PINOT.alertsCount] = alertsCount; + currentConfig[ServiceComponentMetricsEnums.PINOT.state] = state; + + if (!isEqual(allServiceModels["pinot"], currentConfig)) { + allServiceModels["pinot"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === "PINOT" + ); + } + if ( + latestHostOperationMessage && + componentFinishStates.includes(latestHostOperationMessage.state) + || (latestHostOperationMessage.maintenance_state && maintenanceStates.includes(latestHostOperationMessage.maintenance_state)) + ) { + await updateServiceMaintenanceState(latestHostOperationMessage.maintenance_state); + await updateAlertsAndServiceStateData(); + } + }; + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + findMasterSlaveClientComponents(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); + + useEffect(() => { + parseWebSocketMessages(); + parseAlertsWebSocketMessages(); + }, [parsedSocketMessages]); +}; diff --git a/ambari-web/latest/src/hooks/useRangerConfigUpdater.ts b/ambari-web/latest/src/hooks/useRangerConfigUpdater.ts new file mode 100644 index 00000000000..e9562ba06f5 --- /dev/null +++ b/ambari-web/latest/src/hooks/useRangerConfigUpdater.ts @@ -0,0 +1,416 @@ +import { useContext, useEffect, useRef } from "react"; +import { cloneDeep, find, get, isEmpty, isEqual } from "lodash"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { updateServiceAlertsAndStateFromCentralizedApi } from "../Utils/centralizedServiceStateUtils.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums.ts"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import {componentFinishStates, maintenanceStates} from "../screens/Hosts/constants.ts"; +import { Categories } from "../enums/Categories.ts"; +import ConfigsApi from "../api/ConfigsApi.tsx"; + +export const useRangerConfigUpdater = () => { + let isRangerConfigUpdating = useRef(false); + const { + polledHostComponentsData, + masterSlaveClientsData, + serviceStatesData, + } = useContext(ServiceContext); + + // @ts-ignore + const { services, clusterName, parsedSocketMessages } = useContext(AppContext); + + // Early return if RANGER service is not installed + const isRangerInstalled = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "RANGER"); + + if (!isRangerInstalled) { + return; + } + + //@ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + //const vdpStackVersion = get(cluster, "version", "").split("-")[1]; + const isFetchingRangerPluginInfo = useRef(false); // Flag to check if the function is running + + const fetchRangerMasterSlaveClientsData = async () => { + return Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "RANGER" + ); + }; + + const updateComponentObjectForSelectMaster = (componentData: any) => { + let masterComponent = find( + //@ts-ignore + polledHostComponentsData?.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "RANGER" && + get(item, "ServiceComponentInfo.component_name") === + componentData.componentName + ); + + if (masterComponent) { + const hostComponents = get(masterComponent, "host_components"); + if (hostComponents && hostComponents.length > 0) { + hostComponents.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + componentData.componentName + ) { + componentData.hostComponents.forEach((host: any) => { + const polledHostComponentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const componentDataHostName = get(host, "HostRoles.host_name"); + if (componentDataHostName === polledHostComponentHostName) { + host.state = get(hostComponent, "HostRoles.state"); + host.passiveState = get( + hostComponent, + "HostRoles.maintenance_state" + ); + } + }); + } + }); + } + } + return componentData; + }; + + const findMasterSlaveClientComponents = async () => { + const items = await fetchRangerMasterSlaveClientsData(); + + if (!allServiceModels || !allServiceModels["ranger"]) { + return; + } + + const currentConfig = cloneDeep(allServiceModels["ranger"]); + + let masterComponents: any[] = []; + let slaveComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.MASTER) { + if ( + componentData.componentName === "RANGER_ADMIN" || + componentData.componentName === "RANGER_USERSYNC" + ) { + const masterComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + masterComponents.push(masterComponentDataWithState); + } + } else if (componentData.category === Categories.SLAVE) { + slaveComponents.push(componentData); + } + }); + + currentConfig[ServiceComponentMetricsEnums.RANGER.masterComponents] = + masterComponents; + currentConfig[ServiceComponentMetricsEnums.RANGER.slaveComponents] = + slaveComponents; + + if (!isEqual(allServiceModels["ranger"], currentConfig)) { + isRangerConfigUpdating.current = true; + allServiceModels["ranger"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + isRangerConfigUpdating.current = false; + } + // if (!isEqual(allServiceModels["ranger"], currentConfig)) { + // updateQueue.enqueue(async () => { + // allServiceModels["ranger"].updateConfig(currentConfig); + // updateRegistry(allServiceModels); + // }); + // } + }; + + //updating RANGER_TAGSYNC + //@ts-ignore + //copy paste this in spark and it will show the counts + const updateRangerHostComponentsData = async () => { + // 🚀 OPTIMIZATION: Use centralized cached data instead of individual API call + + if (!allServiceModels || !allServiceModels["ranger"]) return; + + let updatedConfig = cloneDeep(allServiceModels["ranger"]); + + // Get RANGER data from centralized cache (Ember.js style) + const rangerComponentsData = cachedServiceApi.getServiceComponentData("RANGER"); + + if (!rangerComponentsData) { + return; + } + + const components = [{ name: "RANGER_TAGSYNC", metric: "rangerTagsyncs" }]; + + components.forEach((component) => { + // Find component in cached data instead of making API call + const hostComponents = rangerComponentsData.find((item: any) => + item.ServiceComponentInfo?.component_name === component.name + ); + + if (hostComponents && hostComponents.ServiceComponentInfo) { + const installedCount = hostComponents.ServiceComponentInfo.installed_count; + const startedCount = hostComponents.ServiceComponentInfo.started_count; + const totalCount = hostComponents.ServiceComponentInfo.total_count; + + updatedConfig[ + ServiceComponentMetricsEnums.RANGER[ + `${component.metric}Started` as keyof typeof ServiceComponentMetricsEnums.RANGER + ] as any + ] = startedCount; + updatedConfig[ + ServiceComponentMetricsEnums.RANGER[ + `${component.metric}Installed` as keyof typeof ServiceComponentMetricsEnums.RANGER + ] as any + ] = installedCount; + updatedConfig[ + ServiceComponentMetricsEnums.RANGER[ + `${component.metric}Total` as keyof typeof ServiceComponentMetricsEnums.RANGER + ] as any + ] = totalCount; + } + }); + + if ( + !isEqual(allServiceModels["ranger"], updatedConfig) && + !isRangerConfigUpdating.current + ) { + isRangerConfigUpdating.current = true; + allServiceModels["ranger"].updateConfig(updatedConfig); + updateRegistry(allServiceModels); + isRangerConfigUpdating.current = false; + } + }; + + const parseWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.destination === "/events/hostcomponents" + ); + } + + if ( + latestHostOperationMessage && + latestHostOperationMessage.hostComponents + ) { + for (const hostComponent of latestHostOperationMessage.hostComponents) { + if (hostComponent.currentState in componentFinishStates) { + //await updateRangerHostComponentsData(); + await findMasterSlaveClientComponents(); + } + } + } + }; + + + const fetchRangerPluginInfo = async () => { + if (isFetchingRangerPluginInfo.current) { + return; + } + + + let configsData: any = {}; + + if (!allServiceModels || !allServiceModels["ranger"]) return; + + const desiredConfigsResponse = + await ConfigsApi.getDesiredConfigsInfo(clusterName); + + + const configTags = [ + "ranger-hdfs-plugin-properties", + "ranger-hbase-plugin-properties", + "hive-env", + "ranger-yarn-plugin-properties", + ]; + + const configTagValues = configTags.map((tag) => + get(desiredConfigsResponse, `data.Clusters.desired_configs.${tag}.tag`) + ); + + + const rangerPluginInfo = await ConfigsApi.getEnabledConfigsForRangerPlugins( + //@ts-ignore + ...configTagValues, + clusterName + ); + + + const rangerPluginPropertiesKeys = Object.keys( + ServiceComponentMetricsEnums.RANGER.pluginProperties + ); + + + rangerPluginPropertiesKeys.forEach((key) => { + configsData[key] = rangerPluginInfo.data.items.find( + (item: any) => item.type === key + ); + }); + + const pluginStatusMapping = { + "ranger-hdfs-plugin-properties": + get( + configsData, + "ranger-hdfs-plugin-properties.properties.ranger-hdfs-plugin-enabled" + ) === "Yes" + ? "Enabled" + : "Disabled", + "ranger-hbase-plugin-properties": + get( + configsData, + "ranger-hbase-plugin-properties.properties.ranger-hbase-plugin-enabled" + ) === "Yes" + ? "Enabled" + : "Disabled", + "hive-env": + get(configsData, "hive-env.properties.hive_security_authorization") === + "Ranger" + ? "Enabled" + : "Disabled", + "ranger-yarn-plugin-properties": + get( + configsData, + "ranger-yarn-plugin-properties.properties.ranger-yarn-plugin-enabled" + ) === "Yes" + ? "Enabled" + : "Disabled", + }; + + let currentConfig = cloneDeep(allServiceModels["ranger"]); + Object.keys(pluginStatusMapping).forEach((key) => { + const propertyKey = + key as keyof typeof ServiceComponentMetricsEnums.RANGER.pluginProperties; + currentConfig[ + ServiceComponentMetricsEnums.RANGER.pluginProperties[propertyKey] + ] = + //@ts-ignore + pluginStatusMapping[key]; + }); + + // Object.keys(pluginStatusMapping).forEach((key) => { + // currentConfig[ + // ServiceComponentMetricsEnums.RANGER.pluginProperties[ + // key as keyof typeof ServiceComponentMetricsEnums.RANGER.pluginProperties + // ] + // ] = + // pluginStatusMapping[ + // key as keyof typeof ServiceComponentMetricsEnums.RANGER.pluginProperties + // ]; + // }); + + if (!isEqual(allServiceModels["ranger"], currentConfig)) { + isRangerConfigUpdating.current = true; + allServiceModels["ranger"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + isRangerConfigUpdating.current = false; + } + }; + + const updateRangerAdminComponent = async () => { + //@ts-ignore + if (!polledHostComponentsData?.items || !allServiceModels["ranger"]) return; + + const currentConfig = cloneDeep(allServiceModels["ranger"]); + let rangerAdmins = [] as any; + + let rangerAdmin = find( + //@ts-ignore + polledHostComponentsData.items, + (item: any) => + get(item, "ServiceComponentInfo.service_name") === "RANGER" && + get(item, "ServiceComponentInfo.component_name") === "RANGER_ADMIN" + ); + + if (!isEmpty(rangerAdmin)) { + rangerAdmin.host_components.forEach((hostComponent: any) => { + if (get(hostComponent, "HostRoles.component_name") === "RANGER_ADMIN") { + const hostComponentData = { + componentName: "RANGER_ADMIN", + hostName: get(hostComponent, "HostRoles.host_name"), + state: get(hostComponent, "HostRoles.state"), + }; + rangerAdmins.push(hostComponentData); + } + }); + currentConfig[ServiceComponentMetricsEnums.RANGER.rangerAdmins] = + rangerAdmins; + } + + // Only update if we have changes + if ( + !isEqual(allServiceModels["ranger"], currentConfig) && + !isRangerConfigUpdating.current + ) { + isRangerConfigUpdating.current = true; + allServiceModels["ranger"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + isRangerConfigUpdating.current = false; + } + }; + + const updateAlertsAndServiceStateData = async () => { + // Use centralized service state API instead of individual call + updateServiceAlertsAndStateFromCentralizedApi("RANGER", "ranger", allServiceModels, updateRegistry); + }; + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep(allServiceModels["ranger"]); + configToBeUpdated.isInPassiveForService = maintenanceState === maintenanceStates[0]; //signifies ON, assigns ON/PFF + + if (!isEqual(allServiceModels["ranger"], configToBeUpdated)) { + allServiceModels["ranger"].updateConfig(configToBeUpdated); + updateRegistry(allServiceModels); + } + } + + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === "RANGER" + ); + } + if ( + latestHostOperationMessage && + componentFinishStates.includes(latestHostOperationMessage.state) + || (latestHostOperationMessage.maintenance_state && maintenanceStates.includes(latestHostOperationMessage.maintenance_state)) + ) { + await updateServiceMaintenanceState(latestHostOperationMessage.maintenance_state); + await updateAlertsAndServiceStateData(); + } + }; + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + findMasterSlaveClientComponents(); + updateRangerAdminComponent(); + fetchRangerPluginInfo(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + updateRangerHostComponentsData(); + }, []); + + useEffect(() => { + parseWebSocketMessages(); + parseAlertsWebSocketMessages(); + }, [parsedSocketMessages]); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); +}; diff --git a/ambari-web/latest/src/hooks/useRangerKMSConfigUpdater.ts b/ambari-web/latest/src/hooks/useRangerKMSConfigUpdater.ts new file mode 100644 index 00000000000..1621b99206d --- /dev/null +++ b/ambari-web/latest/src/hooks/useRangerKMSConfigUpdater.ts @@ -0,0 +1,172 @@ +import { useContext, useEffect } from "react"; +import { cloneDeep, find, get, isEqual } from "lodash"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { updateServiceAlertsAndStateFromCentralizedApi } from "../Utils/centralizedServiceStateUtils.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums.ts"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import { Categories } from "../enums/Categories.ts"; +import {componentFinishStates, maintenanceStates} from "../screens/Hosts/constants.ts"; + +export const useRangerKMSConfigUpdater = () => { + const { parsedSocketMessages } = useContext(AppContext); + const { polledHostComponentsData, masterSlaveClientsData, serviceStatesData } = + useContext(ServiceContext); + const { services} = useContext(AppContext); + //@ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + + const isRangerKMSInstalled = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "RANGER_KMS"); + + if (!isRangerKMSInstalled) { + return; + } + + const fetchRangerKMSMasterSlaveClientsData = async () => { + // 🚀 OPTIMIZATION: Try centralized cache first, fallback to masterSlaveClientsData + + let rangerKMSComponentsData = cachedServiceApi.getServiceComponentData("RANGER_KMS"); + + if (!rangerKMSComponentsData) { + rangerKMSComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "RANGER_KMS" + ); + } else { + } + + return rangerKMSComponentsData; + }; + + + const updateComponentObjectForSelectMaster = (componentData: any) => { + let masterComponent = find( + //@ts-ignore + polledHostComponentsData?.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "RANGER_KMS" && + get(item, "ServiceComponentInfo.component_name") === + componentData.componentName + ); + + if (masterComponent) { + const hostComponents = get(masterComponent, "host_components"); + if (hostComponents && hostComponents.length > 0) { + hostComponents.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + componentData.componentName + ) { + componentData.hostComponents.forEach((host: any) => { + const polledHostComponentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const componentDataHostName = get(host, "HostRoles.host_name"); + if (componentDataHostName === polledHostComponentHostName) { + host.state = get(hostComponent, "HostRoles.state"); + host.passiveState = get( + hostComponent, + "HostRoles.maintenance_state" + ); + } + }); + } + }); + } + } + return componentData; + }; + + const findRangerKMSMasterComponents = async () => { + const items = await fetchRangerKMSMasterSlaveClientsData(); + + if (!allServiceModels["ranger_kms"]) { + return; + } + + const currentConfig = cloneDeep(allServiceModels["ranger_kms"]); + let masterComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.MASTER) { + if (componentData.componentName === "RANGER_KMS_SERVER") { + const masterComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + masterComponents.push(masterComponentDataWithState); + } + } + }); + + currentConfig[ServiceComponentMetricsEnums.RANGER_KMS.masterComponents] = + masterComponents; + + if (!isEqual(allServiceModels["ranger_kms"], currentConfig)) { + allServiceModels["ranger_kms"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + //usePolling(pollServiceComponentInfoApi, 3000); + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep(allServiceModels["ranger_kms"]); + configToBeUpdated.isInPassiveForService = maintenanceState === maintenanceStates[0]; //signifies ON, assigns ON/PFF + + if (!isEqual(allServiceModels["ranger_kms"], configToBeUpdated)) { + allServiceModels["ranger_kms"].updateConfig(configToBeUpdated); + updateRegistry(allServiceModels); + } + } + + const updateAlertsAndServiceStateData = async () => { + // Use centralized service state API instead of individual call + updateServiceAlertsAndStateFromCentralizedApi("RANGER_KMS", "ranger_kms", allServiceModels, updateRegistry); + }; + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === "RANGER_KMS" + ); + } + if ( + latestHostOperationMessage && + componentFinishStates.includes(latestHostOperationMessage.state) + || (latestHostOperationMessage.maintenance_state && maintenanceStates.includes(latestHostOperationMessage.maintenance_state)) + ) { + await updateServiceMaintenanceState(latestHostOperationMessage.maintenance_state); + await updateAlertsAndServiceStateData(); + } + }; + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + findRangerKMSMasterComponents(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + parseAlertsWebSocketMessages(); + }, [parsedSocketMessages]); + + useEffect(() => { + //pollServiceComponentInfoApi(); + findRangerKMSMasterComponents(); + }, []); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); +}; diff --git a/ambari-web/latest/src/hooks/useSSMConfigUpdater.ts b/ambari-web/latest/src/hooks/useSSMConfigUpdater.ts new file mode 100644 index 00000000000..5eaa12570bb --- /dev/null +++ b/ambari-web/latest/src/hooks/useSSMConfigUpdater.ts @@ -0,0 +1,251 @@ +import { useContext, useEffect } from "react"; +import { cloneDeep, find, get, isEmpty, isEqual } from "lodash"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { updateServiceAlertsAndStateFromCentralizedApi } from "../Utils/centralizedServiceStateUtils.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums.ts"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import {componentFinishStates, maintenanceStates} from "../screens/Hosts/constants.ts"; +import { Categories } from "../enums/Categories.ts"; + +export const useSSMConfigUpdater = () => { + const { + polledHostComponentsData, + masterSlaveClientsData, + serviceStatesData, + } = useContext(ServiceContext); + const { services } = useContext(AppContext); + const { parsedSocketMessages } = useContext(AppContext); + //@ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + //const vdpStackVersion = get(cluster, "version", "").split("-")[1]; + + const isSSMInstalled = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "SSM"); + + if (!isSSMInstalled) { + return; + } + + const fetchSSMMasterSlaveClientsData = async () => { + // 🚀 OPTIMIZATION: Try centralized cache first, fallback to masterSlaveClientsData + + let ssmComponentsData = cachedServiceApi.getServiceComponentData("SSM"); + + if (!ssmComponentsData) { + ssmComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "SSM" + ); + } else { + } + + return ssmComponentsData; + }; + + const updateComponentObjectForSelectMaster = (componentData: any) => { + let masterComponent = find( + //@ts-ignore + polledHostComponentsData?.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "SSM" && + get(item, "ServiceComponentInfo.component_name") === + componentData.componentName + ); + + if (masterComponent) { + const hostComponents = get(masterComponent, "host_components"); + if (hostComponents && hostComponents.length > 0) { + hostComponents.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + componentData.componentName + ) { + componentData.hostComponents.forEach((host: any) => { + const polledHostComponentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const componentDataHostName = get(host, "HostRoles.host_name"); + if (componentDataHostName === polledHostComponentHostName) { + host.is_active_ssm_ip = get( + hostComponent, + "processes[0].HostComponentProcess.active_ssm_ip" + ); + host.state = get(hostComponent, "HostRoles.state"); + host.passiveState = get( + hostComponent, + "HostRoles.maintenance_state" + ); + } + }); + } + }); + } + } + return componentData; + }; + + const findMasterSlaveComponents = async () => { + const items = await fetchSSMMasterSlaveClientsData(); + + if (!allServiceModels["ssm"]) { + return; + } + + const currentConfig = cloneDeep(allServiceModels["ssm"]); + let masterComponents: any[] = []; + let slaveComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.MASTER) { + if (componentData.componentName === "SSM_SERVER") { + const masterComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + masterComponents.push(masterComponentDataWithState); + } + } else if (componentData.category === Categories.SLAVE) { + slaveComponents.push(componentData); + } + }); + + currentConfig[ServiceComponentMetricsEnums.SSM.masterComponents] = + masterComponents; + currentConfig[ServiceComponentMetricsEnums.SSM.slaveComponents] = + slaveComponents; + + if (!isEqual(allServiceModels["ssm"], currentConfig)) { + allServiceModels["ssm"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + //check for updating metrics monitor + //@ts-ignore + const parseWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.destination === "/events/hostcomponents" + ); + } + + if ( + latestHostOperationMessage && + latestHostOperationMessage.hostComponents + ) { + for (const hostComponent of latestHostOperationMessage.hostComponents) { + if (hostComponent.currentState in componentFinishStates) { + //await updateRangerHostComponentsData(); + await findMasterSlaveComponents(); + } + } + } + }; + + + const updateSmartServerComponent = async () => { + //@ts-ignore + if (!polledHostComponentsData?.items) return; + + const currentConfig = cloneDeep(allServiceModels["ssm"]); + let smartServers = [] as any; + + let smartServer = find( + //@ts-ignore + polledHostComponentsData.items, + (item: any) => + get(item, "ServiceComponentInfo.service_name") === "SSM" && + get(item, "ServiceComponentInfo.component_name") === "SSM_SERVER" + ); + + if (!isEmpty(smartServer)) { + smartServer.host_components.forEach((hostComponent: any) => { + if (get(hostComponent, "HostRoles.component_name") === "SSM_SERVER") { + const hostComponentData = { + componentName: "SSM_SERVER", + hostName: get(hostComponent, "HostRoles.host_name"), + haState: get( + hostComponent, + "processes[0].HostComponentProcess.active_ssm_ip" + ), + }; + smartServers.push(hostComponentData); + } + }); + currentConfig[ + ServiceComponentMetricsEnums.SSM + .smartServers as keyof typeof ServiceComponentMetricsEnums.SSM + ] = smartServers; + } + if (!isEqual(allServiceModels["ssm"], currentConfig)) { + allServiceModels["ssm"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + //usePolling(pollServiceComponentInfoApi, 1000); + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep(allServiceModels["ssm"]); + configToBeUpdated.isInPassiveForService = maintenanceState === maintenanceStates[0]; //signifies ON, assigns ON/PFF + + if (!isEqual(allServiceModels["ssm"], configToBeUpdated)) { + allServiceModels["ssm"].updateConfig(configToBeUpdated); + updateRegistry(allServiceModels); + } + } + + const updateAlertsAndServiceStateData = async () => { + // Use centralized service state API instead of individual call + updateServiceAlertsAndStateFromCentralizedApi("SSM", "ssm", allServiceModels, updateRegistry); + }; + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === "SSM" + ); + } + if ( + latestHostOperationMessage && + componentFinishStates.includes(latestHostOperationMessage.state) + || (latestHostOperationMessage.maintenance_state && maintenanceStates.includes(latestHostOperationMessage.maintenance_state)) + ) { + await updateServiceMaintenanceState(latestHostOperationMessage.maintenance_state); + await updateAlertsAndServiceStateData(); + } + }; + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + findMasterSlaveComponents(); + updateSmartServerComponent(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + //pollServiceComponentInfoApi(); + findMasterSlaveComponents(); + //updateAmbariMetricsHostComponentsData(); + }, []); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); + + useEffect(() => { + parseWebSocketMessages(); + parseAlertsWebSocketMessages(); + }, [parsedSocketMessages]); +}; diff --git a/ambari-web/latest/src/hooks/useSpark3ConfigUpdater.ts b/ambari-web/latest/src/hooks/useSpark3ConfigUpdater.ts new file mode 100644 index 00000000000..83b19eafdd1 --- /dev/null +++ b/ambari-web/latest/src/hooks/useSpark3ConfigUpdater.ts @@ -0,0 +1,301 @@ +import { useContext, useEffect } from "react"; +import { cloneDeep, find, get, isEmpty, isEqual } from "lodash"; +import { ServiceApi } from "../api/ServiceApi.ts"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { updateServiceAlertsAndStateFromCentralizedApi } from "../Utils/centralizedServiceStateUtils.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums.ts"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import { Categories } from "../enums/Categories.ts"; +import {componentFinishStates, maintenanceStates} from "../screens/Hosts/constants.ts"; + +export const useSpark3ConfigUpdater = () => { + const { + polledHostComponentsData, + masterSlaveClientsData, + serviceStatesData, + } = useContext(ServiceContext); + + // @ts-ignore + const { services, clusterName, parsedSocketMessages } = useContext(AppContext); + + // Early return if SPARK3 service is not installed + const isSpark3Installed = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "SPARK3"); + + if (!isSpark3Installed) { + return; + } + + //@ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + //const vdpStackVersion = get(cluster, "version", "").split("-")[1]; + + const fetchSpark3MasterSlaveClientsData = async () => { + // 🚀 OPTIMIZATION: Try centralized cache first, fallback to masterSlaveClientsData + + let spark3ComponentsData = cachedServiceApi.getServiceComponentData("SPARK3"); + + if (!spark3ComponentsData) { + spark3ComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "SPARK3" + ); + } else { + } + + return spark3ComponentsData; + }; + + const updateComponentObjectForSelectMaster = (componentData: any) => { + let masterComponent = find( + //@ts-ignore + polledHostComponentsData?.items, + (item: any) => + get(item, "ServiceComponentInfo.service_name") === "SPARK3" && + get(item, "ServiceComponentInfo.component_name") === + componentData.componentName + ); + + if (masterComponent) { + const hostComponents = get(masterComponent, "host_components"); + if (hostComponents && hostComponents.length > 0) { + hostComponents.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + componentData.componentName + ) { + componentData.hostComponents.forEach((host: any) => { + const polledHostComponentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const componentDataHostName = get(host, "HostRoles.host_name"); + if (componentDataHostName === polledHostComponentHostName) { + host.state = get(hostComponent, "HostRoles.state"); + host.passiveState = get( + hostComponent, + "HostRoles.maintenance_state" + ); + } + }); + } + }); + } + } + return componentData; + }; + + + const updateSpark3HostComponentsData = async () => { + let updatedConfig = cloneDeep(allServiceModels["spark3"]); + const serviceName = "SPARK3"; // Replace with the desired service name + const fields = `ServiceComponentInfo/service_name,ServiceComponentInfo/category,ServiceComponentInfo/installed_count,ServiceComponentInfo/started_count,ServiceComponentInfo/init_count,ServiceComponentInfo/install_failed_count,ServiceComponentInfo/unknown_count,ServiceComponentInfo/total_count,ServiceComponentInfo/display_name,host_components/HostRoles/host_name&minimal_response=true`; + await ServiceApi.getAllServiceComponentsListAndInitialMetrics( + clusterName, + `${fields}&ServiceComponentInfo/service_name=${serviceName}` + ); + + const components = [ + { name: "LIVY3_SERVER", metric: "livyForSpark3Server" }, + { name: "SPARK3_THRIFTSERVER", metric: "spark3ThriftServer" }, + ]; + + components.forEach((component) => { + // const hostComponents = findHostComponentItems("RANGER", component.name, response); + const hostComponents = { ServiceComponentInfo: {} as any }; + + if (!hostComponents || !hostComponents.ServiceComponentInfo) { + return; // Skip if hostComponents is undefined or doesn't have ServiceComponentInfo + } + + const installedCount = + hostComponents.ServiceComponentInfo.installed_count; + const startedCount = hostComponents.ServiceComponentInfo.started_count; + const totalCount = hostComponents.ServiceComponentInfo.total_count; + updatedConfig[ + ServiceComponentMetricsEnums.SPARK3[ + `${component.metric}Started` as keyof typeof ServiceComponentMetricsEnums.SPARK3 + ] as any + ] = startedCount; + updatedConfig[ + ServiceComponentMetricsEnums.SPARK3[ + `${component.metric}Installed` as keyof typeof ServiceComponentMetricsEnums.SPARK3 + ] as any + ] = installedCount; + updatedConfig[ + ServiceComponentMetricsEnums.SPARK3[ + `${component.metric}Total` as keyof typeof ServiceComponentMetricsEnums.SPARK3 + ] as any + ] = totalCount; + }); + + if (!isEqual(allServiceModels["spark3"], updatedConfig)) { + allServiceModels["spark3"].updateConfig(updatedConfig); + updateRegistry(allServiceModels); + } + }; + + const updateSpark3HistoryServerComponent = async () => { + //@ts-ignore + if (!polledHostComponentsData?.items) return; + + const currentConfig = cloneDeep(allServiceModels["spark3"]); + let spark3HistoryServers = [] as any; + + let spark3HistoryServer = find( + //@ts-ignore + polledHostComponentsData.items, + (item: any) => + get(item, "ServiceComponentInfo.service_name") === "SPARK3" && + get(item, "ServiceComponentInfo.component_name") === + "SPARK3_JOBHISTORYSERVER" + ); + + if (!isEmpty(spark3HistoryServer)) { + spark3HistoryServer.host_components.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + "SPARK3_JOBHISTORYSERVER" + ) { + const hostComponentData = { + componentName: "SPARK3_JOBHISTORYSERVER", + hostName: get(hostComponent, "HostRoles.host_name"), + state: get(hostComponent, "HostRoles.state"), + }; + spark3HistoryServers.push(hostComponentData); + } + }); + currentConfig[ + ServiceComponentMetricsEnums.SPARK3.spark3JobHistoryServers + ] = spark3HistoryServers; + } + if (!isEqual(allServiceModels["spark3"], currentConfig)) { + allServiceModels["spark3"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + const findMasterSlaveClientComponents = async () => { + const items = await fetchSpark3MasterSlaveClientsData(); + + if (!allServiceModels["spark3"]) { + return; + } + + const currentConfig = cloneDeep(allServiceModels["spark3"]); + let masterComponents: any[] = []; + let slaveConponents: any[] = []; + let clientComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.MASTER) { + const masterComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + masterComponents.push(masterComponentDataWithState); + } else if (componentData.category === Categories.SLAVE) { + slaveConponents.push(componentData); + } else { + const spark3ClientsInstalled = componentData.installedCount; + currentConfig[ServiceComponentMetricsEnums.SPARK3.spark3Clients] = + spark3ClientsInstalled; + clientComponents.push(componentData); + } + }); + + currentConfig[ServiceComponentMetricsEnums.SPARK3.masterComponents] = + masterComponents; + currentConfig[ServiceComponentMetricsEnums.SPARK3.slaveComponents] = + slaveConponents; + currentConfig[ServiceComponentMetricsEnums.SPARK3.clientComponents] = + clientComponents; + + if (!isEqual(allServiceModels["spark3"], currentConfig)) { + allServiceModels["spark3"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + const parseWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.destination === "/events/hostcomponents" + ); + } + + if ( + latestHostOperationMessage && + latestHostOperationMessage.hostComponents + ) { + for (const hostComponent of latestHostOperationMessage.hostComponents) { + if (hostComponent.currentState in componentFinishStates) { + await findMasterSlaveClientComponents(); + } + } + } + }; + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep(allServiceModels["spark3"]); + configToBeUpdated.isInPassiveForService = maintenanceState === maintenanceStates[0]; //signifies ON, assigns ON/PFF + + if (!isEqual(allServiceModels["spark3"], configToBeUpdated)) { + allServiceModels["spark3"].updateConfig(configToBeUpdated); + updateRegistry(allServiceModels); + } + } + + const updateAlertsAndServiceStateData = async () => { + // Use centralized service state API instead of individual call + updateServiceAlertsAndStateFromCentralizedApi("SPARK3", "spark3", allServiceModels, updateRegistry); + }; + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === "SPARK3" + ); + } + if ( + latestHostOperationMessage && + componentFinishStates.includes(latestHostOperationMessage.state) + || (latestHostOperationMessage.maintenance_state && maintenanceStates.includes(latestHostOperationMessage.maintenance_state)) + ) { + await updateServiceMaintenanceState(latestHostOperationMessage.maintenance_state); + await updateAlertsAndServiceStateData(); + } + }; + + //usePolling(pollServiceComponentInfoApi, 3000); + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + findMasterSlaveClientComponents(); + updateSpark3HistoryServerComponent(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + //pollServiceComponentInfoApi(); + findMasterSlaveClientComponents(); + updateSpark3HostComponentsData(); + }, []); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); + + useEffect(() => { + parseWebSocketMessages(); + parseAlertsWebSocketMessages(); + }, [parsedSocketMessages]); +}; diff --git a/ambari-web/latest/src/hooks/useTezConfigUpdater.ts b/ambari-web/latest/src/hooks/useTezConfigUpdater.ts new file mode 100644 index 00000000000..360bbda31b8 --- /dev/null +++ b/ambari-web/latest/src/hooks/useTezConfigUpdater.ts @@ -0,0 +1,134 @@ +import { useContext, useEffect } from "react"; +import { cloneDeep, get, isEqual } from "lodash"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { updateServiceAlertsAndStateFromCentralizedApi } from "../Utils/centralizedServiceStateUtils.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums.ts"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import { Categories } from "../enums/Categories.ts"; +import {componentFinishStates, maintenanceStates} from "../screens/Hosts/constants.ts"; + +export const useTezConfigUpdater = () => { + // @ts-ignore + const { services, clusterName, parsedSocketMessages } = useContext(AppContext); + + // Early return if TEZ service is not installed + const isTezInstalled = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "TEZ"); + + if (!isTezInstalled) { + return; + } + + const { polledHostComponentsData, masterSlaveClientsData, serviceStatesData } = + useContext(ServiceContext); + //@ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + + const fetchTezMasterSlaveClientsData = async () => { + // 🚀 OPTIMIZATION: Try centralized cache first, fallback to masterSlaveClientsData + + let tezComponentsData = cachedServiceApi.getServiceComponentData("TEZ"); + + if (!tezComponentsData) { + tezComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "TEZ" + ); + } else { + } + + return tezComponentsData; + }; + + const findTezClientComponents = async () => { + const items = await fetchTezMasterSlaveClientsData(); + + if (!allServiceModels["tez"]) { + return; + } + + const currentConfig = cloneDeep(allServiceModels["tez"]); + let clientComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.CLIENT) { + const tezClientsInstalled = componentData.installedCount; + currentConfig[ServiceComponentMetricsEnums.TEZ.tezClientsInstalled] = + tezClientsInstalled; + clientComponents.push(componentData); + } + }); + + currentConfig[ServiceComponentMetricsEnums.ZOOKEEPER.clientComponents] = + clientComponents; + + if (!isEqual(allServiceModels["tez"], currentConfig)) { + allServiceModels["tez"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + //usePolling(pollServiceComponentInfoApi, 3000); + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep(allServiceModels["tez"]); + configToBeUpdated.isInPassiveForService = maintenanceState === maintenanceStates[0]; //signifies ON, assigns ON/PFF + + if (!isEqual(allServiceModels["tez"], configToBeUpdated)) { + allServiceModels["tez"].updateConfig(configToBeUpdated); + updateRegistry(allServiceModels); + } + } + + const updateAlertsAndServiceStateData = async () => { + // Use centralized service state API instead of individual call + updateServiceAlertsAndStateFromCentralizedApi("TEZ", "tez", allServiceModels, updateRegistry); + }; + + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === "TEZ" + ); + } + if ( + latestHostOperationMessage && + componentFinishStates.includes(latestHostOperationMessage.state) + || (latestHostOperationMessage.maintenance_state && maintenanceStates.includes(latestHostOperationMessage.maintenance_state)) + ) { + await updateServiceMaintenanceState(latestHostOperationMessage.maintenance_state); + await updateAlertsAndServiceStateData(); + } + }; + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + findTezClientComponents(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + parseAlertsWebSocketMessages(); + }, [parsedSocketMessages]); + + useEffect(() => { + // pollServiceComponentInfoApi(); + findTezClientComponents(); + }, []); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); +}; diff --git a/ambari-web/latest/src/hooks/useTrinoGatewayConfigUpdater.ts b/ambari-web/latest/src/hooks/useTrinoGatewayConfigUpdater.ts new file mode 100644 index 00000000000..fb50ea32a3c --- /dev/null +++ b/ambari-web/latest/src/hooks/useTrinoGatewayConfigUpdater.ts @@ -0,0 +1,214 @@ +import { useContext, useEffect } from "react"; +import { cloneDeep, find, get, isEmpty, isEqual } from "lodash"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { updateServiceAlertsAndStateFromCentralizedApi } from "../Utils/centralizedServiceStateUtils.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums.ts"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import { Categories } from "../enums/Categories.ts"; +import { + componentFinishStates, + maintenanceStates, +} from "../screens/Hosts/constants.ts"; + +export const useTrinoGatewayConfigUpdater = () => { + const { polledHostComponentsData, masterSlaveClientsData, serviceStatesData } = + useContext(ServiceContext); + const { services } = useContext(AppContext); + //@ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + const { parsedSocketMessages } = useContext(AppContext); + const serviceNameForServiceModel = "trino_gateway"; + const serviceNameForPolledApi = "TRINO_GATEWAY"; + + const isTrinoGatewayInstalled = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "TRINO_GATEWAY"); + + if (!isTrinoGatewayInstalled) { + return; + } + + const fetchTrinoGatewayMasterSlaveClientsData = async () => { + // 🚀 OPTIMIZATION: Try centralized cache first, fallback to masterSlaveClientsData + + let trinoGatewayComponentsData = cachedServiceApi.getServiceComponentData("TRINO_GATEWAY"); + + if (!trinoGatewayComponentsData) { + trinoGatewayComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === serviceNameForPolledApi + ); + } else { + } + + return trinoGatewayComponentsData; + }; + + const updateComponentObjectForSelectMaster = (componentData: any) => { + let masterComponent = find( + //@ts-ignore + polledHostComponentsData?.items, + (item: any) => + get(item, "ServiceComponentInfo.service_name") === + serviceNameForPolledApi && + get(item, "ServiceComponentInfo.component_name") === + componentData.componentName + ); + + if (masterComponent) { + const hostComponents = get(masterComponent, "host_components"); + if (hostComponents && hostComponents.length > 0) { + hostComponents.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + componentData.componentName + ) { + componentData.hostComponents.forEach((host: any) => { + const polledHostComponentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const componentDataHostName = get(host, "HostRoles.host_name"); + if (componentDataHostName === polledHostComponentHostName) { + host.state = get(hostComponent, "HostRoles.state"); + host.passiveState = get( + hostComponent, + "HostRoles.maintenance_state" + ); + } + }); + } + }); + } + } + return componentData; + }; + const findTrinoGatewayMasterComponents = async () => { + const items = await fetchTrinoGatewayMasterSlaveClientsData(); + if (!allServiceModels[serviceNameForServiceModel]) { + return; + } + + const currentConfig = cloneDeep( + allServiceModels[serviceNameForServiceModel] + ); + let masterComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.MASTER) { + const masterComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + masterComponents.push(masterComponentDataWithState); + } + }); + + currentConfig[ServiceComponentMetricsEnums.KYUUBI.masterComponents] = + masterComponents; + + if (!isEqual(allServiceModels[serviceNameForServiceModel], currentConfig)) { + allServiceModels[serviceNameForServiceModel].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep( + allServiceModels[serviceNameForServiceModel] + ); + configToBeUpdated.isInPassiveForService = + maintenanceState === maintenanceStates[0]; + + if ( + !isEqual(allServiceModels[serviceNameForServiceModel], configToBeUpdated) + ) { + allServiceModels[serviceNameForServiceModel].updateConfig( + configToBeUpdated + ); + updateRegistry(allServiceModels); + } + }; + + const updateAlertsAndServiceStateData = async () => { + // Use centralized service state API instead of individual call + updateServiceAlertsAndStateFromCentralizedApi("TRINO_GATEWAY", "trino_gateway", allServiceModels, updateRegistry); + }; + + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === serviceNameForPolledApi + ); + } + if ( + (latestHostOperationMessage && + componentFinishStates.includes(latestHostOperationMessage.state)) || + (latestHostOperationMessage.maintenance_state && maintenanceStates.includes(latestHostOperationMessage.maintenance_state)) + ) { + await updateServiceMaintenanceState( + latestHostOperationMessage.maintenance_state + ); + await updateAlertsAndServiceStateData(); + } + }; + + const updateTrinoGatewayComponent = async () => { + //@ts-ignore + if (!polledHostComponentsData?.items) return; + + const currentConfig = cloneDeep(allServiceModels["trino_gateway"]); + if (isEmpty(currentConfig)) { + return; + } + + let trinoGateway = find( + //@ts-ignore + polledHostComponentsData.items, + (item: any) => + get(item, "ServiceComponentInfo.service_name") === "TRINO_GATEWAY" && + get(item, "ServiceComponentInfo.component_name") === "TRINO_GATEWAY" + ); + trinoGateway.state = get( + trinoGateway.host_components[0], + "HostRoles.state" + ); + currentConfig["trinoGateway"] = trinoGateway; + + // Only update if we have changes + if ( + !isEqual(allServiceModels["trino_gateway"], currentConfig) && + !isEmpty(currentConfig) + ) { + allServiceModels["trino_gateway"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + findTrinoGatewayMasterComponents(); + updateTrinoGatewayComponent(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); + + useEffect(() => { + if (!isEmpty(parsedSocketMessages)) { + parseAlertsWebSocketMessages(); + } + }, [parsedSocketMessages]); +}; diff --git a/ambari-web/latest/src/hooks/useYarnConfigUpdater.ts b/ambari-web/latest/src/hooks/useYarnConfigUpdater.ts new file mode 100644 index 00000000000..ce04fb6fff5 --- /dev/null +++ b/ambari-web/latest/src/hooks/useYarnConfigUpdater.ts @@ -0,0 +1,821 @@ +import { useContext, useEffect, useRef, useState } from "react"; +import { cloneDeep, find, get, isEmpty, isEqual } from "lodash"; +import { ServiceApi } from "../api/ServiceApi"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { centralizedServiceStateApi } from "../api/CentralizedServiceStateApi.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import {componentFinishStates, maintenanceStates} from "../screens/Hosts/constants"; +import { Categories } from "../enums/Categories"; +import bytesToSize from "../Utils/numberUtils.ts"; +import objectUtils from "../Utils/objectUtils.ts"; + +export const useYarnConfigUpdater = () => { + const { + polledHostComponentsData, + masterSlaveClientsData, + serviceStatesData, + } = useContext(ServiceContext); + + // @ts-ignore + const { services, clusterName, parsedSocketMessages } = useContext(AppContext); + + // Early return if YARN service is not installed + const isYarnInstalled = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "YARN"); + + if (!isYarnInstalled) { + return; + } + + // @ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + const [serverClockTime, setServerClockTime] = useState(); + const hasResourceManagerHAEnabledUseEffectRunOnce = useRef(false); + + const fetchServerCLockTime = async () => { + const fields = "?fields=RootServiceComponents/server_clock"; + const responseData = await ServiceApi.ambariService(fields); + const serverClock = get( + responseData, + "RootServiceComponents.server_clock", + null + ); + setServerClockTime(serverClock); + }; + + const fetchYARNMasterSlaveClientsData = async () => { + let yarnComponentsData = cachedServiceApi.getServiceComponentData("YARN"); + + if (!yarnComponentsData) { + yarnComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "YARN" + ); + } + + return yarnComponentsData; + }; + + const updateComponentObjectForSelectMaster = (componentData: any) => { + let masterComponent = find( + //@ts-ignore + polledHostComponentsData?.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "YARN" && + get(item, "ServiceComponentInfo.component_name") === + componentData.componentName + ); + + if (masterComponent) { + const hostComponents = get(masterComponent, "host_components"); + if (hostComponents && hostComponents.length > 0) { + hostComponents.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + componentData.componentName + ) { + componentData.hostComponents.forEach((host: any) => { + const polledHostComponentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const componentDataHostName = get(host, "HostRoles.host_name"); + if (componentDataHostName === polledHostComponentHostName) { + host.state = get(hostComponent, "HostRoles.state"); + host.passiveState = get( + hostComponent, + "HostRoles.maintenance_state" + ); + // const yarnServiceObject = + // allServiceModels["yarn"].getServiceObject(); + // host.healthStatusMappedValue = + // yarnServiceObject.findHealthStatusMapValueForSingleHost( + // host.state + // ); + if (componentData.componentName === "RESOURCEMANAGER") { + //check if ha is enabled + if (allServiceModels["yarn"].isRMHAEnabled) { + const haState = get(hostComponent, "HostRoles.ha_state"); + // Format the HA status to match Ember display (e.g., "ACTIVE" -> "ACTIVE") + host.haStatus = haState ? haState.toUpperCase() : ""; + } else { + // For non-HA mode, don't show any HA status + host.haStatus = ""; + } + } + } + }); + } + }); + } + } + return componentData; + }; + const findMasterSlaveClientComponents = async () => { + const items = await fetchYARNMasterSlaveClientsData(); + + if (!allServiceModels["yarn"]) { + return; + } + + const currentConfig = cloneDeep(allServiceModels["yarn"]); + let masterComponents: any[] = []; + let slaveComponents: any[] = []; + let clientComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.MASTER) { + const masterComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + masterComponents.push(masterComponentDataWithState); + } else if (componentData.category === Categories.SLAVE) { + slaveComponents.push(componentData); + } else if (componentData.category === Categories.CLIENT) { + const yarnClientsInstalled = componentData.installedCount; + currentConfig[ServiceComponentMetricsEnums.YARN.yarnClients] = + yarnClientsInstalled; + clientComponents.push(componentData); + clientComponents.push(componentData); + } + }); + + currentConfig[ServiceComponentMetricsEnums.YARN.masterComponents] = + masterComponents; + currentConfig[ServiceComponentMetricsEnums.YARN.slaveComponents] = + slaveComponents; + currentConfig[ServiceComponentMetricsEnums.YARN.clientComponents] = + clientComponents; + + if (!isEqual(allServiceModels["yarn"], currentConfig)) { + allServiceModels["yarn"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + // const updateHDFSHostComponentsData = async () => { + // let updatedConfig = cloneDeep(allServiceModels["hdfs"]); + // + // const serviceName = "HDFS"; // Replace with the desired service name + // const fields = `ServiceComponentInfo/service_name,ServiceComponentInfo/category,ServiceComponentInfo/installed_count,ServiceComponentInfo/started_count,ServiceComponentInfo/init_count,ServiceComponentInfo/install_failed_count,ServiceComponentInfo/unknown_count,ServiceComponentInfo/total_count,ServiceComponentInfo/display_name,host_components/HostRoles/host_name&minimal_response=true`; + // const response = + // await ServiceApi.getAllServiceComponentsListAndInitialMetrics( + // clusterName, + // `${fields}&ServiceComponentInfo/service_name=${serviceName}` + // ); + // + // let datanodes = [] as any; + // + // const components = [ + // { name: "DATANODE", metric: "dataNodes" }, + // { name: "ROUTER", metric: "routers" }, + // { name: "NFS_GATEWAY", metric: "nfsGateways" }, + // { name: "JOURNALNODE", metric: "journalNodes" }, + // ]; + // + // components.forEach((component) => { + // const hostComponents = findHostComponentItems( + // "HDFS", + // component.name, + // response + // ); + // const installedCount = + // hostComponents.ServiceComponentInfo.installed_count; + // const startedCount = hostComponents.ServiceComponentInfo.started_count; + // const totalCount = hostComponents.ServiceComponentInfo.total_count; + // updatedConfig[ + // ServiceComponentMetricsEnums.HDFS[ + // `${component.metric}Started` as keyof typeof ServiceComponentMetricsEnums.HDFS + // ] as any + // ] = startedCount; + // updatedConfig[ + // ServiceComponentMetricsEnums.HDFS[ + // `${component.metric}Installed` as keyof typeof ServiceComponentMetricsEnums.HDFS + // ] as any + // ] = installedCount; + // updatedConfig[ + // ServiceComponentMetricsEnums.HDFS[ + // `${component.metric}Total` as keyof typeof ServiceComponentMetricsEnums.HDFS + // ] as any + // ] = totalCount; + // }); + // + // const datanode = find( + // response.data.items, + // (item) => + // get(item, "ServiceComponentInfo.service_name") === "HDFS" && + // get(item, "ServiceComponentInfo.component_name") === components[0].name + // ); + // + // if (datanode) { + // datanode.host_components.forEach((hostComponent: any) => { + // if (get(hostComponent, "HostRoles.component_name") === "DATANODE") { + // const hostComponentData = { + // componentName: components[0].name, + // hostName: get(hostComponent, "HostRoles.host_name"), + // state: get(hostComponent, "HostRoles.state"), + // }; + // datanodes.push(hostComponentData); + // } + // }); + // + // if (datanodes.length > 0) { + // updatedConfig[ServiceComponentMetricsEnums.HDFS.datanodes] = datanodes; + // } + // } + // if (!isEqual(allServiceModels["hdfs"], updatedConfig)) { + // allServiceModels["hdfs"].updateConfig(updatedConfig); + // updateRegistry(allServiceModels); + // } + // }; + + const parseWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.destination === "/events/hostcomponents" + ); + } + + if ( + latestHostOperationMessage && + latestHostOperationMessage.hostComponents + ) { + for (const hostComponent of latestHostOperationMessage.hostComponents) { + if (hostComponent.currentState in componentFinishStates) { + await findMasterSlaveClientComponents(); + } + } + } + }; + + const calclateResourceManagereUptime = (startTime: number) => { + const currentConfig = cloneDeep(allServiceModels["yarn"]); + const yarnServiceObj = currentConfig.getServiceObject(); + let clientClock = Date.now(); + + let serverClock = serverClockTime; + if (!serverClock) { + return null; // Return null if serverClockTime is not available + } + + serverClock = serverClock.toString(); + serverClock = serverClock.length < 13 ? serverClock + "000" : serverClock; + const clockDistance = serverClock - clientClock; + const uptime = startTime; + if (uptime && uptime > 0) { + const appDateTime = Date.now() + clockDistance; + let diff = appDateTime - uptime; + if (diff < 0) { + diff = 0; + } + const formatted = yarnServiceObj.timingFormat(diff); + return formatted.toString(); + } + }; + + const calcDiskUsagePartandPercent = () => { + const currentConfig = cloneDeep(allServiceModels["yarn"]); + const yarnServiceObj = currentConfig?.getServiceObject(); + const heapMemoryUsed = yarnServiceObj?.jvmMemoryHeapUsed; + const heapMemoryMax = yarnServiceObj?.jvmMemoryHeapMax; + + const updates = [ + { + key: "diskPartResourceManagerHeapMemory", + value: yarnServiceObj?.diskPart(heapMemoryUsed, heapMemoryMax), + }, + ]; + + updates.forEach(({ key, value }) => { + if (!value && value !== 0) { + return; + } + currentConfig[key] = value.toString(); + }); + + if (!isEqual(allServiceModels["yarn"], currentConfig)) { + allServiceModels["yarn"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + + const updateYARNMasterComponents = async () => { + //@ts-ignore + if (!polledHostComponentsData?.items) return; + + const updates = cloneDeep(allServiceModels["yarn"]); + let activeResourceManagers = [] as any; + let standbyResourceManagers = [] as any; + let nonActiveStandbyResourceManagers = [] as any; + let isResourceManagerHaEnabled = allServiceModels["yarn"].isRMHAEnabled; + let resourceManager = find( + //@ts-ignore + polledHostComponentsData.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "YARN" && + get(item, "ServiceComponentInfo.component_name") === "RESOURCEMANAGER" + ); + if (resourceManager && isResourceManagerHaEnabled) { + resourceManager.host_components.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === "RESOURCEMANAGER" + ) { + const hostComponentData = { + componentName: "RESOURCEMANAGER", + hostName: get(hostComponent, "HostRoles.host_name"), + haStatus: get(hostComponent, "HostRoles.ha_state"), + state: get(hostComponent, "HostRoles.state"), + }; + if ( + hostComponentData.state === componentFinishStates[1] && + hostComponentData.haStatus && + hostComponentData.haStatus.toUpperCase() === "ACTIVE" + ) { + //updates[ServiceComponentMetricsEnums.HDFS["nameNode"]] = hostComponentData; + activeResourceManagers.push(hostComponentData); + return; + } else if ( + hostComponentData.state === componentFinishStates[1] && + hostComponentData.haStatus && + hostComponentData.haStatus.toUpperCase() === "STANDBY" + ) { + standbyResourceManagers.push(hostComponentData); + return; + } + nonActiveStandbyResourceManagers.push(hostComponentData); + } + }); + } else if (resourceManager && !isResourceManagerHaEnabled) { + resourceManager.host_components.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === "RESOURCEMANAGER" + ) { + const hostComponentData = { + componentName: "RESOURCEMANAGER", + hostName: get(hostComponent, "HostRoles.host_name"), + state: get(hostComponent, "HostRoles.state"), + }; + updates[ServiceComponentMetricsEnums.YARN["resourceManager"]] = + hostComponentData; + nonActiveStandbyResourceManagers.push(hostComponentData); + } + }); + } + updates[ServiceComponentMetricsEnums.YARN["activeResourceManagers"]] = + activeResourceManagers; + updates[ServiceComponentMetricsEnums.YARN["standbyResourceManagers"]] = + standbyResourceManagers; + updates[ + ServiceComponentMetricsEnums.YARN["nonActiveStandbyResourceManagers"] + ] = nonActiveStandbyResourceManagers; + + // Only update if we have changes + if (!isEqual(allServiceModels["yarn"], updates)) { + allServiceModels["yarn"].updateConfig(updates); + updateRegistry(allServiceModels); + } + }; + + //@ts-ignore + const isRMAEnabled = async () => { + const yarnComponentsData = cachedServiceApi.getServiceComponentData("YARN"); + + if (!yarnComponentsData) { + return; + } + + const resourceManager = yarnComponentsData.find((item: any) => + item.ServiceComponentInfo?.component_name === "RESOURCEMANAGER" + ); + + if (resourceManager && resourceManager.host_components && resourceManager.host_components.length > 1) { + const updates = { + [ServiceComponentMetricsEnums.YARN["isRMHAEnabled"]]: true, + }; + + if ( + !isEqual(updates, { + isRMHAEnabled: + allServiceModels["yarn"][ + ServiceComponentMetricsEnums.YARN["isRMHAEnabled"] + ], + }) + ) { + allServiceModels["yarn"].updateConfig(updates); + updateRegistry(allServiceModels); + } + } + }; + + // const findHostComponentItems = ( + // serviceName: string, + // componentName: any, + // response: any + // ) => { + // const item = find(response.data.items, (item) => { + // return ( + // get(item, "ServiceComponentInfo.service_name") === serviceName && + // get(item, "ServiceComponentInfo.component_name") === componentName + // ); + // }); + // return item; + // }; + + const updateYARNData = () => { + const findMetrics = (data: any, metricParams: any) => { + let yarnMetrics = new Map(); + const item = find( + data.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "YARN" && + get(item, "ServiceComponentInfo.component_name") === "RESOURCEMANAGER" + ); + + if (item) { + const isRMHAEnabled = allServiceModels["yarn"]?.isRMHAEnabled; + let hostComponent = {}; + if (isRMHAEnabled) { + hostComponent = find(item.host_components, (hostComponent) => { + return ( + get(hostComponent, "HostRoles.component_name") === + "RESOURCEMANAGER" && + get(hostComponent, "HostRoles.state") === "STARTED" && + get(hostComponent, "HostRoles.ha_state") === "ACTIVE" + ); + }); + } else { + hostComponent = find(item.host_components, (hostComponent) => { + return ( + get(hostComponent, "HostRoles.component_name") === + "RESOURCEMANAGER" && + get(hostComponent, "HostRoles.state") === "STARTED" + ); + }); + } + if (hostComponent) { + metricParams.forEach((metricParam: any) => { + //check for metrics.Queue.root + let metricParamValue = get( + hostComponent, + `metrics.yarn.Queue.root.${metricParam}`, + null + ); + + // console.log( + // "metricParam === ", + // metricParam, + // "metricParamValue===", + // metricParamValue + // ); + + //metrics.Queue.root + if (metricParamValue !== null && metricParamValue !== undefined) { + if ( + metricParam === "AvailableMB" || + metricParam == "ReservedMB" || + (metricParam == "UsedAMResourceMB" && + typeof metricParamValue === "number") + ) { + //value multiplied as the data we get from API is in MB's + //@ts-ignore + metricParamValue = bytesToSize(metricParamValue * 1024 * 1024); + } + yarnMetrics.set(metricParam, metricParamValue); + return; + } else { + //not found in yarn queue root so check in yarn cluster + metricParamValue = get( + hostComponent, + `metrics.yarn.ClusterMetrics.${metricParam}`, + null + ); + if (metricParamValue !== null && metricParamValue !== undefined) { + yarnMetrics.set(metricParam, metricParamValue); + return; + } else { + metricParamValue = get( + hostComponent, + `metrics.jvm.${metricParam}`, + null + ); + if ( + metricParamValue !== null && + metricParamValue !== undefined + ) { + yarnMetrics.set(metricParam, metricParamValue); + return; + } + //not found in FSNamesystem or jvm metrics so check in runtime metrics + metricParamValue = get( + hostComponent, + `metrics.runtime.${metricParam}`, + null + ); + if (metricParam === "StartTime") { + //pass the startTime to calculate namenode uptime + if (metricParamValue !== null) { + metricParamValue = + calclateResourceManagereUptime(metricParamValue); + } + yarnMetrics.set(metricParam, metricParamValue); + return; + } + //else { + // //not found in FSNamesystem or jvm metrics or runtime metrics so check in namenode metrics + // metricParamValue = get( + // hostComponent, + // `metrics.dfs.namenode.${metricParam}`, + // null + // ); + // if (metricParam === "Safemode") { + // metricParamValue = + // allServiceModels["hdfs"]?.findSafeModeStatus( + // metricParamValue + // ); + // } else if (metricParam === "UpgradeFinalized") { + // const currentHostName = get( + // hostComponent, + // "HostRoles.host_name" + // ); + // const healthStatus = + // allServiceModels["hdfs"]?.workStatusValues[ + // currentHostName as any + // ]?.healthStatus; + // metricParamValue = allServiceModels[ + // "hdfs" + // ].findUpgradeStatus(metricParamValue, healthStatus); + // } else if ( + // metricParam === "LiveNodes" || + // metricParam === "DeadNodes" || + // metricParam === "DecomNodes" + // ) { + // const dataNodesStatusObj = get( + // hostComponent, + // `metrics.dfs.namenode.${metricParam}`, + // null + // ); + // metricParamValue = + // allServiceModels["hdfs"]?.countKeysMatchingPattern( + // dataNodesStatusObj + // ); + // } + // dfsMetrics.set(metricParam, metricParamValue); + // return; + // } + } + } + }); + } + } + return yarnMetrics; + }; + + const fetchComponentsData = async () => { + let updatedConfig = cloneDeep(allServiceModels["yarn"]); + + if (isEmpty(polledHostComponentsData)) { + return; + } + + // Simulating the fetching of YARB metric keys and finding metrics + const yarnMetricsQueueRoot = Object.keys( + ServiceComponentMetricsEnums.YARN.metrics.yarn.Queue.root + ); + const yarnMetricsYarnCluster = Object.keys( + ServiceComponentMetricsEnums.YARN.metrics.yarn.clusterMetrics + ); + const yarnMetricsJvm = Object.keys( + ServiceComponentMetricsEnums.YARN.metrics.jvm + ); + const yarnMetricsRuntime = Object.keys( + ServiceComponentMetricsEnums.YARN.metrics.runTime + ); + + const yarnPolledComponentData = find( + //@ts-ignore + polledHostComponentsData.items, + (item) => + get(item, "ServiceComponentInfo.service_name") === "YARN" && + get(item, "ServiceComponentInfo.component_name") === "RESOURCEMANAGER" + ); + + // component.queue = JSON.stringify({ + // 'root': self.parseObject(root) + // }); + + const getYarnQueueRootKeysPolledData = get( + yarnPolledComponentData, + "metrics.yarn.Queue.root", + {} + ); + + const rootStringifiedData = JSON.stringify({ + root: objectUtils.parseObject(getYarnQueueRootKeysPolledData), + }); + + const queueRecursiveCountData = () => { + const queueKeysParsed = JSON.parse(rootStringifiedData); + return objectUtils.recursiveKeysCount(queueKeysParsed); + }; + const queuesCount = queueRecursiveCountData(); + + updatedConfig.queueKeysPolledFormattedData = queuesCount + " Queues"; + + const metricsMapForMetricsQueueRoot = findMetrics( + polledHostComponentsData, + yarnMetricsQueueRoot + ); + const metricsMapForYarnCluster = findMetrics( + polledHostComponentsData, + yarnMetricsYarnCluster + ); + const metricsMapForJvm = findMetrics( + polledHostComponentsData, + yarnMetricsJvm + ); + const metricsMapForRunTime = findMetrics( + polledHostComponentsData, + yarnMetricsRuntime + ); + const currentMetrics = {}; + const newMetrics = {}; + + if (!isEmpty(yarnMetricsRuntime)) { + yarnMetricsRuntime.forEach((key) => { + const metricKey = + ServiceComponentMetricsEnums.YARN.metrics.runTime[ + key as keyof typeof ServiceComponentMetricsEnums.YARN.metrics.runTime + ]; + const metricValue = metricsMapForRunTime.get(key); + if (metricValue || metricValue >= 0) { + //@ts-ignore + currentMetrics[metricKey as string] = + allServiceModels["yarn"][metricKey as string]; + //@ts-ignore + newMetrics[metricKey as string] = metricValue; + updatedConfig[metricKey] = metricValue; + } + }); + } + + if (!isEmpty(yarnMetricsQueueRoot)) { + yarnMetricsQueueRoot.forEach((key) => { + const metricKey = + ServiceComponentMetricsEnums.YARN.metrics.yarn.Queue.root[ + key as keyof typeof ServiceComponentMetricsEnums.YARN.metrics.yarn.Queue.root + ]; + const metricValue = metricsMapForMetricsQueueRoot.get(key); + if (metricValue || metricValue >= 0) { + //@ts-ignore + currentMetrics[metricKey as string] = + allServiceModels["yarn"][metricKey as string]; + //@ts-ignore + newMetrics[metricKey as string] = metricValue; + updatedConfig[metricKey] = metricValue; + } + }); + } + if (!isEmpty(yarnMetricsYarnCluster)) { + yarnMetricsYarnCluster.forEach((key) => { + const metricKey = + ServiceComponentMetricsEnums.YARN.metrics.yarn.clusterMetrics[ + key as keyof typeof ServiceComponentMetricsEnums.YARN.metrics.yarn.clusterMetrics + ]; + const metricValue = metricsMapForYarnCluster.get(key); + if (metricValue || metricValue >= 0) { + //@ts-ignore + currentMetrics[metricKey as string] = + allServiceModels["yarn"][metricKey as string]; + //@ts-ignore + newMetrics[metricKey as string] = metricValue; + updatedConfig[metricKey] = metricValue; + } + }); + } + if (!isEmpty(yarnMetricsJvm)) { + yarnMetricsJvm.forEach((key) => { + const metricKey = + ServiceComponentMetricsEnums.YARN.metrics.jvm[ + key as keyof typeof ServiceComponentMetricsEnums.YARN.metrics.jvm + ]; + const metricValue = metricsMapForJvm.get(key); + if (metricValue || metricValue >= 0) { + //@ts-ignore + currentMetrics[metricKey as string] = + allServiceModels["yarn"][metricKey as string]; + //@ts-ignore + newMetrics[metricKey as string] = metricValue; + updatedConfig[metricKey] = metricValue; + } + }); + } + if (!isEqual(updatedConfig, allServiceModels["yarn"])) { + allServiceModels["yarn"].updateConfig(updatedConfig); + updateRegistry(allServiceModels); + } + }; + fetchComponentsData(); + }; + + // usePolling(updateHDFSData, 5000); + + //usePolling(pollServiceComponentInfoApi, 3000); + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep(allServiceModels["yarn"]); + configToBeUpdated.isInPassiveForService = maintenanceState === maintenanceStates[0]; //signifies ON, assigns ON/PFF + + if (!isEqual(allServiceModels["yarn"], configToBeUpdated)) { + allServiceModels["yarn"].updateConfig(configToBeUpdated); + updateRegistry(allServiceModels); + } + } + + const updateAlertsAndServiceStateData = async () => { + const currentConfig = cloneDeep(allServiceModels["yarn"]); + const serviceName = "YARN"; + + // Use centralized service state API instead of individual call + const serviceStateData = centralizedServiceStateApi.getServiceStateData(serviceName); + + if (!serviceStateData) return; + + const { alertsCount, hasCriticalAlerts, state } = serviceStateData; + + if (!alertsCount && alertsCount !== 0) return; + + currentConfig[ServiceComponentMetricsEnums.AMBARI_METRICS.hasCriticalAlerts] = hasCriticalAlerts; + currentConfig[ServiceComponentMetricsEnums.YARN.alertsCount] = alertsCount; + currentConfig[ServiceComponentMetricsEnums.YARN.state] = state; + + if (!isEqual(allServiceModels["yarn"], currentConfig)) { + allServiceModels["yarn"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === "YARN" + ); + } + if ( + latestHostOperationMessage && + (componentFinishStates.includes(latestHostOperationMessage.state) + || (latestHostOperationMessage.maintenance_state && maintenanceStates.includes(latestHostOperationMessage.maintenance_state))) + ) { + await updateServiceMaintenanceState(latestHostOperationMessage.maintenance_state); + await updateAlertsAndServiceStateData(); + } + }; + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + updateYARNData(); + updateYARNMasterComponents(); + findMasterSlaveClientComponents(); + fetchServerCLockTime(); + calcDiskUsagePartandPercent(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + if ( + hasResourceManagerHAEnabledUseEffectRunOnce.current || + !allServiceModels["yarn"] + ) { + return; + } + isRMAEnabled(); + hasResourceManagerHAEnabledUseEffectRunOnce.current = true; + }, [allServiceModels]); + + useEffect(() => { + fetchServerCLockTime(); + //isRMAEnabled(); + //findMasterSlaveClientComponents(); + }, []); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); + + useEffect(() => { + parseWebSocketMessages(); + parseAlertsWebSocketMessages(); + }, [parsedSocketMessages]); +}; diff --git a/ambari-web/latest/src/hooks/useZkConfigUpdater.ts b/ambari-web/latest/src/hooks/useZkConfigUpdater.ts new file mode 100644 index 00000000000..f83d6ea574b --- /dev/null +++ b/ambari-web/latest/src/hooks/useZkConfigUpdater.ts @@ -0,0 +1,177 @@ +import { useContext, useEffect } from "react"; +import { cloneDeep, find, get, isEmpty, isEqual } from "lodash"; +import { cachedServiceApi } from "../api/CachedServiceApi.ts"; +import { updateServiceAlertsAndStateFromCentralizedApi } from "../Utils/centralizedServiceStateUtils.ts"; +import { ServiceComponentMetricsEnums } from "../enums/ServiceComponentMetricsEnums.ts"; +import { AppContext } from "../store/context.tsx"; +import { ServiceContext } from "../store/ServiceContext.tsx"; +import { Categories } from "../enums/Categories.ts"; +import {componentFinishStates, maintenanceStates} from "../screens/Hosts/constants.ts"; + +export const useZkConfigUpdater = () => { + const { polledHostComponentsData, masterSlaveClientsData, serviceStatesData } = + useContext(ServiceContext); + + // @ts-ignore + const { services, clusterName, parsedSocketMessages } = useContext(AppContext); + + // Early return if ZOOKEEPER service is not installed + const isZookeeperInstalled = services && Array.isArray(services) && + services.some((service: any) => service.ServiceInfo.service_name === "ZOOKEEPER"); + + if (!isZookeeperInstalled) { + return; + } + + //@ts-ignore + const { allServiceModels, updateRegistry } = useContext(ServiceContext); + + const fetchZkMasterSlaveClientsData = async () => { + let zkComponentsData = cachedServiceApi.getServiceComponentData("ZOOKEEPER"); + + if (!zkComponentsData) { + zkComponentsData = Object.values(masterSlaveClientsData).filter( + (item) => get(item, "ServiceComponentInfo.service_name") === "ZOOKEEPER" + ); + } + + return zkComponentsData; + }; + + const updateComponentObjectForSelectMaster = (componentData: any) => { + let masterComponent = find( + //@ts-ignore + polledHostComponentsData?.items, + (item: any) => + get(item, "ServiceComponentInfo.service_name") === "ZOOKEEPER" && + get(item, "ServiceComponentInfo.component_name") === + componentData.componentName + ); + + if (masterComponent) { + const hostComponents = get(masterComponent, "host_components"); + if (hostComponents && hostComponents.length > 0) { + hostComponents.forEach((hostComponent: any) => { + if ( + get(hostComponent, "HostRoles.component_name") === + componentData.componentName + ) { + componentData.hostComponents.forEach((host: any) => { + const polledHostComponentHostName = get( + hostComponent, + "HostRoles.host_name" + ); + const componentDataHostName = get(host, "HostRoles.host_name"); + if (componentDataHostName === polledHostComponentHostName) { + host.state = get(hostComponent, "HostRoles.state"); + host.passiveState = get( + hostComponent, + "HostRoles.maintenance_state" + ); + } + }); + } + }); + } + } + return componentData; + }; + const findZkMasterClientComponents = async () => { + const items = await fetchZkMasterSlaveClientsData(); + + if (!allServiceModels["zk"]) { + return; + } + + const currentConfig = cloneDeep(allServiceModels["zk"]); + let masterComponents: any[] = []; + let clientComponents: any[] = []; + + items?.forEach((item: any) => { + let componentData = { + componentName: get(item, "ServiceComponentInfo.component_name"), + displayName: get(item, "ServiceComponentInfo.display_name"), + category: get(item, "ServiceComponentInfo.category"), + installedCount: get(item, "ServiceComponentInfo.installed_count"), + startedCount: get(item, "ServiceComponentInfo.started_count"), + totalCount: get(item, "ServiceComponentInfo.total_count"), + hostComponents: get(item, "host_components"), + }; + + if (componentData.category === Categories.MASTER) { + const masterComponentDataWithState = + updateComponentObjectForSelectMaster(componentData); + masterComponents.push(masterComponentDataWithState); + } else { + const zkClientsInstalled = componentData.installedCount; + currentConfig[ + ServiceComponentMetricsEnums.ZOOKEEPER.zkClientsInstalled + ] = zkClientsInstalled; + clientComponents.push(componentData); + } + }); + + currentConfig[ServiceComponentMetricsEnums.ZOOKEEPER.masterComponents] = + masterComponents; + currentConfig[ServiceComponentMetricsEnums.ZOOKEEPER.clientComponents] = + clientComponents; + + if (!isEqual(allServiceModels["zk"], currentConfig)) { + allServiceModels["zk"].updateConfig(currentConfig); + updateRegistry(allServiceModels); + } + }; + + const updateServiceMaintenanceState = (maintenanceState: string) => { + let configToBeUpdated = cloneDeep(allServiceModels["zk"]); + configToBeUpdated.isInPassiveForService = maintenanceState === maintenanceStates[0]; //signifies ON, assigns ON/PFF + + if (!isEqual(allServiceModels["zk"], configToBeUpdated)) { + allServiceModels["zk"].updateConfig(configToBeUpdated); + updateRegistry(allServiceModels); + } + } + + const updateAlertsAndServiceStateData = async () => { + // Use centralized service state API instead of individual call + updateServiceAlertsAndStateFromCentralizedApi("ZOOKEEPER", "zk", allServiceModels, updateRegistry); + }; + + const parseAlertsWebSocketMessages = async () => { + let latestHostOperationMessage = {} as any; + if (parsedSocketMessages.length > 0) { + latestHostOperationMessage = parsedSocketMessages.find( + (message) => message.service_name === "ZOOKEEPER" + ); + } + if ( + latestHostOperationMessage && + componentFinishStates.includes(latestHostOperationMessage.state) + || (latestHostOperationMessage.maintenance_state && maintenanceStates.includes(latestHostOperationMessage.maintenance_state)) + ) { + await updateServiceMaintenanceState(latestHostOperationMessage.maintenance_state); + await updateAlertsAndServiceStateData(); + } + }; + + useEffect(() => { + findZkMasterClientComponents(); + }, []); + + useEffect(() => { + //@ts-ignore + if (polledHostComponentsData?.items) { + findZkMasterClientComponents(); + } + }, [polledHostComponentsData]); + + useEffect(() => { + updateAlertsAndServiceStateData(); + }, [allServiceModels, serviceStatesData]); + + useEffect(() => { + if (!isEmpty(parsedSocketMessages)) { + parseAlertsWebSocketMessages(); + } + }, [parsedSocketMessages]); +}; diff --git a/ambari-web/latest/src/models/Tez.ts b/ambari-web/latest/src/models/Tez.ts new file mode 100644 index 00000000000..3648a2d238d --- /dev/null +++ b/ambari-web/latest/src/models/Tez.ts @@ -0,0 +1,49 @@ +import Service from "./service.ts"; + +type TezServiceData = { + tezClientsStarted: number; + tezClientsInstalled: number; + tezClientsTotal: number; + clientComponents: []; + alertsCount: number; + serviceState: string; + hasCriticalAlerts?: boolean; + isClientOnlyService: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; +class TezService extends Service { + tezClientsStarted: number; + tezClientsInstalled: number; + tezClientsTotal: number; + clientComponents: []; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isClientOnlyService: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + constructor(data: TezServiceData) { + super(data as any); + this.tezClientsStarted = data.tezClientsStarted || 0; + this.tezClientsInstalled = data.tezClientsInstalled || 0; + this.tezClientsTotal = data.tezClientsTotal || 0; + this.clientComponents = data.clientComponents || []; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isClientOnlyService = true; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): TezService { + return this; + } +} +export default TezService; diff --git a/ambari-web/latest/src/models/ambari_metrics.ts b/ambari-web/latest/src/models/ambari_metrics.ts new file mode 100644 index 00000000000..82059e7c442 --- /dev/null +++ b/ambari-web/latest/src/models/ambari_metrics.ts @@ -0,0 +1,63 @@ +import Service from "./service"; + +type HostComponent = { + componentName: string; + hostName: string; +}; + +type AmbariMetricsServiceData = { + metricsMonitorsStartedCount: number; + metricsMonitorsInstalledCount: number; + metricsMonitorsTotalCount: number; + masterComponents: []; + slaveComponents: []; + grafana: HostComponent; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; + +class AmbariMetricsService extends Service { + metricsMonitorsStartedCount: number; + metricsMonitorsInstalledCount: number; + metricsMonitorsTotalCount: number; + masterComponents: []; + slaveComponents: []; + grafana: HostComponent; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + + constructor(data: AmbariMetricsServiceData) { + //@ts-ignore + super(data); + this.metricsMonitorsStartedCount = data.metricsMonitorsStartedCount || 0; + this.metricsMonitorsInstalledCount = + data.metricsMonitorsInstalledCount || 0; + this.metricsMonitorsTotalCount = data.metricsMonitorsTotalCount || 0; + this.masterComponents = data.masterComponents || []; + this.slaveComponents = data.slaveComponents || []; + this.grafana = data.grafana || null; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = + data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): AmbariMetricsService { + return this; + } +} + +export default AmbariMetricsService; diff --git a/ambari-web/latest/src/models/hbase.ts b/ambari-web/latest/src/models/hbase.ts new file mode 100644 index 00000000000..50ffb7fee6a --- /dev/null +++ b/ambari-web/latest/src/models/hbase.ts @@ -0,0 +1,169 @@ +import Service from "./service"; +import bytesToSize from "../Utils/numberUtils.ts"; + +type HostComponent = { + componentName: string; + hostName: string; + haNameSpace?: string; + clusterIdValue?: string; + haStatus?: string; +}; + +type HBaseServiceData = { + // master: HostComponent | null; + activeHbaseMasters: HostComponent[]; + standbyHbaseMasters: HostComponent[]; + nonActiveStandbyHbaseMasters: HostComponent[]; + regionServersStarted: number; + regionServersInstalled: number; + regionServersTotal: number; + phoenixServersStarted: number; + phoenixServersInstalled: number; + phoenixServersTotal: number; + masterStartTime: string; + masterActiveTime: string; + averageLoad: string; + regionsInTransition: number; + heapMemoryUsed: number; + heapMemoryMax: number; + masterComponents: []; + slaveComponents: []; + clientComponents: []; + diskPartHbaseMasterHeap: string; + percentHbaseMasterHeap: string; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; + +class HBaseService extends Service { + // master: HostComponent | null; + activeHbaseMasters: HostComponent[]; + standbyHbaseMasters: HostComponent[]; + nonActiveStandbyHbaseMasters: HostComponent[]; + regionServersStarted: number; + regionServersInstalled: number; + regionServersTotal: number; + phoenixServersStarted: number; + phoenixServersInstalled: number; + phoenixServersTotal: number; + masterStartTime: string; + masterActiveTime: string; + averageLoad: string; + regionsInTransition: number; + heapMemoryUsed: number; + heapMemoryMax: number; + masterComponents: []; + slaveComponents: []; + clientComponents: []; + diskPartHbaseMasterHeap: string; + percentHbaseMasterHeap: string; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + + + constructor(data: HBaseServiceData) { + //@ts-ignore + super(data); + // this.master = data.master || null; + this.regionServersStarted = data.regionServersStarted || 0; + this.regionServersInstalled = data.regionServersInstalled || 0; + this.regionServersTotal = data.regionServersTotal || 0; + this.phoenixServersStarted = data.phoenixServersStarted || 0; + this.phoenixServersInstalled = data.phoenixServersInstalled || 0; + this.phoenixServersTotal = data.phoenixServersTotal || 0; + this.masterStartTime = data.masterStartTime || "Not Running"; + this.masterActiveTime = data.masterActiveTime || "Not Running"; + this.averageLoad = data.averageLoad || "n/a"; + this.regionsInTransition = data.regionsInTransition || 0; + this.heapMemoryUsed = data.heapMemoryUsed || 0; + this.heapMemoryMax = data.heapMemoryMax || 0; + this.masterComponents = data.masterComponents || []; + this.slaveComponents = data.slaveComponents || []; + this.clientComponents = data.clientComponents || []; + this.activeHbaseMasters = data.activeHbaseMasters || []; + this.standbyHbaseMasters = data.standbyHbaseMasters || []; + this.nonActiveStandbyHbaseMasters = data.nonActiveStandbyHbaseMasters || []; + this.diskPartHbaseMasterHeap = data.diskPartHbaseMasterHeap || "N/A"; + this.percentHbaseMasterHeap = data.percentHbaseMasterHeap || "N/A"; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + //@ts-ignore + findCapacityPercentage(capacity, capacityTotal) { + let percent = + capacityTotal && capacity && capacityTotal > 0 + ? ((capacity * 100) / capacityTotal).toFixed(2) + : 0; + if (isNaN(percent) || percent < 0) { + percent = "N/A"; + } + return `${percent}%`; + } + + diskPart(capacity: number, capacityTotal: number) { + return `${bytesToSize(capacity, 1, "parseFloat")} / ${bytesToSize(capacityTotal, 1, "parseFloat")}`; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): HBaseService { + return this; + } + + timingFormat(time: any) { + if (!time) { + return null; + } + + time = parseInt(time); + const fullTime = time; + let duration = ""; + + if (time === 0) { + return "0s"; + } + + const oneSecMs = 1000; + const oneMinMs = 60000; + const oneHourMs = 3600000; + const oneDayMs = 86400000; + let days, hours, minutes, seconds; + + [days, time] = this.extractTimeUnit(time, oneDayMs, "d"); + [hours, time] = this.extractTimeUnit(time, oneHourMs, "h"); + [minutes, time] = this.extractTimeUnit(time, oneMinMs, "m"); + duration += days + hours + minutes; + if (fullTime < oneDayMs) { + [seconds, time] = this.extractTimeUnit(time, oneSecMs, "s"); + duration += seconds; + if (fullTime < oneSecMs) { + duration += "1s"; + } + } + return duration.trim(); + } + + extractTimeUnit(time: any, unitValue: any, unitSuffix: any) { + let result = ""; + if (time >= unitValue) { + result = Math.floor(time / unitValue) + `${unitSuffix} `; + time -= Math.floor(time / unitValue) * unitValue; + } + return [result, time]; + } +} + +export default HBaseService; diff --git a/ambari-web/latest/src/models/hdfs.ts b/ambari-web/latest/src/models/hdfs.ts new file mode 100644 index 00000000000..a1b20ecc171 --- /dev/null +++ b/ambari-web/latest/src/models/hdfs.ts @@ -0,0 +1,367 @@ +import Service from "./service"; +import bytesToSize from "../Utils/numberUtils.ts"; + +type HostComponent = { + componentName: string; + hostName: string; + haNameSpace?: string; + clusterIdValue?: string; + haStatus?: string; +}; + +type HDFSServiceData = { + version: string; + nameNode: HostComponent | null; + snameNode: HostComponent | null; + activeNameNodes: HostComponent[]; + standbyNameNodes: HostComponent[]; + nonActiveStandbyNamenodes: HostComponent[]; + datanodes: HostComponent[]; + zookeeperFailoverControllers: HostComponent[]; + dataNodesStarted: number; + dataNodesInstalled: number; + routersInstalled: number; + dataNodesTotal: number; + routersTotal: number; + routersStarted: number; + nfsGatewaysStarted: number; + nfsGatewaysInstalled: number; + nfsGatewaysTotal: number; + namespaces:any; + isNamespaceLoaded:boolean; + journalNodes: HostComponent[]; + nameNodeStartTimeValues: { [key: string]: any }; + jvmMemoryHeapUsedValues: { [key: string]: any }; + jvmMemoryHeapMaxValues: { [key: string]: any }; + decommissionDataNodes: HostComponent[]; + liveDataNodes: HostComponent[]; + deadDataNodes: HostComponent[]; + capacityUsed: number; + capacityTotal: number; + liveRouters: HostComponent[]; + deadRouters: HostComponent[]; + capacityRemaining: number; + capacityNonDfsUsed: number; + dfsTotalBlocksValues: string; + dfsCorruptBlocksValues: string; + dfsMissingBlocksValues: string; + dfsUnderReplicatedBlocksValues: string; + dfsTotalFilesValues: string; + workStatusValues: {}; + healthStatusValues: { [key: string]: string }; + safeModeStatus: string; + upgradeFinalized: boolean; + nameNodeRpcValues: { [key: string]: any }; + metricsNotAvailable: boolean; + hostComponents: HostComponent[]; + isNameNodeHaEnabled?: boolean; + percentDFSUsed: string; + diskPartDFSUsed: string; + percentDFSRemaining: string; + diskPartDFSRemaining: string; + percentNonDFSUsed: string; + diskPartNonDFSUsed: string; + diskPartNamenodeHeap: string; + percentNamenodeHeap: string; + namenodeUptime: string; + masterComponents: []; + slaveComponents: []; + clientComponents: []; + liveNodesDataNodes: number; + deadNodesDataNodes: number; + decommissionedNodesDataNodes: number; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; + +class HDFSService extends Service { + version: string; + nameNode: HostComponent | null; + snameNode: HostComponent | null; + activeNameNodes: HostComponent[]; + standbyNameNodes: HostComponent[]; + datanodes: HostComponent[]; + zookeeperFailoverControllers: HostComponent[]; + isNameNodeHaEnabled: boolean; + dataNodesStarted: number; + dataNodesInstalled: number; + routersInstalled: number; + dataNodesTotal: number; + routersTotal: number; + routersStarted: number; + nfsGatewaysStarted: number; + nfsGatewaysInstalled: number; + nfsGatewaysTotal: number; + journalNodes: HostComponent[]; + nameNodeStartTimeValues: { [key: string]: any }; + jvmMemoryHeapUsedValues: { [key: string]: any }; + jvmMemoryHeapMaxValues: { [key: string]: any }; + decommissionDataNodes: HostComponent[]; + liveDataNodes: HostComponent[]; + deadDataNodes: HostComponent[]; + capacityUsed: number; + capacityTotal: number; + liveRouters: HostComponent[]; + deadRouters: HostComponent[]; + capacityRemaining: number; + capacityNonDfsUsed: number; + dfsTotalBlocksValues: string; + dfsCorruptBlocksValues: string; + dfsMissingBlocksValues: string; + dfsUnderReplicatedBlocksValues: string; + dfsTotalFilesValues: string; + workStatusValues: {}; + healthStatusValues: { [key: string]: string }; + // upgradeStatusValues: { [key: string]: any }; + safeModeStatus: string; + upgradeFinalized: boolean; + nameNodeRpcValues: { [key: string]: any }; + metricsNotAvailable: boolean; + hostComponents: HostComponent[]; + percentDFSUsed: string; + diskPartDFSUsed: string; + percentDFSRemaining: string; + diskPartDFSRemaining: string; + percentNonDFSUsed: string; + diskPartNonDFSUsed: string; + diskPartNamenodeHeap: string; + percentNamenodeHeap: string; + namenodeUptime: string; + masterComponents: []; + slaveComponents: []; + clientComponents: []; + liveNodesDataNodes: number; + deadNodesDataNodes: number; + decommissionedNodesDataNodes: number; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + isNamespaceLoaded: boolean; + namespaces:any; + federationNamespaces: any[]; + nameNodeMetricsByHost?: { [key: string]: any }; + nameNodeMetricsByNamespace?: { [key: string]: any }; + + constructor(data: HDFSServiceData) { + //@ts-ignore + super(data); + this.version = data.version || ""; + this.nameNode = data.nameNode || null; + this.snameNode = data.snameNode || null; + this.activeNameNodes = data.activeNameNodes || []; + this.standbyNameNodes = data.standbyNameNodes || []; + this.datanodes = data.datanodes || []; + this.zookeeperFailoverControllers = data.zookeeperFailoverControllers || []; + this.isNameNodeHaEnabled = data.isNameNodeHaEnabled || false; + this.dataNodesStarted = data.dataNodesStarted || 0; + this.dataNodesInstalled = data.dataNodesInstalled || 0; + this.routersInstalled = data.routersInstalled || 0; + this.dataNodesTotal = data.dataNodesTotal || 0; + this.routersTotal = data.routersTotal || 0; + this.routersStarted = data.routersStarted || 0; + this.nfsGatewaysStarted = data.nfsGatewaysStarted || 0; + this.nfsGatewaysInstalled = data.nfsGatewaysInstalled || 0; + this.nfsGatewaysTotal = data.nfsGatewaysTotal || 0; + this.journalNodes = data.journalNodes || []; + this.nameNodeStartTimeValues = data.nameNodeStartTimeValues || {}; + this.jvmMemoryHeapUsedValues = data.jvmMemoryHeapUsedValues || {}; + this.jvmMemoryHeapMaxValues = data.jvmMemoryHeapMaxValues || {}; + this.decommissionDataNodes = data.decommissionDataNodes || []; + this.liveDataNodes = data.liveDataNodes || []; + this.deadDataNodes = data.deadDataNodes || []; + this.capacityUsed = data.capacityUsed || 0; + this.capacityTotal = data.capacityTotal || 0; + this.liveRouters = data.liveRouters || []; + this.deadRouters = data.deadRouters || []; + this.capacityRemaining = data.capacityRemaining || 0; + this.capacityNonDfsUsed = data.capacityNonDfsUsed || 0; + this.dfsTotalBlocksValues = data.dfsTotalBlocksValues || "N/A"; + this.dfsCorruptBlocksValues = data.dfsCorruptBlocksValues || "N/A"; + this.dfsMissingBlocksValues = data.dfsMissingBlocksValues || "N/A"; + this.dfsUnderReplicatedBlocksValues = + data.dfsUnderReplicatedBlocksValues || "N/A"; + this.dfsTotalFilesValues = data.dfsTotalFilesValues || "N/A"; + this.workStatusValues = data.workStatusValues || {}; + this.healthStatusValues = this.calculateHealthStatusValues( + data.workStatusValues + ); + // this.upgradeStatusValues = data.upgradeStatusValues || {}; + this.safeModeStatus = data.safeModeStatus || "N/A"; + this.upgradeFinalized = data.upgradeFinalized || false; + this.nameNodeRpcValues = data.nameNodeRpcValues || {}; + this.metricsNotAvailable = data.metricsNotAvailable || false; + this.hostComponents = data.hostComponents || []; + this.percentDFSUsed = data.percentDFSUsed || "N/A"; + this.diskPartDFSUsed = data.diskPartDFSUsed || "N/A"; + this.percentDFSRemaining = data.percentDFSRemaining || "N/A"; + this.diskPartDFSRemaining = data.diskPartDFSRemaining || "N/A"; + this.percentNonDFSUsed = data.percentNonDFSUsed || "N/A"; + this.diskPartNonDFSUsed = data.diskPartNonDFSUsed || "N/A"; + this.diskPartNamenodeHeap = data.diskPartNamenodeHeap || "N/A"; + this.percentNamenodeHeap = data.percentNamenodeHeap || "N/A"; + this.namenodeUptime = data.namenodeUptime || "N/A"; + this.masterComponents = data.masterComponents || []; + this.slaveComponents = data.slaveComponents || []; + this.clientComponents = data.clientComponents || []; + this.liveNodesDataNodes = data.liveNodesDataNodes || 0; + this.deadNodesDataNodes = data.deadNodesDataNodes || 0; + this.decommissionedNodesDataNodes = data.decommissionedNodesDataNodes || 0; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + this.isNamespaceLoaded = data.isNamespaceLoaded || false; + this.namespaces = data.namespaces || []; + this.federationNamespaces = []; + } + + get isNnHaEnabled(): boolean { + return ( + !this.snameNode && + this.hostComponents.filter( + (component) => component.componentName === "NAMENODE" + ).length > 1 + ); + } + + private calculateHealthStatusValues(workStatusValues: { + [key: string]: any; + }): { [key: string]: string } { + const healthStatusMap: any = { + STARTED: "green", + STARTING: "green-blinking", + INSTALLED: "red", + STOPPING: "red-blinking", + UNKNOWN: "yellow", + }; + return Object.keys(workStatusValues || {}).reduce((acc, key) => { + return { + ...acc, + [key]: healthStatusMap[workStatusValues[key]] || "yellow", + }; + }, {}); + } + + //@ts-ignore + private findHealthStatusMapValueForSingleHost = (hostState: any) => { + const healthStatusMap: any = { + STARTED: "green", + STARTING: "green-blinking", + INSTALLED: "red", + STOPPING: "red-blinking", + UNKNOWN: "yellow", + }; + return healthStatusMap[hostState] || "yellow"; + }; + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): HDFSService { + return this; + } + findCapacityPercentage(capacity: number, capacityTotal: number) { + let percent = + capacityTotal && capacity && capacityTotal > 0 + ? ((capacity * 100) / capacityTotal).toFixed(2) + : 0; + if (isNaN(percent) || percent < 0) { + percent = "N/A"; + } + return `${percent}%`; + } + + diskPart(capacity: number, capacityTotal: number) { + return `${bytesToSize(capacity, 1, "parseFloat")} / ${bytesToSize(capacityTotal, 1, "parseFloat")}`; + } + + timingFormat(time: any) { + if (!time) { + return null; + } + + time = parseInt(time); + const fullTime = time; + let duration = ""; + + if (time === 0) { + return "0s"; + } + + const oneSecMs = 1000; + const oneMinMs = 60000; + const oneHourMs = 3600000; + const oneDayMs = 86400000; + let days, hours, minutes, seconds; + + [days, time] = this.extractTimeUnit(time, oneDayMs, "d"); + [hours, time] = this.extractTimeUnit(time, oneHourMs, "h"); + [minutes, time] = this.extractTimeUnit(time, oneMinMs, "m"); + duration += days + hours + minutes; + if (fullTime < oneDayMs) { + [seconds, time] = this.extractTimeUnit(time, oneSecMs, "s"); + duration += seconds; + if (fullTime < oneSecMs) { + duration += "1s"; + } + } + return duration.trim(); + } + + extractTimeUnit(time: any, unitValue: any, unitSuffix: any) { + let result = ""; + if (time >= unitValue) { + result = Math.floor(time / unitValue) + `${unitSuffix} `; + time -= Math.floor(time / unitValue) * unitValue; + } + return [result, time]; + } + + findSafeModeStatus(safeModeStatusValue: any) { + const safeMode = safeModeStatusValue; + if (safeMode == null) { + return "n/a"; + } else if (safeMode.length === 0) { + return "Not in safe mode"; + } else { + return "In safe mode"; + } + } + + findUpgradeStatus(upgradeStatusValue: any, healthStatus: any) { + const upgradeStatus = upgradeStatusValue; + if (upgradeStatus) { + return "No pending upgrade"; + } else if (upgradeStatus === false && healthStatus === "green") { + return "Upgrade not finalized"; + } else { + // upgrade status == null + return "n/a"; + } + } + + countKeysMatchingPattern(data: any): number { + //console.log("Data inside:", data); + let matchingKeys: string[] = []; + try { + data = JSON.parse(data); + const pattern = /\.visa\.com:\d+$/; + const keys = Object.keys(data); + //console.log("All keys:", keys); + matchingKeys = keys.filter((key) => pattern.test(key)); + //console.log("Matching keys:", matchingKeys); + } catch (e) { + console.error("Error parsing data:", e); + } + return matchingKeys.length; + } +} + +export default HDFSService; diff --git a/ambari-web/latest/src/models/hive.ts b/ambari-web/latest/src/models/hive.ts new file mode 100644 index 00000000000..3b7cf5c7d29 --- /dev/null +++ b/ambari-web/latest/src/models/hive.ts @@ -0,0 +1,52 @@ +import Service from "./service.ts"; + +// type HostComponent = { +// componentName: string; +// hostNames: string; +// }; + +type HiveServiceData = { + masterComponents: []; + clientComponents: []; + hiveClientsInstalled: number; + hiveServer2JDBCURL: string; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; +class HiveService extends Service { + masterComponents: []; + clientComponents: []; + hiveClientsInstalled: number; + hiveServer2JDBCURL: string; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + constructor(data: HiveServiceData) { + //@ts-ignore + super(data); + this.masterComponents = data.masterComponents || []; + this.clientComponents = data.clientComponents || []; + this.hiveClientsInstalled = data.hiveClientsInstalled || 0; + this.hiveServer2JDBCURL = data.hiveServer2JDBCURL || ""; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): HiveService { + return this; + } +} +export default HiveService; diff --git a/ambari-web/latest/src/models/kerberos.ts b/ambari-web/latest/src/models/kerberos.ts new file mode 100644 index 00000000000..dee240b5d67 --- /dev/null +++ b/ambari-web/latest/src/models/kerberos.ts @@ -0,0 +1,44 @@ +import Service from "./service.ts"; + +type KerberosServiceData = { + clientComponents: []; + kerberosClientsInstalled: number; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isClientOnlyService: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; +class KerberosService extends Service { + clientComponents: []; + kerberosClientsInstalled: number; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isClientOnlyService: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + constructor(data: KerberosServiceData) { + //@ts-ignore + super(data); + this.clientComponents = data.clientComponents || []; + this.kerberosClientsInstalled = data.kerberosClientsInstalled || 0; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isClientOnlyService = true; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): KerberosService { + return this; + } +} +export default KerberosService; diff --git a/ambari-web/latest/src/models/kyuubi.ts b/ambari-web/latest/src/models/kyuubi.ts new file mode 100644 index 00000000000..d699641e57f --- /dev/null +++ b/ambari-web/latest/src/models/kyuubi.ts @@ -0,0 +1,40 @@ +import Service from "./service.ts"; + +type KyuubiServiceData = { + masterComponents: []; + alertsCount: any; + serviceState: string; + hasCriticalAlerts?: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; +class KyuubiService extends Service { + masterComponents: []; + alertsCount: any; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + constructor(data: KyuubiServiceData) { + //@ts-ignore + super(data); + this.alertsCount = data.alertsCount || 0; + this.masterComponents = data.masterComponents || []; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = + data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): KyuubiService { + return this; + } +} +export default KyuubiService; diff --git a/ambari-web/latest/src/models/mapreduce2.ts b/ambari-web/latest/src/models/mapreduce2.ts new file mode 100644 index 00000000000..a67432a347d --- /dev/null +++ b/ambari-web/latest/src/models/mapreduce2.ts @@ -0,0 +1,55 @@ +import Service from "./service"; + +type HostComponent = { + componentName: string; + hostName: string; + //haNameSpace?: string; + //clusterIdValue?: string; +}; + +type MapReduce2ServiceData = { + jobHistoryServer: HostComponent | null; + masterComponents: []; + clientComponents: []; + mapReduce2Clients: number; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; + +class MapReduce2Service extends Service { + jobHistoryServer: HostComponent | null; + masterComponents: []; + clientComponents: []; + mapReduce2Clients: number; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + constructor(data: MapReduce2ServiceData) { + super(data as any); + this.jobHistoryServer = data.jobHistoryServer || null; + this.masterComponents = data.masterComponents || []; + this.clientComponents = data.clientComponents || []; + this.mapReduce2Clients = data.mapReduce2Clients || 0; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): MapReduce2Service { + return this; + } +} + +export default MapReduce2Service; \ No newline at end of file diff --git a/ambari-web/latest/src/models/pinot.ts b/ambari-web/latest/src/models/pinot.ts new file mode 100644 index 00000000000..231f7f774c2 --- /dev/null +++ b/ambari-web/latest/src/models/pinot.ts @@ -0,0 +1,88 @@ +import Service from "./service"; + +type HostComponent = { + componentName: string; + hostName: string; + state?: string; + passiveState?: string; +}; + +type PinotServiceData = { + pinotController: HostComponent[] | []; + pinotBroker: HostComponent[] | []; + pinotMinion: HostComponent[] | []; + pinotServer: HostComponent[] | []; + masterComponents: []; + slaveComponents: []; + pinotBrokerStartedCount: number; + pinotBrokerInstalledCount: number; + pinotBrokerTotalCount: number; + pinotMinionStartedCount: number; + pinotMinionInstalledCount: number; + pinotMinionTotalCount: number; + pinotServerStartedCount: number; + pinotServerInstalledCount: number; + pinotServerTotalCount: number; + alertsCount: number; + serviceState: string; + hasCriticalAlerts?: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; + +class PinotService extends Service { + pinotController: HostComponent[] | []; + pinotBroker: HostComponent[] | []; + pinotMinion: HostComponent[] | []; + pinotServer: HostComponent[] | []; + masterComponents: []; + slaveComponents: []; + pinotBrokerStartedCount: number; + pinotBrokerInstalledCount: number; + pinotBrokerTotalCount: number; + pinotMinionStartedCount: number; + pinotMinionInstalledCount: number; + pinotMinionTotalCount: number; + pinotServerStartedCount: number; + pinotServerInstalledCount: number; + pinotServerTotalCount: number; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + constructor(data: PinotServiceData) { + super(data as any); + this.pinotController = data.pinotController || []; + this.pinotBroker = data.pinotBroker || []; + this.pinotMinion = data.pinotMinion || []; + this.pinotServer = data.pinotServer || []; + this.masterComponents = data.masterComponents || []; + this.slaveComponents = data.slaveComponents || []; + this.pinotBrokerStartedCount = data.pinotBrokerStartedCount || 0; + this.pinotBrokerInstalledCount = data.pinotBrokerInstalledCount || 0; + this.pinotBrokerTotalCount = data.pinotBrokerTotalCount || 0; + this.pinotMinionStartedCount = data.pinotMinionStartedCount || 0; + this.pinotMinionInstalledCount = data.pinotMinionInstalledCount || 0; + this.pinotMinionTotalCount = data.pinotMinionTotalCount || 0; + this.pinotServerStartedCount = data.pinotServerStartedCount || 0; + this.pinotServerInstalledCount = data.pinotServerInstalledCount || 0; + this.pinotServerTotalCount = data.pinotServerTotalCount || 0; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): PinotService { + return this; + } +} + +export default PinotService; diff --git a/ambari-web/latest/src/models/ranger.ts b/ambari-web/latest/src/models/ranger.ts new file mode 100644 index 00000000000..99f6562b1ce --- /dev/null +++ b/ambari-web/latest/src/models/ranger.ts @@ -0,0 +1,72 @@ +import Service from "./service"; + +type HostComponent = { + componentName: string; + hostName: string; +}; + +type RangerServiceData = { + rangerTagSyncsStarted: number; + rangerTagSyncsInstalled: number; + rangerTagSyncsTotal: number; + masterComponents: []; + slaveComponents: []; + rangerAdmins: HostComponent[]; + rangerHDFSPluginProperties: string; + rangerHbasePluginProperties: string; + rangerHivePluginProperties: string; + rangerYarnPluginProperties: string; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; + +class RangerService extends Service { + rangerTagSyncsStarted: number; + rangerTagSyncsInstalled: number; + rangerTagSyncsTotal: number; + masterComponents: []; + slaveComponents: []; + rangerAdmins: HostComponent[]; + rangerHDFSPluginProperties: string; + rangerHbasePluginProperties: string; + rangerHivePluginProperties: string; + rangerYarnPluginProperties: string; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + constructor(data: RangerServiceData) { + //@ts-ignore + super(data); + this.rangerTagSyncsStarted = data.rangerTagSyncsStarted || 0; + this.rangerTagSyncsInstalled = data.rangerTagSyncsInstalled || 0; + this.rangerTagSyncsTotal = data.rangerTagSyncsTotal || 0; + this.masterComponents = data.masterComponents || []; + this.slaveComponents = data.slaveComponents || []; + this.rangerAdmins = data.rangerAdmins || []; + this.rangerHDFSPluginProperties = data.rangerHDFSPluginProperties || ""; + this.rangerHbasePluginProperties = data.rangerHbasePluginProperties || ""; + this.rangerHivePluginProperties = data.rangerHivePluginProperties || ""; + this.rangerYarnPluginProperties = data.rangerYarnPluginProperties || ""; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): RangerService { + return this; + } +} + +export default RangerService; diff --git a/ambari-web/latest/src/models/ranger_kms.ts b/ambari-web/latest/src/models/ranger_kms.ts new file mode 100644 index 00000000000..424af6f585d --- /dev/null +++ b/ambari-web/latest/src/models/ranger_kms.ts @@ -0,0 +1,38 @@ +import Service from "./service.ts"; + +type RangerKMSServiceData = { + masterComponents: []; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; +class RangerKMSService extends Service { + masterComponents: []; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + constructor(data: RangerKMSServiceData) { + //@ts-ignore + super(data); + this.masterComponents = data.masterComponents || []; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): RangerKMSService { + return this; + } +} +export default RangerKMSService; diff --git a/ambari-web/latest/src/models/spark3.ts b/ambari-web/latest/src/models/spark3.ts new file mode 100644 index 00000000000..593d7de38b9 --- /dev/null +++ b/ambari-web/latest/src/models/spark3.ts @@ -0,0 +1,81 @@ +import Service from "./service"; + +type HostComponent = { + componentName: string; + hostName: string; + //haNameSpace?: string; + //clusterIdValue?: string; +}; + +type Spark3ServiceData = { + spark3JobHistoryServers: HostComponent[] | []; + masterComponents: []; + slaveComponents: []; + clientComponents: []; + spark3Clients: number; + livyForSpark3ServerInstalledCount: number; + livyForSpark3ServerStartedCount: number; + livyForSpark3ServerTotalCount: number; + spark3ThriftServerInstalledCount: number; + spark3ThriftServerStartedCount: number; + spark3ThriftServerTotalCount: number; + alertsCount: number; + serviceState: string; + hasCriticalAlerts?: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; + +class Spark3Service extends Service { + spark3JobHistoryServers: HostComponent[] | []; + masterComponents: []; + slaveComponents: []; + clientComponents: []; + spark3Clients: number; + livyForSpark3ServerInstalledCount: number; + livyForSpark3ServerStartedCount: number; + livyForSpark3ServerTotalCount: number; + spark3ThriftServerInstalledCount: number; + spark3ThriftServerStartedCount: number; + spark3ThriftServerTotalCount: number; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + constructor(data: Spark3ServiceData) { + super(data as any); + this.spark3JobHistoryServers = data.spark3JobHistoryServers || []; + this.masterComponents = data.masterComponents || []; + this.slaveComponents = data.slaveComponents || []; + this.clientComponents = data.clientComponents || []; + this.spark3Clients = data.spark3Clients || 0; + this.livyForSpark3ServerInstalledCount = + data.livyForSpark3ServerInstalledCount || 0; + this.livyForSpark3ServerStartedCount = + data.livyForSpark3ServerStartedCount || 0; + this.livyForSpark3ServerTotalCount = + data.livyForSpark3ServerTotalCount || 0; + this.spark3ThriftServerInstalledCount = + data.spark3ThriftServerInstalledCount || 0; + this.spark3ThriftServerStartedCount = + data.spark3ThriftServerStartedCount || 0; + this.spark3ThriftServerTotalCount = data.spark3ThriftServerTotalCount || 0; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): Spark3Service { + return this; + } +} + +export default Spark3Service; diff --git a/ambari-web/latest/src/models/sqoop.ts b/ambari-web/latest/src/models/sqoop.ts new file mode 100644 index 00000000000..410f4f16b80 --- /dev/null +++ b/ambari-web/latest/src/models/sqoop.ts @@ -0,0 +1,51 @@ +import Service from "./service.ts"; + +type SqoopServiceData = { + sqoopClientsStarted: number; + sqoopClientsInstalled: number; + sqoopClientsTotal: number; + clientComponents: []; + alertsCount: number; + serviceState: string; + hasCriticalAlerts?: boolean; + isClientOnlyService: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; + +class SqoopService extends Service { + sqoopClientsStarted: number; + sqoopClientsInstalled: number; + sqoopClientsTotal: number; + clientComponents: []; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isClientOnlyService: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + constructor(data: SqoopServiceData) { + super(data as any); + this.sqoopClientsStarted = data.sqoopClientsStarted || 0; + this.sqoopClientsInstalled = data.sqoopClientsInstalled || 0; + this.sqoopClientsTotal = data.sqoopClientsTotal || 0; + this.clientComponents = data.clientComponents || []; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isClientOnlyService = true; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): SqoopService { + return this; + } +} + +export default SqoopService; diff --git a/ambari-web/latest/src/models/ssm.ts b/ambari-web/latest/src/models/ssm.ts new file mode 100644 index 00000000000..337a46cc164 --- /dev/null +++ b/ambari-web/latest/src/models/ssm.ts @@ -0,0 +1,60 @@ +import Service from "./service"; + +type HostComponent = { + componentName: string; + hostName: string; +}; + +type SSMServiceData = { + smartAgentsStartedCount: number; + smartAgentsInstalledCount: number; + smartAgentsTotalCount: number; + masterComponents: []; + slaveComponents: []; + smartServers: HostComponent[]; + alertsCount: number; + serviceState: string; + hasCriticalAlerts?: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; + +class SSMService extends Service { + smartAgentsStartedCount: number; + smartAgentsInstalledCount: number; + smartAgentsTotalCount: number; + masterComponents: []; + slaveComponents: []; + smartServers: HostComponent[]; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + constructor(data: SSMServiceData) { + //@ts-ignore + super(data); + this.smartAgentsStartedCount = data.smartAgentsStartedCount || 0; + this.smartAgentsInstalledCount = data.smartAgentsInstalledCount || 0; + this.smartAgentsTotalCount = data.smartAgentsTotalCount || 0; + this.masterComponents = data.masterComponents || []; + this.slaveComponents = data.slaveComponents || []; + this.smartServers = data.smartServers || null; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): SSMService { + return this; + } +} + +export default SSMService; diff --git a/ambari-web/latest/src/models/trino.ts b/ambari-web/latest/src/models/trino.ts new file mode 100644 index 00000000000..024187e7947 --- /dev/null +++ b/ambari-web/latest/src/models/trino.ts @@ -0,0 +1,67 @@ +import Service from "./service"; + +type HostComponent = { + componentName: string; + hostName: string; + //haNameSpace?: string; + //clusterIdValue?: string; +}; + +type TrinoServiceData = { + trinoCoordinators: HostComponent[] | []; + masterComponents: []; + slaveComponents: []; + clientComponents: []; + trinoClients: number; + trinoWorkerInstalledCount: number; + trinoWorkerStartedCount: number; + trinoWorkerTotalCount: number; + alertsCount: number; + serviceState: string; + hasCriticalAlerts?: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; + +class TrinoService extends Service { + trinoCoordinators: HostComponent[] | []; + masterComponents: []; + slaveComponents: []; + clientComponents: []; + trinoClients: number; + trinoWorkerInstalledCount: number; + trinoWorkerStartedCount: number; + trinoWorkerTotalCount: number; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + constructor(data: TrinoServiceData) { + super(data as any); + this.trinoCoordinators = data.trinoCoordinators || []; + this.masterComponents = data.masterComponents || []; + this.slaveComponents = data.slaveComponents || []; + this.clientComponents = data.clientComponents || []; + this.trinoClients = data.trinoClients || 0; + this.trinoWorkerInstalledCount = data.trinoWorkerInstalledCount || 0; + this.trinoWorkerStartedCount = data.trinoWorkerStartedCount || 0; + this.trinoWorkerTotalCount = data.trinoWorkerTotalCount || 0; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): TrinoService { + return this; + } +} + +export default TrinoService; diff --git a/ambari-web/latest/src/models/trinogateway.ts b/ambari-web/latest/src/models/trinogateway.ts new file mode 100644 index 00000000000..5e4c2d2fb6f --- /dev/null +++ b/ambari-web/latest/src/models/trinogateway.ts @@ -0,0 +1,49 @@ +import Service from "./service.ts"; + +// type HostComponent = { +// componentName: string; +// hostName: string; +// }; + + +type TrinoGatewayServiceData = { + masterComponents: []; + trinoGateway: any; + alertsCount: any; + serviceState: string; + hasCriticalAlerts?: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; +class TrinoGatewayService extends Service { + masterComponents: []; + trinoGateway: any; + alertsCount: any; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + constructor(data: TrinoGatewayServiceData) { + //@ts-ignore + super(data); + this.alertsCount = data.alertsCount || 0; + this.masterComponents = data.masterComponents || []; + this.trinoGateway = data.trinoGateway || null; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = + data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): TrinoGatewayService { + return this; + } +} +export default TrinoGatewayService; diff --git a/ambari-web/latest/src/models/yarn.ts b/ambari-web/latest/src/models/yarn.ts new file mode 100644 index 00000000000..ebee9c1e14d --- /dev/null +++ b/ambari-web/latest/src/models/yarn.ts @@ -0,0 +1,291 @@ +import Service from "./service"; +import bytesToSize from "../Utils/numberUtils.ts"; +//@ts-ignore +import objectUtils from "../Utils/objectUtils.ts"; + +type HostComponent = { + componentName: string; + hostName: string; + //haNameSpace?: string; + //clusterIdValue?: string; +}; + +type YARNServiceData = { + resourceManager: HostComponent | null; + activeResourceManager: HostComponent | null; + activeResourceManagers: HostComponent[]; + standbyResourceManagers: HostComponent[]; + nonActiveStandbyResourceManagers: HostComponent[]; + appTimelineServer: HostComponent | null; + nodeManagersStarted: number; + nodeManagersInstalled: number; + nodeManagersTotal: number; + nodeManagersCountActive: number; + nodeManagersCountLost: number; + nodeManagersCountUnhealthy: number; + nodeManagersCountRebooted: number; + nodeManagersCountDecommissioned: number; + containersAllocated: number; + containersPending: number; + containersReserved: number; + appsSubmitted: number; + appsRunning: number; + appsPending: number; + appsCompleted: number; + appsKilled: number; + appsFailed: number; + resourceManagerStartTime: number; + jvmMemoryHeapUsed: number; + jvmMemoryHeapMax: number; + diskPartResourceManagerHeapMemory: string; + allocatedMemory: number; + usedMemory: number; + reservedMemory: number; + availableMemory: number; + queue: string; + allQueueNames: string[]; + childQueueNames: string[]; + hostComponents: HostComponent[]; + masterComponents: []; + slaveComponents: []; + clientComponents: []; + isRMHaEnabled: boolean; + resourceManagerUptime: string; + queueKeysPolledFormattedData: any; + alertsCount: number; + serviceState: string; + hasCriticalAlerts?: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; + +class YARNService extends Service { + resourceManager: HostComponent | null; + activeResourceManager: HostComponent | null; + activeResourceManagers: HostComponent[]; + standbyResourceManagers: HostComponent[]; + nonActiveStandbyResourceManagers: HostComponent[]; + appTimelineServer: HostComponent | null; + nodeManagersStarted: number; + nodeManagersInstalled: number; + nodeManagersTotal: number; + nodeManagersCountActive: number; + nodeManagersCountLost: number; + nodeManagersCountUnhealthy: number; + nodeManagersCountRebooted: number; + nodeManagersCountDecommissioned: number; + containersAllocated: number; + containersPending: number; + containersReserved: number; + appsSubmitted: number; + appsRunning: number; + appsPending: number; + appsCompleted: number; + appsKilled: number; + appsFailed: number; + resourceManagerStartTime: number; + jvmMemoryHeapUsed: number; + jvmMemoryHeapMax: number; + diskPartResourceManagerHeapMemory: string; + allocatedMemory: number; + usedMemory: number; + reservedMemory: number; + availableMemory: number; + queue: string; + allQueueNames: string[]; + childQueueNames: string[]; + hostComponents: HostComponent[]; + masterComponents: []; + slaveComponents: []; + clientComponents: []; + isRMHaEnabled: boolean; + resourceManagerUptime: string; + queueKeysPolledFormattedData: any; + alertsCount: number; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + constructor(data: YARNServiceData) { + //@ts-ignore + super(data); + this.resourceManager = data.resourceManager || null; + this.activeResourceManager = data.activeResourceManager || null; + this.activeResourceManagers = data.activeResourceManagers || []; + this.standbyResourceManagers = data.standbyResourceManagers || []; + this.nonActiveStandbyResourceManagers = + data.nonActiveStandbyResourceManagers || []; + this.appTimelineServer = data.appTimelineServer || null; + this.nodeManagersStarted = data.nodeManagersStarted || 0; + this.nodeManagersInstalled = data.nodeManagersInstalled || 0; + this.nodeManagersTotal = data.nodeManagersTotal || 0; + this.nodeManagersCountActive = data.nodeManagersCountActive || 0; + this.nodeManagersCountLost = data.nodeManagersCountLost || 0; + this.nodeManagersCountUnhealthy = data.nodeManagersCountUnhealthy || 0; + this.nodeManagersCountRebooted = data.nodeManagersCountRebooted || 0; + this.nodeManagersCountDecommissioned = + data.nodeManagersCountDecommissioned || 0; + this.containersAllocated = data.containersAllocated || 0; + this.containersPending = data.containersPending || 0; + this.containersReserved = data.containersReserved || 0; + this.appsSubmitted = data.appsSubmitted || 0; + this.appsRunning = data.appsRunning || 0; + this.appsPending = data.appsPending || 0; + this.appsCompleted = data.appsCompleted || 0; + this.appsKilled = data.appsKilled || 0; + this.appsFailed = data.appsFailed || 0; + this.resourceManagerStartTime = data.resourceManagerStartTime || 0; + this.jvmMemoryHeapUsed = data.jvmMemoryHeapUsed || 0; + this.jvmMemoryHeapMax = data.jvmMemoryHeapMax || 0; + this.diskPartResourceManagerHeapMemory = data.diskPartResourceManagerHeapMemory || ""; + this.allocatedMemory = data.allocatedMemory || 0; + this.usedMemory = data.usedMemory || 0; + this.reservedMemory = data.reservedMemory || 0; + this.availableMemory = data.availableMemory || 0; + this.queue = data.queue || ""; + this.allQueueNames = data.allQueueNames || []; + this.childQueueNames = data.childQueueNames || []; + this.hostComponents = data.hostComponents || []; + this.masterComponents = data.masterComponents || []; + this.slaveComponents = data.slaveComponents || []; + this.clientComponents = data.clientComponents || []; + this.resourceManagerUptime = data.resourceManagerUptime || ""; + this.isRMHaEnabled = data.isRMHaEnabled || false; + this.queueKeysPolledFormattedData = data.queueKeysPolledFormattedData || {}; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + get isResourceManagerHaEnabled(): boolean { + return ( + this.hostComponents.filter( + (component) => component.componentName === "RESOURCEMANAGER" + ).length > 1 + ); + } + + get ahsWebPort(): string { + //@ts-ignore + const yarnConf = App.db.getConfigs().findProperty("type", "yarn-site"); + if (yarnConf) { + return yarnConf.properties["yarn.timeline-service.webapp.address"].match( + /:(\d+)/ + )[1]; + } + return "8188"; + } + + get queueFormatted(): any { + const queue = JSON.parse(this.queue); + return objectUtils.recursiveTree(queue); + } + + get queuesCount(): number { + const queue = JSON.parse(this.queue); + return objectUtils.recursiveKeysCount(queue) ?? 0; + } + + + get maxMemory(): number { + return this.allocatedMemory + this.availableMemory; + } + + queueNames(): void { + const queueString = this.queue; + let allQueueNames: string[] = []; + let childQueueNames: string[] = []; + if (queueString != null) { + const queues = JSON.parse(queueString); + const addQueues = (queuesObj: any, path: string): string[] => { + let names: string[] = []; + for (const subQueue in queuesObj) { + if (queuesObj[subQueue] instanceof Object) { + const qFN = path === "" ? subQueue : `${path}/${subQueue}`; + names.push(qFN); + const subNames = addQueues(queuesObj[subQueue], qFN); + names = names.concat(subNames); + if (!subNames || subNames.length < 1) { + childQueueNames.push(qFN); + } + } + } + return names; + }; + allQueueNames = addQueues(queues, ""); + } + this.allQueueNames = allQueueNames; + this.childQueueNames = childQueueNames; + } + + getServiceObject(): YARNService { + return this; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + diskPart(capacity: number, capacityTotal: number) { + return `${bytesToSize(capacity, 1, "parseFloat")} / ${bytesToSize(capacityTotal, 1, "parseFloat")}`; + } + + timingFormat(time: any) { + if (!time) { + return null; + } + + time = parseInt(time); + const fullTime = time; + let duration = ""; + + if (time === 0) { + return "0s"; + } + + const oneSecMs = 1000; + const oneMinMs = 60000; + const oneHourMs = 3600000; + const oneDayMs = 86400000; + let days, hours, minutes, seconds; + + [days, time] = this.extractTimeUnit(time, oneDayMs, "d"); + [hours, time] = this.extractTimeUnit(time, oneHourMs, "h"); + [minutes, time] = this.extractTimeUnit(time, oneMinMs, "m"); + duration += days + hours + minutes; + if (fullTime < oneDayMs) { + [seconds, time] = this.extractTimeUnit(time, oneSecMs, "s"); + duration += seconds; + if (fullTime < oneSecMs) { + duration += "1s"; + } + } + return duration.trim(); + } + + extractTimeUnit(time: any, unitValue: any, unitSuffix: any) { + let result = ""; + if (time >= unitValue) { + result = Math.floor(time / unitValue) + `${unitSuffix} `; + time -= Math.floor(time / unitValue) * unitValue; + } + return [result, time]; + } + + parseObject(obj: Record): Record { + const res: Record = {}; + for (const p in obj) { + if (Object.prototype.hasOwnProperty.call(obj, p)) { + if (obj[p] instanceof Object) { + res[p] = this.parseObject(obj[p]); + } + } + } + return res; + } +} + +export default YARNService; diff --git a/ambari-web/latest/src/models/zookeeper.ts b/ambari-web/latest/src/models/zookeeper.ts new file mode 100644 index 00000000000..733a3e58c77 --- /dev/null +++ b/ambari-web/latest/src/models/zookeeper.ts @@ -0,0 +1,52 @@ +import Service from "./service.ts"; + +type HostComponent = { + componentName: string; + hostNames: string; +}; + +type ZkServiceData = { + zookeeperServers: HostComponent[]; + masterComponents: []; + clientComponents: []; + zkClientsInstalled: number; + alertsCount: any; + serviceState: string; + hasCriticalAlerts?: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; +}; +class ZkService extends Service { + masterComponents: []; + clientComponents: []; + zkClientsInstalled: number; + alertsCount: any; + serviceState: string; + hasCriticalAlerts: boolean; + isRestartRequiredForService: boolean; + isInPassiveForService: boolean; + + + constructor(data: ZkServiceData) { + //@ts-ignore + super(data); + this.alertsCount = data.alertsCount || 0; + this.masterComponents = data.masterComponents || []; + this.clientComponents = data.clientComponents || []; + this.zkClientsInstalled = data.zkClientsInstalled || 0; + this.alertsCount = data.alertsCount || 0; + this.serviceState = data.serviceState || ""; + this.hasCriticalAlerts = data.hasCriticalAlerts || false; + this.isRestartRequiredForService = data.isRestartRequiredForService || false; + this.isInPassiveForService = data.isInPassiveForService || false; + } + + updateConfig(updates: Partial) { + Object.assign(this, updates); + } + + getServiceObject(): ZkService { + return this; + } +} +export default ZkService;