VOM: vhost-use interfaces

Change-Id: Iee1574d1f0f081ccc4a90fd9825a0b5e254aa642
Signed-off-by: Neale Ranns <neale.ranns@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 9fa96af..f943bf4 100644
--- a/src/vpp-api/vom/interface.cpp
+++ b/src/vpp-api/vom/interface.cpp
@@ -38,7 +38,8 @@
  */
 interface::interface(const std::string& name,
                      interface::type_t itf_type,
-                     interface::admin_state_t itf_state)
+                     interface::admin_state_t itf_state,
+                     const std::string& tag)
   : m_hdl(handle_t::INVALID)
   , m_name(name)
   , m_type(itf_type)
@@ -46,13 +47,15 @@
   , m_table_id(route::DEFAULT_TABLE)
   , m_l2_address(l2_address_t::ZERO, rc_t::UNSET)
   , m_oper(oper_state_t::DOWN)
+  , m_tag(tag)
 {
 }
 
 interface::interface(const std::string& name,
                      interface::type_t itf_type,
                      interface::admin_state_t itf_state,
-                     const route_domain& rd)
+                     const route_domain& rd,
+                     const std::string& tag)
   : m_hdl(handle_t::INVALID)
   , m_name(name)
   , m_type(itf_type)
@@ -61,6 +64,7 @@
   , m_table_id(m_rd->table_id())
   , m_l2_address(l2_address_t::ZERO, rc_t::UNSET)
   , m_oper(oper_state_t::DOWN)
+  , m_tag(tag)
 {
 }
 
@@ -73,6 +77,7 @@
   , m_table_id(o.m_table_id)
   , m_l2_address(o.m_l2_address)
   , m_oper(o.m_oper)
+  , m_tag(o.m_tag)
 {
 }
 
@@ -221,7 +226,13 @@
   }
 
   s << " admin-state:" << m_state.to_string()
-    << " oper-state:" << m_oper.to_string() << "]";
+    << " oper-state:" << m_oper.to_string();
+
+  if (!m_tag.empty()) {
+    s << " tag:[" << m_tag << "]";
+  }
+
+  s << "]";
 
   return (s.str());
 }
@@ -246,10 +257,20 @@
   } else if (type_t::BVI == m_type) {
     q.push(new interface_cmds::loopback_create_cmd(m_hdl, m_name));
     q.push(new interface_cmds::set_tag(m_hdl, m_name));
+    /*
+     * set the m_tag for pretty-print
+     */
+    m_tag = m_name;
   } else if (type_t::AFPACKET == m_type) {
     q.push(new interface_cmds::af_packet_create_cmd(m_hdl, m_name));
+    if (!m_tag.empty())
+      q.push(new interface_cmds::set_tag(m_hdl, m_tag));
   } else if (type_t::TAP == m_type) {
     q.push(new interface_cmds::tap_create_cmd(m_hdl, m_name));
+    if (!m_tag.empty())
+      q.push(new interface_cmds::set_tag(m_hdl, m_tag));
+  } else if (type_t::VHOST == m_type) {
+    q.push(new interface_cmds::vhost_create_cmd(m_hdl, m_name, m_tag));
   } else {
     m_hdl.set(rc_t::OK);
   }
@@ -266,6 +287,8 @@
     q.push(new interface_cmds::af_packet_delete_cmd(m_hdl, m_name));
   } else if (type_t::TAP == m_type) {
     q.push(new interface_cmds::tap_delete_cmd(m_hdl));
+  } else if (type_t::VHOST == m_type) {
+    q.push(new interface_cmds::vhost_delete_cmd(m_hdl, m_name));
   }
 
   return (q);
@@ -345,6 +368,12 @@
 }
 
 void
+interface::set(const admin_state_t& state)
+{
+  m_state = state;
+}
+
+void
 interface::set(const l2_address_t& addr)
 {
   assert(rc_t::UNSET == m_l2_address.rc());
@@ -365,6 +394,12 @@
 }
 
 void
