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

org.sentrysoftware.metricshub.extension.wbem.WbemRequestExecutor Maven / Gradle / Ivy

Go to download

MetricsHub WBEM Extension implementation providing a set of functionalities used to process WBEM criterion and source.

There is a newer version: 0.9.07
Show newest version
package org.sentrysoftware.metricshub.extension.wbem;

/*-
 * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
 * MetricsHub Wbem 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 io.opentelemetry.instrumentation.annotations.SpanAttribute;
import io.opentelemetry.instrumentation.annotations.WithSpan;
import java.net.URI;
import java.net.URL;
import java.util.List;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.sentrysoftware.metricshub.engine.common.exception.ClientException;
import org.sentrysoftware.metricshub.engine.common.helpers.LoggingHelper;
import org.sentrysoftware.metricshub.engine.common.helpers.TextTableHelper;
import org.sentrysoftware.metricshub.engine.common.helpers.ThreadHelper;
import org.sentrysoftware.metricshub.engine.telemetry.TelemetryManager;
import org.sentrysoftware.vcenter.VCenterClient;
import org.sentrysoftware.wbem.client.WbemExecutor;
import org.sentrysoftware.wbem.client.WbemQueryResult;
import org.sentrysoftware.wbem.javax.wbem.WBEMException;

@Slf4j
public class WbemRequestExecutor {

	/**
	 * Invalid namespace Exception. Thrown when the specified namespace does not
	 * exist.
	 */
	public static final int CIM_ERR_INVALID_NAMESPACE = 3;

	/**
	 * Invalid class specified. For e.g. when one tries to add an instance for a
	 * class that does not exist. This error message uses one parameter, the
	 * invalid class name.
	 */
	public static final int CIM_ERR_INVALID_CLASS = 5;

	/**
	 * Element cannot be found. This error message uses one parameter, the
	 * element that cannot be found.
	 */
	public static final int CIM_ERR_NOT_FOUND = 6;

	/**
	 * Determine if a vCenter server is configured and call the appropriate method to run the WBEM query.
	 * 
* * @param hostname Hostname * @param wbemConfig WBEM Protocol configuration, incl. credentials * @param query WQL query to execute * @param namespace WBEM namespace * @return A table (as a {@link List} of {@link List} of {@link String}s) * resulting from the execution of the query. * @throws ClientException when anything goes wrong (details in cause) */ @WithSpan("WBEM") public List> executeWbem( @NonNull @SpanAttribute("host.hostname") final String hostname, @NonNull @SpanAttribute("wbem.config") final WbemConfiguration wbemConfig, @NonNull @SpanAttribute("wbem.query") final String query, @NonNull @SpanAttribute("wbem.namespace") final String namespace, @NonNull final TelemetryManager telemetryManager ) throws ClientException { // handle vCenter case if (wbemConfig.getVCenter() != null) { return doVCenterQuery(hostname, wbemConfig, query, namespace, telemetryManager); } else { return doWbemQuery(hostname, wbemConfig, query, namespace); } } /** * Perform a WBEM query using vCenter ticket authentication. *
* * @param hostname Hostname * @param wbemConfig WBEM Protocol configuration, incl. credentials * @param query WQL query to execute * @param namespace WBEM namespace * @return A table (as a {@link List} of {@link List} of {@link String}s) * resulting from the execution of the query. * @throws ClientException when anything goes wrong (details in cause) */ private List> doVCenterQuery( @NonNull final String hostname, @NonNull final WbemConfiguration wbemConfig, @NonNull final String query, @NonNull final String namespace, @NonNull final TelemetryManager telemetryManager ) throws ClientException { String ticket = telemetryManager.getHostProperties().getVCenterTicket(); if (ticket == null) { ticket = refreshVCenterTicket( wbemConfig.getVCenter(), wbemConfig.getUsername(), wbemConfig.getPassword(), hostname, wbemConfig.getTimeout() ); } final WbemConfiguration vCenterWbemConfig = WbemConfiguration .builder() .username(ticket) .password(ticket.toCharArray()) .namespace(wbemConfig.getNamespace()) .port(wbemConfig.getPort()) .protocol(wbemConfig.getProtocol()) .timeout(wbemConfig.getTimeout()) .build(); try { return doWbemQuery(hostname, vCenterWbemConfig, query, namespace); } catch (ClientException e) { if (isRefreshTicketNeeded(e.getCause())) { ticket = refreshVCenterTicket( wbemConfig.getVCenter(), wbemConfig.getUsername(), wbemConfig.getPassword(), hostname, wbemConfig.getTimeout() ); vCenterWbemConfig.setUsername(ticket); vCenterWbemConfig.setPassword(ticket.toCharArray()); return doWbemQuery(hostname, vCenterWbemConfig, query, namespace); } else { throw e; } } finally { telemetryManager.getHostProperties().setVCenterTicket(ticket); } } /** * Perform a query to a vCenterServer in order to obtain an authentication ticket. *
* * @param vCenter vCenter server FQDN or IP * @param username Username * @param password Password * @param hostname Hostname * @param timeout Timeout * @return A ticket String * @throws ClientException when anything goes wrong (details in cause) */ private String refreshVCenterTicket( @NonNull String vCenter, @NonNull String username, @NonNull char[] password, @NonNull String hostname, @NonNull Long timeout ) throws ClientException { VCenterClient.setDebug(() -> true, log::debug); try { String ticket = ThreadHelper.execute( () -> VCenterClient.requestCertificate(vCenter, username, new String(password), hostname), timeout ); if (ticket == null) { throw new ClientException("Cannot get the ticket through vCenter module"); } return ticket; } catch (Exception e) { if (e instanceof InterruptedException) { Thread.currentThread().interrupt(); } log.error("Hostname {} - Vcenter ticket refresh query failed. Exception: {}", e); throw new ClientException("vCenter refresh ticket query failed on " + hostname + ".", e); } } /** * Assess whether the exception (or any of its causes) is an access denied error saying that we must refresh the vCenter ticket. *
* * @param t Exception to verify * @return whether specified exception tells us that the ticket needs to be refreshed */ private static boolean isRefreshTicketNeeded(Throwable t) { if (t == null) { return false; } if (t instanceof WBEMException wbemException) { final int cimErrorType = wbemException.getID(); return cimErrorType == WBEMException.CIM_ERR_ACCESS_DENIED; } // Now check recursively the cause return isRefreshTicketNeeded(t.getCause()); } /** * Perform a WBEM query. *
* * @param hostname Hostname * @param wbemConfig WBEM Protocol configuration, incl. credentials * @param query WQL query to execute * @param namespace WBEM namespace * @return A table (as a {@link List} of {@link List} of {@link String}s) * resulting from the execution of the query. * @throws ClientException when anything goes wrong (details in cause) */ public List> doWbemQuery( final String hostname, final WbemConfiguration wbemConfig, final String query, final String namespace ) throws ClientException { try { String urlSpec = String.format("%s://%s:%d", wbemConfig.getProtocol().toString(), hostname, wbemConfig.getPort()); final URL url = new URI(urlSpec).toURL(); LoggingHelper.trace(() -> log.trace( "Executing WBEM request:\n- Hostname: {}\n- Port: {}\n- Protocol: {}\n- URL: {}\n" + // NOSONAR "- Username: {}\n- Query: {}\n- Namespace: {}\n- Timeout: {} s\n", hostname, wbemConfig.getPort(), wbemConfig.getProtocol().toString(), url, wbemConfig.getUsername(), query, namespace, wbemConfig.getTimeout() ) ); final long startTime = System.currentTimeMillis(); WbemQueryResult wbemQueryResult = WbemExecutor.executeWql( url, namespace, wbemConfig.getUsername(), wbemConfig.getPassword(), query, wbemConfig.getTimeout().intValue() * 1000, null ); final long responseTime = System.currentTimeMillis() - startTime; List> result = wbemQueryResult.getValues(); LoggingHelper.trace(() -> log.trace( "Executed WBEM request:\n- Hostname: {}\n- Port: {}\n- Protocol: {}\n- URL: {}\n" + // NOSONAR "- Username: {}\n- Query: {}\n- Namespace: {}\n- Timeout: {} s\n- Result:\n{}\n- response-time: {}\n", hostname, wbemConfig.getPort(), wbemConfig.getProtocol().toString(), url, wbemConfig.getUsername(), query, namespace, wbemConfig.getTimeout(), TextTableHelper.generateTextTable(wbemQueryResult.getProperties(), result), responseTime ) ); return result; } catch (Exception e) { // NOSONAR an exception is already thrown throw new ClientException("WBEM query failed on " + hostname + ".", e); } } /** * Assess whether an exception (or any of its causes) is simply an error saying that the * requested namespace of class doesn't exist, which is considered okay. *
* * @param t Exception to verify * @return whether specified exception is acceptable while performing namespace detection */ public boolean isAcceptableException(Throwable t) { if (t == null) { return false; } if (t instanceof WBEMException wbemException) { final int cimErrorType = wbemException.getID(); return isAcceptableWbemError(cimErrorType); } else if ( // CHECKSTYLE:OFF t instanceof org.sentrysoftware.wbem.client.exceptions.WqlQuerySyntaxException // CHECKSTYLE:ON ) { return true; } // Now check recursively the cause return isAcceptableException(t.getCause()); } /** * Whether this error id is an acceptable WBEM error. * * @param errorId integer value representing the id of the WBEM exception * @return boolean value */ private boolean isAcceptableWbemError(final int errorId) { // CHECKSTYLE:OFF return ( errorId == WBEMException.CIM_ERR_INVALID_NAMESPACE || errorId == WBEMException.CIM_ERR_INVALID_CLASS || errorId == WBEMException.CIM_ERR_NOT_FOUND ); // CHECKSTYLE:ON } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy