diff --git a/pins_infra_deps.bzl b/pins_infra_deps.bzl index 967e8db7..ff1ff6b9 100644 --- a/pins_infra_deps.bzl +++ b/pins_infra_deps.bzl @@ -235,3 +235,15 @@ def pins_infra_deps(): ], sha256 = "a89e203d3cf264e564fcb96b6e06dd70bc0557356eb48400ce4b5d97c2c3720d", ) + if not native.existing_rule("com_github_nlohmann_json"): + http_archive( + name = "com_github_nlohmann_json", + # JSON for Modern C++ + url = "https://github.com/nlohmann/json/archive/v3.7.3.zip", + strip_prefix = "json-3.7.3", + sha256 = "e109cd4a9d1d463a62f0a81d7c6719ecd780a52fb80a22b901ed5b6fe43fb45b", + build_file_content = """cc_library(name="json", + visibility=["//visibility:public"], + hdrs=["single_include/nlohmann/json.hpp"] + )""", + ) diff --git a/tests/thinkit_sanity_tests.cc b/tests/thinkit_sanity_tests.cc index 227eb55a..450a6326 100644 --- a/tests/thinkit_sanity_tests.cc +++ b/tests/thinkit_sanity_tests.cc @@ -21,22 +21,30 @@ #include "absl/status/statusor.h" #include "absl/time/time.h" +#include "glog/logging.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "gutil/status.h" #include "gutil/status_matchers.h" #include "p4/v1/p4runtime.grpc.pb.h" #include "p4_pdpi/p4_runtime_session.h" +#include "proto/gnmi/gnmi.grpc.pb.h" +#include "include/nlohmann/json.hpp" #include "thinkit/ssh_client.h" #include "thinkit/switch.h" namespace pins_test { namespace { +using ::nlohmann::json; using ::testing::Eq; } // namespace +constexpr char kOpenconfigStr[] = "openconfig"; +constexpr char kStateUp[] = "UP"; +constexpr char kInterfaces[] = "interfaces"; + void TestSSHCommand(thinkit::SSHClient& ssh_client, thinkit::Switch& sut) { ASSERT_OK_AND_ASSIGN(std::string output, ssh_client.RunCommand(sut.ChassisName(), "echo foo", @@ -53,4 +61,95 @@ void TestP4Session(thinkit::Switch& sut) { pdpi::P4RuntimeSession::Create(std::move(sut_p4runtime_stub), kDeviceId)); } +void TestGnmiCheckSpecificInterfaceStateOperation(thinkit::Switch& sut, + std::string if_name) { + ASSERT_OK_AND_ASSIGN(auto sut_gnmi_stub, sut.CreateGnmiStub()); + gnmi::GetRequest req; + req.set_type(gnmi::GetRequest_DataType_STATE); + req.mutable_prefix()->set_origin(kOpenconfigStr); + gnmi::Path* path = req.add_path(); + path->add_elem()->set_name(kInterfaces); + auto elem = path->add_elem(); + elem->set_name("interface"); + (*elem->mutable_key())["name"] = if_name; + path->add_elem()->set_name("state"); + path->add_elem()->set_name("oper-status"); + + req.set_encoding(::gnmi::Encoding::JSON_IETF); + LOG(INFO) << "Sending GET request: " << req.ShortDebugString(); + + gnmi::GetResponse resp; + grpc::ClientContext context; + ASSERT_OK(sut_gnmi_stub->Get(&context, req, &resp)); + LOG(INFO) << "Received GET response: " << resp.ShortDebugString(); + ASSERT_EQ(resp.notification_size(), 1); + ASSERT_EQ(resp.notification(0).update_size(), 1); + const std::string val_str = + resp.notification(0).update(0).val().json_ietf_val(); + auto const resp_json = json::parse(val_str); + auto const oper_status = resp_json.find("openconfig-interfaces:oper-status"); + ASSERT_NE(oper_status, resp_json.end()); + EXPECT_THAT(oper_status->dump(), testing::HasSubstr(kStateUp)); +} + +void TestGnmiCheckInterfaceStateOperation(thinkit::Switch& sut) { + ASSERT_OK_AND_ASSIGN(auto sut_gnmi_stub, sut.CreateGnmiStub()); + gnmi::GetRequest req; + req.mutable_prefix()->set_origin(kOpenconfigStr); + req.set_type(gnmi::GetRequest_DataType_STATE); + gnmi::Path* path = req.add_path(); + path->add_elem()->set_name(kInterfaces); + req.set_encoding(::gnmi::Encoding::JSON_IETF); + LOG(INFO) << "Sending GET request: " << req.ShortDebugString(); + + gnmi::GetResponse resp; + grpc::ClientContext context; + ASSERT_OK(sut_gnmi_stub->Get(&context, req, &resp)); + LOG(INFO) << "Received GET response: " << resp.ShortDebugString(); + ASSERT_EQ(resp.notification_size(), 1); + ASSERT_EQ(resp.notification(0).update_size(), 1); + auto const resp_json = + json::parse(resp.notification(0).update(0).val().json_ietf_val()); + auto const oc_intf_json = resp_json.find("openconfig-interfaces:interfaces"); + ASSERT_NE(oc_intf_json, resp_json.end()); + auto const oc_intf_list_json = oc_intf_json->find("interface"); + ASSERT_NE(oc_intf_list_json, oc_intf_json->end()); + for (auto const& element : oc_intf_list_json->items()) { + auto const element_state_json = element.value().find("state"); + ASSERT_NE(element_state_json, element.value().end()); + + auto const element_status_json = element_state_json->find("oper-status"); + ASSERT_NE(element_status_json, element_state_json->end()); + + EXPECT_THAT(element_status_json->dump(), testing::HasSubstr(kStateUp)); + } +} +void TestGnmiGetInterfaceOperation(thinkit::Switch& sut) { + ASSERT_OK_AND_ASSIGN(auto sut_gnmi_stub, sut.CreateGnmiStub()); + gnmi::GetRequest req; + req.mutable_prefix()->set_origin(kOpenconfigStr); + req.set_type(gnmi::GetRequest_DataType_ALL); + gnmi::Path* path = req.add_path(); + path->add_elem()->set_name(kInterfaces); + req.set_encoding(::gnmi::Encoding::JSON_IETF); + LOG(INFO) << "Sending GET request: " << req.ShortDebugString(); + gnmi::GetResponse resp; + grpc::ClientContext context; + ASSERT_OK(sut_gnmi_stub->Get(&context, req, &resp)); + LOG(INFO) << "Received GET response: " << resp.ShortDebugString(); +} + +void TestGnmiGetAllOperation(thinkit::Switch& sut) { + ASSERT_OK_AND_ASSIGN(auto sut_gnmi_stub, sut.CreateGnmiStub()); + gnmi::GetRequest req; + req.mutable_prefix()->set_origin(kOpenconfigStr); + req.set_type(gnmi::GetRequest_DataType_ALL); + req.set_encoding(gnmi::Encoding::JSON_IETF); + LOG(INFO) << "Sending GET request: " << req.ShortDebugString(); + gnmi::GetResponse resp; + grpc::ClientContext context; + ASSERT_OK(sut_gnmi_stub->Get(&context, req, &resp)); + LOG(INFO) << "Received GET response: " << resp.ShortDebugString(); +} + } // namespace pins_test diff --git a/tests/thinkit_sanity_tests.h b/tests/thinkit_sanity_tests.h index f31b3f77..2d8199cd 100644 --- a/tests/thinkit_sanity_tests.h +++ b/tests/thinkit_sanity_tests.h @@ -26,6 +26,18 @@ void TestSSHCommand(thinkit::SSHClient& ssh_client, thinkit::Switch& sut); // Tests that P4 Sessions can be established with the switch. void TestP4Session(thinkit::Switch& sut); +// Tests that gNMI get interface works fine with SUT. +void TestGnmiGetInterfaceOperation(thinkit::Switch& sut); + +// Tests that gNMI get all works fine with SUT. +void TestGnmiGetAllOperation(thinkit::Switch& sut); + +// Tests that SUT all ports state is UP. +void TestGnmiCheckInterfaceStateOperation(thinkit::Switch& sut); + +// Tests that SUT specific port state is UP. +void TestGnmiCheckSpecificInterfaceStateOperation(thinkit::Switch& sut, + std::string if_name); } // namespace pins_test #endif // GOOGLE_TESTS_THINKIT_SANITY_TESTS_H_