+interface::set(const std::string& tag)
+{
+  m_tag = tag;
+}
+
+void
 interface::enable_stats_i(interface::stat_listener& el)
 {
   if (!m_stats) {
@@ -429,6 +464,20 @@
 void
 interface::event_handler::handle_populate(const client_db::key_t& key)
 {
+  std::shared_ptr<interface_cmds::vhost_dump_cmd> vcmd =
+    std::make_shared<interface_cmds::vhost_dump_cmd>();
+
+  HW::enqueue(vcmd);
+  HW::write();
+
+  for (auto& vhost_itf_record : *vcmd) {
+    std::shared_ptr<interface> vitf =
+      interface_factory::new_vhost_user_interface(
+        vhost_itf_record.get_payload());
+    VOM_LOG(log_level_t::DEBUG) << "dump: " << vitf->to_string();
+    OM::commit(key, *vitf);
+  }
+
   /*
    * dump VPP current states
    */
diff --git a/src/vpp-api/vom/interface.hpp b/src/vpp-api/vom/interface.hpp
index da0db40..5d29510 100644
--- a/src/vpp-api/vom/interface.hpp
+++ b/src/vpp-api/vom/interface.hpp
@@ -91,6 +91,11 @@
     const static type_t TAP;
 
     /**
+     * vhost-user interface type
+     */
+    const static type_t VHOST;
+
+    /**
      * Convert VPP's name of the interface to a type
      */
     static type_t from_string(const std::string& str);
@@ -157,7 +162,10 @@
   /**
    * Construct a new object matching the desried state
    */
-  interface(const std::string& name, type_t type, admin_state_t state);
+  interface(const std::string& name,
+            type_t type,
+            admin_state_t state,
+            const std::string& tag = "");
   /**
    * Construct a new object matching the desried state mapped
    * to a specific route_domain
@@ -165,7 +173,8 @@
   interface(const std::string& name,
             type_t type,
             admin_state_t state,
-            const route_domain& rd);
+            const route_domain& rd,
+            const std::string& tag = "");
   /**
    * Destructor
    */
@@ -215,6 +224,11 @@
   const l2_address_t& l2_address() const;
 
   /**
+   * Set the admin state of the interface
+   */
+  void set(const admin_state_t& state);
+
+  /**
    * Set the L2 Address
    */
   void set(const l2_address_t& addr);
@@ -225,6 +239,11 @@
   void set(const oper_state_t& state);
 
   /**
+   * Set the tag to the interface
+   */
+  void set(const std::string& tag);
+
+  /**
    * Comparison operator - only used for UT
    */
   virtual bool operator==(const interface& i) const;
@@ -581,6 +600,11 @@
   oper_state_t m_oper;
 
   /**
+   * tag of the interface
+   */
+  std::string m_tag;
+
+  /**
    * A map of all interfaces keyed against VPP's handle
    */
   static std::map<handle_t, std::weak_ptr<interface>> m_hdl_db;
diff --git a/src/vpp-api/vom/interface_cmds.cpp b/src/vpp-api/vom/interface_cmds.cpp
index 031aaea..df34154 100644
--- a/src/vpp-api/vom/interface_cmds.cpp
+++ b/src/vpp-api/vom/interface_cmds.cpp
@@ -20,6 +20,7 @@
 DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON;
 DEFINE_VAPI_MSG_IDS_AF_PACKET_API_JSON;
 DEFINE_VAPI_MSG_IDS_TAP_API_JSON;
+DEFINE_VAPI_MSG_IDS_VHOST_USER_API_JSON;
 DEFINE_VAPI_MSG_IDS_STATS_API_JSON;
 
 namespace VOM {
@@ -129,6 +130,50 @@
   return (s.str());
 }
 
+vhost_create_cmd::vhost_create_cmd(HW::item<handle_t>& item,
+                                   const std::string& name,
+                                   const std::string& tag)
+  : create_cmd(item, name)
+  , m_tag(tag)
+{
+}
+
+rc_t
+vhost_create_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  memset(payload.sock_filename, 0, sizeof(payload.sock_filename));
+  memcpy(payload.sock_filename, m_name.c_str(),
+         std::min(m_name.length(), sizeof(payload.sock_filename)));
+  memset(payload.tag, 0, sizeof(payload.tag));
+
+  if (!m_tag.empty())
+    memcpy(payload.tag, m_tag.c_str(),
+           std::min(m_tag.length(), sizeof(payload.tag)));
+
+  payload.is_server = 1;
+  payload.use_custom_mac = 0;
+  payload.renumber = 0;
+
+  VAPI_CALL(req.execute());
+
+  m_hw_item = wait();
+
+  return rc_t::OK;
+}
+
+std::string
+vhost_create_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "vhost-intf-create: " << m_hw_item.to_string() << " name:" << m_name
+    << " tag:" << m_tag;
+
+  return (s.str());
+}
+
 loopback_delete_cmd::loopback_delete_cmd(HW::item<handle_t>& item)
   : delete_cmd(item)
 {
@@ -215,6 +260,35 @@
   return (s.str());
 }
 
+vhost_delete_cmd::vhost_delete_cmd(HW::item<handle_t>& item,
+                                   const std::string& name)
+  : delete_cmd(item, name)
+{
+}
+
+rc_t
+vhost_delete_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.sw_if_index = m_hw_item.data().value();
+
+  VAPI_CALL(req.execute());
+
+  wait();
+
+  return rc_t::OK;
+}
+std::string
+vhost_delete_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "vhost-itf-delete: " << m_hw_item.to_string() << " name:" << m_name;
+
+  return (s.str());
+}
+
 state_change_cmd::state_change_cmd(HW::item<interface::admin_state_t>& state,
                                    const HW::item<handle_t>& hdl)
   : rpc_cmd(state)
