From 386ccdd61e8256c8b21ee27ee2fc12438fc5ca98 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 17 Oct 2023 11:30:20 +0200 Subject: Adding upstream version 1.43.0. Signed-off-by: Daniel Baumann --- libnetdata/dyn_conf/tests/sample_test_config.json | 22 ++ .../dyn_conf/tests/sub_tests/test_parent_child.rb | 192 +++++++++++++++ libnetdata/dyn_conf/tests/test_dyncfg.rb | 266 +++++++++++++++++++++ libnetdata/dyn_conf/tests/test_plugin/test.plugin | 250 +++++++++++++++++++ 4 files changed, 730 insertions(+) create mode 100644 libnetdata/dyn_conf/tests/sample_test_config.json create mode 100644 libnetdata/dyn_conf/tests/sub_tests/test_parent_child.rb create mode 100755 libnetdata/dyn_conf/tests/test_dyncfg.rb create mode 100755 libnetdata/dyn_conf/tests/test_plugin/test.plugin (limited to 'libnetdata/dyn_conf/tests') diff --git a/libnetdata/dyn_conf/tests/sample_test_config.json b/libnetdata/dyn_conf/tests/sample_test_config.json new file mode 100644 index 000000000..a6595f124 --- /dev/null +++ b/libnetdata/dyn_conf/tests/sample_test_config.json @@ -0,0 +1,22 @@ +{ + "http_endpoints": { + "parent": { + "host": "127.0.0.1", + "mguid": null, + "port": 20001, + "ssl": false + }, + "child": { + "host": "127.0.0.1", + "mguid": "3bc2f7de-1445-11ee-9ed7-3c7c3f21784c", + "port": 19999, + "ssl": false + } + }, + "global": { + "test_plugin_name": "external_plugin", + "test_array_module_name": "module_of_the_future", + "test_single_module_name": "module_of_the_future_single_type", + "test_job_name": "fixed_job" + } +} diff --git a/libnetdata/dyn_conf/tests/sub_tests/test_parent_child.rb b/libnetdata/dyn_conf/tests/sub_tests/test_parent_child.rb new file mode 100644 index 000000000..820db77f8 --- /dev/null +++ b/libnetdata/dyn_conf/tests/sub_tests/test_parent_child.rb @@ -0,0 +1,192 @@ +class ParentChildTest + @@plugin_cfg = <<~HEREDOC +{ "test" : "true" } +HEREDOC + @@plugin_cfg2 = <<~HEREDOC +{ "asdfgh" : "asdfgh" } +HEREDOC + + @@job_cfg = <<~HEREDOC +{ "i am newly created job" : "true" } +HEREDOC + + def initialize + @parent = $config[:http_endpoints][:parent] + @child = $config[:http_endpoints][:child] + @plugin = $config[:global][:test_plugin_name] + @arry_mod = $config[:global][:test_array_module_name] + @single_mod = $config[:global][:test_single_module_name] + @test_job = $config[:global][:test_job_name] + end + def check_test_plugin_modules_list(host, child = nil) + rc = DynCfgHttpClient.get_plugin_module_list(host, @plugin, child) + assert_eq(rc.code, 200, "as HTTP code for get_module_list request on plugin \"#{@plugin}\"") + modules = nil + assert_nothing_raised do + modules = JSON.parse(rc.parsed_response, symbolize_names: true) + end + assert_has_key?(modules, :modules) + assert_eq(modules[:modules].count, 2, "as number of modules in plugin \"#{@plugin}\"") + modules[:modules].each do |m| + assert_has_key?(m, :name) + assert_has_key?(m, :type) + assert_is_one_of(m[:type], "job_array", "single") + end + assert_eq_str(modules[:modules][0][:name], @arry_mod, "name of first module in plugin \"#{@plugin}\"") + assert_eq_str(modules[:modules][1][:name], @single_mod, "name of second module in plugin \"#{@plugin}\"") + end + def run + TEST_SUITE("Parent/Child plugin config") + + TEST("parent/child/get_plugin_list", "Get child (hops:1) plugin list trough parent") + plugins = DynCfgHttpClient.get_plugin_list(@parent, @child) + assert_eq(plugins.code, 200, "as HTTP code for get_plugin_list request") + assert_nothing_raised do + plugins = JSON.parse(plugins.parsed_response, symbolize_names: true) + end + assert_has_key?(plugins, :configurable_plugins) + assert_array_include?(plugins[:configurable_plugins], @plugin) + PASS() + + TEST("parent/child/(set/get)plugin_config", "Set then get and compare child (hops:1) plugin config trough parent") + rc = DynCfgHttpClient.set_plugin_config(@parent, @plugin, @@plugin_cfg, @child) + assert_eq(rc.code, 200, "as HTTP code for set_plugin_config request") + + rc = DynCfgHttpClient.get_plugin_config(@parent, @plugin, @child) + assert_eq(rc.code, 200, "as HTTP code for get_plugin_config request") + assert_eq_str(rc.parsed_response.chomp!, @@plugin_cfg, "as plugin config") + + # We do this twice with different configs to ensure first config was not loaded from persistent storage (from previous tests) + rc = DynCfgHttpClient.set_plugin_config(@parent, @plugin, @@plugin_cfg2, @child) + assert_eq(rc.code, 200, "as HTTP code for set_plugin_config request 2") + + rc = DynCfgHttpClient.get_plugin_config(@parent, @plugin, @child) + assert_eq(rc.code, 200, "as HTTP code for get_plugin_config request 2") + assert_eq_str(rc.parsed_response.chomp!, @@plugin_cfg2, "set/get plugin config 2") + PASS() + + TEST("child/get_plugin_config", "Get child (hops:0) plugin config and compare with what we got trough parent (set_plugin_config from previous test)") + rc = DynCfgHttpClient.get_plugin_config(@child, @plugin, nil) + assert_eq(rc.code, 200, "as HTTP code for get_plugin_config request") + assert_eq_str(rc.parsed_response.chomp!, @@plugin_cfg2.chomp, "as plugin config") + PASS() + + TEST("parent/child/plugin_module_list", "Get child (hops:1) plugin module list trough parent and check its contents") + check_test_plugin_modules_list(@parent, @child) + PASS() + + TEST("child/plugin_module_list", "Get child (hops:0) plugin module list directly and check its contents") + check_test_plugin_modules_list(@child, nil) + PASS() + + TEST("parent/child/module/jobs", "Get list of jobs from child (hops:1) trough parent and check its contents, check job updates") + rc = DynCfgHttpClient.get_job_list(@parent, @plugin, @arry_mod, @child) + assert_eq(rc.code, 200, "as HTTP code for get_jobs request") + jobs = nil + assert_nothing_raised do + jobs = JSON.parse(rc.parsed_response, symbolize_names: true) + end + assert_has_key?(jobs, :jobs) + new_job = jobs[:jobs].find {|i| i[:name] == @test_job} + assert_not_nil(new_job) + assert_has_key?(new_job, :status) + assert_not_eq_str(new_job[:status], "unknown", "job status is other than unknown") + assert_has_key?(new_job, :flags) + assert_array_include?(new_job[:flags], "JOB_FLG_STREAMING_PUSHED") + PASS() + + TEST("child/module/jobs", "Get list of jobs direct from child (hops:0) and check its contents, check job updates") + rc = DynCfgHttpClient.get_job_list(@child, @plugin, @arry_mod, nil) + assert_eq(rc.code, 200, "as HTTP code for get_jobs request") + jobs = nil + assert_nothing_raised do + jobs = JSON.parse(rc.parsed_response, symbolize_names: true) + end + assert_has_key?(jobs, :jobs) + new_job = jobs[:jobs].find {|i| i[:name] == @test_job} + assert_not_nil(new_job) + assert_has_key?(new_job, :status) + assert_not_eq_str(new_job[:status], "unknown", "job status is other than unknown") + assert_has_key?(new_job, :flags) + + assert_array_not_include?(new_job[:flags], "JOB_FLG_STREAMING_PUSHED") # this is plugin directly at child so it should not show this flag + PASS() + + TEST("parent/child/single_module/jobs", "Attempt getting list of jobs from child (hops:1) trough parent on single module. Check it fails properly") + rc = DynCfgHttpClient.get_job_list(@parent, @plugin, @single_mod, @child) + assert_eq(rc.code, 400, "as HTTP code for get_jobs request") + assert_eq_str(rc.parsed_response, '400 - this module is not array type', "as HTTP code for get_jobs request on single module") + PASS() + + created_job = SecureRandom.uuid + TEST("parent/child/module/cr_del_job", "Create and delete job on child (hops:1) trough parent") + # create new job + rc = DynCfgHttpClient.create_job(@parent, @plugin, @arry_mod, created_job, @@job_cfg, @child) + assert_eq_http_code(rc, 200, "as HTTP code for create_job request") + # check this job is in job list @parent + rc = DynCfgHttpClient.get_job_list(@parent, @plugin, @arry_mod, @child) + assert_eq(rc.code, 200, "as HTTP code for get_jobs request") + jobs = nil + assert_nothing_raised do + jobs = JSON.parse(rc.parsed_response, symbolize_names: true) + end + assert_has_key?(jobs, :jobs) + new_job = jobs[:jobs].find {|i| i[:name] == created_job} + assert_not_nil(new_job) + # check this job is in job list @child + rc = DynCfgHttpClient.get_job_list(@child, @plugin, @arry_mod, nil) + assert_eq(rc.code, 200, "as HTTP code for get_jobs request") + jobs = nil + assert_nothing_raised do + jobs = JSON.parse(rc.parsed_response, symbolize_names: true) + end + assert_has_key?(jobs, :jobs) + new_job = jobs[:jobs].find {|i| i[:name] == created_job} + assert_not_nil(new_job) + # check we can get job config back + rc = DynCfgHttpClient.get_job_config(@parent, @plugin, @arry_mod, created_job, @child) + assert_eq(rc.code, 200, "as HTTP code for get_job_config request") + assert_eq_str(rc.parsed_response.chomp!, @@job_cfg, "as job config") + # delete job + rc = DynCfgHttpClient.delete_job(@parent, @plugin, @arry_mod, created_job, @child) + assert_eq(rc.code, 200, "as HTTP code for delete_job request") + # Check it is not in parents job list anymore + rc = DynCfgHttpClient.get_job_list(@parent, @plugin, @arry_mod, @child) + assert_eq(rc.code, 200, "as HTTP code for get_jobs request") + jobs = nil + assert_nothing_raised do + jobs = JSON.parse(rc.parsed_response, symbolize_names: true) + end + assert_has_key?(jobs, :jobs) + new_job = jobs[:jobs].find {|i| i[:name] == created_job} + assert_nil(new_job) + # Check it is not in childs job list anymore + rc = DynCfgHttpClient.get_job_list(@child, @plugin, @arry_mod, nil) + assert_eq(rc.code, 200, "as HTTP code for get_jobs request") + jobs = nil + assert_nothing_raised do + jobs = JSON.parse(rc.parsed_response, symbolize_names: true) + end + assert_has_key?(jobs, :jobs) + new_job = jobs[:jobs].find {|i| i[:name] == created_job} + assert_nil(new_job) + PASS() + + TEST("parent/child/module/del_undeletable_job", "Try delete job on child (child rejects), check failure case works (hops:1)") + # test if plugin rejects job deletion the job still remains in list as it should + rc = DynCfgHttpClient.delete_job(@parent, @plugin, @arry_mod, @test_job, @child) + assert_eq(rc.code, 500, "as HTTP code for delete_job request") + rc = DynCfgHttpClient.get_job_list(@parent, @plugin, @arry_mod, @child) + assert_eq(rc.code, 200, "as HTTP code for get_jobs request") + jobs = nil + assert_nothing_raised do + jobs = JSON.parse(rc.parsed_response, symbolize_names: true) + end + assert_has_key?(jobs, :jobs) + job = jobs[:jobs].find {|i| i[:name] == @test_job} + assert_not_nil(job) + PASS() + end +end + +ParentChildTest.new.run() diff --git a/libnetdata/dyn_conf/tests/test_dyncfg.rb b/libnetdata/dyn_conf/tests/test_dyncfg.rb new file mode 100755 index 000000000..1b4b3a068 --- /dev/null +++ b/libnetdata/dyn_conf/tests/test_dyncfg.rb @@ -0,0 +1,266 @@ +#!/usr/bin/env ruby + +require 'json' +require 'httparty' +require 'pastel' +require 'securerandom' + +ARGV.length == 1 or raise "Usage: #{$0} " +config_file = ARGV[0] + +File.exist?(config_file) or raise "File not found: #{config_file}" + +$config = JSON.parse(File.read(config_file), symbolize_names: true) + +$plugin_name = $config[:global][:test_plugin_name] +$pastel = Pastel.new + +class TestRunner + attr_reader :stats + def initialize + @stats = { + :suites => 0, + :tests => 0, + :assertions => 0 + } + @test = nil + end + def add_assertion() + @stats[:assertions] += 1 + end + def FAIL(msg, exception = nil, loc = nil) + puts $pastel.red.bold(" ✕ FAIL") + STDERR.print " " + if loc + STDERR.print $pastel.yellow("@#{loc.path}:#{loc.lineno}: ") + else + STDERR.print $pastel.yellow("@#{caller_locations(1, 1).first.path}:#{caller_locations(1, 1).first.lineno}: ") + end + STDERR.puts msg + STDERR.puts exception.full_message(:highlight => true) if exception + STDERR.puts $pastel.yellow(" Backtrace:") + caller.each do |line| + STDERR.puts " #{line}" + end + exit 1 + end + def PASS() + STDERR.puts $pastel.green.bold(" ✓ PASS") + @stats[:tests] += 1 + @test = nil + end + def TEST_SUITE(name) + puts $pastel.bold("• TEST SUITE: \"#{name}\"") + @stats[:suites] += 1 + end + def assert_no_test_running() + unless @test.nil? + STDERR.puts $pastel.red("\nFATAL: Test \"#{@test}\" did not call PASS() or FAIL()!") + exit 1 + end + end + def TEST(name, description = nil) + assert_no_test_running() + @test = name + col = 0 + txt = " ├─ T: #{name} " + col += txt.length + print $pastel.bold(txt) + + tab = 50 + rem = tab - (col % tab) + rem.times do putc ' ' end + col += rem + + if (description) + txt = " - #{description} " + col += txt.length + print txt + + tab = 180 + rem = tab - (col % tab) + rem.times do putc '.' end + end + end + def FINALIZE() + assert_no_test_running() + end +end + +$test_runner = TestRunner.new +def FAIL(msg, exception = nil, loc = nil) + $test_runner.FAIL(msg, exception, loc) +end +def PASS() + $test_runner.PASS() +end +def TEST_SUITE(name) + $test_runner.TEST_SUITE(name) +end +def TEST(name, description = nil) + $test_runner.TEST(name, description) +end + +def assert_eq(got, expected, msg = nil) + unless got == expected + FAIL("Expected #{expected}, got #{got} #{msg ? "(#{msg})" : ""}", nil, caller_locations(1, 1).first) + end + $test_runner.add_assertion() +end +def assert_eq_http_code(got, expected, msg = nil) + unless got.code == expected + FAIL("Expected #{expected}, got #{got}. Server \"#{got.parsed_response}\" #{msg ? "(#{msg})" : ""}", nil, caller_locations(1, 1).first) + end + $test_runner.add_assertion() +end +def assert_eq_str(got, expected, msg = nil) + unless got == expected + FAIL("Strings do not match #{msg ? "(#{msg})" : ""}", nil, caller_locations(1, 1).first) + end + $test_runner.add_assertion() +end +def assert_not_eq_str(got, expected, msg = nil) + unless got != expected + FAIL("Strings shoud not match #{msg ? "(#{msg})" : ""}", nil, caller_locations(1, 1).first) + end + $test_runner.add_assertion() +end +def assert_nothing_raised() + begin + yield + rescue Exception => e + FAIL("Unexpected exception of type #{e.class} raised. Msg: \"#{e.message}\"", e, caller_locations(1, 1).first) + end + $test_runner.add_assertion() +end +def assert_has_key?(hash, key) + unless hash.has_key?(key) + FAIL("Expected key \"#{key}\" in hash", nil, caller_locations(1, 1).first) + end + $test_runner.add_assertion() +end +def assert_array_include?(array, value) + unless array.include?(value) + FAIL("Expected array to include \"#{value}\"", nil, caller_locations(1, 1).first) + end + $test_runner.add_assertion() +end +def assert_array_not_include?(array, value) + if array.include?(value) + FAIL("Expected array to not include \"#{value}\"", nil, caller_locations(1, 1).first) + end + $test_runner.add_assertion() +end +def assert_is_one_of(value, *values) + unless values.include?(value) + FAIL("Expected value to be one of #{values.join(", ")}", nil, caller_locations(1, 1).first) + end + $test_runner.add_assertion() +end +def assert_not_nil(value) + if value.nil? + FAIL("Expected value to not be nil", nil, caller_locations(1, 1).first) + end + $test_runner.add_assertion() +end +def assert_nil(value) + unless value.nil? + FAIL("Expected value to be nil", nil, caller_locations(1, 1).first) + end + $test_runner.add_assertion() +end + + +class DynCfgHttpClient + def self.protocol(cfg) + return cfg[:ssl] ? 'https://' : 'http://' + end + def self.url_base(host) + return "#{protocol(host)}#{host[:host]}:#{host[:port]}" + end + def self.get_url_cfg_base(host, child = nil) + url = url_base(host) + url += "/host/#{child[:mguid]}" if child + url += "/api/v2/config" + return url + end + def self.get_url_cfg_plugin(host, plugin, child = nil) + return get_url_cfg_base(host, child) + '/' + plugin + end + def self.get_url_cfg_module(host, plugin, mod, child = nil) + return get_url_cfg_plugin(host, plugin, child) + '/' + mod + end + def self.get_url_cfg_job(host, plugin, mod, job_id, child = nil) + return get_url_cfg_module(host, plugin, mod, child) + "/#{job_id}" + end + def self.get_plugin_list(host, child = nil) + begin + return HTTParty.get(get_url_cfg_base(host, child), verify: false, format: :plain) + rescue => e + FAIL(e.message, e) + end + end + def self.get_plugin_config(host, plugin, child = nil) + begin + return HTTParty.get(get_url_cfg_plugin(host, plugin, child), verify: false) + rescue => e + FAIL(e.message, e) + end + end + def self.set_plugin_config(host, plugin, cfg, child = nil) + begin + return HTTParty.put(get_url_cfg_plugin(host, plugin, child), verify: false, body: cfg) + rescue => e + FAIL(e.message, e) + end + end + def self.get_plugin_module_list(host, plugin, child = nil) + begin + return HTTParty.get(get_url_cfg_plugin(host, plugin, child) + "/modules", verify: false, format: :plain) + rescue => e + FAIL(e.message, e) + end + end + def self.get_job_list(host, plugin, mod, child = nil) + begin + return HTTParty.get(get_url_cfg_module(host, plugin, mod, child) + "/jobs", verify: false, format: :plain) + rescue => e + FAIL(e.message, e) + end + end + def self.create_job(host, plugin, mod, job_id, job_cfg, child = nil) + begin + return HTTParty.post(get_url_cfg_job(host, plugin, mod, job_id, child), verify: false, body: job_cfg) + rescue => e + FAIL(e.message, e) + end + end + def self.delete_job(host, plugin, mod, job_id, child = nil) + begin + return HTTParty.delete(get_url_cfg_job(host, plugin, mod, job_id, child), verify: false) + rescue => e + FAIL(e.message, e) + end + end + def self.get_job_config(host, plugin, mod, job_id, child = nil) + begin + return HTTParty.get(get_url_cfg_job(host, plugin, mod, job_id, child), verify: false, format: :plain) + rescue => e + FAIL(e.message, e) + end + end + def self.set_job_config(host, plugin, mod, job_id, job_cfg, child = nil) + begin + return HTTParty.put(get_url_cfg_job(host, plugin, mod, job_id, child), verify: false, body: job_cfg) + rescue => e + FAIL(e.message, e) + end + end +end + +require_relative 'sub_tests/test_parent_child.rb' + +$test_runner.FINALIZE() +puts $pastel.green.bold("All tests passed!") +puts ("Total #{$test_runner.stats[:assertions]} assertions, #{$test_runner.stats[:tests]} tests in #{$test_runner.stats[:suites]} suites") +exit 0 diff --git a/libnetdata/dyn_conf/tests/test_plugin/test.plugin b/libnetdata/dyn_conf/tests/test_plugin/test.plugin new file mode 100755 index 000000000..b890ab314 --- /dev/null +++ b/libnetdata/dyn_conf/tests/test_plugin/test.plugin @@ -0,0 +1,250 @@ +#!/usr/bin/env ruby + +# bogus chart that we create just so there is at least one chart +CHART_TYPE = 'lines' +UPDATE_EVERY = 1 +PRIORITY = 100000 +CHART_NAME = 'number_of_processes' +DIMENSION_NAME = 'running' + +$plugin_name = "external_plugin" +$plugin_version = "0.0.1" +$plugin_config = <<-HEREDOC +test_plugin_config +hableba hableba hableba +HEREDOC + +$array_module_name = 'module_of_the_future' +$fixed_job_name = 'fixed_job' + +$modules = { + $array_module_name => { + :type => :job_array, + :jobs => { + $fixed_job_name => { + :type => :fixed, + :config => <<-HEREDOC +fixed_job_config +HEREDOC + }, + }, + :config => <<-HEREDOC +module_of_the_future_config +HEREDOC + }, + "module_of_the_future_single_type" => { + :type => :single, + :jobs => {}, + :config => <<-HEREDOC +module_of_the_future_single_type_config +HEREDOC + } +} + +def out(str) + $log.puts "2 NETDATA> #{str}" + $stdout.puts str + $stdout.flush + $log.flush +end + +def log(str) + $log.puts "LOG > #{str}" + $log.flush +end + +#TODO this is AI code, verify +def split_with_quotes(str) + result = [] + current_word = "" + in_quotes = false + escaped = false + + str.each_char do |char| + if char == '\\' && !escaped + escaped = true + next + end + + if char == '"' && !escaped + in_quotes = !in_quotes + current_word << char + elsif char == ' ' && !in_quotes + result << current_word unless current_word.empty? + current_word = "" + else + current_word << char + end + + escaped = false + end + + result << current_word unless current_word.empty? + + result +end + + +def print_startup_messages + out "DYNCFG_ENABLE #{$plugin_name}" + $modules.each do |name, module_config| + out "DYNCFG_REGISTER_MODULE #{name} #{module_config[:type]}" + end + out "CHART system.#{CHART_NAME} '' 'Number of running processes' 'processes' processes processes.#{CHART_NAME} #{CHART_TYPE} #{PRIORITY} #{UPDATE_EVERY}" + out "DIMENSION #{DIMENSION_NAME} '' absolute 1 1" + + $modules.each do |mod_name, mod| + next unless mod[:type] == :job_array + mod[:jobs].each do |job_name, job| + next unless job[:type] == :fixed + out "DYNCFG_REGISTER_JOB #{mod_name} #{job_name} stock 0" + out "REPORT_JOB_STATUS #{$array_module_name} #{$fixed_job_name} running 0" + end + end +end + +def function_result(txid, msg, result) + out "FUNCTION_RESULT_BEGIN #{txid} #{result} text/plain 5" + out msg + out "FUNCTION_RESULT_END" +end + +def process_payload_function(params) + log "payload function #{params[:fncname]}, #{params[:fncparams]}" + fnc_name, mod_name, job_name = params[:fncparams] + case fnc_name + when 'set_plugin_config' + $plugin_config = params[:payload] + function_result(params[:txid], "plugin config set", 1) + when 'set_module_config' + mod = $modules[mod_name] + return function_result(params[:txid], "no such module", 0) if mod.nil? + mod[:config] = params[:payload] + function_result(params[:txid], "module config set", 1) + when 'set_job_config' + mod = $modules[mod_name] + return function_result(params[:txid], "no such module", 0) if mod.nil? + job = mod[:jobs][job_name] + if job.nil? + job = Hash.new if job.nil? + job[:type] = :dynamic + mod[:jobs][job_name] = job + end + job[:config] = params[:payload] + function_result(params[:txid], "job config set", 1) + end +end + +def process_function(params) + log "normal function #{params[:fncname]}, #{params[:fncparams]}" + fnc_name, mod_name, job_name = params[:fncparams] + case fnc_name + when 'get_plugin_config' + function_result(params[:txid], $plugin_config, 1) + when 'get_module_config' + return function_result(params[:txid], "no such module", 0) unless $modules.has_key?(mod_name) + function_result(params[:txid], $modules[mod_name][:config], 1) + when 'get_job_config' + mod = $modules[mod_name] + return function_result(params[:txid], "no such module", 0) if mod.nil? + job = mod[:jobs][job_name] + return function_result(params[:txid], "no such job", 0) if job.nil? + function_result(params[:txid], job[:config], 1) + when 'delete_job' + mod = $modules[mod_name] + return function_result(params[:txid], "no such module", 0) if mod.nil? + job = mod[:jobs][job_name] + return function_result(params[:txid], "no such job", 0) if job.nil? + if job[:type] == :fixed + return function_result(params[:txid], "this job can't be deleted", 0) + else + mod[:jobs].delete(job_name) + function_result(params[:txid], "job deleted", 1) + end + end +end + +$inflight_incoming = nil +def process_input(input) + words = split_with_quotes(input) + + unless $inflight_incoming.nil? + if input == "FUNCTION_PAYLOAD_END" + log $inflight_incoming[:payload] + process_payload_function($inflight_incoming) + $inflight_incoming = nil + else + $inflight_incoming[:payload] << input + $inflight_incoming[:payload] << "\n" + end + return + end + + case words[0] + when "FUNCTION", "FUNCTION_PAYLOAD" + params = {} + params[:command] = words[0] + params[:txid] = words[1] + params[:timeout] = words[2].to_i + params[:fncname] = words[3] + params[:fncname] = params[:fncname][1..-2] if params[:fncname].start_with?('"') && params[:fncname].end_with?('"') + if params[:command] == "FUNCTION_PAYLOAD" + $inflight_incoming = Hash.new + params[:fncparams] = split_with_quotes(params[:fncname]) + params[:fncname] = params[:fncparams][0] + $inflight_incoming[:txid] = params[:txid] + $inflight_incoming[:fncname] = params[:fncname] + $inflight_incoming[:params] = params + $inflight_incoming[:fncparams] = params[:fncparams] + $inflight_incoming[:payload] = "" + else + params[:fncparams] = split_with_quotes(params[:fncname]) + params[:fncname] = params[:fncparams][0] + process_function(params) + end + end +end + +def read_and_output_metric + processes = `ps -e | wc -l`.to_i - 1 # -1 to exclude the header line + timestamp = Time.now.to_i + + puts "BEGIN system.#{CHART_NAME}" + puts "SET #{DIMENSION_NAME} = #{processes}" + puts "END" +end + +def the_main + $stderr.reopen("/tmp/test_plugin_err.log", "w") + $log = File.open("/tmp/test_plugin.log", "w") + $log.puts "Starting plugin" + print_startup_messages + $log.puts "init done" + $log.flush + + last_metric_time = Time.now + + loop do + time_since_last_metric = Time.now - last_metric_time + + # If it's been more than 1 second since we collected metrics, collect them now + if time_since_last_metric >= 1 + read_and_output_metric + last_metric_time = Time.now + end + + # Use select to wait for input, but only wait up to the time remaining until we need to collect metrics again + remaining_time = [1 - time_since_last_metric, 0].max + if select([$stdin], nil, nil, remaining_time) + input = $stdin.gets + next if input.class != String + input.chomp! + $log.puts "RAW INPUT< #{input}" + $log.flush + process_input(input) + end + end +end + + +the_main if __FILE__ == $PROGRAM_NAME -- cgit v1.2.3