sync code

This commit is contained in:
Ned Wright
2024-09-17 10:53:09 +00:00
parent 3fe0b42fcd
commit 586150fe4f
143 changed files with 1886 additions and 380 deletions

View File

@@ -30,6 +30,7 @@ add_subdirectory(version)
add_subdirectory(tenant_manager)
add_subdirectory(compression)
add_subdirectory(attachments)
add_subdirectory(report_messaging)
add_library(ngen_core SHARED ".")
target_link_libraries(
@@ -38,6 +39,7 @@ target_link_libraries(
"table;debug_is;shell_cmd;metric;tenant_manager;messaging;encryptor;time_proxy;singleton;mainloop;environment;logging;report;rest"
"config;intelligence_is_v2;event_is;memory_consumption;connkey"
"instance_awareness;socket_is;agent_details;agent_details_reporter;buffers;cpu;agent_core_utilities"
"report_messaging"
-Wl,-no-whole-archive
)

View File

@@ -183,6 +183,8 @@ copyFile(const string &src, const string &dest, bool overide_if_exists, mode_t p
}
dbgTrace(D_INFRA_UTILS) << "Finished attempt to copy file. Res: " << (bytes_copied != -1 ? "Success" : "Error");
close(source_fd);
close(dest_fd);
return bytes_copied != -1;
}
@@ -261,6 +263,66 @@ touchFile(const string &path)
return true;
}
bool
copyDirectory(const string &src_dir_path, const string &dst_dir_path)
{
dbgFlow(D_INFRA_UTILS)
<< "Trying to copy directory. Source: "
<< src_dir_path
<< ", Destination: "
<< dst_dir_path;
if (!exists(src_dir_path) || !isDirectory(src_dir_path)) {
dbgDebug(D_INFRA_UTILS) << "Failed to copy directory. Error: source directory does not exist";
return false;
}
if (!exists(dst_dir_path)) {
if (!makeDir(dst_dir_path, 0755)) {
dbgDebug(D_INFRA_UTILS) << "Failed to copy directory. Error: failed to create destination directory";
return false;
}
}
if (!isDirectory(dst_dir_path)) {
dbgDebug(D_INFRA_UTILS) << "Failed to copy directory. Error: destination path is not a directory";
return false;
}
struct dirent *entry = nullptr;
DIR *directory = opendir(src_dir_path.c_str());
if (!directory) {
dbgWarning(D_INFRA_UTILS) << "Fail to open directory. Path: " << src_dir_path;
return false;
}
while ((entry = readdir(directory))) {
string entry_name = entry->d_name;
static const string curr_dir(".");
static const string parent_dir("..");
if (entry_name == curr_dir || entry_name == parent_dir) {
dbgTrace(D_INFRA_UTILS) << "Skipping irrelevant directory entries. Entry name: " << entry_name;
continue;
}
string src_entry_path = src_dir_path + (src_dir_path.back() == '/' ? "" : "/") + entry_name;
string dst_entry_path = dst_dir_path + (dst_dir_path.back() == '/' ? "" : "/") + entry_name;
struct stat statbuf;
if (!stat(src_entry_path.c_str(), &statbuf)) {
if (S_ISDIR(statbuf.st_mode)) {
copyDirectory(src_entry_path, dst_entry_path);
} else {
copyFile(src_entry_path, dst_entry_path, true);
}
}
}
closedir(directory);
return true;
}
string
convertToHumanReadable(uint64_t size_in_bytes)
{

View File

@@ -10,6 +10,8 @@
using namespace std;
using namespace testing;
USE_DEBUG_FLAG(D_INFRA_UTILS);
auto contexts = make_pair(std::vector<Context *>(), false);
class AgentCoreUtilUT : public Test
@@ -17,6 +19,7 @@ class AgentCoreUtilUT : public Test
public:
AgentCoreUtilUT()
{
Debug::setUnitTestFlag(D_INFRA_UTILS, Debug::DebugLevel::TRACE);
ON_CALL(mock_env, getActiveContexts()).WillByDefault(ReturnPointee(&contexts));
}
@@ -123,6 +126,59 @@ TEST_F(AgentCoreUtilUT, isDirectoryTest)
EXPECT_EQ(NGEN::Filesystem::isDirectory("./test"), true);
}
TEST_F(AgentCoreUtilUT, copyDirectoryTest)
{
string sourceDir = cptestFnameInExeDir("sourceDir1");
string destDir = cptestFnameInExeDir("destDir1");
cout << "sourceDir: " << sourceDir << endl;
NGEN::Filesystem::makeDir(sourceDir);
NGEN::Filesystem::makeDir(sourceDir + "/subdir1");
NGEN::Filesystem::makeDir(sourceDir + "/subdir2");
NGEN::Filesystem::makeDir(destDir);
ofstream file_1(sourceDir + "/file1.txt");
ASSERT_TRUE(file_1.is_open());
file_1 << "File 1 content";
file_1.close();
ofstream file_2(sourceDir + "/subdir1/file2.txt");
ASSERT_TRUE(file_2.is_open());
file_2 << "File 2 content";
file_2.close();
ofstream file_3(sourceDir + "/subdir2/file3.txt");
ASSERT_TRUE(file_3.is_open());
file_3 << "File 3 content";
file_3.close();
ASSERT_TRUE(NGEN::Filesystem::copyDirectory(sourceDir, destDir));
EXPECT_TRUE(NGEN::Filesystem::exists(destDir));
EXPECT_TRUE(NGEN::Filesystem::exists(destDir + "/file1.txt"));
EXPECT_TRUE(NGEN::Filesystem::exists(destDir + "/subdir1/file2.txt"));
EXPECT_TRUE(NGEN::Filesystem::exists(destDir + "/subdir2/file3.txt"));
ifstream file1(destDir + "/file1.txt");
string content1((istreambuf_iterator<char>(file1)), istreambuf_iterator<char>());
EXPECT_EQ(content1, "File 1 content");
file1.close();
ifstream file2(destDir + "/subdir1/file2.txt");
string content2((istreambuf_iterator<char>(file2)), istreambuf_iterator<char>());
EXPECT_EQ(content2, "File 2 content");
file2.close();
ifstream file3(destDir + "/subdir2/file3.txt");
string content3((istreambuf_iterator<char>(file3)), istreambuf_iterator<char>());
EXPECT_EQ(content3, "File 3 content");
file3.close();
NGEN::Filesystem::deleteDirectory(sourceDir + "/subdir1", true);
NGEN::Filesystem::deleteDirectory(sourceDir + "/subdir2", true);
NGEN::Filesystem::deleteDirectory(sourceDir, true);
NGEN::Filesystem::deleteDirectory(destDir + "/subdir1", true);
NGEN::Filesystem::deleteDirectory(destDir + "/subdir2", true);
NGEN::Filesystem::deleteDirectory(destDir, true);
}
TEST_F(AgentCoreUtilUT, removeTrailingWhitespacesTest)
{
string str_with_trailing_whitespace = "str_with_trailing_whitespace\n\n\n\r \n\n\r";

View File

@@ -27,6 +27,7 @@ using namespace std;
USE_DEBUG_FLAG(D_ORCHESTRATOR);
static const AlertInfo alert(AlertTeam::CORE, "agent details");
const map<string, I_AgentDetails::MachineType> AgentDetails::machineTypes({
{ "Amazon EC2", I_AgentDetails::MachineType::AWS },
@@ -381,7 +382,7 @@ AgentDetails::convertProxyProtocolToString(ProxyProtocol proto) const
case ProxyProtocol::HTTP: return "http";
case ProxyProtocol::HTTPS: return "https";
}
dbgAssert(false) << "Unsupported Proxy Protocol " << static_cast<int>(proto);
dbgAssert(false) << alert << "Unsupported Proxy Protocol " << static_cast<int>(proto);
return "";
}
@@ -469,7 +470,9 @@ AgentDetails::loadProxyType(ProxyProtocol protocol)
{
dbgFlow(D_ORCHESTRATOR) << "Loading proxy type: " << convertProxyProtocolToString(protocol);
dbgAssert(protocol == ProxyProtocol::HTTP || protocol == ProxyProtocol::HTTPS)
<< "Unsupported Proxy Protocol " << static_cast<int>(protocol);
<< alert
<< "Unsupported Proxy Protocol "
<< static_cast<int>(protocol);
static const map<ProxyProtocol, string> env_var_name = {
{ProxyProtocol::HTTPS, "https_proxy"},

View File

@@ -17,6 +17,8 @@
using namespace std;
static const AlertInfo alert(AlertTeam::CORE, "buffer i/s");
Buffer::Buffer(vector<u_char> &&vec)
:
len(vec.size())
@@ -142,7 +144,7 @@ Buffer::operator+(const Buffer &other) const
Buffer
Buffer::getSubBuffer(uint start, uint end) const
{
dbgAssert(start<=end && end<=len) << "Buffer::getSubBuffer() returned: Illegal scoping of buffer";
dbgAssert(start<=end && end<=len) << alert << "Buffer::getSubBuffer() returned: Illegal scoping of buffer";
if (start == end) return Buffer();
Buffer res;
@@ -176,7 +178,7 @@ Buffer::getSubBuffer(uint start, uint end) const
Maybe<uint>
Buffer::findFirstOf(char ch, uint start) const
{
dbgAssert(start <= len) << "Buffer::findFirstOf() returned: Cannot set a start point after buffer's end";
dbgAssert(start <= len) << alert << "Buffer::findFirstOf() returned: Cannot set a start point after buffer's end";
for (; start < len; ++start) {
if ((*this)[start] == ch) return start;
@@ -187,7 +189,7 @@ Buffer::findFirstOf(char ch, uint start) const
Maybe<uint>
Buffer::findFirstOf(const Buffer &buf, uint start) const
{
dbgAssert(start <= len) << "Buffer::findFirstOf() returned: Cannot set a start point after buffer's end";
dbgAssert(start <= len) << alert << "Buffer::findFirstOf() returned: Cannot set a start point after buffer's end";
for (; start + buf.size() <= len; ++start) {
auto sub_buffer = getSubBuffer(start, start + buf.size());
@@ -199,7 +201,9 @@ Buffer::findFirstOf(const Buffer &buf, uint start) const
Maybe<uint>
Buffer::findFirstNotOf(char ch, uint start) const
{
dbgAssert(start <= len) << "Buffer::findFirstNotOf() returned: Cannot set a start point after buffer's end";
dbgAssert(start <= len)
<< alert
<< "Buffer::findFirstNotOf() returned: Cannot set a start point after buffer's end";
for (; start < len; ++start) {
if ((*this)[start] != ch) return start;
}
@@ -209,7 +213,7 @@ Buffer::findFirstNotOf(char ch, uint start) const
Maybe<uint>
Buffer::findLastOf(char ch, uint start) const
{
dbgAssert(start <= len) << "Buffer::findLastOf() returned: Cannot set a start point after buffer's end";
dbgAssert(start <= len) << alert << "Buffer::findLastOf() returned: Cannot set a start point after buffer's end";
for (; 0 < start; --start) {
if ((*this)[start - 1] == ch) return start - 1;
}
@@ -219,7 +223,9 @@ Buffer::findLastOf(char ch, uint start) const
Maybe<uint>
Buffer::findLastNotOf(char ch, uint start) const
{
dbgAssert(start <= len) << "Buffer::findLastNotOf() returned: Cannot set a start point after buffer's end";
dbgAssert(start <= len)
<< alert
<< "Buffer::findLastNotOf() returned: Cannot set a start point after buffer's end";
for (; 0 < start; --start) {
if ((*this)[start - 1] != ch) return start - 1;
}
@@ -229,7 +235,7 @@ Buffer::findLastNotOf(char ch, uint start) const
void
Buffer::truncateHead(uint size)
{
dbgAssert(size <= len) << "Cannot set a new start of buffer after the buffer's end";
dbgAssert(size <= len) << alert << "Cannot set a new start of buffer after the buffer's end";
if (size == 0) return;
if (size == len) {
clear();
@@ -255,7 +261,7 @@ Buffer::truncateHead(uint size)
void
Buffer::truncateTail(uint size)
{
dbgAssert(size <= len) << "Cannot set a new end of buffer after the buffer's end";
dbgAssert(size <= len) << alert << "Cannot set a new end of buffer after the buffer's end";
if (size == 0) return;
if (size == len) {
clear();
@@ -279,14 +285,14 @@ Buffer::truncateTail(uint size)
void
Buffer::keepHead(uint size)
{
dbgAssert(size <= len) << "Cannot set a new end of buffer before the buffer's start";
dbgAssert(size <= len) << alert << "Cannot set a new end of buffer before the buffer's start";
truncateTail(len - size);
}
void
Buffer::keepTail(uint size)
{
dbgAssert(size <= len) << "Cannot set a new start of buffer after the buffer's end";
dbgAssert(size <= len) << alert << "Cannot set a new start of buffer after the buffer's end";
truncateHead(len - size);
}
@@ -376,7 +382,7 @@ Buffer::operator[](uint offset) const
}
return fast_path_ptr[offset];
}
dbgAssert(offset < len) << "Buffer::operator returned: attempted an access outside the buffer";
dbgAssert(offset < len) << alert << "Buffer::operator returned: attempted an access outside the buffer";
return *(begin() + offset);
}

View File

@@ -67,10 +67,11 @@ Buffer::CharIterator::operator==(const CharIterator &other) const
return (cur_seg == other.cur_seg) && (offset == other.offset);
}
static const AlertInfo alert(AlertTeam::CORE, "buffer i/s");
const u_char &
Buffer::CharIterator::operator*() const
{
dbgAssert(ptr != nullptr) << "Buffer::CharIterator is not pointing to a real value";
dbgAssert(ptr != nullptr) << alert << "Buffer::CharIterator is not pointing to a real value";
return ptr[offset];
}

View File

@@ -25,7 +25,10 @@ public:
);
setCompressionDebugFunction(
CompressionUtilsDebugLevel::COMPRESSION_DBG_LEVEL_ASSERTION,
[](const char *assert_message) { dbgAssert(false) << assert_message; }
[](const char *assert_message)
{
dbgAssert(false) << AlertInfo(AlertTeam::CORE, "testing") << assert_message;
}
);
}

View File

@@ -53,6 +53,7 @@ operator<<(ostream &os, const IPType &t)
return os << "Invalid(" << static_cast<uint>(t) << ")";
}
static const AlertInfo alert(AlertTeam::CORE, "IP address");
// Format an IP address. Use a pair, becuase it depends on the type (v4/v6)
ostream &
IPAddr::print(ostream &os) const
@@ -63,12 +64,12 @@ IPAddr::print(ostream &os) const
switch (type) {
case IPType::V4: {
formatted_addr = inet_ntop(AF_INET, &v4, buf, sizeof(buf));
dbgAssert(formatted_addr == buf) << "Failed to convert an IPv4 address";
dbgAssert(formatted_addr == buf) << alert("conversion error") << "Failed to convert an IPv4 address";
break;
}
case IPType::V6: {
formatted_addr = inet_ntop(AF_INET6, &v6, buf, sizeof(buf));
dbgAssert(formatted_addr == buf) << "Failed to convert an IPv6 address";
dbgAssert(formatted_addr == buf) << alert("conversion error") << "Failed to convert an IPv6 address";
break;
}
case IPType::UNINITIALIZED: {
@@ -115,8 +116,10 @@ ConnKey::reverse()
size_t
ConnKey::hash() const
{
dbgAssert(src.type != IPType::UNINITIALIZED) << "ConnKey::hash was called on an uninitialized object";
size_t seed = 0; // XXX: random seed for security?
dbgAssert(src.type != IPType::UNINITIALIZED)
<< alert("hashing")
<< "ConnKey::hash was called on an uninitialized object";
size_t seed = 0;
hashCombine(seed, static_cast<u_char>(src.type));
hashCombine(seed, src.proto);
hashCombine(seed, src);

View File

@@ -80,7 +80,11 @@ cptestParseHex(const string &hex_text)
size_t l = t.size();
if (l==0) continue;
if (t[l-1]==':') continue; // tcpdump uses xxxx: to mark offsets, not data. So ignore it.
dbgAssert(t.size() %2 == 0) << "Expecting an even number of hex digits, " << t << " is invalid";
dbgAssert(t.size() %2 == 0)
<< AlertInfo(AlertTeam::CORE, "testing")
<< "Expecting an even number of hex digits, "
<< t
<< " is invalid";
for (uint i=0; i<t.size()/2; i++) {
u_char n = strtoul(t.substr(i*2, 2).c_str(), nullptr, 16);
v.push_back(n);

View File

@@ -415,7 +415,7 @@ TCPPacket::Impl::emit_tcp_options(vector<u_char> &pkt) const
while (optbuf.size()%4 != 0) {
vec_append(optbuf, TCPOption::NOP);
}
dbgAssert(optbuf.size() <= 40) << "too many tcp options. max is 40 bytes";
dbgAssert(optbuf.size() <= 40) << AlertInfo(AlertTeam::CORE, "testing") << "too many tcp options. max is 40 bytes";
vec_append(pkt, optbuf);
}

View File

@@ -8,7 +8,7 @@ TEST(CPTest, PrepareToDie)
{
cptestPrepareToDie();
auto die = []() {
dbgAssert(false) << "You killed my father";
dbgAssert(false) << AlertInfo(AlertTeam::CORE, "testing") << "You killed my father";
};
EXPECT_DEATH(die(), "You killed my father");
}

View File

@@ -26,6 +26,7 @@
#include "config.h"
#include "i_instance_awareness.h"
#include "i_signal_handler.h"
#include "hash_combine.h"
using namespace std;
@@ -283,6 +284,16 @@ private:
S2C_PARAM(string, output);
};
void
AlertInfo::evalParams()
{
id = 0;
hashCombine(id, family_id);
hashCombine(id, functionality);
hashCombine(id, description);
hashCombine(id, static_cast<size_t>(team));
}
// LCOV_EXCL_START - function is covered in unit-test, but not detected bt gcov
Debug::Debug(
const string &file_name,
@@ -830,6 +841,14 @@ Debug::isCommunicationFlag(const DebugFlags &flag)
);
}
void
Debug::sendAlert(const AlertInfo &alert)
{
for (auto &added_stream : current_active_streams) {
added_stream->sendAlert(alert);
}
}
Debug::DebugLevel Debug::lowest_global_level = default_level;
I_TimeGet *Debug::time = nullptr;
I_MainLoop *Debug::mainloop = nullptr;

View File

@@ -55,6 +55,7 @@ public:
);
virtual void finishMessage() { *stream << std::endl; }
virtual void sendAlert(const AlertInfo &) {}
std::ostream * getStream() const { return stream; }
@@ -112,6 +113,7 @@ public:
) override;
void finishMessage() override;
void sendAlert(const AlertInfo &_alert) override { possible_alert = _alert; }
private:
void sendBufferedMessages();
@@ -133,6 +135,7 @@ private:
std::string trace_id;
std::string span_id;
uint line;
Maybe<AlertInfo, void> possible_alert = genError("");
};
#endif // __DEBUG_EX_H__

View File

@@ -39,11 +39,32 @@ void doPMExecTrace() { dbgTrace(D_PM_EXEC) << "PM_EXEC trace message"; line = to
template <typename ...Args> void doManyFlags(Args ...args) { dbgDebug(args...) << "stab"; line = to_string(__LINE__); }
TEST(DebugBaseTest, alert_obkect)
{
AlertInfo alert1(AlertTeam::CORE, "testing");
EXPECT_EQ(alert1.getTeam(), AlertTeam::CORE);
EXPECT_EQ(alert1.getFunctionality(), "testing");
EXPECT_EQ(alert1.getDescription(), "");
EXPECT_EQ(alert1.getFamilyId(), 0u);
EXPECT_NE(alert1.getId(), 0u);
auto alert2 = alert1("additional data", 5);
EXPECT_EQ(alert2.getTeam(), AlertTeam::CORE);
EXPECT_EQ(alert2.getFunctionality(), "testing");
EXPECT_EQ(alert2.getDescription(), "additional data");
EXPECT_EQ(alert2.getFamilyId(), 5u);
EXPECT_NE(alert2.getId(), 0u);
EXPECT_NE(alert1.getId(), alert2.getId());
}
TEST(DebugBaseTest, death_on_panic)
{
cptestPrepareToDie();
EXPECT_DEATH(dbgAssert(1==2) << "Does your school teach otherwise?", "Does your school teach otherwise?");
EXPECT_DEATH(
dbgAssert(1==2) << AlertInfo(AlertTeam::CORE, "testing") << "Does your school teach otherwise?",
"Does your school teach otherwise?"
);
}
TEST(DebugBaseTest, default_levels)
@@ -1014,7 +1035,7 @@ TEST(DebugFogTest, fog_stream)
" \"agentId\": \"Unknown\",\n"
" \"issuingFunction\": \"handleThresholdReach\",\n"
" \"issuingFile\": \"debug_streams.cc\",\n"
" \"issuingLine\": 344,\n"
" \"issuingLine\": 364,\n"
" \"eventTraceId\": \"\",\n"
" \"eventSpanId\": \"\",\n"
" \"issuingEngineVersion\": \"\",\n"
@@ -1117,3 +1138,123 @@ TEST(DebugFogTest, fog_stream)
EXPECT_CALL(mock_mainloop, stop(0));
Debug::fini();
}
TEST(DebugFogTest, alert_fog_stream)
{
ConfigComponent conf;
::Environment env;
env.preload();
env.init();
stringstream capture_debug;
conf.preload();
StrictMock<MockMainLoop> mock_mainloop;
StrictMock<MockTimeGet> mock_time;
NiceMock<MockAgentDetails> mock_agent_details;
ON_CALL(mock_agent_details, getFogDomain()).WillByDefault(Return(Maybe<string>(string("fog_domain.com"))));
ON_CALL(mock_agent_details, getFogPort()).WillByDefault(Return(Maybe<uint16_t>(443)));
EXPECT_CALL(mock_agent_details, getAgentId()).WillRepeatedly(Return("Unknown"));
EXPECT_CALL(mock_agent_details, getOrchestrationMode()).WillRepeatedly(Return(OrchestrationMode::ONLINE));
EXPECT_CALL(mock_time, getWalltimeStr(_)).WillRepeatedly(Return(string("2016-11-13T17:31:24.087")));
I_MainLoop::Routine send_debug_routine = nullptr;
EXPECT_CALL(mock_mainloop, addRecurringRoutine(_, _, _, _, _))
.WillOnce(DoAll(SaveArg<2>(&send_debug_routine), Return(0)));
StrictMock<MockMessaging> messaging_mock;
string message_body;
EXPECT_CALL(messaging_mock, sendAsyncMessage(
_,
"/api/v1/agents/events/bulk",
_,
_,
_,
_
)).WillRepeatedly(SaveArg<2>(&message_body));
Singleton::Consume<Config::I_Config>::from(conf)->loadConfiguration(
vector<string>{"--orchestration-mode=online_mode"}
);
Debug::preload();
string config_json =
"{"
" \"Debug I/S\": {"
" \"Sent debug bulk size\": ["
" {"
" \"value\": 2"
" }"
" ]"
" },"
" \"Debug\": [{"
" \"Streams\": ["
" {"
" \"Output\": \"FOG\""
" },"
" {"
" \"Output\": \"STDOUT\""
" }"
" ]"
" }]"
"}";
istringstream ss(config_json);
Singleton::Consume<Config::I_Config>::from(conf)->loadConfiguration(ss);
Debug::DebugAlert("MockFile", "MockFunction", 0, Debug::DebugLevel::ERROR, D_FW).getStreamAggr()
<< AlertInfo(AlertTeam::CORE, "testing")
<< "Generic error message";
string expected_message =
"{\n"
" \"logs\": [\n"
" {\n"
" \"id\": 1,\n"
" \"log\": {\n"
" \"eventTime\": \"2016-11-13T17:31:24.087\",\n"
" \"eventName\": \"Debug message\",\n"
" \"eventSeverity\": \"High\",\n"
" \"eventPriority\": \"Low\",\n"
" \"eventType\": \"Code Related\",\n"
" \"eventLevel\": \"Log\",\n"
" \"eventLogLevel\": \"error\",\n"
" \"eventAudience\": \"Internal\",\n"
" \"eventAudienceTeam\": \"\",\n"
" \"eventFrequency\": 0,\n"
" \"eventTags\": [\n"
" \"Informational\"\n"
" ],\n"
" \"eventSource\": {\n"
" \"agentId\": \"Unknown\",\n"
" \"issuingFunction\": \"MockFunction\",\n"
" \"issuingFile\": \"MockFile\",\n"
" \"issuingLine\": 0,\n"
" \"eventTraceId\": \"\",\n"
" \"eventSpanId\": \"\",\n"
" \"issuingEngineVersion\": \"\",\n"
" \"serviceName\": \"Unnamed Nano Service\"\n"
" },\n"
" \"eventData\": {\n"
" \"eventMessage\": \"Generic error message\",\n"
" \"eventId\": 6255310698607853351,\n"
" \"eventFamilyId\": 0,\n"
" \"eventFunctionality\": \"testing\",\n"
" \"eventDescription\": \"\",\n"
" \"eventResponseTeam\": \"Core\"\n"
" }\n"
" }\n"
" }\n"
" ]\n"
"}";
send_debug_routine();
EXPECT_EQ(message_body, expected_message);
EXPECT_CALL(mock_mainloop, doesRoutineExist(0)).WillOnce(Return(true));
EXPECT_CALL(mock_mainloop, stop(0));
Debug::fini();
}

View File

@@ -230,6 +230,18 @@ DebugFogStream::printHeader(
}
}
static string
getTeam(const Maybe<AlertInfo, void> &alert)
{
switch((*alert).getTeam()) {
case AlertTeam::CORE: return "Core";
case AlertTeam::WAAP: return "Waap";
case AlertTeam::SDWAN: return "SDWAN";
case AlertTeam::IOT: return "IoT";
}
return "Core";
}
void
DebugFogStream::finishMessage()
{
@@ -265,6 +277,14 @@ DebugFogStream::finishMessage()
);
message_to_fog << LogField("eventMessage", message.str());
if (possible_alert.ok()) {
message_to_fog << LogField("eventId", (*possible_alert).getId());
message_to_fog << LogField("eventFamilyId", (*possible_alert).getFamilyId());
message_to_fog << LogField("eventFunctionality", (*possible_alert).getFunctionality());
message_to_fog << LogField("eventDescription", (*possible_alert).getDescription());
message_to_fog << LogField("eventResponseTeam", getTeam(possible_alert));
}
if (!getConfigurationWithDefault<bool>(true, "Debug I/S", "Enable bulk of debugs")) {
LogRest rest(move(message_to_fog));
Singleton::Consume<I_MainLoop>::by<Debug>()->addOneTimeRoutine(
@@ -370,18 +390,20 @@ DebugFogStream::getSeverity() const
return Severity::CRITICAL;
}
static const AlertInfo alert(AlertTeam::CORE, "debug configuration");
LogLevel
DebugFogStream::getLogLevel() const
{
switch (level) {
case Debug::DebugLevel::NOISE: dbgAssert(false) << "Impossible LogLevel 'Noise'"; break;
case Debug::DebugLevel::NOISE: dbgAssert(false) << alert << "Impossible LogLevel 'Noise'"; break;
case Debug::DebugLevel::TRACE: return LogLevel::TRACE;
case Debug::DebugLevel::DEBUG: return LogLevel::DEBUG;
case Debug::DebugLevel::WARNING: return LogLevel::WARNING;
case Debug::DebugLevel::INFO: return LogLevel::INFO;
case Debug::DebugLevel::ERROR: return LogLevel::ERROR;
case Debug::DebugLevel::ASSERTION: return LogLevel::ERROR;
case Debug::DebugLevel::NONE: dbgAssert(false) << "Impossible LogLevel 'None'"; break;
case Debug::DebugLevel::NONE: dbgAssert(false) << alert << "Impossible LogLevel 'None'"; break;
}
return LogLevel::INFO;

View File

@@ -42,6 +42,8 @@ Context::getAllStrings(const EnvKeyAttr::ParamAttr &param) const
return result;
}
static const AlertInfo alert(AlertTeam::CORE, "environment contexts");
const std::string
Context::convertToString(MetaDataType type)
{
@@ -58,9 +60,9 @@ Context::convertToString(MetaDataType type)
case MetaDataType::Direction: return "direction";
case MetaDataType::Email: return "email";
case MetaDataType::COUNT:
dbgAssert(false) << "COUNT is not a valid meta data type";
dbgAssert(false) << alert << "COUNT is not a valid meta data type";
}
dbgAssert(false) << "Reached impossible case with type=" << static_cast<int>(type);
dbgAssert(false) << alert << "Reached impossible case with type=" << static_cast<int>(type);
return "";
}

View File

@@ -213,8 +213,9 @@ Environment::Impl::registerContext(Context *ptr)
void
Environment::Impl::unregisterContext(Context *ptr)
{
dbgAssert(active_contexts.first.back() == ptr) <<
"Contexts are supposed to unregister in reverse order to their registration";
dbgAssert(active_contexts.first.back() == ptr)
<< AlertInfo(AlertTeam::CORE, "environment contexts")
<< "Contexts are supposed to unregister in reverse order to their registration";
active_contexts.first.pop_back();
}

View File

@@ -97,7 +97,7 @@ Span::convertSpanContextTypeToString(ContextType type)
return "Follows from";
}
}
dbgAssert(false) << "Span context not supported";
dbgAssert(false) << AlertInfo(AlertTeam::CORE, "tracing") << "Span context not supported";
return string();
}

View File

@@ -32,7 +32,14 @@ public:
}
operator const T *() const { return ptr; }
const T & operator*() const { dbgAssert(ptr != nullptr) << "Accessing a moved pointer"; return *ptr; }
const T &
operator*() const
{
dbgAssert(ptr != nullptr) << AlertInfo(AlertTeam::CORE, "buffer i/s") << "Accessing a moved pointer";
return *ptr;
}
const T * operator->() const { return ptr; }
private:

View File

@@ -36,6 +36,58 @@ class I_SignalHandler;
namespace Config { enum class Errors; }
std::ostream & operator<<(std::ostream &, const Config::Errors &);
enum class AlertTeam { CORE, WAAP, SDWAN, IOT };
class AlertInfo
{
public:
template <typename ... Args>
AlertInfo(AlertTeam _team, const std::string &func, const Args & ... args) : team(_team), functionality(func)
{
evalParams(args ...);
}
template <typename ... Args>
AlertInfo
operator()(const Args & ... args) const
{
AlertInfo res = *this;
res.evalParams(args ...);
return res;
}
AlertTeam getTeam() const { return team; }
const std::string & getFunctionality() const { return functionality; }
const std::string & getDescription() const { return description; }
std::size_t getId() const { return id; }
std::size_t getFamilyId() const { return family_id; }
private:
template <typename ... Args>
void
evalParams(const std::string &_description, const Args & ... args)
{
description = _description;
evalParams(args ...);
}
template <typename ... Args>
void
evalParams(const std::size_t &fam_id, const Args & ... args)
{
family_id = fam_id;
evalParams(args ...);
}
void evalParams();
AlertTeam team;
std::string functionality;
std::size_t id;
std::size_t family_id = 0;
std::string description;
};
class Debug
:
Singleton::Consume<I_TimeGet>,
@@ -93,6 +145,8 @@ public:
std::set<std::ostream *> streams;
};
class DebugAlert;
class DebugLockState
{
private:
@@ -208,6 +262,7 @@ private:
);
void isCommunicationFlag(const DebugFlags &flag);
void sendAlert(const AlertInfo &alert);
static DebugLevel lowest_global_level;
static I_TimeGet *time;
@@ -225,6 +280,34 @@ private:
std::set<std::shared_ptr<DebugStream>> current_active_streams;
};
class Debug::DebugAlert
{
class DebugAlertImpl
{
public:
DebugAlertImpl(Debug &_debug) : debug(_debug) {}
DebugStreamAggr &
operator<<(const AlertInfo &alert) __attribute__((warn_unused_result))
{
debug.sendAlert(alert);
return debug.getStreamAggr();
}
private:
Debug &debug;
};
public:
template <typename ... Args> DebugAlert(const Args & ... args) : debug(args...) {}
DebugAlertImpl getStreamAggr() __attribute__((warn_unused_result)) { return DebugAlertImpl(debug); }
private:
Debug debug;
};
#define USE_DEBUG_FLAG(x) extern const Debug::DebugFlags x
// This function extract the base name from a full path.
@@ -245,7 +328,7 @@ getBaseName(const char *iter, const char *base)
#define dbgAssert(cond) \
if (CP_LIKELY(cond)) { \
} else Debug(__FILENAME__, __FUNCTION__, __LINE__).getStreamAggr()
} else Debug::DebugAlert(__FILENAME__, __FUNCTION__, __LINE__).getStreamAggr()
// Macros to allow simple debug messaging
#define DBG_GENERIC(level, ...) \