@@ -475,10 +549,6 @@
   return (s.str());
 }
 
-dump_cmd::dump_cmd()
-{
-}
-
 stats_disable_cmd::stats_disable_cmd(const handle_t& handle)
   : rpc_cmd(m_res)
   , m_swifindex(handle)
@@ -521,6 +591,10 @@
   return (s.str());
 }
 
+dump_cmd::dump_cmd()
+{
+}
+
 bool
 dump_cmd::operator==(const dump_cmd& other) const
 {
@@ -548,6 +622,34 @@
   return ("itf-dump");
 }
 
+vhost_dump_cmd::vhost_dump_cmd()
+{
+}
+
+bool
+vhost_dump_cmd::operator==(const vhost_dump_cmd& other) const
+{
+  return (true);
+}
+
+rc_t
+vhost_dump_cmd::issue(connection& con)
+{
+  m_dump.reset(new msg_t(con.ctx(), std::ref(*this)));
+
+  VAPI_CALL(m_dump->execute());
+
+  wait();
+
+  return rc_t::OK;
+}
+
+std::string
+vhost_dump_cmd::to_string() const
+{
+  return ("vhost-itf-dump");
+}
+
 set_tag::set_tag(HW::item<handle_t>& item, const std::string& name)
   : rpc_cmd(item)
   , m_name(name)
diff --git a/src/vpp-api/vom/interface_cmds.hpp b/src/vpp-api/vom/interface_cmds.hpp
index f21a7f3..62762ef 100644
--- a/src/vpp-api/vom/interface_cmds.hpp
+++ b/src/vpp-api/vom/interface_cmds.hpp
@@ -27,6 +27,7 @@
 #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>
 
 namespace VOM {
@@ -110,6 +111,30 @@
 };
 
 /**
+ * A functor class that creates an interface
+ */
+class vhost_create_cmd
+  : public interface::create_cmd<vapi::Create_vhost_user_if>
+{
+public:
+  vhost_create_cmd(HW::item<handle_t>& item,
+                   const std::string& name,
+                   const std::string& tag);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+private:
+  const std::string m_tag;
+};
+
+/**
  * A command class to delete loopback interfaces in VPP
  */
 class loopback_delete_cmd : public interface::delete_cmd<vapi::Delete_loopback>
@@ -175,6 +200,25 @@
 };
 
 /**
+ * A functor class that deletes a Vhost interface
+ */
+class vhost_delete_cmd
+  : public interface::delete_cmd<vapi::Delete_vhost_user_if>
+{
+public:
+  vhost_delete_cmd(HW::item<handle_t>& item, const std::string& name);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+};
+
+/**
  * A command class to set tag on interfaces
  */
 class set_tag
@@ -479,6 +523,32 @@
    */
   bool operator==(const dump_cmd& i) const;
 };
