Provide RPC to fetch PCEP session stats 07/81107/4
authorAjay Lele <ajayslele@gmail.com>
Fri, 22 Mar 2019 01:32:49 +0000 (18:32 -0700)
committerClaudio David Gasparini <claudio.gasparini@pantheon.tech>
Mon, 8 Apr 2019 07:02:23 +0000 (07:02 +0000)
JIRA: BGPCEP-871
Change-Id: Ia457f3752a004a9abf868d8a53d7c2a46c360263
Signed-off-by: Ajay Lele <ajayslele@gmail.com>
docs/pcep/pcep-user-guide-session-statistics.rst
pcep/ietf-stateful07/src/main/yang/odl-pcep-stateful-stats.yang
pcep/topology/topology-api/src/main/yang/pcep-topology-stats-rpc.yang [new file with mode: 0644]
pcep/topology/topology-stats/pom.xml
pcep/topology/topology-stats/src/main/java/org/opendaylight/bgpcep/pcep/topology/stats/rpc/TopologyStatsRpcServiceImpl.java [new file with mode: 0644]
pcep/topology/topology-stats/src/main/resources/OSGI-INF/blueprint/pcep-stats.xml [moved from pcep/topology/topology-stats/src/main/resources/OSGI-INF/blueprint/pcep-provider-stats.xml with 80% similarity]
pcep/topology/topology-stats/src/test/java/org/opendaylight/bgpcep/pcep/topology/stats/rpc/TopologyStatsRpcServiceImplTest.java [new file with mode: 0644]

index f40236ff4067801a2d7e3748ada6cfd82ba18a23..8a6f091d02ef58fca68ce6cb9776d2699145811c 100644 (file)
@@ -112,3 +112,91 @@ Usage
 @line 36: **delegated-lsps-count** - The number of delegated LSPs (tunnels) from PCC.
 
 @line 37: **synchronized** - Represents synchronization status.
