Skip to content

Commit

Permalink
Merge branch 'main' into branch_thinkit4
Browse files Browse the repository at this point in the history
  • Loading branch information
bhagatgit authored Jul 22, 2024
2 parents d19782e + ca7d506 commit 871cd35
Show file tree
Hide file tree
Showing 6 changed files with 487 additions and 33 deletions.
10 changes: 7 additions & 3 deletions lib/gnmi/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,25 @@ package(

cc_library(
name = "gnmi_helper",
testonly = 1,
srcs = ["gnmi_helper.cc"],
hdrs = ["gnmi_helper.h"],
deps = [
"//gutil:status",
"//gutil:status_matchers",
"//p4_pdpi:p4_runtime_session",
"//thinkit:ssh_client",
"//thinkit:switch",
"@com_github_gnmi//proto/gnmi:gnmi_cc_proto",
"@com_github_gnmi//proto/gnmi:gnmi_cc_grpc_proto",
"@com_github_gnmi//proto/gnmi_ext:gnmi_ext_cc_proto",
"@com_github_google_glog//:glog",
"@com_github_nlohmann_json//:nlohmann_json",
"@com_github_p4lang_p4runtime//:p4runtime_cc_grpc",
"@com_google_absl//absl/numeric:int128",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest",
"@com_google_protobuf//:protobuf_lite",
"@com_googlesource_code_re2//:re2",
],
)
Expand All @@ -50,10 +50,14 @@ cc_test(
srcs = ["gnmi_helper_test.cc"],
deps = [
":gnmi_helper",
"//gutil:proto",
"//gutil:proto_matchers",
"//gutil:status_matchers",
"//gutil:testing",
"@com_github_gnmi//proto/gnmi:gnmi_cc_proto",
"@com_github_gnmi//proto/gnmi:gnmi_cc_grpc_proto",
"@com_github_p4lang_p4runtime//:p4runtime_cc_grpc",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
],
Expand Down
150 changes: 128 additions & 22 deletions lib/gnmi/gnmi_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,29 @@

#include "lib/gnmi/gnmi_helper.h"

#include <cctype>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "absl/numeric/int128.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/match.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/strings/str_format.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"

#include "absl/strings/strip.h"
#include "absl/time/time.h"
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "google/protobuf/map.h"
#include "grpcpp/impl/codegen/client_context.h"
#include "grpcpp/impl/codegen/string_ref.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 "proto/gnmi/gnmi.pb.h"
#include "re2/re2.h"
#include "include/nlohmann/json.hpp"
Expand Down Expand Up @@ -149,7 +148,7 @@ absl::StatusOr<std::string> ParseGnmiGetResponse(
const auto match_tag_json = resp_json.find(match_tag);
if (match_tag_json == resp_json.end()) {
return gutil::InternalErrorBuilder().LogError()
<< match_tag << "not present in JSON response "
<< match_tag << " not present in JSON response "
<< response.ShortDebugString();
}
return match_tag_json->dump();
Expand Down Expand Up @@ -253,24 +252,31 @@ absl::Status CheckAllInterfaceUpOverGnmi(gnmi::gNMI::Stub& stub,

std::vector<std::string> unavailable_interfaces;
for (auto const& element : oc_intf_list_json->items()) {
auto const element_name_json = element.value().find("name");
const auto element_name_json = element.value().find("name");
if (element_name_json == element.value().end()) {
return absl::NotFoundError(
absl::StrCat("'name' not found: ", element.value().dump()));
}
auto const element_interface_state_json = element.value().find("state");
std::string name = std::string(StripQuotes(element_name_json->dump()));

// TODO: Remove once CpuX contains the oper-state subtree.
if (absl::StartsWith(name, "Cpu")) {
LOG(INFO) << "Skipping " << name << ".";
continue;
}

const auto element_interface_state_json = element.value().find("state");
if (element_interface_state_json == element.value().end()) {
return absl::NotFoundError(absl::StrCat(
"'state' not found: ", element.value().find("name")->dump()));
return absl::NotFoundError(absl::StrCat("'state' not found: ", name));
}
auto const element_status_json =
const auto element_status_json =
element_interface_state_json->find("oper-status");
if (element_status_json == element_interface_state_json->end()) {
return absl::NotFoundError(absl::StrCat(
"'oper-status' not found: ", element.value().find("name")->dump()));
return absl::NotFoundError(
absl::StrCat("'oper-status' not found: ", name));
}
if (element_status_json->dump().find("UP") == grpc::string_ref::npos) {
unavailable_interfaces.push_back(element.value().find("name")->dump());
if (!absl::StrContains(element_status_json->dump(), "UP")) {
unavailable_interfaces.push_back(name);
}
}
if (!unavailable_interfaces.empty()) {
Expand All @@ -282,7 +288,7 @@ absl::Status CheckAllInterfaceUpOverGnmi(gnmi::gNMI::Stub& stub,
}

absl::StatusOr<std::string> GetGnmiStatePathInfo(
gnmi::gNMI::Stub* sut_gnmi_stub, absl::string_view state_path,
gnmi::gNMI::StubInterface* sut_gnmi_stub, absl::string_view state_path,
absl::string_view resp_parse_str) {
ASSIGN_OR_RETURN(gnmi::GetRequest request,
BuildGnmiGetRequest(state_path, gnmi::GetRequest::STATE));
Expand Down Expand Up @@ -373,4 +379,104 @@ absl::StatusOr<OperStatus> GetInterfaceOperStatusOverGnmi(
return OperStatus::kUnknown;
}

absl::StatusOr<std::vector<std::string>> ParseAlarms(
const std::string& alarms_json) {
auto alarms_array = json::parse(alarms_json);
if (!alarms_array.is_array()) {
return absl::InvalidArgumentError(
"Input JSON should be an array of alarms.");
}

std::vector<std::string> alarm_messages;
for (const auto& alarm : alarms_array) {
auto state = alarm.find("state");
if (state == alarm.end()) {
return absl::InvalidArgumentError(
"Input JSON alarm does not have a state field.");
}

// The state of an alarm will look like:
// {
// "id": ...
// "resource": "linkqual:linkqual"
// "severity": "openconfig-alarm-types:WARNING"
// "text": "INACTIVE: Unknown"
// "time-created": ...
// "type-id": "Software Error"
// }
//
// We can build an error message to look like (missing fields will be
// omitted):
// [linkqual:linkqual WARNING] Software Error INACTIVE: Unknown
std::string message = "[";
auto resource = state->find("resource");
if (resource != state->end()) {
absl::StrAppend(&message, StripQuotes(resource->dump()), " ");
}
auto severity = state->find("severity");
if (severity != state->end()) {
// We only need the last part.
std::vector<std::string> parts =
absl::StrSplit(StripQuotes(severity->dump()), ':');
absl::StrAppend(&message, parts.back());
}
absl::StrAppend(&message, "] ");
auto type_id = state->find("type-id");
if (type_id != state->end()) {
absl::StrAppend(&message, StripQuotes(type_id->dump()), " ");
}
auto text = state->find("text");
if (text != state->end()) {
absl::StrAppend(&message, StripQuotes(text->dump()));
}
alarm_messages.push_back(std::move(message));
}
return alarm_messages;
}

absl::StatusOr<std::vector<std::string>> GetAlarms(
gnmi::gNMI::StubInterface& gnmi_stub) {
ASSIGN_OR_RETURN(
gnmi::GetRequest request,
BuildGnmiGetRequest("system/alarms", gnmi::GetRequest::STATE));
LOG(INFO) << "Sending GET request: " << request.ShortDebugString();
gnmi::GetResponse response;
grpc::ClientContext context;
grpc::Status status = gnmi_stub.Get(&context, request, &response);
if (!status.ok()) {
return gutil::GrpcStatusToAbslStatus(status);
}

LOG(INFO) << "Received GET response: " << response.ShortDebugString();
if (response.notification_size() != 1) {
return gutil::InternalErrorBuilder().LogError()
<< "Unexpected size in response (should be 1): "
<< response.notification_size();
}
if (response.notification(0).update_size() != 1) {
return gutil::InternalErrorBuilder().LogError()
<< "Unexpected update size in response (should be 1): "
<< response.notification(0).update_size();
}

const auto response_json =
json::parse(response.notification(0).update(0).val().json_ietf_val());
const auto alarms_json = response_json.find("openconfig-system:alarms");
// If alarms returns an empty subtree, assume no alarms and return an empty
// list.
if (alarms_json == response_json.end()) {
return std::vector<std::string>();
}

const auto alarm_json = alarms_json->find("alarm");
if (alarm_json == alarms_json->end()) {
return std::vector<std::string>();
}
return ParseAlarms(alarm_json->dump());
}

absl::string_view StripQuotes(absl::string_view string) {
return absl::StripPrefix(absl::StripSuffix(string, "\""), "\"");
}

} // namespace pins_test
18 changes: 16 additions & 2 deletions lib/gnmi/gnmi_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
#ifndef GOOGLE_LIB_GNMI_GNMI_HELPER_H_
#define GOOGLE_LIB_GNMI_GNMI_HELPER_H_

#include <cstddef>
#include <string>
#include <type_traits>
#include <vector>

#include "absl/numeric/int128.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
Expand Down Expand Up @@ -65,7 +67,7 @@ absl::Status SetGnmiConfigPath(gnmi::gNMI::Stub* sut_gnmi_stub,
GnmiSetType operation, absl::string_view value);

absl::StatusOr<std::string> GetGnmiStatePathInfo(
gnmi::gNMI::Stub* sut_gnmi_stub, absl::string_view state_path,
gnmi::gNMI::StubInterface* sut_gnmi_stub, absl::string_view state_path,
absl::string_view resp_parse_str);

template <class T>
Expand Down Expand Up @@ -118,5 +120,17 @@ gnmi::Path ConvertOCStringToPath(absl::string_view oc_path);
absl::StatusOr<OperStatus> GetInterfaceOperStatusOverGnmi(
gnmi::gNMI::Stub& stub, absl::string_view if_name);

// Parses the alarms JSON array returned from a gNMI Get request to
// "openconfig-system:system/alarms/alarm". Returns the list of alarms.
absl::StatusOr<std::vector<std::string>> ParseAlarms(
const std::string& alarms_json);

// Gets alarms over gNMI.
absl::StatusOr<std::vector<std::string>> GetAlarms(
gnmi::gNMI::StubInterface& gnmi_stub);

// Strips the beginning and ending double-quotes from the `string`.
absl::string_view StripQuotes(absl::string_view string);

} // namespace pins_test
#endif // GOOGLE_LIB_GNMI_GNMI_HELPER_H_
Loading

0 comments on commit 871cd35

Please sign in to comment.