diff --git a/tests/qos/BUILD.bazel b/tests/qos/BUILD.bazel index 24ac5725..b0485844 100644 --- a/tests/qos/BUILD.bazel +++ b/tests/qos/BUILD.bazel @@ -31,6 +31,7 @@ cc_library( ], deps = [ ":gnmi_parsers", + ":qos_test_util", "//gutil:collections", "//gutil:overload", "//gutil:proto", @@ -136,3 +137,19 @@ cmd_diff_test( tools = [":gnmi_parsers_test_runner"], ) +cc_library( + name = "qos_test_util", + srcs = ["qos_test_util.cc"], + hdrs = ["qos_test_util.h"], + deps = [ + "//lib/gnmi:gnmi_helper", + "//thinkit:generic_testbed", + "//thinkit/proto:generic_testbed_cc_proto", + "@com_github_gnmi//proto/gnmi:gnmi_cc_proto", + "@com_github_gnmi//proto/gnmi:gnmi_cc_grpc_proto", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + ], +) diff --git a/tests/qos/cpu_qos_test.cc b/tests/qos/cpu_qos_test.cc index ce99ee70..b7d291d9 100644 --- a/tests/qos/cpu_qos_test.cc +++ b/tests/qos/cpu_qos_test.cc @@ -71,6 +71,7 @@ #include "sai_p4/instantiations/google/sai_pd.pb.h" #include "tests/forwarding/util.h" #include "tests/qos/gnmi_parsers.h" +#include "tests/qos/qos_test_util.h" #include "thinkit/control_device.h" #include "thinkit/generic_testbed.h" #include "thinkit/mirror_testbed.h" @@ -252,100 +253,6 @@ absl::StatusOr SetUpPuntToCPUWithRateLimit( return pi_acl_entry; } -// These are the counters we track in these tests. -struct QueueCounters { - int64_t num_packets_transmitted = 0; - int64_t num_packet_dropped = 0; -}; - -std::ostream &operator<<(std::ostream &os, const QueueCounters &counters) { - return os << absl::StreamFormat( - "QueueCounters{" - ".num_packets_transmitted = %d, " - ".num_packets_dropped = %d" - "}", - counters.num_packets_transmitted, counters.num_packet_dropped); -} - -// TODO: Move this to a helper library. -absl::StatusOr GetGnmiQueueCounters( - absl::string_view port, absl::string_view queue, - gnmi::gNMI::StubInterface &gnmi_stub) { - QueueCounters counters; - const std::string openconfig_transmit_count_state_path = absl::Substitute( - "qos/interfaces/interface[interface-id=$0]" - "/output/queues/queue[name=$1]/state/transmit-pkts", - port, queue); - - ASSIGN_OR_RETURN( - std::string transmit_counter_response, - GetGnmiStatePathInfo(&gnmi_stub, openconfig_transmit_count_state_path, - "openconfig-qos:transmit-pkts")); - - if (!absl::SimpleAtoi(StripQuotes(transmit_counter_response), - &counters.num_packets_transmitted)) { - return absl::InternalError(absl::StrCat("Unable to parse counter from ", - transmit_counter_response)); - } - - const std::string openconfig_drop_count_state_path = absl::Substitute( - "qos/interfaces/interface[interface-id=$0]" - "/output/queues/queue[name=$1]/state/dropped-pkts", - port, queue); - - ASSIGN_OR_RETURN( - std::string drop_counter_response, - GetGnmiStatePathInfo(&gnmi_stub, openconfig_drop_count_state_path, - "openconfig-qos:dropped-pkts")); - - if (!absl::SimpleAtoi(StripQuotes(drop_counter_response), - &counters.num_packet_dropped)) { - return absl::InternalError( - absl::StrCat("Unable to parse counter from ", drop_counter_response)); - } - - return counters; -} - -// Returns the total number of packets enqueued for the queue with the given -// `QueueCounters`. -int64_t CumulativeNumPacketsEnqueued(const QueueCounters &counters) { - return counters.num_packet_dropped + counters.num_packets_transmitted; -} - -absl::Status SetPortSpeed(const std::string &port_speed, - const std::string &iface, - gnmi::gNMI::StubInterface &gnmi_stub) { - std::string ops_config_path = absl::StrCat( - "interfaces/interface[name=", iface, "]/ethernet/config/port-speed"); - std::string ops_val = - absl::StrCat("{\"openconfig-if-ethernet:port-speed\":", port_speed, "}"); - RETURN_IF_ERROR(pins_test::SetGnmiConfigPath(&gnmi_stub, ops_config_path, - GnmiSetType::kUpdate, ops_val)); - return absl::OkStatus(); -} - -absl::Status SetPortMtu(int port_mtu, const std::string &interface_name, - gnmi::gNMI::StubInterface &gnmi_stub) { - std::string config_path = absl::StrCat( - "interfaces/interface[name=", interface_name, "]/config/mtu"); - std::string value = absl::StrCat("{\"config:mtu\":", port_mtu, "}"); - RETURN_IF_ERROR(pins_test::SetGnmiConfigPath(&gnmi_stub, config_path, - GnmiSetType::kUpdate, value)); - return absl::OkStatus(); -} - -absl::StatusOr CheckLinkUp(const std::string &iface, - gnmi::gNMI::StubInterface &gnmi_stub) { - std::string oper_status_state_path = - absl::StrCat("interfaces/interface[name=", iface, "]/state/oper-status"); - std::string parse_str = "openconfig-interfaces:oper-status"; - ASSIGN_OR_RETURN( - std::string ops_response, - GetGnmiStatePathInfo(&gnmi_stub, oper_status_state_path, parse_str)); - return ops_response == "\"UP\""; -} - absl::StatusOr MakeIpv4PacketWithDscp( const netaddr::MacAddress &dst_mac, const netaddr::Ipv4Address &dst_ip, int dscp) { @@ -410,27 +317,6 @@ absl::StatusOr MakeIpv6PacketWithDscp( return packet; } -absl::StatusOr> -ParseIpv4DscpToQueueMapping(absl::string_view gnmi_config) { - // TODO: Actually parse config -- hard-coded for now. - absl::flat_hash_map queue_by_dscp; - for (int dscp = 0; dscp < 64; ++dscp) queue_by_dscp[dscp] = "BE1"; - for (int dscp = 8; dscp <= 11; ++dscp) queue_by_dscp[dscp] = "AF1"; - queue_by_dscp[13] = "LLQ1"; - for (int dscp = 16; dscp <= 19; ++dscp) queue_by_dscp[dscp] = "AF2"; - queue_by_dscp[21] = "LLQ2"; - for (int dscp = 24; dscp <= 27; ++dscp) queue_by_dscp[dscp] = "AF3"; - for (int dscp = 32; dscp <= 35; ++dscp) queue_by_dscp[dscp] = "AF4"; - for (int dscp = 48; dscp <= 59; ++dscp) queue_by_dscp[dscp] = "NC1"; - return queue_by_dscp; -} - -absl::StatusOr> -ParseIpv6DscpToQueueMapping(absl::string_view gnmi_config) { - // TODO: Actually parse config -- hard-coded for now. - return ParseIpv4DscpToQueueMapping(gnmi_config); -} - // Represents a link connecting the switch under test (SUT) to a control device. struct SutToControlLink { std::string sut_port_gnmi_name; diff --git a/tests/qos/qos_test_util.cc b/tests/qos/qos_test_util.cc new file mode 100644 index 00000000..ad2af0cd --- /dev/null +++ b/tests/qos/qos_test_util.cc @@ -0,0 +1,143 @@ +#include "tests/qos/qos_test_util.h" + +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" +#include "absl/strings/substitute.h" +#include "gutil/status.h" +#include "lib/gnmi/gnmi_helper.h" + +namespace pins_test { +absl::StatusOr GetGnmiQueueCounters( + absl::string_view port, absl::string_view queue, + gnmi::gNMI::StubInterface &gnmi_stub) { + QueueCounters counters; + + const std::string openconfig_transmit_count_state_path = absl::Substitute( + "qos/interfaces/interface[interface-id=$0]" + "/output/queues/queue[name=$1]/state/transmit-pkts", + port, queue); + + ASSIGN_OR_RETURN( + std::string transmit_counter_response, + GetGnmiStatePathInfo(&gnmi_stub, openconfig_transmit_count_state_path, + "openconfig-qos:transmit-pkts")); + + if (!absl::SimpleAtoi(StripQuotes(transmit_counter_response), + &counters.num_packets_transmitted)) { + return absl::InternalError(absl::StrCat("Unable to parse counter from ", + transmit_counter_response)); + } + + const std::string openconfig_drop_count_state_path = absl::Substitute( + "qos/interfaces/interface[interface-id=$0]" + "/output/queues/queue[name=$1]/state/dropped-pkts", + port, queue); + + ASSIGN_OR_RETURN( + std::string drop_counter_response, + GetGnmiStatePathInfo(&gnmi_stub, openconfig_drop_count_state_path, + "openconfig-qos:dropped-pkts")); + + if (!absl::SimpleAtoi(StripQuotes(drop_counter_response), + &counters.num_packet_dropped)) { + return absl::InternalError( + absl::StrCat("Unable to parse counter from ", drop_counter_response)); + } + return counters; +} + +// Returns the total number of packets enqueued for the queue with the given +// `QueueCounters`. +int64_t CumulativeNumPacketsEnqueued(const QueueCounters &counters) { + return counters.num_packet_dropped + counters.num_packets_transmitted; +} + +absl::Status SetPortSpeed(const std::string &port_speed, + const std::string &iface, + gnmi::gNMI::StubInterface &gnmi_stub) { + std::string ops_config_path = absl::StrCat( + "interfaces/interface[name=", iface, "]/ethernet/config/port-speed"); + std::string ops_val = + absl::StrCat("{\"openconfig-if-ethernet:port-speed\":", port_speed, "}"); + + RETURN_IF_ERROR(pins_test::SetGnmiConfigPath(&gnmi_stub, ops_config_path, + GnmiSetType::kUpdate, ops_val)); + + return absl::OkStatus(); +} + +absl::Status SetPortMtu(int port_mtu, const std::string &interface_name, + gnmi::gNMI::StubInterface &gnmi_stub) { + std::string config_path = absl::StrCat( + "interfaces/interface[name=", interface_name, "]/config/mtu"); + std::string value = absl::StrCat("{\"config:mtu\":", port_mtu, "}"); + + RETURN_IF_ERROR(pins_test::SetGnmiConfigPath(&gnmi_stub, config_path, + GnmiSetType::kUpdate, value)); + + return absl::OkStatus(); +} + +absl::StatusOr CheckLinkUp(const std::string &iface, + gnmi::gNMI::StubInterface &gnmi_stub) { + std::string oper_status_state_path = + absl::StrCat("interfaces/interface[name=", iface, "]/state/oper-status"); + + std::string parse_str = "openconfig-interfaces:oper-status"; + ASSIGN_OR_RETURN( + std::string ops_response, + GetGnmiStatePathInfo(&gnmi_stub, oper_status_state_path, parse_str)); + + return ops_response == "\"UP\""; +} + +// Go over the connections and return vector of connections +// whose links are up. +absl::StatusOr> GetReadyIxiaLinks( + thinkit::GenericTestbed &generic_testbed, + gnmi::gNMI::StubInterface &gnmi_stub) { + std::vector links; + + absl::flat_hash_map interface_info = + generic_testbed.GetSutInterfaceInfo(); + // Loop through the interface_info looking for Ixia/SUT interface pairs, + // checking if the link is up. Add the pair to connections. + for (const auto &[interface, info] : interface_info) { + bool sut_link_up = false; + if (info.interface_modes.contains(thinkit::TRAFFIC_GENERATOR)) + { + ASSIGN_OR_RETURN(sut_link_up, CheckLinkUp(interface, gnmi_stub)); + if (sut_link_up) { + links.push_back({ + .ixia_interface = info.peer_interface_name, + .sut_interface = interface, + }); + } + } + } + + return links; +} + +absl::StatusOr> +ParseIpv4DscpToQueueMapping(absl::string_view gnmi_config) { + // TODO: Actually parse config -- hard-coded for now. + absl::flat_hash_map queue_by_dscp; + for (int dscp = 0; dscp < 64; ++dscp) queue_by_dscp[dscp] = "BE1"; + for (int dscp = 8; dscp <= 11; ++dscp) queue_by_dscp[dscp] = "AF1"; + queue_by_dscp[13] = "LLQ1"; + for (int dscp = 16; dscp <= 19; ++dscp) queue_by_dscp[dscp] = "AF2"; + queue_by_dscp[21] = "LLQ2"; + for (int dscp = 24; dscp <= 27; ++dscp) queue_by_dscp[dscp] = "AF3"; + for (int dscp = 32; dscp <= 35; ++dscp) queue_by_dscp[dscp] = "AF4"; + for (int dscp = 48; dscp <= 59; ++dscp) queue_by_dscp[dscp] = "NC1"; + return queue_by_dscp; +} + +absl::StatusOr> +ParseIpv6DscpToQueueMapping(absl::string_view gnmi_config) { + // TODO: Actually parse config -- hard-coded for now. + return ParseIpv4DscpToQueueMapping(gnmi_config); +} + +} // namespace pins_test diff --git a/tests/qos/qos_test_util.h b/tests/qos/qos_test_util.h new file mode 100644 index 00000000..806dd7b3 --- /dev/null +++ b/tests/qos/qos_test_util.h @@ -0,0 +1,74 @@ +#ifndef PINS_TESTS_QOS_QOS_TEST_UTIL_H_ +#define PINS_TESTS_QOS_QOS_TEST_UTIL_H_ + +#include "absl/status/statusor.h" +#include "absl/strings/str_format.h" +#include "proto/gnmi/gnmi.grpc.pb.h" +#include "proto/gnmi/gnmi.pb.h" +#include "thinkit/generic_testbed.h" +#include "thinkit/proto/generic_testbed.pb.h" + +namespace pins_test { +// These are the counters we track in these tests. +struct QueueCounters { + int64_t num_packets_transmitted = 0; + int64_t num_packet_dropped = 0; +}; + +// Operator to pretty print Queue Counters. +inline std::ostream &operator<<(std::ostream &os, + const QueueCounters &counters) { + return os << absl::StreamFormat( + "QueueCounters{" + ".num_packets_transmitted = %d, " + ".num_packets_dropped = %d" + "}", + counters.num_packets_transmitted, counters.num_packet_dropped); +} + +// Get queue counters for a port queue. +absl::StatusOr GetGnmiQueueCounters( + absl::string_view port, absl::string_view queue, + gnmi::gNMI::StubInterface &gnmi_stub); + +// Get total packets (transmitted + dropped) for port queue. +int64_t CumulativeNumPacketsEnqueued(const QueueCounters &counters); + +// Set port speed using gNMI. +absl::Status SetPortSpeed(const std::string &port_speed, + const std::string &iface, + gnmi::gNMI::StubInterface &gnmi_stub); + +// Set port MTU using gNMI. +absl::Status SetPortMtu(int port_mtu, const std::string &interface_name, + gnmi::gNMI::StubInterface &gnmi_stub); + +// Check if switch port link is up. +absl::StatusOr CheckLinkUp(const std::string &iface, + gnmi::gNMI::StubInterface &gnmi_stub); + +// Structure represents a link between SUT and Ixia. +// This is represented by Ixia interface name and the SUT's gNMI interface +// name. +struct IxiaLink { + std::string ixia_interface; + std::string sut_interface; +}; + +// Go over the connections and return vector of connections +// whose links are up. +absl::StatusOr> GetReadyIxiaLinks( + thinkit::GenericTestbed &generic_testbed, + gnmi::gNMI::StubInterface &gnmi_stub); + +// Parse IPv4 DSCP to queue mapping from gnmi configuration. +absl::StatusOr> +ParseIpv4DscpToQueueMapping(absl::string_view gnmi_config); + +// Parse IPv6 DSCP to queue mapping from gnmi configuration. +absl::StatusOr> +ParseIpv6DscpToQueueMapping(absl::string_view gnmi_config); + +} // namespace pins_test + +#endif // PINS_TESTS_QOS_QOS_TEST_UTIL_H_ diff --git a/tests/thinkit_gnmi_interface_tests.cc b/tests/thinkit_gnmi_interface_tests.cc index 0b330d11..e97e84fb 100644 --- a/tests/thinkit_gnmi_interface_tests.cc +++ b/tests/thinkit_gnmi_interface_tests.cc @@ -71,6 +71,8 @@ void BreakoutDuringPortInUse(thinkit::Switch& sut, GetBreakoutStateInfoForPort(sut_gnmi_stub, port_info.port_name, port_info.curr_breakout_mode)); + LOG(INFO) << "Using port " << port_info.port_name + << " with current breakout mode " << port_info.curr_breakout_mode; // Verify that all ports for the selected port are operationally up. auto resp_parse_str = "openconfig-interfaces:oper-status"; for (const auto& p : orig_breakout_info) {