+
+
+Following RPC can be used to fetch PCEP session statistics. If PCEP topology and/or PCC node is not specified in input,
+statistics for all PCEP sessions under the context are returned.
+
+Usage
+'''''
+
+**URL:** ``/restconf/operations/pcep-topology-stats-rpc:get-stats``
+
+**Method:** ``POST``
+
+**Content-Type:** ``application/xml``
+
+**Request Body:**
+
+.. code-block:: xml
+
+   <input xmlns="urn:opendaylight:params:xml:ns:yang:pcep:topology:stats:rpc">
+      <topology>
+         <topology-id>pcep-topology</topology-id>
+         <node>
+            <node-id>pcc://43.43.43.43</node-id>
+         </node>
+      </topology>
+   </input>
+
+**Response Body:**
+
+.. code-block:: xml
+
+   <output xmlns="urn:opendaylight:params:xml:ns:yang:pcep:topology:stats:rpc">
+      <topology>
+         <topology-id>pcep-topology</topology-id>
+         <node>
+            <node-id>pcc://43.43.43.43</node-id>
+            <pcep-session-state>
+               <synchronized>true</synchronized>
+               <peer-capabilities>
+                  <stateful xmlns="urn:opendaylight:params:xml:ns:yang:pcep:stateful:stats">true</stateful>
+                  <instantiation xmlns="urn:opendaylight:params:xml:ns:yang:pcep:stateful:stats">true</instantiation>
+                  <active xmlns="urn:opendaylight:params:xml:ns:yang:pcep:stateful:stats">true</active>
+               </peer-capabilities>
+               <local-pref>
+                  <keepalive>30</keepalive>
+                  <deadtimer>120</deadtimer>
+                  <session-id>1</session-id>
+                  <ip-address>127.0.0.1</ip-address>
+               </local-pref>
+               <session-duration>4:01:59:46</session-duration>
+               <messages>
+                  <unknown-msg-received>0</unknown-msg-received>
+                  <received-msg-count>11752</received-msg-count>
+                  <error-messages>
+                     <last-sent-error>
+                        <error-type>0</error-type>
+                        <error-value>0</error-value>
+                     </last-sent-error>
+                     <received-error-msg-count>0</received-error-msg-count>
+                     <last-received-error>
+                        <error-type>0</error-type>
+                        <error-value>0</error-value>
+                     </last-received-error>
+                     <sent-error-msg-count>0</sent-error-msg-count>
+                  </error-messages>
+                  <sent-msg-count>11759</sent-msg-count>
+                  <last-sent-msg-timestamp>1553547804</last-sent-msg-timestamp>
+                  <reply-time>
+                     <average-time>0</average-time>
+                     <min-time>0</min-time>
+                     <max-time>0</max-time>
+                  </reply-time>
+                  <received-rpt-msg-count xmlns="urn:opendaylight:params:xml:ns:yang:pcep:stateful:stats">1</received-rpt-msg-count>
+                  <sent-init-msg-count xmlns="urn:opendaylight:params:xml:ns:yang:pcep:stateful:stats">0</sent-init-msg-count>
+                  <last-received-rpt-msg-timestamp xmlns="urn:opendaylight:params:xml:ns:yang:pcep:stateful:stats">1553195032</last-received-rpt-msg-timestamp>
+                  <sent-upd-msg-count xmlns="urn:opendaylight:params:xml:ns:yang:pcep:stateful:stats">0</sent-upd-msg-count>
+               </messages>
+               <peer-pref>
+                  <keepalive>30</keepalive>
+                  <deadtimer>120</deadtimer>
+                  <session-id>8</session-id>
+                  <ip-address>127.0.0.1</ip-address>
+               </peer-pref>
+               <delegated-lsps-count>0</delegated-lsps-count>
+            </pcep-session-state>
+         </node>
+      </topology>
+   </output>
index 026fb3a158faeedcbe5e198dee94544240164b53..5618ed6423224a77a45024374f89c224ba86bc43 100644 (file)
@@ -7,9 +7,9 @@ module odl-pcep-stateful-stats {
     import network-topology { prefix nt; revision-date 2013-10-21; }
     import network-topology-pcep { prefix pn; revision-date 2018-11-09; }
     import yang-ext { prefix ext; revision-date 2013-07-09; }
-    import pcep-session-stats { prefix pss; revision-date 2017-11-13; }
     import network-pcep-topology-stats { prefix npts; revision-date 2018-11-09; }
     import odl-pcep-sync-optimizations { prefix opso; revision-date 2018-11-09; }
+    import pcep-topology-stats-rpc { prefix ptsr; revision-date 2019-03-21; }
 
     description
         "This module contains the PCEP Stateful stats YANG definitions for
@@ -70,6 +70,13 @@ module odl-pcep-stateful-stats {
         uses stateful-messages-grouping;
     }
 
+    augment "/ptsr:get-stats/ptsr:output/ptsr:topology/ptsr:node/ptsr:pcep-session-state/ptsr:messages" {
+        ext:augment-identifier stateful-messages-rpc-aug;
+        description "Augment Pcep session stats RPC output with Stateful session stats";
+
+        uses stateful-messages-grouping;
+    }
+
     grouping stateful-preferences {
         leaf instantiation {
             description "Represents peer's instantiation capability.";
@@ -98,6 +105,13 @@ module odl-pcep-stateful-stats {
         uses stateful-preferences;
     }
 
+    augment "/ptsr:get-stats/ptsr:output/ptsr:topology/ptsr:node/ptsr:pcep-session-state/ptsr:peer-capabilities" {
+        ext:augment-identifier stateful-capabilities-rpc-aug;
+        description "Augment Pcep session stats RPC output with Remote peer's (PCC) advertised stateful capabilities.";
+
+        uses stateful-preferences;
+    }
+
     augment "/nt:network-topology/nt:topology/nt:node/npts:pcep-session-state/npts:local-pref" {
         when "../../../nt:topology-types/pn:topology-pcep";
         ext:augment-identifier pcep-entity-id-stats-aug;
@@ -105,4 +119,11 @@ module odl-pcep-stateful-stats {
 
         uses opso:speaker-entity-id;
     }
-}
\ No newline at end of file
+
+    augment "/ptsr:get-stats/ptsr:output/ptsr:topology/ptsr:node/ptsr:pcep-session-state/ptsr:local-pref" {
+        ext:augment-identifier pcep-entity-id-rpc-aug;
+        description "Augment Pcep session stats RPC output with PCEP Entity Identifier";
+
+        uses opso:speaker-entity-id;
+    }
+}
diff --git a/pcep/topology/topology-api/src/main/yang/pcep-topology-stats-rpc.yang b/pcep/topology/topology-api/src/main/yang/pcep-topology-stats-rpc.yang
new file mode 100644 (file)
index 0000000..9650f24
--- /dev/null
@@ -0,0 +1,63 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module pcep-topology-stats-rpc {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:pcep:topology:stats:rpc";
+    prefix ptsr;
+
+    import network-topology { prefix nt; revision-date 2013-10-21; }
+    import pcep-session-stats { prefix pss; revision-date 2017-11-13; }
+
+    description
+        "This module contains rpc for fetching PCEP session statistics.
+
+         Copyright (c) 2019 Lumina Networks, Inc. and others. All rights reserved.;
+
+         This program and the accompanying materials are made available
+         under the terms of the Eclipse Public License v1.0 which
+         accompanies this distribution, and is available at
+         http://www.eclipse.org/legal/epl-v10.html";
+
+    organization "Lumina Networks, Inc.";
+    contact "Ajay Lele <alele@luminanetworks.com>";
+
+    revision "2019-03-21" {
+        description
+            "Initial revision.";
+    }
+
+    rpc get-stats {
+        description
+            "Fetch PCEP session statistics for given PCEP topology and node.
+            If topology and/or node details are absent, statistics for all
+            nodes under the context are returned.";
+        input {
+            list topology {
+                key "topology-id";
+                leaf topology-id {
+                    type nt:topology-id;
+                }
+                list node {
+                    key "node-id";
+                    leaf node-id {
+                        type nt:node-id;
+                    }
+                }
+            }
+        }
+        output {
+            list topology {
+                key "topology-id";
+                leaf topology-id {
+                    type nt:topology-id;
+                }
+                list node {
+                    key "node-id";
+                    leaf node-id {
+                        type nt:node-id;
+                    }
+                    uses pss:pcep-session-state-grouping;
+                }
+            }
+        }
+    }
+}
index 937b6b160db7322f43415394997dab4cf9ba7415..87c79a80c047b42804318883221f99e0d1771178 100644 (file)
             <groupId>${project.groupId}</groupId>
             <artifactId>pcep-topology-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>pcep-ietf-stateful07</artifactId>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>pcep-topology-spi</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>programming-spi</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
             <artifactId>mdsal-binding-api</artifactId>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
+        <!--Test dependencies-->
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-dom-adapter</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-dom-adapter</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <scm>
@@ -68,4 +88,4 @@
         <url>https://wiki.opendaylight.org/view/BGP_LS_PCEP:Main</url>
         <tag>HEAD</tag>
     </scm>
-</project>
\ No newline at end of file
+</project>
diff --git a/pcep/topology/topology-stats/src/main/java/org/opendaylight/bgpcep/pcep/topology/stats/rpc/TopologyStatsRpcServiceImpl.java b/pcep/topology/topology-stats/src/main/java/org/opendaylight/bgpcep/pcep/topology/stats/rpc/TopologyStatsRpcServiceImpl.java
new file mode 100644 (file)
index 0000000..534d4ac
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2019 Lumina Networks, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.bgpcep.pcep.topology.stats.rpc;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
+import org.opendaylight.bgpcep.programming.spi.SuccessfulRpcResult;
+import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.DataObjectModification;
+import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
+import org.opendaylight.mdsal.binding.api.DataTreeModification;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.PcepEntityIdRpcAug;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.PcepEntityIdRpcAugBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.PcepEntityIdStatsAug;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.StatefulCapabilitiesRpcAug;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.StatefulCapabilitiesRpcAugBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.StatefulCapabilitiesStatsAug;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.StatefulMessagesRpcAug;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.StatefulMessagesRpcAugBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.StatefulMessagesStatsAug;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.LocalPref;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.LocalPrefBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.Messages;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.MessagesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.PeerCapabilities;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.PeerCapabilitiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.grouping.PcepSessionState;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.grouping.PcepSessionStateBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.GetStatsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.GetStatsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.GetStatsOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.PcepTopologyStatsRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.stats.rev181109.PcepTopologyNodeStatsAug;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TopologyStatsRpcServiceImpl
+        implements PcepTopologyStatsRpcService, ClusteredDataTreeChangeListener<PcepSessionState>, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(TopologyStatsRpcServiceImpl.class);
+
+    private final DataBroker dataBroker;
+    private final ConcurrentMap<InstanceIdentifier<PcepSessionState>, PcepSessionState> sessionStateMap =
+            new ConcurrentHashMap<>();
+    private ListenerRegistration<TopologyStatsRpcServiceImpl> listenerRegistration;
+
+    public TopologyStatsRpcServiceImpl(final DataBroker dataBroker) {
+        this.dataBroker = requireNonNull(dataBroker);
+    }
+
+    public synchronized void init() {
+        LOG.info("Initializing PCEP Topology Stats RPC service.");
+        this.listenerRegistration = this.dataBroker.registerDataTreeChangeListener(
+                DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL,
+                        InstanceIdentifier.builder(NetworkTopology.class).child(Topology.class).child(Node.class)
+                                .augmentation(PcepTopologyNodeStatsAug.class).child(PcepSessionState.class).build()),
+                this);
+    }
+
+    @Override
+    public void onDataTreeChanged(final Collection<DataTreeModification<PcepSessionState>> changes) {
+        changes.forEach(change -> {
+            final InstanceIdentifier<PcepSessionState> iid = change.getRootPath().getRootIdentifier();
+            final DataObjectModification<PcepSessionState> mod = change.getRootNode();
+            switch (mod.getModificationType()) {
+                case SUBTREE_MODIFIED:
+                case WRITE:
+                    sessionStateMap.put(iid, mod.getDataAfter());
+                    break;
+                case DELETE:
+                    sessionStateMap.remove(iid);
+                    break;
+                default:
+            }
+        });
+    }
+
+    @Override
+    public synchronized void close() {
+        LOG.info("Closing PCEP Topology Stats RPC service.");
+        if (this.listenerRegistration != null) {
+            this.listenerRegistration.close();
+            this.listenerRegistration = null;
+        }
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:LineLength")
+    public ListenableFuture<RpcResult<GetStatsOutput>> getStats(final GetStatsInput input) {
+        final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.input.Topology> iTopologies =
+                input.getTopology();
+        final List<TopologyId> iTopologyIds;
+        if (iTopologies != null) {
+            iTopologyIds = iTopologies.stream().map(
+                    org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.input.Topology::getTopologyId)
+                    .collect(Collectors.toList());
+        } else {
+            iTopologyIds = getAvailableTopologyIds();
+        }
+
+        final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.output.Topology> oTopologies =
+                new ArrayList<>();
+        iTopologyIds.forEach(iTopologyId -> {
+            final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.input.topology.Node> iNodes;
+            if (input.getTopology() != null) {
+                iNodes = input.getTopology().stream().filter(t -> t.getTopologyId().equals(iTopologyId)).findFirst()
+                        .get().getNode();
+            } else {
+                iNodes = null;
+            }
+
+            final List<NodeId> iNodeIds;
+            if (iNodes != null) {
+                iNodeIds = iNodes.stream().map(
+                        org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.input.topology.Node::getNodeId)
+                        .collect(Collectors.toList());
+            } else {
+                iNodeIds = getAvailableNodeIds(iTopologyId);
+            }
+
+            final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.output.topology.Node> oNodes =
+                    new ArrayList<>();
+            iNodeIds.forEach(iNodeId -> {
+                final InstanceIdentifier<PcepSessionState> iid = InstanceIdentifier.builder(NetworkTopology.class)
+                        .child(Topology.class, new TopologyKey(iTopologyId)).child(Node.class, new NodeKey(iNodeId))
+                        .augmentation(PcepTopologyNodeStatsAug.class).child(PcepSessionState.class).build();
+                if (!sessionStateMap.containsKey(iid)) {
+                    LOG.debug("Pcep session stats not available for node {} in topology {}", iNodeId.getValue(),
+                            iTopologyId.getValue());
+                }
+                oNodes.add(
+                        new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.output.topology.NodeBuilder()
+                                .setNodeId(iNodeId)
+                                .setPcepSessionState(transformStatefulAugmentation(sessionStateMap.get(iid))).build());
+            });
+
+            oTopologies.add(
+                    new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.output.TopologyBuilder()
+                            .setTopologyId(iTopologyId).setNode(oNodes).build());
+        });
+
+        final RpcResult<GetStatsOutput> res =
+                SuccessfulRpcResult.create(new GetStatsOutputBuilder().setTopology(oTopologies).build());
+        return Futures.immediateFuture(res);
+    }
+
+    /*
+     * Replace stateful topology augmentations with ones for rpc in PCEP session
+     * stats data
+     */
+    private PcepSessionState transformStatefulAugmentation(final PcepSessionState pcepSessionState) {
+        if (pcepSessionState == null) {
+            return null;
+        }
+
+        final PcepSessionStateBuilder sb = new PcepSessionStateBuilder(pcepSessionState);
+
+        final Messages topoMessage = pcepSessionState.getMessages();
+        if (topoMessage != null) {
+            final StatefulMessagesStatsAug messageStatsAug = topoMessage.augmentation(StatefulMessagesStatsAug.class);
+            if (messageStatsAug != null) {
+                final StatefulMessagesRpcAug messageRpcAug = new StatefulMessagesRpcAugBuilder()
+                        .setLastReceivedRptMsgTimestamp(messageStatsAug.getLastReceivedRptMsgTimestamp())
+                        .setReceivedRptMsgCount(messageStatsAug.getReceivedRptMsgCount())
+                        .setSentInitMsgCount(messageStatsAug.getSentInitMsgCount())
+                        .setSentUpdMsgCount(messageStatsAug.getSentUpdMsgCount()).build();
+                sb.setMessages(new MessagesBuilder(topoMessage).removeAugmentation(StatefulMessagesStatsAug.class)
+                        .addAugmentation(StatefulMessagesRpcAug.class, messageRpcAug).build());
+            }
+        }
+
+        final PeerCapabilities topoPeerCapability = pcepSessionState.getPeerCapabilities();
+        if (topoPeerCapability != null) {
+            final StatefulCapabilitiesStatsAug capabilityStatsAug =
+                    topoPeerCapability.augmentation(StatefulCapabilitiesStatsAug.class);
+            if (capabilityStatsAug != null) {
+                final StatefulCapabilitiesRpcAug capabilityRpcAug = new StatefulCapabilitiesRpcAugBuilder()
+                        .setActive(capabilityStatsAug.isActive()).setInstantiation(capabilityStatsAug.isInstantiation())
+                        .setStateful(capabilityStatsAug.isStateful()).build();
+                sb.setPeerCapabilities(new PeerCapabilitiesBuilder(topoPeerCapability)
+                        .removeAugmentation(StatefulCapabilitiesStatsAug.class)
+                        .addAugmentation(StatefulCapabilitiesRpcAug.class, capabilityRpcAug).build());
+            }
+        }
+
+        final LocalPref topoLocalPref = pcepSessionState.getLocalPref();
+        if (topoLocalPref != null) {
+            final PcepEntityIdStatsAug entityStatsAug = topoLocalPref.augmentation(PcepEntityIdStatsAug.class);
+            if (entityStatsAug != null) {
+                final PcepEntityIdRpcAug entityRpcAug = new PcepEntityIdRpcAugBuilder()
+                        .setSpeakerEntityIdValue(entityStatsAug.getSpeakerEntityIdValue()).build();
+                sb.setLocalPref(new LocalPrefBuilder(topoLocalPref).removeAugmentation(PcepEntityIdStatsAug.class)
+                        .addAugmentation(PcepEntityIdRpcAug.class, entityRpcAug).build());
+            }
+        }
+
+        return sb.build();
+    }
+
+    private List<TopologyId> getAvailableTopologyIds() {
+        return sessionStateMap.keySet().stream().map(iid -> iid.firstKeyOf(Topology.class).getTopologyId()).distinct()
+                .collect(Collectors.toList());
+    }
+
+    private List<NodeId> getAvailableNodeIds(final TopologyId topologyId) {
+        return sessionStateMap.keySet().stream()
+                .filter(iid -> iid.firstKeyOf(Topology.class).getTopologyId().equals(topologyId))
+                .map(iid -> iid.firstKeyOf(Node.class).getNodeId()).collect(Collectors.toList());
+    }
+}
similarity index 80%
rename from pcep/topology/topology-stats/src/main/resources/OSGI-INF/blueprint/pcep-provider-stats.xml
rename to pcep/topology/topology-stats/src/main/resources/OSGI-INF/blueprint/pcep-stats.xml
index 90232a3fbf4c4054b3455b355c1a67d690ea65cb..c832c46b8c52b0a090bb8291d8733a7472d1b78b 100644 (file)
     <odl:clustered-app-config id="pcepStatsConfig"
         binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.odl.pcep.stats.provider.config.rev171113.PcepProvider"/>
 
+    <bean id="topologyStatsRpcService"
+          class="org.opendaylight.bgpcep.pcep.topology.stats.rpc.TopologyStatsRpcServiceImpl"
+          init-method="init" destroy-method="close">
+        <argument ref="dataBroker"/>
+    </bean>
+    <odl:rpc-implementation ref="topologyStatsRpcService"/>
+
     <bean id="topologyStatsRegistry"
           class="org.opendaylight.bgpcep.pcep.topology.stats.provider.TopologyStatsProviderImpl"
           init-method="init" destroy-method="close">
@@ -24,4 +31,4 @@
     </bean>
     <service ref="topologyStatsRegistry"
              interface="org.opendaylight.bgpcep.pcep.topology.spi.stats.TopologySessionStatsRegistry"/>
-</blueprint>
\ No newline at end of file
+</blueprint>
diff --git a/pcep/topology/topology-stats/src/test/java/org/opendaylight/bgpcep/pcep/topology/stats/rpc/TopologyStatsRpcServiceImplTest.java b/pcep/topology/topology-stats/src/test/java/org/opendaylight/bgpcep/pcep/topology/stats/rpc/TopologyStatsRpcServiceImplTest.java
new file mode 100644 (file)
index 0000000..bb633ca
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2019 Lumina Networks, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.bgpcep.pcep.topology.stats.rpc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.api.WriteTransaction;
+import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractConcurrentDataBrokerTest;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.PcepEntityIdRpcAug;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.PcepEntityIdRpcAugBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.PcepEntityIdStatsAug;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.PcepEntityIdStatsAugBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.StatefulCapabilitiesRpcAug;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.StatefulCapabilitiesRpcAugBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.StatefulCapabilitiesStatsAug;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.StatefulCapabilitiesStatsAugBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.StatefulMessagesRpcAug;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.StatefulMessagesRpcAugBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.StatefulMessagesStatsAug;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stateful.stats.rev181109.StatefulMessagesStatsAugBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.error.messages.grouping.ErrorMessages;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.error.messages.grouping.ErrorMessagesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.LocalPref;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.LocalPrefBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.Messages;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.MessagesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.PeerCapabilities;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.PeerCapabilitiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.PeerPrefBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.grouping.PcepSessionState;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.pcep.session.state.grouping.PcepSessionStateBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.reply.time.grouping.ReplyTime;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.stats.rev171113.reply.time.grouping.ReplyTimeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.GetStatsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.GetStatsInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.GetStatsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.GetStatsOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.stats.rev181109.PcepTopologyNodeStatsAug;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.stats.rev181109.PcepTopologyNodeStatsAugBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopologyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+public class TopologyStatsRpcServiceImplTest extends AbstractConcurrentDataBrokerTest {
+    private static final String TOPOLOGY_ID1 = "pcep-topology-1";
+    private static final String TOPOLOGY_ID2 = "pcep-topology-2";
+    private static final String NONEXISTENT_TOPOLOGY = "nonexistent-topology";
+    private static final String NONPCEP_TOPOLOGY = "nonpcep-topology";
+    private static final String NODE_ID1 = "pcc://1.1.1.1";
+    private static final String NODE_ID2 = "pcc://2.2.2.2";
+    private static final String NODE_ID3 = "pcc://3.3.3.3";
+    private static final String NONEXISTENT_NODE = "pcc://4.4.4.4";
+    private static final String NONPCEP_NODE = "nonpcep-node";
+
+    TopologyStatsRpcServiceImpl rpcService;
+
+    @Before
+    public void setUp() throws Exception {
+        rpcService = new TopologyStatsRpcServiceImpl(getDataBroker());
+        rpcService.init();
+
+        // PCEP topology with one PCC node
+        final Topology t1 = createTopology(TOPOLOGY_ID1, Collections.singletonList(createPcepNode(NODE_ID1)));
+
+        // PCEP topology with two PCC node
+        final Topology t2 =
+                createTopology(TOPOLOGY_ID2, Arrays.asList(createPcepNode(NODE_ID2), createPcepNode(NODE_ID3)));
+
+        // Non-PCEP topology with one non-PCC node
+        final Topology t3 = createTopology(NONPCEP_TOPOLOGY,
+                Collections.singletonList(new NodeBuilder().setNodeId(new NodeId(NONPCEP_NODE)).build()));
+
+        final WriteTransaction wtx = getDataBroker().newWriteOnlyTransaction();
+        final NetworkTopologyBuilder ntb = new NetworkTopologyBuilder();
+        ntb.setTopology(Arrays.asList(t1, t2, t3));
+        wtx.put(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(NetworkTopology.class).build(),
+                ntb.build());
+        wtx.commit().get();
+    }
+
+    private Topology createTopology(final String topologyId, final List<Node> nodes) {
+        return new TopologyBuilder().setTopologyId(new TopologyId(topologyId)).setNode(nodes).build();
+    }
+
+    private Node createPcepNode(final String nodeId) {
+        return new NodeBuilder().setNodeId(new NodeId(nodeId))
+                .addAugmentation(PcepTopologyNodeStatsAug.class,
+                        new PcepTopologyNodeStatsAugBuilder().setPcepSessionState(createTopologySessionState()).build())
+                .build();
+    }
+
+    private PcepSessionState createTopologySessionState() {
+        final ReplyTime replyTime = new ReplyTimeBuilder().setAverageTime(1L).setMaxTime(3L).setMinTime(2L).build();
+
+        final ErrorMessages errorMsg =
+                new ErrorMessagesBuilder().setReceivedErrorMsgCount(1L).setSentErrorMsgCount(2L).build();
+
+        final StatefulMessagesStatsAug statefulMsg =
+                new StatefulMessagesStatsAugBuilder().setLastReceivedRptMsgTimestamp(1553183614L).setSentUpdMsgCount(1L)
+                        .setReceivedRptMsgCount(2L).setSentInitMsgCount(3L).build();
+
+        final Messages messages = new MessagesBuilder().setLastSentMsgTimestamp(1553183734L).setUnknownMsgReceived(1)
+                .setSentMsgCount(5L).setReceivedMsgCount(4L).setReplyTime(replyTime).setErrorMessages(errorMsg)
+                .addAugmentation(StatefulMessagesStatsAug.class, statefulMsg).build();
+
+        final PeerCapabilities capabilities = new PeerCapabilitiesBuilder()
+                .addAugmentation(StatefulCapabilitiesStatsAug.class, new StatefulCapabilitiesStatsAugBuilder()
+                        .setStateful(true).setInstantiation(true).setActive(true).build())
+                .build();
+
+        final LocalPref localPref =
+                new LocalPrefBuilder().setKeepalive((short) 30).setDeadtimer((short) 120).setIpAddress("127.0.0.1")
+                        .setSessionId(0).addAugmentation(PcepEntityIdStatsAug.class, new PcepEntityIdStatsAugBuilder()
+                                .setSpeakerEntityIdValue(new byte[] {0x01, 0x02, 0x03, 0x04}).build())
+                        .build();
+
+        return new PcepSessionStateBuilder().setSynchronized(true).setSessionDuration("0:00:05:18")
+                .setDelegatedLspsCount(1).setLocalPref(localPref).setPeerPref(new PeerPrefBuilder(localPref).build())
+                .setPeerCapabilities(capabilities).setMessages(messages).build();
+    }
+
+    private PcepSessionState createRpcSessionState() {
+        final ReplyTime replyTime = new ReplyTimeBuilder().setAverageTime(1L).setMaxTime(3L).setMinTime(2L).build();
+
+        final ErrorMessages errorMsg =
+                new ErrorMessagesBuilder().setReceivedErrorMsgCount(1L).setSentErrorMsgCount(2L).build();
+
+        final StatefulMessagesRpcAug statefulMsg =
+                new StatefulMessagesRpcAugBuilder().setLastReceivedRptMsgTimestamp(1553183614L).setSentUpdMsgCount(1L)
+                        .setReceivedRptMsgCount(2L).setSentInitMsgCount(3L).build();
+
+        final Messages messages = new MessagesBuilder().setLastSentMsgTimestamp(1553183734L).setUnknownMsgReceived(1)
+                .setSentMsgCount(5L).setReceivedMsgCount(4L).setReplyTime(replyTime).setErrorMessages(errorMsg)
+                .addAugmentation(StatefulMessagesRpcAug.class, statefulMsg).build();
+
+        final PeerCapabilities capabilities = new PeerCapabilitiesBuilder()
+                .addAugmentation(StatefulCapabilitiesRpcAug.class, new StatefulCapabilitiesRpcAugBuilder()
+                        .setStateful(true).setInstantiation(true).setActive(true).build())
+                .build();
+
+        final LocalPref localPref =
+                new LocalPrefBuilder().setKeepalive((short) 30).setDeadtimer((short) 120).setIpAddress("127.0.0.1")
+                        .setSessionId(0).addAugmentation(PcepEntityIdRpcAug.class, new PcepEntityIdRpcAugBuilder()
+                                .setSpeakerEntityIdValue(new byte[] {0x01, 0x02, 0x03, 0x04}).build())
+                        .build();
+
+        return new PcepSessionStateBuilder().setSynchronized(true).setSessionDuration("0:00:05:18")
+                .setDelegatedLspsCount(1).setLocalPref(localPref).setPeerPref(new PeerPrefBuilder(localPref).build())
+                .setPeerCapabilities(capabilities).setMessages(messages).build();
+    }
+
+    @Test
+    public void testGetStatsNoMatch() throws Exception {
+        GetStatsInput in;
+        GetStatsOutput out;
+
+        // Non-existing topology
+        in = createGetStatsInput(NONEXISTENT_TOPOLOGY, null);
+        out = createGetStatsOutput(NONEXISTENT_TOPOLOGY, Collections.emptyList(), null);
+        performTest(in, out);
+
+        // Non-existent node
+        in = createGetStatsInput(TOPOLOGY_ID1, Collections.singletonList(NONEXISTENT_NODE));
+        out = createGetStatsOutput(TOPOLOGY_ID1, Collections.singletonList(NONEXISTENT_NODE), null);
+        performTest(in, out);
+
+        // Non-PCEP topology
+        in = createGetStatsInput(NONPCEP_TOPOLOGY, Collections.singletonList(NONPCEP_NODE));
+        out = createGetStatsOutput(NONPCEP_TOPOLOGY, Collections.singletonList(NONPCEP_NODE), null);
+        performTest(in, out);
+    }
+
+    @Test
+    public void testGetStatsPartialMatch() throws Exception {
+        GetStatsInput in;
+        GetStatsOutput out;
+
+        // Match one PCEP topology
+        in = createGetStatsInput(TOPOLOGY_ID1, null);
+        out = createGetStatsOutput(TOPOLOGY_ID1, Collections.singletonList(NODE_ID1), createRpcSessionState());
+        performTest(in, out);
+
+        // Match one PCEP node in one topology
+        in = createGetStatsInput(TOPOLOGY_ID2, Collections.singletonList(NODE_ID3));
+        out = createGetStatsOutput(TOPOLOGY_ID2, Collections.singletonList(NODE_ID3), createRpcSessionState());
+        performTest(in, out);
+
+        // Match two PCEP nodes in one topology
+        in = createGetStatsInput(TOPOLOGY_ID2, Arrays.asList(NODE_ID2, NODE_ID3));
+        out = createGetStatsOutput(TOPOLOGY_ID2, Arrays.asList(NODE_ID2, NODE_ID3), createRpcSessionState());
+        performCountTest(in, out);
+    }
+
+    @Test
+    @SuppressWarnings("checkstyle:LineLength")
+    public void testGetStatsAllMatch() throws Exception {
+        GetStatsInput in;
+
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.output.Topology ot1 =
+                createGetStatsOutput(TOPOLOGY_ID1, Collections.singletonList(NODE_ID1), createRpcSessionState())
+                        .getTopology().get(0);
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.output.Topology ot2 =
+                createGetStatsOutput(TOPOLOGY_ID2, Arrays.asList(NODE_ID2, NODE_ID3), createRpcSessionState())
+                        .getTopology().get(0);
+        final GetStatsOutput out = new GetStatsOutputBuilder().setTopology(Arrays.asList(ot1, ot2)).build();
+
+        // Implicitly match all PCEP topologies and nodes
+        in = createGetStatsInput(null, null);
+        performCountTest(in, out);
+
+        // Explicitly match all PCEP topologies and nodes
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.input.Topology it1 =
+                createGetStatsInput(TOPOLOGY_ID1, Collections.singletonList(NODE_ID1)).getTopology().get(0);
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.input.Topology it2 =
+                createGetStatsInput(TOPOLOGY_ID2, Arrays.asList(NODE_ID2, NODE_ID3)).getTopology().get(0);
+        in = new GetStatsInputBuilder().setTopology(Arrays.asList(it1, it2)).build();
+        performCountTest(in, out);
+    }
+
+    private void performTest(final GetStatsInput in, final GetStatsOutput out) throws Exception {
+        final RpcResult<GetStatsOutput> result = rpcService.getStats(in).get();
+        assertEquals(result.getResult(), out);
+        assertTrue(result.isSuccessful());
+        assertTrue(result.getErrors().isEmpty());
+    }
+
+    /*
+     * When topology and/or node list is expected to contain more than one item,
+     * direct comparison will fail due to potential list ordering differences. So
+     * just compare the number of nodes
+     */
+    private void performCountTest(final GetStatsInput in, final GetStatsOutput out) throws Exception {
+        final RpcResult<GetStatsOutput> result = rpcService.getStats(in).get();
+        assertEquals(result.getResult().getTopology().size(), out.getTopology().size());
+        assertTrue(result.isSuccessful());
+        assertEquals(result.getResult().getTopology().stream().flatMap(t -> t.getNode().stream()).count(),
+                out.getTopology().stream().flatMap(t -> t.getNode().stream()).count());
+        assertTrue(result.isSuccessful());
+        assertTrue(result.getErrors().isEmpty());
+    }
+
+    @SuppressWarnings("checkstyle:LineLength")
+    private GetStatsInput createGetStatsInput(final String topologyId, final List<String> nodeIds) {
+        final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.input.topology.Node> nodes;
+        if (nodeIds != null) {
+            nodes = nodeIds.stream().map(
+                nodeId -> new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.input.topology.NodeBuilder()
+                        .setNodeId(new NodeId(nodeId)).build())
+                .collect(Collectors.toList());
+        } else {
+            nodes = null;
+        }
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.input.Topology topology;
+        if (topologyId != null) {
+            topology =
+                    new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.input.TopologyBuilder()
+                            .setTopologyId(new TopologyId(topologyId)).setNode(nodes).build();
+        } else {
+            topology = null;
+        }
+        return new GetStatsInputBuilder().setTopology(topology != null ? Collections.singletonList(topology) : null)
+                .build();
+    }
+
+    @SuppressWarnings("checkstyle:LineLength")
+    private GetStatsOutput createGetStatsOutput(final String topologyId, final List<String> nodeIds,
+            final PcepSessionState state) {
+        final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.output.topology.Node> nodes;
+        if (nodeIds != null) {
+            nodes = nodeIds.stream().map(
+                nodeId -> new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.output.topology.NodeBuilder()
+                        .setNodeId(new NodeId(nodeId)).setPcepSessionState(state).build())
+                .collect(Collectors.toList());
+        } else {
+            nodes = null;
+        }
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.output.Topology topology;
+        if (topologyId != null) {
+            topology =
+                    new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.topology.stats.rpc.rev190321.get.stats.output.TopologyBuilder()
+                            .setTopologyId(new TopologyId(topologyId)).setNode(nodes).build();
+        } else {
+            topology = null;
+        }
+        return new GetStatsOutputBuilder().setTopology(topology != null ? Collections.singletonList(topology) : null)
+                .build();
+    }
+
+    @After
+    public void tearDown() {
+        rpcService.close();
+    }
+}