View File

@@ -320,7 +320,7 @@ template <typename T, typename TErr>
const T &
Maybe<T, TErr>::unpack() const
{
dbgAssert(set) << "Maybe value is not set";
dbgAssert(set) << AlertInfo(AlertTeam::CORE, "maybe i/s") << "Maybe value is not set";
return val;
}
@@ -328,7 +328,7 @@ template <typename T, typename TErr>
T &&
Maybe<T, TErr>::unpackMove()
{
dbgAssert(set) << "No value to be moved";
dbgAssert(set) << AlertInfo(AlertTeam::CORE, "maybe i/s") << "No value to be moved";
return std::move(val);
}
@@ -336,7 +336,7 @@ template <typename T, typename TErr>
TErr
Maybe<T, TErr>::getErr() const
{
dbgAssert(!set) << "Maybe value is set";
dbgAssert(!set) << AlertInfo(AlertTeam::CORE, "maybe i/s") << "Maybe value is set";
return err.err;
}
@@ -344,7 +344,7 @@ template <typename T, typename TErr>
const Error<TErr> &
Maybe<T, TErr>::passErr() const
{
dbgAssert(!set) << "Maybe value is set";
dbgAssert(!set) << AlertInfo(AlertTeam::CORE, "maybe i/s") << "Maybe value is set";
return err;
}

