Bug-2080: BGP statistics. 18/12018/5
authorMilos Fabian <milfabia@cisco.com>
Thu, 9 Oct 2014 07:09:00 +0000 (09:09 +0200)
committerMilos Fabian <milfabia@cisco.com>
Tue, 21 Oct 2014 07:51:48 +0000 (07:51 +0000)
-per BGP peer statistics, related with bgp-peer config module

-peer and speaker preferences
-proposed holdtimer-value, BGP ID, AS number, Ip address, port, 4-byte AS capa., advertized routing tables
-session's holdtimer value and keep-alive
-received/sent BGP messages statistics (last timestamp, count)
-all msgs, keep-alive msgs, update msgs, notification msgs (error code and subcode)
-number of time the session was established
-routes count per routing table
-op. reset statistics
-op. reset session

-statistics reachable via Netconf, Restconf - as operational data and JMX (JConsole, VisulaVM, Jolokia) - as Runtime Bean.

Change-Id: I797535744176eea61d538dfa00d23cc6bfdd3da2
Signed-off-by: Milos Fabian <milfabia@cisco.com>
16 files changed:
bgp/rib-impl/src/main/java/org/opendaylight/controller/config/yang/bgp/rib/impl/BGPPeerModule.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractBGPSessionNegotiator.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPPeer.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPSessionImpl.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPSessionStats.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/RIBImpl.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPSessionStatistics.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/RIB.java
bgp/rib-impl/src/main/yang/odl-bgp-rib-impl-cfg.yang
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/ApplicationPeerTest.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/BGPSessionImplTest.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/FSMTest.java
pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/PCEPSessionState.java
pcep/topology-provider/src/main/java/org/opendaylight/bgpcep/pcep/topology/provider/SessionListenerState.java
util/src/main/java/org/opendaylight/protocol/util/StatisticsUtil.java [new file with mode: 0644]
util/src/test/java/org/opendaylight/protocol/util/StatisticsUtilTest.java [new file with mode: 0644]

index 7fd2ae27a49225e455eaee896b09d05fb317db83..e768d1adfc8dbf1acfe0736bbbbc252540d9f29f 100644 (file)
@@ -106,6 +106,7 @@ public final class BGPPeerModule extends org.opendaylight.controller.config.yang
 
         final BGPSessionPreferences prefs = new BGPSessionPreferences(r.getLocalAs(), getHoldtimer(), r.getBgpIdentifier(), tlvs);
         final BGPPeer bgpClientPeer = new BGPPeer(peerName(getHostWithoutValue()), r);
+        bgpClientPeer.registerRootRuntimeBean(getRootRuntimeBeanRegistratorWrapper());
 
         getPeerRegistryBackwards().addPeer(getHostWithoutValue(), bgpClientPeer, prefs);
 
