vom: Add support for new stats 22/16122/24
authorMohsin Kazmi <sykazmi@cisco.com>
Wed, 21 Nov 2018 18:00:46 +0000 (19:00 +0100)
committerNeale Ranns <nranns@cisco.com>
Thu, 13 Dec 2018 07:46:02 +0000 (07:46 +0000)
This patch deprecates old stats and adds support for
new stats.

This implements the PULL design where client will enable
stats on objects (i.e. interfaces, etc), register a call
back function which will run once stats will be ready for
enabled object and call HW::read_stats() periodically to
read stats for enabled objects from VPP.

Change-Id: I32525a417427be87408d01f8bc93a731602ff690
Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
14 files changed:
extras/vom/CMakeLists.txt
extras/vom/vom/CMakeLists.txt
extras/vom/vom/hw.cpp
extras/vom/vom/hw.hpp
extras/vom/vom/interface.cpp
extras/vom/vom/interface.hpp
extras/vom/vom/interface_cmds.cpp
extras/vom/vom/interface_cmds.hpp
extras/vom/vom/stat_client.cpp [new file with mode: 0644]
extras/vom/vom/stat_client.hpp [new file with mode: 0644]
extras/vom/vom/stat_reader.cpp [new file with mode: 0644]
extras/vom/vom/stat_reader.hpp [new file with mode: 0644]
extras/vom/vom/test_stats.cpp [new file with mode: 0644]
test/ext/vom_test.cpp

index 6ed14cd..a04df5d 100644 (file)
@@ -24,6 +24,7 @@ set(CMAKE_INSTALL_MESSAGE NEVER)
 
 find_package(VPP)
 find_package(Threads REQUIRED)
+find_package(Boost OPTIONAL_COMPONENTS system filesystem)
 
 add_subdirectory(vom)
 
index 47e73b6..bd8986a 100644 (file)
@@ -11,6 +11,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+unset (VPPAPICLIENT_LIB)
 unset (VAPICLIENT_LIB)
 unset (ACL_FILE)
 unset (NAT_FILE)
@@ -20,13 +21,20 @@ unset (IGMP_FILE)
 unset (VOM_SOURCES)
 unset (VOM_HEADERS)
 
+find_library(VPPAPICLIENT_LIB NAMES vppapiclient REQUIRED)
+find_path(VPPAPICLIENT_INCLUDE_DIR NAMES vpp-api/client/vppapiclient.h)
 find_library(VAPICLIENT_LIB NAMES vapiclient REQUIRED)
 find_path(VAPICLIENT_INCLUDE_DIR NAMES vapi/vapi.hpp)
 
+
+if(NOT VPPAPICLIENT_INCLUDE_DIR OR NOT VPPAPICLIENT_LIB)
+  message(FATAL_ERROR "Cannot find vppapiclient library and/or headers")
+endif()
 if(NOT VAPICLIENT_INCLUDE_DIR OR NOT VAPICLIENT_LIB)
   message(FATAL_ERROR "Cannot find vapiclient library and/or headers")
 endif()
 
+include_directories(${VPPAPICLIENT_INCLUDE_DIR})
 include_directories(${VAPICLIENT_INCLUDE_DIR})
 include_directories(${CMAKE_SOURCE_DIR})
 
