require 'fluent/input' require 'fluent/config/error' require 'yaml/store' require 'fileutils' module Fluent class DscMonitoringInput < Input Fluent::Plugin.register_input('dsc_monitor', self) config_param :tag, :string, :default=>nil config_param :check_install_interval, :time, :default=>86400 config_param :check_status_interval, :time, :default=>1800 config_param :dsc_cache_file, :string, :default=>'/var/opt/microsoft/omsagent/state/dsc_cache.yml' def configure(conf) super if !@tag raise Fluent::ConfigError, "'tag' option is required on dsc_checks input" end end def start super @finished_check_install = false @finished_check_status = false @thread_check_install = Thread.new(&method(:run_check_install)) @thread_check_status = Thread.new(&method(:run_check_status)) if !File.exists?(dsc_cache_file) File.new(dsc_cache_file, "w", 0644) else File.chmod(0644, dsc_cache_file) end @dsc_cache = YAML::Store.new(dsc_cache_file) # The existing file will be read. end def check_install dpkg = (%x(which dpkg > /dev/null 2>&1; echo $?)).to_i if dpkg == 0 %x(dpkg --list omsconfig > /dev/null 2>&1; echo $?).to_i else %x(rpm -qi omsconfig > /dev/null 2>&1; echo $?).to_i end end def run_check_install until @finished_check_install @install_status = check_install if @install_status == 1 router.emit(@tag, Time.now.to_f, {"message"=>"omsconfig is not installed, OMS Portal \ configuration will not be applied and solutions such as Change Tracking and Update Assessment will \ not function properly. omsconfig can be installed by rerunning the omsagent installation"}) end sleep @check_install_interval end end def get_dsc_status begin python = %x(which python3) if !python.empty? dsc_status = %x(/opt/microsoft/omsconfig/Scripts/python3/TestDscConfiguration.py) else # assume python2, since /some/ python is an install prereq and we have a rescue below regardless dsc_status = %x(/opt/microsoft/omsconfig/Scripts/TestDscConfiguration.py) end rescue => error OMS::Log.error_once("Running TestDscConfiguration.py returned error : #{error}") return 2 end if dsc_status.match('"InDesiredState": true') return 0 else return 1 end end def run_check_status begin sleep @check_status_interval until @finished_check_status && @install_status == 0 dsc_status = get_dsc_status # returns value of test dsc configuration from the cache # if key does not exist, assigns the string as value stored_dsc_status = @dsc_cache.transaction { @dsc_cache.fetch(:status, "TestDscConfiguration.py run status not stored yet") } if dsc_status == 1 and stored_dsc_status == 1 router.emit(@tag, Time.now.to_f, {"message"=>"Two subsequent runs of TestDscConfiguration.py \ returned omsconfig resource(s) that are not in desired state. – please check omsconfig.log on local machine for more details."}) end if dsc_status == 2 and stored_dsc_status == 2 router.emit(@tag, Time.now.to_f, {"message"=>"Two subsequent attempts to run TestDscConfiguration.py \ failed – please check omsconfig.log on local machine for more details."}) end # store test dsc configuration status in the cache @dsc_cache.transaction { @dsc_cache[:status] = dsc_status @dsc_cache.commit } sleep @check_status_interval end rescue => e $log.error "DSC check status failed. Error: #{e}" end end def shutdown super @finished_check_install = true @finished_check_status = true @thread_check_install.exit @thread_check_status.exit @thread_check_install.join @thread_check_status.join end end end