// 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 "environment.h" #include "context.h" #include "singleton.h" #include "common.h" #include "debug.h" #include "evaluator_registration.h" #include "environment_evaluator.h" #include "i_rest_api.h" #include "rest.h" #include "environment/trace.h" #include "boost/uuid/uuid.hpp" #include "boost/uuid/uuid_generators.hpp" #include "boost/uuid/uuid_io.hpp" #include "config.h" #include "environment/tracing_metric.h" using namespace std; USE_DEBUG_FLAG(D_ENVIRONMENT); USE_DEBUG_FLAG(D_TRACE); class Environment::Impl : public Singleton::Provide::From { public: void init(); void fini(); void preload(); void setActiveTenantAndProfile(const string &tenant_id, const string &profile_id) override; void unsetActiveTenantAndProfile() override; void registerContext(Context *ptr) override; void unregisterContext(Context *ptr) override; ActiveContexts createEnvironment() override; ActiveContexts saveEnvironment() override; void loadEnvironment(ActiveContexts &&env) override; Context & getConfigurationContext() override; map getAllStrings(const EnvKeyAttr::ParamAttr ¶ms) const override; map getAllUints(const EnvKeyAttr::ParamAttr ¶ms) const override; map getAllBools(const EnvKeyAttr::ParamAttr ¶ms) const override; string getCurrentTrace() const override; string getCurrentSpan() const override; map getCurrentHeadersMap() override; void startNewTrace(bool new_span, const string &_trace_id) override; void startNewSpan(Span::ContextType _type, const string &prev_span, const string &trace) override; using on_exit = scope_exit>; on_exit startNewSpanScope(Span::ContextType _type, const string &prev_span, const string &trace) override; void finishTrace(const string &trace) override; void finishSpan(const string &span) override; private: const ActiveContexts & getActiveContexts() const override { return active_contexts; } void loadEnvConfig(); I_TenantManager *tenant_manager = nullptr; ActiveContexts active_contexts; Context global; map active_traces; map active_spans; map tracing_stats; TracingMetric tracing_metric; TraceEvent trace_event; TraceFinishEvent trace_finish_event; bool is_metric_enabled = false; TracingStatus tracing_status = TracingStatus::OFF; bool was_initialized = false; }; class DeclareBooleanVariable : public ServerRest { public: void doCall() override { auto func = genEvaluator(expr); if (!func.ok()) { dbgWarning(D_ENVIRONMENT) << "Failed to generate boolean function: " << func.getErr(); return; } dbgTrace(D_ENVIRONMENT) << "Boolean function was generated"; auto env = Singleton::Consume::from(); env->getConfigurationContext().registerFunc(name, func.unpackMove()); } private: C2S_PARAM(string, name); C2S_PARAM(string, expr); }; void Environment::Impl::loadEnvConfig() { auto tracing_conf = getConfigurationWithDefault(false, "environment", "enable tracing"); if (tracing_status == TracingStatus::DISABLED) return; tracing_status = tracing_conf ? TracingStatus::ON : TracingStatus::OFF; if (tracing_status == TracingStatus::ON && !is_metric_enabled) { auto metric_report_interval = chrono::seconds( getConfigurationWithDefault(600, "environment", "tracingMetricReportInterval") ); tracing_metric.init( "tracing", ReportIS::AudienceTeam::AGENT_CORE, ReportIS::IssuingEngine::AGENT_CORE, metric_report_interval, false ); tracing_metric.registerListener(); is_metric_enabled = true; } } void Environment::Impl::init() { was_initialized = true; loadEnvConfig(); if (!Singleton::exists()) return; auto rest = Singleton::Consume::by(); rest->addRestCall(RestAction::ADD, "declare-boolean-variable"); } void Environment::Impl::fini() { } void Environment::Impl::setActiveTenantAndProfile(const string &tenant_id, const string &profile_id) { if (tenant_manager == nullptr) tenant_manager = Singleton::Consume::by(); tenant_manager->addActiveTenantAndProfile(tenant_id, profile_id); registerValue("ActiveTenantId", tenant_id); registerValue("ActiveProfileId", profile_id); } void Environment::Impl::unsetActiveTenantAndProfile() { getConfigurationContext().unregisterKey("ActiveTenantId"); getConfigurationContext().unregisterKey("ActiveProfileId"); } map Environment::Impl::getAllStrings(const EnvKeyAttr::ParamAttr ¶ms) const { map result; for (auto &iter : active_contexts.first) { auto partial_results = iter->getAllStrings(params); for (auto &entry : partial_results) { result.emplace(entry); } } return result; } map Environment::Impl::getAllUints(const EnvKeyAttr::ParamAttr ¶ms) const { map result; for (auto &iter : active_contexts.first) { auto partial_results = iter->getAllUints(params); for (auto &entry : partial_results) { result.emplace(entry); } } return result; } map Environment::Impl::getAllBools(const EnvKeyAttr::ParamAttr ¶ms) const { map result; for (auto &iter : active_contexts.first) { auto partial_results = iter->getAllBools(params); for (auto &entry : partial_results) { result.emplace(entry); } } return result; } void Environment::Impl::registerContext(Context *ptr) { active_contexts.first.push_back(ptr); } void Environment::Impl::unregisterContext(Context *ptr) { 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(); } string Environment::Impl::getCurrentTrace() const { if (tracing_status != TracingStatus::ON) return ""; auto trace = get("trace id"); if (trace.ok()) return trace.unpack(); return ""; } string Environment::Impl::getCurrentSpan() const { if (tracing_status != TracingStatus::ON) return ""; auto span = get("span id"); if (span.ok()) return span.unpack(); return ""; } map Environment::Impl::getCurrentHeadersMap() { map tracing_headers; auto trace_id = getCurrentTrace(); if (!trace_id.empty()) { tracing_headers["X-Trace-Id"] = trace_id; } else { string correlation_id_string = "00000000-0000-0000-0000-000000000000"; try { boost::uuids::random_generator uuid_random_gen; correlation_id_string = boost::uuids::to_string(uuid_random_gen()); } catch (const boost::uuids::entropy_error &e) { dbgTrace(D_ENVIRONMENT) << "Failed to generate random correlation id - entropy exception. Exception: " << e.what(); tracing_status = TracingStatus::DISABLED; } tracing_headers["X-Trace-Id"] = correlation_id_string; } auto span_id = getCurrentSpan(); if (!span_id.empty()) { tracing_headers["X-Span-Id"] = span_id; } auto exec_name = get("Executable Name"); if (exec_name.ok() && *exec_name != "") { string executable_name = *exec_name; auto file_path_end = executable_name.find_last_of("/"); if (file_path_end != string::npos) { executable_name = executable_name.substr(file_path_end + 1); } auto file_sufix_start = executable_name.find_first_of("."); if (file_sufix_start != string::npos) { executable_name = executable_name.substr(0, file_sufix_start); } tracing_headers["X-Calling-Service"] = executable_name; } return tracing_headers; } void Environment::Impl::startNewTrace(bool new_span, const string &_trace_id) { if (tracing_status != TracingStatus::ON) return; try { TraceWrapper trace(_trace_id); auto trace_id = trace.getTraceId(); active_traces.emplace(trace_id, trace); tracing_stats[trace_id] = 0; if (new_span) { SpanWrapper span(trace_id); auto span_id = span.getSpanId(); active_spans.emplace(span_id, span); } trace_event.setTraceAmount(active_traces.size()); trace_event.notify(); } catch (const boost::uuids::entropy_error &e) { tracing_status = TracingStatus::DISABLED; dbgWarning(D_TRACE) << "Failed to generate random id - entropy exception. Exception: " << e.what(); return; } } void Environment::Impl::startNewSpan(Span::ContextType context_type, const string &prev_span, const string &trace) { if (tracing_status != TracingStatus::ON) return; string selected_trace = !trace.empty() ? trace : getCurrentTrace(); string selected_span = !prev_span.empty() ? prev_span : getCurrentSpan(); try { SpanWrapper span(selected_trace, context_type, selected_span); active_spans.emplace(span.getSpanId(), span); } catch (const boost::uuids::entropy_error &e) { tracing_status = TracingStatus::DISABLED; dbgWarning(D_TRACE) << "Failed to generate random id - entropy exception. Exception: " << e.what(); return; } } void Environment::Impl::finishTrace(const string &trace) { if (tracing_status != TracingStatus::ON) return; auto deleted_trace = trace.empty() ? getCurrentTrace() : trace; if (deleted_trace.empty()) { dbgWarning(D_ENVIRONMENT) << "There is no current trace to finish"; return; } trace_finish_event.setSpanAmount(tracing_stats[deleted_trace]); active_traces.erase(deleted_trace); tracing_stats.erase(deleted_trace); trace_event.setTraceAmount(active_traces.size()); trace_event.notify(); trace_finish_event.notify(); } void Environment::Impl::finishSpan(const string &span) { if (tracing_status != TracingStatus::ON) return; auto deleted_span = span.empty() ? getCurrentSpan() : span; if (deleted_span.empty()) { dbgWarning(D_ENVIRONMENT) << "There is no current span to finish"; return; } auto span_iter = active_spans.find(deleted_span); if (span_iter != active_spans.end()) { auto trace_id = span_iter->second.getTraceId(); tracing_stats[trace_id]++; } active_spans.erase(deleted_span); } scope_exit> Environment::Impl::startNewSpanScope(Span::ContextType context_type, const string &prev_span, const string &trace) { startNewSpan(context_type, prev_span, trace); function release_function = [&] () { finishSpan(""); }; return make_scope_exit(move(release_function)); } I_Environment::ActiveContexts Environment::Impl::createEnvironment() { return ActiveContexts({ &global }, Debug::DebugLockState::getState()); } I_Environment::ActiveContexts Environment::Impl::saveEnvironment() { return move(active_contexts); } void Environment::Impl::loadEnvironment(ActiveContexts &&env) { active_contexts = move(env); Debug::DebugLockState::setState(active_contexts.second); } Context & Environment::Impl::getConfigurationContext() { return global; } void Environment::Impl::preload() { registerBaseEvaluators(); global.activate(); registerExpectedConfiguration("environment", "enable tracing"); registerExpectedConfiguration("environment", "tracingMetricReportInterval"); registerConfigLoadCb( [this] () { if (was_initialized) loadEnvConfig(); } ); } Environment::Environment() : Component("Environment"), pimpl(make_unique()) {} Environment::~Environment() {} void Environment::init() { pimpl->init(); } void Environment::fini() { pimpl->fini(); } void Environment::preload() { pimpl->preload(); }