@@ -160,6 +168,8 @@ list(APPEND VOM_SOURCES
   route_cmds.cpp
   route_domain.cpp
   route_domain_cmds.cpp
+  stat_client.cpp
+  stat_reader.cpp
   sub_interface_cmds.cpp
   sub_interface.cpp
   tap_interface.cpp
@@ -257,6 +267,8 @@ list(APPEND VOM_HEADERS
   rpc_cmd.hpp
   singular_db.hpp
   singular_db_funcs.hpp
+  stat_client.hpp
+  stat_reader.hpp
   sub_interface.hpp
   tap_interface.hpp
   types.hpp
@@ -271,8 +283,20 @@ add_vpp_library(vom
 
   INSTALL_HEADERS ${VOM_HEADERS}
 
-  LINK_LIBRARIES ${VAPICLIENT_LIB} Threads::Threads boost_thread
-    ${BOOST_SYSTEM_LIB} ${BOOST_FILESYSTEM_LIB} ${BOOST_ASIO_LIB} m rt
+  LINK_LIBRARIES ${VPPAPICLIENT_LIB} ${VAPICLIENT_LIB} Threads::Threads
+    ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} m rt
 
   COMPONENT libvom
 )
+
+if (Boost_FOUND)
+  if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+    add_definitions(-stdlib=libstdc++)
+  endif()
+  add_executable(vom_stats_test test_stats.cpp)
+  if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+    target_link_libraries(vom_stats_test vom stdc++)
+  else()
+    target_link_libraries(vom_stats_test vom)
+  endif()
+endif()
index 0952b60..54f0aa2 100644 (file)
@@ -16,6 +16,7 @@
 #include "vom/hw.hpp"
 #include "vom/hw_cmds.hpp"
 #include "vom/logger.hpp"
+#include "vom/stat_reader.hpp"
 
 namespace VOM {
 HW::cmd_q::cmd_q()
@@ -170,6 +171,11 @@ HW::cmd_q::write()
  * The single Command Queue
  */
 HW::cmd_q* HW::m_cmdQ;
+
+/*
+ * single stat reader
+ */
+stat_reader* HW::m_statReader;
 HW::item<bool> HW::m_poll_state;
 
 /**
@@ -179,6 +185,17 @@ void
 HW::init(HW::cmd_q* f)
 {
   m_cmdQ = f;
+  m_statReader = new stat_reader();
+}
+
+/**
+ * Initialse the connection to VPP
+ */
+void
+HW::init(HW::cmd_q* f, stat_reader* s)
+{
+  m_cmdQ = f;
+  m_statReader = s;
 }
 
 /**
@@ -188,6 +205,7 @@ void
 HW::init()
 {
   m_cmdQ = new cmd_q();
+  m_statReader = new stat_reader();
 }
 
 void
@@ -211,12 +229,13 @@ HW::enqueue(std::queue<cmd*>& cmds)
 bool
 HW::connect()
 {
-  return m_cmdQ->connect();
+  return (m_cmdQ->connect() && m_statReader->connect());
 }
 
 void
 HW::disconnect()
 {
+  m_statReader->disconnect();
   m_cmdQ->disconnect();
 }
 
@@ -249,6 +268,12 @@ HW::poll()
   return (m_poll_state);
 }
 
+void
+HW::read_stats()
+{
+  m_statReader->read();
+}
+
 template <>
 std::string
 HW::item<bool>::to_string() const
index 9ba4750..d10a93a 100644 (file)
@@ -29,6 +29,7 @@
 
 namespace VOM {
 
+class stat_reader;
 class cmd;
 class HW
 {
@@ -286,6 +287,12 @@ public:
    */
   static void init(cmd_q* f);
 
+  /**
+   * Initialise the HW connection to VPP - the UT version passing
+   * a mock Q.
+   */
+  static void init(cmd_q* f, stat_reader* s);
+
   /**
    * Initialise the HW
    */
@@ -326,12 +333,22 @@ public:
    */
   static bool poll();
 
+  /**
+   * read stats from stat segment
+   */
+  static void read_stats();
+
 private:
   /**
    * The command Q toward HW
    */
   static cmd_q* m_cmdQ;
 
+  /**
+   * The stat reader toward HW
+   */
+  static stat_reader* m_statReader;
+
   /**
    * HW::item representing the connection state as determined by polling
    */
index c1894c2..bbbc187 100644 (file)
@@ -23,6 +23,7 @@
 #include "vom/logger.hpp"
 #include "vom/prefix.hpp"
 #include "vom/singular_db_funcs.hpp"
+#include "vom/stat_reader.hpp"
 #include "vom/tap_interface_cmds.hpp"
 
 namespace VOM {
@@ -175,13 +176,8 @@ interface::sweep()
       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
   }
 
-  if (m_stats) {
-    if (stats_type_t::DETAILED == m_stats_type) {
-      HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd(
-        m_stats_type, handle_i(), false));
-    }
-    HW::enqueue(new interface_cmds::stats_disable_cmd(m_hdl.data()));
-    m_stats.reset();
+  if (m_listener) {
+    disable_stats();
   }
 
   // If the interface is up, bring it down
@@ -209,16 +205,8 @@ interface::replay()
     HW::enqueue(new interface_cmds::state_change_cmd(m_state, m_hdl));
   }
 
-  if (m_stats) {
-    if (stats_type_t::DETAILED == m_stats_type) {
-      m_stats_type.set(rc_t::NOOP);
-      HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd(
-        m_stats_type, handle_i(), true));
-    }
-    stat_listener& listener = m_stats->listener();
-    listener.status().set(rc_t::NOOP);
-    m_stats.reset(new interface_cmds::stats_enable_cmd(listener, handle_i()));
-    HW::enqueue(m_stats);
+  if (m_listener) {
+    enable_stats(m_listener, m_stats_type.data());
   }
 
   if (m_table_id && (m_table_id.data() != route::DEFAULT_TABLE)) {
@@ -424,26 +412,101 @@ interface::set(const std::string& tag)
 }
 
 void
-interface::enable_stats_i(interface::stat_listener& el, const stats_type_t& st)
+interface::set(counter_t count, const std::string& stat_type)
+{
+  if ("rx" == stat_type)
+    m_stats.m_rx = count;
+  else if ("tx" == stat_type)
+    m_stats.m_tx = count;
+  else if ("rx-unicast" == stat_type)
+    m_stats.m_rx_unicast = count;
+  else if ("tx-unicast" == stat_type)
+    m_stats.m_tx_unicast = count;
+  else if ("rx-multicast" == stat_type)
+    m_stats.m_rx_multicast = count;
+  else if ("tx-multicast" == stat_type)
+    m_stats.m_tx_multicast = count;
+  else if ("rx-broadcast" == stat_type)
+    m_stats.m_rx_broadcast = count;
+  else if ("tx-broadcast" == stat_type)
+    m_stats.m_rx_broadcast = count;
+}
+
+const interface::stats_t&
+interface::get_stats(void) const
+{
+  return m_stats;
+}
+
+void
+interface::publish_stats()
 {
-  if (!m_stats) {
+  m_listener->handle_interface_stat(*this);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const interface::stats_t& stats)
+{
+  os << "["
+     << "rx [packets " << stats.m_rx.packets << ", bytes " << stats.m_rx.bytes
+     << "]"
+     << " rx-unicast [packets " << stats.m_rx_unicast.packets << ", bytes "
+     << stats.m_rx_unicast.bytes << "]"
+     << " rx-multicast [packets " << stats.m_rx_multicast.packets << ", bytes "
+     << stats.m_rx_multicast.bytes << "]"
+     << " rx-broadcast [packets " << stats.m_rx_broadcast.packets << ", bytes "
+     << stats.m_rx_broadcast.bytes << "]"
+     << " tx [packets " << stats.m_tx.packets << ", bytes " << stats.m_tx.bytes
+     << "]"
+     << " tx-unicast [packets " << stats.m_tx_unicast.packets << ", bytes "
+     << stats.m_tx_unicast.bytes << "]"
+     << " tx-multicast [packets " << stats.m_tx_multicast.packets << ", bytes "
+     << stats.m_tx_multicast.bytes << "]"
+     << " tx-broadcast [packets " << stats.m_tx_broadcast.packets << ", bytes "
+     << stats.m_tx_broadcast.bytes << "]]" << std::endl;
+
+  return (os);
+}
+
+void
+interface::enable_stats_i(interface::stat_listener* el, const stats_type_t& st)
+{
+  if (el != NULL) {
     if (stats_type_t::DETAILED == st) {
-      m_stats_type = st;
+      m_stats_type.set(rc_t::NOOP);
       HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd(
         m_stats_type, handle_i(), true));
     }
-    m_stats.reset(new interface_cmds::stats_enable_cmd(el, handle_i()));
-    HW::enqueue(m_stats);
-    HW::write();
+    stat_reader::registers(*this);
+    m_listener = el;
   }
 }
 
 void
-interface::enable_stats(interface::stat_listener& el, const stats_type_t& st)
+interface::enable_stats(interface::stat_listener* el, const stats_type_t& st)
 {
   singular()->enable_stats_i(el, st);
 }
 
+void
+interface::disable_stats_i()
+{
+  if (m_listener != NULL) {
+    if (stats_type_t::DETAILED == m_stats_type) {
+      HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd(
+        m_stats_type, handle_i(), false));
+    }
+    stat_reader::unregisters(*this);
+    m_listener = NULL;
+  }
+}
+
+void
+interface::disable_stats()
+{
+  singular()->disable_stats_i();
+}
+
 std::shared_ptr<interface>
 interface::singular_i() const
 {
index 42dfa67..100c3ca 100644 (file)
 #include "vom/route_domain.hpp"
 #include "vom/rpc_cmd.hpp"
 #include "vom/singular_db.hpp"
+#include "vom/stat_client.hpp"
 
 namespace VOM {
 /**
  * Forward declaration of the stats and events command
  */
 namespace interface_cmds {
-class stats_enable_cmd;
 class events_cmd;
 };
+class stat_reader;
 
 /**
  * A representation of an interface in VPP
@@ -184,6 +185,21 @@ public:
     oper_state_t(int v, const std::string& s);
   };
 
+  /**
+   * stats_t:
+   */
+  struct stats_t
+  {
+    counter_t m_rx;
+    counter_t m_tx;
+    counter_t m_rx_unicast;
+    counter_t m_tx_unicast;
+    counter_t m_rx_multicast;
+    counter_t m_tx_multicast;
+    counter_t m_rx_broadcast;
+    counter_t m_tx_broadcast;
+  };
+
   /**
    * Construct a new object matching the desried state
    */
@@ -268,6 +284,11 @@ public:
    */
   void set(const std::string& tag);
 
+  /**
+   * Get the interface stats
+   */
+  const stats_t& get_stats(void) const;
+
   /**
    * Comparison operator - only used for UT
    */
@@ -441,12 +462,13 @@ public:
      */
     stat_listener();
 
+    virtual ~stat_listener() = default;
+
     /**
      * Virtual function called on the listener when the command has data
      * ready to process
      */
-    virtual void handle_interface_stat(
-      interface_cmds::stats_enable_cmd* cmd) = 0;
+    virtual void handle_interface_stat(const interface&) = 0;
 
     /**
      * Return the HW::item representing the status
@@ -478,9 +500,14 @@ public:
   /**
    * Enable stats for this interface
    */
-  void enable_stats(stat_listener& el,
+  void enable_stats(stat_listener* el,
                     const stats_type_t& st = stats_type_t::NORMAL);
 
+  /**
+   * Disable stats for this interface
+   */
+  void disable_stats();
+
   /**
    * Enable the reception of events of all interfaces
    */
@@ -499,7 +526,6 @@ protected:
   void set(const handle_t& handle);
   friend class interface_factory;
   friend class pipe;
-
   /**
    * The SW interface handle VPP has asigned to the interface
    */
@@ -580,10 +606,30 @@ private:
 
   static event_handler m_evh;
 
+  /**
+   * friend with stat_reader
+   */
+  friend stat_reader;
+
+  /**
+   * publish stats
+   */
+  void publish_stats();
+
+  /**
+   * Set the interface stat
+   */
+  void set(counter_t count, const std::string& stat_type);
+
   /**
    * enable the interface stats in the singular instance
    */
-  void enable_stats_i(stat_listener& el, const stats_type_t& st);
+  void enable_stats_i(stat_listener* el, const stats_type_t& st);
+
+  /**
+   * disable the interface stats in the singular instance
+   */
+  void disable_stats_i();
 
   /**
    * Commit the acculmulated changes into VPP. i.e. to a 'HW" write.
@@ -622,11 +668,6 @@ private:
    */
   std::shared_ptr<route_domain> m_rd;
 
-  /**
-   * shared pointer to the stats object for this interface.
-   */
-  std::shared_ptr<interface_cmds::stats_enable_cmd> m_stats;
-
   /**
    * The state of the interface
    */
@@ -647,6 +688,16 @@ private:
    */
   HW::item<stats_type_t> m_stats_type;
 
+  /**
+   * Interface stats
+   */
+  stats_t m_stats;
+
+  /**
+   * reference to stat listener
+   */
+  stat_listener* m_listener;
+
   /**
    * Operational state of the interface
    */
@@ -683,6 +734,11 @@ private:
 
   static std::shared_ptr<interface_cmds::events_cmd> m_events_cmd;
 };
+
+/**
+ *  stream insertion operator for interface stats
+ */
+std::ostream& operator<<(std::ostream& os, const interface::stats_t& stats);
 };
 /*
  * fd.io coding-style-patch-verification: ON
index c4fd661..3a7fb50 100644 (file)
@@ -20,7 +20,6 @@ DEFINE_VAPI_MSG_IDS_VPE_API_JSON;
 DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON;
 DEFINE_VAPI_MSG_IDS_AF_PACKET_API_JSON;
 DEFINE_VAPI_MSG_IDS_VHOST_USER_API_JSON;
-DEFINE_VAPI_MSG_IDS_STATS_API_JSON;
 
 namespace VOM {
 namespace interface_cmds {
@@ -473,137 +472,6 @@ events_cmd::to_string() const
   return ("itf-events");
 }
 
-/**
- * Interface statistics
- */
-stats_enable_cmd::stats_enable_cmd(interface::stat_listener& el,
-                                   const handle_t& handle)
-  : event_cmd(el.status())
-  , m_listener(el)
-  , m_swifindex(handle)
-{
-}
-
-bool
-stats_enable_cmd::operator==(const stats_enable_cmd& other) const
-{
-  return (true);
-}
-
-rc_t
-stats_enable_cmd::issue(connection& con)
-{
-  /*
-   * First set the call back to handle the interface stats
-   */
-  m_reg.reset(new reg_t(con.ctx(), std::ref(*(static_cast<event_cmd*>(this)))));
-
-  /*
-   * then send the request to enable them
-   */
-  msg_t req(con.ctx(), 1, std::ref(*(static_cast<rpc_cmd*>(this))));
-
-  auto& payload = req.get_request().get_payload();
-  payload.enable_disable = 1;
-  payload.pid = getpid();
-  payload.num = 1;
-
-  payload.sw_ifs[0] = m_swifindex.value();
-
-  VAPI_CALL(req.execute());
-
-  wait();
-
-  return (rc_t::OK);
-}
-
-void
-stats_enable_cmd::retire(connection& con)
-{
-  /*
-   * disable interface stats.
-   */
-  msg_t req(con.ctx(), 1, std::ref(*(static_cast<rpc_cmd*>(this))));
-
-  auto& payload = req.get_request().get_payload();
-  payload.enable_disable = 0;
-  payload.pid = getpid();
-  payload.num = 1;
-  payload.sw_ifs[0] = m_swifindex.value();
-
-  VAPI_CALL(req.execute());
-
-  wait();
-}
-
-interface::stat_listener&
-stats_enable_cmd::listener() const
-{
-  return m_listener;
-}
-
-void
-stats_enable_cmd::set(const rc_t& rc)
-{
-  m_listener.status().set(rc);
-}
-
-void
-stats_enable_cmd::notify()
-{
-  m_listener.handle_interface_stat(this);
-}
-
-std::string
-stats_enable_cmd::to_string() const
-{
-  std::ostringstream s;
-  s << "itf-stats-enable itf:" << m_swifindex.to_string();
-  return (s.str());
-}
-
-stats_disable_cmd::stats_disable_cmd(const handle_t& handle)
-  : rpc_cmd(m_res)
-  , m_swifindex(handle)
-{
-}
-
-bool
-stats_disable_cmd::operator==(const stats_disable_cmd& other) const
-{
-  return (true);
-}
-
-rc_t
-stats_disable_cmd::issue(connection& con)
-{
-  /*
-   * then send the request to enable them
-   */
-  msg_t req(con.ctx(), 1, std::ref(*this));
-
-  auto& payload = req.get_request().get_payload();
-  payload.enable_disable = 0;
-  payload.pid = getpid();
-  payload.num = 1;
-
-  payload.sw_ifs[0] = m_swifindex.value();
-
-  VAPI_CALL(req.execute());
-
-  wait();
-
-  return (rc_t::OK);
-}
-
-std::string
-stats_disable_cmd::to_string() const
-{
-  std::ostringstream s;
-  s << "itf-stats-disable itf:" << m_swifindex.to_string();
-  return (s.str());
-}
-
 dump_cmd::dump_cmd()
 {
 }
index b646e4e..13a47e6 100644 (file)
@@ -25,7 +25,6 @@
 
 #include <vapi/af_packet.api.vapi.hpp>
 #include <vapi/interface.api.vapi.hpp>
-#include <vapi/stats.api.vapi.hpp>
 #include <vapi/tap.api.vapi.hpp>
 #include <vapi/vhost_user.api.vapi.hpp>
 #include <vapi/vpe.api.vapi.hpp>
@@ -406,101 +405,6 @@ private:
   interface::event_listener& m_listener;
 };
 
