test.plugin 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. #!/usr/bin/env ruby
  2. # bogus chart that we create just so there is at least one chart
  3. CHART_TYPE = 'lines'
  4. UPDATE_EVERY = 1
  5. PRIORITY = 100000
  6. CHART_NAME = 'number_of_processes'
  7. DIMENSION_NAME = 'running'
  8. $plugin_name = "external_plugin"
  9. $plugin_version = "0.0.1"
  10. $plugin_config = <<-HEREDOC
  11. test_plugin_config
  12. hableba hableba hableba
  13. HEREDOC
  14. $array_module_name = 'module_of_the_future'
  15. $fixed_job_name = 'fixed_job'
  16. $modules = {
  17. $array_module_name => {
  18. :type => :job_array,
  19. :jobs => {
  20. $fixed_job_name => {
  21. :type => :fixed,
  22. :config => <<-HEREDOC
  23. fixed_job_config
  24. HEREDOC
  25. },
  26. },
  27. :config => <<-HEREDOC
  28. module_of_the_future_config
  29. HEREDOC
  30. },
  31. "module_of_the_future_single_type" => {
  32. :type => :single,
  33. :jobs => {},
  34. :config => <<-HEREDOC
  35. module_of_the_future_single_type_config
  36. HEREDOC
  37. }
  38. }
  39. def out(str)
  40. $log.puts "2 NETDATA> #{str}"
  41. $stdout.puts str
  42. $stdout.flush
  43. $log.flush
  44. end
  45. def log(str)
  46. $log.puts "LOG > #{str}"
  47. $log.flush
  48. end
  49. #TODO this is AI code, verify
  50. def split_with_quotes(str)
  51. result = []
  52. current_word = ""
  53. in_quotes = false
  54. escaped = false
  55. str.each_char do |char|
  56. if char == '\\' && !escaped
  57. escaped = true
  58. next
  59. end
  60. if char == '"' && !escaped
  61. in_quotes = !in_quotes
  62. current_word << char
  63. elsif char == ' ' && !in_quotes
  64. result << current_word unless current_word.empty?
  65. current_word = ""
  66. else
  67. current_word << char
  68. end
  69. escaped = false
  70. end
  71. result << current_word unless current_word.empty?
  72. result
  73. end
  74. def print_startup_messages
  75. out "DYNCFG_ENABLE #{$plugin_name}"
  76. $modules.each do |name, module_config|
  77. out "DYNCFG_REGISTER_MODULE #{name} #{module_config[:type]}"
  78. end
  79. out "CHART system.#{CHART_NAME} '' 'Number of running processes' 'processes' processes processes.#{CHART_NAME} #{CHART_TYPE} #{PRIORITY} #{UPDATE_EVERY}"
  80. out "DIMENSION #{DIMENSION_NAME} '' absolute 1 1"
  81. $modules.each do |mod_name, mod|
  82. next unless mod[:type] == :job_array
  83. mod[:jobs].each do |job_name, job|
  84. next unless job[:type] == :fixed
  85. out "DYNCFG_REGISTER_JOB #{mod_name} #{job_name} stock 0"
  86. out "REPORT_JOB_STATUS #{$array_module_name} #{$fixed_job_name} running 0"
  87. end
  88. end
  89. end
  90. def function_result(txid, msg, result)
  91. out "FUNCTION_RESULT_BEGIN #{txid} #{result} text/plain 5"
  92. out msg
  93. out "FUNCTION_RESULT_END"
  94. end
  95. def process_payload_function(params)
  96. log "payload function #{params[:fncname]}, #{params[:fncparams]}"
  97. fnc_name, mod_name, job_name = params[:fncparams]
  98. case fnc_name
  99. when 'set_plugin_config'
  100. $plugin_config = params[:payload]
  101. function_result(params[:txid], "plugin config set", 1)
  102. when 'set_module_config'
  103. mod = $modules[mod_name]
  104. return function_result(params[:txid], "no such module", 0) if mod.nil?
  105. mod[:config] = params[:payload]
  106. function_result(params[:txid], "module config set", 1)
  107. when 'set_job_config'
  108. mod = $modules[mod_name]
  109. return function_result(params[:txid], "no such module", 0) if mod.nil?
  110. job = mod[:jobs][job_name]
  111. if job.nil?
  112. job = Hash.new if job.nil?
  113. job[:type] = :dynamic
  114. mod[:jobs][job_name] = job
  115. end
  116. job[:config] = params[:payload]
  117. function_result(params[:txid], "job config set", 1)
  118. end
  119. end
  120. def process_function(params)
  121. log "normal function #{params[:fncname]}, #{params[:fncparams]}"
  122. fnc_name, mod_name, job_name = params[:fncparams]
  123. case fnc_name
  124. when 'get_plugin_config'
  125. function_result(params[:txid], $plugin_config, 1)
  126. when 'get_module_config'
  127. return function_result(params[:txid], "no such module", 0) unless $modules.has_key?(mod_name)
  128. function_result(params[:txid], $modules[mod_name][:config], 1)
  129. when 'get_job_config'
  130. mod = $modules[mod_name]
  131. return function_result(params[:txid], "no such module", 0) if mod.nil?
  132. job = mod[:jobs][job_name]
  133. return function_result(params[:txid], "no such job", 0) if job.nil?
  134. function_result(params[:txid], job[:config], 1)
  135. when 'delete_job'
  136. mod = $modules[mod_name]
  137. return function_result(params[:txid], "no such module", 0) if mod.nil?
  138. job = mod[:jobs][job_name]
  139. return function_result(params[:txid], "no such job", 0) if job.nil?
  140. if job[:type] == :fixed
  141. return function_result(params[:txid], "this job can't be deleted", 0)
  142. else
  143. mod[:jobs].delete(job_name)
  144. function_result(params[:txid], "job deleted", 1)
  145. end
  146. end
  147. end
  148. $inflight_incoming = nil
  149. def process_input(input)
  150. words = split_with_quotes(input)
  151. unless $inflight_incoming.nil?
  152. if input == "FUNCTION_PAYLOAD_END"
  153. log $inflight_incoming[:payload]
  154. process_payload_function($inflight_incoming)
  155. $inflight_incoming = nil
  156. else
  157. $inflight_incoming[:payload] << input
  158. $inflight_incoming[:payload] << "\n"
  159. end
  160. return
  161. end
  162. case words[0]
  163. when "FUNCTION", "FUNCTION_PAYLOAD"
  164. params = {}
  165. params[:command] = words[0]
  166. params[:txid] = words[1]
  167. params[:timeout] = words[2].to_i
  168. params[:fncname] = words[3]
  169. params[:fncname] = params[:fncname][1..-2] if params[:fncname].start_with?('"') && params[:fncname].end_with?('"')
  170. if params[:command] == "FUNCTION_PAYLOAD"
  171. $inflight_incoming = Hash.new
  172. params[:fncparams] = split_with_quotes(params[:fncname])
  173. params[:fncname] = params[:fncparams][0]
  174. $inflight_incoming[:txid] = params[:txid]
  175. $inflight_incoming[:fncname] = params[:fncname]
  176. $inflight_incoming[:params] = params
  177. $inflight_incoming[:fncparams] = params[:fncparams]
  178. $inflight_incoming[:payload] = ""
  179. else
  180. params[:fncparams] = split_with_quotes(params[:fncname])
  181. params[:fncname] = params[:fncparams][0]
  182. process_function(params)
  183. end
  184. end
  185. end
  186. def read_and_output_metric
  187. processes = `ps -e | wc -l`.to_i - 1 # -1 to exclude the header line
  188. timestamp = Time.now.to_i
  189. puts "BEGIN system.#{CHART_NAME}"
  190. puts "SET #{DIMENSION_NAME} = #{processes}"
  191. puts "END"
  192. end
  193. def the_main
  194. $stderr.reopen("/tmp/test_plugin_err.log", "w")
  195. $log = File.open("/tmp/test_plugin.log", "w")
  196. $log.puts "Starting plugin"
  197. print_startup_messages
  198. $log.puts "init done"
  199. $log.flush
  200. last_metric_time = Time.now
  201. loop do
  202. time_since_last_metric = Time.now - last_metric_time
  203. # If it's been more than 1 second since we collected metrics, collect them now
  204. if time_since_last_metric >= 1
  205. read_and_output_metric
  206. last_metric_time = Time.now
  207. end
  208. # Use select to wait for input, but only wait up to the time remaining until we need to collect metrics again
  209. remaining_time = [1 - time_since_last_metric, 0].max
  210. if select([$stdin], nil, nil, remaining_time)
  211. input = $stdin.gets
  212. next if input.class != String
  213. input.chomp!
  214. $log.puts "RAW INPUT< #{input}"
  215. $log.flush
  216. process_input(input)
  217. end
  218. end
  219. end
  220. the_main if __FILE__ == $PROGRAM_NAME