diff options
-rw-r--r-- | extras/vom/CMakeLists.txt | 1 | ||||
-rw-r--r-- | extras/vom/vom/CMakeLists.txt | 28 | ||||
-rw-r--r-- | extras/vom/vom/hw.cpp | 27 | ||||
-rw-r--r-- | extras/vom/vom/hw.hpp | 17 | ||||
-rw-r--r-- | extras/vom/vom/interface.cpp | 111 | ||||
-rw-r--r-- | extras/vom/vom/interface.hpp | 78 | ||||
-rw-r--r-- | extras/vom/vom/interface_cmds.cpp | 132 | ||||
-rw-r--r-- | extras/vom/vom/interface_cmds.hpp | 96 | ||||
-rw-r--r-- | extras/vom/vom/stat_client.cpp | 233 | ||||
-rw-r--r-- | extras/vom/vom/stat_client.hpp | 221 | ||||
-rw-r--r-- | extras/vom/vom/stat_reader.cpp | 117 | ||||
-rw-r--r-- | extras/vom/vom/stat_reader.hpp | 103 | ||||
-rw-r--r-- | extras/vom/vom/test_stats.cpp | 92 | ||||
-rw-r--r-- | test/ext/vom_test.cpp | 2 |
14 files changed, 991 insertions, 267 deletions
diff --git a/extras/vom/CMakeLists.txt b/extras/vom/CMakeLists.txt index 6ed14cd75a2..a04df5d52ab 100644 --- a/extras/vom/CMakeLists.txt +++ b/extras/vom/CMakeLists.txt @@ -24,6 +24,7 @@ set(CMAKE_INSTALL_MESSAGE NEVER) find_package(VPP) find_package(Threads REQUIRED) +find_package(Boost OPTIONAL_COMPONENTS system filesystem) add_subdirectory(vom) diff --git a/extras/vom/vom/CMakeLists.txt b/extras/vom/vom/CMakeLists.txt index 47e73b6e7a0..bd8986a4864 100644 --- a/extras/vom/vom/CMakeLists.txt +++ b/extras/vom/vom/CMakeLists.txt @@ -11,6 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +unset (VPPAPICLIENT_LIB) unset (VAPICLIENT_LIB) unset (ACL_FILE) unset (NAT_FILE) @@ -20,13 +21,20 @@ unset (IGMP_FILE) unset (VOM_SOURCES) unset (VOM_HEADERS) +find_library(VPPAPICLIENT_LIB NAMES vppapiclient REQUIRED) +find_path(VPPAPICLIENT_INCLUDE_DIR NAMES vpp-api/client/vppapiclient.h) find_library(VAPICLIENT_LIB NAMES vapiclient REQUIRED) find_path(VAPICLIENT_INCLUDE_DIR NAMES vapi/vapi.hpp) + +if(NOT VPPAPICLIENT_INCLUDE_DIR OR NOT VPPAPICLIENT_LIB) + message(FATAL_ERROR "Cannot find vppapiclient library and/or headers") +endif() if(NOT VAPICLIENT_INCLUDE_DIR OR NOT VAPICLIENT_LIB) message(FATAL_ERROR "Cannot find vapiclient library and/or headers") endif() +include_directories(${VPPAPICLIENT_INCLUDE_DIR}) include_directories(${VAPICLIENT_INCLUDE_DIR}) include_directories(${CMAKE_SOURCE_DIR}) @@ -160,6 +168,8 @@ list(APPEND VOM_SOURCES route_cmds.cpp route_domain.cpp route_domain_cmds.cpp + stat_client.cpp + stat_reader.cpp sub_interface_cmds.cpp sub_interface.cpp tap_interface.cpp @@ -257,6 +267,8 @@ list(APPEND VOM_HEADERS rpc_cmd.hpp singular_db.hpp singular_db_funcs.hpp + stat_client.hpp + stat_reader.hpp sub_interface.hpp tap_interface.hpp types.hpp @@ -271,8 +283,20 @@ add_vpp_library(vom INSTALL_HEADERS ${VOM_HEADERS} - LINK_LIBRARIES ${VAPICLIENT_LIB} Threads::Threads boost_thread - ${BOOST_SYSTEM_LIB} ${BOOST_FILESYSTEM_LIB} ${BOOST_ASIO_LIB} m rt + LINK_LIBRARIES ${VPPAPICLIENT_LIB} ${VAPICLIENT_LIB} Threads::Threads + ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} m rt COMPONENT libvom ) + +if (Boost_FOUND) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + add_definitions(-stdlib=libstdc++) + endif() + add_executable(vom_stats_test test_stats.cpp) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + target_link_libraries(vom_stats_test vom stdc++) + else() + target_link_libraries(vom_stats_test vom) + endif() +endif() diff --git a/extras/vom/vom/hw.cpp b/extras/vom/vom/hw.cpp index 0952b60f9d9..54f0aa2c02f 100644 --- a/extras/vom/vom/hw.cpp +++ b/extras/vom/vom/hw.cpp @@ -16,6 +16,7 @@ #include "vom/hw.hpp" #include "vom/hw_cmds.hpp" #include "vom/logger.hpp" +#include "vom/stat_reader.hpp" namespace VOM { HW::cmd_q::cmd_q() @@ -170,6 +171,11 @@ HW::cmd_q::write() * The single Command Queue */ HW::cmd_q* HW::m_cmdQ; + +/* + * single stat reader + */ +stat_reader* HW::m_statReader; HW::item<bool> HW::m_poll_state; /** @@ -179,6 +185,17 @@ void HW::init(HW::cmd_q* f) { m_cmdQ = f; + m_statReader = new stat_reader(); +} + +/** + * Initialse the connection to VPP + */ +void +HW::init(HW::cmd_q* f, stat_reader* s) +{ + m_cmdQ = f; + m_statReader = s; } /** @@ -188,6 +205,7 @@ void HW::init() { m_cmdQ = new cmd_q(); + m_statReader = new stat_reader(); } void @@ -211,12 +229,13 @@ HW::enqueue(std::queue<cmd*>& cmds) bool HW::connect() { - return m_cmdQ->connect(); + return (m_cmdQ->connect() && m_statReader->connect()); } void HW::disconnect() { + m_statReader->disconnect(); m_cmdQ->disconnect(); } @@ -249,6 +268,12 @@ HW::poll() return (m_poll_state); } +void +HW::read_stats() +{ + m_statReader->read(); +} + template <> std::string HW::item<bool>::to_string() const diff --git a/extras/vom/vom/hw.hpp b/extras/vom/vom/hw.hpp index 9ba47505619..d10a93aa59b 100644 --- a/extras/vom/vom/hw.hpp +++ b/extras/vom/vom/hw.hpp @@ -29,6 +29,7 @@ namespace VOM { +class stat_reader; class cmd; class HW { @@ -287,6 +288,12 @@ public: static void init(cmd_q* f); /** + * Initialise the HW connection to VPP - the UT version passing + * a mock Q. + */ + static void init(cmd_q* f, stat_reader* s); + + /** * Initialise the HW */ static void init(); @@ -326,6 +333,11 @@ public: */ static bool poll(); + /** + * read stats from stat segment + */ + static void read_stats(); + private: /** * The command Q toward HW @@ -333,6 +345,11 @@ private: static cmd_q* m_cmdQ; /** + * The stat reader toward HW + */ + static stat_reader* m_statReader; + + /** * HW::item representing the connection state as determined by polling */ static HW::item<bool> m_poll_state; diff --git a/extras/vom/vom/interface.cpp b/extras/vom/vom/interface.cpp index c1894c2fbdf..bbbc187747c 100644 --- a/extras/vom/vom/interface.cpp +++ b/extras/vom/vom/interface.cpp @@ -23,6 +23,7 @@ #include "vom/logger.hpp" #include "vom/prefix.hpp" #include "vom/singular_db_funcs.hpp" +#include "vom/stat_reader.hpp" #include "vom/tap_interface_cmds.hpp" namespace VOM { @@ -175,13 +176,8 @@ interface::sweep() new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl)); } - if (m_stats) { - if (stats_type_t::DETAILED == m_stats_type) { - HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd( - m_stats_type, handle_i(), false)); - } - HW::enqueue(new interface_cmds::stats_disable_cmd(m_hdl.data())); - m_stats.reset(); + if (m_listener) { + disable_stats(); } // If the interface is up, bring it down @@ -209,16 +205,8 @@ interface::replay() HW::enqueue(new interface_cmds::state_change_cmd(m_state, m_hdl)); } - if (m_stats) { - if (stats_type_t::DETAILED == m_stats_type) { - m_stats_type.set(rc_t::NOOP); - HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd( - m_stats_type, handle_i(), true)); - } - stat_listener& listener = m_stats->listener(); - listener.status().set(rc_t::NOOP); - m_stats.reset(new interface_cmds::stats_enable_cmd(listener, handle_i())); - HW::enqueue(m_stats); + if (m_listener) { + enable_stats(m_listener, m_stats_type.data()); } if (m_table_id && (m_table_id.data() != route::DEFAULT_TABLE)) { @@ -424,26 +412,101 @@ interface::set(const std::string& tag) } void -interface::enable_stats_i(interface::stat_listener& el, const stats_type_t& st) +interface::set(counter_t count, const std::string& stat_type) +{ + if ("rx" == stat_type) + m_stats.m_rx = count; + else if ("tx" == stat_type) + m_stats.m_tx = count; + else if ("rx-unicast" == stat_type) + m_stats.m_rx_unicast = count; + else if ("tx-unicast" == stat_type) + m_stats.m_tx_unicast = count; + else if ("rx-multicast" == stat_type) + m_stats.m_rx_multicast = count; + else if ("tx-multicast" == stat_type) + m_stats.m_tx_multicast = count; + else if ("rx-broadcast" == stat_type) + m_stats.m_rx_broadcast = count; + else if ("tx-broadcast" == stat_type) + m_stats.m_rx_broadcast = count; +} + +const interface::stats_t& +interface::get_stats(void) const +{ + return m_stats; +} + +void +interface::publish_stats() { - if (!m_stats) { + m_listener->handle_interface_stat(*this); +} + +std::ostream& +operator<<(std::ostream& os, const interface::stats_t& stats) +{ + os << "[" + << "rx [packets " << stats.m_rx.packets << ", bytes " << stats.m_rx.bytes + << "]" + << " rx-unicast [packets " << stats.m_rx_unicast.packets << ", bytes " + << stats.m_rx_unicast.bytes << "]" + << " rx-multicast [packets " << stats.m_rx_multicast.packets << ", bytes " + << stats.m_rx_multicast.bytes << "]" + << " rx-broadcast [packets " << stats.m_rx_broadcast.packets << ", bytes " + << stats.m_rx_broadcast.bytes << "]" + << " tx [packets " << stats.m_tx.packets << ", bytes " << stats.m_tx.bytes + << "]" + << " tx-unicast [packets " << stats.m_tx_unicast.packets << ", bytes " + << stats.m_tx_unicast.bytes << "]" + << " tx-multicast [packets " << stats.m_tx_multicast.packets << ", bytes " + << stats.m_tx_multicast.bytes << "]" + << " tx-broadcast [packets " << stats.m_tx_broadcast.packets << ", bytes " + << stats.m_tx_broadcast.bytes << "]]" << std::endl; + + return (os); +} + +void +interface::enable_stats_i(interface::stat_listener* el, const stats_type_t& st) +{ + if (el != NULL) { if (stats_type_t::DETAILED == st) { - m_stats_type = st; + m_stats_type.set(rc_t::NOOP); HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd( m_stats_type, handle_i(), true)); } - m_stats.reset(new interface_cmds::stats_enable_cmd(el, handle_i())); - HW::enqueue(m_stats); - HW::write(); + stat_reader::registers(*this); + m_listener = el; } } void -interface::enable_stats(interface::stat_listener& el, const stats_type_t& st) +interface::enable_stats(interface::stat_listener* el, const stats_type_t& st) { singular()->enable_stats_i(el, st); } +void +interface::disable_stats_i() +{ + if (m_listener != NULL) { + if (stats_type_t::DETAILED == m_stats_type) { + HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd( + m_stats_type, handle_i(), false)); + } + stat_reader::unregisters(*this); + m_listener = NULL; + } +} + +void +interface::disable_stats() +{ + singular()->disable_stats_i(); +} + std::shared_ptr<interface> interface::singular_i() const { diff --git a/extras/vom/vom/interface.hpp b/extras/vom/vom/interface.hpp index 42dfa67e03d..100c3caf4bc 100644 --- a/extras/vom/vom/interface.hpp +++ b/extras/vom/vom/interface.hpp @@ -25,15 +25,16 @@ #include "vom/route_domain.hpp" #include "vom/rpc_cmd.hpp" #include "vom/singular_db.hpp" +#include "vom/stat_client.hpp" namespace VOM { /** * Forward declaration of the stats and events command */ namespace interface_cmds { -class stats_enable_cmd; class events_cmd; }; +class stat_reader; /** * A representation of an interface in VPP @@ -185,6 +186,21 @@ public: }; /** + * stats_t: + */ + struct stats_t + { + counter_t m_rx; + counter_t m_tx; + counter_t m_rx_unicast; + counter_t m_tx_unicast; + counter_t m_rx_multicast; + counter_t m_tx_multicast; + counter_t m_rx_broadcast; + counter_t m_tx_broadcast; + }; + + /** * Construct a new object matching the desried state */ interface(const std::string& name, @@ -269,6 +285,11 @@ public: void set(const std::string& tag); /** + * Get the interface stats + */ + const stats_t& get_stats(void) const; + + /** * Comparison operator - only used for UT */ virtual bool operator==(const interface& i) const; @@ -441,12 +462,13 @@ public: */ stat_listener(); + virtual ~stat_listener() = default; + /** * Virtual function called on the listener when the command has data * ready to process */ - virtual void handle_interface_stat( - interface_cmds::stats_enable_cmd* cmd) = 0; + virtual void handle_interface_stat(const interface&) = 0; /** * Return the HW::item representing the status @@ -478,10 +500,15 @@ public: /** * Enable stats for this interface */ - void enable_stats(stat_listener& el, + void enable_stats(stat_listener* el, const stats_type_t& st = stats_type_t::NORMAL); /** + * Disable stats for this interface + */ + void disable_stats(); + + /** * Enable the reception of events of all interfaces */ static void enable_events(interface::event_listener& el); @@ -499,7 +526,6 @@ protected: void set(const handle_t& handle); friend class interface_factory; friend class pipe; - /** * The SW interface handle VPP has asigned to the interface */ @@ -581,9 +607,29 @@ private: static event_handler m_evh; /** + * friend with stat_reader + */ + friend stat_reader; + + /** + * publish stats + */ + void publish_stats(); + + /** + * Set the interface stat + */ + void set(counter_t count, const std::string& stat_type); + + /** * enable the interface stats in the singular instance */ - void enable_stats_i(stat_listener& el, const stats_type_t& st); + void enable_stats_i(stat_listener* el, const stats_type_t& st); + + /** + * disable the interface stats in the singular instance + */ + void disable_stats_i(); /** * Commit the acculmulated changes into VPP. i.e. to a 'HW" write. @@ -623,11 +669,6 @@ private: std::shared_ptr<route_domain> m_rd; /** - * shared pointer to the stats object for this interface. - */ - std::shared_ptr<interface_cmds::stats_enable_cmd> m_stats; - - /** * The state of the interface */ HW::item<admin_state_t> m_state; @@ -648,6 +689,16 @@ private: HW::item<stats_type_t> m_stats_type; /** + * Interface stats + */ + stats_t m_stats; + + /** + * reference to stat listener + */ + stat_listener* m_listener; + + /** * Operational state of the interface */ oper_state_t m_oper; @@ -683,6 +734,11 @@ private: static std::shared_ptr<interface_cmds::events_cmd> m_events_cmd; }; + +/** + * stream insertion operator for interface stats + */ +std::ostream& operator<<(std::ostream& os, const interface::stats_t& stats); }; /* * fd.io coding-style-patch-verification: ON diff --git a/extras/vom/vom/interface_cmds.cpp b/extras/vom/vom/interface_cmds.cpp index c4fd6613a0e..3a7fb50f64b 100644 --- a/extras/vom/vom/interface_cmds.cpp +++ b/extras/vom/vom/interface_cmds.cpp @@ -20,7 +20,6 @@ DEFINE_VAPI_MSG_IDS_VPE_API_JSON; DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON; DEFINE_VAPI_MSG_IDS_AF_PACKET_API_JSON; DEFINE_VAPI_MSG_IDS_VHOST_USER_API_JSON; -DEFINE_VAPI_MSG_IDS_STATS_API_JSON; namespace VOM { namespace interface_cmds { @@ -473,137 +472,6 @@ events_cmd::to_string() const return ("itf-events"); } -/** - * Interface statistics - */ -stats_enable_cmd::stats_enable_cmd(interface::stat_listener& el, - const handle_t& handle) - : event_cmd(el.status()) - , m_listener(el) - , m_swifindex(handle) -{ -} - -bool -stats_enable_cmd::operator==(const stats_enable_cmd& other) const -{ - return (true); -} - -rc_t -stats_enable_cmd::issue(connection& con) -{ - /* - * First set the call back to handle the interface stats - */ - m_reg.reset(new reg_t(con.ctx(), std::ref(*(static_cast<event_cmd*>(this))))); - - /* - * then send the request to enable them - */ - msg_t req(con.ctx(), 1, std::ref(*(static_cast<rpc_cmd*>(this)))); - - auto& payload = req.get_request().get_payload(); - payload.enable_disable = 1; - payload.pid = getpid(); - payload.num = 1; - - payload.sw_ifs[0] = m_swifindex.value(); - - VAPI_CALL(req.execute()); - - wait(); - - return (rc_t::OK); -} - -void -stats_enable_cmd::retire(connection& con) -{ - /* - * disable interface stats. - */ - msg_t req(con.ctx(), 1, std::ref(*(static_cast<rpc_cmd*>(this)))); - - auto& payload = req.get_request().get_payload(); - payload.enable_disable = 0; - payload.pid = getpid(); - payload.num = 1; - payload.sw_ifs[0] = m_swifindex.value(); - - VAPI_CALL(req.execute()); - - wait(); -} - -interface::stat_listener& -stats_enable_cmd::listener() const -{ - return m_listener; -} - -void -stats_enable_cmd::set(const rc_t& rc) -{ - m_listener.status().set(rc); -} - -void -stats_enable_cmd::notify() -{ - m_listener.handle_interface_stat(this); -} - -std::string -stats_enable_cmd::to_string() const -{ - std::ostringstream s; - s << "itf-stats-enable itf:" << m_swifindex.to_string(); - return (s.str()); -} - -stats_disable_cmd::stats_disable_cmd(const handle_t& handle) - : rpc_cmd(m_res) - , m_swifindex(handle) -{ -} - -bool -stats_disable_cmd::operator==(const stats_disable_cmd& other) const -{ - return (true); -} - -rc_t -stats_disable_cmd::issue(connection& con) -{ - /* - * then send the request to enable them - */ - msg_t req(con.ctx(), 1, std::ref(*this)); - - auto& payload = req.get_request().get_payload(); - payload.enable_disable = 0; - payload.pid = getpid(); - payload.num = 1; - - payload.sw_ifs[0] = m_swifindex.value(); - - VAPI_CALL(req.execute()); - - wait(); - - return (rc_t::OK); -} - -std::string -stats_disable_cmd::to_string() const -{ - std::ostringstream s; - s << "itf-stats-disable itf:" << m_swifindex.to_string(); - return (s.str()); -} - dump_cmd::dump_cmd() { } diff --git a/extras/vom/vom/interface_cmds.hpp b/extras/vom/vom/interface_cmds.hpp index b646e4ec9b8..13a47e6a6d7 100644 --- a/extras/vom/vom/interface_cmds.hpp +++ b/extras/vom/vom/interface_cmds.hpp @@ -25,7 +25,6 @@ #include <vapi/af_packet.api.vapi.hpp> #include <vapi/interface.api.vapi.hpp> -#include <vapi/stats.api.vapi.hpp> #include <vapi/tap.api.vapi.hpp> #include <vapi/vhost_user.api.vapi.hpp> #include <vapi/vpe.api.vapi.hpp> @@ -407,101 +406,6 @@ private: }; /** - * A command class represents our desire to recieve interface stats - */ -class stats_enable_cmd - : public event_cmd<vapi::Want_per_interface_combined_stats, - vapi::Vnet_per_interface_combined_counters> -{ -public: - /** - * Constructor taking the listner to notify - */ - stats_enable_cmd(interface::stat_listener& el, const handle_t& handle); - - /** - * Issue the command to VPP/HW - */ - rc_t issue(connection& con); - - /** - * Retires the command - unsubscribe from the stats. - */ - void retire(connection& con); - - /** - * convert to string format for debug purposes - */ - std::string to_string() const; - - /** - * (re)set status - */ - void set(const rc_t& rc); - - /** - * get listener - */ - interface::stat_listener& listener() const; - - /** - * Comparison operator - only used for UT - */ - bool operator==(const stats_enable_cmd& i) const; - - /** - * Called when it's time to poke the listeners - */ - void notify(); - -private: - /** - * The listeners to notify when data/stats arrive - */ - interface::stat_listener& m_listener; - - /** - * The interface on which we are enabling states - */ - const handle_t& m_swifindex; -}; - -/** - * A command class represents our desire to recieve interface stats - */ -class stats_disable_cmd - : public rpc_cmd<HW::item<bool>, vapi::Want_per_interface_combined_stats> -{ -public: - /** - * Constructor taking the listner to notify - */ - stats_disable_cmd(const handle_t& handle); - - /** - * 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 stats_disable_cmd& i) const; - -private: - HW::item<bool> m_res; - /** - * The interface on which we are disabling states - */ - handle_t m_swifindex; -}; - -/** * A cmd class that Dumps all the Vpp interfaces */ class dump_cmd : public VOM::dump_cmd<vapi::Sw_interface_dump> diff --git a/extras/vom/vom/stat_client.cpp b/extras/vom/vom/stat_client.cpp new file mode 100644 index 00000000000..b348c6c24eb --- /dev/null +++ b/extras/vom/vom/stat_client.cpp @@ -0,0 +1,233 @@ +/* + * 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/stat_client.hpp" + +namespace VOM { + +stat_client::stat_data_t::stat_data_t() + : m_name("") + , m_type(STAT_DIR_TYPE_ILLEGAL) +{ +} + +stat_client::stat_data_t::stat_data_t(stat_segment_data_t* stat_seg_data) + : m_name(stat_seg_data->name) + , m_type(stat_seg_data->type) +{ + switch (m_type) { + case STAT_DIR_TYPE_SCALAR_INDEX: + m_scalar_value = stat_seg_data->scalar_value; + break; + case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE: + m_simple_counter_vec = stat_seg_data->simple_counter_vec; + break; + case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: + m_combined_counter_vec = + reinterpret_cast<counter_t**>(stat_seg_data->combined_counter_vec); + break; + case STAT_DIR_TYPE_ERROR_INDEX: + m_error_Value = stat_seg_data->error_value; + break; + case STAT_DIR_TYPE_ILLEGAL: + break; + } +} + +const std::string& +stat_client::stat_data_t::name() const +{ + return m_name; +} + +const stat_directory_type_t& +stat_client::stat_data_t::type() const +{ + return m_type; +} + +double +stat_client::stat_data_t::get_stat_segment_scalar_data() +{ + return m_scalar_value; +} +uint64_t +stat_client::stat_data_t::get_stat_segment_error_data() +{ + return m_error_Value; +} +uint64_t** +stat_client::stat_data_t::get_stat_segment_simple_counter_data() +{ + return m_simple_counter_vec; +} +counter_t** +stat_client::stat_data_t::get_stat_segment_combined_counter_data() +{ + return m_combined_counter_vec; +} + +stat_client::stat_client(std::string& socket_name) + : m_socket_name(socket_name) + , m_patterns() + , m_stat_connect(0) +{ + m_patterns.push_back("/if"); +} + +stat_client::stat_client(std::vector<std::string>& pattern) + : m_socket_name("/run/vpp/stats.sock") + , m_patterns(pattern) + , m_stat_connect(0) +{ +} + +stat_client::stat_client(std::string socket_name, + std::vector<std::string> patterns) + : m_socket_name(socket_name) + , m_patterns(patterns) + , m_stat_connect(0) +{ +} + +stat_client::stat_client() + : m_socket_name("/run/vpp/stats.sock") + , m_patterns() + , m_stat_connect(0) +{ + m_patterns.push_back("/if"); +} + +stat_client::~stat_client() +{ + stat_segment_vec_free(m_counter_vec); + data_free(); + if (m_stat_connect) + stat_segment_disconnect(); +} + +stat_client::stat_client(const stat_client& o) + : m_socket_name(o.m_socket_name) + , m_patterns(o.m_patterns) +{ +} + +int +stat_client::connect() +{ + if (stat_segment_connect(m_socket_name.c_str()) == 0) + m_stat_connect = 1; + return m_stat_connect; +} + +void +stat_client::disconnect() +{ + if (m_stat_connect) + stat_segment_disconnect(); +} + +int +stat_client::vec_len(void* vec) +{ + return stat_segment_vec_len(vec); +} + +void +stat_client::vec_free(void* vec) +{ + stat_segment_vec_free(vec); +} + +void +stat_client::ls() +{ + uint8_t** string_vec = { 0 }; + for (auto& pattern : m_patterns) { + string_vec = stat_segment_string_vector(string_vec, pattern.c_str()); + } + m_counter_vec = stat_segment_ls(string_vec); + stat_segment_vec_free(string_vec); +} + +const stat_client::stat_data_vec_t& +stat_client::dump() +{ + stat_segment_data_t* ssd; + stat_segment_data_free(m_stat_seg_data); + if (m_stat_data.size()) { + m_stat_data.clear(); + } + ssd = stat_segment_dump(m_counter_vec); + if (!ssd) { + ls(); + return m_stat_data; + } + m_stat_seg_data = ssd; + for (int i = 0; i < stat_segment_vec_len(ssd); i++) { + stat_data_t sd(&ssd[i]); + m_stat_data.push_back(sd); + } + return m_stat_data; +} + +const stat_client::stat_data_vec_t& +stat_client::dump_entry(uint32_t index) +{ + stat_segment_data_t* ssd; + stat_segment_data_free(m_stat_seg_data); + if (m_stat_data.size()) { + m_stat_data.clear(); + } + ssd = stat_segment_dump_entry(index); + if (!ssd) { + ls(); + return m_stat_data; + } + m_stat_seg_data = ssd; + for (int i = 0; i < stat_segment_vec_len(ssd); i++) { + stat_data_t sd(&ssd[i]); + m_stat_data.push_back(sd); + } + return m_stat_data; +} + +void +stat_client::data_free() +{ + stat_segment_data_free(m_stat_seg_data); +} + +double +stat_client::heartbeat() +{ + return stat_segment_heartbeat(); +} + +std::string +stat_client::index_to_name(uint32_t index) +{ + return stat_segment_index_to_name(index); +} + +} // namespace VOM + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/extras/vom/vom/stat_client.hpp b/extras/vom/vom/stat_client.hpp new file mode 100644 index 00000000000..4da968a8204 --- /dev/null +++ b/extras/vom/vom/stat_client.hpp @@ -0,0 +1,221 @@ +/* + * 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_STAT_CLIENT_H__ +#define __VOM_STAT_CLIENT_H__ + +#include <iostream> +#include <string> +#include <vector> + +extern "C" { +#include <vpp-api/client/stat_client.h> +} + +namespace VOM { + +typedef struct +{ + uint64_t packets; + uint64_t bytes; +} counter_t; + +typedef uint64_t stat_counter_t; +/** + * A representation of a stat client in VPP + */ +class stat_client +{ +public: + /** + * stat data representation + */ + struct stat_data_t + { + /** + * stat data constructor + */ + stat_data_t(); + + /** + * stat data custom constructor + */ + stat_data_t(stat_segment_data_t* stat_seg_data); + + /** + * get name of stat + */ + const std::string& name() const; + + /** + * get type of stat + */ + const stat_directory_type_t& type() const; + + /** + * Get pointer to actual data + */ + double get_stat_segment_scalar_data(); + uint64_t get_stat_segment_error_data(); + uint64_t** get_stat_segment_simple_counter_data(); + counter_t** get_stat_segment_combined_counter_data(); + + private: + /** + * name of stat data + */ + const std::string m_name; + + /** + * type of stat data + */ + const stat_directory_type_t m_type; + + /** + * union of pointers to actual stat data + */ + union + { + double m_scalar_value; + uint64_t m_error_Value; + uint64_t** m_simple_counter_vec; + counter_t** m_combined_counter_vec; + }; + }; + + /** + * vector of stat_data_t + */ + typedef std::vector<stat_data_t> stat_data_vec_t; + + /** + * Stat Client constructor with custom socket name + */ + stat_client(std::string& socket_name); + + /** + * Stat Client constructor with custom vector of patterns + */ + stat_client(std::vector<std::string>& pattern); + + /** + * Stat Client constructor with custom socket name and vector of patterns + */ + stat_client(std::string socket_name, std::vector<std::string> patterns); + + /** + * Stat Client constructor + */ + stat_client(); + + /** + * Stat Client destructor + */ + ~stat_client(); + + /** + * Stat Client copy constructor + */ + stat_client(const stat_client& o); + + /** + * Connect to stat segment + */ + int connect(); + + /** + * Disconnect to stat segment + */ + void disconnect(); + + /** + * Get vector length of VPP style vector + */ + int vec_len(void* vec); + + /** + * Free VPP style vector + */ + void vec_free(void* vec); + + /** + * ls on the stat directory using given pattern + */ + void ls(); + + /** + * dump all the stats for given pattern + */ + const stat_data_vec_t& dump(); + + /** + * dump stats for given index in stat directory + */ + const stat_data_vec_t& dump_entry(uint32_t index); + + /** + * Free stat segment data + */ + void data_free(); + + double heartbeat(); + + /** + * get index to name of stat + */ + std::string index_to_name(uint32_t index); + +private: + /** + * socket name + */ + std::string m_socket_name; + + /** + * vector of patterns for stats + */ + std::vector<std::string> m_patterns; + + /** + * connection bit + */ + int m_stat_connect; + + /** + * Pointer to VPP style vector of stat indexes + */ + uint32_t* m_counter_vec; + + /** + * Pointer to stat segment + */ + stat_segment_data_t* m_stat_seg_data; + + /** + * Vector of stat data + */ + stat_data_vec_t m_stat_data; +}; +}; // namespace VOM + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/extras/vom/vom/stat_reader.cpp b/extras/vom/vom/stat_reader.cpp new file mode 100644 index 00000000000..4b054aba5af --- /dev/null +++ b/extras/vom/vom/stat_reader.cpp @@ -0,0 +1,117 @@ +/* + * 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/stat_reader.hpp" +#include "vom/interface.hpp" + +namespace VOM { + +stat_reader::stat_indexes_t stat_reader::m_stat_itf_indexes; + +stat_reader::stat_reader() + : m_client() +{ +} + +stat_reader::stat_reader(stat_client sc) + : m_client(sc) +{ +} + +stat_reader::~stat_reader() +{ +} + +int +stat_reader::connect() +{ + return m_client.connect(); +} + +void +stat_reader::disconnect() +{ + m_client.disconnect(); +} + +void +stat_reader::registers(const interface& intf) +{ + m_stat_itf_indexes.insert(intf.handle().value()); +} + +void +stat_reader::unregisters(const interface& intf) +{ + m_stat_itf_indexes.erase(intf.handle().value()); +} + +void +stat_reader::read() +{ + stat_client::stat_data_vec_t sd = m_client.dump(); + for (auto& sde : sd) { + std::string name; + + if (sde.name().empty()) + continue; + + name = sde.name(); + + switch (sde.type()) { + case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE: + case STAT_DIR_TYPE_ERROR_INDEX: + case STAT_DIR_TYPE_SCALAR_INDEX: + break; + + case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: + if (name.find("/if") != std::string::npos) + name.erase(0, 4); + for (auto& i : m_stat_itf_indexes) { + counter_t count = {.packets = 0, .bytes = 0 }; + for (int k = 0; k < m_client.vec_len( + sde.get_stat_segment_combined_counter_data()); + k++) { + count.packets += + sde.get_stat_segment_combined_counter_data()[k][i].packets; + count.bytes += + sde.get_stat_segment_combined_counter_data()[k][i].bytes; + } + std::shared_ptr<interface> itf = interface::find(i); + if (itf) + itf->set(count, name); + } + break; + + default:; + } + } + + for (auto& i : m_stat_itf_indexes) { + std::shared_ptr<interface> itf = interface::find(i); + if (itf) + itf->publish_stats(); + } +} + +} // namespace VOM + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/extras/vom/vom/stat_reader.hpp b/extras/vom/vom/stat_reader.hpp new file mode 100644 index 00000000000..f90b2561602 --- /dev/null +++ b/extras/vom/vom/stat_reader.hpp @@ -0,0 +1,103 @@ +/* + * 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_STAT_READER_H__ +#define __VOM_STAT_READER_H__ + +#include "vom/stat_client.hpp" +#include <set> + +namespace VOM { + +class interface; + +/** + * Stat reader: single interface to get stats + */ +class stat_reader +{ +public: + /** + * Default Constructor + */ + stat_reader(); + + /** + * Constructor + */ + stat_reader(stat_client sc); + + /** + * Destructor + */ + ~stat_reader(); + + /** + * connection to stat object + */ + virtual int connect(); + + /** + * disconnect to stat object + */ + virtual void disconnect(); + + /** + * read stats for registered objects from stat_segment + * and set those stats to respective objects + */ + virtual void read(); + +private: + /** + * friend to interface class to call stat_register and + * stat_unregister methods + */ + friend class interface; + + /** + * Register objects to get stats for + */ + static void registers(const interface& itf); + + /** + * Unregister objects + */ + static void unregisters(const interface& itf); + + /** + * typedef of stat_indexes + */ + typedef std::set<uint32_t> stat_indexes_t; + + /** + * stat_client object + */ + stat_client m_client; + + /** + * static pointer to set of registered interfaces + */ + static stat_indexes_t m_stat_itf_indexes; +}; +}; +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ +#endif diff --git a/extras/vom/vom/test_stats.cpp b/extras/vom/vom/test_stats.cpp new file mode 100644 index 00000000000..6235dd44e3f --- /dev/null +++ b/extras/vom/vom/test_stats.cpp @@ -0,0 +1,92 @@ +#include <vom/om.hpp> +#include <vom/hw.hpp> +#include <vom/types.hpp> +#include <vom/prefix.hpp> +#include <vom/tap_interface.hpp> + +class listener : public VOM::interface::stat_listener +{ +public: + listener() {} + ~listener() {} + void handle_interface_stat(const VOM::interface& itf) + { + std::cout << itf.name() << " " << itf.get_stats(); + } +}; + +/** + * Run VPP on another terminal before running vom_stats_test + */ +int main() +{ + uint8_t i = 5; + listener *listen = new listener(); + + VOM::HW::init(new VOM::HW::cmd_q()); + VOM::OM::init(); + + while (VOM::HW::connect() != true) + ; + + VOM::tap_interface itf("tap0", VOM::interface::admin_state_t::UP, VOM::route::prefix_t::ZERO); + VOM::OM::write("__TAP__", itf); + + std::shared_ptr<VOM::tap_interface> intf = itf.singular(); + + + VOM::tap_interface itf1("tap1", VOM::interface::admin_state_t::UP, VOM::route::prefix_t::ZERO); + VOM::OM::write("__TAP__", itf1); + + std::shared_ptr<VOM::tap_interface> intf1 = itf1.singular(); + + VOM::tap_interface itf2("tap2", VOM::interface::admin_state_t::UP, VOM::route::prefix_t::ZERO); + VOM::OM::write("__TAP__", itf2); + + std::shared_ptr<VOM::tap_interface> intf2 = itf2.singular(); + + if (VOM::handle_t::INVALID == intf->handle() || VOM::handle_t::INVALID == intf1->handle() + || VOM::handle_t::INVALID == intf2->handle()) + { + std::cout << "Interface index is INVALID" << std::endl; + VOM::HW::disconnect(); + + return 0; + } + else + { + std::cout << "Interface #1 index is " << intf->handle().value() << std::endl; + std::cout << "Interface #2 index is " << intf1->handle().value() << std::endl; + std::cout << "Interface #3 index is " << intf2->handle().value() << std::endl; + } + + intf->enable_stats(listen); + intf1->enable_stats(listen); + intf2->enable_stats(listen); + + while (i--) + { + sleep(3); + std::cout << "stats # " << std::to_string(i) << std::endl; + VOM::HW::read_stats(); + + if (i == 2) + intf->disable_stats(); + + } + + intf1->disable_stats(); + intf2->disable_stats(); + + intf.reset(); + intf1.reset(); + intf2.reset(); + + VOM::OM::remove("__TAP__"); + + delete listen; + sleep(2); + VOM::HW::disconnect(); + + return 0; +} diff --git a/test/ext/vom_test.cpp b/test/ext/vom_test.cpp index fa51c38215d..6aacea9803b 100644 --- a/test/ext/vom_test.cpp +++ b/test/ext/vom_test.cpp @@ -95,7 +95,7 @@ public: class MockListener : public interface::event_listener, public interface::stat_listener { - void handle_interface_stat(interface_cmds::stats_enable_cmd *cmd) + void handle_interface_stat(const interface& itf) { } void handle_interface_event(std::vector<VOM::interface::event> events) |