All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sentrysoftware.metricshub.extension.wmi.WmiExtension Maven / Gradle / Ivy

Go to download

MetricsHub WMI Extension implementation providing a set of functionalities used to process WMI criteria and sources.

There is a newer version: 1.0.01
Show newest version
package org.sentrysoftware.metricshub.extension.wmi;

/*-
 * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
 * MetricsHub WMI Extension
 * ჻჻჻჻჻჻
 * Copyright 2023 - 2024 Sentry Software
 * ჻჻჻჻჻჻
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
 */

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.sentrysoftware.metricshub.engine.common.exception.InvalidConfigurationException;
import org.sentrysoftware.metricshub.engine.configuration.IConfiguration;
import org.sentrysoftware.metricshub.engine.connector.model.identity.criterion.CommandLineCriterion;
import org.sentrysoftware.metricshub.engine.connector.model.identity.criterion.Criterion;
import org.sentrysoftware.metricshub.engine.connector.model.identity.criterion.IpmiCriterion;
import org.sentrysoftware.metricshub.engine.connector.model.identity.criterion.ProcessCriterion;
import org.sentrysoftware.metricshub.engine.connector.model.identity.criterion.ServiceCriterion;
import org.sentrysoftware.metricshub.engine.connector.model.identity.criterion.WmiCriterion;
import org.sentrysoftware.metricshub.engine.connector.model.monitor.task.source.CommandLineSource;
import org.sentrysoftware.metricshub.engine.connector.model.monitor.task.source.IpmiSource;
import org.sentrysoftware.metricshub.engine.connector.model.monitor.task.source.Source;
import org.sentrysoftware.metricshub.engine.connector.model.monitor.task.source.WmiSource;
import org.sentrysoftware.metricshub.engine.extension.IProtocolExtension;
import org.sentrysoftware.metricshub.engine.strategy.detection.CriterionTestResult;
import org.sentrysoftware.metricshub.engine.strategy.source.SourceTable;
import org.sentrysoftware.metricshub.engine.telemetry.MetricFactory;
import org.sentrysoftware.metricshub.engine.telemetry.Monitor;
import org.sentrysoftware.metricshub.engine.telemetry.TelemetryManager;
import org.sentrysoftware.metricshub.extension.win.IWinConfiguration;
import org.sentrysoftware.metricshub.extension.win.WinCommandService;
import org.sentrysoftware.metricshub.extension.win.detection.WinCommandLineCriterionProcessor;
import org.sentrysoftware.metricshub.extension.win.detection.WinIpmiCriterionProcessor;
import org.sentrysoftware.metricshub.extension.win.detection.WinProcessCriterionProcessor;
import org.sentrysoftware.metricshub.extension.win.detection.WinServiceCriterionProcessor;
import org.sentrysoftware.metricshub.extension.win.detection.WmiCriterionProcessor;
import org.sentrysoftware.metricshub.extension.win.detection.WmiDetectionService;
import org.sentrysoftware.metricshub.extension.win.source.WinCommandLineSourceProcessor;
import org.sentrysoftware.metricshub.extension.win.source.WinIpmiSourceProcessor;
import org.sentrysoftware.metricshub.extension.win.source.WmiSourceProcessor;

/**
 * This class implements the {@link IProtocolExtension} contract, reports the supported features,
 * processes WMI sources and criteria.
 */
@Slf4j
public class WmiExtension implements IProtocolExtension {

	/**
	 * Protocol up status value '1.0'
	 */
	public static final Double UP = 1.0;

	/**
	 * Protocol down status value '0.0'
	 */
	public static final Double DOWN = 0.0;

	/**
	 * Up metric name format that will be saved by the metric factory
	 */
	public static final String WMI_UP_METRIC = "metricshub.host.up{protocol=\"wmi\"}";

	/**
	 * WMI namespace
	 */
	public static final String WMI_TEST_NAMESPACE = "root\\cimv2";

	/**
	 * WMI Query used by the protocol health check
	 */
	public static final String WMI_TEST_QUERY = "SELECT Name FROM Win32_ComputerSystem";

	private WmiRequestExecutor wmiRequestExecutor;
	private WmiDetectionService wmiDetectionService;
	private WinCommandService winCommandService;

	/**
	 * Creates a new instance of the {@link WmiExtension} implementation.
	 */
	public WmiExtension() {
		wmiRequestExecutor = new WmiRequestExecutor();
		wmiDetectionService = new WmiDetectionService(wmiRequestExecutor);
		winCommandService = new WinCommandService(wmiRequestExecutor);
	}

	@Override
	public boolean isValidConfiguration(IConfiguration configuration) {
		return configuration instanceof WmiConfiguration;
	}

	@Override
	public Set> getSupportedSources() {
		return Set.of(WmiSource.class, CommandLineSource.class, IpmiSource.class);
	}

	@Override
	public Map, Set>> getConfigurationToSourceMapping() {
		return Map.of(WmiConfiguration.class, Set.of(WmiSource.class));
	}

	@Override
	public Set> getSupportedCriteria() {
		return Set.of(WmiCriterion.class, ServiceCriterion.class, CommandLineCriterion.class, IpmiCriterion.class);
	}

