Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/release-33.x' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
opennms-bamboo committed Jan 16, 2025
2 parents aa0a6c0 + e88ecb2 commit 120f890
Show file tree
Hide file tree
Showing 13 changed files with 385 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,20 @@ See `monitored-services.proto` in the corresponding source distribution for the
$ ssh -p 8101 admin@localhost
...
admin@opennms()> config:edit org.opennms.features.grpc.exporter
admin@opennms()> config:property-set host bsm.onmshs.local:1440 <1>
admin@opennms()> config:property-set tls.cert.path /opt/opennms/etc/tls.cert <2>
admin@opennms()> config:property-set tls.enabled false <3>
admin@opennms()> config:property-set snapshot.interval 3600 false <4>
admin@opennms()> config:property-set host bsm.onmshs.local:1443 <1>
admin@opennms()> config:property-set tenant.id opennms-prime <2>
admin@opennms()> config:property-set tls.cert.path /opt/opennms/etc/tls.cert <3>
admin@opennms()> config:property-set tls.enabled false <4>
admin@opennms()> config:property-set snapshot.interval 3600 <5>
admin@opennms()> config:update
----

<1> Set the hostname of the external gRPC application.
<2> Configure the path to the TLS certificate.
<3> TLS is enabled by default. For testing purposes, it can be disabled by setting this value to false.
<4> Set the interval (in seconds) at which the complete snapshot of services will be sent to the gRPC server.
<2> Set tenant id for the data being sent, defaults to `opennms-prime`
<3> Configure the path to the TLS certificate.
<4> TLS is enabled by default. For testing purposes, it can be disabled by setting this value to false.
<5> Set the interval (in seconds) at which the complete snapshot of services will be sent to the gRPC server,
defaults to 3600 secs.

