Detailed stats collection feature

Use device-input and interface-output feautre arcs to collect unicast, multicast
and broadcast states for RX and TX resp. Since these feature arcs are present only
for 'physical' interfaces (i.e. not su-interfaces) counter collection is supported
only on parent interface types.

Change-Id: I915c235e336b0fc3a3c3de918f95dd674e4e0e4e
Signed-off-by: Neale Ranns <nranns@cisco.com>
Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
diff --git a/src/vpp-api/vom/interface.cpp b/src/vpp-api/vom/interface.cpp
index f323727..435002a 100644
--- a/src/vpp-api/vom/interface.cpp
+++ b/src/vpp-api/vom/interface.cpp
@@ -49,6 +49,7 @@
   , m_state(itf_state)
   , m_table_id(route::DEFAULT_TABLE)
   , m_l2_address(l2_address_t::ZERO, rc_t::UNSET)
+  , m_stats_type(stats_type_t::NORMAL)
   , m_oper(oper_state_t::DOWN)
   , m_tag(tag)
 {
@@ -66,6 +67,7 @@
   , m_state(itf_state)
   , m_table_id(m_rd->table_id())
   , m_l2_address(l2_address_t::ZERO, rc_t::UNSET)
+  , m_stats_type(stats_type_t::NORMAL)
   , m_oper(oper_state_t::DOWN)
   , m_tag(tag)
 {
@@ -79,6 +81,7 @@
   , m_state(o.m_state)
   , m_table_id(o.m_table_id)
   , m_l2_address(o.m_l2_address)
+  , m_stats_type(o.m_stats_type)
   , m_oper(o.m_oper)
   , m_tag(o.m_tag)
 {
@@ -403,9 +406,14 @@
 }
 
 void
-interface::enable_stats_i(interface::stat_listener& el)
+interface::enable_stats_i(interface::stat_listener& el, const stats_type_t& st)
 {
   if (!m_stats) {
+    if (stats_type_t::DETAILED == st) {
+      m_stats_type = st;
+      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();
@@ -413,9 +421,9 @@
 }
 
 void
-interface::enable_stats(interface::stat_listener& el)
+interface::enable_stats(interface::stat_listener& el, const stats_type_t& st)
 {
-  singular()->enable_stats_i(el);
+  singular()->enable_stats_i(el, st);
 }
 
 std::shared_ptr<interface>
diff --git a/src/vpp-api/vom/interface.hpp b/src/vpp-api/vom/interface.hpp
index 0099bde..29903b5 100644
--- a/src/vpp-api/vom/interface.hpp
+++ b/src/vpp-api/vom/interface.hpp
@@ -41,6 +41,15 @@
 class interface : public object_base
 {
 public:
+  struct stats_type_t : public enum_base<stats_type_t>
+  {
+    const static stats_type_t DETAILED;
+    const static stats_type_t NORMAL;
+
+  private:
+    stats_type_t(int v, const std::string& s);
+  };
+
   /**
    * The key for interface's key
    */
@@ -447,7 +456,8 @@
   /**
    * 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);
 
 protected:
   /**
@@ -540,7 +550,7 @@
   /**
    * enable the interface stats in the singular instance
    */
-  void enable_stats_i(stat_listener& el);
+  void enable_stats_i(stat_listener& el, const stats_type_t& st);
 
   /**
    * Commit the acculmulated changes into VPP. i.e. to a 'HW" write.
@@ -600,6 +610,11 @@
   HW::item<l2_address_t> m_l2_address;
 
   /**
+   * The state of the detailed stats collection
+   */
+  HW::item<stats_type_t> m_stats_type;
+
+  /**
    * Operational state of the interface
    */
   oper_state_t m_oper;
diff --git a/src/vpp-api/vom/interface_cmds.cpp b/src/vpp-api/vom/interface_cmds.cpp
index 084d6d5..b1decd2 100644
--- a/src/vpp-api/vom/interface_cmds.cpp
+++ b/src/vpp-api/vom/interface_cmds.cpp
@@ -412,6 +412,48 @@
   return (s.str());
 }
 
+collect_detail_stats_change_cmd::collect_detail_stats_change_cmd(
+  HW::item<interface::stats_type_t>& item,
+  const handle_t& hdl,
+  bool enable)
+  : rpc_cmd(item)
+  , m_hdl(hdl)
+  , m_enable(enable)
+{
+}
+
+bool
+collect_detail_stats_change_cmd::operator==(
+  const collect_detail_stats_change_cmd& other) const
+{
+  return ((m_hdl == other.m_hdl) && (m_hw_item == other.m_hw_item) &&
+          (m_enable == other.m_enable));
+}
+
+rc_t
+collect_detail_stats_change_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.sw_if_index = m_hdl.value();
+  payload.enable_disable = m_enable;
+
+  VAPI_CALL(req.execute());
+
+  m_hw_item.set(wait());
+
+  return (rc_t::OK);
+}
+
+std::string
+collect_detail_stats_change_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "itf-stats: " << m_hw_item.to_string() << " hdl:" << m_hdl.to_string();
+  return (s.str());
+}
+
 events_cmd::events_cmd(interface::event_listener& el)
   : event_cmd(el.status())
   , m_listener(el)
diff --git a/src/vpp-api/vom/interface_cmds.hpp b/src/vpp-api/vom/interface_cmds.hpp
index 62762ef..41ec5ca 100644
--- a/src/vpp-api/vom/interface_cmds.hpp
+++ b/src/vpp-api/vom/interface_cmds.hpp
@@ -332,7 +332,7 @@
 };
 
 /**
- * A command class that binds an interface to an L3 table
+ * A command class that changes the MAC address on an interface
  */
 class set_mac_cmd : public rpc_cmd<HW::item<l2_address_t>,
                                    rc_t,
@@ -368,6 +368,50 @@
 };
 
 /**
+ * A command class that enables detailed stats collection on an interface
+ */
+class collect_detail_stats_change_cmd
+  : public rpc_cmd<HW::item<interface::stats_type_t>,
+                   rc_t,
+                   vapi::Collect_detailed_interface_stats>
+{
+public:
+  /**
+   * Constructor taking the HW::item to update
+   * and the handle of the interface
+   */
+  collect_detail_stats_change_cmd(HW::item<interface::stats_type_t>& item,
+                                  const handle_t& h,
+                                  bool enable);
+
+  /**
+   * 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 collect_detail_stats_change_cmd& i) const;
+
+private:
+  /**
+   * the handle of the interface to update
+   */
+  const handle_t& m_hdl;
+
+  /**
+   * enable or disable the detailed stats collection
+   */
+  bool m_enable;
+};
+
+/**
  * A command class represents our desire to recieve interface events
  */
 class events_cmd
diff --git a/src/vpp-api/vom/interface_types.cpp b/src/vpp-api/vom/interface_types.cpp
index 2e7eee3..1272302 100644
--- a/src/vpp-api/vom/interface_types.cpp
+++ b/src/vpp-api/vom/interface_types.cpp
@@ -35,6 +35,9 @@
 const interface::admin_state_t interface::admin_state_t::DOWN(0, "down");
 const interface::admin_state_t interface::admin_state_t::UP(1, "up");
 
+const interface::stats_type_t interface::stats_type_t::DETAILED(0, "detailed");
+const interface::stats_type_t interface::stats_type_t::NORMAL(1, "normal");
+
 interface::type_t
 interface::type_t::from_string(const std::string& str)
 {
@@ -77,6 +80,11 @@
 {
 }
 
+interface::stats_type_t::stats_type_t(int v, const std::string& s)
+  : enum_base<interface::stats_type_t>(v, s)
+{
+}
+
 interface::admin_state_t
 interface::admin_state_t::from_int(uint8_t v)
 {