summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVasilis Kalintiris <vasilis@netdata.cloud>2022-07-20 10:36:46 +0300
committervkalintiris <vasilis@netdata.cloud>2022-09-29 15:59:39 +0300
commit5c77145d0df7e091d030476c480ab8d9cbceb89e (patch)
tree6d522795d42e485b52019ad59d2392139e139e55
parent8be3acb5cbc1a9f3fd9cb0760ab350e9a2094658 (diff)
Profile plugin.
-rw-r--r--.gitignore4
-rw-r--r--Makefile.am6
-rw-r--r--collectors/profile.plugin/plugin_profile.cc143
-rw-r--r--collectors/profile.plugin/plugin_profile.h18
-rw-r--r--daemon/main.c13
-rw-r--r--daemon/signals.c2
-rw-r--r--daemon/static_threads.c26
-rw-r--r--daemon/static_threads_linux.c8
-rw-r--r--daemon/static_threads_macos.c2
-rw-r--r--libnetdata/log/log.c2
-rw-r--r--ml/Config.cc2
-rwxr-xr-xmultind/driver.py119
-rwxr-xr-xmultind/genconf.py121
-rw-r--r--multind/netdata.conf.tmpl44
-rw-r--r--multind/stream.conf.tmpl15
15 files changed, 504 insertions, 21 deletions
diff --git a/.gitignore b/.gitignore
index 275b5d0076..6d7eca5ff0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -232,3 +232,7 @@ Session.*.vim
# Judy stuff
JudyLTables.c
judyltablesgen
+
+# multind
+multind/nd*
+multind/*.yml
diff --git a/Makefile.am b/Makefile.am
index 34026e9888..9c27347368 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -203,6 +203,11 @@ APPS_PLUGIN_FILES = \
$(LIBNETDATA_FILES) \
$(NULL)
+PROFILE_PLUGIN_FILES = \
+ collectors/profile.plugin/plugin_profile.cc \
+ collectors/profile.plugin/plugin_profile.h \
+ $(NULL)
+
CHECKS_PLUGIN_FILES = \
collectors/checks.plugin/plugin_checks.c \
$(NULL)
@@ -909,6 +914,7 @@ NETDATA_FILES = \
$(API_PLUGIN_FILES) \
$(EXPORTING_ENGINE_FILES) \
$(CHECKS_PLUGIN_FILES) \
+ $(PROFILE_PLUGIN_FILES) \
$(HEALTH_PLUGIN_FILES) \
$(ML_FILES) \
$(ML_TESTS_FILES) \
diff --git a/collectors/profile.plugin/plugin_profile.cc b/collectors/profile.plugin/plugin_profile.cc
new file mode 100644
index 0000000000..ff5faf9ccf
--- /dev/null
+++ b/collectors/profile.plugin/plugin_profile.cc
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "plugin_profile.h"
+
+#include <chrono>
+#include <memory>
+#include <vector>
+#include <random>
+#include <sstream>
+#include <string>
+#include <map>
+
+class Dimension {
+public:
+ Dimension(RRDDIM *RD, size_t Index) :
+ RD(RD), Index(Index), NumUpdates(0) {}
+
+ void update() {
+ collected_number CN = ++NumUpdates % 3 ? Index + 1 : Index;
+ rrddim_set_by_pointer(RD->rrdset, RD, CN);
+ }
+
+private:
+ RRDDIM *RD;
+ size_t Index;
+ size_t NumUpdates;
+};
+
+class Chart {
+ static size_t ChartIdx;
+
+public:
+ static std::shared_ptr<Chart> create(const char *Id, size_t UpdateEvery) {
+ RRDSET *RS = rrdset_create(
+ localhost,
+ "prof_type",
+ Id, // id
+ NULL, // name
+ "prof_family",
+ NULL, // ctx
+ Id, // title
+ "prof_units",
+ "prof_plugin",
+ "prof_module",
+ 41000 + ChartIdx++,
+ static_cast<int>(UpdateEvery),
+ RRDSET_TYPE_LINE
+ );
+ if (!RS)
+ fatal("Could not create chart %s", Id);
+
+ return std::make_shared<Chart>(RS);
+ }
+
+public:
+ Chart(RRDSET *RS) : RS(RS) {}
+
+ void createDimension(const char *Name, size_t Index) {
+ RRDDIM *RD = rrddim_add(
+ RS, Name, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE
+ );
+ if (!RD)
+ fatal("Could not create dimension %s.%s", rrdset_id(RS), Name);
+
+ Dimensions.push_back(std::make_shared<Dimension>(RD, Index));
+ }
+
+ void update(size_t NumIterations) {
+ if ((NumIterations % RS->update_every) != 0)
+ return;
+
+ rrdset_next(RS);
+
+ for (std::shared_ptr<Dimension> &D : Dimensions)
+ D->update();
+ rrdset_done(RS);
+ }
+
+private:
+ RRDSET *RS;
+ std::vector<std::shared_ptr<Dimension>> Dimensions;
+};
+
+size_t Chart::ChartIdx = 0;
+
+static void profile_main_cleanup(void *ptr)
+{
+ struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
+ static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
+
+ info("cleaning up...");
+
+ static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
+}
+
+static std::vector<std::shared_ptr<Chart>> createCharts(size_t NumCharts, unsigned NumDimsPerChart, time_t UpdateEvery) {
+ std::vector<std::shared_ptr<Chart>> Charts;
+ Charts.reserve(NumCharts);
+
+ constexpr size_t Len = 1024;
+ char Buf[Len];
+
+ for (size_t ChartIdx = 0; ChartIdx != NumCharts; ChartIdx++) {
+ snprintfz(Buf, Len, "chart_%zu", ChartIdx + 1);
+ std::shared_ptr<Chart> C = Chart::create(Buf, UpdateEvery);
+
+ for (size_t DimIdx = 0; DimIdx != NumDimsPerChart; DimIdx++) {
+ snprintfz(Buf, Len, "dim_%zu", DimIdx + 1);
+ C->createDimension(Buf, DimIdx + 1);
+ }
+
+ Charts.push_back(std::move(C));
+ }
+
+ return Charts;
+}
+
+static void updateCharts(std::vector<std::shared_ptr<Chart>> &Charts, size_t NumIterations) {
+ for (std::shared_ptr<Chart> &C : Charts)
+ C->update(NumIterations);
+}
+
+void *profile_main(void *ptr)
+{
+ netdata_thread_cleanup_push(profile_main_cleanup, ptr);
+
+ size_t NumCharts = config_get_number(CONFIG_SECTION_GLOBAL, "profplug charts", 1);
+ size_t NumDimsPerChart = config_get_number(CONFIG_SECTION_GLOBAL, "profplug dimensions", 1);
+
+ std::vector<std::shared_ptr<Chart>> Charts = createCharts(NumCharts, NumDimsPerChart, 1);
+
+ heartbeat_t HB;
+ heartbeat_init(&HB);
+ size_t NumIterations = 0;
+
+ while (!netdata_exit) {
+ heartbeat_next(&HB, USEC_PER_SEC);
+ updateCharts(Charts, ++NumIterations);
+ }
+
+ netdata_thread_cleanup_pop(1);
+ return NULL;
+}
diff --git a/collectors/profile.plugin/plugin_profile.h b/collectors/profile.plugin/plugin_profile.h
new file mode 100644
index 0000000000..e5629b9d94
--- /dev/null
+++ b/collectors/profile.plugin/plugin_profile.h
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef PLUGIN_PROFILE_H
+#define PLUGIN_PROFILE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "daemon/common.h"
+
+void *profile_main(void *ptr);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* PLUGIN_PROFILE_H */
diff --git a/daemon/main.c b/daemon/main.c
index 6d3ca585dd..1a82359bfd 100644
--- a/daemon/main.c
+++ b/daemon/main.c
@@ -392,13 +392,10 @@ static void security_init(){
#endif
static void log_init(void) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/debug.log", netdata_configured_log_dir);
- stdout_filename = config_get(CONFIG_SECTION_LOGS, "debug", filename);
-
- snprintfz(filename, FILENAME_MAX, "%s/error.log", netdata_configured_log_dir);
- stderr_filename = config_get(CONFIG_SECTION_LOGS, "error", filename);
+ stdout_filename = config_get(CONFIG_SECTION_LOGS, "debug", "/dev/stdout");
+ stderr_filename = config_get(CONFIG_SECTION_LOGS, "error", "/dev/stdout");
+ char filename[FILENAME_MAX + 1];
snprintfz(filename, FILENAME_MAX, "%s/access.log", netdata_configured_log_dir);
stdaccess_filename = config_get(CONFIG_SECTION_LOGS, "access", filename);
@@ -1440,7 +1437,9 @@ int main(int argc, char **argv) {
// initialize internal registry
registry_init();
// fork the spawn server
+#if 0
spawn_init();
+#endif
/*
* Libuv uv_spawn() uses SIGCHLD internally:
* https://github.com/libuv/libuv/blob/cc51217a317e96510fbb284721d5e6bc2af31e33/src/unix/process.c#L485
@@ -1511,7 +1510,9 @@ int main(int argc, char **argv) {
// ------------------------------------------------------------------------
// Initialize netdata agent command serving from cli and signals
+#if 0
commands_init();
+#endif
info("netdata initialization completed. Enjoy real-time performance monitoring!");
netdata_ready = 1;
diff --git a/daemon/signals.c b/daemon/signals.c
index b991d46bf1..60c679d81a 100644
--- a/daemon/signals.c
+++ b/daemon/signals.c
@@ -257,7 +257,9 @@ void signals_handle(void) {
case NETDATA_SIGNAL_EXIT_CLEANLY:
error_log_limit_unlimited();
info("SIGNAL: Received %s. Cleaning up to exit...", name);
+#if 0
commands_exit();
+#endif
netdata_cleanup_and_exit(0);
exit(0);
break;
diff --git a/daemon/static_threads.c b/daemon/static_threads.c
index 2575bd4ca6..7609eb3081 100644
--- a/daemon/static_threads.c
+++ b/daemon/static_threads.c
@@ -12,13 +12,14 @@ extern void *pluginsd_main(void *ptr);
extern void *service_main(void *ptr);
extern void *statsd_main(void *ptr);
extern void *timex_main(void *ptr);
+extern void *profile_main(void *ptr);
const struct netdata_static_thread static_threads_common[] = {
{
.name = "PLUGIN[timex]",
.config_section = CONFIG_SECTION_PLUGINS,
.config_name = "timex",
- .enabled = 1,
+ .enabled = 0,
.thread = NULL,
.init_routine = NULL,
.start_routine = timex_main
@@ -36,7 +37,7 @@ const struct netdata_static_thread static_threads_common[] = {
.name = "PLUGIN[idlejitter]",
.config_section = CONFIG_SECTION_PLUGINS,
.config_name = "idlejitter",
- .enabled = 1,
+ .enabled = 0,
.thread = NULL,
.init_routine = NULL,
.start_routine = cpuidlejitter_main
@@ -54,7 +55,7 @@ const struct netdata_static_thread static_threads_common[] = {
.name = "GLOBAL_STATS",
.config_section = NULL,
.config_name = NULL,
- .enabled = 1,
+ .enabled = 0,
.thread = NULL,
.init_routine = NULL,
.start_routine = global_statistics_main
@@ -63,7 +64,7 @@ const struct netdata_static_thread static_threads_common[] = {
.name = "HEALTH",
.config_section = NULL,
.config_name = NULL,
- .enabled = 1,
+ .enabled = 0,
.thread = NULL,
.init_routine = NULL,
.start_routine = health_main
@@ -72,7 +73,7 @@ const struct netdata_static_thread static_threads_common[] = {
.name = "PLUGINSD",
.config_section = NULL,
.config_name = NULL,
- .enabled = 1,
+ .enabled = 0,
.thread = NULL,
.init_routine = NULL,
.start_routine = pluginsd_main
@@ -90,7 +91,7 @@ const struct netdata_static_thread static_threads_common[] = {
.name = "STATSD",
.config_section = NULL,
.config_name = NULL,
- .enabled = 1,
+ .enabled = 0,
.thread = NULL,
.init_routine = NULL,
.start_routine = statsd_main
@@ -99,7 +100,7 @@ const struct netdata_static_thread static_threads_common[] = {
.name = "EXPORTING",
.config_section = NULL,
.config_name = NULL,
- .enabled = 1,
+ .enabled = 0,
.thread = NULL,
.init_routine = NULL,
.start_routine = exporting_main
@@ -122,13 +123,22 @@ const struct netdata_static_thread static_threads_common[] = {
.init_routine = NULL,
.start_routine = socket_listen_main_static_threaded
},
+ {
+ .name = "PROFILE",
+ .config_section = NULL,
+ .config_name = NULL,
+ .enabled = 1,
+ .thread = NULL,
+ .init_routine = NULL,
+ .start_routine = profile_main
+ },
#ifdef ENABLE_ACLK
{
.name = "ACLK_Main",
.config_section = NULL,
.config_name = NULL,
- .enabled = 1,
+ .enabled = 0,
.thread = NULL,
.init_routine = NULL,
.start_routine = aclk_main
diff --git a/daemon/static_threads_linux.c b/daemon/static_threads_linux.c
index 5f7a67768e..00bafcb7b7 100644
--- a/daemon/static_threads_linux.c
+++ b/daemon/static_threads_linux.c
@@ -13,7 +13,7 @@ const struct netdata_static_thread static_threads_linux[] = {
.name = "PLUGIN[tc]",
.config_section = CONFIG_SECTION_PLUGINS,
.config_name = "tc",
- .enabled = 1,
+ .enabled = 0,
.thread = NULL,
.init_routine = NULL,
.start_routine = tc_main
@@ -22,7 +22,7 @@ const struct netdata_static_thread static_threads_linux[] = {
.name = "PLUGIN[diskspace]",
.config_section = CONFIG_SECTION_PLUGINS,
.config_name = "diskspace",
- .enabled = 1,
+ .enabled = 0,
.thread = NULL,
.init_routine = NULL,
.start_routine = diskspace_main
@@ -31,7 +31,7 @@ const struct netdata_static_thread static_threads_linux[] = {
.name = "PLUGIN[proc]",
.config_section = CONFIG_SECTION_PLUGINS,
.config_name = "proc",
- .enabled = 1,
+ .enabled = 0,
.thread = NULL,
.init_routine = NULL,
.start_routine = proc_main
@@ -40,7 +40,7 @@ const struct netdata_static_thread static_threads_linux[] = {
.name = "PLUGIN[cgroups]",
.config_section = CONFIG_SECTION_PLUGINS,
.config_name = "cgroups",
- .enabled = 1,
+ .enabled = 0,
.thread = NULL,
.init_routine = NULL,
.start_routine = cgroups_main
diff --git a/daemon/static_threads_macos.c b/daemon/static_threads_macos.c
index ae34a1363d..9991abac01 100644
--- a/daemon/static_threads_macos.c
+++ b/daemon/static_threads_macos.c
@@ -9,7 +9,7 @@ const struct netdata_static_thread static_threads_macos[] = {
.name = "PLUGIN[macos]",
.config_section = CONFIG_SECTION_PLUGINS,
.config_name = "macos",
- .enabled = 1,
+ .enabled = 0,
.thread = NULL,
.init_routine = NULL,
.start_routine = macos_main
diff --git a/libnetdata/log/log.c b/libnetdata/log/log.c
index a209baefc9..f3c32acbb0 100644
--- a/libnetdata/log/log.c
+++ b/libnetdata/log/log.c
@@ -598,7 +598,7 @@ void open_all_log_files() {
// error log throttling
time_t error_log_throttle_period = 1200;
-unsigned long error_log_errors_per_period = 200;
+unsigned long error_log_errors_per_period = 999999;
unsigned long error_log_errors_per_period_backup = 0;
int error_log_limit(int reset) {
diff --git a/ml/Config.cc b/ml/Config.cc
index 63b570156a..3336de6b99 100644
--- a/ml/Config.cc
+++ b/ml/Config.cc
@@ -22,7 +22,7 @@ static T clamp(const T& Value, const T& Min, const T& Max) {
void Config::readMLConfig(void) {
const char *ConfigSectionML = CONFIG_SECTION_ML;
- bool EnableAnomalyDetection = config_get_boolean(ConfigSectionML, "enabled", true);
+ bool EnableAnomalyDetection = config_get_boolean(ConfigSectionML, "enabled", false);
/*
* Read values
diff --git a/multind/driver.py b/multind/driver.py
new file mode 100755
index 0000000000..1d0071a70b
--- /dev/null
+++ b/multind/driver.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+import json
+
+import yaml
+
+from string import Template
+
+import genconf
+
+import click
+
+def to_yaml(child_agents, level_agents):
+ pass
+
+
+class Agent:
+ def __init__(self, executable, uid, web_port, rm):
+ self.executable = executable
+ self.uid = uid
+ self.web_port = web_port
+ self.stream_ports = []
+ self.rm = rm
+
+ def link(self, agent):
+ self.stream_ports.append(agent.web_port)
+
+ def genconf(self):
+ self.cfg = genconf.Config(
+ self.executable,
+ self.uid,
+ self.web_port,
+ self.stream_ports,
+ "dbengine", # mode
+ "no", # replication
+ self.rm, # rm
+ )
+ self.cfg.generate()
+ print(self)
+
+ def window(self):
+ return {
+ 'layout': 'even-horizontal',
+ 'panes': [
+ f"{self.executable} -W keepopenfds -D -c {self.cfg.netdata_config_file}",
+ f"sleep 5 && cd {self.cfg.hostname}/var/log/netdata/ && less -N -S +F error.log"
+ ]
+ }
+
+ def __repr__(self):
+ return f'Agent(uid={self.uid}, web_port={self.web_port}, stream_ports={self.stream_ports})'
+
+ def __str__(self):
+ return f'Agent(uid={self.uid}, web_port={self.web_port}, stream_ports={self.stream_ports})'
+
+@click.command()
+@click.option('--executable', required=True, type=str)
+@click.option('--childs', required=True, type=int)
+@click.option('--levels', required=True, type=int)
+@click.option('--active-active', required=True, type=bool)
+@click.option('--rm', required=True, type=bool)
+def main(executable, childs, levels, active_active, rm):
+ child_agents = [
+ Agent(executable, uid, 20000 + uid, rm) for uid in range(childs)
+ ]
+
+ level_agents = [
+ [
+ Agent(executable, 100 * (level + 1), 20000 + 100 * (level + 1), rm),
+ Agent(executable, 100 * (level + 1) + 1, 20000 + 100 * (level + 1) + 1, rm)
+ ] for level in range(levels)
+ ]
+
+ if not active_active:
+ level_agents = [[x[0]] for x in level_agents]
+
+ for la in level_agents:
+ if len(la) == 2:
+ la[0].link(la[1])
+ la[1].link(la[0])
+
+ if levels != 0:
+ for ca in child_agents:
+ for la in level_agents[0]:
+ ca.link(la)
+
+ for (la, next_la) in zip(level_agents, level_agents[1:]):
+ for lower_level_agent in la:
+ for higher_level_agent in next_la:
+ lower_level_agent.link(higher_level_agent)
+
+ for ca in child_agents:
+ ca.genconf()
+
+ for la in level_agents:
+ for a in la:
+ a.genconf()
+
+ d = {
+ 'name': 'streaming',
+ 'root': os.path.realpath(os.path.dirname(__file__)),
+ 'windows': []
+ }
+
+ for ca in child_agents:
+ d['windows'].append({ f'c{ca.uid}': ca.window() })
+
+ for li, la in enumerate(level_agents):
+ for ai, a in enumerate(la):
+ d['windows'].append({ f'l{li}{ai}': a.window() })
+
+ with open('tmux-session.yml', 'w') as outfile:
+ yaml.dump(d, outfile, default_flow_style=False)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/multind/genconf.py b/multind/genconf.py
new file mode 100755
index 0000000000..ebec37db62
--- /dev/null
+++ b/multind/genconf.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+import json
+import signal
+import shutil
+import subprocess
+import time
+import uuid
+
+from string import Template
+
+import click
+
+memory_modes = [
+ "dbengine", "ram", "save", "map", "alloc", "none",
+]
+
+def expand_template(src, dst, subs):
+ with open(src) as fp:
+ text = fp.read()
+ t = Template(text).substitute(subs)
+
+ with open(dst, "w") as fp:
+ fp.write(t)
+
+class Config:
+ def __init__(self, executable, uid, web_port, stream_ports,
+ mode="dbengine", replication="no", rm="yes"):
+ self.options = {
+ "executable": executable,
+ "uid": uid,
+ "web-port": web_port,
+ "stream-ports": stream_ports,
+ "mode": mode,
+ "replication": replication,
+ "rm": rm,
+ }
+
+ self.hostname = f"nd{uid}"
+
+ self.rfs = os.path.join(os.getcwd(), self.hostname)
+ self.config_directory = os.path.join(self.rfs, "etc/netdata")
+ self.log_directory = os.path.join(self.rfs, "var/log/netdata")
+ self.cache_directory = os.path.join(self.rfs, "var/cache/netdata")
+ self.lib_directory = os.path.join(self.rfs, "var/lib/netdata")
+
+ self.netdata_config_file = os.path.realpath(os.path.join(self.rfs, os.pardir, f"nd{uid}.conf"))
+ self.stream_config_file = os.path.join(self.config_directory, "stream.conf")
+ self.registry_id_file = os.path.join(self.lib_directory, 'registry', 'netdata.public.unique.id')
+
+ self.netdata_opts = {
+ "hostname": self.hostname,
+ "config_directory": self.config_directory,
+ "log_directory": self.log_directory,
+ "cache_directory": self.cache_directory,
+ "lib_directory": self.lib_directory,
+ "memory_mode": mode,
+ "replication": replication,
+ "web_port": web_port,
+ }
+
+ self.stream_opts = {
+ "destinations": " ".join(["tcp:localhost:" + str(port) for port in stream_ports])
+ }
+
+ def generate(self):
+ if self.options["rm"]:
+ try:
+ shutil.rmtree(self.rfs)
+ except:
+ pass
+
+ os.makedirs(self.config_directory, exist_ok=True)
+ os.makedirs(self.log_directory, exist_ok=True)
+ os.makedirs(self.cache_directory, exist_ok=True)
+ os.makedirs(self.lib_directory, exist_ok=True)
+ os.makedirs(os.path.dirname(self.registry_id_file), exist_ok=True)
+
+ expand_template("netdata.conf.tmpl", self.netdata_config_file, self.netdata_opts)
+ expand_template("stream.conf.tmpl", self.stream_config_file, self.stream_opts)
+
+ with open(self.registry_id_file, "w") as fp:
+ fp.write(str(uuid.uuid3(uuid.NAMESPACE_DNS, self.hostname)))
+
+
+ def __str__(self):
+ d = {
+ "opts": self.options,
+ "netdata_opts": self.netdata_opts,
+ "stream_opts": self.stream_opts,
+ }
+ return json.dumps(d, indent=4)
+
+@click.command()
+@click.option('--executable', required=True, type=str)
+@click.option('--uid', required=True, type=int)
+@click.option('--web-port', required=True, type=int)
+@click.option('--stream-ports', required=True, type=int, multiple=True)
+@click.option('--mode', required=False, type=click.Choice(memory_modes), default=memory_modes[0])
+@click.option('--replication', required=False, type=bool, default=False)
+@click.option('--rm', required=False, type=bool, default=False)
+def main(executable, uid, web_port, stream_ports,
+ mode, replication, rm):
+
+ if replication:
+ replication = "yes"
+ else:
+ replication = "no"
+
+ cfg = Config(
+ executable, uid, web_port, stream_ports,
+ mode, str(replication).lower(), rm
+ )
+ print(cfg)
+
+ cfg.generate()
+
+if __name__ == '__main__':
+ main()
diff --git a/multind/netdata.conf.tmpl b/multind/netdata.conf.tmpl
new file mode 100644
index 0000000000..fd938c1423
--- /dev/null
+++ b/multind/netdata.conf.tmpl
@@ -0,0 +1,44 @@
+[global]
+ hostname = $hostname
+ config directory = $config_directory
+ log directory = $log_directory
+ cache directory = $cache_directory
+ lib directory = $lib_directory
+ memory mode = $memory_mode
+ errors flood protection period = 9999
+ errors to trigger flood protection = 999999
+
+[health]
+ enabled = no
+
+[ml]
+ enabled = no
+
+[plugins]
+ enable running new plugins = no
+ timex = no
+ checks = no
+ idlejitter = no
+ tc = no
+ diskspace = no
+ proc = no
+ cgroups = no
+ statsd = no
+ slabinfo = no
+ go.d = no
+ charts.d = no
+ fping = no
+ ioping = no
+ python.d = no
+ apps = no
+ ebpf = no
+ macos = no
+
+[replication]
+ enabled = $replication
+ seconds to replicate on first connection = 3600
+ max queries per second = 128
+ log replication operations = yes
+
+[web]
+ default port = $web_port
diff --git a/multind/stream.conf.tmpl b/multind/stream.conf.tmpl
new file mode 100644
index 0000000000..13fef4a2c5
--- /dev/null
+++ b/multind/stream.conf.tmpl
@@ -0,0 +1,15 @@
+[stream]
+ enabled = yes
+ destination = $destinations
+ api key = 11111111-2222-3333-4444-555555555555
+ timeout seconds = 60
+ send charts matching = *
+ buffer size bytes = 1048576
+ reconnect delay seconds = 5
+ initial clock resync iterations = 60
+ enable compression = yes
+
+[11111111-2222-3333-4444-555555555555]
+ enabled = yes
+ enable compression = yes
+ allow from = *