== Enable gRPC Exporter

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,9 @@ public List<Event> evaluateAndCreateEvents(CollectionResourceWrapper resource, M
// This reference contains the function that will be used by each evaluator to retrieve the status
AtomicReference<EvaluateFunction> evaluateFunctionRef = new AtomicReference<>(null);

// compute scope here, see NMS-16966
final Scope scope = getScopeForResource(resource);

// Depending on the type of threshold, we want to evaluate it differently
// Threshold Values like value, rearm, trigger and expression are interpolated and cached in state so that
// subsequent evaluations don't need to do any interpolation.
Expand All @@ -250,7 +253,6 @@ public void visit(ThresholdConfigWrapper thresholdConfigWrapper) {
ThresholdValuesSupplier thresholdValuesSupplier = new ThresholdValuesSupplier() {
@Override
public ThresholdEvaluatorState.ThresholdValues get() {
Scope scope = getScopeForResource(resource);
ThresholdEvaluatorState.ThresholdValues thresholdValues = thresholdConfigWrapper.interpolateThresholdValues(scope);
thresholdValues.setDsValue(computedValue);
return thresholdValues;
Expand All @@ -272,8 +274,6 @@ public void visit(ExpressionConfigWrapper expressionConfigWrapper) {
// also retrieve the interpolated expression so we can persist it in our state going forward
@Override
public ExpressionConfigWrapper.ExpressionThresholdValues get() throws ThresholdExpressionException {

Scope scope = getScopeForResource(resource);
return expressionConfigWrapper
.interpolateAndEvaluate(values, scope);
}
Expand Down Expand Up @@ -311,24 +311,28 @@ public double get(String evaluatedExpression) throws ThresholdExpressionExceptio
return events;
}

public Scope getScopeForResource(CollectionResourceWrapper resource) {
public static Scope getScopeForResource(EntityScopeProvider entityScopeProvider, CollectionResourceWrapper resource) {
// Default to empty scopes and then attempt to populate each of node, interface, and service
// scopes below
Scope[] scopes = new Scope[]{EmptyScope.EMPTY, EmptyScope.EMPTY, EmptyScope.EMPTY};

if (resource != null) {
scopes[0] = m_entityScopeProvider.getScopeForNode(resource.getNodeId());
scopes[0] = entityScopeProvider.getScopeForNode(resource.getNodeId());
String interfaceIp = resource.getHostAddress();
if (interfaceIp != null) {
scopes[1] = m_entityScopeProvider.getScopeForInterface(resource.getNodeId(),
scopes[1] = entityScopeProvider.getScopeForInterface(resource.getNodeId(),
interfaceIp);
scopes[2] = m_entityScopeProvider.getScopeForService(resource.getNodeId(),
scopes[2] = entityScopeProvider.getScopeForService(resource.getNodeId(),
InetAddresses.forString(interfaceIp), resource.getServiceName());
}
}
return new FallbackScope(scopes);
}

public Scope getScopeForResource(CollectionResourceWrapper resource) {
return getScopeForResource(m_entityScopeProvider, resource);
}

/**
* <p>addThreshold</p>
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,9 @@ protected final List<Event> applyThresholds(CollectionResourceWrapper resourceWr
LOG.debug("applyThresholds: Ignoring resource {} because required attributes map is empty.", resourceWrapper);
return eventsList;
}
// compute scope here, see NMS-16966
final var scope = ThresholdEntity.getScopeForResource(m_entityScopeProvider, resourceWrapper);

LOG.debug("applyThresholds: Applying thresholds on {} using {} attributes.", resourceWrapper, attributesMap.size());
Date date = new Date();
synchronized(m_thresholdGroups) {
Expand Down Expand Up @@ -285,8 +288,6 @@ protected final List<Event> applyThresholds(CollectionResourceWrapper resourceWr
if(!valueMissing || relaxed) {
LOG.info("applyThresholds: All attributes found for {}, evaluating", resourceWrapper);

final var scope = thresholdEntity.getScopeForResource(resourceWrapper);

resourceWrapper.setDsLabel(Interpolator.interpolate(thresholdEntity.getDatasourceLabel(), scope).output);
try {
List<Event> thresholdEvents = thresholdEntity.evaluateAndCreateEvents(resourceWrapper, values, date);
Expand Down Expand Up @@ -314,6 +315,9 @@ protected boolean passedThresholdFilters(CollectionResourceWrapper resource, Thr
return false;
}

// compute scope here, see NMS-16966
final var scope = ThresholdEntity.getScopeForResource(m_entityScopeProvider, resource);

// Find the filters for threshold definition for selected group/dataSource
final List<ResourceFilter> filters = thresholdEntity.getThresholdConfig().getBasethresholddef().getResourceFilters();
if (filters.size() == 0) return true;
Expand All @@ -326,8 +330,6 @@ protected boolean passedThresholdFilters(CollectionResourceWrapper resource, Thr
LOG.debug("passedThresholdFilters: filter #{}: field={}, regex='{}'", count, f.getField(), f.getContent().orElse(null));
count++;

final var scope = thresholdEntity.getScopeForResource(resource);

// Read Resource Attribute and apply filter rules if attribute is not null
String attr = resource.getFieldValue(Interpolator.interpolate(f.getField(), scope).output);
if (attr != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
package org.opennms.features.grpc.exporter;

import com.google.protobuf.Empty;
import io.grpc.ClientInterceptor;
import io.grpc.ConnectivityState;
import io.grpc.ManagedChannel;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
Expand All @@ -49,7 +50,6 @@ public class GrpcExporterClient {
public static final String FOREIGN_TYPE = "OpenNMS";

private final String host;
private final Integer port;
private final String tlsCertPath;

private final boolean tlsEnabled;
Expand All @@ -63,27 +63,23 @@ public class GrpcExporterClient {
private final AtomicBoolean reconnecting = new AtomicBoolean(false);
private final AtomicBoolean stopped = new AtomicBoolean(false);
private Callback inventoryCallback;
private final ClientInterceptor clientInterceptor;

public GrpcExporterClient(final String host,
final Integer port,
final String tlsCertPath,
final boolean tlsEnabled) {
final boolean tlsEnabled,
final ClientInterceptor clientInterceptor) {
this.host = Objects.requireNonNull(host);
this.port = Objects.requireNonNull(port);
this.tlsCertPath = tlsCertPath;
this.tlsEnabled = tlsEnabled;
this.clientInterceptor = clientInterceptor;
this.scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("grpc-exporter-connect"));
}

public void start() throws SSLException {
final NettyChannelBuilder channelBuilder;
if (this.port > 0) {
channelBuilder = NettyChannelBuilder.forAddress(this.host, this.port)
final NettyChannelBuilder channelBuilder = NettyChannelBuilder.forTarget(this.host)
.intercept(clientInterceptor)
.keepAliveWithoutCalls(true);
} else {
channelBuilder = NettyChannelBuilder.forTarget(this.host)
.keepAliveWithoutCalls(true);
}

if (tlsEnabled && tlsCertPath != null && !tlsCertPath.isBlank()) {
channel = channelBuilder.useTransportSecurity()
Expand All @@ -104,7 +100,7 @@ public void start() throws SSLException {

this.monitoredServiceSyncStub = ServiceSyncGrpc.newStub(this.channel);
connectStreams();
LOG.info("GrpcExporterClient started connection to {}:{}", this.host, this.port);
LOG.info("GrpcExporterClient started connection to {}", this.host);
}

public void stop() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Licensed to The OpenNMS Group, Inc (TOG) under one or more
* contributor license agreements. See the LICENSE.md file
* distributed with this work for additional information
* regarding copyright ownership.
*
* TOG licenses this file to You under the GNU Affero General
* Public License Version 3 (the "License") or (at your option)
* any later version. You may not use this file except in
* compliance with the License. You may obtain a copy of the
* License at:
*
* https://www.gnu.org/licenses/agpl-3.0.txt
*
* 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.
*/

package org.opennms.features.grpc.exporter;

import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.ForwardingClientCall;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;

public class GrpcHeaderInterceptor implements ClientInterceptor {

private final Metadata metadata;

public GrpcHeaderInterceptor(String tenantId) {
metadata = new Metadata();
metadata.put(Metadata.Key.of("tenant-id", Metadata.ASCII_STRING_MARSHALLER), tenantId);
}

@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
return new HeaderAttachingClientCall<>(next.newCall(method, callOptions), metadata);
}

private final static class HeaderAttachingClientCall<ReqT, RespT>
extends ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT> {

private final Metadata metadata;
HeaderAttachingClientCall(ClientCall<ReqT, RespT> call, Metadata metadataToAttach) {
super(call);
this.metadata = metadataToAttach;
}

@Override
public void start(Listener<RespT> responseListener, Metadata headers) {
headers.merge(metadata);
super.start(responseListener, headers);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
<cm:property-placeholder id="grpcExporterProperties" persistent-id="org.opennms.features.grpc.exporter"
update-strategy="reload">
<cm:default-properties>
<cm:property name="host" value="bsm.onmshs.local"/>
<cm:property name="port" value="1443"/>
<cm:property name="host" value="bsm.onmshs.local:1443"/>
<cm:property name="snapshot.interval" value="3600"/>
<cm:property name="tls.enabled" value="true"/>
<cm:property name="tls.cert.path" value=""/>
<cm:property name="tenant.id" value="opennms-prime"/>
</cm:default-properties>
</cm:property-placeholder>

Expand All @@ -24,9 +24,13 @@

<bean id="grpcExporterClient" class="org.opennms.features.grpc.exporter.GrpcExporterClient" init-method="start" destroy-method="stop">
<argument value="${host}"/>
<argument value="${port}"/>
<argument value="${tls.cert.path}"/>
<argument value="${tls.enabled}"/>
<argument ref="grpcClientInterceptor"/>
</bean>

<bean id="grpcClientInterceptor" class="org.opennms.features.grpc.exporter.GrpcHeaderInterceptor">
<argument value="${tenant.id}"/>
</bean>

<bean id="inventoryService" class="org.opennms.features.grpc.exporter.InventoryService" init-method="start" destroy-method="stop" >
Expand Down
21 changes: 20 additions & 1 deletion features/poller/api/pom.xml
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,24 @@
<artifactId>hibernate-jpa-2.0-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.core</artifactId>
<version>${eclipselinkVersion}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.moxy</artifactId>
<version>${eclipselinkVersion}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
</project>
2 changes: 2 additions & 0 deletions features/poller/api/src/main/java/org/opennms/netmgt/poller/DeviceConfig.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement(name="device-config")
@XmlAccessorType(XmlAccessType.NONE)
Expand All @@ -37,6 +38,7 @@ public class DeviceConfig {
private String filename;

@XmlAttribute(name="scriptOutput")
@XmlJavaTypeAdapter(EscapeSequenceAdapter.class)
private String scriptOutput;

public DeviceConfig(String scriptOutput) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2025 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2025 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) 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.
*
* OpenNMS(R) 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <[email protected]>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/

package org.opennms.netmgt.poller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class EscapeSequenceAdapter extends XmlAdapter<String, String> {
private static final Logger LOG = LoggerFactory.getLogger(EscapeSequenceAdapter.class);

public EscapeSequenceAdapter() {

}

@Override
public String unmarshal(String v) throws Exception {
if (v != null) {
v = v.replace("&#xd;", "\r")
.replace("&#xa;", "\n");
}
return v;

}

@Override
public String marshal(String v) throws Exception {
if (v != null) {
v = v.replace("\r", "&#xd;")
.replace("\n", "&#xa;");
}
return v;

}
}
Loading

0 comments on commit 120f890

Please sign in to comment.