index e865690bbec291ca4da963b577d12fa9492f6b79..b6f459e641aa22dc0330797b364fcde50d07a8d7 100644 (file)
@@ -182,7 +182,7 @@ public abstract class AbstractBGPSessionNegotiator extends AbstractSessionNegoti
         try {
             final BGPSessionListener peer = this.registry.getPeer(getRemoteIp(), getSourceId(openObj, getPreferences()), getDestinationId(openObj, getPreferences()));
             this.sendMessage(new KeepaliveBuilder().build());
-            this.session = new BGPSessionImpl(peer, this.channel, openObj, getPreferences().getHoldTime());
+            this.session = new BGPSessionImpl(peer, this.channel, openObj, getPreferences());
             this.state = State.OpenConfirm;
             LOG.debug("Channel {} moved to OpenConfirm state with remote proposal {}", this.channel, openObj);
         } catch (final BGPDocumentedException e) {
index 266a32d2fff2284c0c5665aa5319704dd37c3def..4d92b23b334fc688bbae298082fd0cf9dec7675b 100644 (file)
@@ -11,11 +11,20 @@ package org.opendaylight.protocol.bgp.rib.impl;
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
 import com.google.common.net.InetAddresses;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.BGPPeerRuntimeMXBean;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.BGPPeerRuntimeRegistration;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.BGPPeerRuntimeRegistrator;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.BgpPeerState;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.BgpSessionState;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.RouteTable;
 import org.opendaylight.protocol.bgp.rib.impl.spi.AdjRIBsOutRegistration;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionStatistics;
 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
 import org.opendaylight.protocol.bgp.rib.impl.spi.ReusableBGPPeer;
 import org.opendaylight.protocol.bgp.rib.spi.BGPSession;
@@ -33,7 +42,8 @@ import org.slf4j.LoggerFactory;
  * Class representing a peer. We have a single instance for each peer, which provides translation from BGP events into
  * RIB actions.
  */
-public class BGPPeer implements ReusableBGPPeer, Peer, AutoCloseable {
+public class BGPPeer implements ReusableBGPPeer, Peer, AutoCloseable, BGPPeerRuntimeMXBean {
+
     private static final Logger LOG = LoggerFactory.getLogger(BGPPeer.class);
 
     @GuardedBy("this")
@@ -48,6 +58,10 @@ public class BGPPeer implements ReusableBGPPeer, Peer, AutoCloseable {
     @GuardedBy("this")
     private AdjRIBsOutRegistration reg;
 
+    private BGPPeerRuntimeRegistrator registrator;
+    private BGPPeerRuntimeRegistration runtimeReg;
+    private long sessionEstablishedCounter = 0L;
+
     public BGPPeer(final String name, final RIB rib) {
         this.rib = Preconditions.checkNotNull(rib);
         this.name = name;
@@ -86,6 +100,10 @@ public class BGPPeer implements ReusableBGPPeer, Peer, AutoCloseable {
         if (session instanceof BGPSessionImpl) {
             reg = rib.registerRIBsOut(this, new SessionRIBsOut((BGPSessionImpl) session));
         }
+        this.sessionEstablishedCounter++;
+        if (this.registrator != null) {
+            this.runtimeReg = this.registrator.register(this);
+        }
     }
 
     private synchronized void cleanup() {
@@ -142,6 +160,10 @@ public class BGPPeer implements ReusableBGPPeer, Peer, AutoCloseable {
 
     @GuardedBy("this")
     private void dropConnection() {
+        if (this.runtimeReg != null) {
+            this.runtimeReg.close();
+            this.runtimeReg = null;
+        }
         if (this.session != null) {
             this.session.close();
             this.session = null;
@@ -152,4 +174,43 @@ public class BGPPeer implements ReusableBGPPeer, Peer, AutoCloseable {
     public synchronized byte[] getRawIdentifier() {
         return rawIdentifier;
     }
+
+    @Override
+    public void resetSession() {
+        releaseConnection();
+    }
+
+    @Override
+    public void resetStats() {
+        if (this.session instanceof BGPSessionStatistics) {
+            ((BGPSessionStatistics) session).resetSessionStats();
+        }
+    }
+
+    public void registerRootRuntimeBean(final BGPPeerRuntimeRegistrator registrator) {
+        this.registrator = registrator;
+    }
+
+    @Override
+    public BgpSessionState getBgpSessionState() {
+        if (this.session instanceof BGPSessionStatistics) {
+            return ((BGPSessionStatistics) session).getBgpSesionState();
+        }
+        return new BgpSessionState();
+    }
+
+    @Override
+    public BgpPeerState getBgpPeerState() {
+        final BgpPeerState peerState = new BgpPeerState();
+        final List<RouteTable> routes = Lists.newArrayList();
+        for (final TablesKey tablesKey : this.tables) {
+            final RouteTable routeTable = new RouteTable();
+            routeTable.setTableType("afi=" + tablesKey.getAfi().getSimpleName() + ",safi=" + tablesKey.getSafi().getSimpleName());
+            routeTable.setRoutesCount(this.rib.getRoutesCount(tablesKey));
+            routes.add(routeTable);
+        }
+        peerState.setRouteTable(routes);
+        peerState.setSessionEstablishedCount(this.sessionEstablishedCounter);
+        return peerState;
+    }
 }
index c7837329a29bef549ff83715c2c8cf7d1543b994..7bf53bf54a609fc58b3688da74407ac8395dc912 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.protocol.bgp.rib.impl;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Sets;
 import io.netty.channel.Channel;
@@ -20,9 +21,12 @@ import java.util.Date;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.BgpSessionState;
 import org.opendaylight.protocol.bgp.parser.AsNumberUtil;
 import org.opendaylight.protocol.bgp.parser.BGPError;
 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionStatistics;
 import org.opendaylight.protocol.bgp.rib.spi.BGPSession;
 import org.opendaylight.protocol.bgp.rib.spi.BGPSessionListener;
 import org.opendaylight.protocol.bgp.rib.spi.BGPTerminationReason;
@@ -45,7 +49,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @VisibleForTesting
-public class BGPSessionImpl extends AbstractProtocolSession<Notification> implements BGPSession {
+public class BGPSessionImpl extends AbstractProtocolSession<Notification> implements BGPSession, BGPSessionStatistics {
 
     private static final Logger LOG = LoggerFactory.getLogger(BGPSessionImpl.class);
 
@@ -98,6 +102,12 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
     private final int keepAlive;
     private final AsNumber asNumber;
     private final Ipv4Address bgpId;
+    private BGPSessionStats sessionStats;
+
+    public BGPSessionImpl(final BGPSessionListener listener, final Channel channel, final Open remoteOpen, final BGPSessionPreferences localPreferences) {
+        this(listener, channel, remoteOpen, localPreferences.getHoldTime());
+        this.sessionStats = new BGPSessionStats(remoteOpen, this.holdTimerValue, this.keepAlive, channel, Optional.of(localPreferences), this.tableTypes);
+    }
 
     public BGPSessionImpl(final BGPSessionListener listener, final Channel channel, final Open remoteOpen, final int localHoldTimer) {
         this.listener = Preconditions.checkNotNull(listener);
@@ -113,7 +123,8 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
             for (final BgpParameters param : remoteOpen.getBgpParameters()) {
                 final CParameters cp = param.getCParameters();
                 if (cp instanceof MultiprotocolCase) {
-                    final TablesKey tt = new TablesKey(((MultiprotocolCase) cp).getMultiprotocolCapability().getAfi(), ((MultiprotocolCase) cp).getMultiprotocolCapability().getSafi());
+                    final TablesKey tt = new TablesKey(((MultiprotocolCase) cp).getMultiprotocolCapability().getAfi(),
+                            ((MultiprotocolCase) cp).getMultiprotocolCapability().getSafi());
                     LOG.trace("Added table type to sync {}", tt);
                     tts.add(tt);
                     tats.add(new BgpTableTypeImpl(tt.getAfi(), tt.getSafi()));
@@ -140,6 +151,8 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
             }, this.keepAlive, TimeUnit.SECONDS);
         }
         this.bgpId = remoteOpen.getBgpIdentifier();
+        this.sessionStats = new BGPSessionStats(remoteOpen, this.holdTimerValue, this.keepAlive, channel, Optional.<BGPSessionPreferences>absent(),
+                this.tableTypes);
     }
 
     @Override
@@ -161,6 +174,7 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
     public synchronized void handleMessage(final Notification msg) {
         // Update last reception time
         this.lastMessageReceivedAt = System.nanoTime();
+        this.sessionStats.updateReceivedMsgTotal();
 
         if (msg instanceof Open) {
             // Open messages should not be present here
@@ -172,10 +186,12 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
             this.closeWithoutMessage();
             this.listener.onSessionTerminated(this, new BGPTerminationReason(BGPError.forValue(((Notify) msg).getErrorCode(),
                 ((Notify) msg).getErrorSubcode())));
+            this.sessionStats.updateReceivedMsgErr((Notify) msg);
         } else if (msg instanceof Keepalive) {
             // Keepalives are handled internally
             LOG.trace("Received KeepAlive messsage.");
             this.kaCounter++;
+            this.sessionStats.updateReceivedMsgKA();
             if (this.kaCounter >= 2) {
                 this.sync.kaReceived();
             }
@@ -183,6 +199,7 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
             // All others are passed up
             this.listener.onMessage(this, msg);
             this.sync.updReceived((Update) msg);
+            this.sessionStats.updateReceivedMsgUpd();
         }
     }
 
@@ -207,6 +224,12 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
                     }
                 });
             this.lastMessageSentAt = System.nanoTime();
+            this.sessionStats.updateSentMsgTotal();
+            if (msg instanceof Update) {
+                this.sessionStats.updateSentMsgUpd();
+            } else if (msg instanceof Notify) {
+                this.sessionStats.updateSentMsgErr((Notify) msg);
+            }
         } catch (final Exception e) {
             LOG.warn("Message {} was not sent.", msg, e);
         }
@@ -275,6 +298,7 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
         if (ct >= nextKeepalive) {
             this.sendMessage(KEEP_ALIVE);
             nextKeepalive = this.lastMessageSentAt + TimeUnit.SECONDS.toNanos(this.keepAlive);
+            this.sessionStats.updateSentMsgKA();
         }
         this.channel.eventLoop().schedule(new Runnable() {
             @Override
@@ -302,6 +326,7 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
 
     @Override
     protected synchronized void sessionUp() {
+        this.sessionStats.startSessionStopwatch();
         this.state = State.Up;
         this.listener.onSessionUp(this);
     }
@@ -327,11 +352,20 @@ public class BGPSessionImpl extends AbstractProtocolSession<Notification> implem
     synchronized void schedule(final Runnable task) {
         Preconditions.checkState(this.channel != null);
         this.channel.eventLoop().submit(task);
-
     }
 
     @VisibleForTesting
     protected synchronized void setLastMessageSentAt(final long lastMessageSentAt) {
         this.lastMessageSentAt = lastMessageSentAt;
     }
+
+    @Override
+    public BgpSessionState getBgpSesionState() {
+        return this.sessionStats.getBgpSessionState(this.state);
+    }
+
+    @Override
+    public synchronized void resetSessionStats() {
+        this.sessionStats.resetStats();
+    }
 }
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPSessionStats.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPSessionStats.java
new file mode 100644 (file)
index 0000000..87bb58e
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, 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.protocol.bgp.rib.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.Lists;
+import io.netty.channel.Channel;
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.AdvertizedTableTypes;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.BgpSessionState;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.ErrorMsgs;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.ErrorReceived;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.ErrorSent;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.KeepAliveMsgs;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.MessagesStats;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.PeerPreferences;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.Received;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.Sent;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.SpeakerPreferences;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.TotalMsgs;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.UpdateMsgs;
+import org.opendaylight.protocol.bgp.rib.impl.BGPSessionImpl.State;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
+import org.opendaylight.protocol.util.StatisticsUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Notify;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Open;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.BgpParameters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.bgp.parameters.CParameters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.bgp.parameters.c.parameters.As4BytesCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.c.parameters.MultiprotocolCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.open.bgp.parameters.c.parameters.multiprotocol._case.MultiprotocolCapability;
+
+final class BGPSessionStats {
+    private final Stopwatch sessionStopwatch;
+    private final BgpSessionState stats;
+    private final TotalMsgs totalMsgs = new TotalMsgs();
+    private final KeepAliveMsgs kaMsgs = new KeepAliveMsgs();
+    private final UpdateMsgs updMsgs = new UpdateMsgs();
+    private final ErrorMsgs errMsgs = new ErrorMsgs();
+
+    public BGPSessionStats(final Open remoteOpen, final int holdTimerValue, final int keepAlive, final Channel channel,
+            final Optional<BGPSessionPreferences> localPreferences, final Collection<BgpTableType> tableTypes) {
+        this.sessionStopwatch = new Stopwatch();
+        this.stats = new BgpSessionState();
+        this.stats.setHoldtimeCurrent(holdTimerValue);
+        this.stats.setKeepaliveCurrent(keepAlive);
+        this.stats.setPeerPreferences(setPeerPref(remoteOpen, channel, tableTypes));
+        this.stats.setSpeakerPreferences(setSpeakerPref(channel, localPreferences));
+        initMsgs();
+    }
+
+    private void initMsgs() {
+        this.totalMsgs.setReceived(new Received());
+        this.totalMsgs.setSent(new Sent());
+        this.kaMsgs.setReceived(new Received());
+        this.kaMsgs.setSent(new Sent());
+        this.updMsgs.setReceived(new Received());
+        this.updMsgs.setSent(new Sent());
+        this.errMsgs.setErrorReceived(new ErrorReceived());
+        this.errMsgs.setErrorSent(new ErrorSent());
+    }
+
+    public void startSessionStopwatch() {
+        this.sessionStopwatch.start();
+    }
+
+    public void updateSentMsgTotal() {
+        updateSentMsg(this.totalMsgs.getSent());
+    }
+
+    public void updateReceivedMsgTotal() {
+        updateReceivedMsg(this.totalMsgs.getReceived());
+    }
+
+    public void updateReceivedMsgKA() {
+        updateReceivedMsg(this.kaMsgs.getReceived());
+    }
+
+    public void updateSentMsgKA() {
+        updateSentMsg(this.kaMsgs.getSent());
+    }
+
+    public void updateSentMsgUpd() {
+        updateSentMsg(this.updMsgs.getSent());
+    }
+
+    public void updateReceivedMsgUpd() {
+        updateReceivedMsg(this.updMsgs.getReceived());
+    }
+
+    public void updateReceivedMsgErr(final Notify error) {
+        Preconditions.checkNotNull(error);
+        final ErrorReceived received = this.errMsgs.getErrorReceived();
+        received.setCount(received.getCount() + 1);
+        received.setTimestamp(StatisticsUtil.getCurrentTimestampInSeconds());
+        received.setCode(error.getErrorCode());
+        received.setSubCode(error.getErrorSubcode());
+    }
+
+    public void updateSentMsgErr(final Notify error) {
+        Preconditions.checkNotNull(error);
+        final ErrorSent sent = this.errMsgs.getErrorSent();
+        sent.setCount(sent.getCount() + 1);
+        sent.setTimestamp(StatisticsUtil.getCurrentTimestampInSeconds());
+        sent.setCode(error.getErrorCode());
+        sent.setSubCode(error.getErrorSubcode());
+    }
+
+    public BgpSessionState getBgpSessionState(final State state) {
+        Preconditions.checkNotNull(state);
+        final MessagesStats msgs = new MessagesStats();
+        msgs.setTotalMsgs(this.totalMsgs);
+        msgs.setErrorMsgs(this.errMsgs);
+        msgs.setKeepAliveMsgs(this.kaMsgs);
+        msgs.setUpdateMsgs(this.updMsgs);
+        this.stats.setSessionDuration(StatisticsUtil.formatElapsedTime(this.sessionStopwatch.elapsed(TimeUnit.SECONDS)));
+        this.stats.setSessionState(state.toString());
+        this.stats.setMessagesStats(msgs);
+        return this.stats;
+    }
+
+    public void resetStats() {
+        initMsgs();
+    }
+
+    private static void updateReceivedMsg(final Received received) {
+        Preconditions.checkNotNull(received);
+        received.setCount(received.getCount() + 1);
+        received.setTimestamp(StatisticsUtil.getCurrentTimestampInSeconds());
+    }
+
+    private static boolean isAs4ByteCapable(final CParameters cp) {
+        if (cp instanceof As4BytesCase) {
+            return ((As4BytesCase) cp).getAs4BytesCapability() != null;
+        }
+        return false;
+    }
+
+    private static void updateSentMsg(final Sent sent) {
+        Preconditions.checkNotNull(sent);
+        sent.setCount(sent.getCount() + 1);
+        sent.setTimestamp(StatisticsUtil.getCurrentTimestampInSeconds());
+    }
+
+    private static AdvertizedTableTypes addTableType(final BgpTableType type) {
+        Preconditions.checkNotNull(type);
+        final AdvertizedTableTypes att = new AdvertizedTableTypes();
+        att.setAfi(type.getAfi().getSimpleName());
+        att.setSafi(type.getSafi().getSimpleName());
+        return att;
+    }
+
+    private static SpeakerPreferences setSpeakerPref(final Channel channel, final Optional<BGPSessionPreferences> localPreferences) {
+        Preconditions.checkNotNull(channel);
+        final SpeakerPreferences pref = new SpeakerPreferences();
+        final InetSocketAddress isa = (InetSocketAddress) channel.localAddress();
+        pref.setAddress(isa.getAddress().getHostAddress());
+        pref.setPort(isa.getPort());
+        final List<AdvertizedTableTypes> tt = Lists.newArrayList();
+        if (localPreferences.isPresent()) {
+            final BGPSessionPreferences localPref = localPreferences.get();
+            pref.setBgpId(localPref.getBgpId().getValue());
+            pref.setAs(localPref.getMyAs().getValue());
+            pref.setHoldtime(localPref.getHoldTime());
+            if (localPref.getParams() != null && !localPref.getParams().isEmpty()) {
+                for (final BgpParameters param : localPref.getParams()) {
+                    final CParameters cp = param.getCParameters();
+                    if (cp instanceof MultiprotocolCase) {
+                        final MultiprotocolCapability mc = ((MultiprotocolCase) cp).getMultiprotocolCapability();
+                        final AdvertizedTableTypes att = new AdvertizedTableTypes();
+                        att.setAfi(mc.getAfi().getSimpleName());
+                        att.setSafi(mc.getSafi().getSimpleName());
+                        tt.add(att);
+                    }
+                    pref.setFourOctetAsCapability(isAs4ByteCapable(cp));
+                }
+            }
+        }
+        pref.setAdvertizedTableTypes(tt);
+        return pref;
+    }
+
+    private static PeerPreferences setPeerPref(final Open remoteOpen, final Channel channel, final Collection<BgpTableType> tableTypes) {
+        Preconditions.checkNotNull(remoteOpen);
+        Preconditions.checkNotNull(channel);
+        final PeerPreferences pref = new PeerPreferences();
+        final InetSocketAddress isa = (InetSocketAddress) channel.remoteAddress();
+        pref.setAddress(isa.getAddress().getHostAddress());
+        pref.setPort(isa.getPort());
+        pref.setBgpId(remoteOpen.getBgpIdentifier().getValue());
+        pref.setAs(remoteOpen.getMyAsNumber().longValue());
+        pref.setHoldtime(remoteOpen.getHoldTimer());
+        final List<AdvertizedTableTypes> tt = Lists.newArrayList();
+        for (final BgpTableType t : tableTypes) {
+            tt.add(addTableType(t));
+        }
+        if (remoteOpen.getBgpParameters() != null && !remoteOpen.getBgpParameters().isEmpty()) {
+            for (final BgpParameters param : remoteOpen.getBgpParameters()) {
+                pref.setFourOctetAsCapability(isAs4ByteCapable(param.getCParameters()));
+            }
+        }
+        pref.setAdvertizedTableTypes(tt);
+        return pref;
+    }
+}
index c3786f4bd5dddc00d2b592974c25e886dcd7ed18..2cd28a8f0f0436088b84ed336a113c7f27d19b20 100644 (file)
@@ -28,6 +28,7 @@ import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
 import org.opendaylight.protocol.bgp.rib.DefaultRibReference;
@@ -66,9 +67,12 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRibBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.routes.Ipv4RoutesCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.routes.Ipv6RoutesCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Ipv4AddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.UnicastSubsequentAddressFamily;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -91,6 +95,7 @@ public final class RIBImpl extends DefaultRibReference implements AutoCloseable,
     private final List<BgpTableType> localTables;
     private final RIBTables tables;
     private final BlockingQueue<Peer> peers;
+    private final DataBroker dataBroker;
     private final Thread scheduler = new Thread(new Runnable() {
 
         @Override
@@ -134,6 +139,7 @@ public final class RIBImpl extends DefaultRibReference implements AutoCloseable,
         this.localTables = ImmutableList.copyOf(localTables);
         this.tables = new RIBTables(extensions);
         this.peers = new LinkedBlockingQueue<>();
+        this.dataBroker = dps;
 
         LOG.debug("Instantiating RIB table {} at {}", ribId, getInstanceIdentifier());
 
@@ -381,4 +387,29 @@ public final class RIBImpl extends DefaultRibReference implements AutoCloseable,
     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
     }
+
+    @Override
+    public long getRoutesCount(final TablesKey key) {
+        try {
+            final Optional<Tables> tableMaybe = this.dataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL,
+                    getInstanceIdentifier().child(LocRib.class).child(Tables.class, key)).checkedGet();
+            if (tableMaybe.isPresent()) {
+                final Tables table = tableMaybe.get();
+                if (table.getRoutes() instanceof Ipv4RoutesCase) {
+                    final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
+                    if (routesCase.getIpv4Routes() != null && routesCase.getIpv4Routes().getIpv4Route() != null) {
+                        return routesCase.getIpv4Routes().getIpv4Route().size();
+                    }
+                } else if (table.getRoutes() instanceof Ipv6RoutesCase) {
+                    final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
+                    if (routesCase.getIpv6Routes() != null && routesCase.getIpv6Routes().getIpv6Route() != null) {
+                        return routesCase.getIpv6Routes().getIpv6Route().size();
+                    }
+                }
+            }
+        } catch (ReadFailedException e) {
+            //no-op
+        }
+        return 0;
+    }
 }
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPSessionStatistics.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPSessionStatistics.java
new file mode 100644 (file)
index 0000000..1ea8e49
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, 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.protocol.bgp.rib.impl.spi;
+
+import org.opendaylight.controller.config.yang.bgp.rib.impl.BgpSessionState;
+
+/**
+ * Serves to expose BGP session statistics to the BGP Peer, to which the session is related.
+ */
+public interface BGPSessionStatistics {
+
+    /**
+     * Retrieves actual BGP session state. Containing all information collected from the session.
+     *
+     * @return State of the BGP session.
+     */
+    BgpSessionState getBgpSesionState();
+
+    /**
+     * Resets BGP session statistics. Sets counters values to zero.
+     */
+    void resetSessionStats();
+}
index 6d74d5f5a39bec0cb25c707954b9fc30b6fb5463..e81c45a18493671fbb54028bb6fbe8b32e93d685 100644 (file)
@@ -8,7 +8,6 @@
 package org.opendaylight.protocol.bgp.rib.impl.spi;
 
 import java.util.List;
-
 import org.opendaylight.protocol.bgp.rib.spi.Peer;
 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
@@ -40,4 +39,6 @@ public interface RIB {
     ReconnectStrategyFactory getSessionStrategyFactory();
 
     AdjRIBsOutRegistration registerRIBsOut(Peer bgpPeer, AdjRIBsOut aro);
+
+    long getRoutesCount(TablesKey key);
 }
index 22991f173f41be60cd974eee561f9cb664ad1cf9..42e9a6fa010a0b83f6ca3b4327b8a1c04746c823 100644 (file)
@@ -17,6 +17,7 @@ module odl-bgp-rib-impl-cfg {
     import protocol-framework { prefix pf; revision-date 2014-03-13; }
     import odl-tcpmd5-cfg { prefix tcpmd5; revision-date 2014-04-27; }
     import odl-tcpmd5-netty-cfg { prefix tcpmd5n; revision-date 2014-04-27; }
+    import rpc-context { prefix rpcx; revision-date 2013-06-17; }
 
     organization "Cisco Systems, Inc.";
 
@@ -360,6 +361,219 @@ module odl-bgp-rib-impl-cfg {
         }
     }
 
+    grouping message-state {
+        leaf count {
+            description "Total number of BGP messages.";
+            type uint32;
+            default 0;
+        }
+
+        leaf timestamp {
+            description "The BGP message timestamp (seconds).";
+            type uint32;
+            default 0;
+        }
+    }
+
+    grouping message-stats {
+        container received {
+            description "The received BGP messages statistics.";
+            uses message-state;
+        }
+
+        container sent {
+            description "The sent BGP messages statistics.";
+            uses message-state;
+        }
+    }
+
+    grouping error {
+        leaf code {
+            description "The BGP error code.";
+            type uint8;
+            default 0;
+        }
+
+        leaf sub-code {
+            description "The BGP error sub-code.";
+            type uint8;
+            default 0;
+        }
+    }
+
+    grouping preferences {
+        leaf bgp-id {
+            description "The BGP Identifier.";
+            type string;
+            default "";
+        }
+
+        leaf address {
+            description "The IP address of BGP connection.";
+            type string;
+            default "";
+        }
+
+        leaf port {
+            description "The port for connection between the BGP peers.";
+            type uint16;
+            default 0;
+        }
+
+        leaf as {
+            description "Autonomous system number.";
+            type uint32;
+            default 0;
+        }
+
+        leaf holdtime {
+            description "Time interval (in seconds) for HoldTimer proposed by the peer.";
+            type uint16;
+            default 0;
+        }
+
+        leaf four-octet-as-capability {
+            description "The BGP peer 4 byte AS numbers support capability.";
+            type boolean;
+            default "false";
+        }
+
+        list advertized-table-types {
+            description "The BGP Table-type capabilities advertized by the BGP peer.";
+            leaf afi {
+                description "Address Family Identifier.";
+                type string;
+                default "";
+            }
+            leaf safi {
+                description "Subsequent Address Family Identifier.";
+                type string;
+                default "";
+            }
+        }
+    }
+
+    identity peer-rpc;
+
+    augment "/config:modules/config:module/config:state" {
+        case bgp-peer {
+            when "/config:modules/config:module/config:type = 'bgp-peer'";
+
+            rpcx:rpc-context-instance "peer-rpc";
+
+            container bgp-peer-state {
+                list route-table {
+
+                    leaf table-type {
+                        description "The table name - composed of AFI and SAFI.";
+                        type string;
+                    }
+
+                    leaf routes-count {
+                        description "The total number of routes in table.";
+                        type uint32;
+                        default 0;
+                    }
+                }
+
+                leaf session-established-count {
+                    description "The total number of time the BGP session was transitioned to Up state.";
+                    type uint32;
+                    default 0;
+                }
+            }
+
+            container bgp-session-state {
+
+                leaf session-state {
+                    description "The BGP peer connection state.";
+                    type string;
+                }
+
+                leaf session-duration {
+                    description "The session duration (time formated d:HH:mm:ss).";
+                    type string;
+                }
+
+                leaf holdtime-current {
+                    description "Time interval (in seconds) for HoldTimer established with the peer.";
+                    type uint16;
+                    default 0;
+                }
+
+                leaf keepalive-current {
+                    description "Time interval (in seconds) for KeepAlive established with the peer.";
+                    type uint16;
+                    default 0;
+                }
+
+                container speaker-preferences {
+                    description "The BGP speaker preferences, to which this BGP peer is connected.";
+                    uses preferences;
+                }
+
+                container peer-preferences {
+                    description "The BGP peer preferences.";
+                    uses preferences;
+                }
+
+                container messages-stats {
+                    description "The BGP messages statistics.";
+                    container total-msgs {
+                        description "The statistics for all received/sent BGP messages.";
+                        uses message-stats;
+                    }
+
+                    container keep-alive-msgs {
+                        description "The statistics for received/sent BGP Keep-Alive messages.";
+                        uses message-stats;
+                    }
+
+                    container update-msgs {
+                        description "The statistics for received/sent BGP Update messages.";
+                        uses message-stats;
+                    }
+
+                    container error-msgs {
+                        container error-received {
+                            description "The received BGP Error (notification) messages statistics.";
+                            uses message-state;
+                            uses error;
+                        }
+
+                        container error-sent {
+                            description "The sent BGP Error (notification) messages statistics.";
+                            uses message-state;
+                            uses error;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    rpc reset-session {
+        description "Restart the session between BGP peers";
+        input {
+            uses rpcx:rpc-context-ref {
+                refine context-instance {
+                    rpcx:rpc-context-instance peer-rpc;
+                }
+            }
+        }
+     }
+
+     rpc reset-stats {
+        description "Reset the BGP peer statistics.";
+        input {
+            uses rpcx:rpc-context-ref {
+                refine context-instance {
+                    rpcx:rpc-context-instance peer-rpc;
+                }
+            }
+        }
+     }
+
     identity rib-impl {
         base config:module-type;
         config:provided-service bgprib:rib;
index cdbaa165d2c6c3e9323ef5da71e919a8705f51d3..3f995c05173969c613c8a0ae3967e66c45b13adf 100644 (file)
@@ -17,6 +17,7 @@ import com.google.common.util.concurrent.CheckedFuture;
 import io.netty.channel.Channel;
 import io.netty.channel.EventLoop;
 import java.math.BigInteger;
+import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -37,10 +38,12 @@ import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
 import org.opendaylight.protocol.bgp.rib.spi.AdjRIBsIn;
@@ -161,7 +164,7 @@ public class ApplicationPeerTest {
 
     @SuppressWarnings("unchecked")
     @Before
-    public void setUp() throws InterruptedException, ExecutionException {
+    public void setUp() throws InterruptedException, ExecutionException, ReadFailedException {
         MockitoAnnotations.initMocks(this);
         final List<BgpTableType> localTables = new ArrayList<>();
         this.routes = new ArrayList<>();
@@ -207,6 +210,11 @@ public class ApplicationPeerTest {
         this.r = new RIBImpl(new RibId("test"), new AsNumber(5L), new Ipv4Address("127.0.0.1"),
             context , this.dispatcher, this.tcpStrategyFactory, this.tcpStrategyFactory, this.dps, localTables);
         this.peer = new ApplicationPeer(new ApplicationRibId("t"), new Ipv4Address("127.0.0.1"), this.r);
+        final ReadOnlyTransaction readTx = Mockito.mock(ReadOnlyTransaction.class);
+        Mockito.doReturn(readTx).when(this.dps).newReadOnlyTransaction();
+        final CheckedFuture<Optional<DataObject>, ReadFailedException> readFuture = Mockito.mock(CheckedFuture.class);
+        Mockito.doReturn(Optional.<DataObject>absent()).when(readFuture).checkedGet();
+        Mockito.doReturn(readFuture).when(readTx).read(Mockito.eq(LogicalDatastoreType.OPERATIONAL), Mockito.any(InstanceIdentifier.class));
     }
 
     @Test
@@ -259,6 +267,8 @@ public class ApplicationPeerTest {
         Mockito.doReturn(null).when(this.eventLoop).schedule(any(Runnable.class), any(long.class), any(TimeUnit.class));
         Mockito.doReturn(Boolean.TRUE).when(this.channel).isWritable();
         Mockito.doReturn(null).when(this.channel).close();
+        Mockito.doReturn(new InetSocketAddress("localhost", 12345)).when(this.channel).remoteAddress();
+        Mockito.doReturn(new InetSocketAddress("localhost", 12345)).when(this.channel).localAddress();
         final List<BgpParameters> params = Lists.newArrayList(new BgpParametersBuilder().setCParameters(new MultiprotocolCaseBuilder()
             .setMultiprotocolCapability(new MultiprotocolCapabilityBuilder().setAfi(Ipv4AddressFamily.class).setSafi(UnicastSubsequentAddressFamily.class).build()).build()).build());
         this.session = new BGPSessionImpl(this.classic, this.channel, new OpenBuilder().setBgpIdentifier(new Ipv4Address("1.1.1.1")).setHoldTimer(50).setMyAsNumber(72).setBgpParameters(params).build(), 30);
@@ -284,9 +294,13 @@ public class ApplicationPeerTest {
         assertEquals(2, this.routes.size());
 
         //create new peer so that it gets advertized routes from RIB
-        final BGPPeer testingPeer = new BGPPeer("testingPeer", this.r);
-        testingPeer.onSessionUp(this.session);
-        assertEquals(4, this.routes.size());
+        try (final BGPPeer testingPeer = new BGPPeer("testingPeer", this.r)) {
+            testingPeer.onSessionUp(this.session);
+            assertEquals(4, this.routes.size());
+            assertEquals(1, testingPeer.getBgpPeerState().getSessionEstablishedCount().intValue());
+            assertEquals(1, testingPeer.getBgpPeerState().getRouteTable().size());
+            assertNotNull(testingPeer.getBgpSessionState());
+        }
 
         ub.setNlri(null);
         ub.setWithdrawnRoutes(new WithdrawnRoutesBuilder().setWithdrawnRoutes(prefs).build());
index 367e0e5fa44c69522ec0b27423f45f92375fd8ff..3177feccad3aeb432ea11f0890ad7f0954aef674 100644 (file)
@@ -8,6 +8,8 @@
 
 package org.opendaylight.protocol.bgp.rib.impl;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
@@ -35,6 +37,7 @@ import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.config.yang.bgp.rib.impl.BgpSessionState;
 import org.opendaylight.protocol.bgp.parser.BGPError;
 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
@@ -63,6 +66,8 @@ public class BGPSessionImplTest {
     private static final int HOLD_TIMER = 3;
     private static final AsNumber AS_NUMBER = new AsNumber(30L);
     private static final Ipv4Address BGP_ID = new Ipv4Address("1.1.1.2");
+    private static final String LOCAL_IP = "1.1.1.4";
+    private static final int LOCAL_PORT = 12345;
 
     @Mock
     private EventLoop eventLoop;
@@ -120,6 +125,7 @@ public class BGPSessionImplTest {
         }).when(this.eventLoop).schedule(any(Runnable.class), any(long.class), any(TimeUnit.class));
         doReturn("TestingChannel").when(this.speakerListener).toString();
         doReturn(new InetSocketAddress(InetAddress.getByName(BGP_ID.getValue()), 179)).when(this.speakerListener).remoteAddress();
+        doReturn(new InetSocketAddress(InetAddress.getByName(LOCAL_IP), LOCAL_PORT)).when(this.speakerListener).localAddress();
         doReturn(this.pipeline).when(this.speakerListener).pipeline();
         doReturn(this.pipeline).when(this.pipeline).replace(any(ChannelHandler.class), any(String.class), any(ChannelHandler.class));
         doReturn(mock(ChannelFuture.class)).when(this.speakerListener).close();
@@ -130,26 +136,58 @@ public class BGPSessionImplTest {
     @Test
     public void testBGPSession() {
         this.bgpSession.sessionUp();
-        Assert.assertEquals(BGPSessionImpl.State.Up, this.bgpSession.getState());
-        Assert.assertEquals(AS_NUMBER, this.bgpSession.getAsNumber());
-        Assert.assertEquals(BGP_ID, this.bgpSession.getBgpId());
-        Assert.assertEquals(1, this.bgpSession.getAdvertisedTableTypes().size());
-        Assert.assertTrue(this.listener.up);
+        assertEquals(BGPSessionImpl.State.Up, this.bgpSession.getState());
+        assertEquals(AS_NUMBER, this.bgpSession.getAsNumber());
+        assertEquals(BGP_ID, this.bgpSession.getBgpId());
+        assertEquals(1, this.bgpSession.getAdvertisedTableTypes().size());
+        assertTrue(this.listener.up);
+        //test stats
+        BgpSessionState state = this.bgpSession.getBgpSesionState();
+        assertEquals(HOLD_TIMER, state.getHoldtimeCurrent().intValue());
+        assertEquals(1, state.getKeepaliveCurrent().intValue());
+        assertEquals("Up", state.getSessionState());
+        assertEquals(BGP_ID.getValue(), state.getPeerPreferences().getAddress());
+        assertEquals(AS_NUMBER.getValue(), state.getPeerPreferences().getAs());
+        assertEquals(BGP_ID.getValue(), state.getPeerPreferences().getBgpId());
+        assertEquals(1, state.getPeerPreferences().getAdvertizedTableTypes().size());
+        assertEquals(HOLD_TIMER, state.getPeerPreferences().getHoldtime().intValue());
+        assertTrue(state.getPeerPreferences().getFourOctetAsCapability());
+        assertEquals(LOCAL_IP, state.getSpeakerPreferences().getAddress());
+        assertEquals(LOCAL_PORT, state.getSpeakerPreferences().getPort().intValue());
+        assertEquals(0, state.getMessagesStats().getTotalMsgs().getReceived().getCount().longValue());
+        assertEquals(0, state.getMessagesStats().getTotalMsgs().getSent().getCount().longValue());
 
         this.bgpSession.handleMessage(new UpdateBuilder().build());
-        Assert.assertEquals(1, this.listener.getListMsg().size());
-        Assert.assertTrue(this.listener.getListMsg().get(0) instanceof Update);
+        assertEquals(1, this.listener.getListMsg().size());
+        assertTrue(this.listener.getListMsg().get(0) instanceof Update);
+        assertEquals(1, state.getMessagesStats().getTotalMsgs().getReceived().getCount().longValue());
+        assertEquals(1, state.getMessagesStats().getUpdateMsgs().getReceived().getCount().longValue());
+        assertEquals(0, state.getMessagesStats().getUpdateMsgs().getSent().getCount().longValue());
 
         this.bgpSession.handleMessage(new KeepaliveBuilder().build());
         this.bgpSession.handleMessage(new KeepaliveBuilder().build());
+        assertEquals(3, state.getMessagesStats().getTotalMsgs().getReceived().getCount().longValue());
+        assertEquals(2, state.getMessagesStats().getKeepAliveMsgs().getReceived().getCount().longValue());
+        assertEquals(0, state.getMessagesStats().getKeepAliveMsgs().getSent().getCount().longValue());
 
         this.bgpSession.close();
-        Assert.assertEquals(BGPSessionImpl.State.Idle, this.bgpSession.getState());
-        Assert.assertEquals(1, this.receivedMsgs.size());
-        Assert.assertTrue(this.receivedMsgs.get(0) instanceof Notify);
+        assertEquals(BGPSessionImpl.State.Idle, this.bgpSession.getState());
+        assertEquals(1, this.receivedMsgs.size());
+        assertTrue(this.receivedMsgs.get(0) instanceof Notify);
         final Notify error = (Notify) this.receivedMsgs.get(0);
-        Assert.assertEquals(BGPError.CEASE.getCode(), error.getErrorCode().shortValue());
+        assertEquals(BGPError.CEASE.getCode(), error.getErrorCode().shortValue());
+        assertEquals(BGPError.CEASE.getSubcode(), error.getErrorSubcode().shortValue());
         Mockito.verify(this.speakerListener).close();
+        assertEquals(3, state.getMessagesStats().getTotalMsgs().getReceived().getCount().longValue());
+        assertEquals(1, state.getMessagesStats().getTotalMsgs().getSent().getCount().longValue());
+        assertEquals(1, state.getMessagesStats().getErrorMsgs().getErrorSent().getCount().longValue());
+        assertEquals(BGPError.CEASE.getCode(), state.getMessagesStats().getErrorMsgs().getErrorSent().getCode().shortValue());
+        assertEquals(BGPError.CEASE.getSubcode(), state.getMessagesStats().getErrorMsgs().getErrorSent().getSubCode().shortValue());
+
+        this.bgpSession.resetSessionStats();
+        assertEquals(0, state.getMessagesStats().getTotalMsgs().getReceived().getCount().longValue());
+        assertEquals(0, state.getMessagesStats().getTotalMsgs().getSent().getCount().longValue());
+        assertEquals(0, state.getMessagesStats().getErrorMsgs().getErrorSent().getCount().longValue());
     }
 
     @Test
@@ -167,6 +205,9 @@ public class BGPSessionImplTest {
     @Test
     public void testHandleNotifyMsg() {
         this.bgpSession.handleMessage(new NotifyBuilder().setErrorCode(BGPError.BAD_BGP_ID.getCode()).setErrorSubcode(BGPError.BAD_BGP_ID.getSubcode()).build());
+        assertEquals(1, this.bgpSession.getBgpSesionState().getMessagesStats().getErrorMsgs().getErrorReceived().getCount().longValue());
+        assertEquals(BGPError.BAD_BGP_ID.getCode(), this.bgpSession.getBgpSesionState().getMessagesStats().getErrorMsgs().getErrorReceived().getCode().shortValue());
+        assertEquals(BGPError.BAD_BGP_ID.getSubcode(), this.bgpSession.getBgpSesionState().getMessagesStats().getErrorMsgs().getErrorReceived().getSubCode().shortValue());
         Assert.assertEquals(BGPSessionImpl.State.Idle, this.bgpSession.getState());
         Mockito.verify(this.speakerListener).close();
     }
index 6d185e61eb1ac4fadb8f3a6b289ddc147de84436..ba12d392f61bedf0339e362df64f5d7fbba356e4 100644 (file)
@@ -118,6 +118,7 @@ public class FSMTest {
         doReturn(null).when(this.eventLoop).schedule(any(Runnable.class), any(long.class), any(TimeUnit.class));
         doReturn("TestingChannel").when(this.speakerListener).toString();
         doReturn(new InetSocketAddress(peerAddress, 179)).when(this.speakerListener).remoteAddress();
+        doReturn(new InetSocketAddress(peerAddress, 179)).when(this.speakerListener).localAddress();
         doReturn(this.pipeline).when(this.speakerListener).pipeline();
         doReturn(this.pipeline).when(this.pipeline).replace(any(ChannelHandler.class), any(String.class), any(ChannelHandler.class));
         doReturn(mock(ChannelFuture.class)).when(this.speakerListener).close();
index be4e34baef62670fcb812c30d3b9457a6d7acaa3..ab15b99745df94f8d416ea7c0ad58562206ab94b 100644 (file)
@@ -11,7 +11,7 @@ package org.opendaylight.protocol.pcep.impl;
 import com.google.common.base.Preconditions;
 import io.netty.channel.Channel;
 import java.net.InetSocketAddress;
-import java.util.concurrent.TimeUnit;
+import org.opendaylight.protocol.util.StatisticsUtil;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.stats.rev141006.pcep.session.state.LocalPref;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.stats.rev141006.pcep.session.state.LocalPrefBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.stats.rev141006.pcep.session.state.Messages;
@@ -56,7 +56,7 @@ final class PCEPSessionState {
         this.errorsBuilder.setSentErrorMsgCount(this.sentErrMsgCount);
         this.errorsBuilder.setLastReceivedError(this.lastReceivedErrorBuilder.build());
         this.errorsBuilder.setLastSentError(this.lastSentErrorBuilder.build());
-        this.msgsBuilder.setLastSentMsgTimestamp(TimeUnit.MILLISECONDS.toSeconds(this.lastSentMsgTimestamp));
+        this.msgsBuilder.setLastSentMsgTimestamp(this.lastSentMsgTimestamp);
         this.msgsBuilder.setReceivedMsgCount(this.receivedMsgCount);
         this.msgsBuilder.setSentMsgCount(this.sentMsgCount);
         this.msgsBuilder.setUnknownMsgReceived(unknownMessagesCount);
@@ -103,7 +103,7 @@ final class PCEPSessionState {
     }
 
     public void updateLastSentMsg() {
-        this.lastSentMsgTimestamp = System.currentTimeMillis();
+        this.lastSentMsgTimestamp = StatisticsUtil.getCurrentTimestampInSeconds();
         this.sentMsgCount++;
     }
 
index 65dc595f637bcf3d2f7bcd16aa8cb4575a2b69c2..699fbec60a9d8cb5b6de0af1b98cf22c5456a52d 100644 (file)
@@ -22,6 +22,7 @@ import org.opendaylight.controller.config.yang.pcep.topology.provider.ReplyTime;
 import org.opendaylight.controller.config.yang.pcep.topology.provider.SessionState;
 import org.opendaylight.controller.config.yang.pcep.topology.provider.StatefulMessages;
 import org.opendaylight.protocol.pcep.PCEPSession;
+import org.opendaylight.protocol.util.StatisticsUtil;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.crabbe.initiated.rev131126.Pcinitiate;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.Pcupd;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.Message;
@@ -70,7 +71,7 @@ final class SessionListenerState {
 
     public StatefulMessages getStatefulMessages() {
         final StatefulMessages msgs = new StatefulMessages();
-        msgs.setLastReceivedRptMsgTimestamp(TimeUnit.MILLISECONDS.toSeconds(this.lastReceivedRptMsgTimestamp));
+        msgs.setLastReceivedRptMsgTimestamp(this.lastReceivedRptMsgTimestamp);
         msgs.setReceivedRptMsgCount(this.receivedRptMsgCount);
         msgs.setSentInitMsgCount(this.sentInitMsgCount);
         msgs.setSentUpdMsgCount(this.sentUpdMsgCount);
@@ -112,7 +113,7 @@ final class SessionListenerState {
         state.setLocalPref(this.localPref);
         state.setPeerPref(this.peerPref);
         state.setMessages(getMessageStats(session.getMessages()));
-        state.setSessionDuration(formatElapsedTime(this.sessionUpDuration.elapsed(TimeUnit.SECONDS)));
+        state.setSessionDuration(StatisticsUtil.formatElapsedTime(this.sessionUpDuration.elapsed(TimeUnit.SECONDS)));
         return state;
     }
 
@@ -121,7 +122,7 @@ final class SessionListenerState {
     }
 
     public void updateLastReceivedRptMsg() {
-        this.lastReceivedRptMsgTimestamp = System.currentTimeMillis();
+        this.lastReceivedRptMsgTimestamp = StatisticsUtil.getCurrentTimestampInSeconds();
         this.receivedRptMsgCount++;
     }
 
@@ -171,12 +172,4 @@ final class SessionListenerState {
         msgs.setUnknownMsgReceived(msgs.getUnknownMsgReceived());
         return msgs;
     }
-
-    private static String formatElapsedTime(final long seconds) {
-        return String.format("%2d:%02d:%02d:%02d",
-                TimeUnit.SECONDS.toDays(seconds),
-                TimeUnit.SECONDS.toHours(seconds) - TimeUnit.DAYS.toHours(TimeUnit.SECONDS.toDays(seconds)),
-                TimeUnit.SECONDS.toMinutes(seconds) - TimeUnit.HOURS.toMinutes(TimeUnit.SECONDS.toHours(seconds)),
-                seconds - TimeUnit.MINUTES.toSeconds(TimeUnit.SECONDS.toMinutes(seconds)));
-    }
 }
diff --git a/util/src/main/java/org/opendaylight/protocol/util/StatisticsUtil.java b/util/src/main/java/org/opendaylight/protocol/util/StatisticsUtil.java
new file mode 100644 (file)
index 0000000..7b99af9
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, 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.protocol.util;
+
+import com.google.common.base.Preconditions;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Statistics utility class.
+ */
+public final class StatisticsUtil {
+
+    private StatisticsUtil() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Formats elapsed time in seconds to form days:hours:minutes:seconds.
+     *
+     * @param seconds Elapsed time in seconds.
+     * @return Formated time as string d:hh:mm:ss
+     */
+    public static String formatElapsedTime(final long seconds) {
+        Preconditions.checkArgument(seconds >= 0);
+        return String.format("%1d:%02d:%02d:%02d",
+                TimeUnit.SECONDS.toDays(seconds),
+                TimeUnit.SECONDS.toHours(seconds) - TimeUnit.DAYS.toHours(TimeUnit.SECONDS.toDays(seconds)),
+                TimeUnit.SECONDS.toMinutes(seconds) - TimeUnit.HOURS.toMinutes(TimeUnit.SECONDS.toHours(seconds)),
+                seconds - TimeUnit.MINUTES.toSeconds(TimeUnit.SECONDS.toMinutes(seconds)));
+    }
+
+    /**
+     * Get current time in seconds.
+     *
+     * @return the difference, measured in seconds, between the current time and midnight, January 1, 1970 UTC.
+     * @see System.currentTimeMillis()
+     */
+    public static long getCurrentTimestampInSeconds() {
+        return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
+    }
+}
diff --git a/util/src/test/java/org/opendaylight/protocol/util/StatisticsUtilTest.java b/util/src/test/java/org/opendaylight/protocol/util/StatisticsUtilTest.java
new file mode 100644 (file)
index 0000000..682e697
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, 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.protocol.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import org.junit.Test;
+
+public class StatisticsUtilTest {
+
+    @Test
+    public void testGetCurrentTimestampInSeconds() {
+        assertEquals(System.currentTimeMillis() / 1000, StatisticsUtil.getCurrentTimestampInSeconds());
+    }
+
+    @Test
+    public void testFormatElapsedTime() {
+        assertEquals("1:01:01:01", StatisticsUtil.formatElapsedTime(90061));
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testFormatElapsedTimeInvalidInput() {
+        StatisticsUtil.formatElapsedTime(-1);
+    }
+
+    @Test(expected=UnsupportedOperationException.class)
+    public void testPrivateConstructor() throws Throwable {
+        final Constructor<StatisticsUtil> c = StatisticsUtil.class.getDeclaredConstructor();
+        c.setAccessible(true);
+        try {
+            c.newInstance();
+        } catch (InvocationTargetException e) {
+            throw e.getCause();
+        }
+    }
+
+}