View File

@@ -110,7 +110,9 @@ template <typename Key>
const Key &
Table<Key>::Impl::ExpList::getEarliest() const
{
dbgAssert(!list.empty()) << "Cannot access the earliest member of an empty list";
dbgAssert(!list.empty())
<< AlertInfo(AlertTeam::CORE, "table")
<< "Cannot access the earliest member of an empty list";
return list.back().getKey();
}

View File

@@ -36,6 +36,7 @@ class I_Intelligence_IS_V2
{
public:
virtual bool sendInvalidation(const Intelligence::Invalidation &invalidation) const = 0;
virtual bool isIntelligenceHealthy() const = 0;
virtual Maybe<uint> registerInvalidation(
const Intelligence::Invalidation &invalidation,
const std::function<void(const Intelligence::Invalidation &)> &callback

View File

@@ -48,6 +48,8 @@ public:
return addRestCall(oper, uri, std::make_unique<SpecificRestInit<T>>());
}
virtual bool addGetCall(const std::string &uri, const std::function<std::string()> &callback) = 0;
virtual uint16_t getListeningPort() const = 0;
protected:

View File

@@ -24,6 +24,7 @@ public:
using Response = Intelligence::Response;
MOCK_CONST_METHOD1(sendInvalidation, bool(const Invalidation &invalidation));
MOCK_CONST_METHOD0(isIntelligenceHealthy, bool(void));
MOCK_METHOD2(registerInvalidation, Maybe<uint>(const Invalidation &invalidation, const InvalidationCb &callback));
MOCK_METHOD1(unregisterInvalidation, void(uint id));
MOCK_CONST_METHOD5(

View File

@@ -9,6 +9,7 @@ class MockRestApi : public Singleton::Provide<I_RestApi>::From<MockProvider<I_Re
{
public:
MOCK_CONST_METHOD0(getListeningPort, uint16_t());
MOCK_METHOD2(addGetCall, bool(const std::string &, const std::function<std::string()> &));
// You can't mock a function with an R-value reference. So mock a slightly different one
MOCK_METHOD3(mockRestCall, bool(RestAction, const std::string &, const std::unique_ptr<RestInit> &));

View File

@@ -34,7 +34,7 @@ DEFINE_FLAG(D_INFRA, D_ALL)
DEFINE_FLAG(D_SIGNAL_HANDLER, D_INFRA)
DEFINE_FLAG(D_TENANT_MANAGER, D_INFRA)
DEFINE_FLAG(D_MONITORING, D_INFRA)
DEFINE_FLAG(D_HEALTH_CHECK_MANAGER, D_INFRA)
DEFINE_FLAG(D_SERVICE_HEALTH_STATUS, D_INFRA)
DEFINE_FLAG(D_REPORT, D_INFRA)
DEFINE_FLAG(D_REPORT_BULK, D_REPORT)
DEFINE_FLAG(D_TRACE, D_INFRA)
@@ -137,6 +137,7 @@ DEFINE_FLAG(D_COMPONENT, D_ALL)
DEFINE_FLAG(D_HTTP_MANAGER, D_COMPONENT)
DEFINE_FLAG(D_ORCHESTRATOR, D_COMPONENT)
DEFINE_FLAG(D_HEALTH_CHECK_MANAGER, D_ORCHESTRATOR)
DEFINE_FLAG(D_HEALTH_CHECK, D_ORCHESTRATOR)
DEFINE_FLAG(D_AGENT_DETAILS, D_ORCHESTRATOR)
DEFINE_FLAG(D_LOCAL_POLICY, D_ORCHESTRATOR)
@@ -166,7 +167,6 @@ DEFINE_FLAG(D_COMPONENT, D_ALL)
DEFINE_FLAG(D_IOT_QUERY_INTELLIGENCE, D_IOT_AUXILIARY)
DEFINE_FLAG(D_IOT_SAVE_PERSISTENT, D_IOT_AUXILIARY)
DEFINE_FLAG(D_IOT_DOCKER, D_IOT_AUXILIARY)
DEFINE_FLAG(D_IOT_REST_AUTHENTICATION, D_IOT_AUXILIARY)
DEFINE_FLAG(D_IOT_ENFORCE, D_IOT_NEXT)
DEFINE_FLAG(D_IOT_ENFORCE_POLICY, D_IOT_ENFORCE)
DEFINE_FLAG(D_IOT_ENFORCE_ASSETS, D_IOT_ENFORCE)

View File

@@ -16,7 +16,9 @@
#include <chrono>
#include <vector>
#include <map>
#include "metric/metric_metadata.h"
#include "metric/metric_calc.h"
#include "metric/all_metric_event.h"
#include "i_mainloop.h"
@@ -26,6 +28,7 @@
#include "i_messaging.h"
#include "i_rest_api.h"
#include "report/report_enums.h"
#include "flags.h"
namespace MetricCalculations
{
@@ -38,6 +41,10 @@ namespace MetricCalculations
template <typename PrintableKey, typename Metric> class MetricMap;
} // MetricCalculations
MetricMetadata::DotName operator"" _dot(const char *, std::size_t);
MetricMetadata::Units operator"" _unit(const char *, std::size_t);
MetricMetadata::Description operator"" _desc(const char *, std::size_t);
class LogRest;
class GenericMetric
@@ -51,6 +58,8 @@ class GenericMetric
public Listener<AllMetricEvent>
{
public:
enum class Stream { FOG, DEBUG, PROMETHEUS, AIOPS, COUNT };
void
init(
const std::string &_metric_name,
@@ -79,7 +88,8 @@ public:
static std::string getName() { return "GenericMetric"; }
std::string generateReport(bool with_reset);
std::string generateReport() const;
void resetMetrics();
void upon(const AllMetricEvent &) override;
std::string respond(const AllMetricEvent &event) override;
std::string getListenerName() const override;
@@ -87,6 +97,9 @@ public:
std::string getMetricName() const;
std::chrono::seconds getReportInterval() const;
void turnOnStream(Stream stream) { active_streams.setFlag(stream); }
void turnOffStream(Stream stream) { active_streams.unsetFlag(stream); }
protected:
virtual void sendLog(const LogRest &metric_client_rest) const;
@@ -98,6 +111,7 @@ private:
void handleMetricStreamSending();
void generateLog();
void generateDebug();
I_MainLoop *i_mainloop;
I_TimeGet *i_time;
@@ -107,12 +121,14 @@ private:
ReportIS::Audience audience;
std::chrono::seconds report_interval;
std::vector<MetricCalc *> calcs;
Flags<Stream> active_streams;
bool reset;
bool force_buffering = false;
Context ctx;
};
#include "metric/counter.h"
#include "metric/no_reset_counter.h"
#include "metric/max.h"
#include "metric/min.h"
#include "metric/average.h"

View File

@@ -25,12 +25,18 @@ template <typename T>
class Average : public MetricCalc
{
public:
Average(GenericMetric *metric, const std::string &title) : MetricCalc(metric, title), sum(0), count(0) {}
template <typename ... Args>
Average(GenericMetric *metric, const std::string &title, const Args & ... args)
:
MetricCalc(metric, title, args ...),
sum(0),
count(0)
{
}
void
report(const T &new_value)
{
was_once_reported = true;
sum += new_value;
count++;
}
@@ -38,7 +44,6 @@ public:
void
reset() override
{
was_once_reported = false;
sum = 0;
count = 0;
}
@@ -46,19 +51,19 @@ public:
double
getAverage() const
{
return (was_once_reported) ? double(sum)/count : 0;
return (count > 0) ? double(sum)/count : 0;
}
void
save(cereal::JSONOutputArchive &ar) const override
{
ar(cereal::make_nvp(calc_title, getAverage()));
ar(cereal::make_nvp(getMetricName(), getAverage()));
}
LogField
getLogField() const override
{
return LogField(calc_title, static_cast<uint64_t>(getAverage()));
return LogField(getMetricName(), static_cast<uint64_t>(getAverage()));
}
private:

View File

@@ -24,12 +24,17 @@ namespace MetricCalculations
class Counter : public MetricCalc
{
public:
Counter(GenericMetric *metric, const std::string &title) : MetricCalc(metric, title), counter(0) {}
template <typename ... Args>
Counter(GenericMetric *metric, const std::string &title, const Args & ... args)
:
MetricCalc(metric, title, args ...),
counter(0)
{
}
void
reset() override
{
was_once_reported = false;
counter = 0;
}
@@ -42,20 +47,19 @@ public:
void
save(cereal::JSONOutputArchive &ar) const override
{
ar(cereal::make_nvp(calc_title, getCounter()));
ar(cereal::make_nvp(getMetricName(), getCounter()));
}
void
report(const uint64_t &new_value)
{
was_once_reported = true;
counter += new_value;
}
LogField
getLogField() const override
{
return LogField(calc_title, static_cast<uint64_t>(getCounter()));
return LogField(getMetricName(), static_cast<uint64_t>(getCounter()));
}
private:

View File

@@ -25,12 +25,16 @@ template <typename T>
class LastReportedValue : public MetricCalc
{
public:
LastReportedValue(GenericMetric *metric, const std::string &title) : MetricCalc(metric, title) {}
template <typename ... Args>
LastReportedValue(GenericMetric *metric, const std::string &title, const Args & ... args)
:
MetricCalc(metric, title, args ...)
{
}
void
reset() override
{
was_once_reported = false;
last_reported = T();
}
@@ -43,24 +47,23 @@ public:
void
save(cereal::JSONOutputArchive &ar) const override
{
ar(cereal::make_nvp(calc_title, getLastReportedValue()));
ar(cereal::make_nvp(getMetricName(), getLastReportedValue()));
}
void
report(const T &new_value)
{
was_once_reported = true;
last_reported = new_value;
}
LogField
getLogField() const override
{
return LogField(calc_title, static_cast<uint64_t>(getLastReportedValue()));
return LogField(getMetricName(), static_cast<uint64_t>(getLastReportedValue()));
}
private:
T last_reported;
T last_reported{};
};
} // namespace MetricCalculations

View File

@@ -25,23 +25,29 @@ template <typename T>
class Max : public MetricCalc
{
public:
Max(GenericMetric *metric, const std::string &title) : Max(metric, title, std::numeric_limits<T>::min()) {}
Max(GenericMetric *metric, const std::string &title, T min_val)
Max(GenericMetric *metric, const std::string &title) : Max(metric, title, 0) {}
template<typename ... Args>
Max(GenericMetric *metric, const std::string &title, T min_val, const Args & ... args)
:
MetricCalc(metric, title), max(min_val), reset_value(min_val) {}
MetricCalc(metric, title, args ...),
max(min_val),
reset_value(min_val)
{
}
void
report(const T &new_value)
{
was_once_reported = true;
if (new_value > max) max = new_value;
if (new_value > max || first) max = new_value;
first = false;
}
void
reset() override
{
was_once_reported = false;
max = reset_value;
first = true;
}
T
@@ -53,18 +59,19 @@ public:
void
save(cereal::JSONOutputArchive &ar) const override
{
ar(cereal::make_nvp(calc_title, max));
ar(cereal::make_nvp(getMetricName(), max));
}
LogField
getLogField() const override
{
return LogField(calc_title, static_cast<uint64_t>(getMax()));
return LogField(getMetricName(), static_cast<uint64_t>(getMax()));
}
private:
T max;
T reset_value;
bool first = true;
};
} // namespace MetricCalculations

View File

@@ -24,19 +24,53 @@
class GenericMetric;
enum class MetricType { GAUGE, COUNTER };
class MetricCalc
{
public:
MetricCalc(GenericMetric *metric, const std::string &calc_title);
template<typename ... Args>
MetricCalc(GenericMetric *metric, const std::string &calc_title, const Args & ... args)
{
setMetadata("BaseName", calc_title);
addMetric(metric);
parseMetadata(args ...);
}
virtual void reset() = 0;
virtual void save(cereal::JSONOutputArchive &) const = 0;
virtual LogField getLogField() const = 0;
bool wasOnceReported() const { return was_once_reported; }
std::string getMetricName() const { return getMetadata("BaseName"); }
std::string getMetricDotName() const { return getMetadata("DotName"); }
std::string getMetircUnits() const { return getMetadata("Units"); }
std::string getMetircDescription() const { return getMetadata("Description"); }
std::string getMetadata(const std::string &metadata) const;
virtual MetricType getMetricType() const { return MetricType::GAUGE; }
void setMetricDotName(const std::string &name) { setMetadata("DotName", name); }
void setMetircUnits(const std::string &units) { setMetadata("Units", units); }
void setMetircDescription(const std::string &description) { setMetadata("Description", description); }
void setMetadata(const std::string &metadata, const std::string &value);
protected:
bool was_once_reported = false;
std::string calc_title;
void addMetric(GenericMetric *metric);
template <typename Metadata, typename ... OtherMetadata>
void
parseMetadata(const Metadata &metadata, const OtherMetadata & ... other_metadata)
{
parseMetadata(metadata);
parseMetadata(other_metadata ...);
}
void parseMetadata(const MetricMetadata::DotName &name) { setMetricDotName(name.val); }
void parseMetadata(const MetricMetadata::Units &units) { setMetircUnits(units.val); }
void parseMetadata(const MetricMetadata::Description &description) { setMetircDescription(description.val); }
void parseMetadata() {}
private:
std::map<std::string, std::string> metadata;
};
#endif // __METRIC_CALC_H__

View File

@@ -46,6 +46,14 @@ class MetricMap : public MetricCalc
void clear() { inner_map.clear(); }
MetricType
getMetricType() const
{
auto first = begin();
if (first == end()) return MetricType::GAUGE;
return first->second.getMetricType();
}
typename std::map<std::string, Metric>::const_iterator begin() const { return inner_map.begin(); }
typename std::map<std::string, Metric>::const_iterator end() const { return inner_map.end(); }
@@ -54,27 +62,31 @@ class MetricMap : public MetricCalc
};
public:
MetricMap(GenericMetric *metric, const std::string &title) : MetricCalc(metric, title) {}
template <typename ... Args>
MetricMap(GenericMetric *metric, const std::string &title, const Args & ... args)
:
MetricCalc(metric, title, args ...)
{
}
void
reset() override
{
was_once_reported = false;
metric_map.clear();
if (getMetricType() == MetricType::GAUGE) metric_map.clear();
}
void
save(cereal::JSONOutputArchive &ar) const override
{
ar(cereal::make_nvp(calc_title, metric_map));
ar(cereal::make_nvp(getMetricName(), metric_map));
}
MetricType getMetricType() const override { return metric_map.getMetricType(); }
template <typename ... Values>
void
report(const PrintableKey &key, const Values & ... new_values)
{
was_once_reported = true;
std::stringstream string_key;
string_key << key;
auto metric = metric_map.emplace(string_key.str(), Metric(nullptr, string_key.str())).first;
@@ -84,7 +96,7 @@ public:
LogField
getLogField() const override
{
LogField field(calc_title);
LogField field(getMetricName());
for (auto &metric : metric_map) {
field.addFields(metric.second.getLogField());

View File

@@ -0,0 +1,43 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __METRIC_METADATA_H__
#define __METRIC_METADATA_H__
#ifndef __GENERIC_METRIC_H__
#error metric/metric_metadata.h should not be included directly
#endif // __GENERIC_METRIC_H_
#include <string>
namespace MetricMetadata
{
struct DotName
{
std::string val;
};
struct Units
{
std::string val;
};
struct Description
{
std::string val;
};
} //MetricMetadata
#endif // __METRIC_METADATA_H__

View File

@@ -25,21 +25,29 @@ template <typename T>
class Min : public MetricCalc
{
public:
Min(GenericMetric *metric, const std::string &title) : Min(metric, title, std::numeric_limits<T>::max()) {}
Min(GenericMetric *metric, const std::string &title, T max_val) : MetricCalc(metric, title), min(max_val) {}
Min(GenericMetric *metric, const std::string &title) : Min(metric, title, 0) {}
template<typename ... Args>
Min(GenericMetric *metric, const std::string &title, T max_val, const Args & ... args)
:
MetricCalc(metric, title, args ...),
min(max_val),
reset_value(max_val)
{
}
void
report(const T &new_value)
{
was_once_reported = true;
if (new_value < min) min = new_value;
if (new_value < min || first) min = new_value;
first = false;
}
void
reset() override
{
was_once_reported = false;
min = std::numeric_limits<T>::max();
min = reset_value;
first = true;
}
T
@@ -51,17 +59,19 @@ public:
void
save(cereal::JSONOutputArchive &ar) const override
{
ar(cereal::make_nvp(calc_title, min));
ar(cereal::make_nvp(getMetricName(), min));
}
LogField
getLogField() const override
{
return LogField(calc_title, static_cast<uint64_t>(getMin()));
return LogField(getMetricName(), static_cast<uint64_t>(getMin()));
}
private:
T min;
T reset_value;
bool first = true;
};
} // namespace MetricCalculations

View File

@@ -0,0 +1,68 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __NO_RESET_COUNTER_H__
#define __NO_RESET_COUNTER_H__
#ifndef __GENERIC_METRIC_H__
#error metric/no_reset_counter.h should not be included directly
#endif // __GENERIC_METRIC_H_
namespace MetricCalculations
{
class NoResetCounter : public MetricCalc
{
public:
template <typename ... Args>
NoResetCounter(GenericMetric *metric, const std::string &title, const Args & ... args)
:
MetricCalc(metric, title, args ...),
counter(0)
{
}
void reset() override {}
MetricType getMetricType() const override { return MetricType::COUNTER; }
uint64_t
getCounter() const
{
return counter;
}
void
save(cereal::JSONOutputArchive &ar) const override
{
ar(cereal::make_nvp(getMetricName(), getCounter()));
}
void
report(const uint64_t &new_value)
{
counter += new_value;
}
LogField
getLogField() const override
{
return LogField(getMetricName(), static_cast<uint64_t>(getCounter()));
}
private:
uint64_t counter;
};
} // namespace MetricCalculations
#endif // __NO_RESET_COUNTER_H__

View File

@@ -28,12 +28,17 @@ template <typename T, uint N>
class TopValues : public MetricCalc
{
public:
TopValues(GenericMetric *metric, const std::string &title) : MetricCalc(metric, title) { values.reserve(N); }
template <typename ... Args>
TopValues(GenericMetric *metric, const std::string &title, const Args & ... args)
:
MetricCalc(metric, title, args ...)
{
values.reserve(N);
}
void
report(const T &new_value)
{
was_once_reported = true;
if (values.size() < N) {
values.push_back(new_value);
return;
@@ -51,7 +56,6 @@ public:
void
reset() override
{
was_once_reported = false;
values.clear();
}
@@ -66,13 +70,13 @@ public:
void
save(cereal::JSONOutputArchive &ar) const override
{
ar(cereal::make_nvp(calc_title, getTopValues()));
ar(cereal::make_nvp(getMetricName(), getTopValues()));
}
LogField
getLogField() const override
{
return LogField(calc_title, getTopValues());
return LogField(getMetricName(), getTopValues());
}
private:

View File

@@ -162,7 +162,9 @@ class LogField : Singleton::Consume<I_Environment>
void
addFields(const LogField &)
{
dbgAssert(false) << "Trying to add a log field to a 'type'ed field";
dbgAssert(false)
<< AlertInfo(AlertTeam::CORE, "report i/s")
<< "Trying to add a log field to a 'type'ed field";
}
// LCOV_EXCL_STOP

View File

@@ -26,7 +26,7 @@ public:
void
setBulkSize(uint size)
{
dbgAssert(size > 0) << "Bulk size must be larger than 0";
dbgAssert(size > 0) << AlertInfo(AlertTeam::CORE, "report i/s") << "Bulk size must be larger than 0";
dbgDebug(D_REPORT_BULK) << "Bulk size is set to " << size;
bulk_size = size;
}

View File

@@ -0,0 +1,130 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __REPORT_MESSAGING_H__
#define __REPORT_MESSAGING_H__
#include "singleton.h"
#include "i_time_get.h"
#include "i_messaging.h"
#include "report/report.h"
class ReportMessaging
:
Singleton::Consume<I_Messaging>,
Singleton::Consume<I_TimeGet>
{
public:
template <typename ...Args, typename T>
ReportMessaging(
const std::string &title,
const ReportIS::AudienceTeam &audience_team,
const T &obj,
Args ...args)
:
ReportMessaging(
title,
audience_team,
obj,
MessageCategory::GENERIC,
std::forward<Args>(args)...
)
{
}
template <typename ...Args, typename T>
ReportMessaging(
const std::string &title,
const ReportIS::AudienceTeam &audience_team,
const T &obj,
const MessageCategory &message_type,
Args ...args)
:
ReportMessaging(
title,
audience_team,
ReportIS::Severity::INFO,
ReportIS::Priority::LOW,
obj,
message_type,
std::forward<Args>(args)...
)
{
}
template <typename ...Args, typename T>
ReportMessaging(
const std::string &title,
const ReportIS::AudienceTeam &audience_team,
const ReportIS::Severity &severity,
const ReportIS::Priority &priority,
const T &obj,
Args ...args)
:
ReportMessaging(
title,
audience_team,
severity,
priority,
obj,
MessageCategory::GENERIC,
std::forward<Args>(args)...
)
{
}
template <typename ...Args, typename T>
ReportMessaging(
const std::string &title,
const ReportIS::AudienceTeam &audience_team,
const ReportIS::Severity &severity,
const ReportIS::Priority &priority,
const T &obj,
const MessageCategory &message_type,
Args ...args)
:
report(
title,
Singleton::Consume<I_TimeGet>::by<ReportMessaging>()->getWalltime(),
ReportIS::Type::EVENT,
ReportIS::Level::LOG,
ReportIS::LogLevel::INFO,
ReportIS::Audience::INTERNAL,
audience_team,
severity,
priority,
std::chrono::seconds(0),
std::forward<Args>(args)...
),
message_type_tag(message_type)
{
report << LogField("eventObject", obj);
}
~ReportMessaging();
ReportMessaging & operator<<(const LogField &field);
Maybe<void, HTTPResponse> sendReportSynchronously();
void setForceBuffering(bool _force_buffering);
private:
Report report;
bool is_async_message = true;
bool force_buffering = false;
MessageCategory message_type_tag;
};
#endif // __REPORT_MESSAGING_H__

View File

@@ -0,0 +1,43 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __SERVICE_HEALTH_UPDATE_EVENT_H__
#define __SERVICE_HEALTH_UPDATE_EVENT_H__
#include "event.h"
#include "singleton.h"
#include "config.h"
#include "debug.h"
#include "customized_cereal_map.h"
class ServiceHealthUpdateEvent : public Event<ServiceHealthUpdateEvent>
{
public:
ServiceHealthUpdateEvent() {}
ServiceHealthUpdateEvent(
const std::string &_component,
const std::string &_error)
:
component(_component),
error(_error) {}
bool isHealthyUpdate() const { return component.empty(); }
const std::string & getComponent() const { return component; }
const std::string & getError() const { return error; }
private:
std::string component = "";
std::string error = "";
};
#endif // __SERVICE_HEALTH_UPDATE_EVENT_H__

View File

@@ -48,7 +48,10 @@ Opaque &
I_Table::getState()
{
Opaque *ptr = static_cast<Opaque *>(getState(typeid(Opaque)));
dbgAssert(ptr != nullptr) << "Trying to access a non existing opaque " << typeid(Opaque).name();
dbgAssert(ptr != nullptr)
<< AlertInfo(AlertTeam::CORE, "table")
<< "Trying to access a non existing opaque "
<< typeid(Opaque).name();
return *ptr;
}

View File

@@ -48,6 +48,8 @@ std::string convertToHumanReadable(uint64_t size_in_bytes);
std::string getFileName(const std::string &path);
bool copyDirectory(const std::string &src_dir_path, const std::string &dst_dir_path);
}// namespace Filesystem
namespace Regex

View File

@@ -88,6 +88,7 @@ public:
operator==(const IPAddr &other) const
{
dbgAssert(type!=IPType::UNINITIALIZED && other.type!=IPType::UNINITIALIZED)
<< AlertInfo(AlertTeam::CORE, "connkey")
<< "Called on an uninitialized IPType object";
// Always compairing as if IPv6, in case of Ipv4 the rest of the address is zeroed out.
int ip_len = (other.type == IPType::V4) ? sizeof(v4.s_addr) : sizeof(v6.s6_addr);
@@ -307,7 +308,9 @@ public:
IPType
getType() const
{
dbgAssert(src.type == dst.type) << "Mismatch in connection types (Src and Dst types are not identical)";
dbgAssert(src.type == dst.type)
<< AlertInfo(AlertTeam::CORE, "connkey")
<< "Mismatch in connection types (Src and Dst types are not identical)";
return src.type;
}

View File

@@ -27,13 +27,13 @@ public:
operator T &()
{
dbgAssert(is_active) << "Tried to access a non-existing variable";
dbgAssert(is_active) << AlertInfo(AlertTeam::CORE, "rest i/s") << "Tried to access a non-existing variable";
return val;
}
operator const T &() const
{
dbgAssert(is_active) << "Tried to access a non-existing variable";
dbgAssert(is_active) << AlertInfo(AlertTeam::CORE, "rest i/s") << "Tried to access a non-existing variable";
return val;
}

View File

@@ -34,6 +34,8 @@ static const string invalidation_uri = "/api/v2/intelligence/invalidation";
static const string registration_uri = "/api/v2/intelligence/invalidation/register";
static const string query_uri = "/api/v2/intelligence/assets/query";
static const string queries_uri = "/api/v2/intelligence/assets/queries";
static const string fog_health_uri = "/access-manager/health/live";
static const string intelligence_health_uri = "/show-health";
class I_InvalidationCallBack
{
@@ -291,6 +293,16 @@ private:
I_MainLoop *mainloop;
};
class IntelligenceHealth : public ClientRest
{
public:
bool isLocalHealthy() const { return healthy.isActive() && healthy.get(); }
private:
S2C_PARAM(bool, healthy);
};
class IntelligenceComponentV2::Impl
:
Singleton::Provide<I_Intelligence_IS_V2>::From<IntelligenceComponentV2>
@@ -314,6 +326,35 @@ public:
rest_api->addRestCall<ReceiveInvalidation>(RestAction::SET, "new-invalidation/source/invalidation");
}
bool
isIntelligenceHealthy() const override
{
dbgFlow(D_INTELLIGENCE) << "Checking intelligence health";
IntelligenceHealth healthObj;
if (hasLocalIntelligenceSupport()) {
dbgDebug(D_INTELLIGENCE) << "Local intelligence supported";
return sendLocalIntelligenceToLocalServer(healthObj).ok();
}
dbgTrace(D_INTELLIGENCE) << "Checking connection to the FOG";
auto response = message->sendSyncMessage(
HTTPMethod::GET,
fog_health_uri,
string(""),
MessageCategory::INTELLIGENCE
);
if (response.ok() && response.unpack().getHTTPStatusCode() == HTTPStatusCode::HTTP_OK) {
dbgTrace(D_INTELLIGENCE) << "Connected to the FOG";
return true;
}
dbgTrace(D_INTELLIGENCE)
<< "No connection to the FOG. "
<< (response.ok() ? response.unpack() : response.getErr()).toString();
return false;
}
bool
sendInvalidation(const Invalidation &invalidation) const override
{
@@ -487,6 +528,36 @@ private:
return genError("Could not send local intelligence invalidation");
}
Maybe<Response>
sendIntelligenceRequestImpl(const IntelligenceHealth &, const MessageMetadata &req_md) const
{
dbgFlow(D_INTELLIGENCE) << "Sending intelligence health check";
IntelligenceHealth healthObj;
auto req_data = message->sendSyncMessage(
HTTPMethod::GET,
intelligence_health_uri,
healthObj,
MessageCategory::INTELLIGENCE,
req_md
);
if (req_data.ok() && healthObj.isLocalHealthy()) {
dbgDebug(D_INTELLIGENCE) << "Intelligence is healthy.";
return Response();
}
if (!req_data.ok()) {
dbgDebug(D_INTELLIGENCE)
<< "Intelligence is not healthy. Body: "
<< req_data.getErr().getBody()
<< " Error: "
<< req_data.getErr().toString();
} else {
dbgDebug(D_INTELLIGENCE) << "Intelligence return unhealthy status.";
}
return genError("Intelligence is not healthy");
}
Maybe<Response>
sendIntelligenceRequestImpl(
const InvalidationRegistration::RestCall &registration,

View File

@@ -1341,3 +1341,59 @@ TEST_F(IntelligenceComponentTestV2, ignoreInProgressQueryTest_2)
EXPECT_EQ(objects_ids.size(), 2u);
}
TEST_F(IntelligenceComponentTestV2, foghealthy)
{
Debug::setUnitTestFlag(D_INTELLIGENCE, Debug::DebugLevel::TRACE);
I_Intelligence_IS_V2 *intell = Singleton::Consume<I_Intelligence_IS_V2>::by<IntelligenceComponentTestV2>();
HTTPResponse fog_res(
HTTPStatusCode::HTTP_OK,
string(
"{"
" \"up\": true,"
" \"timestamp\":\"\""
"}"
)
);
EXPECT_CALL(
messaging_mock,
sendSyncMessage(HTTPMethod::GET, "/access-manager/health/live", _, MessageCategory::INTELLIGENCE, _)
).WillOnce(Return(fog_res));
EXPECT_TRUE(intell->isIntelligenceHealthy());
}
TEST_F(IntelligenceComponentTestV2, localIntelligenceHealthy)
{
Debug::setUnitTestFlag(D_INTELLIGENCE, Debug::DebugLevel::TRACE);
stringstream configuration;
configuration << "{";
configuration << " \"agentSettings\":[";
configuration << " {\"key\":\"agent.config.useLocalIntelligence\",\"id\":\"id1\",\"value\":\"true\"}";
configuration << " ],";
configuration << " \"intelligence\":{";
configuration << " \"local intelligence server ip\":\"127.0.0.1\",";
configuration << " \"local intelligence server primary port\":9090";
configuration << " }";
configuration << "}";
Singleton::Consume<Config::I_Config>::from(conf)->loadConfiguration(configuration);
I_Intelligence_IS_V2 *intell = Singleton::Consume<I_Intelligence_IS_V2>::by<IntelligenceComponentTestV2>();
string localHealthy(
"{\n"
" \"healthy\": true\n"
"}\n"
);
EXPECT_CALL(
messaging_mock,
sendSyncMessage(HTTPMethod::GET, "/show-health", _, MessageCategory::INTELLIGENCE, _)
).WillOnce(Return(HTTPResponse(HTTPStatusCode::HTTP_OK, localHealthy)));
EXPECT_CALL(mock_rest, getListeningPort()).Times(1).WillRepeatedly(Return(8888));
EXPECT_TRUE(intell->isIntelligenceHealthy());
}

View File

@@ -76,7 +76,10 @@ RoutineWrapper::resume()
void
RoutineWrapper::invoke(pull_type &pull, I_MainLoop::Routine func)
{
dbgAssert(active != nullptr) << "Trying to invoke without an active routine";
dbgAssert(active != nullptr)
<< AlertInfo(AlertTeam::CORE, "mainloop i/s")
<< "Trying to invoke without an active routine";
active->pull = move(pull); // First invokation (other invokaction will start inside `func`), set the `pull` object
func();
}

View File

@@ -34,6 +34,8 @@ USE_DEBUG_FLAG(D_MAINLOOP);
bool fini_signal_flag = false;
static const AlertInfo alert(AlertTeam::CORE, "mainloop i/s");
class MainloopStop {};
class MainloopComponent::Impl : Singleton::Provide<I_MainLoop>::From<MainloopComponent>
@@ -225,7 +227,7 @@ MainloopComponent::Impl::reportStartupEvent()
void
MainloopComponent::Impl::run()
{
dbgAssert(!is_running) << "MainloopComponent::Impl::run was called while it was already running";
dbgAssert(!is_running) << alert << "MainloopComponent::Impl::run was called while it was already running";
is_running = true;
bool has_primary_routines = true;
@@ -465,7 +467,7 @@ MainloopComponent::Impl::getCurrentRoutineId() const
void
MainloopComponent::Impl::yield(bool force)
{
dbgAssert(curr_iter != routines.end()) << "Calling 'yield' without a running current routine";
dbgAssert(curr_iter != routines.end()) << alert << "Calling 'yield' without a running current routine";
if (do_stop) throw MainloopStop();
if (!force && getTimer()->getMonotonicTime() < stop_time) return;
@@ -506,7 +508,7 @@ MainloopComponent::Impl::stopAll()
void
MainloopComponent::Impl::stop()
{
dbgAssert(curr_iter != routines.end()) << "Attempting to stop a routine when none is running";
dbgAssert(curr_iter != routines.end()) << alert << "Attempting to stop a routine when none is running";
stop(curr_iter);
}
@@ -524,7 +526,7 @@ MainloopComponent::Impl::stop(RoutineID id)
void
MainloopComponent::Impl::halt()
{
dbgAssert(curr_iter != routines.end()) << "Calling 'halt' without a running current routine";
dbgAssert(curr_iter != routines.end()) << alert << "Calling 'halt' without a running current routine";
curr_iter->second.halt();
yield(true);
}
@@ -533,7 +535,7 @@ void
MainloopComponent::Impl::halt(RoutineID id)
{
auto iter = routines.find(id);
dbgAssert(iter != routines.end()) << "No routine " << id << " to halt";
dbgAssert(iter != routines.end()) << alert << "No routine " << id << " to halt";
iter->second.halt();
if (iter == curr_iter) yield(true);
}
@@ -542,7 +544,7 @@ void
MainloopComponent::Impl::resume(RoutineID id)
{
auto iter = routines.find(id);
dbgAssert(iter != routines.end()) << "No routine " << id << " to resume";
dbgAssert(iter != routines.end()) << alert << "No routine " << id << " to resume";
iter->second.resume();
}

View File

@@ -39,7 +39,7 @@ public:
init()
{
server_fd = socket(AF_INET, SOCK_STREAM, 0);
dbgAssert(server_fd >= 0) << "Failed to open a socket";
dbgAssert(server_fd >= 0) << AlertInfo(AlertTeam::CORE, "messaging i/s") << "Failed to open a socket";
int socket_enable = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &socket_enable, sizeof(int));

View File

@@ -92,7 +92,10 @@ string
HTTPResponse::toString() const
{
auto code = status_code_to_string.find(status_code);
dbgAssert(code != status_code_to_string.end()) << "Unknown status code " << int(status_code);
dbgAssert(code != status_code_to_string.end())
<< AlertInfo(AlertTeam::CORE, "messaging i/s")
<< "Unknown status code "
<< int(status_code);
return "[Status-code]: " + code->second + ", [Body]: " + (body.empty() ? "{}" : body);
}

View File

@@ -26,7 +26,29 @@ using namespace ReportIS;
USE_DEBUG_FLAG(D_METRICS);
MetricCalc::MetricCalc(GenericMetric *metric, const string &title) : calc_title(title)
MetricMetadata::DotName operator"" _dot(const char *str, size_t) { return MetricMetadata::DotName{str}; }
MetricMetadata::Units operator"" _unit(const char *str, size_t) { return MetricMetadata::Units{str}; }
MetricMetadata::Description operator"" _desc(const char *str, size_t) { return MetricMetadata::Description{str}; }
string
MetricCalc::getMetadata(const string &key) const
{
auto value = metadata.find(key);
return value != metadata.end() ? value->second : "";
}
void
MetricCalc::setMetadata(const string &key, const string &value)
{
if (value.empty()) {
metadata.erase(key);
} else {
metadata[key] = value;
}
}
void
MetricCalc::addMetric(GenericMetric *metric)
{
// Only top level metric should add themselves to the metric. Nested metrics will be served by their parent.
if (metric != nullptr) metric->addCalc(this);
@@ -69,6 +91,9 @@ GenericMetric::init(
bool _force_buffering
)
{
turnOnStream(Stream::FOG);
turnOnStream(Stream::DEBUG);
i_mainloop = Singleton::Consume<I_MainLoop>::by<GenericMetric>();
i_time = Singleton::Consume<I_TimeGet>::by<GenericMetric>();
metric_name = _metric_name;
@@ -95,22 +120,10 @@ GenericMetric::init(
void
GenericMetric::handleMetricStreamSending()
{
auto metric_debug = getConfigurationWithDefault<bool>(true, "metric", "debugMetricSendEnable");
auto report_str = generateReport(false);
if (!report_str.empty() && metric_debug) {
dbgTrace(D_METRICS) << report_str;
}
if (active_streams.isSet(Stream::DEBUG)) generateDebug();
if (active_streams.isSet(Stream::FOG)) generateLog();
auto metric_fog = getConfigurationWithDefault<bool>(true, "metric", "fogMetricSendEnable");
if (!report_str.empty() && metric_fog) {
generateLog();
}
if (reset) {
for(auto &calc : calcs) {
calc->reset();
}
}
if (reset) resetMetrics();
}
string
@@ -126,24 +139,26 @@ GenericMetric::getReportInterval() const
}
string
GenericMetric::generateReport(bool with_reset)
GenericMetric::generateReport() const
{
stringstream ss;
bool any_reported_calc = false;
{
cereal::JSONOutputArchive ar(ss);
ar(cereal::make_nvp("Metric", metric_name));
ar(cereal::make_nvp("Reporting interval", report_interval.count()));
for(auto &calc : calcs) {
if (calc->wasOnceReported()) {
calc->save(ar);
if (with_reset) calc->reset();
any_reported_calc = true;
}
calc->save(ar);
}
}
return any_reported_calc ? ss.str() : "";
return ss.str();
}
void
GenericMetric::resetMetrics()
{
for(auto &calc : calcs) {
calc->reset();
}
}
void
@@ -155,16 +170,16 @@ GenericMetric::addCalc(MetricCalc *calc)
void
GenericMetric::upon(const AllMetricEvent &event)
{
auto report_str = generateReport(event.getReset());
if (!report_str.empty()) {
dbgTrace(D_METRICS) << report_str;
}
dbgTrace(D_METRICS) << generateReport();
if (event.getReset()) resetMetrics();
}
string
GenericMetric::respond(const AllMetricEvent &event)
{
return generateReport(event.getReset());
auto res = generateReport();
if (event.getReset()) resetMetrics();
return res;
}
string GenericMetric::getListenerName() const { return metric_name; }
@@ -172,6 +187,8 @@ string GenericMetric::getListenerName() const { return metric_name; }
void
GenericMetric::generateLog()
{
if (!getConfigurationWithDefault<bool>(true, "metric", "fogMetricSendEnable")) return;
set<ReportIS::Tags> tags;
Report metric_to_fog(
metric_name,
@@ -191,7 +208,7 @@ GenericMetric::generateLog()
);
for (auto &calc : calcs) {
if (calc->wasOnceReported()) metric_to_fog << calc->getLogField();
metric_to_fog << calc->getLogField();
}
if (Singleton::exists<I_Environment>()) {
@@ -227,6 +244,13 @@ GenericMetric::generateLog()
sendLog(metric_client_rest);
}
void
GenericMetric::generateDebug()
{
if (!getConfigurationWithDefault<bool>(true, "metric", "debugMetricSendEnable")) return;
dbgTrace(D_METRICS) << generateReport();
}
void
GenericMetric::sendLog(const LogRest &metric_client_rest) const
{

View File

@@ -20,6 +20,16 @@ using namespace MetricCalculations;
USE_DEBUG_FLAG(D_METRICS);
TEST(BaseMetric, generic_metadata)
{
Max<int> test(nullptr, "cpuMax", 0, "cpu.max"_dot, "percent"_unit, "CPU utilization percentage"_desc);
EXPECT_EQ(test.getMetricName(), "cpuMax");
EXPECT_EQ(test.getMetricDotName(), "cpu.max");
EXPECT_EQ(test.getMetircUnits(), "percent");
EXPECT_EQ(test.getMetircDescription(), "CPU utilization percentage");
}
class CPUEvent : public Event<CPUEvent>
{
public:
@@ -53,6 +63,7 @@ public:
last_report.report(event.getCPU());
avg.report(event.getCPU());
samples_counter.report(1);
total_samples_counter.report(1);
top_usage.report(event.getCPU());
}
@@ -61,6 +72,7 @@ public:
Average<double> avg{this, "cpuAvg"};
LastReportedValue<double> last_report{this, "cpuCurrent"};
Counter samples_counter{this, "cpuCounter"};
NoResetCounter total_samples_counter{this, "cpuTotalCounter"};
TopValues<double, 3> top_usage{this, "cpuTops"};
};
@@ -129,10 +141,12 @@ public:
upon(const HttpTransaction &event) override
{
avg.report(event.getUrl(), event.getBytes());
total.report(event.getUrl(), 1);
}
private:
MetricMap<string, Average<double>> avg{this, "PerUrlAvg"};
MetricMap<string, NoResetCounter> total{this, "TotalRequests"};
};
class MetricTest : public Test
@@ -189,7 +203,6 @@ TEST_F(MetricTest, basicMetricTest)
EXPECT_EQ(cpu_mt.getReportInterval().count(), 5);
routine();
EXPECT_EQ(debug_output.str(), "");
CPUEvent cpu_event;
cpu_event.setProcessCPU(89);
@@ -204,6 +217,7 @@ TEST_F(MetricTest, basicMetricTest)
" \"cpuAvg\": 89.0,\n"
" \"cpuCurrent\": 89.0,\n"
" \"cpuCounter\": 1,\n"
" \"cpuTotalCounter\": 1,\n"
" \"cpuTops\": [\n"
" 89.0\n"
" ]\n"
@@ -249,6 +263,7 @@ TEST_F(MetricTest, basicMetricTest)
" \"cpuAvg\": 89,\n"
" \"cpuCurrent\": 89,\n"
" \"cpuCounter\": 1,\n"
" \"cpuTotalCounter\": 1,\n"
" \"cpuTops\": [\n"
" 89.0\n"
" ]\n"
@@ -273,6 +288,7 @@ TEST_F(MetricTest, basicMetricTest)
" \"cpuAvg\": 89.5,\n"
" \"cpuCurrent\": 90.0,\n"
" \"cpuCounter\": 2,\n"
" \"cpuTotalCounter\": 2,\n"
" \"cpuTops\": [\n"
" 89.0,\n"
" 90.0\n"
@@ -318,6 +334,7 @@ TEST_F(MetricTest, basicMetricTest)
" \"cpuAvg\": 89,\n"
" \"cpuCurrent\": 90,\n"
" \"cpuCounter\": 2,\n"
" \"cpuTotalCounter\": 2,\n"
" \"cpuTops\": [\n"
" 89.0,\n"
" 90.0\n"
@@ -343,6 +360,7 @@ TEST_F(MetricTest, basicMetricTest)
" \"cpuAvg\": 93.0,\n"
" \"cpuCurrent\": 100.0,\n"
" \"cpuCounter\": 3,\n"
" \"cpuTotalCounter\": 3,\n"
" \"cpuTops\": [\n"
" 89.0,\n"
" 90.0,\n"
@@ -389,6 +407,7 @@ TEST_F(MetricTest, basicMetricTest)
" \"cpuAvg\": 93,\n"
" \"cpuCurrent\": 100,\n"
" \"cpuCounter\": 3,\n"
" \"cpuTotalCounter\": 3,\n"
" \"cpuTops\": [\n"
" 89.0,\n"
" 90.0,\n"
@@ -449,6 +468,7 @@ TEST_F(MetricTest, printMetricsTest)
" \"cpuAvg\": 89.0,\n"
" \"cpuCurrent\": 89.0,\n"
" \"cpuCounter\": 1,\n"
" \"cpuTotalCounter\": 1,\n"
" \"cpuTops\": [\n"
" 89.0\n"
" ]\n"
@@ -481,7 +501,6 @@ TEST_F(MetricTest, metricTestWithReset)
EXPECT_EQ(cpu_mt.getReportInterval().count(), 5);
routine();
EXPECT_EQ(debug_output.str(), "");
CPUEvent cpu_event;
cpu_event.setProcessCPU(89);
@@ -496,6 +515,7 @@ TEST_F(MetricTest, metricTestWithReset)
" \"cpuAvg\": 89.0,\n"
" \"cpuCurrent\": 89.0,\n"
" \"cpuCounter\": 1,\n"
" \"cpuTotalCounter\": 1,\n"
" \"cpuTops\": [\n"
" 89.0\n"
" ]\n"
@@ -542,6 +562,7 @@ TEST_F(MetricTest, metricTestWithReset)
" \"cpuAvg\": 89,\n"
" \"cpuCurrent\": 89,\n"
" \"cpuCounter\": 1,\n"
" \"cpuTotalCounter\": 1,\n"
" \"cpuTops\": [\n"
" 89.0\n"
" ]\n"
@@ -566,6 +587,7 @@ TEST_F(MetricTest, metricTestWithReset)
" \"cpuAvg\": 90.0,\n"
" \"cpuCurrent\": 90.0,\n"
" \"cpuCounter\": 1,\n"
" \"cpuTotalCounter\": 2,\n"
" \"cpuTops\": [\n"
" 90.0\n"
" ]\n"
@@ -610,6 +632,7 @@ TEST_F(MetricTest, metricTestWithReset)
" \"cpuAvg\": 90,\n"
" \"cpuCurrent\": 90,\n"
" \"cpuCounter\": 1,\n"
" \"cpuTotalCounter\": 2,\n"
" \"cpuTops\": [\n"
" 90.0\n"
" ]\n"
@@ -634,6 +657,7 @@ TEST_F(MetricTest, metricTestWithReset)
" \"cpuAvg\": 100.0,\n"
" \"cpuCurrent\": 100.0,\n"
" \"cpuCounter\": 1,\n"
" \"cpuTotalCounter\": 3,\n"
" \"cpuTops\": [\n"
" 100.0\n"
" ]\n"
@@ -678,6 +702,7 @@ TEST_F(MetricTest, metricTestWithReset)
" \"cpuAvg\": 100,\n"
" \"cpuCurrent\": 100,\n"
" \"cpuCounter\": 1,\n"
" \"cpuTotalCounter\": 3,\n"
" \"cpuTops\": [\n"
" 100.0\n"
" ]\n"
@@ -707,7 +732,17 @@ TEST_F(MetricTest, generateReportWithReset)
EXPECT_EQ(cpu_mt.getReportInterval().count(), 5);
routine();
EXPECT_EQ(debug_output.str(), "");
auto init_report = cpu_mt.generateReport();
EXPECT_NE(init_report, "");
EXPECT_THAT(init_report, HasSubstr("\"Metric\": \"CPU usage\""));
EXPECT_THAT(init_report, HasSubstr("\"Reporting interval\": 5,"));
EXPECT_THAT(init_report, HasSubstr("cpuMax"));
EXPECT_THAT(init_report, HasSubstr("cpuMin"));
EXPECT_THAT(init_report, HasSubstr("cpuAvg"));
EXPECT_THAT(init_report, HasSubstr("cpuCurrent"));
EXPECT_THAT(init_report, HasSubstr("cpuTops"));
CPUEvent cpu_event;
cpu_event.setProcessCPU(89);
@@ -722,6 +757,7 @@ TEST_F(MetricTest, generateReportWithReset)
" \"cpuAvg\": 89.0,\n"
" \"cpuCurrent\": 89.0,\n"
" \"cpuCounter\": 1,\n"
" \"cpuTotalCounter\": 1,\n"
" \"cpuTops\": [\n"
" 89.0\n"
" ]\n"
@@ -768,6 +804,7 @@ TEST_F(MetricTest, generateReportWithReset)
" \"cpuAvg\": 89,\n"
" \"cpuCurrent\": 89,\n"
" \"cpuCounter\": 1,\n"
" \"cpuTotalCounter\": 1,\n"
" \"cpuTops\": [\n"
" 89.0\n"
" ]\n"
@@ -779,13 +816,28 @@ TEST_F(MetricTest, generateReportWithReset)
EXPECT_EQ(message_body, expected_message);
debug_output.str("");
auto report = cpu_mt.generateReport(true);
auto report = cpu_mt.generateReport();
cpu_mt.resetMetrics();
EXPECT_THAT(metric_str, HasSubstr(report));
EXPECT_EQ(message_body, expected_message);
debug_output.str("");
report = cpu_mt.generateReport();
metric_str =
"{\n"
" \"Metric\": \"CPU usage\",\n"
" \"Reporting interval\": 5,\n"
" \"cpuMax\": 0.0,\n"
" \"cpuMin\": 0.0,\n"
" \"cpuAvg\": 0.0,\n"
" \"cpuCurrent\": 0.0,\n"
" \"cpuCounter\": 0,\n"
" \"cpuTotalCounter\": 1,\n"
" \"cpuTops\": []\n"
"}";
EXPECT_EQ(report, metric_str);
debug_output.str("");
routine();
EXPECT_EQ(debug_output.str(), "");
cpu_event.setProcessCPU(90);
cpu_event.notify();
@@ -799,6 +851,7 @@ TEST_F(MetricTest, generateReportWithReset)
" \"cpuAvg\": 90.0,\n"
" \"cpuCurrent\": 90.0,\n"
" \"cpuCounter\": 1,\n"
" \"cpuTotalCounter\": 2,\n"
" \"cpuTops\": [\n"
" 90.0\n"
" ]\n"
@@ -843,6 +896,7 @@ TEST_F(MetricTest, generateReportWithReset)
" \"cpuAvg\": 90,\n"
" \"cpuCurrent\": 90,\n"
" \"cpuCounter\": 1,\n"
" \"cpuTotalCounter\": 2,\n"
" \"cpuTops\": [\n"
" 90.0\n"
" ]\n"
@@ -867,6 +921,7 @@ TEST_F(MetricTest, generateReportWithReset)
" \"cpuAvg\": 95.0,\n"
" \"cpuCurrent\": 100.0,\n"
" \"cpuCounter\": 2,\n"
" \"cpuTotalCounter\": 3,\n"
" \"cpuTops\": [\n"
" 90.0,\n"
" 100.0\n"
@@ -912,6 +967,7 @@ TEST_F(MetricTest, generateReportWithReset)
" \"cpuAvg\": 95,\n"
" \"cpuCurrent\": 100,\n"
" \"cpuCounter\": 2,\n"
" \"cpuTotalCounter\": 3,\n"
" \"cpuTops\": [\n"
" 90.0,\n"
" 100.0\n"
@@ -987,6 +1043,7 @@ TEST_F(MetricTest, allMetricTest)
" \"cpuAvg\": 93.0,\n"
" \"cpuCurrent\": 100.0,\n"
" \"cpuCounter\": 3,\n"
" \"cpuTotalCounter\": 3,\n"
" \"cpuTops\": [\n"
" 89.0,\n"
" 90.0,\n"
@@ -1040,6 +1097,24 @@ TEST_F(MetricTest, testMapMetric)
" \"PerUrlAvg\": {\n"
" \"/index.html\": 25.0,\n"
" \"/index2.html\": 20.0\n"
" },\n"
" \"TotalRequests\": {\n"
" \"/index.html\": 2,\n"
" \"/index2.html\": 1\n"
" }\n"
"}";
EXPECT_THAT(debug_output.str(), HasSubstr(msg_str));
debug_output.str("");
routine();
msg_str =
"{\n"
" \"Metric\": \"Bytes per URL\",\n"
" \"Reporting interval\": 5,\n"
" \"PerUrlAvg\": {},\n"
" \"TotalRequests\": {\n"
" \"/index.html\": 2,\n"
" \"/index2.html\": 1\n"
" }\n"
"}";
EXPECT_THAT(debug_output.str(), HasSubstr(msg_str));

View File

@@ -19,6 +19,8 @@ using namespace ReportIS;
#include <unordered_map>
static const AlertInfo alert(AlertTeam::CORE, "report i/s");
Maybe<ReportIS::Severity>
TagAndEnumManagement::convertStringToSeverity(const string &severity)
{
@@ -158,7 +160,7 @@ TagAndEnumManagement::convertToString(const StreamType &stream_type)
case StreamType::COUNT: break;
}
dbgAssert(false) << "Unknown log stream type. Type: " << static_cast<int>(stream_type);
dbgAssert(false) << alert << "Unknown log stream type. Type: " << static_cast<int>(stream_type);
return "";
}
@@ -173,7 +175,7 @@ TagAndEnumManagement::convertToString(const Severity &severity)
case Severity::INFO: return "Info";
}
dbgAssert(false) << "Reached an impossible severity value of: " << static_cast<int>(severity);
dbgAssert(false) << alert << "Reached an impossible severity value of: " << static_cast<int>(severity);
return "";
}
@@ -186,7 +188,7 @@ TagAndEnumManagement::convertToString(const Type &type)
case Type::CODE: return "Code Related";
}
dbgAssert(false) << "Reached an impossible type value of: " << static_cast<int>(type);
dbgAssert(false) << alert << "Reached an impossible type value of: " << static_cast<int>(type);
return "";
}
@@ -201,7 +203,7 @@ TagAndEnumManagement::convertToString(const Level &level)
case Level::CUSTOM: return "Custom";
}
dbgAssert(false) << "Reached an impossible type value of: " << static_cast<int>(level);
dbgAssert(false) << alert << "Reached an impossible type value of: " << static_cast<int>(level);
return "";
}
@@ -216,7 +218,7 @@ TagAndEnumManagement::convertToString(const LogLevel &log_level)
case LogLevel::ERROR: return "error";
}
dbgAssert(false) << "Reached an impossible type value of: " << static_cast<int>(log_level);
dbgAssert(false) << alert << "Reached an impossible type value of: " << static_cast<int>(log_level);
return "";
}
@@ -228,7 +230,7 @@ TagAndEnumManagement::convertToString(const Audience &audience)
case Audience::INTERNAL: return "Internal";
}
dbgAssert(false) << "Reached an impossible audience value of: " << static_cast<int>(audience);
dbgAssert(false) << alert << "Reached an impossible audience value of: " << static_cast<int>(audience);
return "";
}
@@ -242,7 +244,7 @@ TagAndEnumManagement::convertToString(const Priority &priority)
case Priority::LOW: return "Low";
}
dbgAssert(false) << "Reached impossible priority value of: " << static_cast<int>(priority);
dbgAssert(false) << alert << "Reached impossible priority value of: " << static_cast<int>(priority);
return "";
}
@@ -261,7 +263,7 @@ TagAndEnumManagement::convertToString(const Notification &notification)
case Notification::SDWAN_POLICY_WARNING_LOG: return "c58d490e-6aa0-43da-bfaa-7edad0a57b7a";
}
dbgAssert(false) << "Reached impossible notification value of: " << static_cast<int>(notification);
dbgAssert(false) << alert << "Reached impossible notification value of: " << static_cast<int>(notification);
return "";
}
@@ -279,7 +281,7 @@ TagAndEnumManagement::convertToString(const IssuingEngine &issuing_engine)
case IssuingEngine::HORIZON_TELEMETRY_METRICS: return "horizonTelemetryMetrics";
}
dbgAssert(false) << "Reached impossible engine value of: " << static_cast<int>(issuing_engine);
dbgAssert(false) << alert << "Reached impossible engine value of: " << static_cast<int>(issuing_engine);
return "";
}

