This is an automated email from the ASF dual-hosted git repository.
arshad pushed a commit to branch frontend-refactor
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/frontend-refactor by this push:
new d808da2cc3 AMBARI-26376 : Ambari Web React: Check host component
(#4057)
d808da2cc3 is described below
commit d808da2cc30de048503347a3e4e5c0a8feeb6c84
Author: Himanshu Maurya <[email protected]>
AuthorDate: Tue Sep 9 11:50:05 2025 +0530
AMBARI-26376 : Ambari Web React: Check host component (#4057)
---
ambari-web/latest/src/hooks/useHostChecks.tsx | 1098 ++++++++++++++++++++
ambari-web/latest/src/screens/Hosts/HostChecks.tsx | 530 ++++++++++
ambari-web/latest/src/screens/Hosts/constants.ts | 445 ++++++++
ambari-web/latest/src/screens/Hosts/helpers.ts | 109 ++
4 files changed, 2182 insertions(+)
diff --git a/ambari-web/latest/src/hooks/useHostChecks.tsx
b/ambari-web/latest/src/hooks/useHostChecks.tsx
new file mode 100644
index 0000000000..e1a15a1422
--- /dev/null
+++ b/ambari-web/latest/src/hooks/useHostChecks.tsx
@@ -0,0 +1,1098 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useContext, useEffect, useRef, useState } from "react";
+import {
+ finishStates,
+ minDiskSpace,
+ minDiskSpaceUsrLib,
+} from "../screens/Hosts/constants";
+import { HostsApi } from "../api/hostsApi";
+import usePolling from "./usePolling";
+import { cloneDeep, get, isEmpty, map, set } from "lodash";
+import { AppContext } from "../store/context";
+import { sortPropertyLight } from "../screens/Hosts/helpers";
+
+enum BootStatus {
+ PENDING = "PENDING",
+ DONE = "DONE",
+ REGISTERING = "REGISTERING",
+ REGISTERED = "REGISTERED",
+ FAILED = "FAILED",
+ RUNNING = "RUNNING",
+}
+
+type BootHostType = {
+ name: string;
+};
+
+export const initialWarningData: any = {
+ allWarnings: [],
+ repoCategoryWarnings: [],
+ diskCategoryWarnings: [],
+ thpCategoryWarnings: [],
+ hostCheckWarnings: [],
+ warningsByHost: [],
+ jdkCategoryWarnings: [],
+};
+
+export const useHostChecks = (
+ isAddHostWizard = false,
+ isClusterInstallationWizard = false
+) => {
+ const { clusterName, ambariProperties } = useContext(AppContext);
+
+ const [requestID, setRequestID] = useState(-1);
+ const [warningData, setWarningData] = useState<any>(initialWarningData);
+ const [componentInfo, setComponentInfo] = useState({});
+ const [isHostCheckRunning, setIsHostCheckRunning] = useState(false);
+
+ const hostsPackagesData = useRef([]);
+ const hostCheckResult = useRef(null);
+ const skipBootstrap = useRef(false);
+ const hostPackagesData = useRef([]);
+ const bootHosts = useRef<BootHostType[]>([]);
+ const dataForHostCheck = useRef({});
+
+ const getHostCheckTasks = async () => {
+ const response = await HostsApi.getRequestStatus(
+ requestID,
+
"Requests/inputs,Requests/request_status,tasks/Tasks/host_name,tasks/Tasks/structured_out/host_resolution_check/hosts_with_failures,tasks/Tasks/structured_out/host_resolution_check/failed_count,tasks/Tasks/structured_out/installed_packages,tasks/Tasks/structured_out/last_agent_env_check,tasks/Tasks/structured_out/transparentHugePage,tasks/Tasks/stdout,tasks/Tasks/stderr,tasks/Tasks/error_log,tasks/Tasks/command_detail,tasks/Tasks/status&minimal_response=true"
+ );
+ getHostCheckTasksSuccess(response);
+ };
+
+ const { pausePolling, resumePolling } = usePolling(getHostCheckTasks, 1000);
+
+ useEffect(() => {
+ pausePolling();
+ if (!isClusterInstallationWizard) {
+ getClusterComponents();
+ }
+ }, []);
+
+ useEffect(() => {
+ if (requestID !== -1) {
+ resumePolling();
+ }
+ }, [requestID]);
+
+ const getClusterComponents = async () => {
+ const response = await HostsApi.getClusterComponents(
+ clusterName,
+ "ServiceComponentInfo/component_name,host_components/HostRoles/host_name"
+ );
+ setComponentInfo(response);
+ };
+
+ const getMasterComponentHosts = () => {
+ const components = get(componentInfo, "items", []);
+ return components
+ .map((component: any) => {
+ return get(component, "host_components", []).map(
+ (hostComponent: any) => {
+ return get(hostComponent, "HostRoles.host_name", "");
+ }
+ );
+ })
+ .flat();
+ };
+
+ const cleanup = () => {
+ setWarningData(initialWarningData);
+ };
+
+ const stopHostCheck = () => {
+ setIsHostCheckRunning(false);
+ pausePolling();
+ cleanup();
+ };
+
+ const requestToPerformHostCheck = async () => {
+ if (!isEmpty(dataForHostCheck.current)) {
+ const data = {
+ RequestInfo: get(dataForHostCheck.current, "RequestInfo", {}),
+ "Requests/resource_filters": [
+ get(dataForHostCheck.current, "resource_filters", {}),
+ ],
+ };
+ const response = await HostsApi.makeRequest(data);
+ setRequestID(get(response, "Requests.id", -1));
+ }
+ };
+
+ const getDataForCheckRequest = (
+ checkExecuteList: string,
+ addHostsParameter: boolean
+ ) => {
+ const newHosts = bootHosts.current
+ .filter((host) => get(host, "bootStatus") === BootStatus.REGISTERED)
+ .map((host) => get(host, "name"));
+ const hosts = isAddHostWizard
+ ? [...new Set([...getMasterComponentHosts(), ...newHosts])]
+ : newHosts;
+ const hostsString = hosts.join(",");
+ if (hostsString.length === 0) return null;
+ const jdkLocation = get(
+ ambariProperties,
+ "RootServiceComponents.properties.jdk_location",
+ ""
+ );
+ const RequestInfo = {
+ action: "check_host",
+ context: "Check host",
+ parameters: {
+ check_execute_list: checkExecuteList,
+ jdk_location: jdkLocation,
+ threshold: "20",
+ },
+ };
+ if (addHostsParameter) {
+ set(RequestInfo, "parameters.hosts", hostsString);
+ }
+ const resourceFilters = {
+ hosts: hostsString,
+ };
+ return {
+ RequestInfo,
+ resource_filters: resourceFilters,
+ };
+ };
+
+ const getGeneralHostCheck = () => {
+ const data = getDataForCheckRequest(
+
"last_agent_env_check,installed_packages,existing_repos,transparentHugePage",
+ false
+ );
+ if (data) {
+ requestToPerformHostCheck();
+ } else {
+ stopHostCheck();
+ }
+ };
+
+ const filterHostsData = (data: any) => {
+ const bootHostNames = new Set(
+ bootHosts.current.map((bootHost: any) => bootHost.name)
+ );
+ const filteredData = {
+ href: data.href,
+ tasks: data.tasks.filter((task: any) =>
+ bootHostNames.has(task.Tasks.host_name)
+ ),
+ };
+ return filteredData;
+ };
+
+ const filterBootHosts = (data: any) => {
+ const bootHostNames = new Set(
+ bootHosts.current.map((bootHost: any) => bootHost.name)
+ );
+ const filteredData = {
+ href: data.href,
+ items: data.items.filter((host: any) =>
+ bootHostNames.has(host.Hosts.host_name)
+ ),
+ };
+ return filteredData;
+ };
+
+ const parseHostCheckWarnings = (warningDataCopy: any, data: any) => {
+ data = filterHostsData(data);
+ let warnings: any[] = [];
+ let warning;
+ let hosts: any[] = [];
+ let warningCategories = {
+ fileFoldersWarnings: {},
+ packagesWarnings: {},
+ processesWarnings: {},
+ servicesWarnings: {},
+ usersWarnings: {},
+ alternativeWarnings: {},
+ };
+
+ sortPropertyLight(data.tasks, "Tasks.host_name").forEach((_task: any) => {
+ let hostName = get(_task, "Tasks.host_name", "");
+ let host: any = {
+ name: hostName,
+ warnings: [],
+ };
+
+ if (
+ !get(_task, "Tasks.structured_out", "") ||
+ !get(_task, "Tasks.structured_out.last_agent_env_check", "")
+ ) {
+ return;
+ }
+
+ let lastAgentEnvCheck = get(
+ _task,
+ "Tasks.structured_out.last_agent_env_check",
+ ""
+ );
+
+ let stackFoldersAndFiles = get(
+ lastAgentEnvCheck,
+ "stackFoldersAndFiles",
+ []
+ );
+ stackFoldersAndFiles.forEach((path: any) => {
+ warning = get(warningCategories, "fileFoldersWarnings." + path.name);
+ if (warning) {
+ warning.hosts.push(hostName);
+ warning.hostsLong.push(hostName);
+ } else {
+ warning = {
+ name: path.name,
+ hosts: [hostName],
+ hostsLong: [hostName],
+ category: "fileFolders",
+ };
+ set(warningCategories, "fileFoldersWarnings." + path.name, warning);
+ }
+ host.warnings.push(warning);
+ });
+
+ // parse all package warnings for host
+ let _hostPackagesData = hostsPackagesData.current.find(
+ (hpd: any) => get(hpd, "hostName") === hostName
+ );
+
+ if (!isEmpty(_hostPackagesData)) {
+ get(_hostPackagesData, "installedPackages", []).forEach(
+ (_package: any) => {
+ warning = get(
+ warningCategories,
+ "packagesWarnings." + _package.name
+ );
+ if (warning) {
+ warning.hosts.push(hostName);
+ warning.hostsLong.push(hostName);
+ warning.version = _package.version;
+ } else {
+ warning = {
+ name: _package.name,
+ version: _package.version,
+ hosts: [hostName],
+ hostsLong: [hostName],
+ category: "packages",
+ };
+ set(
+ warningCategories,
+ "packagesWarnings." + _package.name,
+ warning
+ );
+ }
+ host.warnings.push(warning);
+ }
+ );
+ }
+
+ // parse all process warnings for host
+ let hostHealth = lastAgentEnvCheck.hostHealth;
+
+ let liveServices = get(hostHealth, "liveServices", null);
+ let javaProcs = get(hostHealth, "activeJavaProcs", null);
+
+ if (javaProcs) {
+ javaProcs.forEach((process: any) => {
+ warning = get(warningCategories, "processesWarnings." + process.pid);
+ if (warning) {
+ warning.hosts.push(hostName);
+ warning.hostsLong.push(hostName);
+ } else {
+ let command = `pid=${process.pid}, user=${process.user}`;
+ warning = {
+ name:
+ command.length > 36
+ ? command.substring(0, 35) + "..."
+ : command,
+ hosts: [hostName],
+ hostsLong: [hostName],
+ category: "processes",
+ user: process.user,
+ pid: process.pid,
+ command: command,
+ };
+ set(warningCategories, "processesWarnings." + process.pid,
warning);
+ }
+ host.warnings.push(warning);
+ });
+ }
+
+ // parse all service warnings for host
+ if (liveServices) {
+ liveServices.forEach((service: any) => {
+ if (service.status === "Unhealthy") {
+ warning = get(
+ warningCategories,
+ "servicesWarnings." + service.name
+ );
+ if (warning) {
+ warning.hosts.push(hostName);
+ warning.hostsLong.push(hostName);
+ } else {
+ warning = {
+ name: service.name,
+ hosts: [hostName],
+ hostsLong: [hostName],
+ category: "services",
+ };
+ set(
+ warningCategories,
+ "servicesWarnings" + service.name,
+ warning
+ );
+ }
+ host.warnings.push(warning);
+ }
+ });
+ }
+
+ // parse all user warnings for host
+ let existingUsers = lastAgentEnvCheck.existingUsers;
+ if (existingUsers) {
+ existingUsers.forEach((user: any) => {
+ warning = get(warningCategories, "usersWarnings." + user.name);
+ if (warning) {
+ warning.hosts.push(hostName);
+ warning.hostsLong.push(hostName);
+ } else {
+ warning = {
+ name: user.name,
+ hosts: [hostName],
+ hostsLong: [hostName],
+ category: "users",
+ };
+ set(warningCategories, "usersWarnings." + user.name, warning);
+ }
+ host.warnings.push(warning);
+ });
+ }
+
+ // parse misc warnings for host
+ let umask = lastAgentEnvCheck.umask;
+ if (umask && umask > 23) {
+ warning = warnings.find(
+ (w) => w.category === "misc" && w.name === umask
+ );
+ if (warning) {
+ warning.hosts.push(hostName);
+ warning.hostsLong.push(hostName);
+ } else {
+ warning = {
+ name: umask,
+ hosts: [hostName],
+ hostsLong: [hostName],
+ category: "misc",
+ };
+ warnings.push(warning);
+ }
+ host.warnings.push(warning);
+ }
+
+ let firewallRunning = lastAgentEnvCheck.firewallRunning;
+ if (firewallRunning !== null && firewallRunning) {
+ let name = `${lastAgentEnvCheck.firewallName} Running`;
+ warning = warnings.find(
+ (w) => w.category === "firewall" && w.name === name
+ );
+ if (warning) {
+ warning.hosts.push(hostName);
+ warning.hostsLong.push(hostName);
+ } else {
+ warning = {
+ name: name,
+ hosts: [hostName],
+ hostsLong: [hostName],
+ category: "firewall",
+ };
+ warnings.push(warning);
+ }
+ host.warnings.push(warning);
+ }
+
+ if (lastAgentEnvCheck.alternatives) {
+ lastAgentEnvCheck.alternatives.forEach((alternative: any) => {
+ warning = get(
+ warningCategories,
+ "alternativeWarnings" + alternative.name
+ );
+ if (warning) {
+ warning.hosts.push(hostName);
+ warning.hostsLong.push(hostName);
+ } else {
+ warning = {
+ name: alternative.name,
+ target: alternative.target,
+ hosts: [hostName],
+ hostsLong: [hostName],
+ category: "alternatives",
+ };
+ set(
+ warningCategories,
+ "alternativeWarnings" + alternative.name,
+ warning
+ );
+ }
+ host.warnings.push(warning);
+ });
+ }
+
+ if (lastAgentEnvCheck.reverseLookup === false) {
+ let name = "Reverse Lookup validation failed on";
+ warning = warnings.find(
+ (w) => w.category === "reverseLookup" && w.name === name
+ );
+ if (warning) {
+ warning.hosts.push(hostName);
+ warning.hostsLong.push(hostName);
+ } else {
+ warning = {
+ name: name,
+ hosts: [hostName],
+ hostsLong: [hostName],
+ category: "reverseLookup",
+ };
+ warnings.push(warning);
+ }
+ host.warnings.push(warning);
+ }
+ hosts.push(host);
+ });
+
+ for (let categoryId in warningCategories) {
+ let category = get(warningCategories, categoryId);
+ for (let warningId in category) {
+ warnings.push(category[warningId]);
+ }
+ }
+
+ hosts.unshift({
+ name: "All Hosts",
+ warnings: warnings,
+ });
+
+ set(warningDataCopy, "allWarnings", warnings);
+ set(warningDataCopy, "warningsByHost", hosts);
+ };
+
+ function parseWarnings(warningDataCopy: any, data: any) {
+ data = filterBootHosts(data);
+ let warnings: any[] = [];
+ let warning;
+ let hosts: any[] = [];
+ let warningCategories = {
+ fileFoldersWarnings: {},
+ packagesWarnings: {},
+ processesWarnings: {},
+ servicesWarnings: {},
+ usersWarnings: {},
+ alternativeWarnings: {},
+ };
+
+ sortPropertyLight(data.items, "Hosts.host_name").forEach((_host: any) => {
+ let host: any = {
+ name: _host.Hosts.host_name,
+ warnings: [],
+ };
+ if (!_host.Hosts.last_agent_env) {
+ return;
+ }
+
+ let stackFoldersAndFiles = get(
+ _host,
+ "Hosts.last_agent_env.stackFoldersAndFiles",
+ []
+ );
+ stackFoldersAndFiles.forEach((path: any) => {
+ warning = get(warningCategories, "fileFoldersWarnings." + path.name);
+ if (warning) {
+ warning.hosts.push(_host.Hosts.host_name);
+ warning.hostsLong.push(_host.Hosts.host_name);
+ } else {
+ warning = {
+ name: path.name,
+ hosts: [_host.Hosts.host_name],
+ hostsLong: [_host.Hosts.host_name],
+ category: "fileFolders",
+ };
+ set(warningCategories, "fileFoldersWarnings." + path.name, warning);
+ }
+ host.warnings.push(warning);
+ });
+
+ let _hostPackagesData: any = hostsPackagesData.current.find(
+ (hpd: any) => get(hpd, "hostName") === get(_host, "Hosts.host_name")
+ );
+ if (_hostPackagesData) {
+ _hostPackagesData.installedPackages.forEach((_package: any) => {
+ warning = get(warningCategories, "packagesWarnings." +
_package.name);
+ if (warning) {
+ warning.hosts.push(_host.Hosts.host_name);
+ warning.hostsLong.push(_host.Hosts.host_name);
+ warning.version = _package.version;
+ } else {
+ warning = {
+ name: _package.name,
+ version: _package.version,
+ hosts: [_host.Hosts.host_name],
+ hostsLong: [_host.Hosts.host_name],
+ category: "packages",
+ };
+ set(
+ warningCategories,
+ "packagesWarnings." + _package.name,
+ warning
+ );
+ }
+ host.warnings.push(warning);
+ });
+ }
+
+ let javaProcs = _host.Hosts.last_agent_env.hostHealth
+ ? _host.Hosts.last_agent_env.hostHealth.activeJavaProcs
+ : _host.Hosts.last_agent_env.javaProcs;
+ if (javaProcs) {
+ javaProcs.forEach((process: any) => {
+ warning = get(warningCategories, "processesWarnings." + process.pid);
+ if (warning) {
+ warning.hosts.push(_host.Hosts.host_name);
+ warning.hostsLong.push(_host.Hosts.host_name);
+ } else {
+ let command = "pid=" + process.pid + ", user=" + process.user;
+ warning = {
+ name:
+ command.length > 36
+ ? command.substring(0, 35) + "..."
+ : command,
+ hosts: [_host.Hosts.host_name],
+ hostsLong: [_host.Hosts.host_name],
+ category: "processes",
+ user: process.user,
+ pid: process.pid,
+ command: command,
+ };
+ set(warningCategories, "processesWarnings." + process.pid,
warning);
+ }
+ host.warnings.push(warning);
+ });
+ }
+
+ if (
+ _host.Hosts.last_agent_env.hostHealth &&
+ _host.Hosts.last_agent_env.hostHealth.liveServices
+ ) {
+ _host.Hosts.last_agent_env.hostHealth.liveServices.forEach(
+ (service: any) => {
+ if (service.status === "Unhealthy") {
+ warning = get(
+ warningCategories,
+ "servicesWarnings" + service.name
+ );
+ if (warning) {
+ warning.hosts.push(_host.Hosts.host_name);
+ warning.hostsLong.push(_host.Hosts.host_name);
+ } else {
+ warning = {
+ name: service.name,
+ hosts: [_host.Hosts.host_name],
+ hostsLong: [_host.Hosts.host_name],
+ category: "services",
+ };
+ set(
+ warningCategories,
+ "servicesWarnings" + service.name,
+ warning
+ );
+ }
+ host.warnings.push(warning);
+ }
+ }
+ );
+ }
+
+ if (_host.Hosts.last_agent_env.existingUsers) {
+ _host.Hosts.last_agent_env.existingUsers.forEach((user: any) => {
+ warning = get(warningCategories, "usersWarnings." + user.name);
+ if (warning) {
+ warning.hosts.push(_host.Hosts.host_name);
+ warning.hostsLong.push(_host.Hosts.host_name);
+ } else {
+ warning = {
+ name: user.name,
+ hosts: [_host.Hosts.host_name],
+ hostsLong: [_host.Hosts.host_name],
+ category: "users",
+ };
+ set(warningCategories, "usersWarnings." + user.name, warning);
+ }
+ host.warnings.push(warning);
+ });
+ }
+
+ let umask = _host.Hosts.last_agent_env.umask;
+ if (umask && umask > 23) {
+ warning = warnings.find(
+ (w) => w.category === "misc" && w.name === umask
+ );
+ if (warning) {
+ warning.hosts.push(_host.Hosts.host_name);
+ warning.hostsLong.push(_host.Hosts.host_name);
+ } else {
+ warning = {
+ name: umask,
+ hosts: [_host.Hosts.host_name],
+ hostsLong: [_host.Hosts.host_name],
+ category: "misc",
+ };
+ warnings.push(warning);
+ }
+ host.warnings.push(warning);
+ }
+
+ let firewallRunning = _host.Hosts.last_agent_env.firewallRunning;
+ if (firewallRunning !== null && firewallRunning) {
+ let name = _host.Hosts.last_agent_env.firewallName + " Running";
+ warning = warnings.find(
+ (w) => w.category === "firewall" && w.name === name
+ );
+ if (warning) {
+ warning.hosts.push(_host.Hosts.host_name);
+ warning.hostsLong.push(_host.Hosts.host_name);
+ } else {
+ warning = {
+ name: name,
+ hosts: [_host.Hosts.host_name],
+ hostsLong: [_host.Hosts.host_name],
+ category: "firewall",
+ };
+ warnings.push(warning);
+ }
+ host.warnings.push(warning);
+ }
+
+ if (_host.Hosts.last_agent_env.alternatives) {
+ _host.Hosts.last_agent_env.alternatives.forEach((alternative: any) => {
+ warning = get(
+ warningCategories,
+ "alternativeWarnings" + alternative.name
+ );
+ if (warning) {
+ warning.hosts.push(_host.Hosts.host_name);
+ warning.hostsLong.push(_host.Hosts.host_name);
+ } else {
+ warning = {
+ name: alternative.name,
+ target: alternative.target,
+ hosts: [_host.Hosts.host_name],
+ hostsLong: [_host.Hosts.host_name],
+ category: "alternatives",
+ };
+ set(
+ warningCategories,
+ "alternativeWarnings" + alternative.name,
+ warning
+ );
+ }
+ host.warnings.push(warning);
+ });
+ }
+
+ if (_host.Hosts.last_agent_env.reverseLookup === false) {
+ let name = "Reverse Lookup validation failed on";
+ warning = warnings.find(
+ (w) => w.category === "reverseLookup" && w.name === name
+ );
+ if (warning) {
+ warning.hosts.push(_host.Hosts.host_name);
+ warning.hostsLong.push(_host.Hosts.host_name);
+ } else {
+ warning = {
+ name: name,
+ hosts: [_host.Hosts.host_name],
+ hostsLong: [_host.Hosts.host_name],
+ category: "reverseLookup",
+ };
+ warnings.push(warning);
+ }
+ host.warnings.push(warning);
+ }
+ hosts.push(host);
+ });
+
+ for (let categoryId in warningCategories) {
+ let category = get(warningCategories, categoryId);
+ for (let warningId in category) {
+ warnings.push(category[warningId]);
+ }
+ }
+
+ hosts.unshift({
+ name: "All Hosts",
+ warnings: warnings,
+ });
+
+ set(warningDataCopy, "allWarnings", warnings);
+ set(warningDataCopy, "warningsByHost", hosts);
+ }
+
+ const setHostDataWithSkipBootstrap = (host: any) => {
+ set(host, "cpu", 2);
+ set(host, "memory", parseInt("2000000").toFixed(2));
+ set(host, "disk_info", [
+ { mountpoint: "/", type: "ext4" },
+ { mountpoint: "/grid/0", type: "ext4" },
+ { mountpoint: "/grid/1", type: "ext4" },
+ { mountpoint: "/grid/2", type: "ext4" },
+ ]);
+ return host;
+ };
+
+ const setHostDataFromLoadedHostInfo = (host: any, hostInfo: any) => {
+ set(host, "cpu", get(hostInfo, "Hosts.cpu_count", 2));
+ set(
+ host,
+ "memory",
+ parseInt(get(hostInfo, "Hosts.total_mem", "2000000")).toFixed(2)
+ );
+ set(
+ host,
+ "disk_info",
+ get(hostInfo, "Hosts.disk_info", []).filter(
+ (h: any) => h.mountpoint != "/boot"
+ )
+ );
+ set(host, "os_type", get(hostInfo, "Hosts.os_type", ""));
+ set(host, "os_family", get(hostInfo, "Hosts.os_family", ""));
+ set(host, "os_arch", get(hostInfo, "Hosts.os_arch", ""));
+ set(host, "ip", get(hostInfo, "Hosts.ip", ""));
+ return host;
+ };
+
+ const checkHostOSType = (osFamily: any, hostName: any) => {
+ const stacks: any[] = []; //TODO: figure out and get the stack from
content or App
+ if (!isEmpty(stacks)) {
+ const selectedStack = stacks.find((stack: any) => stack.isSelected);
+ const selectedOS: any[] = [];
+ let isValid = false;
+ if (selectedStack && selectedStack.operatingSystems) {
+ selectedStack.operatingSystems
+ .filter((os: any) => os.isSelected)
+ .forEach((os: any) => {
+ selectedOS.push(os.osType);
+ if (os.osType === osFamily) {
+ isValid = true;
+ }
+ });
+ }
+ if (isValid) {
+ return "";
+ } else {
+ return `Host (${hostName}) is ${osFamily} OS type, but the
repositories chosen in "Select Stack" step was ${[
+ ...new Set(selectedOS),
+ ].join(", ")}. Selected repositories do not support this host OS
type.`;
+ }
+ } else {
+ return "";
+ }
+ };
+
+ const checkHostDiskSpace = (hostName: any, diskInfo: any) => {
+ const minFreeRootSpace = minDiskSpace * 1024 * 1024;
+ const minFreeUsrLibSpace = minDiskSpaceUsrLib * 1024 * 1024;
+ let warningString = "";
+
+ diskInfo.forEach((info: any) => {
+ switch (info.mountpoint) {
+ case "/":
+ warningString =
+ info.available < minFreeRootSpace
+ ? `A minimum of ${minDiskSpace}GB is required for
"${info.mountpoint}" mount. ${warningString}`
+ : warningString;
+ break;
+ case "/usr":
+ case "/usr/lib":
+ warningString =
+ info.available < minFreeUsrLibSpace
+ ? `A minimum of ${minDiskSpaceUsrLib}GB is required for
"${info.mountpoint}" mount. ${warningString}`
+ : warningString;
+ break;
+ default:
+ break;
+ }
+ });
+
+ return warningString
+ ? `Not enough disk space on host (${hostName}). ${warningString}`
+ : "";
+ };
+
+ const checkTHP = (hostName: any, transparentHugePage: any) => {
+ if (transparentHugePage === "always") {
+ return hostName;
+ } else {
+ return "";
+ }
+ };
+
+ const getHostInfoSuccessCallback = (data: any) => {
+ const warningDataCopy = cloneDeep(warningData);
+ const hosts = bootHosts.current;
+ let repoWarnings: any[] = [],
+ diskWarnings: any[] = [],
+ thpWarnings: any[] = [],
+ hostsRepoNames: any[] = [],
+ hostsDiskContext: any[] = [],
+ thpContext: any[] = [],
+ hostsContext: any[] = [],
+ hostsDiskNames: any[] = [],
+ thpHostsNames: any[] = [];
+
+ const hostCheckRes = hostCheckResult.current;
+ if (hostCheckRes) {
+ parseHostCheckWarnings(warningDataCopy, hostCheckRes);
+ hostCheckResult.current = null;
+ } else {
+ parseWarnings(warningDataCopy, data);
+ }
+ hosts.forEach((_host) => {
+ const host = get(data, "items", []).filter((item: any) => {
+ return get(item, "Hosts.host_name") === get(_host, "name");
+ })[0];
+ if (skipBootstrap.current) {
+ _host = setHostDataWithSkipBootstrap(_host);
+ } else if (!isEmpty(host)) {
+ _host = setHostDataFromLoadedHostInfo(_host, host);
+ const hostName = get(host, "Hosts.host_name", "");
+
+ let context = checkHostOSType(
+ get(host, "Hosts.os_family", ""),
+ hostName
+ );
+ if (!isEmpty(context)) {
+ hostsContext.push(context);
+ hostsRepoNames.push(hostName);
+ }
+
+ const diskContext = checkHostDiskSpace(
+ hostName,
+ get(host, "Hosts.disk_info", [])
+ );
+ if (!isEmpty(diskContext)) {
+ hostsDiskContext.push(diskContext);
+ hostsDiskNames.push(hostName);
+ }
+ const _hostPackagesData = hostPackagesData.current.filter(
+ (hostData: any) => {
+ return get(hostData, "hostName") === hostName;
+ }
+ );
+ if (!isEmpty(_hostPackagesData)) {
+ const transparentHugePage = get(
+ _hostPackagesData,
+ "transparentHugePage",
+ []
+ );
+ context = checkTHP(hostName, transparentHugePage);
+ } else {
+ context = checkTHP(
+ hostName,
+ get(host, "Hosts.last_agent_env.transparentHugePage")
+ );
+ }
+ if (!isEmpty(context)) {
+ thpContext.push(context);
+ thpHostsNames.push(hostName);
+ }
+ }
+ });
+
+ if (hostsContext.length > 0) {
+ repoWarnings.push({
+ name: "Repository for OS not available",
+ hosts: hostsContext,
+ hostsLong: hostsContext,
+ hostsNames: hostsRepoNames,
+ category: "repositories",
+ });
+ }
+
+ if (hostsDiskContext.length > 0) {
+ diskWarnings.push({
+ name: "Not enough disk space",
+ hosts: hostsDiskContext,
+ hostsLong: hostsDiskContext,
+ hostsNames: hostsDiskNames,
+ category: "disk",
+ });
+ }
+
+ if (thpContext.length > 0) {
+ thpWarnings.push({
+ name: "Transparent Huge Pages",
+ hosts: thpContext,
+ hostsLong: thpContext,
+ hostsNames: thpHostsNames,
+ category: "thp",
+ });
+ }
+
+ set(warningDataCopy, "repoCategoryWarnings", repoWarnings);
+ set(warningDataCopy, "diskCategoryWarnings", diskWarnings);
+ set(warningDataCopy, "thpCategoryWarnings", thpWarnings);
+ setWarningData(warningDataCopy);
+ };
+
+ const getHostInfo = async () => {
+ const response = await HostsApi.getHostsData(
+
"Hosts/total_mem,Hosts/cpu_count,Hosts/disk_info,Hosts/last_agent_env,Hosts/host_name,Hosts/os_type,Hosts/os_arch,Hosts/os_family,Hosts/ip"
+ );
+ getHostInfoSuccessCallback(response);
+ };
+
+ const parseHostNameResolution = (data: any) => {
+ if (!data) {
+ return;
+ }
+ const warningDataCopy = cloneDeep(warningData);
+ data.tasks.forEach((task: any) => {
+ const name = "Hostname resolution";
+ let hostInfo = get(warningDataCopy, "allWarnings", []).find(
+ (warning: any) => warning.name === name
+ );
+ if (finishStates.includes(task.Tasks.status)) {
+ if (
+ task.Tasks.status === "COMPLETED" &&
+ !!get(task,
"Tasks.structured_out.host_resolution_check.failed_count")
+ ) {
+ const targetHostName = get(task, "Tasks.host_name");
+ const relatedHostNames = get(
+ task,
+ "Tasks.structured_out.host_resolution_check.hosts_with_failures",
+ []
+ );
+ const contextMessage = `${targetHostName} could not resolve: ${
+ relatedHostNames.length
+ } host${relatedHostNames.length === 1 ? "" : "s"}.`;
+ const contextMessageLong = `${targetHostName} could not resolve:
${relatedHostNames.join(
+ ", "
+ )}.`;
+ if (!hostInfo) {
+ hostInfo = {
+ name: name,
+ hosts: [contextMessage],
+ hostsLong: [contextMessageLong],
+ hostsNames: [targetHostName],
+ category: "hostNameResolution",
+ };
+ warningDataCopy.hostCheckWarnings = [
+ ...warningDataCopy.hostCheckWarnings,
+ hostInfo,
+ ];
+ } else {
+ if (!hostInfo.hostsNames.includes(targetHostName)) {
+ hostInfo.hosts.push(contextMessage);
+ hostInfo.hostsLong.push(contextMessageLong);
+ hostInfo.hostsNames.push(targetHostName);
+ warningDataCopy.hostCheckWarnings =
+ warningDataCopy.hostCheckWarnings.map((warning: any) =>
+ warning.name === name ? hostInfo : warning
+ );
+ }
+ }
+ }
+ }
+ });
+ setWarningData(warningDataCopy);
+ };
+
+ const getHostCheckTasksSuccess = (data: any) => {
+ if (isEmpty(data)) {
+ getGeneralHostCheck();
+ return;
+ }
+ if (finishStates.includes(get(data, "Requests.request_status"))) {
+ if (get(data, "Requests.inputs", "").includes("last_agent_env_check")) {
+ pausePolling();
+ setIsHostCheckRunning(false);
+ hostsPackagesData.current = get(data, "tasks", []).map((task: any) => {
+ const installedPackages = get(
+ task,
+ "Tasks.structured_out.installed_packages",
+ []
+ );
+ return {
+ hostName: get(task, "Tasks.host_name", ""),
+ transparentHugePage: get(
+ task,
+ "Tasks.structured_out.transparentHugePage.message",
+ ""
+ ),
+ installedPackages: installedPackages,
+ };
+ });
+ hostCheckResult.current = data;
+ getHostInfo();
+ } else if (
+ get(data, "Requests.inputs", "").includes("host_resolution_check")
+ ) {
+ parseHostNameResolution(data);
+ getGeneralHostCheck();
+ }
+ }
+ };
+
+ const startHostCheck = (bootHostsList: BootHostType[]) => {
+ cleanup();
+ bootHosts.current = bootHostsList;
+ if (!isEmpty(ambariProperties)) {
+ const hostName = map(bootHosts.current, "name").join(",");
+ const jdkLocation = get(
+ ambariProperties,
+ "RootServiceComponents.properties.jdk_location",
+ ""
+ );
+ const requestInfo = {
+ action: "check_host",
+ context: "Check host",
+ parameters: {
+ hosts: hostName,
+ check_execute_list:
+
"last_agent_env_check,installed_packages,existing_repos,transparentHugePage",
+ jdk_location: jdkLocation,
+ threshold: "20",
+ },
+ };
+ const data = {
+ RequestInfo: requestInfo,
+ resource_filters: { hosts: hostName },
+ };
+ dataForHostCheck.current = data;
+ }
+ setIsHostCheckRunning(true);
+ requestToPerformHostCheck();
+ };
+
+ return {
+ startHostCheck,
+ stopHostCheck,
+ isHostCheckRunning,
+ hostCheckResult: warningData,
+ };
+};
diff --git a/ambari-web/latest/src/screens/Hosts/HostChecks.tsx
b/ambari-web/latest/src/screens/Hosts/HostChecks.tsx
new file mode 100644
index 0000000000..181ab1d6c8
--- /dev/null
+++ b/ambari-web/latest/src/screens/Hosts/HostChecks.tsx
@@ -0,0 +1,530 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Accordion, Form } from "react-bootstrap";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import {
+ faCheck,
+ faExclamationTriangle,
+ faExternalLink,
+} from "@fortawesome/free-solid-svg-icons";
+import Spinner from "../../components/Spinner";
+import Modal from "../../components/Modal";
+import { cloneDeep, forEach, get, isEmpty, set } from "lodash";
+import {
+ categoriesToReportMap,
+ checkHostIssues,
+ hostCheckReportConstants,
+ restWarningCategories,
+ restWarningCategoriesToIssuesKeyMap,
+} from "./constants";
+import { useEffect, useState } from "react";
+import modalManager from "../../store/ModalManager";
+import { initialWarningData } from "../../hooks/useHostChecks";
+import { translate } from "../../Utils/Utility";
+
+enum HostOption {
+ ALL = "All Hosts",
+}
+
+type HostChecksModalProps = {
+ isOpen: boolean;
+ onClose: () => void;
+ successCallback: () => void;
+ hostCheckResult: any;
+ loading: boolean;
+};
+
+export const getHostWithIssues = (hostCheckResult: any) => {
+ const hostNameList: any[] = [];
+
+ get(hostCheckResult, "warningsByHost")
+ ?.slice(1)
+ ?.forEach((_host: any) => {
+ if (_host?.warnings?.length) {
+ hostNameList.push({
+ name: _host?.name,
+ count: _host?.warnings?.length,
+ });
+ }
+ });
+
+ restWarningCategories.forEach((category: any) => {
+ if (hostCheckResult?.[category]?.length) {
+ hostCheckResult?.[category]?.[0]?.hostsNames?.forEach(
+ (_hostName: any) => {
+ const prevEntry = hostNameList.find(
+ (host: any) => host?.name === _hostName
+ );
+ if (!prevEntry) {
+ hostNameList.push({
+ name: _hostName,
+ count: 1,
+ });
+ } else {
+ prevEntry.count++;
+ }
+ }
+ );
+ }
+ });
+
+ return hostNameList;
+};
+
+export const getTotalIssuesCount = (hostCheckResult: any) => {
+ let totalIssues = 0;
+ const warningNames = new Set<string>();
+ hostCheckResult.warningsByHost
+ .filter((_host: any) => _host.name === HostOption.ALL)
+ .forEach((_host: any) => {
+ _host.warnings.forEach((warning: any) => {
+ if (warningNames.has(warning.name)) {
+ return;
+ } else {
+ warningNames.add(warning.name);
+ totalIssues++;
+ }
+ });
+ });
+
+ restWarningCategories.forEach((category: any) => {
+ if (hostCheckResult?.[category]?.length) {
+ totalIssues++;
+ }
+ });
+
+ return totalIssues;
+};
+
+export default function HostChecks({
+ isOpen,
+ onClose,
+ successCallback,
+ hostCheckResult,
+ loading,
+}: HostChecksModalProps) {
+ const [issues, setIssues] = useState<any>(checkHostIssues);
+ const [filteredHostCheckRes, setFilteredHostCheckRes] =
+ useState<any>(hostCheckResult);
+ const [selectedHost, setSelectedHost] = useState(HostOption.ALL);
+
+ const showHostListModal = (hostsList: any[]) => {
+ const modalProps = {
+ onClose: () => {},
+ modalTitle: "List of hosts",
+ modalBody: getHostListModalBody(hostsList),
+ successCallback: () => {
+ modalManager.hide();
+ },
+ options: {
+ cancelableViaIcon: true,
+ cancelableViaBtn: false,
+ },
+ };
+ modalManager.show(modalProps);
+ };
+
+ const getHostListModalBody = (hostsList: any[]) => {
+ return (
+ <div>
+ {hostsList.map((host: any) => {
+ return <div className="fs-12 mb-1">{host}</div>;
+ })}
+ </div>
+ );
+ };
+
+ const getWarningListItemStructure = (warning: any, customText: string) => {
+ return (
+ <div className="d-flex mb-1">
+ <div className="fs-12 w-70">{warning.name}</div>
+ <div className="fs-12 w-30">
+ {customText}{" "}
+ <span
+ className="custom-link fs-12"
+ onClick={() => {
+ showHostListModal(warning.hosts);
+ }}
+ >
+ {warning.hosts.length} {warning.hosts.length > 1 ? "hosts" :
"host"}
+ </span>
+ </div>
+ </div>
+ );
+ };
+
+ useEffect(() => {
+ if (
+ JSON.stringify(filteredHostCheckRes) !==
+ JSON.stringify(initialWarningData)
+ ) {
+ const issuesCopy = cloneDeep(checkHostIssues);
+ const warningNames = new Set<string>();
+ filteredHostCheckRes.warningsByHost
+ .filter((_host: any) => _host.name === selectedHost)
+ .forEach((_host: any) => {
+ _host.warnings.forEach((warning: any) => {
+ const category = warning.category;
+ if (warningNames.has(warning.name)) {
+ return;
+ } else {
+ warningNames.add(warning.name);
+ }
+ const issue = get(issuesCopy, category, {});
+ issue.count = issue.count + 1;
+ switch (category) {
+ case "packages":
+ issue.data.push(
+ <div className="d-flex mb-1">
+ <div className="fs-12 w-40">{warning.name}</div>
+ <div className="fs-12 w-30">{warning.version}</div>
+ <div className="fs-12 w-30">
+ Installed on{" "}
+ <span
+ className="custom-link fs-12"
+ onClick={() => {
+ showHostListModal(warning.hosts);
+ }}
+ >
+ {warning.hosts.length} host
+ </span>
+ </div>
+ </div>
+ );
+ break;
+ case "processes":
+ case "firewall":
+ issue.data.push(
+ getWarningListItemStructure(warning, "Running on")
+ );
+ break;
+ case "services":
+ issue.data.push(
+ getWarningListItemStructure(warning, "Unhealthy on")
+ );
+ break;
+ case "users":
+ case "fileFolders":
+ issue.data.push(
+ getWarningListItemStructure(warning, "Present on")
+ );
+ break;
+ case "misc":
+ issue.data.push(
+ getWarningListItemStructure(warning, "Umask on")
+ );
+ break;
+ case "alternatives":
+ issue.data.push(
+ <div className="d-flex mb-1">
+ <div className="fs-12 w-40">{warning.name}</div>
+ <div className="fs-12 w-30">{warning.target}</div>
+ <div className="fs-12 w-30">
+ On{" "}
+ <span
+ className="custom-link fs-12"
+ onClick={() => {
+ showHostListModal(warning.hosts);
+ }}
+ >
+ {warning.hosts.length} host
+ </span>
+ </div>
+ </div>
+ );
+ break;
+ case "reverseLookup":
+ issue.data.push(getWarningListItemStructure(warning, ""));
+ break;
+ }
+ });
+ });
+
+ restWarningCategories.forEach((category) => {
+ const issue = get(
+ issuesCopy,
+ get(restWarningCategoriesToIssuesKeyMap, category),
+ {}
+ );
+ set(issue, "count", get(filteredHostCheckRes, category, []).length);
+ set(
+ issue,
+ "data",
+ get(filteredHostCheckRes, category, []).map((warning: any) => {
+ return (
+ <div>
+ {forEach(warning.hosts, (host: any) => {
+ return (
+ <div className="d-flex mb-1">
+ <div className="fs-12 w-70">
+ {warning.name + " " + host}
+ </div>
+ </div>
+ );
+ })}
+ </div>
+ );
+ })
+ );
+ set(
+ issuesCopy,
+ get(restWarningCategoriesToIssuesKeyMap, category),
+ issue
+ );
+ });
+ setIssues(issuesCopy);
+ }
+ }, [JSON.stringify(filteredHostCheckRes)]);
+
+ useEffect(() => {
+ if (!isEmpty(hostCheckResult)) {
+ if (selectedHost === HostOption.ALL) {
+ setFilteredHostCheckRes(hostCheckResult);
+ } else {
+ const filteredHostCheckResCopy = cloneDeep(hostCheckResult);
+ filteredHostCheckResCopy.warningsByHost =
+ filteredHostCheckResCopy.warningsByHost.filter(
+ (host: any) => host.name === selectedHost
+ );
+ restWarningCategories.forEach((category) => {
+ filteredHostCheckResCopy[category].forEach((warning: any) => {
+ warning.hosts = [selectedHost];
+ warning.hostsLong = [selectedHost];
+ warning.hostsNames = [selectedHost];
+ });
+ });
+ setFilteredHostCheckRes(filteredHostCheckResCopy);
+ }
+ }
+ }, [hostCheckResult, selectedHost]);
+
+ const getIssue = (issue: string) => {
+ return get(issues, issue);
+ };
+
+ const getHostNames = (result: any) => {
+ const hostNames = get(result, "warningsByHost", []).map(
+ (host: any) => host.name
+ );
+ return hostNames;
+ };
+
+ const contentInDetails = () => {
+ let newContent = "";
+ let hostNamesWithWarnings = getHostNames(filteredHostCheckRes);
+ if (selectedHost === HostOption.ALL) {
+ hostNamesWithWarnings = hostNamesWithWarnings.filter(
+ (hostName: string) => hostName !== HostOption.ALL
+ );
+ }
+
+ newContent +=
+ hostCheckReportConstants[
+ "installer.step3.hostWarningsPopup.report.header"
+ ] + new Date();
+ newContent +=
+ hostCheckReportConstants[
+ "installer.step3.hostWarningsPopup.report.hosts"
+ ];
+ newContent += hostNamesWithWarnings.join(" ");
+
+ const isHostPresent = (hostNames: string[]) => {
+ return hostNames.some((name) => hostNamesWithWarnings.includes(name));
+ };
+
+ const processContent = filteredHostCheckRes?.allWarnings?.filter(
+ (item: any) =>
+ item?.category === "processes" && isHostPresent(item?.hosts)
+ );
+ if (processContent.length) {
+ newContent +=
+ hostCheckReportConstants[
+ "installer.step3.hostWarningsPopup.report.process"
+ ];
+ processContent.forEach((process: any, i: number) => {
+ process?.hosts?.forEach((host: any, j: number) => {
+ if (!!i || !!j) {
+ newContent += ",";
+ }
+ newContent += `(${host},${process?.user},${process?.pid})`;
+ });
+ });
+ }
+
+ categoriesToReportMap.forEach((category) => {
+ if (restWarningCategories.includes(category?.key)) {
+ const catContent = filteredHostCheckRes?.[category?.key];
+ if (catContent.length) {
+ newContent += hostCheckReportConstants[category?.label];
+ newContent += catContent?.[0]?.hostsNames?.join(category?.separator);
+ }
+ } else {
+ const catContent = filteredHostCheckRes?.allWarnings?.filter(
+ (item: any) =>
+ item?.category === category?.key && isHostPresent(item?.hosts)
+ );
+ if (catContent.length) {
+ newContent += hostCheckReportConstants[category?.label];
+ if (category?.mapProperty) {
+ newContent += catContent
+ .map((warning: any) => warning[category?.mapProperty])
+ .join(category?.separator);
+ }
+ }
+ }
+ });
+
+ newContent += "</p>";
+ return newContent;
+ };
+
+ const getModalBody = () => {
+ if (loading) {
+ return <Spinner />;
+ }
+
+ return (
+ <div>
+ <div className="fs-12 mb-1">
+ Host Checks found{" "}
+ <span className="fw-bold text-dark fs-12">
+ {getTotalIssuesCount(hostCheckResult)} issues on{" "}
+ {getHostWithIssues(hostCheckResult).length} host.
+ </span>
+ </div>
+ <div className="fs-12 mb-4">
+ After manually resolving the issues, click{" "}
+ <span className="fw-bold text-dark fs-12">Rerun Checks.</span>
+ </div>
+ <div className="d-flex justify-content-between mb-2">
+ <div className="d-flex">
+ <Form.Label className="fw-bold text-dark pt-2 fs-12 me-5">
+ Hosts
+ </Form.Label>
+ <Form.Select
+ className="custom-form-control fs-12"
+ value={selectedHost}
+ onChange={(e) => setSelectedHost(e.target.value as HostOption)}
+ >
+ <option value={HostOption.ALL}>All Hosts</option>
+ {getHostNames(hostCheckResult)
+ .slice(1)
+ .map((hostName: string) => (
+ <option key={hostName} value={hostName}>
+ {hostName}
+ </option>
+ ))}
+ </Form.Select>
+ </div>
+ <div
+ className="custom-link fs-12 pt-2"
+ onClick={() => {
+ const content = contentInDetails();
+ const newWindow = window.open("", "_blank");
+ newWindow?.document.write(content);
+ }}
+ >
+ <FontAwesomeIcon icon={faExternalLink} className="fs-12" /> Show
+ Report
+ </div>
+ </div>
+ <div>
+ {Object.keys(issues).map((issue: string) => {
+ return (
+ <Accordion className="border-0" key={issue}>
+ <Accordion.Item eventKey={issue} className="border-0">
+ <Accordion.Header className="p-0">
+ <div className="d-flex justify-content-between">
+ <div className="d-flex">
+ {getIssue(issue).data.length ? (
+ <FontAwesomeIcon
+ icon={faExclamationTriangle}
+ className="me-2 text-danger"
+ />
+ ) : (
+ <FontAwesomeIcon
+ icon={faCheck}
+ className="me-2 text-success"
+ />
+ )}
+ <div className="fs-12">
+ {getIssue(issue).displayName}
+ </div>
+ </div>
+ <div className="fs-12 px-1">
+ ({getIssue(issue).count})
+ </div>
+ </div>
+ </Accordion.Header>
+ <hr className="m-0" />
+ <Accordion.Body>
+ <div className="fs-12">
+ {getIssue(issue).data.length
+ ? getIssue(issue).dataMessage
+ : `There were no ${getIssue(
+ issue
+ ).displayName.toLowerCase()}`}
+ </div>
+ <hr />
+ <div>
+ {getIssue(issue).data.map((data: any) => {
+ return (
+ <div>
+ {data}
+ <hr className="p-0" />
+ </div>
+ );
+ })}
+ </div>
+ </Accordion.Body>
+ </Accordion.Item>
+ </Accordion>
+ );
+ })}
+ </div>
+ </div>
+ );
+ };
+
+ return (
+ <div>
+ {isOpen ? (
+ <Modal
+ isOpen={isOpen}
+ onClose={onClose}
+ modalTitle={String(
+ translate("installer.step3.warnings.popup.header")
+ )}
+ modalBody={getModalBody()}
+ successCallback={successCallback}
+ options={{
+ okButtonDisabled: loading,
+ okButtonText: String(
+ translate("installer.step3.hostWarningsPopup.rerunChecks")
+ ).toUpperCase(),
+ okButtonVariant: "warning",
+ cancelButtonText: String(translate("common.close")).toUpperCase(),
+ cancelableViaIcon: true,
+ cancelableViaBtn: true,
+ }}
+ />
+ ) : null}
+ </div>
+ );
+}
diff --git a/ambari-web/latest/src/screens/Hosts/constants.ts
b/ambari-web/latest/src/screens/Hosts/constants.ts
new file mode 100644
index 0000000000..95c9aa5aef
--- /dev/null
+++ b/ambari-web/latest/src/screens/Hosts/constants.ts
@@ -0,0 +1,445 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { translate } from "../../Utils/Utility";
+
+export const sortByColIdToKeyMapping = {
+ hostname: "Hosts/host_name",
+ ip: "Hosts/ip",
+ rack: "Hosts/rack_info",
+ cores: "Hosts/cpu_count",
+ ram: "Hosts/total_mem",
+ disk: "metrics/disk/disk_free",
+ load: "metrics/load/load_one",
+};
+
+export const hostMetricsOption = [
+ "Last 1 hour",
+ "Last 2 hours",
+ "Last 4 hours",
+ "Last 12 hours",
+ "Last 24 hours",
+ "Last 1 week",
+ "Last 1 month",
+ "Last 1 year",
+];
+
+export const actionToStateMapping = {
+ start: {
+ PENDING: "INSTALLED",
+ IN_PROGRESS: "STARTING",
+ COMPLETED: "STARTED",
+ FAILED: "INSTALLED",
+ },
+ restart: {
+ PENDING: "INSTALLED",
+ IN_PROGRESS: "STARTING",
+ COMPLETED: "STARTED",
+ FAILED: "INSTALLED",
+ },
+ stop: {
+ PENDING: "STARTED",
+ IN_PROGRESS: "STOPPING",
+ COMPLETED: "INSTALLED",
+ FAILED: "STARTED",
+ },
+};
+
+export const finishStates = ["FAILED", "COMPLETED", "TIMEDOUT", "ABORTED"];
+export const componentFinishStates = ["INSTALLED", "STARTED"];
+export const maintenanceStates = ["ON", "OFF"];
+export const minDiskSpace = 2.0;
+export const minDiskSpaceUsrLib = 1.0;
+
+export const restWarningCategories = [
+ "repoCategoryWarnings",
+ "diskCategoryWarnings",
+ "jdkCategoryWarnings",
+ "hostCheckWarnings",
+ "thpCategoryWarnings",
+];
+
+export const restWarningCategoriesToIssuesKeyMap = {
+ repoCategoryWarnings: "repositories",
+ diskCategoryWarnings: "disk",
+ jdkCategoryWarnings: "jdk",
+ hostCheckWarnings: "hostNameResolution",
+ thpCategoryWarnings: "thp",
+};
+
+export const checkHostIssues = {
+ packages: {
+ displayName: "Package Issues",
+ count: 0,
+ data: [],
+ dataMessage: "The following packages should be uninstalled",
+ noDataMessage: "There were no unwanted packages",
+ },
+ processes: {
+ displayName: "Process Issues",
+ count: 0,
+ data: [],
+ dataMessage: "The following processes should not be running",
+ noDataMessage: "There were no unwanted processes",
+ },
+ thp: {
+ displayName: "Transparent Huge Pages Issues",
+ count: 0,
+ data: [],
+ dataMessage: "The following THP issues should be fixed",
+ noDataMessage: "There were no THP issues",
+ },
+ jdk: {
+ displayName: "JDK Issues",
+ count: 0,
+ data: [],
+ dataMessage: "The following JDK issues should be fixed",
+ noDataMessage: "There were no JDK issues",
+ },
+ disk: {
+ displayName: "Disk Issues",
+ count: 0,
+ data: [],
+ dataMessage: "The following disks should be cleaned",
+ noDataMessage: "There were no disk space issues",
+ },
+ repositories: {
+ displayName: "Repository Issues ",
+ count: 0,
+ data: [],
+ dataMessage:
+ "The following repositories has OS type mis-match with registered hosts",
+ noDataMessage:
+ "There were no repositories OS type mis-match with registered hosts",
+ },
+ firewall: {
+ displayName: "Firewall Issues",
+ count: 0,
+ data: [],
+ dataMessage: "The following firewalls should be disabled",
+ noDataMessage: "There were no firewalls running",
+ },
+ fileFolders: {
+ displayName: "File and Folder Issues",
+ count: 0,
+ data: [],
+ dataMessage: "The following files/folders should be removed",
+ noDataMessage: "There were no unwanted files and folders",
+ },
+ services: {
+ displayName: "Service Issues",
+ count: 0,
+ data: [],
+ dataMessage: "The following services should be stopped",
+ noDataMessage: "There were no unwanted services",
+ },
+ users: {
+ displayName: "User Issues",
+ count: 0,
+ data: [],
+ dataMessage: "The following users should be removed",
+ noDataMessage: "There were no unwanted users",
+ },
+ misc: {
+ displayName: "Misc Issues",
+ count: 0,
+ data: [],
+ dataMessage: "The following issues should be resolved",
+ noDataMessage: "There were no issues",
+ },
+ alternatives: {
+ displayName: "Alternatives Issues",
+ count: 0,
+ data: [],
+ dataMessage: "The following alternatives should be removed",
+ noDataMessage: "There were no alternative issues",
+ },
+ reverseLookup: {
+ displayName: "Reverse Lookup Issues",
+ count: 0,
+ data: [],
+ dataMessage: "The following hosts have reverse lookup issues",
+ noDataMessage: "There were no reverse DNS lookup issues",
+ },
+ hostNameResolution: {
+ displayName: "Hostname Resolution Issues",
+ count: 0,
+ data: [],
+ dataMessage: "The following hosts have hostname resolution issues",
+ noDataMessage: "There were no hostname resolution issues",
+ },
+};
+
+export const categoriesToReportMap = [
+ {
+ key: "thp",
+ label: "installer.step3.hostWarningsPopup.report.thp",
+ separator: " ",
+ },
+ {
+ key: "jdk",
+ label: "installer.step3.hostWarningsPopup.report.jdk",
+ separator: "<br>",
+ },
+ {
+ key: "disk",
+ label: "installer.step3.hostWarningsPopup.report.disk",
+ separator: "<br>",
+ },
+ {
+ key: "repositories",
+ label: "installer.step3.hostWarningsPopup.report.repositories",
+ separator: "<br>",
+ },
+ {
+ key: "hostNameResolution",
+ label: "installer.step3.hostWarningsPopup.report.hostNameResolution",
+ separator: "<br>",
+ },
+ {
+ key: "firewall",
+ label: "installer.step3.hostWarningsPopup.report.firewall",
+ separator: "<br>",
+ mapProperty: "name",
+ },
+ {
+ key: "fileFolders",
+ label: "installer.step3.hostWarningsPopup.report.fileFolders",
+ separator: " ",
+ mapProperty: "name",
+ },
+ {
+ key: "reverseLookup",
+ label: "installer.step3.hostWarningsPopup.report.reverseLookup",
+ separator: " ",
+ mapProperty: "hostsLong",
+ },
+ {
+ key: "packages",
+ label: "installer.step3.hostWarningsPopup.report.package",
+ separator: " ",
+ mapProperty: "name",
+ },
+ {
+ key: "services",
+ label: "installer.step3.hostWarningsPopup.report.service",
+ separator: " ",
+ mapProperty: "name",
+ },
+ {
+ key: "users",
+ label: "installer.step3.hostWarningsPopup.report.user",
+ separator: " ",
+ mapProperty: "name",
+ },
+];
+
+// To be moved to internationalization
+export const hostCheckReportConstants: any = {
+ "installer.step3.hostWarningsPopup.report.header":
+ '<p style="font-family:
monospace">######################################<br># Host Checks
Report<br>#<br># Generated: ',
+ "installer.step3.hostWarningsPopup.report.hosts":
+
"<br>######################################<br><br>######################################<br>#
Hosts<br>#<br># A space delimited list of hosts which have issues.<br>#
Provided so that administrators can easily copy hostnames into scripts, email
etc.<br>######################################<br>HOSTS<br>",
+ "installer.step3.hostWarningsPopup.report.jdk":
+ "<br><br>######################################<br># JDK Check <br>#<br>#
A newline delimited list of JDK
issues.<br>######################################<br>JDK ISSUES<br>",
+ "installer.step3.hostWarningsPopup.report.disk":
+ "<br><br>######################################<br># Disk <br>#<br># A
newline delimited list of disk
issues.<br>######################################<br>DISK ISSUES<br>",
+ "installer.step3.hostWarningsPopup.report.repositories":
+ "<br><br>######################################<br># Repositories
<br>#<br># A newline delimited list of repositories
issues.<br>######################################<br>REPOSITORIES ISSUES<br>",
+ "installer.step3.hostWarningsPopup.report.hostNameResolution":
+ "<br><br>######################################<br># Hostname
Resolution<br>#<br># A newline delimited list of hostname resolution
issues.<br>######################################<br>HOSTNAME RESOLUTION
ISSUES<br>",
+ "installer.step3.hostWarningsPopup.report.thp":
+ "<br><br>######################################<br># Transparent Huge
Pages(THP) <br>#<br># A space delimited list of hostnames on which Transparent
Huge Pages are enabled.<br>######################################<br>THP ISSUES
HOSTS<br>",
+ "installer.step3.hostWarningsPopup.report.firewall":
+ "<br><br>######################################<br># Firewall<br>#<br># A
newline delimited list of firewall
issues.<br>######################################<br>FIREWALL<br>",
+ "installer.step3.hostWarningsPopup.report.fileFolders":
+ "<br><br>######################################<br># Files and
Folders<br>#<br># A space delimited list of files and folders which should not
exist.<br># Provided so that administrators can easily copy paths into scripts,
email etc.<br># Example: rm -r /etc/hadoop
/etc/hbase<br>######################################<br>FILES AND FOLDERS<br>",
+ "installer.step3.hostWarningsPopup.report.reverseLookup":
+ "<br><br>######################################<br># Reverse Lookup<br>#
<br># The hostname was not found in the reverse DNS lookup. This may result in
incorrect behavior. <br># Please check the DNS setup and fix the
issue.<br>######################################<br>REVERSE LOOKUP<br>",
+ "installer.step3.hostWarningsPopup.report.process":
+ "<br><br>######################################<br># Processes<br>#<br># A
comma separated list of process tuples which should not be running.<br>#
Provided so that administrators can easily copy paths into scripts, email
etc.<br>######################################<br>PROCESSES<br>",
+ "installer.step3.hostWarningsPopup.report.package":
+ "<br><br>######################################<br># Packages<br>#<br># A
space delimited list of software packages which should be uninstalled.<br>#
Provided so that administrators can easily copy paths into scripts, email
etc.<br># Example: yum remove hadoop-hdfs
yarn<br>######################################<br>PACKAGES<br>",
+ "installer.step3.hostWarningsPopup.report.service":
+ "<br><br>######################################<br># Services<br>#<br># A
space delimited list of services which should be up and running.<br># Provided
so that administrators can easily copy paths into scripts, email etc.<br>#
Example: services start ntpd
httpd<br>######################################<br>SERVICES<br>",
+ "installer.step3.hostWarningsPopup.report.user":
+ "<br><br>######################################<br># Users<br>#<br># A
space delimited list of users who should not exist.<br># Provided so that
administrators can easily copy paths into scripts, email etc.<br># Example:
userdel hdfs<br>######################################<br>USERS<br>",
+};
+
+// These fields cannot be selected again if already selected in the filter
+export const nonRepeatableHostFieldOptions = ["cpu", "memoryFormatted"];
+
+export const healthClassesForHostFilter = [
+ {
+ label: translate("hosts.host.healthStatusCategory.green"),
+ value: "HEALTHY",
+ name: "healthClass",
+ },
+ {
+ label: translate("hosts.host.healthStatusCategory.red"),
+ value: "UNHEALTHY",
+ name: "healthClass",
+ },
+ {
+ label: translate("hosts.host.healthStatusCategory.orange"),
+ value: "ALERT",
+ name: "healthClass",
+ },
+ {
+ label: translate("hosts.host.healthStatusCategory.yellow"),
+ value: "UNKNOWN",
+ name: "healthClass",
+ },
+ {
+ label: translate("hosts.host.alerts.label"),
+ value: [">0", ">0"],
+ name: "criticalWarningAlertsCount",
+ },
+ {
+ label: translate("common.restart"),
+ value: "true",
+ name: "componentsWithStaleConfigsCount",
+ },
+ {
+ label: translate("common.passive_state"),
+ value: [
+ "ON",
+ "IMPLIED_FROM_HOST",
+ "IMPLIED_FROM_SERVICE",
+ "IMPLIED_FROM_SERVICE_AND_HOST",
+ ],
+ name: "componentsInPassiveStateCount",
+ },
+];
+
+export const hostFilterProperties = [
+ {
+ name: "hostName",
+ key: "Hosts/host_name",
+ type: "MATCH",
+ },
+ {
+ name: "ip",
+ key: "Hosts/ip",
+ type: "MATCH",
+ },
+ {
+ name: "cpu",
+ key: "Hosts/cpu_count",
+ type: "EQUAL",
+ valueType: "number",
+ },
+ {
+ name: "memoryFormatted",
+ key: "Hosts/total_mem",
+ type: "EQUAL",
+ valueType: "ambari-bandwidth",
+ },
+ {
+ name: "loadAvg",
+ key: "metrics/load/load_one",
+ type: "EQUAL",
+ },
+ {
+ name: "rack",
+ key: "Hosts/rack_info",
+ type: "MATCH",
+ },
+ {
+ name: "hostComponents",
+ key: "host_components/HostRoles/component_name",
+ type: "EQUAL",
+ },
+ {
+ name: "services",
+ key: "host_components/HostRoles/service_name",
+ type: "MATCH",
+ },
+ {
+ name: "state",
+ key: "host_components/HostRoles/state",
+ type: "MATCH",
+ },
+ {
+ name: "healthClass",
+ key: "Hosts/host_status",
+ type: "EQUAL",
+ },
+ {
+ name: "criticalWarningAlertsCount",
+ key: "(alerts_summary/CRITICAL{0}|alerts_summary/WARNING{1})",
+ type: "CUSTOM",
+ },
+ {
+ name: "componentsWithStaleConfigsCount",
+ key: "host_components/HostRoles/stale_configs",
+ type: "EQUAL",
+ },
+ {
+ name: "componentsInPassiveStateCount",
+ key: "host_components/HostRoles/maintenance_state",
+ type: "MULTIPLE",
+ },
+ {
+ name: "selected",
+ key: "Hosts/host_name",
+ type: "MULTIPLE",
+ },
+ {
+ name: "version",
+ key: "stack_versions/repository_versions/RepositoryVersions/display_name",
+ type: "EQUAL",
+ },
+ {
+ name: "versionState",
+ key: "stack_versions/HostStackVersions/state",
+ type: "EQUAL",
+ },
+ {
+ name: "hostStackVersion",
+ key: "stack_versions",
+ type: "EQUAL",
+ },
+ {
+ name: "componentState",
+ key: [
+ "(host_components/HostRoles/component_name={0})",
+
"(host_components/HostRoles/component_name={0}&host_components/HostRoles/state={1})",
+
"(host_components/HostRoles/component_name={0}&host_components/HostRoles/desired_admin_state={1})",
+
"(host_components/HostRoles/component_name={0}&host_components/HostRoles/maintenance_state={1})",
+ ],
+ type: "COMBO",
+ },
+];
+
+export const serviceNameToModelKeyMap: any = {
+ HDFS: "hdfs",
+ YARN: "yarn",
+ MAPREDUCE2: "mapreduce2",
+ TEZ: "tez",
+ HIVE: "hive",
+ HBASE: "hbase",
+ ZOOKEEPER: "zk",
+ AMBARI_METRICS: "ambari_metrics",
+ RANGER: "ranger",
+ RANGER_KMS: "ranger_kms",
+ KERBEROS: "kerberos",
+ SPARK3: "spark3",
+ SSM: "ssm",
+ TRINO: "trino",
+};
diff --git a/ambari-web/latest/src/screens/Hosts/helpers.ts
b/ambari-web/latest/src/screens/Hosts/helpers.ts
new file mode 100644
index 0000000000..961352c2cc
--- /dev/null
+++ b/ambari-web/latest/src/screens/Hosts/helpers.ts
@@ -0,0 +1,109 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { get, startCase } from "lodash";
+
+const components = {
+ API: "API",
+ DECOMMISSION_DATANODE: "Update Exclude File",
+ DRPC: "DRPC",
+ FLUME_HANDLER: "Flume",
+ GLUSTERFS: "GLUSTERFS",
+ HBASE: "HBase",
+ HBASE_REGIONSERVER: "RegionServer",
+ HCAT: "HCat Client",
+ HDFS: "HDFS",
+ HISTORYSERVER: "History Server",
+ HIVE_SERVER: "HiveServer2",
+ JCE: "JCE",
+ MAPREDUCE2: "MapReduce2",
+ MYSQL: "MySQL",
+ REST: "REST",
+ SECONDARY_NAMENODE: "SNameNode",
+ STORM_REST_API: "Storm REST API Server",
+ WEBHCAT: "WebHCat",
+ YARN: "YARN",
+ UI: "UI",
+ ZKFC: "ZKFailoverController",
+ ZOOKEEPER: "ZooKeeper",
+ ZOOKEEPER_QUORUM_SERVICE_CHECK: "ZK Quorum Service Check",
+ HAWQ: "HAWQ",
+ PXF: "PXF",
+};
+
+export const normalizeName = (name: string) => {
+ if (!name || typeof name !== "string") return "";
+ if (get(components, name, "")) return get(components, name, "");
+ name = name.toLowerCase();
+ const suffixNoSpaces = ["node", "tracker", "manager"];
+ const suffixRegExp = new RegExp(`(\\w+)(${suffixNoSpaces.join("|")})`, "gi");
+ if (/_/g.test(name)) {
+ name = name
+ .split("_")
+ .map((singleName) => normalizeName(singleName.toUpperCase()))
+ .join(" ");
+ } else if (suffixRegExp.test(name)) {
+ suffixRegExp.lastIndex = 0;
+ const matches = suffixRegExp.exec(name);
+ if (matches) {
+ name =
+ startCase(matches[1].toLowerCase()) +
+ startCase(matches[2].toLowerCase());
+ }
+ }
+ return startCase(name.toLowerCase());
+};
+
+export const normalizeNameBySeparators = (
+ name: string,
+ separators: string[]
+) => {
+ if (!name || typeof name !== "string") return "";
+ name = name.toLowerCase();
+ if (!separators || separators.length === 0) {
+ separators = ["_"];
+ }
+
+ for (const separator of separators) {
+ if (new RegExp(separator, "g").test(name)) {
+ name = name
+ .split(separator)
+ .map((singleName) => {
+ return normalizeName(singleName.toUpperCase());
+ })
+ .join(" ");
+ break;
+ }
+ }
+ return startCase(name.toLowerCase());
+};
+
+export const sortPropertyLight = (data: any[], path: any) => {
+ const realPath = typeof path === "string" ? path.split('.') : [];
+ return data.sort((a, b) => {
+ let aProperty: any = a;
+ let bProperty: any = b;
+ realPath.forEach((key) => {
+ aProperty = aProperty[key];
+ bProperty = bProperty[key];
+ });
+ if (aProperty > bProperty) return 1;
+ if (aProperty < bProperty) return -1;
+ return 0;
+ });
+}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]