diff --git a/lib/gnmi/gnmi_helper.cc b/lib/gnmi/gnmi_helper.cc index 959acad8..784dd1a9 100644 --- a/lib/gnmi/gnmi_helper.cc +++ b/lib/gnmi/gnmi_helper.cc @@ -22,6 +22,14 @@ #include "absl/strings/match.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/strings/str_format.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" + #include "absl/time/time.h" #include "glog/logging.h" #include "gmock/gmock.h" @@ -166,7 +174,7 @@ absl::Status SetGnmiConfigPath(gnmi::gNMI::Stub* sut_gnmi_stub, } absl::Status PushGnmiConfig(gnmi::gNMI::Stub& stub, - const std::string& chassis_name, + absl::string_view chassis_name, const std::string& gnmi_config, absl::uint128 election_id) { gnmi::SetRequest req; @@ -191,6 +199,13 @@ absl::Status PushGnmiConfig(gnmi::gNMI::Stub& stub, return absl::OkStatus(); } +absl::Status PushGnmiConfig(thinkit::Switch& chassis, + const std::string& gnmi_config) { + ASSIGN_OR_RETURN(std::unique_ptr stub, + chassis.CreateGnmiStub()); + return pins_test::PushGnmiConfig(*stub, chassis.ChassisName(), gnmi_config); +} + absl::Status CheckAllInterfaceUpOverGnmi(gnmi::gNMI::Stub& stub) { ASSIGN_OR_RETURN(auto req, BuildGnmiGetRequest("interfaces", gnmi::GetRequest::STATE)); @@ -303,4 +318,40 @@ GnmiGetElementFromTelemetryResponse(const gnmi::SubscribeResponse& response) { return elements; } +absl::StatusOr 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) { + return absl::InternalError( + absl::StrCat("Invalid response: ", response.DebugString())); + } + 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; + } + if (absl::StrContains((oper_status), "DOWN")) { + return OperStatus::kDown; + } + if (absl::StrContains((oper_status), "TESTING")) { + return OperStatus::kTesting; + } + return OperStatus::kUnknown; +} + } // namespace pins_test diff --git a/lib/gnmi/gnmi_helper.h b/lib/gnmi/gnmi_helper.h index 8edefd16..0f40dd12 100644 --- a/lib/gnmi/gnmi_helper.h +++ b/lib/gnmi/gnmi_helper.h @@ -23,6 +23,7 @@ #include "p4_pdpi/p4_runtime_session.h" #include "proto/gnmi/gnmi.grpc.pb.h" #include "proto/gnmi/gnmi.pb.h" +#include "thinkit/switch.h" namespace pins_test { @@ -31,6 +32,13 @@ inline constexpr char kTarget[] = "target"; enum class GnmiSetType : char { kUpdate, kReplace, kDelete }; +enum class OperStatus { + kUnknown, + kUp, + kDown, + kTesting, +}; + // Builds gNMI Set Request for a given OC path, set type and set value. // The path should be in the following format below. // "interfaces/interface[Ethernet0]/config/mtu". @@ -83,13 +91,21 @@ absl::StatusOr> GnmiGetElementFromTelemetryResponse(const gnmi::SubscribeResponse& response); absl::Status PushGnmiConfig( - gnmi::gNMI::Stub& stub, const std::string& chassis_name, + gnmi::gNMI::Stub& stub, absl::string_view chassis_name, const std::string& gnmi_config, absl::uint128 election_id = pdpi::TimeBasedElectionId()); +absl::Status PushGnmiConfig(thinkit::Switch& chassis, + const std::string& gnmi_config); + absl::Status CheckAllInterfaceUpOverGnmi(gnmi::gNMI::Stub& stub); // Returns gNMI Path for OC strings. gnmi::Path ConvertOCStringToPath(absl::string_view oc_path); + +// Gets the operational status of an interface. +absl::StatusOr GetInterfaceOperStatusOverGnmi( + gnmi::gNMI::Stub& stub, absl::string_view if_name); + } // namespace pins_test #endif // GOOGLE_LIB_GNMI_GNMI_HELPER_H_ diff --git a/thinkit/mirror_testbed_fixture.h b/thinkit/mirror_testbed_fixture.h index 2f86b575..4638486d 100644 --- a/thinkit/mirror_testbed_fixture.h +++ b/thinkit/mirror_testbed_fixture.h @@ -36,6 +36,13 @@ class MirrorTestbedInterface { virtual MirrorTestbed& GetMirrorTestbed() = 0; }; +// The Thinkit `TestParams` defines test parameters to +// `MirrorTestbedFixture` class. +struct TestParams { + MirrorTestbedInterface* mirror_testbed; + std::string gnmi_config; +}; + // The ThinKit `MirrorTestbedFixture` class acts as a base test fixture for // platform independent PINS tests. Any platform specific SetUp or TearDown // requirements are abstracted through the ThinKit MirrorTestbedInterface which @@ -63,8 +70,7 @@ class MirrorTestbedInterface { // Individual tests should use the new suite name to take advantage of the // custom setup/teardown: // TEST_P(MyPinsTest, MyTestName) {} -class MirrorTestbedFixture - : public testing::TestWithParam { +class MirrorTestbedFixture : public testing::TestWithParam { protected: // A derived class that needs/wants to do its own setup can override this // method. However, it should take care to call this base setup first. That @@ -83,10 +89,12 @@ class MirrorTestbedFixture return mirror_testbed_interface_->GetMirrorTestbed(); } + std::string GetGnmiConfig() { return GetParam().gnmi_config; } + private: // Takes ownership of the MirrorTestbedInterface parameter. std::unique_ptr mirror_testbed_interface_ = - absl::WrapUnique(GetParam()); + absl::WrapUnique(GetParam().mirror_testbed); }; } // namespace thinkit