-/**
- * A command class represents our desire to recieve interface stats
- */
-class stats_enable_cmd
-  : public event_cmd<vapi::Want_per_interface_combined_stats,
-                     vapi::Vnet_per_interface_combined_counters>
-{
-public:
-  /**
-   * Constructor taking the listner to notify
-   */
-  stats_enable_cmd(interface::stat_listener& el, const handle_t& handle);
-
-  /**
-   * Issue the command to VPP/HW
-   */
-  rc_t issue(connection& con);
-
-  /**
-   * Retires the command - unsubscribe from the stats.
-   */
-  void retire(connection& con);
-
-  /**
-   * convert to string format for debug purposes
-   */
-  std::string to_string() const;
-
-  /**
-   * (re)set status
-   */
-  void set(const rc_t& rc);
-
-  /**
-   * get listener
-   */
-  interface::stat_listener& listener() const;
-
-  /**
-   * Comparison operator - only used for UT
-   */
-  bool operator==(const stats_enable_cmd& i) const;
-
-  /**
-   * Called when it's time to poke the listeners
-   */
-  void notify();
-
-private:
-  /**
-   * The listeners to notify when data/stats arrive
-   */
-  interface::stat_listener& m_listener;
-
-  /**
-   * The interface on which we are enabling states
-   */
-  const handle_t& m_swifindex;
-};
-
-/**
- * A command class represents our desire to recieve interface stats
- */
-class stats_disable_cmd
-  : public rpc_cmd<HW::item<bool>, vapi::Want_per_interface_combined_stats>
-{
-public:
-  /**
-   * Constructor taking the listner to notify
-   */
-  stats_disable_cmd(const handle_t& handle);
-
-  /**
-   * Issue the command to VPP/HW
-   */
-  rc_t issue(connection& con);
-
-  /**
-   * convert to string format for debug purposes
-   */
-  std::string to_string() const;
-
-  /**
-   * Comparison operator - only used for UT
-   */
-  bool operator==(const stats_disable_cmd& i) const;
-
-private:
-  HW::item<bool> m_res;
-  /**
-   * The interface on which we are disabling states
-   */
-  handle_t m_swifindex;
-};
-
 /**
  * A cmd class that Dumps all the Vpp interfaces
  */
