Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions container/features/src/main/resources/features.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1753,6 +1753,7 @@
<feature>opennms-graph-provider-bsm</feature>
<feature>opennms-graph-provider-application</feature>
<feature>opennms-graph-provider-legacy</feature>
<feature>opennms-graph-provider-pathoutage</feature>
<feature>opennms-graph-provider-topology</feature>
</feature>
<feature name="opennms-graph-api" description="OpenNMS :: Features :: Graph :: API" version="${project.version}">
Expand Down Expand Up @@ -1815,6 +1816,11 @@
<feature>opennms-topology-api</feature>
<bundle>mvn:org.opennms.features.graph.provider/org.opennms.features.graph.provider.legacy/${project.version}</bundle>
</feature>
<feature name="opennms-graph-provider-pathoutage" description="OpenNMS :: Features :: Graph :: Provider :: Path Outage" version="${project.version}">
<feature>opennms-graph-service</feature>
<feature>opennms-model</feature>
<bundle>mvn:org.opennms.features.graph.provider/org.opennms.features.graph.provider.pathoutage/${project.version}</bundle>
</feature>
<feature name="opennms-graph-provider-topology" description="OpenNMS :: Features :: Graph :: Provider :: Topology" version="${project.version}">
<feature>opennms-model</feature>
<feature>opennms-graph-service</feature>
Expand Down
129 changes: 129 additions & 0 deletions features/graph/provider/pathoutage/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.opennms.features.graph</groupId>
<artifactId>org.opennms.features.graph.provider</artifactId>
<version>36.0.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.opennms.features.graph.provider</groupId>
<artifactId>org.opennms.features.graph.provider.pathoutage</artifactId>
<name>OpenNMS :: Features :: Graph :: Provider :: Path Outage</name>
<description>Builds a graph from the node parent / critical-path (path outage) relationships.</description>
<packaging>bundle</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.core</artifactId>
<version>${osgiVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.cmpn</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.opennms.features.graph</groupId>
<artifactId>org.opennms.features.graph.api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.opennms</groupId>
<artifactId>opennms-dao-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.opennms</groupId>
<artifactId>opennms-model</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<!-- The IT runs the real Hibernate DAO context against a temporary
database (same harness as the legacy path-outage plugin's ITs). -->
<dependency>
<groupId>org.opennms</groupId>
<artifactId>opennms-config</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.opennms</groupId>
<artifactId>opennms-dao</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.opennms.core.test-api</groupId>
<artifactId>org.opennms.core.test-api.lib</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.opennms.core.test-api</groupId>
<artifactId>org.opennms.core.test-api.db</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.opennms.core.test-api</groupId>
<artifactId>org.opennms.core.test-api.services</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.opennms</groupId>
<artifactId>opennms-rrdtool-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.opennms.features.collection</groupId>
<artifactId>org.opennms.features.collection.persistence.osgi</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.opennms.features.collection</groupId>
<artifactId>org.opennms.features.collection.persistence.rrd</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.opennms.dependencies</groupId>
<artifactId>jrrd2-dependencies</artifactId>
<type>pom</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.opennms.features.config</groupId>
<artifactId>org.opennms.features.config.mock</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.opennms.features.config</groupId>
<artifactId>org.opennms.features.config.upgrade</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* 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.netmgt.graph.provider.pathoutage;

import java.util.LinkedHashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.opennms.netmgt.dao.api.NodeDao;
import org.opennms.netmgt.dao.api.SessionUtils;
import org.opennms.netmgt.graph.api.ImmutableGraph;
import org.opennms.netmgt.graph.api.generic.GenericEdge;
import org.opennms.netmgt.graph.api.generic.GenericGraph;
import org.opennms.netmgt.graph.api.generic.GenericProperties;
import org.opennms.netmgt.graph.api.generic.GenericVertex;
import org.opennms.netmgt.graph.api.info.DefaultGraphInfo;
import org.opennms.netmgt.graph.api.info.GraphInfo;
import org.opennms.netmgt.graph.api.service.GraphProvider;
import org.opennms.netmgt.model.OnmsNode;

import com.google.common.collect.Maps;

/**
* Builds a read-only graph from the node parent / critical-path relationships
* ({@link OnmsNode#getParent()} -- the {@code nodeParentID} column), the same
* data the legacy Vaadin "Path Outage" topology renders. Each participating
* node becomes a vertex and each parent points to its child with a directed
* edge, so the result is the node-parent forest (one tree per top-level node).
*
* <p>Only nodes that take part in a parent-child relationship are included --
* a node with neither a parent nor any children would render as an isolated
* dot that conveys nothing here. On an installation where no node parents are
* set the graph is empty, and the UI shows its discovered-empty state.</p>
*
* <p>Registered as a bare {@link GraphProvider}; the graph service wraps it in
* a single-graph container whose id defaults to the namespace, so it is served
* at {@code /api/v2/graphs/pathoutage/pathoutage}.</p>
*/
public class PathOutageGraphProvider implements GraphProvider {

public static final String NAMESPACE = "pathoutage";
private static final String LABEL = "Path Outage";
private static final String DESCRIPTION =
"Node parent / critical-path hierarchy derived from each node's parent relationship.";

private final NodeDao nodeDao;
private final SessionUtils sessionUtils;

public PathOutageGraphProvider(final NodeDao nodeDao, final SessionUtils sessionUtils) {
this.nodeDao = Objects.requireNonNull(nodeDao);
this.sessionUtils = Objects.requireNonNull(sessionUtils);
}

@Override
public ImmutableGraph<?, ?> loadGraph() {
// OnmsNode.getParent() is a LAZY @ManyToOne, so the whole build -- including
// reading each parent's id/label -- must run inside a read transaction.
return sessionUtils.withReadOnlyTransaction(() -> {
final GenericGraph.GenericGraphBuilder builder = GenericGraph.builder()
.graphInfo(getGraphInfo())
.id(NAMESPACE)
// Resolve node + alarm-based status for the vertices, like the other
// node-backed providers; the SPA also colors by nodeID independently.
.property(GenericProperties.Enrichment.RESOLVE_NODES, true)
.property(GenericProperties.Enrichment.DEFAULT_STATUS, true);

final List<OnmsNode> nodes = nodeDao.findAll();

// First pass: collect the participating node ids (children with a parent,
// plus those parents) and their labels, and remember the parent->child pairs.
final Map<Integer, String> labelById = Maps.newHashMap();
final Set<Integer> participating = new LinkedHashSet<>();
// child -> parent: value-based (unlike a Set of arrays), and a child
// has exactly one parent, so duplicates are structurally impossible.
final Map<Integer, Integer> parentByChild = new LinkedHashMap<>();
for (final OnmsNode node : nodes) {
final OnmsNode parent = node.getParent();
if (parent == null) {
continue;
}
final int childId = node.getId();
final int parentId = parent.getId();
labelById.put(childId, node.getLabel());
labelById.put(parentId, parent.getLabel());
participating.add(childId);
participating.add(parentId);
parentByChild.put(childId, parentId);
}

// Vertices first (an edge requires its endpoints to already be present).
for (final Integer nodeId : participating) {
builder.addVertex(GenericVertex.builder()
.namespace(NAMESPACE)
.id(String.valueOf(nodeId))
.property(GenericProperties.LABEL, labelById.get(nodeId))
.property(GenericProperties.NODE_ID, String.valueOf(nodeId))
.build());
}

// No explicit edge id: GenericEdge derives a deterministic one from
// source->target, stable across loads (a counter would depend on
// NodeDao.findAll() ordering).
for (final Map.Entry<Integer, Integer> pc : parentByChild.entrySet()) {
builder.addEdge(GenericEdge.builder()
.namespace(NAMESPACE)
.source(NAMESPACE, String.valueOf(pc.getValue()))
.target(NAMESPACE, String.valueOf(pc.getKey()))
.build());
}

// Show the whole hierarchy by default; the SPA frames it to fit.
builder.focus().all().apply();
return builder.build();
});
}

@Override
public GraphInfo getGraphInfo() {
final DefaultGraphInfo graphInfo = new DefaultGraphInfo(NAMESPACE);
graphInfo.setLabel(LABEL);
graphInfo.setDescription(DESCRIPTION);
return graphInfo;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 https://osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">

<reference id="nodeDao" interface="org.opennms.netmgt.dao.api.NodeDao" availability="mandatory"/>
<reference id="sessionUtils" interface="org.opennms.netmgt.dao.api.SessionUtils" availability="mandatory"/>

<bean id="pathOutageGraphProvider" class="org.opennms.netmgt.graph.provider.pathoutage.PathOutageGraphProvider">
<argument ref="nodeDao"/>
<argument ref="sessionUtils"/>
</bean>

<!-- Registered as a bare GraphProvider; the graph service wraps it into a
single-graph container whose id defaults to the namespace ("pathoutage").
Without a cacheInvalidateInterval the graph service caches the first load
forever; parent relationships change with provisioning, so expire like the
other node-backed providers do. -->
<service ref="pathOutageGraphProvider" interface="org.opennms.netmgt.graph.api.service.GraphProvider">
<service-properties>
<entry key="cacheInvalidateInterval" value="300"/> <!-- Seconds -->
</service-properties>
</service>

</blueprint>
Loading
Loading