Home > ruby > Script em ruby para monitoramento de serviços – Nagios

Script em ruby para monitoramento de serviços – Nagios

   Estou em um projeto de monitoramento de infra-estrutura de TI onde estou tendo que monitorar roteadores, servidores(tanto linux quando windows). Para o monitoramento dos recursos mais usuais como CPU, memória, trafego, etc, o uso do SNMP e plugins padrões do nagios são bastante eficazes. Entretanto, tive que monitorar processos e daemon destes servidores e pelo menos na busca nos plugins padroes do nagios não encontrei algum que pudesse atender minha demanda. Então parti para o desenvolvimento, teria que criar um script que iria dar um get em MIB de vários servidores buscando determinado processo. Graças aos padrões criados do protocolo SNMP existe na principal “base de dados” de um host uma MIB chamada MIB-2(Padrão na maioria das implementações) que já possue essas informações(como nome de processos, uso de memória por processo, etc). Então vi a chance de aprender uma nova linguagem de programação. Há tempos venho tentando arrumar um tempo para aprender ruby, eis que a chance surgiu. Resolvi desenvolver o script de monitoramento do Nagios em ruby. Fiz um “hello world” em ruby, e logo percebi o poder dessa linguagem. Uma linguagem dinâmicamente tipada, possue um framework criado para desenvolvimento web o Rails, além disso no ruby tudo o que é manipulado é um objeto, não havendo tipos primitivos, ela é altamente produtivo(você escreve pouco e faz muito), e várias outras vantagens :D. O Ruby foi criado por Yukihiro “Matz” Matsumoto, o qual dizia que a linguagem deveria ter o nome de uma pedra preciosa, o qual batizou o nome de ruby.

  Bom para começar a escrever meu plugin do nagios precisaria ver como funciona a parte de sockets no ruby ou usar uma biblioteca pronta. Para minha “sorte” essa bilblioteca existe 😀

http://snmplib.rubyforge.org/

  Utilizei a snmplib, fiz alguns testes com sucesso fiquei impressionado com a rapidez de se “fazer as coisas”. O código que se segue funciona da seguinte forma, ele vai receber via parametros de linha de comando a comunidade SNMP, versão SNMP, nome do processo/aplicativo a verificar, um descritivo além do endereço IP.

#!/usr/bin/env ruby

include SNMP
require ‘optparse’
require ‘pp’
require ‘snmp’

optparse = OptionParser.new do|opts|
 opts.banner = “Usage: check_windows_processes_snmp.rb [options] host”

 options[:opt_comunidade] = nil
  opts.on( ‘-c’, ‘–comunidade NOME’, ‘Nomme da Comunidade SNMP’ ) do|comunidade|
  options[:opt_comunidade] = comunidade
end

 options[:opt_descricao] = nil
  opts.on( ‘-d’, ‘–descricao NOME’, ‘Descricao do processo’ ) do|descricao|
  options[:opt_descricao] = descricao
 end

 options[:opt_versao] = nil
  opts.on( ‘-v’, ‘–versao NOME’, ‘Versao SNMP’ ) d do|versao|
  options[:opt_versao] = versao
 end

 options[:opt_processo] = nil
  opts.on( ‘-p’, ‘–processo NOME’, ‘Nome do Processo’ ) do|processo|
  options[:opt_processo] = processo
 end

 opts.on( ‘-h’, ‘–help’, ‘Display this screen’ ) do
  puts opts
  exit
 end
end

optparse.parse!

if ( (options[:opt_versao] == nil) && (options[:opt_comunidade] == nil) && (options[:opt_processo] == nil) )
  puts “Argumentos inexistente: utilize o parametro -h para obter ajuda”
  exit
end

comunidade = options[:opt_comunidade]
processo = options[:opt_processo]
descricao = options[:opt_descricao]

case options[:opt_versao]
 when “1”
  versao = :SNMPv1
 when “2c”
  versao = :SNMPv2c
 when “3”
  versao = :SNMPv3
end

axou = 0

Manager.open(:Host => ARGV[0],:Version => versao, :Community => comunidade) do |manager|
 ifTable = ObjectId.new(“1.3.6.1.2.1.25.4.2.1.2”)
 next_oid = ifTable
 while next_oid.subtree_of?(ifTable)
  response = manager.get_next(next_oid)
  varbind = response.varbind_list.first
  next_oid = varbind.name
  string = varbind.to_s
  if string.match(processo)
   puts “Processo: #{descricao} STATUS: rodando”
   axou = 1
  end
 end
end

if ( axou != 1 )
 puts “Processo: #{descricao} STATUS: stop”
 exit
end

Encontrei um biblioteca muito interessante chamada optparse (http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html) ela serve para manipular caracteres passados na linha de comando vamos dissecar esse código por partes. Abaixo declaração de bilblioteas como você faz em outras linguagens como C, Perl e python, etc.

include SNMP
require ‘optparse’
require ‘pp’
require ‘snmp’

