Skip to content

Commit

Permalink
Change CheckAllInterfaceUpOverGnmi function to CheckAllInterfaceOperS…
Browse files Browse the repository at this point in the history
…tateOverGnmi.BERT test.
  • Loading branch information
kishanps authored and bibhuprasad-hcl committed Jul 22, 2024
1 parent 3c6e86e commit e78e7a1
Show file tree
Hide file tree
Showing 5 changed files with 307 additions and 12 deletions.
1 change: 1 addition & 0 deletions lib/gnmi/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ cc_library(
"@com_github_google_glog//:glog",
"@com_github_nlohmann_json//:nlohmann_json",
"@com_github_p4lang_p4runtime//:p4runtime_cc_grpc",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/numeric:int128",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
Expand Down
50 changes: 42 additions & 8 deletions lib/gnmi/gnmi_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,9 @@ absl::StatusOr<gnmi::GetResponse> GetAllInterfaceOverGnmi(
return resp;
}

absl::Status CheckAllInterfaceUpOverGnmi(gnmi::gNMI::Stub& stub,
absl::Duration timeout) {
absl::StatusOr<absl::flat_hash_map<std::string, std::string>>
GetInterfaceToOperStatusMapOverGnmi(gnmi::gNMI::StubInterface& stub,
absl::Duration timeout) {
ASSIGN_OR_RETURN(auto req,
BuildGnmiGetRequest("interfaces", gnmi::GetRequest::STATE));
gnmi::GetResponse resp;
Expand All @@ -250,7 +251,7 @@ absl::Status CheckAllInterfaceUpOverGnmi(gnmi::gNMI::Stub& stub,
absl::StrCat("'interface' not found: ", oc_intf_json->dump()));
}

std::vector<std::string> unavailable_interfaces;
absl::flat_hash_map<std::string, std::string> interface_to_oper_status_map;
for (auto const& element : oc_intf_list_json->items()) {
const auto element_name_json = element.value().find("name");
if (element_name_json == element.value().end()) {
Expand All @@ -275,8 +276,23 @@ absl::Status CheckAllInterfaceUpOverGnmi(gnmi::gNMI::Stub& stub,
return absl::NotFoundError(
absl::StrCat("'oper-status' not found: ", name));
}
if (!absl::StrContains(element_status_json->dump(), "UP")) {
unavailable_interfaces.push_back(name);
interface_to_oper_status_map[name] =
std::string(StripQuotes(element_status_json->dump()));
}

return interface_to_oper_status_map;
}

absl::Status CheckAllInterfaceOperStateOverGnmi(
gnmi::gNMI::StubInterface& stub, absl::string_view interface_oper_state,
absl::Duration timeout) {
ASSIGN_OR_RETURN(const auto interface_to_oper_status_map,
GetInterfaceToOperStatusMapOverGnmi(stub, timeout));

std::vector<std::string> unavailable_interfaces;
for (const auto& [interface, oper_status] : interface_to_oper_status_map) {
if (oper_status != interface_oper_state) {
unavailable_interfaces.push_back(interface);
}
}
if (!unavailable_interfaces.empty()) {
Expand Down Expand Up @@ -343,19 +359,38 @@ GnmiGetElementFromTelemetryResponse(const gnmi::SubscribeResponse& response) {
return elements;
}

absl::StatusOr<std::vector<std::string>> GetUpInterfacesOverGnmi(
gnmi::gNMI::StubInterface& stub, absl::Duration timeout) {
ASSIGN_OR_RETURN(const auto interface_to_oper_status_map,
GetInterfaceToOperStatusMapOverGnmi(stub, timeout));

std::vector<std::string> up_interfaces;
for (const auto& [interface, oper_status] : interface_to_oper_status_map) {
// Ignore the interfaces that is not EthernetXX. For example: bond0,
// Loopback0, etc.
if (!absl::StartsWith(interface, "Ethernet")) {
LOG(INFO) << "Ignoring interface: " << interface;
continue;
}
if (oper_status == "UP") {
up_interfaces.push_back(interface);
}
}

return up_interfaces;
}

absl::StatusOr<OperStatus> GetInterfaceOperStatusOverGnmi(
gnmi::gNMI::Stub& stub, absl::string_view if_name) {
std::string if_req = absl::StrCat("interfaces/interface[name=", if_name,
"]/state/oper-status");
ASSIGN_OR_RETURN(auto request,
BuildGnmiGetRequest(if_req, gnmi::GetRequest::STATE));
LOG(INFO) << "Sending GET request: " << request.ShortDebugString();

gnmi::GetResponse response;
grpc::ClientContext context;
grpc::Status status = stub.Get(&context, request, &response);
if (!status.ok()) return gutil::GrpcStatusToAbslStatus(status);
LOG(INFO) << "Received GET response: " << response.ShortDebugString();

if (response.notification_size() != 1 ||
response.notification(0).update_size() != 1) {
Expand All @@ -365,7 +400,6 @@ absl::StatusOr<OperStatus> GetInterfaceOperStatusOverGnmi(
ASSIGN_OR_RETURN(
std::string oper_status,
ParseGnmiGetResponse(response, "openconfig-interfaces:oper-status"));
LOG(INFO) << "Got the operational status: " << oper_status << ".";

if (absl::StrContains((oper_status), "UP")) {
return OperStatus::kUp;
Expand Down
18 changes: 15 additions & 3 deletions lib/gnmi/gnmi_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <type_traits>
#include <vector>

#include "absl/container/flat_hash_map.h"
#include "absl/numeric/int128.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
Expand Down Expand Up @@ -109,13 +110,24 @@ absl::Status CanGetAllInterfaceOverGnmi(
absl::StatusOr<gnmi::GetResponse> GetAllInterfaceOverGnmi(
gnmi::gNMI::Stub& stub, absl::Duration timeout = absl::Seconds(60));

// Checks if all interfaces are up.
absl::Status CheckAllInterfaceUpOverGnmi(
gnmi::gNMI::Stub& stub, absl::Duration timeout = absl::Seconds(60));
// Gets the interface to oper status map.
absl::StatusOr<absl::flat_hash_map<std::string, std::string>>
GetInterfaceToOperStatusMapOverGnmi(gnmi::gNMI::StubInterface& stub,
absl::Duration timeout);

// Checks if all interfaces oper-status is up/down.
absl::Status CheckAllInterfaceOperStateOverGnmi(
gnmi::gNMI::StubInterface& stub, absl::string_view interface_oper_state,
absl::Duration timeout = absl::Seconds(60));

// Returns gNMI Path for OC strings.
gnmi::Path ConvertOCStringToPath(absl::string_view oc_path);

// Gets all the EthernetXX interfaces whose operational status is UP.
absl::StatusOr<std::vector<std::string>> GetUpInterfacesOverGnmi(
gnmi::gNMI::StubInterface& stub,
absl::Duration timeout = absl::Seconds(60));

// Gets the operational status of an interface.
absl::StatusOr<OperStatus> GetInterfaceOperStatusOverGnmi(
gnmi::gNMI::Stub& stub, absl::string_view if_name);
Expand Down
247 changes: 247 additions & 0 deletions lib/gnmi/gnmi_helper_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -359,5 +359,252 @@ TEST(StripQuotes, VariousInputs) {
EXPECT_EQ(StripQuotes(R"("test"")"), R"(test")");
}

TEST(GetInterfaceOperStatusMap, GnmiGetRpcFails) {
gnmi::MockgNMIStub stub;
EXPECT_CALL(stub, Get).WillOnce(
Return(grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, "")));
EXPECT_THAT(GetInterfaceToOperStatusMapOverGnmi(stub, absl::Seconds(60)),
StatusIs(absl::StatusCode::kDeadlineExceeded));
}

TEST(GetInterfaceOperStatusMap, InvalidGnmiGetResponse) {
gnmi::MockgNMIStub stub;
EXPECT_CALL(stub, Get).WillOnce(Return(grpc::Status::OK));
EXPECT_THAT(GetInterfaceToOperStatusMapOverGnmi(stub, absl::Seconds(60)),
StatusIs(absl::StatusCode::kInternal,
testing::HasSubstr("Invalid response")));
}

TEST(GetInterfaceOperStatusMap, GnmiGetResponseWithoutOpenconfigInterface) {
gnmi::MockgNMIStub stub;
EXPECT_CALL(stub, Get).WillOnce(DoAll(
SetArgPointee<2>(gutil::ParseProtoOrDie<gnmi::GetResponse>(
R"pb(notification {
timestamp: 1620348032128305716
prefix { origin: "openconfig" }
update {
path { elem { name: "interfaces" } }
val { json_ietf_val: "{\"openconfig-system:alarms\":{}}" }
}
})pb")),
Return(grpc::Status::OK)));

EXPECT_THAT(GetInterfaceToOperStatusMapOverGnmi(stub, absl::Seconds(60)),
StatusIs(absl::StatusCode::kNotFound,
testing::HasSubstr(
"'openconfig-interfaces:interfaces' not found")));
}

TEST(GetInterfaceOperStatusMap, InterfaceNotFoundInGnmiGetResponse) {
gnmi::MockgNMIStub stub;
EXPECT_CALL(stub, Get).WillOnce(DoAll(
SetArgPointee<2>(gutil::ParseProtoOrDie<gnmi::GetResponse>(
R"pb(notification {
timestamp: 1620348032128305716
prefix { origin: "openconfig" }
update {
path { elem { name: "interfaces" } }
val {
json_ietf_val: "{\"openconfig-interfaces:interfaces\":{}}"
}
}
})pb")),
Return(grpc::Status::OK)));

EXPECT_THAT(GetInterfaceToOperStatusMapOverGnmi(stub, absl::Seconds(60)),
StatusIs(absl::StatusCode::kNotFound,
testing::HasSubstr("'interface' not found")));
}

TEST(GetInterfaceOperStatusMap, InterfaceNameNotFound) {
gnmi::MockgNMIStub stub;
EXPECT_CALL(stub, Get).WillOnce(DoAll(
SetArgPointee<2>(gutil::ParseProtoOrDie<gnmi::GetResponse>(
R"pb(notification {
timestamp: 1620348032128305716
prefix { origin: "openconfig" }
update {
path { elem { name: "interfaces" } }
val {
json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{}]}}"
}
}
})pb")),
Return(grpc::Status::OK)));

EXPECT_THAT(GetInterfaceToOperStatusMapOverGnmi(stub, absl::Seconds(60)),
StatusIs(absl::StatusCode::kNotFound,
testing::HasSubstr("'name' not found")));
}

TEST(GetInterfaceOperStatusMap, InterfaceStateNotFound) {
gnmi::MockgNMIStub stub;
EXPECT_CALL(stub, Get).WillOnce(DoAll(
SetArgPointee<2>(gutil::ParseProtoOrDie<gnmi::GetResponse>(
R"pb(notification {
timestamp: 1620348032128305716
prefix { origin: "openconfig" }
update {
path { elem { name: "interfaces" } }
val {
json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\"}]}}"
}
}
})pb")),
Return(grpc::Status::OK)));

EXPECT_THAT(GetInterfaceToOperStatusMapOverGnmi(stub, absl::Seconds(60)),
StatusIs(absl::StatusCode::kNotFound,
testing::HasSubstr("'state' not found")));
}

TEST(GetInterfaceOperStatusMap, OperStatusNotFoundInState) {
gnmi::MockgNMIStub stub;
EXPECT_CALL(stub, Get).WillOnce(DoAll(
SetArgPointee<2>(gutil::ParseProtoOrDie<gnmi::GetResponse>(
R"pb(notification {
timestamp: 1620348032128305716
prefix { origin: "openconfig" }
update {
path { elem { name: "interfaces" } }
val {
json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"name\":\"Ethernet0\"}}]}}"
}
}
})pb")),
Return(grpc::Status::OK)));

EXPECT_THAT(GetInterfaceToOperStatusMapOverGnmi(stub, absl::Seconds(60)),
StatusIs(absl::StatusCode::kNotFound,
testing::HasSubstr("'oper-status' not found")));
}

TEST(GetInterfaceOperStatusMap, SuccessfullyReturnsInterfaceOperStatusMap) {
gnmi::MockgNMIStub stub;
EXPECT_CALL(stub, Get).WillOnce(DoAll(
SetArgPointee<2>(gutil::ParseProtoOrDie<gnmi::GetResponse>(
R"pb(notification {
timestamp: 1620348032128305716
prefix { origin: "openconfig" }
update {
path { elem { name: "interfaces" } }
val {
json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Cpu0\"},{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"DOWN\"}}]}}"
}
}
})pb")),
Return(grpc::Status::OK)));

auto statusor = GetInterfaceToOperStatusMapOverGnmi(stub, absl::Seconds(60));
ASSERT_OK(statusor);
const absl::flat_hash_map<std::string, std::string> expected_map = {
{"Ethernet0", "DOWN"}};
EXPECT_THAT(*statusor,
::testing::UnorderedPointwise(::testing::Eq(), expected_map));
}

TEST(CheckAllInterfaceOperState, FailsToGetInterfaceOperStatusMap) {
gnmi::MockgNMIStub stub;
EXPECT_CALL(stub, Get).WillOnce(
Return(grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, "")));
EXPECT_THAT(
CheckAllInterfaceOperStateOverGnmi(stub, /*interface_oper_state=*/"UP"),
StatusIs(absl::StatusCode::kDeadlineExceeded));
}

TEST(CheckAllInterfaceOperState, InterfaceNotUp) {
gnmi::MockgNMIStub stub;
EXPECT_CALL(stub, Get).WillOnce(DoAll(
SetArgPointee<2>(gutil::ParseProtoOrDie<gnmi::GetResponse>(
R"pb(notification {
timestamp: 1620348032128305716
prefix { origin: "openconfig" }
update {
path { elem { name: "interfaces" } }
val {
json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"DOWN\"}}]}}"
}
}
})pb")),
Return(grpc::Status::OK)));

EXPECT_THAT(
CheckAllInterfaceOperStateOverGnmi(stub, /*interface_oper_state=*/"UP"),
StatusIs(absl::StatusCode::kUnavailable,
testing::HasSubstr("Interfaces are not ready")));
}

TEST(CheckAllInterfaceOperState, InterfaceNotDown) {
gnmi::MockgNMIStub stub;
EXPECT_CALL(stub, Get).WillOnce(DoAll(
SetArgPointee<2>(gutil::ParseProtoOrDie<gnmi::GetResponse>(
R"pb(notification {
timestamp: 1620348032128305716
prefix { origin: "openconfig" }
update {
path { elem { name: "interfaces" } }
val {
json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"TESTING\"}}]}}"
}
}
})pb")),
Return(grpc::Status::OK)));

EXPECT_THAT(
CheckAllInterfaceOperStateOverGnmi(stub, /*interface_oper_state=*/"DOWN"),
StatusIs(absl::StatusCode::kUnavailable,
testing::HasSubstr("Interfaces are not ready")));
}

TEST(CheckAllInterfaceOperState, AllInterfacesUp) {
gnmi::MockgNMIStub stub;
EXPECT_CALL(stub, Get).WillOnce(DoAll(
SetArgPointee<2>(gutil::ParseProtoOrDie<gnmi::GetResponse>(
R"pb(notification {
timestamp: 1620348032128305716
prefix { origin: "openconfig" }
update {
path { elem { name: "interfaces" } }
val {
json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet1\",\"state\":{\"oper-status\":\"UP\"}}]}}"
}
}
})pb")),
Return(grpc::Status::OK)));

ASSERT_OK(
CheckAllInterfaceOperStateOverGnmi(stub, /*interface_oper_state=*/"UP"));
}

TEST(GetUpInterfaces, FailsToGetInterfaceOperStatusMap) {
gnmi::MockgNMIStub stub;
EXPECT_CALL(stub, Get).WillOnce(
Return(grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, "")));
EXPECT_THAT(GetUpInterfacesOverGnmi(stub),
StatusIs(absl::StatusCode::kDeadlineExceeded));
}

TEST(GetUpInterfaces, SuccessfullyGetsUpInterface) {
gnmi::MockgNMIStub stub;
EXPECT_CALL(stub, Get).WillOnce(DoAll(
SetArgPointee<2>(gutil::ParseProtoOrDie<gnmi::GetResponse>(
R"pb(notification {
timestamp: 1620348032128305716
prefix { origin: "openconfig" }
update {
path { elem { name: "interfaces" } }
val {
json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"bond0\",\"state\":{\"oper-status\":\"UP\"}},{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}}]}}"
}
}
})pb")),
Return(grpc::Status::OK)));

auto statusor = GetUpInterfacesOverGnmi(stub);
ASSERT_OK(statusor);
EXPECT_THAT(*statusor,
testing::ContainerEq(std::vector<std::string>{"Ethernet0"}));
}

} // namespace
} // namespace pins_test
Loading

0 comments on commit e78e7a1

Please sign in to comment.