View File

@@ -0,0 +1,2 @@
add_library(report_messaging report_messaging.cc)
add_subdirectory(report_messaging_ut)

View File

@@ -0,0 +1,74 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "report_messaging.h"
#include <string>
#include "report/log_rest.h"
using namespace std;
static const string url = "/api/v1/agents/events";
ReportMessaging::~ReportMessaging()
{
if (!Singleton::exists<I_Messaging>()) return;
if (!is_async_message) return;
LogRest log_rest(report);
auto messaging = Singleton::Consume<I_Messaging>::by<ReportMessaging>();
try {
messaging->sendAsyncMessage(
HTTPMethod::POST,
url,
log_rest,
message_type_tag,
MessageMetadata(),
force_buffering
);
} catch (...) {}
}
ReportMessaging &
ReportMessaging::operator<<(const LogField &field)
{
report << field;
return *this;
}
class LogRestWithReply : public LogRest
{
public:
LogRestWithReply(const Report &report) : LogRest(report) {}
bool loadJson(const string &) const { return true; }
};
Maybe<void, HTTPResponse>
ReportMessaging::sendReportSynchronously()
{
is_async_message = false;
LogRestWithReply log_rest(report);
auto messaging = Singleton::Consume<I_Messaging>::by<ReportMessaging>();
return messaging->sendSyncMessage(HTTPMethod::POST, url, log_rest, message_type_tag);
}
void
ReportMessaging::setForceBuffering(bool _force_buffering)
{
force_buffering = _force_buffering;
}