	@Override
	public void checkProtocol(TelemetryManager telemetryManager) {
		// Create and set the WMI result to null
		List> wmiResult = null;

		// Retrieve the hostname
		final String hostname = telemetryManager.getHostname();

		// Retrieve WMI Configuration from the telemetry manager host configuration
		final WmiConfiguration wmiConfiguration = (WmiConfiguration) telemetryManager
			.getHostConfiguration()
			.getConfigurations()
			.get(WmiConfiguration.class);

		// Retrieve the host endpoint monitor
		final Monitor hostMonitor = telemetryManager.getEndpointHostMonitor();

		// Stop the health check if there is not an WMI configuration
		if (wmiConfiguration == null) {
			return;
		}

		log.info(
			"Hostname {} - Checking WMI protocol status. Sending a WQL SELECT request on {} namespace.",
			hostname,
			WMI_TEST_NAMESPACE
		);

		// Create the MetricFactory in order to collect the up metric
		final MetricFactory metricFactory = new MetricFactory();

		// Get the strategy time which represents the collect time of the up metric
		final Long strategyTime = telemetryManager.getStrategyTime();

		try {
			wmiResult = wmiRequestExecutor.executeWmi(hostname, wmiConfiguration, WMI_TEST_QUERY, WMI_TEST_NAMESPACE);
		} catch (Exception e) {
			if (wmiRequestExecutor.isAcceptableException(e)) {
				// Generate a metric from the WMI result
				metricFactory.collectNumberMetric(hostMonitor, WMI_UP_METRIC, UP, strategyTime);
				return;
			}
			log.debug(
				"Hostname {} - Checking WMI protocol status. WMI exception when performing a WQL SELECT request on {} namespace: ",
				hostname,
				WMI_TEST_NAMESPACE,
				e
			);
		}

		// Generate a metric from the WMI result
		metricFactory.collectNumberMetric(hostMonitor, WMI_UP_METRIC, wmiResult != null ? UP : DOWN, strategyTime);
	}

	@Override
	public CriterionTestResult processCriterion(
		Criterion criterion,
		String connectorId,
		TelemetryManager telemetryManager
	) {
		final Function configurationRetriever = manager ->
			(IWinConfiguration) manager.getHostConfiguration().getConfigurations().get(WmiConfiguration.class);

		if (criterion instanceof WmiCriterion wmiCriterion) {
			return new WmiCriterionProcessor(wmiDetectionService, configurationRetriever, connectorId)
				.process(wmiCriterion, telemetryManager);
		} else if (criterion instanceof ServiceCriterion serviceCriterion) {
			return new WinServiceCriterionProcessor(wmiDetectionService, configurationRetriever)
				.process(serviceCriterion, telemetryManager);
		} else if (criterion instanceof CommandLineCriterion commandLineCriterion) {
			return new WinCommandLineCriterionProcessor(winCommandService, configurationRetriever, connectorId)
				.process(commandLineCriterion, telemetryManager);
		} else if (criterion instanceof IpmiCriterion ipmiCriterion) {
			return new WinIpmiCriterionProcessor(wmiDetectionService, configurationRetriever)
				.process(ipmiCriterion, telemetryManager);
		} else if (criterion instanceof ProcessCriterion processCriterion) {
			return new WinProcessCriterionProcessor(wmiDetectionService)
				.process(processCriterion, WmiConfiguration.builder().username(null).password(null).timeout(30L).build());
		}

		throw new IllegalArgumentException(
			String.format(
				"Hostname %s - Cannot process criterion %s.",
				telemetryManager.getHostname(),
				criterion != null ? criterion.getClass().getSimpleName() : ""
			)
		);
	}

	@Override
	public SourceTable processSource(Source source, String connectorId, TelemetryManager telemetryManager) {
		final Function configurationRetriever = manager ->
			(IWinConfiguration) manager.getHostConfiguration().getConfigurations().get(WmiConfiguration.class);

		if (source instanceof WmiSource wmiSource) {
			return new WmiSourceProcessor(wmiRequestExecutor, configurationRetriever, connectorId)
				.process(wmiSource, telemetryManager);
		} else if (source instanceof IpmiSource ipmiSource) {
			return new WinIpmiSourceProcessor(wmiRequestExecutor, configurationRetriever, connectorId)
				.process(ipmiSource, telemetryManager);
		} else if (source instanceof CommandLineSource commandLineSource) {
			return new WinCommandLineSourceProcessor(winCommandService, configurationRetriever, connectorId)
				.process(commandLineSource, telemetryManager);
		}

		throw new IllegalArgumentException(
			String.format(
				"Hostname %s - Cannot process source %s.",
				telemetryManager.getHostname(),
				source != null ? source.getClass().getSimpleName() : ""
			)
		);
	}

	@Override
	public boolean isSupportedConfigurationType(String configurationType) {
		return "wmi".equalsIgnoreCase(configurationType);
	}

	@Override
	public IConfiguration buildConfiguration(
		@NonNull String configurationType,
		@NonNull JsonNode jsonNode,
		UnaryOperator decrypt
	) throws InvalidConfigurationException {
		try {
			final WmiConfiguration wmiConfiguration = newObjectMapper().treeToValue(jsonNode, WmiConfiguration.class);

			if (decrypt != null) {
				final char[] password = wmiConfiguration.getPassword();
				if (password != null) {
					// Decrypt the password
					wmiConfiguration.setPassword(decrypt.apply(password));
				}
			}

			return wmiConfiguration;
		} catch (Exception e) {
			final String errorMessage = String.format(
				"Error while reading WMI Configuration: %s. Error: %s",
				jsonNode,
				e.getMessage()
			);
			log.error(errorMessage);
			log.debug("Error while reading WMI Configuration: {}. Stack trace:", jsonNode, e);
			throw new InvalidConfigurationException(errorMessage, e);
		}
	}

	/**
	 * Creates and configures a new instance of the Jackson ObjectMapper for handling YAML data.
	 *
	 * @return A configured ObjectMapper instance.
	 */
	public static JsonMapper newObjectMapper() {
		return JsonMapper
			.builder(new YAMLFactory())
			.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
			.enable(SerializationFeature.INDENT_OUTPUT)
			.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
			.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
			.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false)
			.build();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy