diff options
author | andrew-rykov <arykov@ydb.tech> | 2023-03-07 18:24:01 +0300 |
---|---|---|
committer | andrew-rykov <arykov@ydb.tech> | 2023-03-07 18:24:01 +0300 |
commit | 5d02847e8749429797930fa6cae8e06d2d48ea11 (patch) | |
tree | 427f689d1ec3cef29c3d17f6dc97704c84ab068f | |
parent | fbe3047276b6f2f70eff2930af9e4b8251d8d9af (diff) | |
download | ydb-5d02847e8749429797930fa6cae8e06d2d48ea11.tar.gz |
add different backends to audit config
added to run.cpp
change audit config
-rw-r--r-- | ydb/core/audit/CMakeLists.darwin-x86_64.txt | 3 | ||||
-rw-r--r-- | ydb/core/audit/CMakeLists.linux-aarch64.txt | 3 | ||||
-rw-r--r-- | ydb/core/audit/CMakeLists.linux-x86_64.txt | 3 | ||||
-rw-r--r-- | ydb/core/audit/audit_log.cpp | 11 | ||||
-rw-r--r-- | ydb/core/audit/audit_log.h | 47 | ||||
-rw-r--r-- | ydb/core/audit/audit_log_impl.cpp | 99 | ||||
-rw-r--r-- | ydb/core/audit/audit_log_json_impl.cpp | 65 | ||||
-rw-r--r-- | ydb/core/audit/audit_log_txt_impl.cpp | 62 | ||||
-rw-r--r-- | ydb/core/driver_lib/run/kikimr_services_initializers.cpp | 10 | ||||
-rw-r--r-- | ydb/core/log_backend/log_backend.cpp | 67 | ||||
-rw-r--r-- | ydb/core/log_backend/log_backend.h | 2 | ||||
-rw-r--r-- | ydb/core/protos/config.proto | 21 |
12 files changed, 193 insertions, 200 deletions
diff --git a/ydb/core/audit/CMakeLists.darwin-x86_64.txt b/ydb/core/audit/CMakeLists.darwin-x86_64.txt index 82fbe61796..9d1f26857d 100644 --- a/ydb/core/audit/CMakeLists.darwin-x86_64.txt +++ b/ydb/core/audit/CMakeLists.darwin-x86_64.txt @@ -24,8 +24,7 @@ target_link_libraries(ydb-core-audit PUBLIC library-cpp-resource ) target_sources(ydb-core-audit PRIVATE - ${CMAKE_SOURCE_DIR}/ydb/core/audit/audit_log_json_impl.cpp - ${CMAKE_SOURCE_DIR}/ydb/core/audit/audit_log_txt_impl.cpp + ${CMAKE_SOURCE_DIR}/ydb/core/audit/audit_log_impl.cpp ${CMAKE_SOURCE_DIR}/ydb/core/audit/audit_log.cpp ) diff --git a/ydb/core/audit/CMakeLists.linux-aarch64.txt b/ydb/core/audit/CMakeLists.linux-aarch64.txt index a2eac3443c..dcfbf7c9e5 100644 --- a/ydb/core/audit/CMakeLists.linux-aarch64.txt +++ b/ydb/core/audit/CMakeLists.linux-aarch64.txt @@ -25,8 +25,7 @@ target_link_libraries(ydb-core-audit PUBLIC library-cpp-resource ) target_sources(ydb-core-audit PRIVATE - ${CMAKE_SOURCE_DIR}/ydb/core/audit/audit_log_json_impl.cpp - ${CMAKE_SOURCE_DIR}/ydb/core/audit/audit_log_txt_impl.cpp + ${CMAKE_SOURCE_DIR}/ydb/core/audit/audit_log_impl.cpp ${CMAKE_SOURCE_DIR}/ydb/core/audit/audit_log.cpp ) diff --git a/ydb/core/audit/CMakeLists.linux-x86_64.txt b/ydb/core/audit/CMakeLists.linux-x86_64.txt index a2eac3443c..dcfbf7c9e5 100644 --- a/ydb/core/audit/CMakeLists.linux-x86_64.txt +++ b/ydb/core/audit/CMakeLists.linux-x86_64.txt @@ -25,8 +25,7 @@ target_link_libraries(ydb-core-audit PUBLIC library-cpp-resource ) target_sources(ydb-core-audit PRIVATE - ${CMAKE_SOURCE_DIR}/ydb/core/audit/audit_log_json_impl.cpp - ${CMAKE_SOURCE_DIR}/ydb/core/audit/audit_log_txt_impl.cpp + ${CMAKE_SOURCE_DIR}/ydb/core/audit/audit_log_impl.cpp ${CMAKE_SOURCE_DIR}/ydb/core/audit/audit_log.cpp ) diff --git a/ydb/core/audit/audit_log.cpp b/ydb/core/audit/audit_log.cpp index d576f9fedf..df9e5f65e1 100644 --- a/ydb/core/audit/audit_log.cpp +++ b/ydb/core/audit/audit_log.cpp @@ -9,17 +9,10 @@ namespace NKikimr::NAudit { std::atomic<bool> AUDIT_LOG_ENABLED = false; -THolder<NActors::IActor> CreateAuditWriter(THolder<TLogBackend> auditFile, NKikimrConfig::TAuditConfig_EFormat format) +THolder<NActors::IActor> CreateAuditWriter(TMap<NKikimrConfig::TAuditConfig::EFormat, TVector<THolder<TLogBackend>>> logBackends) { AUDIT_LOG_ENABLED.store(true); - switch (format) { - case NKikimrConfig::TAuditConfig::JSON: - return MakeHolder<TAuditJsonLogActor>(std::move(auditFile)); - case NKikimrConfig::TAuditConfig::TXT: - return MakeHolder<TAuditTxtLogActor>(std::move(auditFile)); - default: - return MakeHolder<TAuditJsonLogActor>(std::move(auditFile)); - } + return MakeHolder<TAuditLogActor>(std::move(logBackends)); } void SendAuditLog(const NActors::TActorSystem* sys, TVector<std::pair<TStringBuf, TString>>& parts) diff --git a/ydb/core/audit/audit_log.h b/ydb/core/audit/audit_log.h index d04fc1f060..48b3e0346b 100644 --- a/ydb/core/audit/audit_log.h +++ b/ydb/core/audit/audit_log.h @@ -72,15 +72,15 @@ struct TEvAuditLog }; }; -class TAuditJsonLogActor final - : public TActor<TAuditJsonLogActor> +class TAuditLogActor final + : public TActor<TAuditLogActor> { private: - const THolder<TLogBackend> AuditFile; + const TMap<NKikimrConfig::TAuditConfig::EFormat, TVector<THolder<TLogBackend>>> LogBackends; public: - TAuditJsonLogActor(THolder<TLogBackend> auditFile) + TAuditLogActor(TMap<NKikimrConfig::TAuditConfig::EFormat, TVector<THolder<TLogBackend>>> logBackends) : TActor(&TThis::StateWork) - , AuditFile(std::move(auditFile)) + , LogBackends(std::move(logBackends)) { } @@ -99,35 +99,15 @@ private: const TEvAuditLog::TEvWriteAuditLog::TPtr& ev, const TActorContext& ctx); - void HandleUnexpectedEvent(STFUNC_SIG); -}; - -class TAuditTxtLogActor final - : public TActor<TAuditTxtLogActor> -{ -private: - const THolder<TLogBackend> AuditFile; -public: - TAuditTxtLogActor(THolder<TLogBackend> auditFile) - : TActor(&TThis::StateWork) - , AuditFile(std::move(auditFile)) - { - } - - static constexpr NKikimrServices::TActivity::EType ActorActivityType() { - return NKikimrServices::TActivity::AUDIT_WRITER_ACTOR; - } + static void WriteLog( + const TString& log, + const TVector<THolder<TLogBackend>>& logBackends); -private: - STFUNC(StateWork); + static TString GetJsonLog( + const TEvAuditLog::TEvWriteAuditLog::TPtr& ev); - void HandlePoisonPill( - const TEvents::TEvPoisonPill::TPtr& ev, - const TActorContext& ctx); - - void HandleWriteAuditLog( - const TEvAuditLog::TEvWriteAuditLog::TPtr& ev, - const TActorContext& ctx); + static TString GetTxtLog( + const TEvAuditLog::TEvWriteAuditLog::TPtr& ev); void HandleUnexpectedEvent(STFUNC_SIG); }; @@ -140,7 +120,6 @@ inline NActors::TActorId MakeAuditServiceID() { return NActors::TActorId(0, TStringBuf("YDB_AUDIT")); } -THolder<NActors::IActor> CreateAuditWriter( - THolder<TLogBackend> auditFile, NKikimrConfig::TAuditConfig_EFormat format); +THolder<NActors::IActor> CreateAuditWriter(TMap<NKikimrConfig::TAuditConfig::EFormat, TVector<THolder<TLogBackend>>> logBackends); } // namespace NKikimr::NAudit diff --git a/ydb/core/audit/audit_log_impl.cpp b/ydb/core/audit/audit_log_impl.cpp new file mode 100644 index 0000000000..a8eae22d9e --- /dev/null +++ b/ydb/core/audit/audit_log_impl.cpp @@ -0,0 +1,99 @@ +#include "audit_log.h" +#include "audit_log_impl.h" + +#include <library/cpp/json/json_value.h> +#include <library/cpp/json/json_writer.h> + +namespace NKikimr::NAudit { + +using namespace NActors; + +void TAuditLogActor::HandlePoisonPill( + const TEvents::TEvPoisonPill::TPtr& ev, + const TActorContext& ctx) +{ + Y_UNUSED(ev); + AUDIT_LOG_ENABLED.store(false); + Die(ctx); +} + +STFUNC(TAuditLogActor::StateWork) +{ + switch (ev->GetTypeRewrite()) { + HFunc(TEvents::TEvPoisonPill, HandlePoisonPill); + HFunc(TEvAuditLog::TEvWriteAuditLog, HandleWriteAuditLog); + default: + HandleUnexpectedEvent(ev, ctx); + break; + } +} + +void TAuditLogActor::WriteLog(const TString& log, const TVector<THolder<TLogBackend>>& logBackends) { + for (auto& logBackend : logBackends) { + try { + logBackend->WriteData( + TLogRecord( + ELogPriority::TLOG_INFO, + log.data(), + log.length())); + } catch (const yexception& e) { + LOG_W("WriteLog:" + << " unable to write audit log (error: " << e.what() << ")"); + } + } +} + +TString TAuditLogActor::GetJsonLog(const TEvAuditLog::TEvWriteAuditLog::TPtr& ev) { + const auto* msg = ev->Get(); + TStringStream ss; + ss << msg->Time << ": "; + NJson::TJsonMap m; + for (auto& [k, v] : msg->Parts) { + m[k] = v; + } + NJson::WriteJson(&ss, &m, false, false); + ss << Endl; + return ss.Str(); +} + +TString TAuditLogActor::GetTxtLog(const TEvAuditLog::TEvWriteAuditLog::TPtr& ev) { + const auto* msg = ev->Get(); + TStringStream ss; + ss << msg->Time << ": "; + for (auto it = msg->Parts.begin(); it != msg->Parts.end(); it++) { + if (it != msg->Parts.begin()) + ss << ", "; + ss << it->first << "=" << it->second; + } + ss << Endl; + return ss.Str(); +} + +void TAuditLogActor::HandleWriteAuditLog(const TEvAuditLog::TEvWriteAuditLog::TPtr& ev, const TActorContext& ctx) { + Y_UNUSED(ctx); + + for (auto& logBackends : LogBackends) { + switch (logBackends.first) { + case NKikimrConfig::TAuditConfig::JSON: + WriteLog(GetJsonLog(ev), logBackends.second); + break; + case NKikimrConfig::TAuditConfig::TXT: + WriteLog(GetTxtLog(ev), logBackends.second); + break; + default: + WriteLog(GetJsonLog(ev), logBackends.second); + break; + } + } +} + +void TAuditLogActor::HandleUnexpectedEvent(STFUNC_SIG) +{ + Y_UNUSED(ctx); + + LOG_W("TAuditLogActor:" + << " unhandled event type: " << ev->GetTypeRewrite() + << " event: " << (ev->HasEvent() ? ev->GetBase()->ToString().data() : "serialized?")); +} + +} // namespace NKikimr::NAudit diff --git a/ydb/core/audit/audit_log_json_impl.cpp b/ydb/core/audit/audit_log_json_impl.cpp deleted file mode 100644 index 4f84d2bd8f..0000000000 --- a/ydb/core/audit/audit_log_json_impl.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "audit_log.h" -#include "audit_log_impl.h" - -#include <library/cpp/json/json_value.h> -#include <library/cpp/json/json_writer.h> - -namespace NKikimr::NAudit { - -using namespace NActors; - -void TAuditJsonLogActor::HandlePoisonPill( - const TEvents::TEvPoisonPill::TPtr& ev, - const TActorContext& ctx) -{ - Y_UNUSED(ev); - AUDIT_LOG_ENABLED.store(false); - Die(ctx); -} - -STFUNC(TAuditJsonLogActor::StateWork) -{ - switch (ev->GetTypeRewrite()) { - HFunc(TEvents::TEvPoisonPill, HandlePoisonPill); - HFunc(TEvAuditLog::TEvWriteAuditLog, HandleWriteAuditLog); - default: - HandleUnexpectedEvent(ev, ctx); - break; - } -} - -void TAuditJsonLogActor::HandleWriteAuditLog(const TEvAuditLog::TEvWriteAuditLog::TPtr& ev, const TActorContext& ctx) { - Y_UNUSED(ctx); - const auto* msg = ev->Get(); - try { - TStringStream ss; - ss << msg->Time << ": "; - NJson::TJsonMap m; - for (auto& [k, v] : msg->Parts) { - m[k] = v; - } - NJson::WriteJson(&ss, &m, false, false); - ss << Endl; - auto json = ss.Str(); - - AuditFile->WriteData( - TLogRecord( - ELogPriority::TLOG_INFO, - json.data(), - json.length())); - } catch (const TFileError& e) { - LOG_W("TAuditJsonLogActor:" - << " unable to write audit log (error: " << e.what() << ")"); - } -} - -void TAuditJsonLogActor::HandleUnexpectedEvent(STFUNC_SIG) -{ - Y_UNUSED(ctx); - - LOG_W("TAuditJsonLogActor:" - << " unhandled event type: " << ev->GetTypeRewrite() - << " event: " << (ev->HasEvent() ? ev->GetBase()->ToString().data() : "serialized?")); -} - -} // namespace NKikimr::NAudit diff --git a/ydb/core/audit/audit_log_txt_impl.cpp b/ydb/core/audit/audit_log_txt_impl.cpp deleted file mode 100644 index 5f25119cc7..0000000000 --- a/ydb/core/audit/audit_log_txt_impl.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "audit_log.h" -#include "audit_log_impl.h" - -namespace NKikimr::NAudit { - -using namespace NActors; - -void TAuditTxtLogActor::HandlePoisonPill( - const TEvents::TEvPoisonPill::TPtr& ev, - const TActorContext& ctx) -{ - Y_UNUSED(ev); - AUDIT_LOG_ENABLED.store(false); - Die(ctx); -} - -STFUNC(TAuditTxtLogActor::StateWork) -{ - switch (ev->GetTypeRewrite()) { - HFunc(TEvents::TEvPoisonPill, HandlePoisonPill); - HFunc(TEvAuditLog::TEvWriteAuditLog, HandleWriteAuditLog); - default: - HandleUnexpectedEvent(ev, ctx); - break; - } -} - -void TAuditTxtLogActor::HandleWriteAuditLog(const TEvAuditLog::TEvWriteAuditLog::TPtr& ev, const TActorContext& ctx) { - Y_UNUSED(ctx); - const auto* msg = ev->Get(); - try { - TStringStream ss; - ss << msg->Time << ": "; - for (auto it = msg->Parts.begin(); it != msg->Parts.end(); it++) { - if (it != msg->Parts.begin()) - ss << ", "; - ss << it->first << "=" << it->second; - } - ss << Endl; - auto text = ss.Str(); - - AuditFile->WriteData( - TLogRecord( - ELogPriority::TLOG_INFO, - text.data(), - text.length())); - } catch (const TFileError& e) { - LOG_W("TAuditTxtLogActor:" - << " unable to write audit log (error: " << e.what() << ")"); - } -} - -void TAuditTxtLogActor::HandleUnexpectedEvent(STFUNC_SIG) -{ - Y_UNUSED(ctx); - - LOG_W("TAuditTxtLogActor:" - << " unhandled event type: " << ev->GetTypeRewrite() - << " event: " << (ev->HasEvent() ? ev->GetBase()->ToString().data() : "serialized?")); -} - -} // namespace NKikimr::NAudit diff --git a/ydb/core/driver_lib/run/kikimr_services_initializers.cpp b/ydb/core/driver_lib/run/kikimr_services_initializers.cpp index 9750acab39..0efe573019 100644 --- a/ydb/core/driver_lib/run/kikimr_services_initializers.cpp +++ b/ydb/core/driver_lib/run/kikimr_services_initializers.cpp @@ -2592,12 +2592,12 @@ TAuditWriterInitializer::TAuditWriterInitializer(const TKikimrRunConfig &runConf void TAuditWriterInitializer::InitializeServices(TActorSystemSetup* setup, const TAppData* appData) { - auto fileBackend = CreateAuditLogBackendWithUnifiedAgent(KikimrRunConfig, appData->Counters); - if (!fileBackend) - return; + auto logBackends = CreateAuditLogBackends(KikimrRunConfig, appData->Counters); + + if (logBackends.size() == 0) + return; - const auto format = Config.GetAuditConfig().GetFormat(); - auto actor = NAudit::CreateAuditWriter(std::move(fileBackend), format); + auto actor = NAudit::CreateAuditWriter(std::move(logBackends)); setup->LocalServices.push_back(std::pair<TActorId, TActorSetupCmd>( NAudit::MakeAuditServiceID(), diff --git a/ydb/core/log_backend/log_backend.cpp b/ydb/core/log_backend/log_backend.cpp index aeeabfaf28..249f1b6a77 100644 --- a/ydb/core/log_backend/log_backend.cpp +++ b/ydb/core/log_backend/log_backend.cpp @@ -62,41 +62,80 @@ TAutoPtr<TLogBackend> CreateMeteringLogBackendWithUnifiedAgent( return NActors::CreateStderrBackend(); } -TAutoPtr<TLogBackend> CreateAuditLogBackendWithUnifiedAgent( - const TKikimrRunConfig& runConfig, - NMonitoring::TDynamicCounterPtr counters) +TAutoPtr<TLogBackend> CreateAuditLogFileBackend( + const TKikimrRunConfig& runConfig) { TAutoPtr<TLogBackend> logBackend; if (!runConfig.AppConfig.HasAuditConfig()) return logBackend; const auto& auditConfig = runConfig.AppConfig.GetAuditConfig(); - if (auditConfig.HasAuditFilePath()) { - const auto& filePath = auditConfig.GetAuditFilePath(); + if (auditConfig.HasFileBackend() && auditConfig.GetFileBackend().HasFilePath()) { + const auto& filePath = auditConfig.GetFileBackend().GetFilePath(); try { logBackend = new TFileLogBackend(filePath); } catch (const TFileError& ex) { - Cerr << "CreateAuditLogBackendWithUnifiedAgent: failed to open file '" << filePath << "': " << ex.what() << Endl; + Cerr << "CreateAuditLogFileBackend: failed to open file '" << filePath << "': " << ex.what() << Endl; exit(1); } } - if (auditConfig.GetUnifiedAgentEnable() && runConfig.AppConfig.HasLogConfig() && runConfig.AppConfig.GetLogConfig().HasUAClientConfig()) { + return logBackend; +} + + +TAutoPtr<TLogBackend> CreateAuditLogUnifiedAgentBackend( + const TKikimrRunConfig& runConfig, + NMonitoring::TDynamicCounterPtr counters) +{ + TAutoPtr<TLogBackend> logBackend; + if (!runConfig.AppConfig.HasAuditConfig()) + return logBackend; + + const auto& auditConfig = runConfig.AppConfig.GetAuditConfig(); + if (auditConfig.HasUnifiedAgentBackend() && runConfig.AppConfig.HasLogConfig() && runConfig.AppConfig.GetLogConfig().HasUAClientConfig()) { const auto& logConfig = runConfig.AppConfig.GetLogConfig(); const auto& uaClientConfig = logConfig.GetUAClientConfig(); auto uaCounters = GetServiceCounters(counters, "utils")->GetSubgroup("subsystem", "ua_client"); - auto logName = runConfig.AppConfig.GetAuditConfig().HasLogName() - ? runConfig.AppConfig.GetAuditConfig().GetLogName() + auto logName = runConfig.AppConfig.GetAuditConfig().GetUnifiedAgentBackend().HasLogName() + ? runConfig.AppConfig.GetAuditConfig().GetUnifiedAgentBackend().GetLogName() : uaClientConfig.GetLogName(); - TAutoPtr<TLogBackend> uaLogBackend = TLogBackendBuildHelper::CreateLogBackendFromUAClientConfig(uaClientConfig, uaCounters, logName); - logBackend = logBackend ? NActors::CreateCompositeLogBackend({logBackend, uaLogBackend}) : uaLogBackend; + logBackend = TLogBackendBuildHelper::CreateLogBackendFromUAClientConfig(uaClientConfig, uaCounters, logName); } - if (logBackend) { - return logBackend; + return logBackend; +} + +TMap<NKikimrConfig::TAuditConfig::EFormat, TVector<THolder<TLogBackend>>> CreateAuditLogBackends( + const TKikimrRunConfig& runConfig, + NMonitoring::TDynamicCounterPtr counters) { + TMap<NKikimrConfig::TAuditConfig::EFormat, TVector<THolder<TLogBackend>>> logBackends; + if (runConfig.AppConfig.HasAuditConfig() && runConfig.AppConfig.GetAuditConfig().HasStderrBackend()) { + auto logBackend = NActors::CreateStderrBackend(); + auto format = runConfig.AppConfig.GetAuditConfig().GetStderrBackend().GetFormat(); + logBackends[format].push_back(std::move(logBackend)); } - return NActors::CreateStderrBackend(); + + if (runConfig.AppConfig.HasAuditConfig() && runConfig.AppConfig.GetAuditConfig().HasFileBackend()) { + auto logBackend = CreateAuditLogFileBackend(runConfig); + if (logBackend) { + auto format = runConfig.AppConfig.GetAuditConfig().GetFileBackend().GetFormat(); + logBackends[format].push_back(std::move(logBackend)); + } + } + + if (runConfig.AppConfig.HasAuditConfig() && runConfig.AppConfig.GetAuditConfig().HasUnifiedAgentBackend()) { + auto logBackend = CreateAuditLogUnifiedAgentBackend(runConfig, counters); + if (logBackend) { + auto format = runConfig.AppConfig.GetAuditConfig().GetUnifiedAgentBackend().GetFormat(); + logBackends[format].push_back(std::move(logBackend)); + } + } + + + return logBackends; } + } // NKikimr diff --git a/ydb/core/log_backend/log_backend.h b/ydb/core/log_backend/log_backend.h index aa681adc2f..afdb4c0cd5 100644 --- a/ydb/core/log_backend/log_backend.h +++ b/ydb/core/log_backend/log_backend.h @@ -14,7 +14,7 @@ TAutoPtr<TLogBackend> CreateMeteringLogBackendWithUnifiedAgent( const TKikimrRunConfig& runConfig, NMonitoring::TDynamicCounterPtr counters); -TAutoPtr<TLogBackend> CreateAuditLogBackendWithUnifiedAgent( +TMap<NKikimrConfig::TAuditConfig::EFormat, TVector<THolder<TLogBackend>>> CreateAuditLogBackends( const TKikimrRunConfig& runConfig, NMonitoring::TDynamicCounterPtr counters); diff --git a/ydb/core/protos/config.proto b/ydb/core/protos/config.proto index 6ffb6805d8..ff3db5d32b 100644 --- a/ydb/core/protos/config.proto +++ b/ydb/core/protos/config.proto @@ -1451,10 +1451,23 @@ message TAuditConfig { TXT = 2; } - optional string AuditFilePath = 1; - optional EFormat Format = 2 [default = JSON]; - optional string LogName = 3; - optional bool UnifiedAgentEnable = 4 [default = false]; + message TStderrBackend { + optional EFormat Format = 1 [default = JSON]; + } + + message TFileBackend { + optional EFormat Format = 1 [default = JSON]; + optional string FilePath = 2; + } + + message TUnifiedAgentBackend { + optional EFormat Format = 1 [default = JSON]; + optional string LogName = 2; + } + + optional TStderrBackend StderrBackend = 1; + optional TFileBackend FileBackend = 2; + optional TUnifiedAgentBackend UnifiedAgentBackend = 3; }; message THiveTabletLimit { |