View File

@@ -0,0 +1,7 @@
link_directories(${BOOST_ROOT}/lib)
add_unit_test(
report_messaging_ut
"report_messaging_ut.cc"
"report_messaging;report;singleton;-lboost_regex;messaging;"
)

View File

@@ -0,0 +1,412 @@
#include "report_messaging.h"
#include <chrono>
#include <string>
#include <sstream>
#include <vector>
#include "config.h"
#include "config_component.h"
#include "cptest.h"
#include "mock/mock_messaging.h"
#include "mock/mock_time_get.h"
#include "mock/mock_environment.h"
#include "cereal/archives/json.hpp"
#include "cereal/types/string.hpp"
#include "cereal/types/common.hpp"
using namespace std;
using namespace testing;
class ReportObject
{
public:
ReportObject(int _integer_val, string _string_val, vector<int> _vec_val)
:
integer_val(_integer_val),
string_val(_string_val),
vec_val(_vec_val)
{
}
void
serialize(cereal::JSONOutputArchive &ar) const
{
ar(cereal::make_nvp("integerVal", integer_val));
ar(cereal::make_nvp("stringVal", string_val));
ar(cereal::make_nvp("vecVal", vec_val));
}
friend ostream &
operator<<(ostream &os, const ReportObject &)
{
return os;
}
private:
int integer_val;
string string_val;
vector<int> vec_val;
};
class ReportMessagingTest : public Test
{
public:
ReportMessagingTest()
{
EXPECT_CALL(mock_time_get, getWalltime()).WillRepeatedly(Return(chrono::microseconds(0)));
EXPECT_CALL(mock_time_get, getWalltimeStr(_)).WillRepeatedly(Return("Best Time ever"));
}
StrictMock<MockMessaging> mock_messaging;
StrictMock<MockTimeGet> mock_time_get;
private:
ConfigComponent config;
};
TEST_F(ReportMessagingTest, title_only)
{
EXPECT_CALL(
mock_messaging,
sendAsyncMessage(
_,
_,
"{\n"
" \"log\": {\n"
" \"eventTime\": \"Best Time ever\",\n"
" \"eventName\": \"test\",\n"
" \"eventSeverity\": \"Info\",\n"
" \"eventPriority\": \"Low\",\n"
" \"eventType\": \"Event Driven\",\n"
" \"eventLevel\": \"Log\",\n"
" \"eventLogLevel\": \"info\",\n"
" \"eventAudience\": \"Internal\",\n"
" \"eventAudienceTeam\": \"Agent Core\",\n"
" \"eventFrequency\": 0,\n"
" \"eventTags\": [\n"
" \"Access Control\"\n"
" ],\n"
" \"eventSource\": {\n"
" \"eventTraceId\": \"\",\n"
" \"eventSpanId\": \"\",\n"
" \"issuingEngineVersion\": \"\",\n"
" \"serviceName\": \"Unnamed Nano Service\"\n"
" },\n"
" \"eventData\": {\n"
" \"eventObject\": 1\n"
" }\n"
" }\n"
"}",
_,
_,
_
)
).Times(1);
ReportMessaging("test", ReportIS::AudienceTeam::AGENT_CORE, 1, ReportIS::Tags::ACCESS_CONTROL);
}
TEST_F(ReportMessagingTest, sync_sending)
{
EXPECT_CALL(
mock_messaging,
sendSyncMessage(
_,
_,
"{\n"
" \"log\": {\n"
" \"eventTime\": \"Best Time ever\",\n"
" \"eventName\": \"test\",\n"
" \"eventSeverity\": \"Info\",\n"
" \"eventPriority\": \"Low\",\n"
" \"eventType\": \"Event Driven\",\n"
" \"eventLevel\": \"Log\",\n"
" \"eventLogLevel\": \"info\",\n"
" \"eventAudience\": \"Internal\",\n"
" \"eventAudienceTeam\": \"Agent Core\",\n"
" \"eventFrequency\": 0,\n"
" \"eventTags\": [\n"
" \"Access Control\"\n"
" ],\n"
" \"eventSource\": {\n"
" \"eventTraceId\": \"\",\n"
" \"eventSpanId\": \"\",\n"
" \"issuingEngineVersion\": \"\",\n"
" \"serviceName\": \"Unnamed Nano Service\"\n"
" },\n"
" \"eventData\": {\n"
" \"eventObject\": 1\n"
" }\n"
" }\n"
"}",
_,
_
)
).WillOnce(Return(HTTPResponse(HTTPStatusCode::HTTP_OK, "response!!")));
ReportMessaging report("test", ReportIS::AudienceTeam::AGENT_CORE, 1, ReportIS::Tags::ACCESS_CONTROL);
EXPECT_TRUE(report.sendReportSynchronously().ok());
}
TEST_F(ReportMessagingTest, with_buffering)
{
EXPECT_CALL(
mock_messaging,
sendAsyncMessage(
_,
_,
"{\n"
" \"log\": {\n"
" \"eventTime\": \"Best Time ever\",\n"
" \"eventName\": \"test\",\n"
" \"eventSeverity\": \"Info\",\n"
" \"eventPriority\": \"Low\",\n"
" \"eventType\": \"Event Driven\",\n"
" \"eventLevel\": \"Log\",\n"
" \"eventLogLevel\": \"info\",\n"
" \"eventAudience\": \"Internal\",\n"
" \"eventAudienceTeam\": \"Agent Core\",\n"
" \"eventFrequency\": 0,\n"
" \"eventTags\": [\n"
" \"Access Control\"\n"
" ],\n"
" \"eventSource\": {\n"
" \"eventTraceId\": \"\",\n"
" \"eventSpanId\": \"\",\n"
" \"issuingEngineVersion\": \"\",\n"
" \"serviceName\": \"Unnamed Nano Service\"\n"
" },\n"
" \"eventData\": {\n"
" \"eventObject\": 1\n"
" }\n"
" }\n"
"}",
_,
_,
true
)
).Times(1);
ReportMessaging report("test", ReportIS::AudienceTeam::AGENT_CORE, 1, ReportIS::Tags::ACCESS_CONTROL);
report.setForceBuffering(true);
}
TEST_F(ReportMessagingTest, with_dynamic_fields)
{
EXPECT_CALL(
mock_messaging,
sendAsyncMessage(
_,
_,
"{\n"
" \"log\": {\n"
" \"eventTime\": \"Best Time ever\",\n"
" \"eventName\": \"test\",\n"
" \"eventSeverity\": \"Info\",\n"
" \"eventPriority\": \"Low\",\n"
" \"eventType\": \"Event Driven\",\n"
" \"eventLevel\": \"Log\",\n"
" \"eventLogLevel\": \"info\",\n"
" \"eventAudience\": \"Internal\",\n"
" \"eventAudienceTeam\": \"Agent Core\",\n"
" \"eventFrequency\": 0,\n"
" \"eventTags\": [\n"
" \"Access Control\"\n"
" ],\n"
" \"eventSource\": {\n"
" \"eventTraceId\": \"\",\n"
" \"eventSpanId\": \"\",\n"
" \"issuingEngineVersion\": \"\",\n"
" \"serviceName\": \"Unnamed Nano Service\"\n"
" },\n"
" \"eventData\": {\n"
" \"eventObject\": 1,\n"
" \"ASD\": \"QWE\"\n"
" }\n"
" }\n"
"}",
_,
_,
_
)
).Times(1);
ReportMessaging("test", ReportIS::AudienceTeam::AGENT_CORE, 1, ReportIS::Tags::ACCESS_CONTROL)
<< LogField("ASD", "QWE");
}
TEST_F(ReportMessagingTest, custom_event_object)
{
EXPECT_CALL(
mock_messaging,
sendAsyncMessage(
_,
_,
"{\n"
" \"log\": {\n"
" \"eventTime\": \"Best Time ever\",\n"
" \"eventName\": \"test\",\n"
" \"eventSeverity\": \"Info\",\n"
" \"eventPriority\": \"Low\",\n"
" \"eventType\": \"Event Driven\",\n"
" \"eventLevel\": \"Log\",\n"
" \"eventLogLevel\": \"info\",\n"
" \"eventAudience\": \"Internal\",\n"
" \"eventAudienceTeam\": \"Agent Core\",\n"
" \"eventFrequency\": 0,\n"
" \"eventTags\": [\n"
" \"Access Control\"\n"
" ],\n"
" \"eventSource\": {\n"
" \"eventTraceId\": \"\",\n"
" \"eventSpanId\": \"\",\n"
" \"issuingEngineVersion\": \"\",\n"
" \"serviceName\": \"Unnamed Nano Service\"\n"
" },\n"
" \"eventData\": {\n"
" \"eventObject\": {\n"
" \"integerVal\": 1,\n"
" \"stringVal\": \"2\",\n"
" \"vecVal\": [\n"
" 1,\n"
" 2,\n"
" 3\n"
" ]\n"
" }\n"
" }\n"
" }\n"
"}",
_,
_,
_
)
).Times(1);
ReportMessaging(
"test",
ReportIS::AudienceTeam::AGENT_CORE,
ReportObject(1, "2", { 1, 2, 3}),
ReportIS::Tags::ACCESS_CONTROL
);
}
TEST_F(ReportMessagingTest, custom_priority)
{
EXPECT_CALL(
mock_messaging,
sendAsyncMessage(
_,
_,
"{\n"
" \"log\": {\n"
" \"eventTime\": \"Best Time ever\",\n"
" \"eventName\": \"test\",\n"
" \"eventSeverity\": \"High\",\n"
" \"eventPriority\": \"Medium\",\n"
" \"eventType\": \"Event Driven\",\n"
" \"eventLevel\": \"Log\",\n"
" \"eventLogLevel\": \"info\",\n"
" \"eventAudience\": \"Internal\",\n"
" \"eventAudienceTeam\": \"Agent Core\",\n"
" \"eventFrequency\": 0,\n"
" \"eventTags\": [\n"
" \"Access Control\"\n"
" ],\n"
" \"eventSource\": {\n"
" \"eventTraceId\": \"\",\n"
" \"eventSpanId\": \"\",\n"
" \"issuingEngineVersion\": \"\",\n"
" \"serviceName\": \"Unnamed Nano Service\"\n"
" },\n"
" \"eventData\": {\n"
" \"eventObject\": {\n"
" \"integerVal\": 1,\n"
" \"stringVal\": \"2\",\n"
" \"vecVal\": [\n"
" 1,\n"
" 2,\n"
" 3\n"
" ]\n"
" }\n"
" }\n"
" }\n"
"}",
_,
_,
_
)
).Times(1);
ReportMessaging(
"test",
ReportIS::AudienceTeam::AGENT_CORE,
ReportIS::Severity::HIGH,
ReportIS::Priority::MEDIUM,
ReportObject(1, "2", {1, 2, 3}),
ReportIS::Tags::ACCESS_CONTROL
);
}
TEST_F(ReportMessagingTest, with_env_details)
{
StrictMock<MockEnvironment> mock_env;
Context context;
context.registerValue<string>("Service Name", "Access Control App");
context.registerValue<string>("Service Version", "1.2.3.0.0");
I_Environment::ActiveContexts active_context({&context}, true);
EXPECT_CALL(mock_env, getActiveContexts()).WillRepeatedly(ReturnRef(active_context));
EXPECT_CALL(mock_env, getCurrentTrace()).WillOnce(Return(string("best trace")));
EXPECT_CALL(mock_env, getCurrentSpan()).WillOnce(Return(string("best span")));
EXPECT_CALL(
mock_messaging,
sendAsyncMessage(
_,
_,
"{\n"
" \"log\": {\n"
" \"eventTime\": \"Best Time ever\",\n"
" \"eventName\": \"test\",\n"
" \"eventSeverity\": \"High\",\n"
" \"eventPriority\": \"Medium\",\n"
" \"eventType\": \"Event Driven\",\n"
" \"eventLevel\": \"Log\",\n"
" \"eventLogLevel\": \"info\",\n"
" \"eventAudience\": \"Internal\",\n"
" \"eventAudienceTeam\": \"Agent Core\",\n"
" \"eventFrequency\": 0,\n"
" \"eventTags\": [\n"
" \"Access Control\"\n"
" ],\n"
" \"eventSource\": {\n"
" \"eventTraceId\": \"best trace\",\n"
" \"eventSpanId\": \"best span\",\n"
" \"issuingEngineVersion\": \"1.2.3.0.0\",\n"
" \"serviceName\": \"Access Control App\"\n"
" },\n"
" \"eventData\": {\n"
" \"eventObject\": {\n"
" \"integerVal\": 1,\n"
" \"stringVal\": \"2\",\n"
" \"vecVal\": [\n"
" 1,\n"
" 2,\n"
" 3\n"
" ]\n"
" }\n"
" }\n"
" }\n"
"}",
_,
_,
_
)
).Times(1);
ReportMessaging(
"test",
ReportIS::AudienceTeam::AGENT_CORE,
ReportIS::Severity::HIGH,
ReportIS::Priority::MEDIUM,
ReportObject(1, "2", {1, 2, 3}),
ReportIS::Tags::ACCESS_CONTROL
);
}