+
+/**
+ * A cmd class that Dumps all the Vpp Interfaces
+ */
+class vhost_dump_cmd : public VOM::dump_cmd<vapi::Sw_interface_vhost_user_dump>
+{
+public:
+  /**
+   * Default Constructor
+   */
+  vhost_dump_cmd();
+
+  /**
+   * 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 vhost_dump_cmd& i) const;
+};
 };
 };
 /*
diff --git a/src/vpp-api/vom/interface_factory.cpp b/src/vpp-api/vom/interface_factory.cpp
index b8815ed..ef26c32 100644
--- a/src/vpp-api/vom/interface_factory.cpp
+++ b/src/vpp-api/vom/interface_factory.cpp
@@ -34,6 +34,7 @@
     interface::admin_state_t::from_int(vd.link_up_down);
   handle_t hdl(vd.sw_if_index);
   l2_address_t l2_address(vd.l2_address, vd.l2_address_length);
+  std::string tag = "";
 
   if (interface::type_t::AFPACKET == type) {
     /*
@@ -46,7 +47,11 @@
    * the interface type more specific
    */
   if (vd.tag[0] != 0) {
-    name = std::string(reinterpret_cast<const char*>(vd.tag));
+    tag = std::string(reinterpret_cast<const char*>(vd.tag));
+  }
+
+  if (!tag.empty() && interface::type_t::LOOPBACK == type) {
+    name = tag;
     type = interface::type_t::from_string(name);
   }
 
@@ -58,6 +63,8 @@
      * TAP interface
      */
     sp = tap_interface(name, state, route::prefix_t()).singular();
+    if (sp && !tag.empty())
+      sp->set(tag);
   } else if ((name.find(".") != std::string::npos) && (0 != vd.sub_id)) {
     /*
      * Sub-interface
@@ -66,15 +73,28 @@
     std::vector<std::string> parts;
     boost::split(parts, name, boost::is_any_of("."));
 
-    interface parent(parts[0], type, state);
+    interface parent(parts[0], type, state, tag);
     sp = sub_interface(parent, state, vd.sub_id).singular();
   } else if (interface::type_t::VXLAN == type) {
     /*
      * there's not enough information in a SW interface record to
-     * construct a VXLAN tunnel. so skip it.
+     * construct a VXLAN tunnel. so skip it. They have
+     * their own dump routines
      */
+  } else if (interface::type_t::VHOST == type) {
+    /*
+     * vhost interfaces already exist in db, look for it using
+     * sw_if_index
+     */
+    sp = interface::find(hdl);
+    if (sp) {
+      sp->set(state);
+      sp->set(l2_address);
+      if (!tag.empty())
+        sp->set(tag);
+    }
   } else {
-    sp = interface(name, type, state).singular();
+    sp = interface(name, type, state, tag).singular();
     sp->set(l2_address);
   }
 
@@ -82,10 +102,25 @@
    * set the handle on the intterface - N.B. this is the sigluar instance
    * not a stack local.
    */
-  sp->set(hdl);
+  if (sp)
+    sp->set(hdl);
 
   return (sp);
 }
+
+std::shared_ptr<interface>
+interface_factory::new_vhost_user_interface(
+  const vapi_payload_sw_interface_vhost_user_details& vd)
+{
+  std::shared_ptr<interface> sp;
+  std::string name = reinterpret_cast<const char*>(vd.sock_filename);
+  interface::type_t type = interface::type_t::from_string(name);
+  handle_t hdl(vd.sw_if_index);
+
+  sp = interface(name, type, interface::admin_state_t::DOWN).singular();
+  sp->set(hdl);
+  return (sp);
+}
 }; // namespace VOM
 
 /*
diff --git a/src/vpp-api/vom/interface_factory.hpp b/src/vpp-api/vom/interface_factory.hpp
index e1cbb38..a2c8199 100644
--- a/src/vpp-api/vom/interface_factory.hpp
+++ b/src/vpp-api/vom/interface_factory.hpp
@@ -21,6 +21,7 @@
 #include "vom/interface.hpp"
 
 #include <vapi/interface.api.vapi.hpp>
+#include <vapi/vhost_user.api.vapi.hpp>
 
 namespace VOM {
 
@@ -32,6 +33,9 @@
    */
   static std::shared_ptr<interface> new_interface(
     const vapi_payload_sw_interface_details& vd);
+
+  static std::shared_ptr<interface> new_vhost_user_interface(
+    const vapi_payload_sw_interface_vhost_user_details& vd);
 };
 };
 
diff --git a/src/vpp-api/vom/interface_types.cpp b/src/vpp-api/vom/interface_types.cpp
index a089f58..6d3dcae 100644
--- a/src/vpp-api/vom/interface_types.cpp
+++ b/src/vpp-api/vom/interface_types.cpp
@@ -27,6 +27,7 @@
 const interface::type_t interface::type_t::LOOPBACK(5, "LOOPBACK");
 const interface::type_t interface::type_t::LOCAL(6, "LOCAL");
 const interface::type_t interface::type_t::TAP(7, "TAP");
+const interface::type_t interface::type_t::VHOST(8, "VHOST");
 
 const interface::oper_state_t interface::oper_state_t::DOWN(0, "down");
 const interface::oper_state_t interface::oper_state_t::UP(1, "up");
@@ -37,7 +38,10 @@
 interface::type_t
 interface::type_t::from_string(const std::string& str)
 {
-  if (str.find("Ethernet") != std::string::npos) {
+  if ((str.find("Virtual") != std::string::npos) ||
+      (str.find("vhost") != std::string::npos)) {
+    return interface::type_t::VHOST;
+  } else if (str.find("Ethernet") != std::string::npos) {
     return interface::type_t::ETHERNET;
   } else if (str.find("vxlan") != std::string::npos) {
     return interface::type_t::VXLAN;