VOM: bond: Add support for LACP 41/10941/9
authorMohsin Kazmi <sykazmi@cisco.com>
Fri, 2 Mar 2018 11:31:37 +0000 (12:31 +0100)
committerNeale Ranns <nranns@cisco.com>
Tue, 27 Mar 2018 15:44:19 +0000 (15:44 +0000)
Change-Id: I0245263b212142858d3305b0f365d8342912dbb9
Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
18 files changed:
src/vpp-api/vom/Makefile.am
src/vpp-api/vom/bond_group_binding.cpp [new file with mode: 0644]
src/vpp-api/vom/bond_group_binding.hpp [new file with mode: 0644]
src/vpp-api/vom/bond_group_binding_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/bond_group_binding_cmds.hpp [new file with mode: 0644]
src/vpp-api/vom/bond_interface.cpp [new file with mode: 0644]
src/vpp-api/vom/bond_interface.hpp [new file with mode: 0644]
src/vpp-api/vom/bond_interface_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/bond_interface_cmds.hpp [new file with mode: 0644]
src/vpp-api/vom/bond_member.cpp [new file with mode: 0644]
src/vpp-api/vom/bond_member.hpp [new file with mode: 0644]
src/vpp-api/vom/interface.cpp
src/vpp-api/vom/interface.hpp
src/vpp-api/vom/interface_factory.cpp
src/vpp-api/vom/interface_factory.hpp
src/vpp-api/vom/interface_types.cpp
src/vpp-api/vom/types.hpp
test/ext/vom_test.cpp

index 45c2c1c..00ab467 100644 (file)
@@ -78,6 +78,11 @@ libvom_la_SOURCES =                  \
        arp_proxy_binding.cpp           \
        arp_proxy_config_cmds.cpp       \
        arp_proxy_config.cpp            \
+       bond_group_binding_cmds.cpp     \
+       bond_group_binding.cpp          \
+       bond_interface_cmds.cpp         \
+       bond_interface.cpp              \
+       bond_member.cpp                 \
        bridge_domain_cmds.cpp          \
        bridge_domain.cpp               \
        bridge_domain_arp_entry.cpp     \
@@ -169,6 +174,9 @@ endif
 vominclude_HEADERS =                   \
        arp_proxy_binding.hpp           \
        arp_proxy_config.hpp            \
+       bond_group_binding.hpp          \
+       bond_interface.hpp              \
+       bond_member.hpp                 \
        bridge_domain.hpp               \
        bridge_domain_arp_entry.hpp     \
        bridge_domain_entry.hpp         \
diff --git a/src/vpp-api/vom/bond_group_binding.cpp b/src/vpp-api/vom/bond_group_binding.cpp
new file mode 100644 (file)
index 0000000..6958eb3
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * 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/bond_group_binding.hpp"
+#include "vom/bond_group_binding_cmds.hpp"
+
+namespace VOM {
+
+/**
+ * A DB of all bond interface binding
+ */
+singular_db<bond_group_binding::key_t, bond_group_binding>
+  bond_group_binding::m_db;
+
+bond_group_binding::event_handler bond_group_binding::m_evh;
+
+bond_group_binding::bond_group_binding(const bond_interface& itf,
+                                       const enslaved_itf_t& itfs)
+  : m_itf(itf.singular())
+  , m_mem_itfs(itfs)
+  , m_binding(false)
+{
+}
+
+bond_group_binding::bond_group_binding(const bond_group_binding& o)
+  : m_itf(o.m_itf)
+  , m_mem_itfs(o.m_mem_itfs)
+  , m_binding(o.m_binding)
+{
+}
+
+bond_group_binding::~bond_group_binding()
+{
+  sweep();
+
+  // not in the DB anymore.
+  m_db.release(key(), this);
+}
+
+const bond_group_binding::key_t
+bond_group_binding::key() const
+{
+  return (m_itf->key() + "-binding");
+}
+
+void
+bond_group_binding::sweep()
+{
+
+  auto it = m_mem_itfs.cbegin();
+  while (it != m_mem_itfs.cend()) {
+    if (m_binding) {
+      HW::enqueue(
+        new bond_group_binding_cmds::unbind_cmd(m_binding, it->hdl()));
+    }
+    HW::write();
+    ++it;
+  }
+}
+
+void
+bond_group_binding::dump(std::ostream& os)
+{
+  m_db.dump(os);
+}
+
+void
+bond_group_binding::replay()
+{
+  auto it = m_mem_itfs.cbegin();
+  while (it != m_mem_itfs.cend()) {
+    if (m_binding) {
+      HW::enqueue(
+        new bond_group_binding_cmds::bind_cmd(m_binding, m_itf->handle(), *it));
+    }
+    HW::write();
+    ++it;
+  }
+}
+
+std::string
+bond_group_binding::to_string() const
+{
+  auto it = m_mem_itfs.cbegin();
+  std::ostringstream s;
+  s << "bond-interface-binding: " << m_itf->to_string() << " slave-itfs: [";
+  while (it != m_mem_itfs.cend()) {
+    s << " " << it->to_string();
+    ++it;
+  }
+  s << "]";
+  return (s.str());
+}
+
+void
+bond_group_binding::update(const bond_group_binding& desired)
+{
+  /*
+   * the desired state is always that the interface should be created
+   */
+  auto it = m_mem_itfs.cbegin();
+  while (it != m_mem_itfs.cend()) {
+    if (!m_binding) {
+      HW::enqueue(
+        new bond_group_binding_cmds::bind_cmd(m_binding, m_itf->handle(), *it));
+    }
+    ++it;
+  }
+}
+
+std::shared_ptr<bond_group_binding>
+bond_group_binding::find_or_add(const bond_group_binding& temp)
+{
+  return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<bond_group_binding>
+bond_group_binding::singular() const
+{
+  return find_or_add(*this);
+}
+
+bond_group_binding::event_handler::event_handler()
+{
+  OM::register_listener(this);
+  inspect::register_handler({ "bond-intf-binding" }, "Bond interface binding",
+                            this);
+}
+
+void
+bond_group_binding::event_handler::handle_replay()
+{
+  m_db.replay();
+}
+
+void
+bond_group_binding::event_handler::handle_populate(const client_db::key_t& key)
+{
+  /*
+   * handle it in interface class
+   */
+}
+
+dependency_t
+bond_group_binding::event_handler::order() const
+{
+  /*
+   * We want enslaved interfaces bind to bond after interface
+   * but before anything else.
+   */
+  return (dependency_t::BOND_BINDING);
+}
+
+void
+bond_group_binding::event_handler::show(std::ostream& os)
+{
+  m_db.dump(os);
+}
+}
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/bond_group_binding.hpp b/src/vpp-api/vom/bond_group_binding.hpp
new file mode 100644 (file)
index 0000000..bfac488
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * 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_BOND_GROUP_BINDING_H__
+#define __VOM_BOND_GROUP_BINDING_H__
+
+#include <set>
+
+#include "vom/bond_interface.hpp"
+#include "vom/bond_member.hpp"
+#include "vom/hw.hpp"
+#include "vom/inspect.hpp"
+#include "vom/interface.hpp"
+#include "vom/object_base.hpp"
+#include "vom/om.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A representation of bond interface binding
+ */
+class bond_group_binding : public object_base
+{
+public:
+  /**
+   * The KEY can be used to uniquely identify the Bond Binding.
+   * (other choices for keys, like the summation of the properties
+   * of the rules, are rather too cumbersome to use
+   */
+  typedef std::string key_t;
+
+  /**
+   * The container type for enslaved itfs
+   */
+  typedef std::set<bond_member> enslaved_itf_t;
+
+  /**
+   * Construct a new object matching the desried state
+   */
+  bond_group_binding(const bond_interface& itf, const enslaved_itf_t& mem);
+
+  /**
+   * Copy Constructor
+   */
+  bond_group_binding(const bond_group_binding& o);
+
+  /**
+   * Destructor
+   */
+  ~bond_group_binding();
+
+  /**
+   * Return the 'singular' of the bond interface binding that matches this
+   * object
+   */
+  std::shared_ptr<bond_group_binding> singular() const;
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * get the key to this object
+   */
+  const key_t key() const;
+
+  /**
+   * Dump all bond interface bindings into the stream provided
+   */
+  static void dump(std::ostream& os);
+
+private:
+  /**
+   * Class definition for listeners to OM events
+   */
+  class event_handler : public OM::listener, public inspect::command_handler
+  {
+  public:
+    event_handler();
+    virtual ~event_handler() = default;
+
+    /**
+     * Handle a populate event
+     */
+    void handle_populate(const client_db::key_t& key);
+
+    /**
+     * Handle a replay event
+     */
+    void handle_replay();
+
+    /**
+     * Show the object in the Singular DB
+     */
+    void show(std::ostream& os);
+
+    /**
+     * Get the sortable Id of the listener
+     */
+    dependency_t order() const;
+  };
+
+  /**
+   * event_handler to register with OM
+   */
+  static event_handler m_evh;
+
+  /**
+   * Enqueue command to the VPP command Q for the update
+   */
+  void update(const bond_group_binding& obj);
+
+  /**
+   * Find or add bond interface binding to the OM
+   */
+  static std::shared_ptr<bond_group_binding> find_or_add(
+    const bond_group_binding& temp);
+
+  /*
+   * It's the OM class that calls singular()
+   */
+  friend class OM;
+
+  /**
+   * It's the singular_db class that calls replay()
+   */
+  friend class singular_db<key_t, bond_group_binding>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * replay the object to create it in hardware
+   */
+  void replay(void);
+
+  /**
+   * A reference counting pointer to the bond interface.
+   * By holding the reference here, we can guarantee that
+   * this object will outlive the interface
+   */
+  std::shared_ptr<bond_interface> m_itf;
+
+  /**
+   * A list of member interfaces.
+   */
+  const enslaved_itf_t m_mem_itfs;
+
+  /**
+   * HW configuration for the binding. The bool representing the
+   * do/don't bind.
+   */
+  HW::item<bool> m_binding;
+
+  /**
+   * A map of all bond interface bindings keyed against the interface +
+   * "binding".
+   */
+  static singular_db<key_t, bond_group_binding> m_db;
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/bond_group_binding_cmds.cpp b/src/vpp-api/vom/bond_group_binding_cmds.cpp
new file mode 100644 (file)
index 0000000..3ffe981
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * 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/bond_group_binding_cmds.hpp"
+
+namespace VOM {
+namespace bond_group_binding_cmds {
+
+bind_cmd::bind_cmd(HW::item<bool>& item,
+                   const handle_t& bond_itf,
+                   const bond_member& itf)
+  : rpc_cmd(item)
+  , m_bond_itf(bond_itf)
+  , m_itf(itf)
+{
+}
+
+bool
+bind_cmd::operator==(const bind_cmd& other) const
+{
+  return ((m_bond_itf == other.m_bond_itf) && (m_itf == other.m_itf));
+}
+
+rc_t
+bind_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+
+  m_itf.to_vpp(payload);
+  payload.bond_sw_if_index = m_bond_itf.value();
+
+  VAPI_CALL(req.execute());
+
+  m_hw_item.set(wait());
+
+  return rc_t::OK;
+}
+
+std::string
+bind_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "bond-itf-bind: " << m_hw_item.to_string()
+    << " bond-itf:" << m_bond_itf.to_string()
+    << " slave-itf:" << m_itf.hdl().to_string();
+
+  return (s.str());
+}
+
+unbind_cmd::unbind_cmd(HW::item<bool>& item, const handle_t& itf)
+  : rpc_cmd(item)
+  , m_itf(itf)
+{
+}
+
+bool
+unbind_cmd::operator==(const unbind_cmd& other) const
+{
+  return (m_itf == other.m_itf);
+}
+
+rc_t
+unbind_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.sw_if_index = m_itf.value();
+
+  VAPI_CALL(req.execute());
+
+  wait();
+  m_hw_item.set(rc_t::NOOP);
+
+  return rc_t::OK;
+}
+
+std::string
+unbind_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "bond-itf-unbind: " << m_hw_item.to_string()
+    << " slave-itf:" << m_itf.to_string();
+
+  return (s.str());
+}
+
+dump_cmd::dump_cmd(const handle_t& hdl)
+  : m_itf(hdl)
+{
+}
+
+dump_cmd::dump_cmd(const dump_cmd& d)
+  : m_itf(d.m_itf)
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+  return (true);
+}
+
+rc_t
+dump_cmd::issue(connection& con)
+{
+  m_dump.reset(new msg_t(con.ctx(), std::ref(*this)));
+  auto& payload = m_dump->get_request().get_payload();
+  payload.sw_if_index = m_itf.value();
+
+  VAPI_CALL(m_dump->execute());
+
+  wait();
+
+  return rc_t::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+  return ("bond-slave-itfs-dump");
+}
+
+}; // namespace bond_group_binding_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/bond_group_binding_cmds.hpp b/src/vpp-api/vom/bond_group_binding_cmds.hpp
new file mode 100644 (file)
index 0000000..71c4f9f
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * 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_BOND_GROUP_BINDING_CMDS_H__
+#define __VOM_BOND_GROUP_BINDING_CMDS_H__
+
+#include "vom/bond_group_binding.hpp"
+#include "vom/dump_cmd.hpp"
+
+#include <vapi/bond.api.vapi.hpp>
+
+namespace VOM {
+namespace bond_group_binding_cmds {
+/**
+ * A command class that binds the slave interface to the bond interface
+ */
+class bind_cmd : public rpc_cmd<HW::item<bool>, rc_t, vapi::Bond_enslave>
+{
+public:
+  /**
+   * Constructor
+   */
+  bind_cmd(HW::item<bool>& item,
+           const handle_t& bond_itf,
+           const bond_member& itf);
+
+  /**
+   * 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 bind_cmd& i) const;
+
+private:
+  /**
+   * sw_if_index of bond interface
+   */
+  const handle_t m_bond_itf;
+
+  /**
+   * member interface of bond group
+   */
+  const bond_member m_itf;
+};
+
+/**
+ * A cmd class that detach slave from a bond interface
+ */
+class unbind_cmd : public rpc_cmd<HW::item<bool>, rc_t, vapi::Bond_detach_slave>
+{
+public:
+  /**
+   * Constructor
+   */
+  unbind_cmd(HW::item<bool>& item, const handle_t& itf);
+
+  /**
+   * 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 unbind_cmd& i) const;
+
+private:
+  /**
+   * slave interface of bond group
+   */
+  const handle_t m_itf;
+};
+
+/**
+ * A cmd class that Dumps slave itfs
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Sw_interface_slave_dump>
+{
+public:
+  /**
+   * Default Constructor
+   */
+  dump_cmd(const handle_t& itf);
+  dump_cmd(const dump_cmd& d);
+  /**
+   * 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 dump_cmd& i) const;
+
+private:
+  /**
+   * The interface to get the addresses for
+   */
+  const handle_t m_itf;
+};
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/bond_interface.cpp b/src/vpp-api/vom/bond_interface.cpp
new file mode 100644 (file)
index 0000000..32a00ad
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * 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/bond_interface.hpp"
+#include "vom/bond_group_binding.hpp"
+#include "vom/bond_group_binding_cmds.hpp"
+#include "vom/bond_interface_cmds.hpp"
+
+namespace VOM {
+/**
+ * Construct a new object matching the desried state
+ */
+bond_interface::bond_interface(const std::string& name,
+                               admin_state_t state,
+                               mode_t mode,
+                               lb_t lb)
+  : interface(name, type_t::BOND, state)
+  , m_l2_address(l2_address_t::ZERO)
+  , m_mode(mode)
+  , m_lb(lb)
+{
+}
+
+bond_interface::bond_interface(const std::string& name,
+                               admin_state_t state,
+                               const l2_address_t& l2_address,
+                               mode_t mode,
+                               lb_t lb)
+  : interface(name, type_t::BOND, state)
+  , m_l2_address(l2_address)
+  , m_mode(mode)
+  , m_lb(lb)
+{
+}
+
+bond_interface::~bond_interface()
+{
+  sweep();
+  release();
+}
+
+bond_interface::bond_interface(const bond_interface& o)
+  : interface(o)
+  , m_l2_address(o.m_l2_address)
+  , m_mode(o.m_mode)
+  , m_lb(o.m_lb)
+{
+}
+
+std::shared_ptr<bond_interface>
+bond_interface::find(const handle_t& hdl)
+{
+  return std::dynamic_pointer_cast<bond_interface>(interface::find(hdl));
+}
+
+void
+bond_interface::set(bond_interface::mode_t mode)
+{
+  m_mode = mode;
+}
+
+void
+bond_interface::set(bond_interface::lb_t lb)
+{
+  m_lb = lb;
+}
+
+std::string
+bond_interface::to_string() const
+{
+  std::ostringstream s;
+
+  s << this->interface::to_string() << " mode:" << m_mode.to_string()
+    << " lb:" << m_lb.to_string();
+
+  return (s.str());
+}
+
+std::queue<cmd*>&
+bond_interface::mk_create_cmd(std::queue<cmd*>& q)
+{
+  q.push(new bond_interface_cmds::create_cmd(m_hdl, name(), m_mode, m_lb,
+                                             m_l2_address));
+
+  return (q);
+}
+
+std::queue<cmd*>&
+bond_interface::mk_delete_cmd(std::queue<cmd*>& q)
+{
+  q.push(new bond_interface_cmds::delete_cmd(m_hdl));
+
+  return (q);
+}
+
+std::shared_ptr<bond_interface>
+bond_interface::singular() const
+{
+  return std::dynamic_pointer_cast<bond_interface>(singular_i());
+}
+
+std::shared_ptr<interface>
+bond_interface::singular_i() const
+{
+  return m_db.find_or_add(name(), *this);
+}
+
+void
+bond_interface::set(handle_t& handle)
+{
+  this->interface::set(handle);
+}
+
+const bond_interface::mode_t bond_interface::mode_t::ROUND_ROBIN(1,
+                                                                 "round-robin");
+const bond_interface::mode_t bond_interface::mode_t::ACTIVE_BACKUP(
+  2,
+  "active-backup");
+const bond_interface::mode_t bond_interface::mode_t::XOR(3, "xor");
+const bond_interface::mode_t bond_interface::mode_t::BROADCAST(4, "broadcast");
+const bond_interface::mode_t bond_interface::mode_t::LACP(5, "lacp");
+const bond_interface::mode_t bond_interface::mode_t::UNSPECIFIED(0,
+                                                                 "unspecified");
+
+const bond_interface::mode_t
+bond_interface::mode_t::from_numeric_val(uint8_t numeric)
+{
+  if (1 == numeric) {
+    return (bond_interface::mode_t::ROUND_ROBIN);
+  }
+  if (2 == numeric) {
+    return (bond_interface::mode_t::ACTIVE_BACKUP);
+  }
+  if (3 == numeric) {
+    return (bond_interface::mode_t::XOR);
+  }
+  if (4 == numeric) {
+    return (bond_interface::mode_t::BROADCAST);
+  }
+  if (5 == numeric) {
+    return (bond_interface::mode_t::LACP);
+  }
+
+  return (bond_interface::mode_t::UNSPECIFIED);
+}
+
+bond_interface::mode_t::mode_t(int v, const std::string& s)
+  : enum_base<bond_interface::mode_t>(v, s)
+{
+}
+
+const bond_interface::lb_t bond_interface::lb_t::L2(0, "l2");
+const bond_interface::lb_t bond_interface::lb_t::L34(1, "l34");
+const bond_interface::lb_t bond_interface::lb_t::L23(2, "l23");
+const bond_interface::lb_t bond_interface::lb_t::UNSPECIFIED(~0, "unspecified");
+
+const bond_interface::lb_t
+bond_interface::lb_t::from_numeric_val(uint8_t numeric)
+{
+  if (0 == numeric) {
+    return (bond_interface::lb_t::L2);
+  }
+  if (1 == numeric) {
+    return (bond_interface::lb_t::L34);
+  }
+  if (2 == numeric) {
+    return (bond_interface::lb_t::L23);
+  }
+
+  return (bond_interface::lb_t::UNSPECIFIED);
+}
+
+bond_interface::lb_t::lb_t(int v, const std::string& s)
+  : enum_base<bond_interface::lb_t>(v, s)
+{
+}
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/bond_interface.hpp b/src/vpp-api/vom/bond_interface.hpp
new file mode 100644 (file)
index 0000000..4584bd1
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * 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_BOND_INTERFACE_H__
+#define __VOM_BOND_INTERFACE_H__
+
+#include "vom/interface.hpp"
+
+namespace VOM {
+/**
+ * A bond-interface. e.g. a bond interface
+ */
+class bond_interface : public interface
+{
+public:
+  /**
+   * A bond interface mode
+   */
+  struct mode_t : enum_base<mode_t>
+  {
+    /**
+     * Round-Robin bond interface mode
+     */
+    const static mode_t ROUND_ROBIN;
+    /**
+     * Active-backup bond interface mode
+     */
+    const static mode_t ACTIVE_BACKUP;
+    /**
+     * XOR bond interface mode
+     */
+    const static mode_t XOR;
+    /**
+     * Broadcast bond interface mode
+     */
+    const static mode_t BROADCAST;
+    /**
+     * LACP bond interface mode
+     */
+    const static mode_t LACP;
+    /**
+     * Unspecificed bond interface mode
+     */
+    const static mode_t UNSPECIFIED;
+
+    /**
+     * Convert VPP's value of the bond to a mode
+     */
+    static const mode_t from_numeric_val(uint8_t v);
+
+  private:
+    /**
+     * Private constructor taking the value and the string name
+     */
+    mode_t(int v, const std::string& s);
+  };
+
+  /**
+    * A bond interface load balance
+    */
+  struct lb_t : enum_base<lb_t>
+  {
+    /**
+     * L2 bond interface lb
+     */
+    const static lb_t L2;
+    /**
+     * L34 bond interface lb
+     */
+    const static lb_t L34;
+    /**
+     * L23 bond interface lb
+     */
+    const static lb_t L23;
+    /**
+     * Unspecificed bond interface lb
+     */
+    const static lb_t UNSPECIFIED;
+
+    /**
+     * Convert VPP's value of the bond to a lb
+     */
+    static const lb_t from_numeric_val(uint8_t v);
+
+  private:
+    /**
+     * Private constructor taking the value and the string name
+     */
+    lb_t(int v, const std::string& s);
+  };
+
+  bond_interface(const std::string& name,
+                 admin_state_t state,
+                 mode_t mode,
+                 lb_t lb = lb_t::UNSPECIFIED);
+
+  bond_interface(const std::string& name,
+                 admin_state_t state,
+                 const l2_address_t& l2_address,
+                 mode_t mode,
+                 lb_t lb = lb_t::UNSPECIFIED);
+
+  ~bond_interface();
+  bond_interface(const bond_interface& o);
+
+  /**
+   * The the singular instance of the bond interface in the DB by handle
+   */
+  static std::shared_ptr<bond_interface> find(const handle_t& hdl);
+
+  /**
+   * Return the matching 'singular instance' of the BOND interface
+   */
+  std::shared_ptr<bond_interface> singular() const;
+
+  /**
+   * set the mode
+   */
+  void set(mode_t mode);
+
+  /**
+   * set the lb
+   */
+  void set(lb_t lb);
+
+  /**
+   * convert to string
+   */
+  virtual std::string to_string() const;
+
+protected:
+  /**
+   * set the handle
+   */
+  void set(handle_t& handle);
+  friend class interface_factory;
+
+private:
+  /**
+   * l2 address on bond interface
+   */
+  l2_address_t m_l2_address;
+
+  /**
+   *  mode on bond interface
+   */
+  mode_t m_mode;
+
+  /**
+   * lb mode on bond interface
+   */
+  lb_t m_lb;
+
+  /**
+   * Return the matching 'instance' of the sub-interface
+   *  over-ride from the base class
+   */
+  std::shared_ptr<interface> singular_i() const;
+
+  /**
+   * Virtual functions to construct an interface create commands.
+   */
+  virtual std::queue<cmd*>& mk_create_cmd(std::queue<cmd*>& cmds);
+
+  /**
+   * Virtual functions to construct an interface delete commands.
+   */
+  virtual std::queue<cmd*>& mk_delete_cmd(std::queue<cmd*>& cmds);
+
+  /*
+   * It's the OM class that call singular()
+   */
+  friend class OM;
+};
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/bond_interface_cmds.cpp b/src/vpp-api/vom/bond_interface_cmds.cpp
new file mode 100644 (file)
index 0000000..d59560d
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * 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/bond_interface_cmds.hpp"
+
+DEFINE_VAPI_MSG_IDS_BOND_API_JSON;
+
+namespace VOM {
+namespace bond_interface_cmds {
+create_cmd::create_cmd(HW::item<handle_t>& item,
+                       const std::string& name,
+                       const bond_interface::mode_t& mode,
+                       const bond_interface::lb_t& lb,
+                       const l2_address_t& l2_address)
+  : interface::create_cmd<vapi::Bond_create>(item, name)
+  , m_mode(mode)
+  , m_lb(lb)
+  , m_l2_address(l2_address)
+{
+}
+
+rc_t
+create_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+
+  if (m_l2_address != l2_address_t::ZERO) {
+    m_l2_address.to_bytes(payload.mac_address, 6);
+    payload.use_custom_mac = 1;
+  }
+
+  payload.mode = m_mode.value();
+  if ((m_mode == bond_interface::mode_t::XOR ||
+       m_mode == bond_interface::mode_t::LACP) &&
+      m_lb != bond_interface::lb_t::UNSPECIFIED)
+    payload.lb = m_lb.value();
+
+  VAPI_CALL(req.execute());
+
+  m_hw_item = wait();
+  if (m_hw_item.rc() == rc_t::OK) {
+    insert_interface();
+  }
+
+  return rc_t::OK;
+}
+
+std::string
+create_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "bond-intf-create: " << m_hw_item.to_string();
+
+  return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<handle_t>& item)
+  : interface::delete_cmd<vapi::Bond_delete>(item)
+{
+}
+
+rc_t
+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();
+  m_hw_item.set(rc_t::NOOP);
+  remove_interface();
+
+  return rc_t::OK;
+}
+
+std::string
+delete_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "bond-itf-delete: " << m_hw_item.to_string();
+
+  return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+  return (true);
+}
+
+rc_t
+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
+dump_cmd::to_string() const
+{
+  return ("bond-itf-dump");
+}
+} // namespace bond_interface_cmds
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/bond_interface_cmds.hpp b/src/vpp-api/vom/bond_interface_cmds.hpp
new file mode 100644 (file)
index 0000000..06a8ac1
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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_BOND_INTERFACE_CMDS_H__
+#define __VOM_BOND_INTERFACE_CMDS_H__
+
+#include "vom/bond_interface.hpp"
+#include "vom/dump_cmd.hpp"
+#include "vom/interface.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/bond.api.vapi.hpp>
+#include <vapi/interface.api.vapi.hpp>
+
+namespace VOM {
+namespace bond_interface_cmds {
+
+/**
+ * A functor class that creates an interface
+ */
+class create_cmd : public interface::create_cmd<vapi::Bond_create>
+{
+public:
+  create_cmd(HW::item<handle_t>& item,
+             const std::string& name,
+             const bond_interface::mode_t& mode,
+             const bond_interface::lb_t& lb,
+             const l2_address_t& l2_address);
+
+  /**
+   * 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 bond_interface::mode_t m_mode;
+  const bond_interface::lb_t m_lb;
+  const l2_address_t m_l2_address;
+};
+
+/**
+ * A functor class that deletes a Tap interface
+ */
+class delete_cmd : public interface::delete_cmd<vapi::Bond_delete>
+{
+public:
+  delete_cmd(HW::item<handle_t>& item);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+};
+
+/**
+ * A cmd class that Dumps all the Vpp Interfaces
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Sw_interface_bond_dump>
+{
+public:
+  /**
+   * Default Constructor
+   */
+  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 dump_cmd& i) const;
+};
+
+}; // namespace bond_interface_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+#endif
diff --git a/src/vpp-api/vom/bond_member.cpp b/src/vpp-api/vom/bond_member.cpp
new file mode 100644 (file)
index 0000000..f1a27b3
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * 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/bond_member.hpp"
+
+namespace VOM {
+/**
+ * Construct a new object matching the desried state
+ */
+bond_member::bond_member(const interface& itf, mode_t mode, rate_t rate)
+  : m_itf(itf.singular())
+  , m_mode(mode)
+  , m_rate(rate)
+{
+}
+
+bond_member::~bond_member()
+{
+}
+
+bond_member::bond_member(const bond_member& o)
+  : m_itf(o.m_itf)
+  , m_mode(o.m_mode)
+  , m_rate(o.m_rate)
+{
+}
+
+void
+bond_member::to_vpp(vapi_payload_bond_enslave& bond_enslave) const
+{
+  bond_enslave.sw_if_index = m_itf->handle().value();
+  bond_enslave.is_passive = (m_mode == mode_t::PASSIVE) ? 1 : 0;
+  bond_enslave.is_long_timeout = (m_rate == rate_t::SLOW) ? 1 : 0;
+}
+
+std::string
+bond_member::to_string() const
+{
+  std::ostringstream s;
+
+  s << m_itf->to_string() << " mode:" << m_mode.to_string()
+    << " rate:" << m_rate.to_string();
+
+  return (s.str());
+}
+
+bool
+bond_member::operator<(const bond_member& itf) const
+{
+  return (m_itf->handle() < itf.m_itf->handle());
+}
+
+void
+bond_member::set(mode_t mode)
+{
+  m_mode = mode;
+}
+
+void
+bond_member::set(rate_t rate)
+{
+  m_rate = rate;
+}
+
+const handle_t
+bond_member::hdl(void) const
+{
+  return m_itf->handle();
+}
+
+bool
+bond_member::operator==(const bond_member& b) const
+{
+  return ((m_itf == b.m_itf) && (m_mode == b.m_mode) && (m_rate == b.m_rate));
+}
+
+const bond_member::mode_t bond_member::mode_t::ACTIVE(0, "active");
+const bond_member::mode_t bond_member::mode_t::PASSIVE(1, "passive");
+
+const bond_member::mode_t
+bond_member::mode_t::from_numeric_val(uint8_t numeric)
+{
+  if (0 == numeric)
+    return (bond_member::mode_t::ACTIVE);
+
+  return (bond_member::mode_t::PASSIVE);
+}
+
+bond_member::mode_t::mode_t(int v, const std::string& s)
+  : enum_base<bond_member::mode_t>(v, s)
+{
+}
+
+const bond_member::rate_t bond_member::rate_t::FAST(0, "fast");
+const bond_member::rate_t bond_member::rate_t::SLOW(1, "slow");
+
+const bond_member::rate_t
+bond_member::rate_t::from_numeric_val(uint8_t numeric)
+{
+  if (0 == numeric)
+    return (bond_member::rate_t::FAST);
+
+  return (bond_member::rate_t::SLOW);
+}
+
+bond_member::rate_t::rate_t(int v, const std::string& s)
+  : enum_base<bond_member::rate_t>(v, s)
+{
+}
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/bond_member.hpp b/src/vpp-api/vom/bond_member.hpp
new file mode 100644 (file)
index 0000000..066933b
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * 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_BOND_MEMBER_H__
+#define __VOM_BOND_MEMBER_H__
+
+#include "vom/interface.hpp"
+#include <vapi/bond.api.vapi.hpp>
+
+namespace VOM {
+/**
+ * A bond-member. e.g. a bond_member interface
+ */
+class bond_member
+{
+public:
+  /**
+   * A member interface mode
+   */
+  struct mode_t : enum_base<mode_t>
+  {
+    /**
+     * Active member interface mode
+     */
+    const static mode_t ACTIVE;
+    /**
+     * Passive member interface mode
+     */
+    const static mode_t PASSIVE;
+
+    /**
+     * Convert VPP's value of the bond to a mode
+     */
+    static const mode_t from_numeric_val(uint8_t v);
+
+  private:
+    /**
+     * Private constructor taking the value and the string name
+     */
+    mode_t(int v, const std::string& s);
+  };
+
+  /**
+   * A member interface rate
+   */
+  struct rate_t : enum_base<rate_t>
+  {
+    /**
+     * Fast member interface rate
+     */
+    const static rate_t FAST;
+    /**
+     * SLOW member interface rate
+     */
+    const static rate_t SLOW;
+
+    /**
+     * Convert VPP's value of the bond to a mode
+     */
+    static const rate_t from_numeric_val(uint8_t v);
+
+  private:
+    /**
+     * Private constructor taking the value and the string name
+     */
+    rate_t(int v, const std::string& s);
+  };
+
+  bond_member(const interface& itf, mode_t mode, rate_t rate);
+
+  ~bond_member();
+  bond_member(const bond_member& o);
+
+  /**
+   * convert to VPP
+   */
+  void to_vpp(vapi_payload_bond_enslave& bond_enslave) const;
+
+  /**
+   * set the mode
+   */
+  void set(mode_t mode);
+
+  /**
+   * set the rate
+   */
+  void set(rate_t rate);
+
+  /**
+   * convert to string
+   */
+  std::string to_string(void) const;
+
+  /**
+   * less-than operator
+   */
+  bool operator<(const bond_member& mem_itf) const;
+
+  /**
+   * Get the interface handle
+   */
+  const handle_t hdl(void) const;
+
+  /**
+   * equality operator
+   */
+  bool operator==(const bond_member& i) const;
+
+private:
+  /**
+   * Refernece conter lock on the parent
+   */
+  const std::shared_ptr<interface> m_itf;
+
+  /**
+   * passive vs active member
+   */
+  mode_t m_mode;
+
+  /**
+   * slow 90sec. vs. fast 3sec timeout
+   */
+  rate_t m_rate;
+};
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
index f943bf4..f323727 100644 (file)
@@ -14,6 +14,9 @@
  */
 
 #include "vom/interface.hpp"
+#include "vom/bond_group_binding.hpp"
+#include "vom/bond_group_binding_cmds.hpp"
+#include "vom/bond_interface_cmds.hpp"
 #include "vom/interface_cmds.hpp"
 #include "vom/interface_factory.hpp"
 #include "vom/l3_binding_cmds.hpp"
@@ -464,6 +467,9 @@ interface::dump(std::ostream& os)
 void
 interface::event_handler::handle_populate(const client_db::key_t& key)
 {
+  /*
+   * dump VPP current states
+   */
   std::shared_ptr<interface_cmds::vhost_dump_cmd> vcmd =
     std::make_shared<interface_cmds::vhost_dump_cmd>();
 
@@ -474,13 +480,10 @@ interface::event_handler::handle_populate(const client_db::key_t& key)
     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();
+    VOM_LOG(log_level_t::DEBUG) << " vhost-dump: " << vitf->to_string();
     OM::commit(key, *vitf);
   }
 
-  /*
-   * dump VPP current states
-   */
   std::shared_ptr<interface_cmds::dump_cmd> cmd =
     std::make_shared<interface_cmds::dump_cmd>();
 
@@ -522,6 +525,60 @@ interface::event_handler::handle_populate(const client_db::key_t& key)
       }
     }
   }
+
+  std::shared_ptr<bond_interface_cmds::dump_cmd> bcmd =
+    std::make_shared<bond_interface_cmds::dump_cmd>();
+
+  HW::enqueue(bcmd);
+  HW::write();
+
+  for (auto& bond_itf_record : *bcmd) {
+    std::shared_ptr<bond_interface> bond_itf =
+      interface_factory::new_bond_interface(bond_itf_record.get_payload());
+
+    VOM_LOG(log_level_t::DEBUG) << " bond-dump:" << bond_itf->to_string();
+
+    /*
+     * Write each of the discovered interfaces into the OM,
+     * but disable the HW Command q whilst we do, so that no
+     * commands are sent to VPP
+     */
+    OM::commit(key, *bond_itf);
+
+    std::shared_ptr<bond_group_binding_cmds::dump_cmd> scmd =
+      std::make_shared<bond_group_binding_cmds::dump_cmd>(
+        bond_group_binding_cmds::dump_cmd(bond_itf->handle()));
+
+    HW::enqueue(scmd);
+    HW::write();
+
+    bond_group_binding::enslaved_itf_t enslaved_itfs;
+
+    for (auto& slave_itf_record : *scmd) {
+      bond_member slave_itf = interface_factory::new_bond_member_interface(
+        slave_itf_record.get_payload());
+
+      VOM_LOG(log_level_t::DEBUG) << " slave-dump:" << slave_itf.to_string();
+
+      /*
+       * Write each of the discovered interfaces into the OM,
+       * but disable the HW Command q whilst we do, so that no
+       * commands are sent to VPP
+       */
+      //      OM::commit(slave_itf->key(), *slave_itf);
+      enslaved_itfs.insert(slave_itf);
+    }
+
+    if (!enslaved_itfs.empty()) {
+      bond_group_binding bid(*bond_itf, enslaved_itfs);
+      /*
+       * Write each of the discovered interfaces into the OM,
+       * but disable the HW Command q whilst we do, so that no
+       * commands are sent to VPP
+       */
+      OM::commit(key, bid);
+    }
+  }
 }
 
 interface::event_handler::event_handler()
index b0aeb73..0099bde 100644 (file)
@@ -95,6 +95,11 @@ public:
      */
     const static type_t VHOST;
 
+    /**
+     * bond interface type
+     */
+    const static type_t BOND;
+
     /**
      * Convert VPP's name of the interface to a type
      */
index ef26c32..417f477 100644 (file)
@@ -15,6 +15,8 @@
 
 #include <boost/algorithm/string.hpp>
 
+#include "vom/bond_interface.hpp"
+#include "vom/bond_member.hpp"
 #include "vom/interface_factory.hpp"
 #include "vom/sub_interface.hpp"
 #include "vom/tap_interface.hpp"
@@ -83,7 +85,7 @@ interface_factory::new_interface(const vapi_payload_sw_interface_details& vd)
      */
   } else if (interface::type_t::VHOST == type) {
     /*
-     * vhost interfaces already exist in db, look for it using
+     * vhost interface already exist in db, look for it using
      * sw_if_index
      */
     sp = interface::find(hdl);
@@ -93,6 +95,10 @@ interface_factory::new_interface(const vapi_payload_sw_interface_details& vd)
       if (!tag.empty())
         sp->set(tag);
     }
+  } else if (interface::type_t::BOND == type) {
+    sp = bond_interface(name, state, l2_address,
+                        bond_interface::mode_t::UNSPECIFIED)
+           .singular();
   } else {
     sp = interface(name, type, state, tag).singular();
     sp->set(l2_address);
@@ -121,6 +127,40 @@ interface_factory::new_vhost_user_interface(
   sp->set(hdl);
   return (sp);
 }
+
+std::shared_ptr<bond_interface>
+interface_factory::new_bond_interface(
+  const vapi_payload_sw_interface_bond_details& vd)
+{
+  std::shared_ptr<bond_interface> sp;
+  std::string name = reinterpret_cast<const char*>(vd.interface_name);
+  handle_t hdl(vd.sw_if_index);
+  bond_interface::mode_t mode =
+    bond_interface::mode_t::from_numeric_val(vd.mode);
+  bond_interface::lb_t lb = bond_interface::lb_t::from_numeric_val(vd.lb);
+  sp = bond_interface::find(hdl);
+  if (sp) {
+    sp->set(mode);
+    sp->set(lb);
+  }
+  return (sp);
+}
+
+bond_member
+interface_factory::new_bond_member_interface(
+  const vapi_payload_sw_interface_slave_details& vd)
+{
+  std::shared_ptr<bond_member> sp;
+  std::string name = reinterpret_cast<const char*>(vd.interface_name);
+  handle_t hdl(vd.sw_if_index);
+  bond_member::mode_t mode =
+    bond_member::mode_t::from_numeric_val(vd.is_passive);
+  bond_member::rate_t rate =
+    bond_member::rate_t::from_numeric_val(vd.is_long_timeout);
+  std::shared_ptr<interface> itf = interface::find(hdl);
+  bond_member bm(*itf, mode, rate);
+  return (bm);
+}
 }; // namespace VOM
 
 /*
index a2c8199..dda5275 100644 (file)
 
 #include <vapi/vapi.hpp>
 
+#include "vom/bond_member.hpp"
 #include "vom/interface.hpp"
 
+#include <vapi/bond.api.vapi.hpp>
 #include <vapi/interface.api.vapi.hpp>
 #include <vapi/vhost_user.api.vapi.hpp>
 
@@ -36,6 +38,12 @@ public:
 
   static std::shared_ptr<interface> new_vhost_user_interface(
     const vapi_payload_sw_interface_vhost_user_details& vd);
+
+  static std::shared_ptr<bond_interface> new_bond_interface(
+    const vapi_payload_sw_interface_bond_details& vd);
+
+  static bond_member new_bond_member_interface(
+    const vapi_payload_sw_interface_slave_details& vd);
 };
 };
 
index 6d3dcae..2e7eee3 100644 (file)
@@ -14,7 +14,6 @@
  */
 
 #include "vom/interface.hpp"
-
 namespace VOM {
 /*
  * constants and enums
@@ -28,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::type_t interface::type_t::BOND(9, "Bond");
 
 const interface::oper_state_t interface::oper_state_t::DOWN(0, "down");
 const interface::oper_state_t interface::oper_state_t::UP(1, "up");
@@ -41,6 +41,8 @@ interface::type_t::from_string(const std::string& str)
   if ((str.find("Virtual") != std::string::npos) ||
       (str.find("vhost") != std::string::npos)) {
     return interface::type_t::VHOST;
+  } else if (str.find("Bond") != std::string::npos) {
+    return interface::type_t::BOND;
   } else if (str.find("Ethernet") != std::string::npos) {
     return interface::type_t::ETHERNET;
   } else if (str.find("vxlan") != std::string::npos) {
index 8543a66..302e5ee 100644 (file)
@@ -52,6 +52,12 @@ enum class dependency_t
    */
   INTERFACE,
 
+  /**
+   * bond group binding is after interfaces but before
+   * anything else
+   */
+  BOND_BINDING,
+
   /**
    * Tunnel or virtual interfaces next
    */
index 435d8fd..29738e2 100644 (file)
@@ -20,6 +20,9 @@
 #include "vom/om.hpp"
 #include "vom/interface.hpp"
 #include "vom/interface_cmds.hpp"
+#include "vom/bond_interface_cmds.hpp"
+#include "vom/bond_group_binding.hpp"
+#include "vom/bond_group_binding_cmds.hpp"
 #include "vom/l2_binding.hpp"
 #include "vom/l2_binding_cmds.hpp"
 #include "vom/l3_binding.hpp"
@@ -178,6 +181,10 @@ public:
                     {
                         rc = handle_derived<interface_cmds::vhost_create_cmd>(f_exp, f_act);
                     }
+                    else if (typeid(*f_exp) == typeid(bond_interface_cmds::create_cmd))
+                    {
+                       rc = handle_derived<bond_interface_cmds::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);
@@ -190,6 +197,10 @@ public:
                     {
                        rc = handle_derived<interface_cmds::vhost_delete_cmd>(f_exp, f_act);
                     }
+                    else if (typeid(*f_exp) == typeid(bond_interface_cmds::delete_cmd))
+                    {
+                       rc = handle_derived<bond_interface_cmds::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);
@@ -206,6 +217,14 @@ public:
                     {
                         rc = handle_derived<interface_cmds::set_tag>(f_exp, f_act);
                     }
+                    else if (typeid(*f_exp) == typeid(bond_group_binding_cmds::bind_cmd))
+                    {
+                       rc = handle_derived<bond_group_binding_cmds::bind_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(bond_group_binding_cmds::unbind_cmd))
+                    {
+                       rc = handle_derived<bond_group_binding_cmds::unbind_cmd>(f_exp, f_act);
+                    }
                     else if (typeid(*f_exp) == typeid(route_domain_cmds::create_cmd))
                     {
                        rc = handle_derived<route_domain_cmds::create_cmd>(f_exp, f_act);
@@ -770,6 +789,80 @@ BOOST_AUTO_TEST_CASE(test_bvi) {
     TRY_CHECK(OM::remove(graham));
 }
 
+BOOST_AUTO_TEST_CASE(test_bond) {
+    VppInit vi;
+    const std::string cb = "CarolBerg";
+    rc_t rc = rc_t::OK;
+
+    /*
+     * creates the interfaces
+     */
+    std::string itf1_name = "afpacket1";
+    interface itf1(itf1_name,
+                   interface::type_t::AFPACKET,
+                   interface::admin_state_t::UP);
+
+    HW::item<handle_t> hw_ifh(2, rc_t::OK);
+    ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf1_name));
+
+    HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP, rc_t::OK);
+    ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
+
+    TRY_CHECK_RC(OM::write(cb, itf1));
+
+    std::string itf2_name = "afpacket2";
+    interface itf2(itf2_name,
+                   interface::type_t::AFPACKET,
+                   interface::admin_state_t::UP);
+
+
+    HW::item<handle_t> hw_ifh2(4, rc_t::OK);
+    ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh2, itf2_name));
+    ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh2));
+
+    TRY_CHECK_RC(OM::write(cb, itf2));
+
+    std::string bond_name = "bond";
+    bond_interface bond_itf(bond_name, interface::admin_state_t::UP,
+                                 bond_interface::mode_t::LACP);
+
+    HW::item<handle_t> hw_ifh3(6, rc_t::OK);
+    ADD_EXPECT(bond_interface_cmds::create_cmd(hw_ifh3, bond_name,
+      bond_interface::mode_t::LACP, bond_interface::lb_t::L2, l2_address_t::ZERO));
+    ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh3));
+
+    TRY_CHECK_RC(OM::write(cb, bond_itf));
+
+    bond_member *bm1 = new bond_member(itf1, bond_member::mode_t::ACTIVE,
+                                         bond_member::rate_t::SLOW);
+    bond_member *bm2 = new bond_member(itf2, bond_member::mode_t::ACTIVE,
+                                         bond_member::rate_t::SLOW);
+    bond_group_binding *bgb = new bond_group_binding(bond_itf, {*bm1, *bm2});
+
+    HW::item<bool> bond_hw_bind(true, rc_t::OK);
+    ADD_EXPECT(bond_group_binding_cmds::bind_cmd(bond_hw_bind, hw_ifh3.data(), *bm1));
+    ADD_EXPECT(bond_group_binding_cmds::bind_cmd(bond_hw_bind, hw_ifh3.data(), *bm2));
+
+    TRY_CHECK_RC(OM::write(cb, *bgb));
+
+    delete bgb;
+    delete bm2;
+    delete bm1;
+
+    STRICT_ORDER_OFF();
+    HW::item<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN, rc_t::OK);
+    ADD_EXPECT(bond_group_binding_cmds::unbind_cmd(bond_hw_bind, hw_ifh.data()));
+    ADD_EXPECT(bond_group_binding_cmds::unbind_cmd(bond_hw_bind, hw_ifh2.data()));
+    ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh2));
+    ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh2, itf2_name));
+    ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh3));
+    ADD_EXPECT(bond_interface_cmds::delete_cmd(hw_ifh3));
+    ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
+    ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf1_name));
+
+    TRY_CHECK(OM::remove(cb));
+}
+
 BOOST_AUTO_TEST_CASE(test_bridge) {
     VppInit vi;
     const std::string franz = "FranzKafka";