Aqui declaramos um objeto do tipo “OptionParser” que será o obejto para manipular os parametros repassados pela linha de comandos. Um parametro interessante dessa biblioteca é o “objeto.on”, o qual insere cada parametros da linha de comando no caso criamos a opção de linha de comando “-c”, “-v”, “-p” e “-d” para você especificar a comunidade SNMP, versão do protocolo SNMP usado, nome do processo/aplicativo e descrição respectivamente.

optparse = OptionParser.new do|opts|
 opts.banner = “Usage: check_windows_processes_snmp.rb [options] host”

 options[:opt_comunidade] = nil
  opts.on( ‘-c’, ‘–comunidade NOME’, ‘Nomme da Comunidade SNMP’ ) do|comunidade|
  options[:opt_comunidade] = comunidade
 end

 options[:opt_descricao] = nil
  opts.on( ‘-d’, ‘–descricao NOME’, ‘Descricao do processo’ ) do|descricao|
  options[:opt_descricao] = descricao
 end

 options[:opt_versao] = nil
  opts.on( ‘-v’, ‘–versao NOME’, ‘Versao SNMP’ ) do|versao|
  options[:opt_versao] = versao
 end

 options[:opt_processo] = nil
  opts.on( ‘-p’, ‘–processo NOME’, ‘Nome do Processo’ ) do|processo|
  options[:opt_processo] = processo
 end

 opts.on( ‘-h’, ‘–help’, ‘Display this screen’ ) do
  puts opts
  exit
 end
end

Aqui fazemos uma validação(precaução) caso o usuário digite o script sem parametro algum.

if ( (options[:opt_versao] == nil) && (options[:opt_comunidade] == nil) && (options[:opt_processo] == nil) )
  puts “Argumentos inexistente: utilize o parametro -h para obter ajuda”
  exit
end

Definimos as variaveis comunidade, processo, descricao e versao obtidos atraves da biblioteca “optparse”. Uma pecualiaridade manipulamos a variavel versao para utilizar a MACRO correspondente a versão do protocolo SNMP. Utilizei uma variavel “axou” para manipular quando encontrar o retorno da busca.

comunidade = options[:opt_comunidade]
processo = options[:opt_processo]
descricao = options[:opt_descricao]

case options[:opt_versao]
  when “1”
    versao = :SNMPv1
  when “2c”
    versao = :SNMPv2c
  when “3”
    versao = :SNMPv3
end

axou = 0

Abaixo temos a parte do código que faz o “get” na MIB dos servidores via biblioteca “snmplib”, são passados os parametros host, versão, comunidade, e o retorno e analisado com o nome do processo passado via linha de comando se der “match” ele escreve que o processo esta rodando no servidor em questão, senão(a variavel “axou” esta definida como falso ou seja igual a zero), ele testa a variavel axou e printa que o processo não esta sendo executado.

Manager.open(:Host => ARGV[0],:Version => versao, :Community => comunidade) do |manager|
   ifTable = ObjectId.new(“1.3.6.1.2.1.25.4.2.1.2”)
   next_oid = ifTable
   while next_oid.subtree_of?(ifTable)
     response = manager.get_next(next_oid)
     varbind = response.varbind_list.first
     next_oid = varbind.name
    string = varbind.to_s
    if string.match(processo)
      puts “Processo: #{descricao} STATUS: rodando”
      axou = 1
    end
   end
end

if ( axou != 1 )
  puts “Processo: #{descricao} STATUS: stop”
  exit
end

Abaixo segue a execução do script.

ricardobarbosa@capsula:~$ ./check_snmp.rb
Argumentos inexistente: utilize o parametro -h para obter ajuda
ricardobarbosa@capsula:~$ ./check_snmp.rb -h
Usage: check_windows_processes_snmp.rb [options] host
-c, –comunidade NOME Nomme da Comunidade SNMP
-d, –descricao NOME Descricao do processo
-v, –versao NOME Versao SNMP
-p, –processo NOME Nome do Processo
-h, –help Display this screen
ricardobarbosa@capsula:~$

