Skip to content

Commit

Permalink
[Thinkit] Change AppendSflowConfig to UpdateSflowConfig. Remove confi…
Browse files Browse the repository at this point in the history
…g speed changes using params as source of truth. (sonic-net#859)



Co-authored-by: kishanps <kishanps@google.com>
  • Loading branch information
VSuryaprasad-HCL and kishanps authored Dec 21, 2024
1 parent 77e57c8 commit 4be2d58
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 268 deletions.
12 changes: 4 additions & 8 deletions tests/sflow/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,10 @@ cc_library(
"//lib/gnmi:gnmi_helper",
"//lib/utils:json_utils",
"//lib/validator:validator_lib",
"//p4_pdpi:ir",
"//p4_pdpi:p4_runtime_session",
"//p4_pdpi:pd",
"//p4_pdpi/packetlib",
"//p4_pdpi/string_encodings:decimal_string",
"//sai_p4/instantiations/google:sai_pd_cc_proto",
"//tests/forwarding:group_programming_util",
"//tests/forwarding:packet_test_util",
"//tests/forwarding:util",
"//tests/lib:p4rt_fixed_table_programming_helper",
"//tests/lib:switch_test_setup_helpers",
"//tests/qos:gnmi_parsers",
Expand All @@ -48,12 +43,11 @@ cc_library(
"//thinkit:ssh_client",
"@com_github_gnmi//proto/gnmi:gnmi_cc_grpc_proto",
"@com_github_grpc_grpc//:grpc++",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:span",
"@com_google_googletest//:gtest",
Expand All @@ -71,6 +65,8 @@ cc_library(
"//lib/utils:json_utils",
"//lib/validator:validator_lib",
"@com_github_gnmi//proto/gnmi:gnmi_cc_grpc_proto",
"@com_github_nlohmann_json//:nlohmann_json",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
Expand All @@ -85,7 +81,7 @@ cc_test(
deps = [
":sflow_util",
"//gutil:status_matchers",
"//lib/utils:json_utils",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
],
)
82 changes: 11 additions & 71 deletions tests/sflow/sflow_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,28 @@

#include "tests/sflow/sflow_test.h"

#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <thread> // NOLINT
#include <thread> // NOLINT: Need threads (instead of fiber) for upstream code.
#include <utility>
#include <vector>

#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
Expand All @@ -45,13 +50,9 @@
#include "lib/utils/json_utils.h"
#include "lib/validator/validator_lib.h"
#include "p4_pdpi/p4_runtime_session.h"
#include "p4_pdpi/packetlib/packetlib.h"
#include "p4_pdpi/pd.h"
#include "p4_pdpi/string_encodings/decimal_string.h"
#include "sai_p4/instantiations/google/sai_pd.pb.h"
#include "tests/forwarding/group_programming_util.h"
#include "tests/forwarding/packet_test_util.h"
#include "tests/forwarding/util.h"
#include "tests/lib/p4rt_fixed_table_programming_helper.h"
#include "tests/lib/switch_test_setup_helpers.h"
#include "tests/qos/gnmi_parsers.h"
Expand Down Expand Up @@ -105,11 +106,6 @@ constexpr int kSamplingRateInterval = 4000;
// This could be parameterized in future if this is platform dependent.
constexpr double kTolerance = 0.15;

constexpr absl::string_view kSpeed100GB =
"\"openconfig-if-ethernet:SPEED_100GB\"";
constexpr absl::string_view kSpeed200GB =
"\"openconfig-if-ethernet:SPEED_200GB\"";

// Vrf prefix used in the test.
constexpr absl::string_view kVrfIdPrefix = "vrf-";

Expand Down Expand Up @@ -742,7 +738,7 @@ void SflowTestFixture::SetUp() {
ASSERT_OK(GetSflowInfoFromSut(testbed_.get(), gnmi_config, &agent_addr_ipv6,
&sflow_enabled_interfaces));
ASSERT_OK_AND_ASSIGN(
gnmi_config, AppendSflowConfig(
gnmi_config, UpdateSflowConfig(
gnmi_config, agent_addr_ipv6, collector_address_and_port,
sflow_enabled_interfaces, sampling_rate, sampling_size));
ASSERT_OK(testbed_->Environment().StoreTestArtifact(
Expand All @@ -764,64 +760,8 @@ void SflowTestFixture::SetUp() {
agent_addr_ipv6, sampling_rate, sampling_size, collector_address_and_port,
sflow_enabled_interfaces));

// TODO: Remove unused set speed in sFlow test.
// Go through all the ports that connect to the Ixia and set them
// first to 200GB.
absl::flat_hash_map<std::string, thinkit::InterfaceInfo> interface_info =
testbed_->GetSutInterfaceInfo();
for (const auto& [interface, info] : interface_info) {
if (info.interface_modes.contains(thinkit::TRAFFIC_GENERATOR)) {
ASSERT_OK(pins_test::SetPortSpeedInBitsPerSecond(std::string(kSpeed200GB),
interface, *gnmi_stub_));
}
}

auto speed_config_applied =
[&interface_info](absl::string_view expected_speed,
gnmi::gNMI::StubInterface* gnmi_stub) -> absl::Status {
for (const auto& [interface, info] : interface_info) {
if (info.interface_modes.contains(thinkit::TRAFFIC_GENERATOR)) {
ASSIGN_OR_RETURN(auto port_speed, GetPortSpeed(interface, gnmi_stub));
if (port_speed != expected_speed) {
return absl::FailedPreconditionError(absl::Substitute(
"Port speed is not converged. Interface $0 "
"speed state path value is $1, expected speed is $2.",
interface, port_speed, expected_speed));
}
}
}
return absl::OkStatus();
};
// Waits for speed config to be applied.
EXPECT_OK(pins_test::WaitForCondition(speed_config_applied, absl::Seconds(30),
kSpeed200GB, gnmi_stub_.get()));
auto links_up = [this]() -> absl::Status {
ASSIGN_OR_RETURN(ready_links_,
GetIxiaConnectedUpLinks(*testbed_, *gnmi_stub_));
if (ready_links_.empty()) {
return absl::FailedPreconditionError("No Ixia links up.");
}
return absl::OkStatus();
};
// Waits for links to be up.
EXPECT_OK(pins_test::WaitForCondition(links_up, absl::Seconds(30)));

// If links didn't come, lets try 100GB as some testbeds have 100GB
// IXIA connections.
if (ready_links_.empty()) {
for (const auto& [interface, info] : interface_info) {
if (info.interface_modes.contains(thinkit::TRAFFIC_GENERATOR)) {
ASSERT_OK(pins_test::SetPortSpeedInBitsPerSecond(
std::string(kSpeed100GB), interface, *gnmi_stub_));
}
}
// Waits for speed config to be applied.
EXPECT_OK(pins_test::WaitForCondition(speed_config_applied,
absl::Seconds(30), kSpeed100GB,
gnmi_stub_.get()));
// Waits for links to come up.
EXPECT_OK(pins_test::WaitForCondition(links_up, absl::Seconds(30)));
}
ASSERT_OK_AND_ASSIGN(ready_links_,
GetIxiaConnectedUpLinks(*testbed_, *gnmi_stub_));
ASSERT_FALSE(ready_links_.empty()) << "Ixia links are not ready";
}

Expand Down
4 changes: 1 addition & 3 deletions tests/sflow/sflow_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,12 @@

#include <memory>
#include <string>
#include <thread> // NOLINT: Need threads (instead of fiber) for upstream code.
#include <vector>

#include "absl/memory/memory.h"
#include "gtest/gtest.h"
#include "p4_pdpi/ir.h"
#include "p4_pdpi/p4_runtime_session.h"
#include "proto/gnmi/gnmi.grpc.pb.h"
#include "thinkit/generic_testbed.h"
#include "thinkit/generic_testbed_fixture.h"
#include "thinkit/ssh_client.h"

Expand Down
108 changes: 79 additions & 29 deletions tests/sflow/sflow_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@

#include "tests/sflow/sflow_util.h"

#include "absl/container/btree_map.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "absl/time/time.h"
#include "gutil/status.h"
#include "include/nlohmann/json.hpp"
#include "lib/gnmi/gnmi_helper.h"
#include "lib/utils/json_utils.h"
#include "lib/validator/validator_lib.h"
Expand Down Expand Up @@ -170,16 +173,15 @@ absl::Status VerifySflowStatesConverged(
return absl::OkStatus();
}

absl::StatusOr<std::string> AppendSflowConfig(
absl::StatusOr<std::string> UpdateSflowConfig(
absl::string_view gnmi_config, absl::string_view agent_addr_ipv6,
const std::vector<std::pair<std::string, int>>& collector_address_and_port,
const absl::flat_hash_set<std::string>& sflow_enabled_interfaces,
const int sampling_rate, const int sampling_header_size) {
ASSIGN_OR_RETURN(auto gnmi_config_json, json_yang::ParseJson(gnmi_config));

if (agent_addr_ipv6.empty()) {
return absl::InvalidArgumentError(
"loopback_address parameter cannot be empty.");
"agent_addr_ipv6 parameter cannot be empty.");
}
if (sflow_enabled_interfaces.empty()) {
return absl::InvalidArgumentError(
Expand All @@ -197,36 +199,84 @@ absl::StatusOr<std::string> AppendSflowConfig(
gnmi_config_json["openconfig-sampling:sampling"]
["openconfig-sampling-sflow:sflow"]["config"]
["agent-id-ipv6"] = agent_addr_ipv6;
// Sort collector IPs and interface names by string order to make sure the
// generated config has a determinisitic order.
std::vector<std::pair<std::string, int>> sorted_collector_address_and_port =
collector_address_and_port;
std::sort(sorted_collector_address_and_port.begin(),
sorted_collector_address_and_port.end());
for (const auto& [address, port] : sorted_collector_address_and_port) {
nlohmann::basic_json<> sflow_collector_config;
sflow_collector_config["address"] = address;
sflow_collector_config["port"] = port;
sflow_collector_config["config"]["address"] = address;
sflow_collector_config["config"]["port"] = port;
gnmi_config_json["openconfig-sampling:sampling"]
["openconfig-sampling-sflow:sflow"]["collectors"]
["collector"]
.push_back(sflow_collector_config);
if (!collector_address_and_port.empty()) {
absl::btree_map<std::string, nlohmann::json>
collector_address_port_to_config_json;
for (const auto& [address, port] : collector_address_and_port) {
nlohmann::basic_json<> sflow_collector_config;
sflow_collector_config["address"] = address;
sflow_collector_config["port"] = port;
sflow_collector_config["config"]["address"] = address;
sflow_collector_config["config"]["port"] = port;
collector_address_port_to_config_json[absl::StrCat(address, ":", port)] =
sflow_collector_config;
}
nlohmann::json& collector_json_array =
gnmi_config_json["openconfig-sampling:sampling"]
["openconfig-sampling-sflow:sflow"]["collectors"]
["collector"];
if (!collector_json_array.empty() &&
collector_json_array.type() != nlohmann::json::value_t::array) {
return absl::InvalidArgumentError(
"json collector field already exists and is not an array.");
}

// Prune existing collector ip configs.
for (nlohmann::json& collector_json : collector_json_array) {
std::string address_and_port = absl::StrCat(
json_yang::GetSimpleJsonValueAsString(collector_json["address"]), ":",
json_yang::GetSimpleJsonValueAsString(collector_json["port"]));
auto it = collector_address_port_to_config_json.find(address_and_port);
if (it != collector_address_port_to_config_json.end()) {
collector_address_port_to_config_json.erase(it);
}
}

// Append collector ip config if any.
for (const auto& [address_and_port, config_json] :
collector_address_port_to_config_json) {
collector_json_array.push_back(config_json);
}
}
absl::btree_set<std::string> sorted_interface_names(
sflow_enabled_interfaces.begin(), sflow_enabled_interfaces.end());
for (const auto& interface_name : sorted_interface_names) {

absl::btree_map<std::string, nlohmann::json> interface_name_to_config_json;
for (const auto& interface_name : sflow_enabled_interfaces) {
nlohmann::basic_json<> sflow_interface_config;
sflow_interface_config["name"] = interface_name;
sflow_interface_config["config"]["name"] = interface_name;
sflow_interface_config["config"]["enabled"] = true;
sflow_interface_config["config"]["ingress-sampling-rate"] = sampling_rate;
gnmi_config_json["openconfig-sampling:sampling"]
["openconfig-sampling-sflow:sflow"]["interfaces"]
["interface"]
.push_back(sflow_interface_config);
sflow_interface_config["enabled"] = true;
sflow_interface_config["ingress-sampling-rate"] = sampling_rate;
interface_name_to_config_json[interface_name] = sflow_interface_config;
}
nlohmann::json& interface_json_array =
gnmi_config_json["openconfig-sampling:sampling"]
["openconfig-sampling-sflow:sflow"]["interfaces"]
["interface"];
if (!interface_json_array.empty() &&
interface_json_array.type() != nlohmann::json::value_t::array) {
return absl::InvalidArgumentError(
"json interface field already exists and is not an array.");
}

// Modify all existing interface config.
for (nlohmann::json& interface_json : interface_json_array) {
std::string interface_name =
json_yang::GetSimpleJsonValueAsString(interface_json["name"]);
auto it = interface_name_to_config_json.find(interface_name);
if (it != interface_name_to_config_json.end()) {
interface_json["config"] = it->second;
interface_name_to_config_json.erase(it);
}
}

// Append interface config if any.
for (const auto& [interface_name, interface_config_json] :
interface_name_to_config_json) {
nlohmann::basic_json<> sflow_interface_json;
sflow_interface_json["name"] = interface_name;
sflow_interface_json["config"] = interface_config_json;
interface_json_array.push_back(sflow_interface_json);
}

return json_yang::DumpJson(gnmi_config_json);
}

Expand Down
9 changes: 5 additions & 4 deletions tests/sflow/sflow_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@ absl::Status VerifySflowStatesConverged(
const std::vector<std::pair<std::string, int>>& collector_address_and_port,
const absl::flat_hash_set<std::string>& sflow_enabled_interfaces);

// Appends sFlow config to `gnmi_config` and returns modified config if success.
// The modified config would sort collector IPs and interface names by string
// order. Returns an FailedPreconditionError if `agent_addr_ipv6` or
// Updates `gnmi_config` with sFlow-related config and returns modified config
// if success. The modified config would sort collector IPs and interface names
// by string order. Returns an FailedPreconditionError if `agent_addr_ipv6` or
// `sflow_enabled_interfaces` is empty.
absl::StatusOr<std::string> AppendSflowConfig(
absl::StatusOr<std::string> UpdateSflowConfig(

absl::string_view gnmi_config, absl::string_view agent_addr_ipv6,
const std::vector<std::pair<std::string, int>>& collector_address_and_port,
const absl::flat_hash_set<std::string>& sflow_enabled_interfaces,
Expand Down
Loading

0 comments on commit 4be2d58

Please sign in to comment.