This is an automated email from the ASF dual-hosted git repository.
alexey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git
The following commit(s) were added to refs/heads/master by this push:
new 50f9347 [mini_chronyd] add reference NTP servers in config
50f9347 is described below
commit 50f9347ceef2e09c56acb5b73b382a49285d6451
Author: Alexey Serbin <[email protected]>
AuthorDate: Tue Sep 10 18:40:27 2019 -0700
[mini_chronyd] add reference NTP servers in config
This patch adds the ability to specify reference NTP servers in chronyd
configuration via the mini_chronyd wrapper. New tests to cover
the newly introduced functionality are added as well.
Change-Id: I6d517bab98afd20ae63f2ff18635c1f579c39adb
Reviewed-on: http://gerrit.cloudera.org:8080/14211
Reviewed-by: Adar Dembo <[email protected]>
Tested-by: Kudu Jenkins
---
src/kudu/clock/test/mini_chronyd-test.cc | 88 ++++++++++++++++++++++++++++++++
src/kudu/clock/test/mini_chronyd.cc | 73 ++++++++++++++++++++++----
src/kudu/clock/test/mini_chronyd.h | 80 +++++++++++++++++++++++++++++
3 files changed, 230 insertions(+), 11 deletions(-)
diff --git a/src/kudu/clock/test/mini_chronyd-test.cc
b/src/kudu/clock/test/mini_chronyd-test.cc
index 6cb01d2..8f6accf 100644
--- a/src/kudu/clock/test/mini_chronyd-test.cc
+++ b/src/kudu/clock/test/mini_chronyd-test.cc
@@ -43,6 +43,38 @@ namespace clock {
class MiniChronydTest: public KuduTest {
};
+// Run chronyd without any reference: neither local reference mode, nor
+// reference NTP server present. Such server cannot be a good NTP source
+// because its clock is unsynchronized.
+TEST_F(MiniChronydTest, UnsynchronizedServer) {
+ MiniChronydOptions options;
+ options.bindaddress = GetBindIpForDaemon(1, kDefaultBindMode);
+ options.port = 10123 + getpid() % 1000;
+ options.local = false;
+ MiniChronyd chrony(options);
+ ASSERT_OK(chrony.Start());
+
+ // No client has talked to the NTP server yet.
+ {
+ MiniChronyd::ServerStats stats;
+ ASSERT_OK(chrony.GetServerStats(&stats));
+ ASSERT_EQ(0, stats.ntp_packets_received);
+ }
+
+ auto s = MiniChronyd::CheckNtpSource(
+ { HostPort(chrony.options().bindaddress, chrony.options().port) });
+ ASSERT_TRUE(s.IsRuntimeError()) << s.ToString();
+ ASSERT_STR_CONTAINS(s.ToString(),
+ "failed measure clock offset from reference NTP
servers");
+
+ // Make sure the client has communicated with the server.
+ {
+ MiniChronyd::ServerStats stats;
+ ASSERT_OK(chrony.GetServerStats(&stats));
+ ASSERT_LT(0, stats.ntp_packets_received);
+ }
+}
+
// This scenario verifies basic functionality of the mini-chronyd wrapper:
// start, stop, manually setting the reference time, etc.
TEST_F(MiniChronydTest, BasicSingleServerInstance) {
@@ -157,5 +189,61 @@ TEST_F(MiniChronydTest, BasicMultipleServerInstances) {
#endif
}
+// This scenario runs multi-tier set of chronyd servers: few servers of
+// stratum X and few more servers of stratum X+1, so the latter use the former
+// as the source for synchronisation. Both set of servers should be a good
clock
+// source to serve NTP clients.
+TEST_F(MiniChronydTest, MultiTierBasic) {
+ const uint16_t base_port = 10123 + getpid() % 1000;
+
+ vector<unique_ptr<MiniChronyd>> servers_0;
+ vector<HostPort> ntp_endpoints_0;
+ for (auto idx = 0; idx < 3; ++idx) {
+ MiniChronydOptions options;
+ options.index = idx;
+ options.bindaddress = GetBindIpForDaemon(idx + 1, kDefaultBindMode);
+ options.port = base_port + idx * 10;
+ unique_ptr<MiniChronyd> chrony(new MiniChronyd(options));
+ ASSERT_OK(chrony->Start());
+ ntp_endpoints_0.emplace_back(chrony->options().bindaddress,
+ chrony->options().port);
+ servers_0.emplace_back(std::move(chrony));
+ }
+
+ vector<unique_ptr<MiniChronyd>> servers_1;
+ vector<HostPort> ntp_endpoints_1;
+ for (auto idx = 3; idx < 5; ++idx) {
+ MiniChronydOptions options;
+ options.index = idx;
+ options.bindaddress = GetBindIpForDaemon(idx + 1, kDefaultBindMode);
+ options.port = base_port + idx * 10;
+ options.local = false;
+ for (const auto& ref : servers_0) {
+ MiniChronydServerOptions server_options;
+ server_options.port = ref->options().port;
+ server_options.address = ref->options().bindaddress;
+ options.servers.emplace_back(std::move(server_options));
+ }
+ unique_ptr<MiniChronyd> chrony(new MiniChronyd(options));
+ ASSERT_OK(chrony->Start());
+ ntp_endpoints_1.emplace_back(chrony->options().bindaddress,
+ chrony->options().port);
+ servers_1.emplace_back(std::move(chrony));
+ }
+
+ {
+ // All chronyd servers that use the system clock as a reference lock should
+ // present themselves as a set of NTP servers suitable for synchronisation.
+ auto s = MiniChronyd::CheckNtpSource(ntp_endpoints_0);
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ }
+ {
+ // All chronyd servers that use the above mentioned chronyd servers
+ // as reference should be a set of good NTP sources as well.
+ auto s = MiniChronyd::CheckNtpSource(ntp_endpoints_1);
+ ASSERT_TRUE(s.ok()) << s.ToString();
+ }
+}
+
} // namespace clock
} // namespace kudu
diff --git a/src/kudu/clock/test/mini_chronyd.cc
b/src/kudu/clock/test/mini_chronyd.cc
index 8f92c5f..183f2b2 100644
--- a/src/kudu/clock/test/mini_chronyd.cc
+++ b/src/kudu/clock/test/mini_chronyd.cc
@@ -57,13 +57,36 @@ using strings::Substitute;
namespace kudu {
namespace clock {
-string MiniChronydOptions::ToString() const {
+string MiniChronydServerOptions::ToString() const {
return Substitute(
- "{bindaddress: $0,"
+ "{address: $0,"
" port: $1,"
- " data_root: $2,"
- " bindcmdaddress: $3}",
- bindaddress, port, data_root, bindcmdaddress);
+ " minpoll: $2,"
+ " maxpoll: $3,"
+ " iburst: $4,"
+ " burst: $5,"
+ " offset: $6}",
+ address, port, minpoll, maxpoll, iburst, burst, offset);
+}
+
+string MiniChronydOptions::ToString() const {
+ string servers_str = "[";
+ for (const auto& s : servers) {
+ servers_str += " " + s.ToString() + ",";
+ }
+ servers_str += "]";
+ return Substitute(
+ "{index: $0,"
+ " data_root: $1,"
+ " bindcmdaddress: $2,"
+ " bindaddress: $3,"
+ " port: $4,"
+ " pidfile: $5,"
+ " local: $6,"
+ " local_stratum: $7,"
+ " servers: $8}",
+ index, data_root, bindcmdaddress, bindaddress, port, pidfile,
+ local, local_stratum, servers_str);
}
// Check that the specified servers are seen as good enough synchronisation
@@ -303,7 +326,7 @@ string MiniChronyd::config_file_path() const {
// instances on the same node, so all the 'defaults' should be customized
// to avoid conflicts.
Status MiniChronyd::CreateConf() {
- static const string kFileTemplate = R"(
+ static const string kFileTemplateCommon = R"(
# Override the default user chronyd NTP server starts because the compiled-in
# default 'root' is not suitable when running chronyd in the context of the
Kudu
# testing framework. It's also be possible to override this parameter in the
@@ -334,10 +357,6 @@ pidfile $4
# is already enabled.
cmdport 0
-# Using the local clock as the clock source (usually it's a high precision
-# oscillator or a NTP server).
-local
-
# NTP clients from all addresses are allowed to access the NTP server that is
# serving requests as specified by the 'bindaddress' and 'port' directives.
allow all
@@ -345,6 +364,18 @@ allow all
# Allow setting the time manually using the cronyc CLI utility.
manual
)";
+
+ static const string kFileTemplateLocal = R"(
+# Use the local clock as the clock source (usually it's a high precision
+# oscillator or a NTP server), and report the stratum as configured.
+local stratum $0
+)";
+
+ static const string kFileTemplateServers = R"(
+# The set of NTP servers to synchronize with.
+$0
+)";
+
if (options_.bindcmdaddress.empty()) {
// The path to Unix domain socket file cannot be longer than ~100 bytes,
// so it's necessary to create a directory with shorter absolute path.
@@ -362,12 +393,32 @@ manual
}
string username;
RETURN_NOT_OK(GetLoggedInUser(&username));
- auto contents = Substitute(kFileTemplate,
+ auto contents = Substitute(kFileTemplateCommon,
username,
options_.bindaddress,
options_.bindcmdaddress,
options_.port,
options_.pidfile);
+ if (options_.local) {
+ contents += Substitute(kFileTemplateLocal, options_.local_stratum);
+ }
+ if (!options_.servers.empty()) {
+ string servers_str;
+ for (const auto& server : options_.servers) {
+ auto str = Substitute(
+ "server $0 port $1 minpoll $2 maxpoll $3 offset $4",
+ server.address, server.port, server.minpoll, server.maxpoll,
server.offset);
+ if (server.iburst) {
+ str += " iburst";
+ }
+ if (server.burst) {
+ str += " burst";
+ }
+ str += "\n";
+ servers_str += str;
+ }
+ contents += Substitute(kFileTemplateServers, servers_str);
+ }
return WriteStringToFile(Env::Default(), contents, config_file_path());
}
diff --git a/src/kudu/clock/test/mini_chronyd.h
b/src/kudu/clock/test/mini_chronyd.h
index fa1862b..5cc5e4f 100644
--- a/src/kudu/clock/test/mini_chronyd.h
+++ b/src/kudu/clock/test/mini_chronyd.h
@@ -34,6 +34,65 @@ class Subprocess;
namespace clock {
+// This structure represents a set of properties for the 'server' configuration
+// directive in the chrony.conf file. All the fields of the structure except
+// for the 'address' directly map into corresponding options of the 'server'
+// configuration directive (see 'man chrony.conf' for details).
+//
+// NOTE: the default values for the most configuration options are different
+// from the defaults used in chronyd's config file (chrony.conf)
+struct MiniChronydServerOptions {
+ // Hostname or IP address of the server.
+ //
+ // Default: ""
+ std::string address;
+
+ // Port number where server listens for NTP requests.
+ //
+ // Default: 123
+ uint16_t port = 123;
+
+ // The minimum interval between requests sent to the server as a power of 2
+ // in seconds.
+ //
+ // Default: -3
+ int8_t minpoll = -3;
+
+ // The maximum interval between requests sent to the server as a power of 2
+ // in seconds.
+ //
+ // Default: 2
+ int8_t maxpoll = 2;
+
+ // With this option enabled, the interval between the first four requests
sent
+ // to the server is much less than interval specified by the 'minpoll'
option,
+ // which allows chronyd to make the first update of the clock shortly after
+ // it has started.
+ //
+ // Default: true
+ bool iburst = true;
+
+ // With this option enabled, chronyd will shorten the interval between up to
+ // four requests to 2 seconds or less when it cannot get a good measurement
+ // from the server.
+ //
+ // Default: true
+ bool burst = true;
+
+ // This option specifies a correction (in seconds) which will be applied to
+ // offsets measured with this source. In test scenarios, this is useful for
+ // fine-tuning the true time offsets as seen by a client in NTP responses
+ // sent from the server. For example, if running multiple chronyd NTP servers
+ // that use the local clock as a reference clock, it's possible to get
precise
+ // distribution of the true time offsets observed by a client.
+ //
+ // Default: 0.0
+ double offset = 0.0;
+
+ // Returns a string representation of the options suitable for debug
printing.
+ std::string ToString() const;
+};
+
// Options to run MiniChronyd with.
struct MiniChronydOptions {
// There might be multiple mini_chronyd run by the same test.
@@ -74,6 +133,27 @@ struct MiniChronydOptions {
// The default may only be used from a gtest unit test.
std::string pidfile;
+ // Whether to run in the 'local reference mode', using the local clock of the
+ // machine as a reference clock. With 'local' set to 'true', chronyd is able
+ // to operate as NTP server synchronised to real time (from the viewpoint of
+ // clients polling it), even if it was never synchronised or the last update
+ // of the clock happened a long time ago.
+ //
+ // Default: true
+ bool local = true;
+
+ // Stratum of the server to report when running in local reference mode.
+ // This directly maps to chronyd's 'stratum' option of the 'local'
+ // configuration directive.
+ //
+ // Default: 10, which is default when running in local reference mode
+ uint8_t local_stratum = 10;
+
+ // NTP servers to use as reference servers for chronyd instance.
+ //
+ // Default: {} (an empty container)
+ std::vector<MiniChronydServerOptions> servers;
+
// Returns a string representation of the options suitable for debug
printing.
std::string ToString() const;
};