View File

@@ -25,6 +25,9 @@ public:
virtual Maybe<std::string> getSchema(const std::string &uri) const = 0;
virtual Maybe<std::string> invokeRest(const std::string &uri, std::istream &in) const = 0;
virtual bool isGetCall(const std::string &uri) const = 0;
virtual std::string invokeGet(const std::string &uri) const = 0;
protected:
~I_RestInvoke() {}
};

View File

@@ -97,6 +97,10 @@ RestConn::parseConn() const
stop();
}
if (method=="GET" && invoke->isGetCall(identifier)) {
return sendResponse("200 OK", invoke->invokeGet(identifier), false);
}
stringstream body;
body.str(readSize(len));
@@ -152,15 +156,16 @@ RestConn::readSize(int len) const
}
void
RestConn::sendResponse(const string &status, const string &body) const
RestConn::sendResponse(const string &status, const string &body, bool add_newline) const
{
stringstream stream;
stream <<
"HTTP/1.1 " << status << "\r\n" <<
"Content-Type: application/json\r\n" <<
"Content-Length: " << (body.size() + 2) << "\r\n" <<
"Content-Length: " << (body.size() + (add_newline ? 2 : 0)) << "\r\n" <<
"\r\n" <<
body << "\r\n";
body;
if (add_newline) stream << "\r\n";
string res = stream.str();

View File

@@ -30,7 +30,7 @@ private:
void stop() const;
std::string readLine() const;
std::string readSize(int len) const;
void sendResponse(const std::string &status, const std::string &body) const;
void sendResponse(const std::string &status, const std::string &body, bool add_newline = true) const;
int fd;
I_MainLoop *mainloop;

View File

@@ -31,6 +31,7 @@ USE_DEBUG_FLAG(D_API);
static const int listen_limit = 100;
static const chrono::milliseconds bind_retry_interval_msec = chrono::milliseconds(500);
static const AlertInfo alert(AlertTeam::CORE, "rest i/s");
#include <iostream>
@@ -47,9 +48,12 @@ public:
bool bindRestServerSocket(struct sockaddr_in &addr, vector<uint16_t> port_range);
bool addRestCall(RestAction oper, const string &uri, unique_ptr<RestInit> &&init) override;
bool addGetCall(const string &uri, const function<string()> &cb) override;
uint16_t getListeningPort() const override { return listening_port; }
Maybe<std::string> getSchema(const std::string &uri) const override;
Maybe<std::string> invokeRest(const std::string &uri, istream &in) const override;
Maybe<string> getSchema(const string &uri) const override;
Maybe<string> invokeRest(const string &uri, istream &in) const override;
bool isGetCall(const string &uri) const override;
string invokeGet(const string &uri) const override;
private:
void prepareConfiguration();
@@ -61,6 +65,7 @@ private:
I_MainLoop::RoutineID id;
I_MainLoop *mainloop;
map<string, unique_ptr<RestInit>> rest_calls;
map<string, function<string()>> get_calls;
uint16_t listening_port = 0;
vector<uint16_t> port_range;
};
@@ -96,8 +101,10 @@ RestServer::Impl::prepareConfiguration()
} else {
auto range_start = getPortConfig("Nano service API Port Range start");
auto range_end = getPortConfig("Nano service API Port Range end");
dbgAssert(range_start.ok() && range_end.ok()) << "Rest port configuration was not provided";
dbgAssert(*range_start < *range_end) << "Rest port range corrupted (lower bound higher then upper bound)";
dbgAssert(range_start.ok() && range_end.ok()) << alert << "Rest port configuration was not provided";
dbgAssert(*range_start < *range_end)
<< alert
<< "Rest port range corrupted (lower bound higher then upper bound)";
port_range.resize(*range_end - *range_start);
for (uint16_t i = 0, port = *range_start; i < port_range.size(); i++, port++) {
@@ -113,7 +120,7 @@ RestServer::Impl::init()
auto init_connection = [this] () {
fd = socket(AF_INET, SOCK_STREAM, 0);
dbgAssert(fd >= 0) << "Failed to open a socket";
dbgAssert(fd >= 0) << alert << "Failed to open a socket";
int socket_enable = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &socket_enable, sizeof(int)) < 0) {
dbgWarning(D_API) << "Could not set the socket options";
@@ -185,11 +192,19 @@ bool
RestServer::Impl::addRestCall(RestAction oper, const string &uri, unique_ptr<RestInit> &&rest)
{
string full_uri = changeActionToString(oper) + uri;
if (get_calls.find(full_uri) != get_calls.end()) return false;
return rest_calls.emplace(make_pair(full_uri, move(rest))).second;
}
Maybe<std::string>
RestServer::Impl::getSchema(const std::string &uri) const
bool
RestServer::Impl::addGetCall(const string &uri, const function<string()> &callback)
{
if (rest_calls.find(uri) != rest_calls.end()) return false;
return get_calls.emplace(uri, callback).second;
}
Maybe<string>
RestServer::Impl::getSchema(const string &uri) const
{
auto iter = rest_calls.find(uri);
if (iter == rest_calls.end()) return genError("No matching REST call was found");
@@ -200,8 +215,8 @@ RestServer::Impl::getSchema(const std::string &uri) const
return out.str();
}
Maybe<std::string>
RestServer::Impl::invokeRest(const std::string &uri, istream &in) const
Maybe<string>
RestServer::Impl::invokeRest(const string &uri, istream &in) const
{
auto iter = rest_calls.find(uri);
if (iter == rest_calls.end()) return genError("No matching REST call was found");
@@ -209,6 +224,19 @@ RestServer::Impl::invokeRest(const std::string &uri, istream &in) const
return instance->performRestCall(in);
}
bool
RestServer::Impl::isGetCall(const string &uri) const
{
return get_calls.find(uri) != get_calls.end();
}
string
RestServer::Impl::invokeGet(const string &uri) const
{
auto instance = get_calls.find(uri);
return instance != get_calls.end() ? instance->second() : "";
}
string
RestServer::Impl::changeActionToString(RestAction oper)
{
@@ -226,7 +254,7 @@ RestServer::Impl::changeActionToString(RestAction oper)
return "delete-";
}
default: {
dbgAssert(false) << "Unknown REST action";
dbgAssert(false) << alert << "Unknown REST action";
return "";
}
}

View File

@@ -1,6 +1,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include "cptest.h"
#include "environment.h"
@@ -143,9 +144,12 @@ TEST_F(RestConfigTest, basic_flow)
auto i_rest = Singleton::Consume<I_RestApi>::from(rest_server);
ASSERT_TRUE(i_rest->addRestCall<TestServer>(RestAction::ADD, "test"));
ASSERT_TRUE(i_rest->addGetCall("stuff", [] () { return string("blabla"); }));
int file_descriptor = socket(AF_INET, SOCK_STREAM, 0);
EXPECT_NE(file_descriptor, -1);
int file_descriptor1 = socket(AF_INET, SOCK_STREAM, 0);
EXPECT_NE(file_descriptor1, -1);
int file_descriptor2 = socket(AF_INET, SOCK_STREAM, 0);
EXPECT_NE(file_descriptor2, -1);
auto primary_port = getConfiguration<uint>("connection", "Nano service API Port Alternative");
struct sockaddr_in sa;
@@ -153,20 +157,34 @@ TEST_F(RestConfigTest, basic_flow)
sa.sin_port = htons(primary_port.unpack());
sa.sin_addr.s_addr = inet_addr("127.0.0.1");
int socket_enable = 1;
EXPECT_EQ(setsockopt(file_descriptor, SOL_SOCKET, SO_REUSEADDR, &socket_enable, sizeof(int)), 0);
EXPECT_EQ(setsockopt(file_descriptor1, SOL_SOCKET, SO_REUSEADDR, &socket_enable, sizeof(int)), 0);
EXPECT_EQ(setsockopt(file_descriptor2, SOL_SOCKET, SO_REUSEADDR, &socket_enable, sizeof(int)), 0);
EXPECT_CALL(messaging, sendSyncMessage(_, _, _, _, _))
.WillRepeatedly(Return(HTTPResponse(HTTPStatusCode::HTTP_OK, "")));
auto mainloop = Singleton::Consume<I_MainLoop>::from(mainloop_comp);
I_MainLoop::Routine stop_routine = [&] () {
EXPECT_EQ(connect(file_descriptor, (struct sockaddr*)&sa, sizeof(struct sockaddr)), 0);
string msg = "POST /add-test HTTP/1.1\r\nContent-Length: 10\r\n\r\n{\"num\": 5}";
EXPECT_EQ(write(file_descriptor, msg.data(), msg.size()), static_cast<int>(msg.size()));
EXPECT_EQ(connect(file_descriptor1, (struct sockaddr*)&sa, sizeof(struct sockaddr)), 0);
string msg1 = "GET /stuff HTTP/1.1\r\n\r\n";
EXPECT_EQ(write(file_descriptor1, msg1.data(), msg1.size()), static_cast<int>(msg1.size()));
EXPECT_EQ(connect(file_descriptor2, (struct sockaddr*)&sa, sizeof(struct sockaddr)), 0);
string msg2 = "POST /add-test HTTP/1.1\r\nContent-Length: 10\r\n\r\n{\"num\": 5}";
EXPECT_EQ(write(file_descriptor2, msg2.data(), msg2.size()), static_cast<int>(msg2.size()));
while(!TestServer::g_num) {
mainloop->yield(true);
}
struct pollfd s_poll;
s_poll.fd = file_descriptor1;
s_poll.events = POLLIN;
s_poll.revents = 0;
while(poll(&s_poll, 1, 0) <= 0) {
mainloop->yield(true);
}
mainloop->stopAll();
};
mainloop->addOneTimeRoutine(
@@ -178,4 +196,11 @@ TEST_F(RestConfigTest, basic_flow)
mainloop->run();
EXPECT_EQ(TestServer::g_num, 5);
char respose[1000];
EXPECT_EQ(read(file_descriptor1, respose, 1000), 76);
EXPECT_EQ(
string(respose, 76),
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: 6\r\n\r\nblabla"
);
}

View File

@@ -29,8 +29,13 @@ Singleton::unregisterSingleton(std::type_index type, void *ptr)
void *
Singleton::get(const std::type_index &type)
{
dbgAssert(singles[type].size() == 1) << "There is no single element from type '" << type.name() << "', "
"number of elements is " << singles[type].size();
dbgAssert(singles[type].size() == 1)
<< AlertInfo(AlertTeam::CORE, "singleton i/s")
<< "There is no single element from type '"
<< type.name()
<< "', number of elements is "
<< singles[type].size();
return *(singles[type].begin());
}

View File

@@ -28,6 +28,7 @@
#include "debug.h"
static const uint udp_max_packet_size = 1024 * 64;
static const AlertInfo alert(AlertTeam::CORE, "socket i/s");
USE_DEBUG_FLAG(D_SOCKET);
@@ -174,8 +175,8 @@ public:
Maybe<unique_ptr<SocketInternal>>
acceptConn(bool is_blocking, const string &authorized_ip = "")
{
dbgAssert(is_server_socket) << "Failed to accept new connections from a client socket";
dbgAssert(socket_int > 0) << "Called with uninitialized server socket";
dbgAssert(is_server_socket) << alert << "Failed to accept new connections from a client socket";
dbgAssert(socket_int > 0) << alert << "Called with uninitialized server socket";
dbgDebug(D_SOCKET) << "Attempt to accept new socket. Server Socket FD: " << socket_int;
int client_socket;

View File

@@ -52,7 +52,9 @@ public:
setMonotonicTime(microseconds new_time) override
{
if (is_monotomic_set) {
dbgAssert((new_time+monotonic_delta) >= monotonic_now) << "Monotonic time must not go back!";
dbgAssert((new_time+monotonic_delta) >= monotonic_now)
<< AlertInfo(AlertTeam::CORE, "time proxy")
<< "Monotonic time must not go back!";
} else {
// The first time that the monotonic time is been set, we take the current value to be the base line.
// This is in order to avoid the clock going backwards.