Info Panel Items

This section is under development. Examples or code snippets may not fully work. However, they are conceptionally correct and should point in the right direction.

An InfoPanelItem object defines dach element in the Info Panel.

All available InfoPanelItem objects are sorted by the order. This allows you to arrange the items in a custom order. After ordering the elements, they are put below the SearchBox and the Vertices in Focus list.

Programmatic

To add items to the Info Panel in the Topology UI, implement the interface InfoPanelItemProvider and expose its implementation via OSGi.

Simple Java InfoPanelItemProvider
public class ExampleInfoPanelItemProvider implements InfoPanelItemProvider {
    @Override
    public Collection<? extends InfoPanelItem> getContributions(GraphContainer container) {
        return Collections.singleton(
                new DefaultInfoPanelItem() (1)
                    .withTitle("Static information") (2)
                    .withOrder(0) (3)
                    .withComponent(
                        new com.vaadin.v7.ui.Label("I am a static component") (4)
                    )
        );
    }
}
1 The default implementation of InfoPanelItem. You may use InfoPanelItem instead if the default implementation is not sufficient.
2 The title of the InfoPanelItem. It is shown above the component.
3 The order.
4 A Vaadin component that actually describes the custom component.

To show information based on a selected vertex or edge, one must inherit the classes EdgeInfoPanelItemProvider or VertexInfoPanelItemProvider. The following example shows a custom EdgeInfoPanelItemProvider.

Simple Java EdgeInfoPanelItemProvider
public class ExampleEdgeInfoPanelItemProvider extends EdgeInfoPanelItemProvider {
    @Override
    protected boolean contributeTo(EdgeRef ref, GraphContainer graphContainer) { (1)
        return "custom-namespace".equals(ref.getNamespace()); // only show if of certain namespace
    }

    @Override
    protected InfoPanelItem createInfoPanelItem(EdgeRef ref, GraphContainer graphContainer) { (2)
        return new DefaultInfoPanelItem()
                .withTitle(ref.getLabel() + " Info")
                .withOrder(0)
                .withComponent(
                        new com.vaadin.v7.ui.Label("Id: " + ref.getId() + ", Namespace: " + ref.getNamespace())
                );
    }
}
1 Is invoked if one and only one edge is selected. It determines if the current edge should provide the InfoPanelItem created by createInfoPanelItem.
2 Is invoked if one and only one edge is selected. It creates the InfoPanelItem to show for the selected edge.

Implementing the provided interfaces/classes is not enough to have it show up. It must also be exposed via a blueprint.xml to the OSGi service registry. The following blueprint.xml snippet describes how to expose any custom InfoPanelItemProvider implementation to the OSGi service registry and have the Topology UI pick it up.

blueprint.xml snippet
<service interface="org.opennms.features.topology.api.info.InfoPanelItemProvider"> (1)
    <bean class="ExampleInfoPanelItemProvider" /> (2)
</service>
1 The service definition must always point to InfoPanelItemProvider.
2 The bean implementing the defined interface.

Scriptable

By simply dropping Jinjava templates (with file extension .html) to ${OPENNMS_HOME}/etc/infopanel a more scriptable approach is available. For more information on Jinjava refer to https://github.com/HubSpot/jinjava.

The following example describes a very simple Jinjava template which is always visible.

Static scriptable template
{% set visible = true %}  (1)
{% set title = "Static information" %} (2)
{% set order = -700 %} (3)

This information is always visible (4)
1 Makes this always visible
2 Defines the title
3 Each info panel item is ordered at the end. Setting it as -700 makes it very likely to pin this to the top of the info panel item.

A template showing custom information may look similar to the following:

Vertex specific template
{% set visible = vertex != null && vertex.namespace == "custom" && vertex.customProperty is defined %} (1)
{% set title = "Custom Information" %}

<table width="100%" border="0">
    <tr>
        <td colspan="3">This information is only visible if a vertex with namespace "custom" is selected</td>
    </tr>
    <tr>
        <td align="right" width="80">Custom Property</td>
        <td width="14"></td>
        <td align="left">{{ vertex.customProperty }}</td>
    </tr>
</table>
1 This template is shown only if a vertex is selected and the selected namespace is "custom".

It is also possible to show performance data.

Including resource graphs

Use the following HTML element to include resource graphs into the info panel:

<div class="graph-container" data-resource-id="RESOURCE_ID" data-graph-name="GRAPH_NAME"></div>

Use optional attributes data-graph-start and data-graph-end to specify the displayed time range in seconds since epoch.

Measurements API template (memory usage)
{# Example template for a simple memory statistic provided by the netsnmp agent #}
{% set visible = node != null && node.sysObjectId == ".1.3.6.1.4.1.8072.3.2.10" %}
{% set order = 110 %}

{# Setting the title #}
{% set title = "System Memory" %}

{# Define resource Id to be used #}
{% set resourceId = "node[" + node.id + "].nodeSnmp[]" %}

{# Define attribute Id to be used #}
{% set attributeId = "hrSystemUptime" %}

{% set total = measurements.getLastValue(resourceId, "memTotalReal")/1000/1024 %}
{% set avail = measurements.getLastValue(resourceId, "memAvailReal")/1000/1024 %}

<table border="0" width="100%">
    <tr>
        <td width="80" align="right" valign="top">Total</td>
        <td width="14"></td>
        <td align="left" valign="top" colspan="2">
            {{ total|round(2) }} GB(s)
        </td>
    </tr>
    <tr>
        <td width="80" align="right" valign="top">Used</td>
        <td width="14"></td>
        <td align="left" valign="top" colspan="2">
            {{ (total-avail)|round(2) }} GB(s)
        </td>
    </tr>
    <tr>
        <td width="80" align="right" valign="top">Available</td>
        <td width="14"></td>
        <td align="left" valign="top" colspan="2">
            {{ avail|round(2) }} GB(s)
        </td>
    </tr>
    <tr>
        <td width="80" align="right" valign="top">Usage</td>
        <td width="14"></td>
        <td align="left" valign="top">
            <meter style="width:100%" min="0" max="{{ total }}" low="{{ 0.5*total }}" high="{{ 0.8*total }}" value="{{ total-avail }}" optimum="0"/>
        </td>
        <td width="1">
            &nbsp;{{ ((total-avail)/total*100)|round(2) }}%
        </td>
    </tr>
</table>
Measurements API template (uptime)
{# Example template for the system uptime provided by the netsnmp agent #}
{% set visible = node != null && node.sysObjectId == ".1.3.6.1.4.1.8072.3.2.10" %}
{% set order = 100 %}

{# Setting the title #}
{% set title = "System Uptime" %}

{# Define resource Id to be used #}
{% set resourceId = "node[" + node.id + "].nodeSnmp[]" %}

{# Define attribute Id to be used #}
{% set attributeId = "hrSystemUptime" %}

<table border="0" width="100%">
    <tr>
        <td width="80" align="right" valign="top">getLastValue()</td>
        <td width="14"></td>
        <td align="left" valign="top">
            {# Querying the last value via the getLastValue() method: #}

            {% set last = measurements.getLastValue(resourceId, attributeId)/100.0/60.0/60.0/24.0 %}
            {{ last|round(2) }} day(s)
        </td>
    </tr>
    <tr>
        <td width="80" align="right" valign="top">query()</td>
        <td width="14"></td>
        <td align="left" valign="top">
            {# Querying the last value via the query() method. A custom function 'currentTimeMillis()' in
            the namespace 'System' is used to get the timestamps for the query: #}

            {% set end = System:currentTimeMillis() %}
            {% set start = end - (15 * 60 * 1000) %}

            {% set values = measurements.query(resourceId, attributeId, start, end, 300000, "AVERAGE") %}

            {# Iterating over the values in reverse order and grab the first value which is not NaN #}
            {% set last = "NaN" %}
            {% for value in values|reverse %}
                {%- if value != "NaN" && last == "NaN" %}
                    {{ (value/100.0/60.0/60.0/24.0)|round(2) }} day(s)
                    {% set last = value %}
                {% endif %}
            {%- endfor %}
        </td>
    </tr>
    <tr>
        <td width="80" align="right" valign="top">Graph</td>
        <td width="14"></td>
        <td align="left" valign="top">
            {# We use the start and end variable here to construct the graph's Url: #}

            <img src="/opennms/graph/graph.png?resourceId=node[{{ node.id }}].nodeSnmp[]&report=netsnmp.hrSystemUptime&start={{ start }}&end={{ end }}&width=170&height=30"/>
        </td>
    </tr>
</table>