Testamos o snmp via comando snmpwalk do tools net-snmp(http://www.net-snmp.org/), vamos buscar os nomes de todos os processos.

ricardobarbosa@capsula:~$ snmpwalk -c public -v 2c 192.168.1.3 hrSWRunName
HOST-RESOURCES-MIB::hrSWRunName.1 = STRING: “init”
HOST-RESOURCES-MIB::hrSWRunName.2 = STRING: “kthreadd”
HOST-RESOURCES-MIB::hrSWRunName.3 = STRING: “migration/0”
HOST-RESOURCES-MIB::hrSWRunName.4 = STRING: “ksoftirqd/0”
HOST-RESOURCES-MIB::hrSWRunName.5 = STRING: “watchdog/0”
HOST-RESOURCES-MIB::hrSWRunName.6 = STRING: “events/0”
HOST-RESOURCES-MIB::hrSWRunName.7 = STRING: “khelper”
HOST-RESOURCES-MIB::hrSWRunName.41 = STRING: “kblockd/0”
HOST-RESOURCES-MIB::hrSWRunName.44 = STRING: “kacpid”
HOST-RESOURCES-MIB::hrSWRunName.45 = STRING: “kacpi_notify”
HOST-RESOURCES-MIB::hrSWRunName.88 = STRING: “kseriod”
HOST-RESOURCES-MIB::hrSWRunName.123 = STRING: “pdflush”
HOST-RESOURCES-MIB::hrSWRunName.124 = STRING: “pdflush”
HOST-RESOURCES-MIB::hrSWRunName.125 = STRING: “kswapd0”
HOST-RESOURCES-MIB::hrSWRunName.167 = STRING: “aio/0”
HOST-RESOURCES-MIB::hrSWRunName.1289 = STRING: “ata/0”
HOST-RESOURCES-MIB::hrSWRunName.1294 = STRING: “ata_aux”
HOST-RESOURCES-MIB::hrSWRunName.1308 = STRING: “scsi_eh_0”
HOST-RESOURCES-MIB::hrSWRunName.1312 = STRING: “scsi_eh_1”
HOST-RESOURCES-MIB::hrSWRunName.2317 = STRING: “kjournald”
HOST-RESOURCES-MIB::hrSWRunName.2473 = STRING: “udevd”
HOST-RESOURCES-MIB::hrSWRunName.2667 = STRING: “kpsmoused”
HOST-RESOURCES-MIB::hrSWRunName.3648 = STRING: “dhclient3”
HOST-RESOURCES-MIB::hrSWRunName.4041 = STRING: “getty”
HOST-RESOURCES-MIB::hrSWRunName.4044 = STRING: “getty”
HOST-RESOURCES-MIB::hrSWRunName.4047 = STRING: “getty”
HOST-RESOURCES-MIB::hrSWRunName.4051 = STRING: “getty”
HOST-RESOURCES-MIB::hrSWRunName.4053 = STRING: “getty”
HOST-RESOURCES-MIB::hrSWRunName.4090 = STRING: “syslogd”
HOST-RESOURCES-MIB::hrSWRunName.4109 = STRING: “dd”
HOST-RESOURCES-MIB::hrSWRunName.4111 = STRING: “klogd”
HOST-RESOURCES-MIB::hrSWRunName.4130 = STRING: “sshd”
HOST-RESOURCES-MIB::hrSWRunName.4186 = STRING: “mysqld_safe”
HOST-RESOURCES-MIB::hrSWRunName.4228 = STRING: “mysqld”
HOST-RESOURCES-MIB::hrSWRunName.4229 = STRING: “logger”
HOST-RESOURCES-MIB::hrSWRunName.4313 = STRING: “asterisk”
HOST-RESOURCES-MIB::hrSWRunName.4359 = STRING: “atd”
HOST-RESOURCES-MIB::hrSWRunName.4370 = STRING: “cron”
HOST-RESOURCES-MIB::hrSWRunName.4392 = STRING: “apache2”
HOST-RESOURCES-MIB::hrSWRunName.4408 = STRING: “apache2”
HOST-RESOURCES-MIB::hrSWRunName.4410 = STRING: “apache2”
HOST-RESOURCES-MIB::hrSWRunName.4412 = STRING: “apache2”
HOST-RESOURCES-MIB::hrSWRunName.4413 = STRING: “apache2”
HOST-RESOURCES-MIB::hrSWRunName.4414 = STRING: “apache2”
HOST-RESOURCES-MIB::hrSWRunName.4415 = STRING: “miniserv.pl”
HOST-RESOURCES-MIB::hrSWRunName.4422 = STRING: “login”
HOST-RESOURCES-MIB::hrSWRunName.4423 = STRING: “bash”
HOST-RESOURCES-MIB::hrSWRunName.4439 = STRING: “bash”
HOST-RESOURCES-MIB::hrSWRunName.4855 = STRING: “snmpd”
ricardobarbosa@capsula:~$

Em seguida executamos nosso script em busca do processo apache2 e do postfix para verificar a inexistencia do processo.

ricardobarbosa@capsula:~$ ./check_snmp.rb -c public -v 2c -d “Servidor HTTP APACHE2” -p apache2 192.168.1.3
Processo: Servidor HTTP APACHE2 STATUS: rodando
ricardobarbosa@capsula:~$
ricardobarbosa@capsula:~$ ./check_snmp.rb -c public -v 2c -d “Servidor SMTP POSTFIX” -p master 192.168.1.3
Processo: Servidor SMTP POSTFIX STATUS: stop
ricardobarbosa@capsula:~$

Agora e necessário configurar o arquivo checkcommands.cfg do nagios e inserir o comando com as variaveis.(para o post não ficar muito extenso não vou passar essa configuração num post futuro eu farei isso :D)
Não sou desenvolvedor atualmente mas já trabalhei como desenvolvedor delphi e php. Gosto de criar códigos que me ajudem na infra-estrutura. Espero que o post seja útil até a próxima.

Att.

Advertisements
Categories: ruby
  1. Luis Otávio
    January 14, 2010 at 7:50 pm

    Quem disse que desenvolvedor Delphi desenvolve alguma coisa? Só fica empurrando mouse o dia inteiro! kkkk. Brincadeira, ótimo post. Abraços.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: