From ed76ee24dfe76fb9400470a4efb3871acd37cad9 Mon Sep 17 00:00:00 2001 From: Mohsin Kazmi Date: Fri, 2 Mar 2018 12:31:37 +0100 Subject: [PATCH] VOM: bond: Add support for LACP Change-Id: I0245263b212142858d3305b0f365d8342912dbb9 Signed-off-by: Mohsin Kazmi --- src/vpp-api/vom/Makefile.am | 8 ++ src/vpp-api/vom/bond_group_binding.cpp | 178 +++++++++++++++++++++++++ src/vpp-api/vom/bond_group_binding.hpp | 186 ++++++++++++++++++++++++++ src/vpp-api/vom/bond_group_binding_cmds.cpp | 147 +++++++++++++++++++++ src/vpp-api/vom/bond_group_binding_cmds.hpp | 138 +++++++++++++++++++ src/vpp-api/vom/bond_interface.cpp | 197 ++++++++++++++++++++++++++++ src/vpp-api/vom/bond_interface.hpp | 197 ++++++++++++++++++++++++++++ src/vpp-api/vom/bond_interface_cmds.cpp | 138 +++++++++++++++++++ src/vpp-api/vom/bond_interface_cmds.hpp | 111 ++++++++++++++++ src/vpp-api/vom/bond_member.cpp | 130 ++++++++++++++++++ src/vpp-api/vom/bond_member.hpp | 147 +++++++++++++++++++++ src/vpp-api/vom/interface.cpp | 65 ++++++++- src/vpp-api/vom/interface.hpp | 5 + src/vpp-api/vom/interface_factory.cpp | 42 +++++- src/vpp-api/vom/interface_factory.hpp | 8 ++ src/vpp-api/vom/interface_types.cpp | 4 +- src/vpp-api/vom/types.hpp | 6 + test/ext/vom_test.cpp | 93 +++++++++++++ 18 files changed, 1794 insertions(+), 6 deletions(-) create mode 100644 src/vpp-api/vom/bond_group_binding.cpp create mode 100644 src/vpp-api/vom/bond_group_binding.hpp create mode 100644 src/vpp-api/vom/bond_group_binding_cmds.cpp create mode 100644 src/vpp-api/vom/bond_group_binding_cmds.hpp create mode 100644 src/vpp-api/vom/bond_interface.cpp create mode 100644 src/vpp-api/vom/bond_interface.hpp create mode 100644 src/vpp-api/vom/bond_interface_cmds.cpp create mode 100644 src/vpp-api/vom/bond_interface_cmds.hpp create mode 100644 src/vpp-api/vom/bond_member.cpp create mode 100644 src/vpp-api/vom/bond_member.hpp diff --git a/src/vpp-api/vom/Makefile.am b/src/vpp-api/vom/Makefile.am index 45c2c1ca7e2..00ab467fe0f 100644 --- a/src/vpp-api/vom/Makefile.am +++ b/src/vpp-api/vom/Makefile.am @@ -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 index 00000000000..6958eb386ba --- /dev/null +++ b/src/vpp-api/vom/bond_group_binding.cpp @@ -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::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::find_or_add(const bond_group_binding& temp) +{ + return (m_db.find_or_add(temp.key(), temp)); +} + +std::shared_ptr +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 index 00000000000..bfac4881a44 --- /dev/null +++ b/src/vpp-api/vom/bond_group_binding.hpp @@ -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 + +#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 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 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 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; + + /** + * 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 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 m_binding; + + /** + * A map of all bond interface bindings keyed against the interface + + * "binding". + */ + static singular_db 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 index 00000000000..3ffe9810173 --- /dev/null +++ b/src/vpp-api/vom/bond_group_binding_cmds.cpp @@ -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& 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& 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 index 00000000000..71c4f9fad64 --- /dev/null +++ b/src/vpp-api/vom/bond_group_binding_cmds.hpp @@ -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 + +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, rc_t, vapi::Bond_enslave> +{ +public: + /** + * Constructor + */ + bind_cmd(HW::item& 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, rc_t, vapi::Bond_detach_slave> +{ +public: + /** + * Constructor + */ + unbind_cmd(HW::item& 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 +{ +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 index 00000000000..32a00ad7f43 --- /dev/null +++ b/src/vpp-api/vom/bond_interface.cpp @@ -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::find(const handle_t& hdl) +{ + return std::dynamic_pointer_cast(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& +bond_interface::mk_create_cmd(std::queue& q) +{ + q.push(new bond_interface_cmds::create_cmd(m_hdl, name(), m_mode, m_lb, + m_l2_address)); + + return (q); +} + +std::queue& +bond_interface::mk_delete_cmd(std::queue& q) +{ + q.push(new bond_interface_cmds::delete_cmd(m_hdl)); + + return (q); +} + +std::shared_ptr +bond_interface::singular() const +{ + return std::dynamic_pointer_cast(singular_i()); +} + +std::shared_ptr +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(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(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 index 00000000000..4584bd14be2 --- /dev/null +++ b/src/vpp-api/vom/bond_interface.hpp @@ -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 + { + /** + * 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 + { + /** + * 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 find(const handle_t& hdl); + + /** + * Return the matching 'singular instance' of the BOND interface + */ + std::shared_ptr 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 singular_i() const; + + /** + * Virtual functions to construct an interface create commands. + */ + virtual std::queue& mk_create_cmd(std::queue& cmds); + + /** + * Virtual functions to construct an interface delete commands. + */ + virtual std::queue& mk_delete_cmd(std::queue& 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 index 00000000000..d59560d4f5f --- /dev/null +++ b/src/vpp-api/vom/bond_interface_cmds.cpp @@ -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& 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(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& item) + : interface::delete_cmd(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 index 00000000000..06a8ac15009 --- /dev/null +++ b/src/vpp-api/vom/bond_interface_cmds.hpp @@ -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 +#include + +namespace VOM { +namespace bond_interface_cmds { + +/** + * A functor class that creates an interface + */ +class create_cmd : public interface::create_cmd +{ +public: + create_cmd(HW::item& 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 +{ +public: + delete_cmd(HW::item& 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 +{ +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 index 00000000000..f1a27b3d1c6 --- /dev/null +++ b/src/vpp-api/vom/bond_member.cpp @@ -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(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(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 index 00000000000..066933b74d7 --- /dev/null +++ b/src/vpp-api/vom/bond_member.hpp @@ -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 + +namespace VOM { +/** + * A bond-member. e.g. a bond_member interface + */ +class bond_member +{ +public: + /** + * A member interface mode + */ + struct mode_t : enum_base + { + /** + * 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 + { + /** + * 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 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 diff --git a/src/vpp-api/vom/interface.cpp b/src/vpp-api/vom/interface.cpp index f943bf4a616..f323727b052 100644 --- a/src/vpp-api/vom/interface.cpp +++ b/src/vpp-api/vom/interface.cpp @@ -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 vcmd = std::make_shared(); @@ -474,13 +480,10 @@ interface::event_handler::handle_populate(const client_db::key_t& key) std::shared_ptr 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 cmd = std::make_shared(); @@ -522,6 +525,60 @@ interface::event_handler::handle_populate(const client_db::key_t& key) } } } + + std::shared_ptr bcmd = + std::make_shared(); + + HW::enqueue(bcmd); + HW::write(); + + for (auto& bond_itf_record : *bcmd) { + std::shared_ptr 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 scmd = + std::make_shared( + 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() diff --git a/src/vpp-api/vom/interface.hpp b/src/vpp-api/vom/interface.hpp index b0aeb739fdb..0099bde42ba 100644 --- a/src/vpp-api/vom/interface.hpp +++ b/src/vpp-api/vom/interface.hpp @@ -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 */ diff --git a/src/vpp-api/vom/interface_factory.cpp b/src/vpp-api/vom/interface_factory.cpp index ef26c3293d3..417f4775a7a 100644 --- a/src/vpp-api/vom/interface_factory.cpp +++ b/src/vpp-api/vom/interface_factory.cpp @@ -15,6 +15,8 @@ #include +#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 +interface_factory::new_bond_interface( + const vapi_payload_sw_interface_bond_details& vd) +{ + std::shared_ptr sp; + std::string name = reinterpret_cast(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 sp; + std::string name = reinterpret_cast(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 itf = interface::find(hdl); + bond_member bm(*itf, mode, rate); + return (bm); +} }; // namespace VOM /* diff --git a/src/vpp-api/vom/interface_factory.hpp b/src/vpp-api/vom/interface_factory.hpp index a2c81993550..dda52752352 100644 --- a/src/vpp-api/vom/interface_factory.hpp +++ b/src/vpp-api/vom/interface_factory.hpp @@ -18,8 +18,10 @@ #include +#include "vom/bond_member.hpp" #include "vom/interface.hpp" +#include #include #include @@ -36,6 +38,12 @@ public: static std::shared_ptr new_vhost_user_interface( const vapi_payload_sw_interface_vhost_user_details& vd); + + static std::shared_ptr 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); }; }; diff --git a/src/vpp-api/vom/interface_types.cpp b/src/vpp-api/vom/interface_types.cpp index 6d3dcae57f2..2e7eee3ce7c 100644 --- a/src/vpp-api/vom/interface_types.cpp +++ b/src/vpp-api/vom/interface_types.cpp @@ -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) { diff --git a/src/vpp-api/vom/types.hpp b/src/vpp-api/vom/types.hpp index 8543a6604bb..302e5ee47b1 100644 --- a/src/vpp-api/vom/types.hpp +++ b/src/vpp-api/vom/types.hpp @@ -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 */ diff --git a/test/ext/vom_test.cpp b/test/ext/vom_test.cpp index 435d8fdf9c4..29738e2437e 100644 --- a/test/ext/vom_test.cpp +++ b/test/ext/vom_test.cpp @@ -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(f_exp, f_act); } + else if (typeid(*f_exp) == typeid(bond_interface_cmds::create_cmd)) + { + rc = handle_derived(f_exp, f_act); + } else if (typeid(*f_exp) == typeid(interface_cmds::loopback_delete_cmd)) { rc = handle_derived(f_exp, f_act); @@ -190,6 +197,10 @@ public: { rc = handle_derived(f_exp, f_act); } + else if (typeid(*f_exp) == typeid(bond_interface_cmds::delete_cmd)) + { + rc = handle_derived(f_exp, f_act); + } else if (typeid(*f_exp) == typeid(interface_cmds::state_change_cmd)) { rc = handle_derived(f_exp, f_act); @@ -206,6 +217,14 @@ public: { rc = handle_derived(f_exp, f_act); } + else if (typeid(*f_exp) == typeid(bond_group_binding_cmds::bind_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(bond_group_binding_cmds::unbind_cmd)) + { + rc = handle_derived(f_exp, f_act); + } else if (typeid(*f_exp) == typeid(route_domain_cmds::create_cmd)) { rc = handle_derived(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 hw_ifh(2, rc_t::OK); + ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf1_name)); + + HW::item 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 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 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 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 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"; -- 2.16.6