Introduce Endpoint#capabilities

refs #8034
This commit is contained in:
Alexander A. Klimov 2020-10-19 12:31:57 +02:00
parent 9e29936b8f
commit 40ac05c182
6 changed files with 46 additions and 6 deletions

View File

@ -1288,6 +1288,7 @@ params | Dictionary
Key | Type | Description Key | Type | Description
---------------------|-------------|------------------ ---------------------|-------------|------------------
capabilities | Number | Bitmask, see `lib/remote/apilistener.hpp`.
version | Number | Icinga 2 version, e.g. 21300 for v2.13.0. version | Number | Icinga 2 version, e.g. 21300 for v2.13.0.
##### Functions ##### Functions

View File

@ -821,11 +821,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object,
std::set<Endpoint::Ptr> endpoints = zone->GetEndpoints(); std::set<Endpoint::Ptr> endpoints = zone->GetEndpoints();
for (const Endpoint::Ptr& childEndpoint : endpoints) { for (const Endpoint::Ptr& childEndpoint : endpoints) {
if (childEndpoint->GetIcingaVersion() < 21300) { if (!(childEndpoint->GetCapabilities() & (uint_fast64_t)ApiCapabilities::ExecuteArbitraryCommand)) {
/* Update execution */ /* Update execution */
double now = Utility::GetTime(); double now = Utility::GetTime();
pending_execution->Set("exit", 126); pending_execution->Set("exit", 126);
pending_execution->Set("output", "Endpoint '" + childEndpoint->GetName() + "' has version < 2.13."); pending_execution->Set("output", "Endpoint '" + childEndpoint->GetName() + "' doesn't support executing arbitrary commands.");
pending_execution->Set("start", now); pending_execution->Set("start", now);
pending_execution->Set("end", now); pending_execution->Set("end", now);
pending_execution->Remove("pending"); pending_execution->Remove("pending");

View File

@ -641,7 +641,7 @@ Value ClusterEvents::ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin,
std::set<Endpoint::Ptr> endpoints = zone->GetEndpoints(); std::set<Endpoint::Ptr> endpoints = zone->GetEndpoints();
for (const Endpoint::Ptr &childEndpoint : endpoints) { for (const Endpoint::Ptr &childEndpoint : endpoints) {
if (childEndpoint->GetIcingaVersion() < 21300) { if (!(childEndpoint->GetCapabilities() & (uint_fast64_t)ApiCapabilities::ExecuteArbitraryCommand)) {
double now = Utility::GetTime(); double now = Utility::GetTime();
Dictionary::Ptr executedParams = new Dictionary(); Dictionary::Ptr executedParams = new Dictionary();
executedParams->Set("execution", params->Get("source")); executedParams->Set("execution", params->Get("source"));
@ -650,7 +650,7 @@ Value ClusterEvents::ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin,
executedParams->Set("service", params->Get("service")); executedParams->Set("service", params->Get("service"));
executedParams->Set("exit", 126); executedParams->Set("exit", 126);
executedParams->Set("output", executedParams->Set("output",
"Endpoint '" + childEndpoint->GetName() + "' has version < 2.13."); "Endpoint '" + childEndpoint->GetName() + "' doesn't support executing arbitrary commands.");
executedParams->Set("start", now); executedParams->Set("start", now);
executedParams->Set("end", now); executedParams->Set("end", now);

View File

@ -522,6 +522,8 @@ static const auto l_AppVersionInt (([]() -> unsigned long {
+ boost::lexical_cast<unsigned long>(match[3].str()); + boost::lexical_cast<unsigned long>(match[3].str());
})()); })());
static const auto l_MyCapabilities (ApiCapabilities::ExecuteArbitraryCommand);
/** /**
* Processes a new client connection. * Processes a new client connection.
* *
@ -667,7 +669,8 @@ void ApiListener::NewClientHandlerInternal(
{ "jsonrpc", "2.0" }, { "jsonrpc", "2.0" },
{ "method", "icinga::Hello" }, { "method", "icinga::Hello" },
{ "params", new Dictionary({ { "params", new Dictionary({
{ "version", (double)l_AppVersionInt } { "version", (double)l_AppVersionInt },
{ "capabilities", (double)l_MyCapabilities }
}) } }) }
}), yc); }), yc);
@ -705,7 +708,8 @@ void ApiListener::NewClientHandlerInternal(
{ "jsonrpc", "2.0" }, { "jsonrpc", "2.0" },
{ "method", "icinga::Hello" }, { "method", "icinga::Hello" },
{ "params", new Dictionary({ { "params", new Dictionary({
{ "version", (double)l_AppVersionInt } { "version", (double)l_AppVersionInt },
{ "capabilities", (double)l_MyCapabilities }
}) } }) }
}), yc); }), yc);
@ -1643,6 +1647,7 @@ Value ApiListener::HelloAPIHandler(const MessageOrigin::Ptr& origin, const Dicti
if (endpoint) { if (endpoint) {
endpoint->SetIcingaVersion((double)params->Get("version")); endpoint->SetIcingaVersion((double)params->Get("version"));
endpoint->SetCapabilities((double)params->Get("capabilities"));
} }
} }
} }

View File

@ -21,6 +21,7 @@
#include <boost/asio/ip/tcp.hpp> #include <boost/asio/ip/tcp.hpp>
#include <boost/asio/spawn.hpp> #include <boost/asio/spawn.hpp>
#include <boost/asio/ssl/context.hpp> #include <boost/asio/ssl/context.hpp>
#include <cstdint>
#include <set> #include <set>
namespace icinga namespace icinga
@ -38,6 +39,35 @@ struct ConfigDirInformation
Dictionary::Ptr Checksums; Dictionary::Ptr Checksums;
}; };
/**
* If the version reported by icinga::Hello is not enough to tell whether
* the peer has a specific capability, add the latter to this bitmask.
*
* Note that due to the capability exchange via JSON-RPC and the state storage via JSON
* the bitmask numbers are stored in IEEE 754 64-bit floats.
* The latter have 53 digit bits which limit the bitmask.
* Not to run out of bits:
*
* Once all Icinga versions which don't have a specific capability are completely EOL,
* remove the respective capability checks and assume the peer has the capability.
* Once all Icinga versions which still check for the capability are completely EOL,
* remove the respective bit from icinga::Hello.
* Once all Icinga versions which still have the respective bit in icinga::Hello
* are completely EOL, remove the bit here.
* Once all Icinga versions which still have the respective bit here
* are completely EOL, feel free to re-use the bit.
*
* completely EOL = not supported, even if an important customer of us used it and
* not expected to appear in a multi-level cluster, e.g. a 4 level cluster with
* v2.11 -> v2.10 -> v2.9 -> v2.8 - v2.7 isn't here
*
* @ingroup remote
*/
enum class ApiCapabilities : uint_fast64_t
{
ExecuteArbitraryCommand = 1u
};
/** /**
* @ingroup remote * @ingroup remote
*/ */

View File

@ -1,6 +1,7 @@
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
#include "base/configobject.hpp" #include "base/configobject.hpp"
#include <cstdint>
library remote; library remote;
@ -24,6 +25,9 @@ class Endpoint : ConfigObject
[state] "unsigned long" icinga_version { [state] "unsigned long" icinga_version {
default {{{ return 0; }}} default {{{ return 0; }}}
}; };
[state] uint_fast64_t capabilities {
default {{{ return 0; }}}
};
[no_user_modify] bool connecting; [no_user_modify] bool connecting;
[no_user_modify] bool syncing; [no_user_modify] bool syncing;