diff --git a/extras/vom/vom/stat_client.cpp b/extras/vom/vom/stat_client.cpp
new file mode 100644 (file)
index 0000000..b348c6c
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/stat_client.hpp"
+
+namespace VOM {
+
+stat_client::stat_data_t::stat_data_t()
+  : m_name("")
+  , m_type(STAT_DIR_TYPE_ILLEGAL)
+{
+}
+
+stat_client::stat_data_t::stat_data_t(stat_segment_data_t* stat_seg_data)
+  : m_name(stat_seg_data->name)
+  , m_type(stat_seg_data->type)
+{
+  switch (m_type) {
+    case STAT_DIR_TYPE_SCALAR_INDEX:
+      m_scalar_value = stat_seg_data->scalar_value;
+      break;
+    case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+      m_simple_counter_vec = stat_seg_data->simple_counter_vec;
+      break;
+    case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
+      m_combined_counter_vec =
+        reinterpret_cast<counter_t**>(stat_seg_data->combined_counter_vec);
+      break;
+    case STAT_DIR_TYPE_ERROR_INDEX:
+      m_error_Value = stat_seg_data->error_value;
+      break;
+    case STAT_DIR_TYPE_ILLEGAL:
+      break;
+  }
+}
+
+const std::string&
+stat_client::stat_data_t::name() const
+{
+  return m_name;
+}
+
+const stat_directory_type_t&
+stat_client::stat_data_t::type() const
+{
+  return m_type;
+}
+
+double
+stat_client::stat_data_t::get_stat_segment_scalar_data()
+{
+  return m_scalar_value;
+}
+uint64_t
+stat_client::stat_data_t::get_stat_segment_error_data()
+{
+  return m_error_Value;
+}
+uint64_t**
+stat_client::stat_data_t::get_stat_segment_simple_counter_data()
+{
+  return m_simple_counter_vec;
+}
+counter_t**
+stat_client::stat_data_t::get_stat_segment_combined_counter_data()
+{
+  return m_combined_counter_vec;
+}
+
+stat_client::stat_client(std::string& socket_name)
+  : m_socket_name(socket_name)
+  , m_patterns()
+  , m_stat_connect(0)
+{
+  m_patterns.push_back("/if");
+}
+
+stat_client::stat_client(std::vector<std::string>& pattern)
+  : m_socket_name("/run/vpp/stats.sock")
+  , m_patterns(pattern)
+  , m_stat_connect(0)
+{
+}
+
+stat_client::stat_client(std::string socket_name,
+                         std::vector<std::string> patterns)
+  : m_socket_name(socket_name)
+  , m_patterns(patterns)
+  , m_stat_connect(0)
+{
+}
+
+stat_client::stat_client()
+  : m_socket_name("/run/vpp/stats.sock")
+  , m_patterns()
+  , m_stat_connect(0)
+{
+  m_patterns.push_back("/if");
+}
+
+stat_client::~stat_client()
+{
+  stat_segment_vec_free(m_counter_vec);
+  data_free();
+  if (m_stat_connect)
+    stat_segment_disconnect();
+}
+
+stat_client::stat_client(const stat_client& o)
+  : m_socket_name(o.m_socket_name)
+  , m_patterns(o.m_patterns)
+{
+}
+
+int
+stat_client::connect()
+{
+  if (stat_segment_connect(m_socket_name.c_str()) == 0)
+    m_stat_connect = 1;
+  return m_stat_connect;
+}
+
+void
+stat_client::disconnect()
+{
+  if (m_stat_connect)
+    stat_segment_disconnect();
+}
+
+int
+stat_client::vec_len(void* vec)
+{
+  return stat_segment_vec_len(vec);
+}
+
+void
+stat_client::vec_free(void* vec)
+{
+  stat_segment_vec_free(vec);
+}
+
+void
+stat_client::ls()
+{
+  uint8_t** string_vec = { 0 };
+  for (auto& pattern : m_patterns) {
+    string_vec = stat_segment_string_vector(string_vec, pattern.c_str());
+  }
+  m_counter_vec = stat_segment_ls(string_vec);
+  stat_segment_vec_free(string_vec);
+}
+
+const stat_client::stat_data_vec_t&
+stat_client::dump()
+{
+  stat_segment_data_t* ssd;
+  stat_segment_data_free(m_stat_seg_data);
+  if (m_stat_data.size()) {
+    m_stat_data.clear();
+  }
+  ssd = stat_segment_dump(m_counter_vec);
+  if (!ssd) {
+    ls();
+    return m_stat_data;
+  }
+  m_stat_seg_data = ssd;
+  for (int i = 0; i < stat_segment_vec_len(ssd); i++) {
+    stat_data_t sd(&ssd[i]);
+    m_stat_data.push_back(sd);
+  }
+  return m_stat_data;
+}
+
+const stat_client::stat_data_vec_t&
+stat_client::dump_entry(uint32_t index)
+{
+  stat_segment_data_t* ssd;
+  stat_segment_data_free(m_stat_seg_data);
+  if (m_stat_data.size()) {
+    m_stat_data.clear();
+  }
+  ssd = stat_segment_dump_entry(index);
+  if (!ssd) {
+    ls();
+    return m_stat_data;
+  }
+  m_stat_seg_data = ssd;
+  for (int i = 0; i < stat_segment_vec_len(ssd); i++) {
+    stat_data_t sd(&ssd[i]);
+    m_stat_data.push_back(sd);
+  }
+  return m_stat_data;
+}
+
+void
+stat_client::data_free()
+{
+  stat_segment_data_free(m_stat_seg_data);
+}
+
+double
+stat_client::heartbeat()
+{
+  return stat_segment_heartbeat();
+}
+
+std::string
+stat_client::index_to_name(uint32_t index)
+{
+  return stat_segment_index_to_name(index);
+}
+
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/stat_client.hpp b/extras/vom/vom/stat_client.hpp
new file mode 100644 (file)
index 0000000..4da968a
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_STAT_CLIENT_H__
+#define __VOM_STAT_CLIENT_H__
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+extern "C" {
+#include <vpp-api/client/stat_client.h>
+}
+
+namespace VOM {
+
+typedef struct
+{
+  uint64_t packets;
+  uint64_t bytes;
+} counter_t;
+
+typedef uint64_t stat_counter_t;
+/**
+ * A representation of a stat client in VPP
+ */
+class stat_client
+{
+public:
+  /**
+   * stat data representation
+   */
+  struct stat_data_t
+  {
+    /**
+     * stat data constructor
+     */
+    stat_data_t();
+
+    /**
+     * stat data custom constructor
+     */
+    stat_data_t(stat_segment_data_t* stat_seg_data);
+
+    /**
+     * get name of stat
+     */
+    const std::string& name() const;
+
+    /**
+     * get type of stat
+     */
+    const stat_directory_type_t& type() const;
+
+    /**
+     * Get pointer to actual data
+     */
+    double get_stat_segment_scalar_data();
+    uint64_t get_stat_segment_error_data();
+    uint64_t** get_stat_segment_simple_counter_data();
+    counter_t** get_stat_segment_combined_counter_data();
+
+  private:
+    /**
+     * name of stat data
+     */
+    const std::string m_name;
+
+    /**
+     * type of stat data
+     */
+    const stat_directory_type_t m_type;
+
+    /**
+     * union of pointers to actual stat data
+     */
+    union
+    {
+      double m_scalar_value;
+      uint64_t m_error_Value;
+      uint64_t** m_simple_counter_vec;
+      counter_t** m_combined_counter_vec;
+    };
+  };
+
+  /**
+   * vector of stat_data_t
+   */
+  typedef std::vector<stat_data_t> stat_data_vec_t;
+
+  /**
+   * Stat Client constructor with custom socket name
+   */
+  stat_client(std::string& socket_name);
+
+  /**
+   * Stat Client constructor with custom vector of patterns
+   */
+  stat_client(std::vector<std::string>& pattern);
+
+  /**
+   * Stat Client constructor with custom socket name and vector of patterns
+   */
+  stat_client(std::string socket_name, std::vector<std::string> patterns);
+
+  /**
+   * Stat Client constructor
+   */
+  stat_client();
+
+  /**
+   * Stat Client destructor
+   */
+  ~stat_client();
+
+  /**
+   * Stat Client copy constructor
+   */
+  stat_client(const stat_client& o);
+
+  /**
+   * Connect to stat segment
+   */
+  int connect();
+
+  /**
+   * Disconnect to stat segment
+   */
+  void disconnect();
+
+  /**
+   * Get vector length of VPP style vector
+   */
+  int vec_len(void* vec);
+
+  /**
+   * Free VPP style vector
+   */
+  void vec_free(void* vec);
+
+  /**
+   * ls on the stat directory using given pattern
+   */
+  void ls();
+
+  /**
+   * dump all the stats for given pattern
+   */
+  const stat_data_vec_t& dump();
+
+  /**
+   * dump stats for given index in stat directory
+   */
+  const stat_data_vec_t& dump_entry(uint32_t index);
+
+  /**
+   * Free stat segment data
+   */
+  void data_free();
+
+  double heartbeat();
+
+  /**
+   * get index to name of stat
+   */
+  std::string index_to_name(uint32_t index);
+
+private:
+  /**
+   * socket name
+   */
+  std::string m_socket_name;
+
+  /**
+   * vector of patterns for stats
+   */
+  std::vector<std::string> m_patterns;
+
+  /**
+   * connection bit
+   */
+  int m_stat_connect;
+
+  /**
+   * Pointer to VPP style vector of stat indexes
+   */
+  uint32_t* m_counter_vec;
+
+  /**
+   * Pointer to stat segment
+   */
+  stat_segment_data_t* m_stat_seg_data;
+
+  /**
+   * Vector of stat data
+   */
+  stat_data_vec_t m_stat_data;
+};
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/stat_reader.cpp b/extras/vom/vom/stat_reader.cpp
new file mode 100644 (file)
index 0000000..4b054ab
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/stat_reader.hpp"
+#include "vom/interface.hpp"
+
+namespace VOM {
+
+stat_reader::stat_indexes_t stat_reader::m_stat_itf_indexes;
+
+stat_reader::stat_reader()
+  : m_client()
+{
+}
+
+stat_reader::stat_reader(stat_client sc)
+  : m_client(sc)
+{
+}
+
+stat_reader::~stat_reader()
+{
+}
+
+int
+stat_reader::connect()
+{
+  return m_client.connect();
+}
+
+void
+stat_reader::disconnect()
+{
+  m_client.disconnect();
+}
+
+void
+stat_reader::registers(const interface& intf)
+{
+  m_stat_itf_indexes.insert(intf.handle().value());
+}
+
+void
+stat_reader::unregisters(const interface& intf)
+{
+  m_stat_itf_indexes.erase(intf.handle().value());
+}
+
+void
+stat_reader::read()
+{
+  stat_client::stat_data_vec_t sd = m_client.dump();
+  for (auto& sde : sd) {
+    std::string name;
+
+    if (sde.name().empty())
+      continue;
+
+    name = sde.name();
+
+    switch (sde.type()) {
+      case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+      case STAT_DIR_TYPE_ERROR_INDEX:
+      case STAT_DIR_TYPE_SCALAR_INDEX:
+        break;
+
+      case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
+        if (name.find("/if") != std::string::npos)
+          name.erase(0, 4);
+        for (auto& i : m_stat_itf_indexes) {
+          counter_t count = {.packets = 0, .bytes = 0 };
+          for (int k = 0; k < m_client.vec_len(
+                                sde.get_stat_segment_combined_counter_data());
+               k++) {
+            count.packets +=
+              sde.get_stat_segment_combined_counter_data()[k][i].packets;
+            count.bytes +=
+              sde.get_stat_segment_combined_counter_data()[k][i].bytes;
+          }
+          std::shared_ptr<interface> itf = interface::find(i);
+          if (itf)
+            itf->set(count, name);
+        }
+        break;
+
+      default:;
+    }
+  }
+
+  for (auto& i : m_stat_itf_indexes) {
+    std::shared_ptr<interface> itf = interface::find(i);
+    if (itf)
+      itf->publish_stats();
+  }
+}
+
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/stat_reader.hpp b/extras/vom/vom/stat_reader.hpp
new file mode 100644 (file)
index 0000000..f90b256
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_STAT_READER_H__
+#define __VOM_STAT_READER_H__
+
+#include "vom/stat_client.hpp"
+#include <set>
+
+namespace VOM {
+
+class interface;
+
+/**
+ * Stat reader: single interface to get stats
+ */
+class stat_reader
+{
+public:
+  /**
+   * Default Constructor
+   */
+  stat_reader();
+
+  /**
+   * Constructor
+   */
+  stat_reader(stat_client sc);
+
+  /**
+   * Destructor
+   */
+  ~stat_reader();
+
+  /**
+   * connection to stat object
+   */
+  virtual int connect();
+
+  /**
+   * disconnect to stat object
+   */
+  virtual void disconnect();
+
+  /**
+   * read stats for registered objects from stat_segment
+   * and set those stats to respective objects
+   */
+  virtual void read();
+
+private:
+  /**
+   * friend to interface class to call stat_register and
+   * stat_unregister methods
+   */
+  friend class interface;
+
+  /**
+   * Register objects to get stats for
+   */
+  static void registers(const interface& itf);
+
+  /**
+   * Unregister objects
+   */
+  static void unregisters(const interface& itf);
+
+  /**
+   * typedef of stat_indexes
+   */
+  typedef std::set<uint32_t> stat_indexes_t;
+
+  /**
+   * stat_client object
+   */
+  stat_client m_client;
+
+  /**
+   * static pointer to set of registered interfaces
+   */
+  static stat_indexes_t m_stat_itf_indexes;
+};
+};
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+#endif
diff --git a/extras/vom/vom/test_stats.cpp b/extras/vom/vom/test_stats.cpp
new file mode 100644 (file)
index 0000000..6235dd4
--- /dev/null
@@ -0,0 +1,92 @@
+#include <vom/om.hpp>
+#include <vom/hw.hpp>
+#include <vom/types.hpp>
+#include <vom/prefix.hpp>
+#include <vom/tap_interface.hpp>
+
+class listener : public VOM::interface::stat_listener
+{
+public:
+  listener() {}
+  ~listener() {}
+  void handle_interface_stat(const VOM::interface& itf)
+  {
+    std::cout << itf.name() << " " << itf.get_stats();
+  }
+};
+
+/**
+ * Run VPP on another terminal before running vom_stats_test
+ */
+int main()
+{
+  uint8_t i = 5;
+  listener *listen = new listener();
+
+  VOM::HW::init(new VOM::HW::cmd_q());
+  VOM::OM::init();
+
+  while (VOM::HW::connect() != true)
+    ;
+
+  VOM::tap_interface itf("tap0", VOM::interface::admin_state_t::UP, VOM::route::prefix_t::ZERO);
+  VOM::OM::write("__TAP__", itf);
+
+  std::shared_ptr<VOM::tap_interface> intf = itf.singular();
+
+
+  VOM::tap_interface itf1("tap1", VOM::interface::admin_state_t::UP, VOM::route::prefix_t::ZERO);
+  VOM::OM::write("__TAP__", itf1);
+
+  std::shared_ptr<VOM::tap_interface> intf1 = itf1.singular();
+
+  VOM::tap_interface itf2("tap2", VOM::interface::admin_state_t::UP, VOM::route::prefix_t::ZERO);
+  VOM::OM::write("__TAP__", itf2);
+
+  std::shared_ptr<VOM::tap_interface> intf2 = itf2.singular();
+
+  if (VOM::handle_t::INVALID == intf->handle() || VOM::handle_t::INVALID == intf1->handle()
+      || VOM::handle_t::INVALID == intf2->handle())
+    {
+      std::cout << "Interface index is INVALID" << std::endl;
+      VOM::HW::disconnect();
+
+      return 0;
+    }
+  else
+    {
+      std::cout << "Interface #1 index is " << intf->handle().value() << std::endl;
+      std::cout << "Interface #2 index is " << intf1->handle().value() << std::endl;
+      std::cout << "Interface #3 index is " << intf2->handle().value() << std::endl;
+    }
+
+  intf->enable_stats(listen);
+  intf1->enable_stats(listen);
+  intf2->enable_stats(listen);
+
+  while (i--)
+    {
+      sleep(3);
+      std::cout << "stats # " << std::to_string(i) << std::endl;
+      VOM::HW::read_stats();
+
+      if (i == 2)
+        intf->disable_stats();
+
+    }
+
+  intf1->disable_stats();
+  intf2->disable_stats();
+
+  intf.reset();
+  intf1.reset();
+  intf2.reset();
+
+  VOM::OM::remove("__TAP__");
+
+  delete listen;
+  sleep(2);
+  VOM::HW::disconnect();
+
+  return 0;
+}
index fa51c38..6aacea9 100644 (file)
@@ -95,7 +95,7 @@ public:
 class MockListener : public interface::event_listener,
                      public interface::stat_listener
 {
-    void handle_interface_stat(interface_cmds::stats_enable_cmd *cmd)
+    void handle_interface_stat(const interface& itf)
     {
     }
     void handle_interface_event(std::vector<VOM::interface::event> events)