VOM: vhost-use interfaces 19/10719/3
authorNeale Ranns <neale.ranns@cisco.com>
Tue, 20 Feb 2018 16:10:44 +0000 (08:10 -0800)
committerDamjan Marion <dmarion.lists@gmail.com>
Thu, 22 Feb 2018 19:57:08 +0000 (19:57 +0000)
Change-Id: Iee1574d1f0f081ccc4a90fd9825a0b5e254aa642
Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
src/vpp-api/vom/interface.cpp
src/vpp-api/vom/interface.hpp
src/vpp-api/vom/interface_cmds.cpp
src/vpp-api/vom/interface_cmds.hpp
src/vpp-api/vom/interface_factory.cpp
src/vpp-api/vom/interface_factory.hpp
src/vpp-api/vom/interface_types.cpp
test/ext/vom_test.cpp

index 9fa96af..f943bf4 100644 (file)
@@ -38,7 +38,8 @@ interface::event_handler interface::m_evh;
  */
 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 @@ interface::interface(const std::string& name,
   , 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 @@ interface::interface(const std::string& name,
   , 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 @@ interface::interface(const interface& o)
   , 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 @@ interface::to_string() const
   }
 
   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 @@ interface::mk_create_cmd(std::queue<cmd*>& q)
   } 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 @@ interface::mk_delete_cmd(std::queue<cmd*>& q)
     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);
@@ -344,6 +367,12 @@ interface::update(const interface& desired)
   }
 }
 
+void
+interface::set(const admin_state_t& state)
+{
+  m_state = state;
+}
+
 void
 interface::set(const l2_address_t& addr)
 {
@@ -364,6 +393,12 @@ interface::set(const oper_state_t& state)
   m_oper = state;
 }
 
+void
+interface::set(const std::string& tag)
+{
+  m_tag = tag;
+}
+
 void
 interface::enable_stats_i(interface::stat_listener& el)
 {
@@ -429,6 +464,20 @@ interface::dump(std::ostream& os)
 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
    */
index da0db40..5d29510 100644 (file)
@@ -90,6 +90,11 @@ public:
      */
     const static type_t TAP;
 
+    /**
+     * vhost-user interface type
+     */
+    const static type_t VHOST;
+
     /**
      * Convert VPP's name of the interface to a type
      */
@@ -157,7 +162,10 @@ public:
   /**
    * 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 @@ public:
   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
    */
@@ -214,6 +223,11 @@ public:
    */
   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
    */
@@ -224,6 +238,11 @@ public:
    */
   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
    */
@@ -580,6 +599,11 @@ private:
    */
   oper_state_t m_oper;
 
+  /**
+   * tag of the interface
+   */
+  std::string m_tag;
+
   /**
    * A map of all interfaces keyed against VPP's handle
    */
index 031aaea..df34154 100644 (file)
@@ -20,6 +20,7 @@ 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_TAP_API_JSON;
+DEFINE_VAPI_MSG_IDS_VHOST_USER_API_JSON;
 DEFINE_VAPI_MSG_IDS_STATS_API_JSON;
 
 namespace VOM {
@@ -129,6 +130,50 @@ tap_create_cmd::to_string() const
   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 @@ tap_delete_cmd::to_string() const
   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 @@ stats_enable_cmd::to_string() const
   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 @@ stats_disable_cmd::to_string() const
   return (s.str());
 }
 
+dump_cmd::dump_cmd()
+{
+}
+
 bool
 dump_cmd::operator==(const dump_cmd& other) const
 {
@@ -548,6 +622,34 @@ dump_cmd::to_string() const
   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)
index f21a7f3..62762ef 100644 (file)
@@ -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 {
@@ -109,6 +110,30 @@ public:
   std::string to_string() const;
 };
 
+/**
+ * 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
  */
@@ -174,6 +199,25 @@ public:
   std::string to_string() const;
 };
 
+/**
+ * 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
  */
@@ -479,6 +523,32 @@ public:
    */
   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;
+};
 };
 };
 /*
index b8815ed..ef26c32 100644 (file)
@@ -34,6 +34,7 @@ interface_factory::new_interface(const vapi_payload_sw_interface_details& vd)
     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 @@ interface_factory::new_interface(const vapi_payload_sw_interface_details& vd)
    * 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 @@ interface_factory::new_interface(const vapi_payload_sw_interface_details& vd)
      * 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 @@ interface_factory::new_interface(const vapi_payload_sw_interface_details& vd)
     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,8 +102,23 @@ interface_factory::new_interface(const vapi_payload_sw_interface_details& vd)
    * 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
index e1cbb38..a2c8199 100644 (file)
@@ -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 @@ public:
    */
   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);
 };
 };
 
index a089f58..6d3dcae 100644 (file)
@@ -27,6 +27,7 @@ const interface::type_t interface::type_t::AFPACKET(4, "AFPACKET");
 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 @@ const interface::admin_state_t interface::admin_state_t::UP(1, "up");
 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;
index 4c09ab4..9f7cfd6 100644 (file)
@@ -174,6 +174,10 @@ public:
                     {
                         rc = handle_derived<interface_cmds::loopback_create_cmd>(f_exp, f_act);
                     }
+                    else if (typeid(*f_exp) == typeid(interface_cmds::vhost_create_cmd))
+                    {
+                        rc = handle_derived<interface_cmds::vhost_create_cmd>(f_exp, f_act);
+                    }
                     else if (typeid(*f_exp) == typeid(interface_cmds::loopback_delete_cmd))
                     {
                         rc = handle_derived<interface_cmds::loopback_delete_cmd>(f_exp, f_act);
@@ -182,6 +186,10 @@ public:
                     {
                         rc = handle_derived<interface_cmds::af_packet_delete_cmd>(f_exp, f_act);
                     }
+                    else if (typeid(*f_exp) == typeid(interface_cmds::vhost_delete_cmd))
+                    {
+                       rc = handle_derived<interface_cmds::vhost_delete_cmd>(f_exp, f_act);
+                    }
                     else if (typeid(*f_exp) == typeid(interface_cmds::state_change_cmd))
                     {
                         rc = handle_derived<interface_cmds::state_change_cmd>(f_exp, f_act);
@@ -605,12 +613,15 @@ BOOST_AUTO_TEST_CASE(test_interface) {
     TRY_CHECK(OM::mark(go));
 
     std::string itf2_name = "afpacket2";
+    std::string itf2_tag = "uuid-of-afpacket2-interface";
     interface itf2(itf2_name,
                    interface::type_t::AFPACKET,
-                   interface::admin_state_t::UP);
+                   interface::admin_state_t::UP,
+                   itf2_tag);
     HW::item<handle_t> hw_ifh2(3, rc_t::OK);
 
     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh2, itf2_name));
+    ADD_EXPECT(interface_cmds::set_tag(hw_ifh2, itf2_tag));
     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh2));
     TRY_CHECK_RC(OM::write(go, itf2));
 
@@ -623,6 +634,23 @@ BOOST_AUTO_TEST_CASE(test_interface) {
     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh2));
     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh2, itf2_name));
     TRY_CHECK(OM::sweep(go));
+
+
+    std::string itf3_name = "/PATH/TO/vhost_user1.sock";
+    std::string itf3_tag = "uuid-of-vhost_user1-interface";
+    interface itf3(itf3_name,
+                   interface::type_t::VHOST,
+                   interface::admin_state_t::UP,
+                   itf3_tag);
+    HW::item<handle_t> hw_ifh3(4, rc_t::OK);
+
+    ADD_EXPECT(interface_cmds::vhost_create_cmd(hw_ifh3, itf3_name, itf3_tag));
+    ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh3));
+    TRY_CHECK_RC(OM::write(go, itf3));
+
+    ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh3));
+    ADD_EXPECT(interface_cmds::vhost_delete_cmd(hw_ifh3, itf3_name));
+    TRY_CHECK(OM::remove(go));
 }
 
 BOOST_AUTO_TEST_CASE(test_bvi) {