The status-get command support was ported without High Availability status.
api/statistic-sample-age-set.json
api/statistic-sample-count-set-all.json
api/statistic-sample-count-set.json
+api/status-get.json
api/stat-lease4-get.json
api/stat-lease6-get.json
api/subnet4-add.json
--- /dev/null
+{
+ "avail": "1.6.3",
+ "brief": [
+ "This command returns server's runtime information.",
+ "It takes no arguments."
+ ],
+ "cmd-syntax": [
+ "{",
+ " \"command\": \"status-get\"",
+ "}"
+ ],
+ "description": "See <xref linkend=\"command-status-get\"/>",
+ "name": "status-get",
+ "resp-comment": [
+ "If the libdhcp_ha (High Availability) hooks library is loaded by the DHCP server receiving this command the response also includes the HA specific status information of the server receiving the command and its partner's status."
+ ],
+ "resp-syntax": [
+ "{",
+ " \"result\": <integer>,",
+ " \"arguments\": {",
+ " \"pid\": <integer>,",
+ " \"uptime\": <uptime in seconds>,",
+ " \"reload\": <time since reload in seconds>,",
+ " \"ha-servers\": {",
+ " \"local\": {",
+ " \"role\": <role of this server as in the configuration file>,",
+ " \"scopes\": <list of scope names served by this server>,",
+ " \"state\": <HA state name of the server receiving the command>,",
+ " },",
+ " \"remote\": {",
+ " \"age\": <the age of the remote status in seconds>,",
+ " \"in-touch\": <indicates if this server communicated with remote>,",
+ " \"last-scopes\": <list of scopes served by partner>,",
+ " \"last-state\": <HA state name of the partner>,",
+ " \"role\": <partner role>",
+ " }",
+ " }",
+ " }",
+ "}"
+ ],
+
+ "support": [
+ "kea-dhcp4",
+ "kea-dhcp6",
+ "kea-dhcp-ddns",
+ "kea-ctrl-agent"
+ ]
+}
"command": "dhcp-enable"
}
+.. _command-status-get:
+
+The status-get Command
+----------------------
+
+The ``status-get`` command returns server's runtime information:
+
+ - pid: process id.
+
+ - uptime: number of seconds since the start of the server.
+
+ - reload: number of seconds since the last configuration (re)load.
+
+ - ha-servers: HA specific status information about the DHCP servers
+ configured to use HA hooks library:
+
+ * local: for the local server the state, the role (primary,
+ secondary, ...) and scopes (i.e. what the server is actually
+ processing).
+
+ * remote: for the remote server the last known state, served
+ HA scopes and the role of the server in HA relationship.
+
+The ``ha-servers`` information is only returned when the command is
+sent to the DHCP servers being in the HA setup. This parameter is
+never returned when the ``status-get`` command is sent to the
+Control Agent or DDNS deamon.
+
+To learn more about the HA status information returned by the
+``status-get`` command please refer to the the :ref:`command-ha-status-get`
+section.
+
.. _command-version-get:
The version-get Command
- shutdown
+- status-get
+
- version-get
.. _agent-commands:
- shutdown
+- status-get
+
- version-get
- config-write
- list-commands
- shutdown
+- status-get
- version-get
.. _d2-tsig-key-list-config:
- leases-reclaim
- list-commands
- shutdown
+- status-get
- version-get
as described in :ref:`commands-common`. In addition, it supports the
- leases-reclaim
- list-commands
- shutdown
+- status-get
- version-get
as described in :ref:`commands-common`. In addition, it supports the
{
"command": "ha-continue"
}
+
+.. _command-ha-status-get:
+
+The status-get Command
+------------------------
+
+The ``status-get`` is the general purpose command supported by several Kea deamons,
+not only DHCP servers. However, when sent to the DHCP server with HA enabled, it
+can be used to get insight into the details of the HA specific status information
+of the servers being in the HA configuration. Not only does the response contain
+the status information of the server receiving this command but also the
+information about its partner, if this information is available.
+
+The following is the example response to the ``status-get`` command including
+the HA status of two load balancing servers:
+
+::
+
+ {
+ "result": 0,
+ "text": "",
+ "arguments": {
+ "pid": 1234,
+ "uptime": 3024,
+ "reload": 1111,
+ "ha-servers": {
+ "local": {
+ "role": "primary",
+ "scopes": [ "server1" ],
+ "state": "load-balancing"
+ },
+ "remote": {
+ "age": 10,
+ "in-touch": true,
+ "role": "secondary",
+ "last-scopes": [ "server2" ],
+ "last-state": "load-balancing"
+ }
+ }
+ }
+ }
+
+
+The ``ha-servers`` map contains two structures: ``local`` and ``remote``. The former
+contains the status information of the server which received the command. The
+latter contains the status information known to the local server about the
+partner. The ``role`` of the partner server is gathered from the local
+configuration file, therefore it should always be available. The remaining
+status information such as ``last-scopes`` and ``last-state`` is not available
+until the local server communicates with the remote by successfully sending
+the ``ha-heartbeat`` command. If at least one such communication took place,
+the returned value of ``in-touch`` parameter is set to ``true``. By examining
+this value, the command sender can determine whether the information about
+the remote server is reliable.
+
+The ``last-scopes`` and ``last-state`` contain the information about the
+HA scopes served by the partner and its state. Note that this information
+is gathered during the heartbeat command exchange, so it may not be
+accurate if the communication problem occur between the partners and this
+status information is not refreshed. In such case, it may be useful to
+send the ``status-get`` command to the partner server directly to check
+its current state. The ``age`` parameter specifies the number of seconds
+since the information from the partner was gathered (the age of this
+information).
-// Copyright (C) 2016-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2016-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
CtrlAgentCommandMgr::instance().registerCommand(SHUT_DOWN_COMMAND,
boost::bind(&DControllerBase::shutdownHandler, this, _1, _2));
+ CtrlAgentCommandMgr::instance().registerCommand(STATUS_GET_COMMAND,
+ boost::bind(&DControllerBase::statusGetHandler, this, _1, _2));
+
CtrlAgentCommandMgr::instance().registerCommand(VERSION_GET_COMMAND,
boost::bind(&DControllerBase::versionGetHandler, this, _1, _2));
}
CtrlAgentCommandMgr::instance().deregisterCommand(CONFIG_TEST_COMMAND);
CtrlAgentCommandMgr::instance().deregisterCommand(CONFIG_WRITE_COMMAND);
CtrlAgentCommandMgr::instance().deregisterCommand(SHUT_DOWN_COMMAND);
+ CtrlAgentCommandMgr::instance().deregisterCommand(STATUS_GET_COMMAND);
CtrlAgentCommandMgr::instance().deregisterCommand(VERSION_GET_COMMAND);
}
-// Copyright (C) 2016-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2016-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
#include <process/testutils/d_test_stubs.h>
#include <boost/pointer_cast.hpp>
#include <sstream>
+#include <unistd.h>
using namespace std;
using namespace isc::agent;
checkCommandRegistered("config-write");
checkCommandRegistered("list-commands");
checkCommandRegistered("shutdown");
+ checkCommandRegistered("status-get");
checkCommandRegistered("version-get");
ctrl->deregisterCommands();
ctrl->deregisterCommands();
}
+// Tests that status-get returns expected info (pid, uptime and reload).
+TEST_F(CtrlAgentControllerTest, statusGet) {
+ // Start the server.
+ time_duration elapsed_time;
+ runWithConfig(valid_agent_config, 500, elapsed_time);
+
+ const DControllerBasePtr& ctrl = getController();
+ ConstElementPtr response;
+ ASSERT_NO_THROW(response = ctrl->statusGetHandler("status-get", ConstElementPtr()));
+ ASSERT_TRUE(response);
+ ASSERT_EQ(Element::map, response->getType());
+ EXPECT_EQ(2, response->size());
+ ConstElementPtr result = response->get("result");
+ ASSERT_TRUE(result);
+ ASSERT_EQ(Element::integer, result->getType());
+ EXPECT_EQ(0, result->intValue());
+ ConstElementPtr arguments = response->get("arguments");
+ ASSERT_EQ(Element::map, arguments->getType());
+
+ // The returned pid should be the pid of our process.
+ auto found_pid = arguments->get("pid");
+ ASSERT_TRUE(found_pid);
+ EXPECT_EQ(static_cast<int64_t>(getpid()), found_pid->intValue());
+
+ // It is hard to check the actual uptime (and reload) as it is based
+ // on current time. Let's just make sure it is within a reasonable
+ // range.
+ auto found_uptime = arguments->get("uptime");
+ ASSERT_TRUE(found_uptime);
+ EXPECT_LE(found_uptime->intValue(), 5);
+ EXPECT_GE(found_uptime->intValue(), 0);
+
+ auto found_reload = arguments->get("reload");
+ ASSERT_TRUE(found_reload);
+ EXPECT_LE(found_reload->intValue(), 5);
+ EXPECT_GE(found_reload->intValue(), 0);
+}
+
}
-// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
CommandMgr::instance().registerCommand(SHUT_DOWN_COMMAND,
boost::bind(&D2Controller::shutdownHandler, this, _1, _2));
+ CommandMgr::instance().registerCommand(STATUS_GET_COMMAND,
+ boost::bind(&DControllerBase::statusGetHandler, this, _1, _2));
+
CommandMgr::instance().registerCommand(VERSION_GET_COMMAND,
boost::bind(&D2Controller::versionGetHandler, this, _1, _2));
}
CommandMgr::instance().deregisterCommand(CONFIG_TEST_COMMAND);
CommandMgr::instance().deregisterCommand(CONFIG_WRITE_COMMAND);
CommandMgr::instance().deregisterCommand(SHUT_DOWN_COMMAND);
+ CommandMgr::instance().deregisterCommand(STATUS_GET_COMMAND);
CommandMgr::instance().deregisterCommand(VERSION_GET_COMMAND);
} catch (...) {
-// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
#include <iostream>
#include <sstream>
#include <thread>
+#include <unistd.h>
using namespace std;
using namespace isc;
EXPECT_TRUE(command_list.find("\"config-test\"") != string::npos);
EXPECT_TRUE(command_list.find("\"config-write\"") != string::npos);
EXPECT_TRUE(command_list.find("\"shutdown\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"status-get\"") != string::npos);
EXPECT_TRUE(command_list.find("\"version-get\"") != string::npos);
// Ok, and now delete the server. It should deregister its commands.
checkListCommands(rsp, "version-get");
}
+// This test verifies that the D2 server handles status-get commands
+TEST_F(CtrlChannelD2Test, statusGet) {
+ EXPECT_NO_THROW(createUnixChannelServer());
+
+ std::string response_txt;
+
+ // Send the version-get command
+ sendUnixCommand("{ \"command\": \"status-get\" }", response_txt);
+ ConstElementPtr response;
+ ASSERT_NO_THROW(response = Element::fromJSON(response_txt));
+ ASSERT_TRUE(response);
+ ASSERT_EQ(Element::map, response->getType());
+ EXPECT_EQ(2, response->size());
+ ConstElementPtr result = response->get("result");
+ ASSERT_TRUE(result);
+ ASSERT_EQ(Element::integer, result->getType());
+ EXPECT_EQ(0, result->intValue());
+ ConstElementPtr arguments = response->get("arguments");
+ ASSERT_EQ(Element::map, arguments->getType());
+
+ // The returned pid should be the pid of our process.
+ auto found_pid = arguments->get("pid");
+ ASSERT_TRUE(found_pid);
+ EXPECT_EQ(static_cast<int64_t>(getpid()), found_pid->intValue());
+
+ // It is hard to check the actual reload time as it is based
+ // on current time. Let's just make sure it is within a reasonable
+ // range.
+ auto found_reload = arguments->get("reload");
+ ASSERT_TRUE(found_reload);
+ EXPECT_LE(found_reload->intValue(), 5);
+ EXPECT_GE(found_reload->intValue(), 0);
+
+ /// @todo uptime is not available in this test, because the launch()
+ /// function is not called. This is not critical to test here,
+ /// because the same logic is tested for CA and in that case the
+ /// uptime is tested.
+}
+
// Tests if the server returns its configuration using config-get.
// Note there are separate tests that verify if toElement() called by the
// config-get handler are actually converting the configuration correctly.
void
ControlledDhcpv4Srv::init(const std::string& file_name) {
+ // Keep the call timestamp.
+ start_ = boost::posix_time::second_clock::universal_time();
+
// Configure the server using JSON file.
ConstElementPtr result = loadConfigFile(file_name);
int rcode;
return (createAnswer(CONTROL_RESULT_SUCCESS, response));
}
+ConstElementPtr
+ControlledDhcpv4Srv::commandStatusGetHandler(const string&,
+ ConstElementPtr /*args*/) {
+ ElementPtr status = Element::createMap();
+ status->set("pid", Element::create(static_cast<int>(getpid())));
+
+ auto now = boost::posix_time::second_clock::universal_time();
+ // Sanity check: start_ is always initialized.
+ if (!start_.is_not_a_date_time()) {
+ auto uptime = now - start_;
+ status->set("uptime", Element::create(uptime.total_seconds()));
+ }
+
+ auto last_commit = CfgMgr::instance().getCurrentCfg()->getLastCommitTime();
+ if (!last_commit.is_not_a_date_time()) {
+ auto reload = now - last_commit;
+ status->set("reload", Element::create(reload.total_seconds()));
+ }
+
+ // todo: number of service threads.
+
+ return (createAnswer(0, status));
+}
+
ConstElementPtr
ControlledDhcpv4Srv::processCommand(const string& command,
ConstElementPtr args) {
} else if (command == "server-tag-get") {
return (srv->commandServerTagGetHandler(command, args));
+ } else if (command == "status-get") {
+ return (srv->commandStatusGetHandler(command, args));
}
ConstElementPtr answer = isc::config::createAnswer(1,
"Unrecognized command:" + command);
CommandMgr::instance().registerCommand("shutdown",
boost::bind(&ControlledDhcpv4Srv::commandShutdownHandler, this, _1, _2));
+ CommandMgr::instance().registerCommand("status-get",
+ boost::bind(&ControlledDhcpv4Srv::commandStatusGetHandler, this, _1, _2));
+
CommandMgr::instance().registerCommand("version-get",
boost::bind(&ControlledDhcpv4Srv::commandVersionGetHandler, this, _1, _2));
CommandMgr::instance().deregisterCommand("statistic-sample-age-set-all");
CommandMgr::instance().deregisterCommand("statistic-sample-count-set");
CommandMgr::instance().deregisterCommand("statistic-sample-count-set-all");
+ CommandMgr::instance().deregisterCommand("status-get");
CommandMgr::instance().deregisterCommand("version-get");
} catch (...) {
commandServerTagGetHandler(const std::string& command,
isc::data::ConstElementPtr args);
+ /// @brief handler for processing 'status-get' command
+ ///
+ /// This handler processes status-get command, which retrieves
+ /// the server process information i.e. the pid and returns it in response.
+ ///
+ /// @param command (ignored)
+ /// @param args (ignored)
+ /// @return process information wrapped in a response
+ isc::data::ConstElementPtr
+ commandStatusGetHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
/// @brief Reclaims expired IPv4 leases and reschedules timer.
///
/// This is a wrapper method for @c AllocEngine::reclaimExpiredLeases4.
-// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
EXPECT_TRUE(command_list.find("\"statistic-sample-age-set-all\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-sample-count-set\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-sample-count-set-all\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"status-get\"") != string::npos);
EXPECT_TRUE(command_list.find("\"version-get\"") != string::npos);
// Ok, and now delete the server. It should deregister its commands.
expected = "{ \"arguments\": { \"server-tag\": \"foobar\" }, \"result\": 0 }";
}
+// This test verifies that the DHCP server handles status-get commands
+TEST_F(CtrlChannelDhcpv4SrvTest, statusGet) {
+ createUnixChannelServer();
+
+ // start_ is initialized by init.
+ ASSERT_THROW(server_->init("/no/such/file"), BadValue);
+
+ std::string response_txt;
+
+ // Send the version-get command
+ sendUnixCommand("{ \"command\": \"status-get\" }", response_txt);
+ ConstElementPtr response;
+ ASSERT_NO_THROW(response = Element::fromJSON(response_txt));
+ ASSERT_TRUE(response);
+ ASSERT_EQ(Element::map, response->getType());
+ EXPECT_EQ(2, response->size());
+ ConstElementPtr result = response->get("result");
+ ASSERT_TRUE(result);
+ ASSERT_EQ(Element::integer, result->getType());
+ EXPECT_EQ(0, result->intValue());
+ ConstElementPtr arguments = response->get("arguments");
+ ASSERT_EQ(Element::map, arguments->getType());
+
+ // The returned pid should be the pid of our process.
+ auto found_pid = arguments->get("pid");
+ ASSERT_TRUE(found_pid);
+ EXPECT_EQ(static_cast<int64_t>(getpid()), found_pid->intValue());
+
+ // It is hard to check the actual uptime (and reload) as it is based
+ // on current time. Let's just make sure it is within a reasonable
+ // range.
+ auto found_uptime = arguments->get("uptime");
+ ASSERT_TRUE(found_uptime);
+ EXPECT_LE(found_uptime->intValue(), 5);
+ EXPECT_GE(found_uptime->intValue(), 0);
+
+ auto found_reload = arguments->get("reload");
+ ASSERT_TRUE(found_reload);
+ EXPECT_LE(found_reload->intValue(), 5);
+ EXPECT_GE(found_reload->intValue(), 0);
+}
+
// This test verifies that the DHCP server immediately removed expired
// This test verifies that the DHCP server immediately removed expired
// leases on leases-reclaim command with remove = true
void
ControlledDhcpv6Srv::init(const std::string& file_name) {
+ // Keep the call timestamp.
+ start_ = boost::posix_time::second_clock::universal_time();
+
// Configure the server using JSON file.
ConstElementPtr result = loadConfigFile(file_name);
int rcode;
return (createAnswer(CONTROL_RESULT_SUCCESS, response));
}
+ConstElementPtr
+ControlledDhcpv6Srv::commandStatusGetHandler(const string&,
+ ConstElementPtr /*args*/) {
+ ElementPtr status = Element::createMap();
+ status->set("pid", Element::create(static_cast<int>(getpid())));
+
+ auto now = boost::posix_time::second_clock::universal_time();
+ // Sanity check: start_ is always initialized.
+ if (!start_.is_not_a_date_time()) {
+ auto uptime = now - start_;
+ status->set("uptime", Element::create(uptime.total_seconds()));
+ }
+
+ auto last_commit = CfgMgr::instance().getCurrentCfg()->getLastCommitTime();
+ if (!last_commit.is_not_a_date_time()) {
+ auto reload = now - last_commit;
+ status->set("reload", Element::create(reload.total_seconds()));
+ }
+
+ // todo: number of service threads.
+
+ return (createAnswer(0, status));
+}
+
isc::data::ConstElementPtr
ControlledDhcpv6Srv::processCommand(const std::string& command,
isc::data::ConstElementPtr args) {
} else if (command == "server-tag-get") {
return (srv->commandServerTagGetHandler(command, args));
+ } else if (command == "status-get") {
+ return (srv->commandStatusGetHandler(command, args));
}
-
return (isc::config::createAnswer(1, "Unrecognized command:"
+ command));
CommandMgr::instance().registerCommand("shutdown",
boost::bind(&ControlledDhcpv6Srv::commandShutdownHandler, this, _1, _2));
+ CommandMgr::instance().registerCommand("status-get",
+ boost::bind(&ControlledDhcpv6Srv::commandStatusGetHandler, this, _1, _2));
+
CommandMgr::instance().registerCommand("version-get",
boost::bind(&ControlledDhcpv6Srv::commandVersionGetHandler, this, _1, _2));
CommandMgr::instance().deregisterCommand("statistic-sample-age-set-all");
CommandMgr::instance().deregisterCommand("statistic-sample-count-set");
CommandMgr::instance().deregisterCommand("statistic-sample-count-set-all");
+ CommandMgr::instance().deregisterCommand("status-get");
CommandMgr::instance().deregisterCommand("version-get");
} catch (...) {
commandServerTagGetHandler(const std::string& command,
isc::data::ConstElementPtr args);
+ /// @brief handler for processing 'status-get' command
+ ///
+ /// This handler processes status-get command, which retrieves
+ /// the server process information i.e. the pid and returns it in response.
+ ///
+ /// @param command (ignored)
+ /// @param args (ignored)
+ /// @return process information wrapped in a response
+ isc::data::ConstElementPtr
+ commandStatusGetHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
/// @brief Reclaims expired IPv6 leases and reschedules timer.
///
/// This is a wrapper method for @c AllocEngine::reclaimExpiredLeases6.
-// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <cstdlib>
+#include <unistd.h>
#include <thread>
using namespace std;
+using namespace isc;
using namespace isc::asiolink;
using namespace isc::config;
using namespace isc::data;
EXPECT_TRUE(command_list.find("\"statistic-sample-age-set-all\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-sample-count-set\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-sample-count-set-all\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"status-get\"") != string::npos);
EXPECT_TRUE(command_list.find("\"version-get\"") != string::npos);
// Ok, and now delete the server. It should deregister its commands.
EXPECT_EQ("{ \"result\": 0, \"text\": \"Shutting down.\" }",response);
}
+// This test verifies that the DHCP server handles status-get commands
+TEST_F(CtrlChannelDhcpv6SrvTest, statusGet) {
+ createUnixChannelServer();
+
+ // start_ is initialized by init.
+ ASSERT_THROW(server_->init("/no/such/file"), BadValue);
+
+ std::string response_txt;
+
+ // Send the version-get command
+ sendUnixCommand("{ \"command\": \"status-get\" }", response_txt);
+ ConstElementPtr response;
+ ASSERT_NO_THROW(response = Element::fromJSON(response_txt));
+ ASSERT_TRUE(response);
+ ASSERT_EQ(Element::map, response->getType());
+ EXPECT_EQ(2, response->size());
+ ConstElementPtr result = response->get("result");
+ ASSERT_TRUE(result);
+ ASSERT_EQ(Element::integer, result->getType());
+ EXPECT_EQ(0, result->intValue());
+ ConstElementPtr arguments = response->get("arguments");
+ ASSERT_EQ(Element::map, arguments->getType());
+
+ // The returned pid should be the pid of our process.
+ auto found_pid = arguments->get("pid");
+ ASSERT_TRUE(found_pid);
+ EXPECT_EQ(static_cast<int64_t>(getpid()), found_pid->intValue());
+
+ // It is hard to check the actual uptime (and reload) as it is based
+ // on current time. Let's just make sure it is within a reasonable
+ // range.
+ auto found_uptime = arguments->get("uptime");
+ ASSERT_TRUE(found_uptime);
+ EXPECT_LE(found_uptime->intValue(), 5);
+ EXPECT_GE(found_uptime->intValue(), 0);
+
+ auto found_reload = arguments->get("reload");
+ ASSERT_TRUE(found_reload);
+ EXPECT_LE(found_reload->intValue(), 5);
+ EXPECT_GE(found_reload->intValue(), 0);
+}
+
// This test verifies that the DHCP server handles version-get commands
TEST_F(CtrlChannelDhcpv6SrvTest, getversion) {
createUnixChannelServer();
-# Copyright (C) 2017-2019 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2017-2020 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
version_test "shell.version"
shell_command_test "shell.list-commands" "list-commands" \
- "[ { \"arguments\": [ \"build-report\", \"config-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"list-commands\", \"shutdown\", \"version-get\" ], \"result\": 0 } ]" ""
+ "[ { \"arguments\": [ \"build-report\", \"config-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"list-commands\", \"shutdown\", \"status-get\", \"version-get\" ], \"result\": 0 } ]" ""
shell_command_test "shell.bogus" "give-me-a-beer" \
"[ { \"result\": 2, \"text\": \"'give-me-a-beer' command not supported. You did not include \\\"service\\\" parameter in the command, which indicates that Kea Control Agent should process this command rather than forward it to one or more Kea servers. If you aimed to send this command to one of the Kea servers you should include the \\\"service\\\" parameter in your request, e.g. \\\"service\\\": [ \\\"dhcp4\\\" ] to forward the command to the DHCPv4 server, or \\\"service\\\": [ \\\"dhcp4\\\", \\\"dhcp6\\\", \\\"d2\\\" ] to forward it to DHCPv4, DHCPv6 and D2 servers etc.\" } ]" ""
shell_command_test "shell.empty-config-test" "config-test" \
-// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
}
}
+ // Set the last commit timestamp.
+ auto now = boost::posix_time::second_clock::universal_time();
+ configuration_->setLastCommitTime(now);
+
// Now we need to set the statistics back.
configuration_->updateStatistics();
}
-// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// This should change the staging configuration so as it becomes a current
// one.
+ auto before = boost::posix_time::second_clock::universal_time();
cfg_mgr.commit();
+ auto after = boost::posix_time::second_clock::universal_time();
const_config = cfg_mgr.getCurrentCfg();
ASSERT_TRUE(const_config);
// Sequence id equal to 1 indicates that the current configuration points
// to the configuration that used to be a staging configuration previously.
EXPECT_EQ(1, const_config->getSequence());
+ // Last commit timestamp should be between before and after.
+ auto reload = const_config->getLastCommitTime();
+ ASSERT_FALSE(reload.is_not_a_date_time());
+ EXPECT_LE(before, reload);
+ EXPECT_GE(after, reload);
// Create a new staging configuration. It should be assigned a new
// sequence id.
-// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
#include <process/config_ctl_info.h>
#include <process/logging_info.h>
#include <util/optional.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
namespace isc {
namespace process {
return (server_tag_);
}
+ /// @brief Returns the last commit timestamp.
+ /// @return the last commit timestamp.
+ boost::posix_time::ptime getLastCommitTime() const {
+ return (last_commit_time_);
+ }
+
+ /// @brief Sets the last commit timestamp.
+ /// @param last_commit_time last commit timestamp.
+ void setLastCommitTime(const boost::posix_time::ptime& last_commit_time) {
+ last_commit_time_ = last_commit_time;
+ }
+
protected:
/// @brief Copies the current configuration to a new configuration.
///
/// @brief Logical name of the server
util::Optional<std::string> server_tag_;
+
+ /// @brief Stores the last commit timestamp.
+ boost::posix_time::ptime last_commit_time_;
};
/// @brief Non-const pointer to the @c ConfigBase.
-// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
post_config_cb();
}
LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg(getConfigSummary(0));
+ // Set the last commit timestamp.
+ auto now = boost::posix_time::second_clock::universal_time();
+ context_->setLastCommitTime(now);
} else {
rollback = true;
}
-// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
<< comment->stringValue());
}
+ // Note that the controller was started.
+ start_ = boost::posix_time::second_clock::universal_time();
+
// Everything is clear for launch, so start the application's
// event loop.
try {
return (createAnswer(COMMAND_SUCCESS, response));
}
+ConstElementPtr
+DControllerBase::statusGetHandler(const std::string&, ConstElementPtr) {
+ ElementPtr status = Element::createMap();
+ status->set("pid", Element::create(static_cast<int>(getpid())));
+
+ auto now = boost::posix_time::second_clock::universal_time();
+ if (!start_.is_not_a_date_time()) {
+ auto uptime = now - start_;
+ status->set("uptime", Element::create(uptime.total_seconds()));
+ }
+
+ auto last_commit = process_->getCfgMgr()->getContext()->getLastCommitTime();
+ if (!last_commit.is_not_a_date_time()) {
+ auto reload = now - last_commit;
+ status->set("reload", Element::create(reload.total_seconds()));
+ }
+
+ return (createAnswer(COMMAND_SUCCESS, status));
+}
+
ConstElementPtr
DControllerBase::versionGetHandler(const std::string&, ConstElementPtr) {
ConstElementPtr answer;
-// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
/// 1. parse command line arguments
/// 2. instantiate and initialize the application process
/// 3. load the configuration file
- /// 4. initialize signal handling
- /// 5. start and wait on the application process event loop
- /// 6. exit to the caller
+ /// 4. record the start timestamp
+ /// 5. initialize signal handling
+ /// 6. start and wait on the application process event loop
+ /// 7. exit to the caller
///
/// It is intended to be called from main() and be given the command line
/// arguments.
serverTagGetHandler(const std::string& command,
isc::data::ConstElementPtr args);
+ /// @brief handler for status-get command
+ ///
+ /// This method handles the status-get command, which retrieves
+ /// the server process information i.e. the pid and returns it in
+ /// response.
+ ///
+ /// @param command (ignored)
+ /// @param args (ignored)
+ /// @return process information wrapped in a response
+ isc::data::ConstElementPtr
+ statusGetHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
protected:
/// @brief Virtual method that provides derivations the opportunity to
/// support additional command line options. It is invoked during command
-// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
/// @brief String value for the shutdown command.
static const std::string SHUT_DOWN_COMMAND("shutdown");
+/// @brief String value for the status-get command.
+static const std::string STATUS_GET_COMMAND("status-get");
+
/// @brief Returned by the process to indicate a command was successful.
static const int COMMAND_SUCCESS = 0;
/// Certainly once during process startup, and possibly later if the user
/// alters configuration. This method must not throw, it should catch any
/// processing errors and return a success or failure answer as described
- /// below.
+ /// below. On success the last commit timestamp must be updated.
///
/// @param config_set a new configuration (JSON) for the process
/// @param check_only true if configuration is to be verified only, not applied
-// Copyright (C) 2014-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
#include <util/pid_file.h>
#include <util/signal_set.h>
#include <boost/noncopyable.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
#include <string>
namespace isc {
/// @brief Manufacture the pid file name
std::string makePIDFileName() const;
+ /// @brief Timestamp of the start of the daemon.
+ boost::posix_time::ptime start_;
+
private:
/// @brief Config file name or empty if config file not used.
std::string config_file_;