Skip to content

Não, clientes (proxies) para serviços JAX-WS não são thread safe pela spec !

Esse é um assunto recorrente em fóruns, e sempre vejo bastante besteira escrita a respeito. Definitivamente, pela especificação JAX-WS (página 57 do documento [1]), “client proxies” não são thread safe, ou seja, tome cuidado. Se criarmos um código cliente para acesso a um webservice, e armazenarmos nosso proxy (Interface para nosso serviço, refente ao javax.xml.ws.Service) em uma classe seguindo o pattern singleton, fatalmente teremos sérios problemas com acesso simultâneo. Óbvio que isso é especificação. Se um vendor fizer a implementação garantindo o acesso concorrente, então esqueça esse post =b.

Pensemos no seguinte exemplo …

Tenho um webservice (JAX-WS) implantado, publicando três métodos (operações) com distintos Beans de retorno. Algo como o trecho abaixo:

Imagem

 

 

Agora, supomos que o código cliente para invocar tal webservice utilize de um singleton para armazenar o proxy.

Imagem

 

Por fim, criamos então nosso código que irá utilizar o singleton acima, recuperando o nosso “service” (proxy), e então chamando na sequência seus três métodos:

Imagem 

Que bacana né? Agora efetue um teste de carga no método acima, e tenho certeza que você se alimentará de diversas exceções do tipo ClassCastException, como essa:

 

2013-09-09 19:39:04,363 ERROR [org.jboss.ws.core.CommonClient] (http-127.0.0.1-8080-3) Exception caught while (preparing for) performing the invocation:
java.lang.ClassCastException: org.rchies.GetCarResponse cannot be cast to org.rchies.GetHouseResponse
at org.rchies.GetHouseResponse$JaxbAccessorF__return.get(FieldAccessor_Ref.java:52)
at com.sun.xml.bind.v2.runtime.reflect.Accessor.getUnadapted(Accessor.java:147)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl$7.get(JAXBContextImpl.java:958)

at org.jboss.ws.metadata.accessor.JAXBAccessor.get(JAXBAccessor.java:48)

 

“Poxa Rafael, isso acontece mesmo. Por quê?”

Como disse no título, e no decorrer deste post, a especificação JAX-WS não garante integridade no acesso concorrente ao proxy, que vem da classe javax.xml.ws.Service. Sendo assim, quando múltiplas threads fazem acesso a um único proxy, as respostas das requisições podem chegar em ordem invertida. Ou seja, o usuário A, que fez a pergunta A ao serviço, pode receber uma resposta B. Simplificando, a thread que chamou o método getCar(), devendo receber um objeto do tipo Car como retorno, pode receber um objeto do tipo House, que seria a resposta de outra thread. É exatamente isso que faz com que as exceções de ClassCast aconteçam.

Mas denovo, o não thread safe vem puramente da especificação. Se alguma implementação conter o controle de acesso concorrente, ai não teremos esse problema. Das implementações que conheço, sei que:

  • Apache CXF é Thread Safe nesse sentido
  • JBossws Native não é Thread Sage nesse sentido

Portanto, fiquem atentos quando utilizarem singletons para gerenciamento do lado cliente de uma requisição JAX-WS e, principalmente, sempre se previnam construindo testes de carga para aplicações com acesso simultâneo. 

O exemplo que mencionei nesse post está no meu github, é bem fácil de simular o problema em questão: https://github.com/rafachies/jaxws-demos/tree/master/wsthreadsafe

[1] – http://download.oracle.com/otndocs/jcp/jaxws-2_0-fr-eval-oth-JSpec/

BRMS: Aplicações dinâmicas com formulários do Guvnor

No mundo de BPM, existem duas formas de utilizarmos os processos definidos em tempo de execução. A primeira delas é utilizar o motor de execução dos processos existente na própria central de governança da solução, ou seja, na mesma plataforma onde se cria os processos.

Porém, existe uma segunda opção, bastante utilizada, que refere-se ao uso do motor de execução embarcado em aplicações. Ou seja, o processo BPM é criado na central de governança da plataforma BPM, entretanto, a execução do processo em si ocorre dentro das aplicações que possuem as API’s do motor BPM embarcadas.

Um exemplo dessa segunda abordagem pode ser visualizado na utilização da solução jBPM, incorporada na solução entreprise da Red Hat denominada BRMS. Neste cenário, temos a central de governança representada pelo Drools Guvnor, onde podemos criar nossos processos BPM, e a figura da nossa aplicação Java, que possui as bibliotecas do jBPM para o gerenciamento do motor de processos. Abaixo segue um ilustrativo desse cenário.

Imagem

 

 

Utilizando a abordagem supramencionada temos prós e contras, como quase tudo no mundo de tecnologia. Entre os pontos contras que normalmente são mencionados temos o seguinte aspecto: Com a aplicação sendo responsável pela execução do processo, temos que a mesma normalmente fica também responsável pelo gerenciamento das tarefas humanas do processo. Ou seja, a aplicação é responsável por prover as interfaces gráficas (“telinhas”) para que os usuários possam tratar as tarefas humanas. Assim, a questão é a seguinte: E se o processo for redefinido no Guvnor (central de governança)? Vou ter de mexer também na minha aplicação (desenvolvimento, teste, deploy, etc)?

 

Pois é, a priori a resposta é sim. Entretanto, podemos aproveitar de uma das features do Guvnor para amenizar esse problema. O Guvnor possui a uma funcionalidade para geração de formulários HTML para as tarefas humanas existentes no processo. Com isso, o console jBPM, também conhecido como jbpm-console, pode tirar proveito disso para prover os formulários quando deseja-se utilizar no console o motor de execução dos processos.

Entretanto, da mesma forma como o jbpm-console consome os formulários gerados pelo Guvnor, nós podemos também consumi-los em nossa aplicação, visto que os mesmos são expostos através de uma interface REST.

Com isso, a ideia é criar na aplicação uma interface gráfica única para manipular as tarefas humanas, injetando código HTML vindo dos fomulários do Guvnor de forma dinâmica. Nesse screencast do vimeo http://vimeo.com/70797537 tem uma visão geral da solução. Qualquer dúvida que tiverem entrem em contato que esclareço o que for necessário.

Abraços,

 

Serviço JAX-WS assíncrono

Antes de mais nada, quero ressaltar que não sou muito fã da utilização de JAX-WS para comunicação Webservice. Não que eu seja aqueles xiitas que odeiam a solução. Mas é que, infelizmente, 95% dos lugares onde vejo esse modelo sendo utilizado, reconheço a velha “bigorna para matar uma formiga”. Nestes cenários, uma simples comunicação HTTP, utilizando, por exemplo, serviços restfull, resolveria de forma muito mais enxuta e elegante o problema. Mas claro, sei que existem casos mais complexos onde o JAX-WS é necessário, principalmente quando estão envolvidas questões de formalidade.
Com isso, de tempos em tempos, preciso fazer alguma atividade relacionada com JAX-WS, e sempre que preciso relembrar algo, ou até mesmo buscar novas soluções, me deparo com documentações e exemplos não muito intuitivos. Deste modo, resolvi postar aqui um primeiro exemplo relacionado a esta tecnologia. Este trata-se de algo que é bastante utilizado no mundo JAX-WS, que é a comunicação assíncrona. Vamos então à narrativa:

Rafael Chies: Imagine que temos um cenário onde um código cliente quer invocar o serviço de um servidor. Porém, o cliente não deseja esperar pela resposta, pois o processo pode ser demorado. Ao invés dessa espera, o cliente deseja que o servidor lhe envie uma nova requisição de resposta, quando a mesma estiver pronta. Essa ideia é ilustrada na figura abaixo:

Requisição e reposta de forma assíncrona.

Leitor: Poxa, legal Rafael, mas como faço para que o meu Webservice lá do servidor seja assíncrono ?

Rafael Chies: Fácil, para isso existe o conceito de OneWay, presente na especificação (JSR224). A implementação da spec nos fornece uma anotação @OneWay, que devemos atribuir a um método void do serviço, que desejamos ser assíncrono. Com isso, quando o serviço receber a requisição, já é enviada a resposta HTTP 202, liberando a conexão com o cliente. Segue abaixo a listagem de exemplo de utilização do OneWay.

Método assíncrono anotado com @OneWay

Leitor: Legal, bem fácil mesmo. Mas, e como que fazemos a amarração de onde o servidor deve enviar a resposta quando a mesma for processada ? E como o cliente vai saber que uma resposta que chegou é de uma determinada requisição original ?

Rafael Chies: Boa pergunta. Para isso existe a especificação WS-Addressing. Esta permite, entre outras coisas, que o cliente envie, no Header da mensagem SOAP, as informações referentes ao retorno da resposta. Abaixo segue um ilustrativo da utilização do WS-Addressing.

SOAP Header

Rafael Chies: Como podemos ver, no header da mensagem SOAP, são enviados os parâmetros ReplyTo e MessageID. O primeiro é referente ao endereço em que o cliente deseja receber a resposta da requisição, enquanto que o segundo é responsável pelo ID único que deve ser enviado pelo cliente. Assim, quando o servidor for enviar a resposta, será enviado esse mesmo ID no parâmetro RelatesTo do Header, permitindo que o cliente faça o link entre requisição e resposta.

Leitor: Massa !! Acho que entendi como tudo funciona na teoria. Mas, veja bem, tem como postar um código para demonstrar esse exemplo ?

Rafael Chies: Claro. Na verdade, para não tornar esse post muito verboso e cansativo, o fonte que trás um exemplo completo deste cenário encontra-se no meu github (https://github.com/rafachies/jaxws-demos). Entretanto, para facilitar a visualização e execução deste código, segue uma imagem que ilustra o fluxo que o código de exemplo percorre.

Fluxo do exemplo

Rafael Chies: Pelo fluxo temos: A classe Main inicia as rotas do Camel. Sim, utilizei Camel pela simplicidade de levantar um serviço, junto ao CXF, sem necessitar de um servidor de aplicações. Também optei por utilizá-lo para fomentar novas implementações que pretendo fazer em próximos exemplos; Depois, A classe RequestSender executará o chamado ao serviço SOAP (RequestService), recebendo imediatamente um response 202 e fechando a conexão HTTP. Lembrando que, antes do envio da requisição, é colocado no header os dados do WS-Addressing; Chegamos ao ServerRoute, que está atrelado ao endpoint RequestService. Este, num primeiro momento, invoca o processor sleeper, que apenas deixa a Thread em espera por dez segundos, provando assim o assincronismo no processo; Na sequência, a rota chama o outro processor, que é quem vai enviar a resposta ao serviço ResponseService do cliente. Lembrando que, para isto, o processor utiliza os parâmetros ReplyTo e MessageID para saber como enviar a resposta; Por fim, o ClientRoute, percebendo a chegada da resposta, repassa a mensagem para o processor do cliente, que imprime a resposta e seu ID.

Bom, para exeuctar o exemplo, rodei pelo Eclipse mesmo, acionando a classe Main. Caso queiram gerar o JAR e executá-lo, sintam-se a vontade, o único problema é que o Camel e CXF geraram muita dependência, necessitando assim a cópia de várias bibliotecas para o devido funcionamento.
Qualquer coisa estou a disposição.

[]’s

Usando Drools com planilhas

Ta aí um dos projetos que eu mais gosto da JBoss, o Drools. Acredito que esse é um daqueles produtos que realmente podem fazer a diferença. E não falo da diferença tecnológica, mas sim de uma diferença que causa impacto no emocional (=b) das pessoas que não são de tecnologia, e muitas vezes querem entender e participar das regras de negócio de um software.

Nesse contexto, uma das funcionalidades que acho simples, e que tem muito a agregar, é a utilização de planilhas para a definição das regras do Drools (Acredito aqui que o leitor tenha noção de como funciona o Drools). Acredito que o manuseio de planilhas é uma prática muito comum entre as pessoas “de negócio”. Portanto, podemos deixar as definições e alterações das regras na mão dessas pessoas, que são as que têm o maior domínio dessas definições/alterações.

Abaixo, segue um exemplo de uma planilha que pode ser interpretada pelo Drools:

Neste exemplo, temos um cenário onde a entrada seria um bean (TrafficViolation) que contém informações sobre um conjunto de infrações causadas no trânsito. A partir deste conjunto, as regras são analisadas e, como resposta, é gerado um valor de penalidade para o infrator.
O maior trabalho na construção das planilhas, legíveis pelo Drools, fica por conta da estrutura da mesma. É necessário respeitar uma certa sequência e posição das linhas e colunas. Segue abaixo uma breve explicação da estrutura do cenário supramencionado.

linha 2: Nome para o conjunto das regras que serão definidas no arquivo.

linha 3: Import para que possamos usar os fatos (beans) em runtime. No nosso caso, o bean a ser utilizado é org.chies.drools.spike.decisiontable.TrafficViolation.

linha 5: Label da tabela de regras que seguirá nas linhas abaixo. No meu caso, pulei a linha 4 apenas por estética, poderia estar tudo junto.

linha 6: Tipo das colunas da tabela que seguirá abaixo. Os nomes são auto explicativos, CONDITION é uma condição para a regra ser atendida (“When” da sintaxe DRL), PRIORITY é a prioridade da regra, e ACTION é a ação a ser tomada quando a condição é satisfeita (“Then” da sintaxe DRL).

linha 7: Verifica a existência de um fato do tipo TrafficViolation na working memory, e referencia o fato através da variável trafficViolation.

linha 8: Contém a condição, no caso dos CONDITIONS, e as ações, no caso das ACTIONS. Repare que, na maioria das CONDITIONS, é referenciado apenas o atributo do bean TrafficViolation, sendo implícito que a condição é a de igualdade. Já no caso da CONDITION de speed, que é uma comparação de “>”, devemos deixar a comparação explicita.

linha 9: Label dado para a condição/ação.

linha 10 a 14: Cada linha representa uma regra, contendo as condições para que a regra seja ativada, e as ações a serem executadas caso isso aconteça.

Bom, acho que era isso que queria passar, apenas a simplicidade e, principalmente, a fácil manutibilidade dessas regras por parte de um participante “não desenvolvedor”. O código completo para este exemplo está no meu github

Para aqueles que usam/gostam do Drools e BRMS, sigam o blog que postarei mais exemplos de valor agregado que esse projeto nos proporciona.

[]’s

Enviando notificações XMPP através do RHQ

Este post é para aqueles que vivem o cotidiano das ferramentas de monitoramento de servidores e plataformas, como o RHQ.
Normalmente, estas ferramentas possuem a funcionalidade de envio de notificações, quando na ocorrência de algum tipo de alerta: memória insuficiente, sobrecarga no processamento, limite de conexões, etc.
A notificação, na maioria dos casos, é realizada através do envio de email, SMS, ou até mesmo de chamada de voz.
O aspecto negativo do email é o seu “teor assíncrono”, sendo que a percepção da notificação pode demorar muito, de acordo com a política de checagem por parte do responsável.
Já o SMS e a chamada de voz, a priori, possuem uma percepção mais instantânea. Porém, existe um certo custo para o envio destas notificações, podendo ser um fator limitante em suas utilizações.

Na minha opinião, uma das formas mais instantânea, e de baixo custo, é a notificação através dos “messengers da vida”, como aqueles que utilizam o protocolo XMPP. Ainda, pelo que tenho visto nas empresas, a maioria delas possuem algum comunicador instantâneo interno, ou até mesmo usam algum provedor externo, como o GTalk. A idéia é aproveitar esta infraestrutura para que os funcionários responsáveis recebam instantâneamente as notificações dos alertas.

Desta forma, iniciei o desenvolvimento de um plugin para o RHQ para implementar este tipo de funcionalidade. A idéia central é fazer com que o RHQ Server seja um usuário no servidor XMPP, e esse possa iniciar um chat e enviar mensagens para os responsáveis por sua execução.
Ja como evolução do plugin, imagino um cenário onde os usuários do RHQ não sejam apenas passivos no recebimento de alertas, mas também serem ativos ao questionar a respeito dos serviços que estão sendo monitorados.
Segue abaixo uma ilustração da idéia inicial da arquitetura.

Arquitetura do plugin XMPP

Arquitetura do plugin XMPP

O primeiro sprint do plugin já foi realizado, o código está no github (https://github.com/rafachies/rhq-xmpp), e produzi um screencast para demonstrar as funcionalidades implementadas (http://vimeo.com/35730049).

Feedback, sugestões e voluntários são bem vindos !!

[]’s

Passo a Passo / Certficado / Java / JKS

Um trabalho que já necessitei várias vezes, e também já fui questionado a respeito, é a construção de chaves e certificados para testes de conexão SSL em Java. Desta forma, resolvi escrever esse post, tanto para ajudar aqueles que precisam, como para geraçao de um backup de informaÇão para meu cérebro =b.

O esquemático abaixo ilustra o passo a passo de geração de arquivos JKS para estabelecer uma comunicação SSL entre clientes e servidores Java.

Passo a passo para geração do keystore e truststre

O primeiro passo consiste na geração da chave privada, peça vital que fica do lado daquele que irá decriptar as informações, normalmente o servidor. A extensão do arquivo normalmente é a “.pem” .

Já no segundo passo, pode-se seguir dois caminhos. Na primeira opção, você pode construir um certificado auto assinado (normalmente usado para testes). Já na segunda, você constrói um arquivo de requisição de certificado e, logo após, deve enviá-lo para um CA (Certificate Authority), para que esse gere o certificado final (que tem o “mesmo valor” que o certificado auto assinado). Lembrando que você é cobrado, e muito, pela atuação desse CA =b.
A diferença entre os dois tipos de certificados é que o primeiro não possui uma assinatura válida, sendo que os diversos browsers e demais meios de comunicação não possuem essa assinatura “home made” em sua base de conhecimento. Isto gera, por exemplo, aquelas mensagens de “Você entende os riscos de acessar esse site”, que aparecem quando acessamos alguns sites HTTPS. Mas tudo bem, para testes isso é irrelevante.

No terceiro passo, devemos gerar o arquivo que será carregado, normalmente, pelo servidor Java (Java SE app, JBoss, Tomcat, etc …). Este arquivo contém tanto uma cópia do certificado, como a chave privada. Isto porque o servidor tem que, tanto enviar o certifiado para se apresentar ao cliente, como ter a chave privada para decriptar os dados que vão trafegar.
Através do comando openssl, é gerado o arquivo de saída com a extensão p12. Porém, a maioria dos servidores necessitam o arquivo no formato JKS (Java Keystore). Assim, precisamos realizar a conversão desse arquivo. Normalmente utilizo a classe PKCS12Import, distribuída em diversas libs que encontramos por ai.

Por fim, no último passo, geramos o arquivo JKS que deverá ser instalado no lado normalmente representado pelo cliente, ou seja, no lado daquele que inicia a conexão. Repare que o arquivo final só é composto do certificado, e não da chave privada.

Pronto! Importando o primeiro JKS (também conhecido como keystore) no servidor, e o segundo JKS (também conhecido como truststore) dentro do cliente, o cenário encontra-se pronto para os testes. A comunicação entre o cliente e o servidor seria como ilustrado na figura abaixo:

Negociaçao SSL

Ainda, no meio do post, usei bastante os termos “normalmente o servidor” e “normalmente o cliente”. Isto porque pode-se também fazer uma autenticação reversa na comunicaÇão SSL, também conhecida como client authentication. Neste cenário, o cliente também terá um keystore, e o servidor armazenará o trustore do cliente. Com isso, o servidor pode ter certeza que o cliente com quem ele está falando é realmente quem ele pensa que é. Com isso, a negociação SSL evolui para “algo parecido” com a ilustração abaixo

Negociação com autenticação do cliente

Bom, muita coisa existe ainda nesse mundo de construção de chaves e certficados. Discutirei mais sobre isso em futuros posts, mas não hesitem em me “procurar” para esclarecer dúvidas ou outras funcionalidades.

[]’s

RHQ enviando Notificações para o Nagios

Fala galera.

Muitos usuários têm adotado o RHQ (JON/JOPR) como ferramenta de monitoramento, não só de instâncias JBoss, como também de outros recursos como: sistema operacional, banco de dados, servidores Web, etc.Entretanto, muitos querem centralizar a visibilidade dos alertas gerados pelo RHQ com os alertas de outras ferramentas de monitoramento. Normalmente, essa centralização acontece através do Nagios, com sua interface sendo visualizada em uma TV gigante.
Diante desta necessidade, vou demonstrar, nesse post, como integrar os alertas gerados pelo RHQ com o Nagios

A arquitetura

Para ajudar o entendimento da arquitetura desta integração, temos a figura 1 abaixo.
Figura 1 - Arquitetura da integração

Figura 1 - Arquitetura da integração

Resumidamente, o RHQ enviará uma notificação na forma de TRAP SNMP para a máquina onde o Nagios estiver presente. Nesta mesma máquina, estará executando a ferramenta que fica esperando pelo TRAP, o SNMPTRAPD. Porém, o TRAP recebido pelo SNMPTRAPD possui uma forma, digamos, “ilegível” para o Nagios. É neste ponto que entra a ferramenta SNMPTT. Esta será a tradutora do TRAP recebido, enviando para o Nagios uma mensagem “legível” através de um plugin do próprio Nagios, o submit_check_result.
Após esta breve explicação do fluxo responsável pela integração, vamos à descrição dos passos para obter a “coisa funcionando”

O Nagios
Para a instalação do Nagios, segui a documentação oficial do produto. No meu caso, como instalei em um Fedora 15, segui o link [1].

Logo após a instalação, é necessário realizar uma configuração para que o Nagios defina o serviço que será responsável pelos TRAPS do RHQ. Para tanto, basta definir um novo Service em /usr/local/nagios/etc/objects/localhost.cfg. No meu exemplo, adicionei o trecho da listagem 1 no fim do arquivo.

# Define a service to check RHQ alerts
define service{
use local-service
name TRAP
check_period 24x7
max_check_attempts 3
normal_check_interval 15
retry_check_interval 5
active_checks_enabled 1
passive_checks_enabled 0
parallelize_check 1
obsess_over_service 0
check_freshness 0
event_handler_enabled 0
flap_detection_enabled 0
process_perf_data 1
retain_status_information 1
retain_nonstatus_information 1
notification_interval 60
notification_period 24x7
notification_options w,u,c,r
notifications_enabled 1
service_description RHQ
is_volatile 1
check_command check-host-alive
max_check_attempts 1
normal_check_interval 1
retry_check_interval 1
passive_checks_enabled 1
check_period none
notification_interval 31536000
host_name localhost
}

Listagem 1 – Serviço do Nágios referente ao RHQ

O destaque aqui vai para o atributo service_descrition (em negrito). O valor deste atributo é quem faz a ponte (a mágica) entre o SNMPTT e o Nagios. Quando o SNMPTT for chamar o Nagios, será esse valor (RHQ) que será informado (veremos isso na próxima sessão).
Após esta configuração, basta inciar o Nagios, bem como o serviço httpd, para que possamos acessar sua interface Web. Ao acessar o menu Current Status -> Service, deverá surgir o serviço RHQ na lista de serviços, como na figura 2.

Figura 2 - Nagios configurado com serviço para TRAP do RHQ

Uma última coisa para ser feita a nível de Nagios, é copiar alguns plugins do Nagios para o diretório de instalação do mesmo. Como já mencionado anteriormente, isto é necessário porque a comunicação entre o SNMPTT e Nagios é realizada através de um dos plugins do Nágio, o submit_check_result. Para isso, a partir do diretório onde foram extraidos os arquivos de instalação do Nagios, realize o comando da listagem 2

mkdir /usr/local/nagios/libexec/eventhandlers
cp -r contrib/eventhandlers/* /usr/local/nagios/libexec/eventhandlers/

Listagem 2 – Cópia dos plugins do Nagios

Ok, temos o Nagios prontinho esperando pelos TRAPS do RHQ. Agora vamos pra configuração das duas ferramentas necessários para essa “ponte” entre o RHQ e o Nagios.

O SNMPTT
Seguindo a ordem inversa do fluxo (RHQ -> SNMPTRAPD -> SNMPTT -> Nagios), vamos agora para a instalação e configuração do SNMPTT. Esta ferramenta é a responsável por tratar o TRAP enviado pelo RHQ (que virá do SNMPTRAPD) e, após traduzi-lo, enviar as informações do mesmo para o Nagios.

Para a instalação desta ferramenta, faça o download em [2] e instale através do passo a passo de [3]. A instalação, neste caso, é a stand-alone (ir até o passo 5 de instalação), pois a ferramenta não executará como um daemon. Ela será inicializada pelo SNMPTRAD toda vez que chegar um novo TRAP. Fica fora do escopo deste post os detalhes da instalação do SNMPTT, uma porque tornaria o texto muito verboso, e em segundo lugar para valorizar o ótimo documento de instalação que a ferramenta já possui.

Uma única configuração que não vi no tutotial [3] foi a cópia do script snmpttconvertmib para o diretório /usr/sbin. Acho legal comentar isso, pois caso contrário, no próximo passo o leitor pode estranhar a origem de tal script. Faça a cópia se o arquivo ainda não estiver lá.

Ainda, para o devido funcionamento dos scripts do snmptt, baseados em perl, alguns pacotes do net-snmp são necessários no sistema operacional. Abaixo, na figura 3, segue a lista dos pacotes que tive de instalar no meu Fedora 15.

Figura 3 - Pacotes a serem instalados

Tendo realizada a instalação básica, passemos às configurações necessárias para o cenário proposto. A configuração é realizada apenas com a alteração do arquivo /etc/snmp/snmptt.ini (gerado após a instalação). Neste arquivo, remova a primeira linha destacada na listagem 3 e edite a segunda linha destacada.

[General]
mode = standalone
multiple_event = 1
dns_enable = 1
strip_domain = 1
strip_domain_list = <<END
your.domain
END
[...]
resolve_value_ip_addresses = 0
net_snmp_perl_enable = 1
net_snmp_perl_best_guess = 0
translate_log_trap_oid = 0
translate_value_oids = 1
translate_enterprise_oid_format = 1
translate_trap_oid_format = 1

Listagem 3 – Configuração do SNMPTT

Feito isso, chegou a hora de o SNMPTT “ficar por dentro” do TRAP que será enviado pelo RHQ. Isto é realizado através do reconhecimento da MIB que o RHQ distribui, localizada em $RHQ_HOME/etc/RHQ-mib.txt.

Para tanto, basta executar o comando do MIB Converter. Este comando converte a MIB do RHQ para um arquivo de configuração no formato do SNMPTT. O comando em questão pode ser visualizado na listagem 4.

snmpttconvertmib --in=$RHQ_HOME/etc/RHQ-mib.txt --out=/etc/snmp/snmptt.conf.rhq --exec='/usr/local/nagios/libexec/eventhandlers/submit_check_result $r RHQ 2'
Listagem 4 – Conversão da MIB do RHQ

Explicando o comando: em –in é informada a MIB do JON; em –out é informado o arquivo de saída onde ficará a definição da MIB no formato SNMPTT; em –exec é informado o comando que será invocado quando algum TRAP referente a MIB de entrada for recebido. Este comando é um comando externo do Nagios, que permite com que recursos levem uma notificação ao Nagios, e não fiquem esperando pela coleta do Nagios, que é o “padrão” utilizado. Esta ação também é conhecida como check permissivo.

Por fim, ao executar o comando da listagem 4, o leitor receberá uma mensagem não muito bacana, como a listada abaixo.

Done
Total translations: 0
Successful translations: 0
Failed translations: 0
The MIB file did not contain any TRAP-TYPE or NOTIFICATION-TYPE definitions,
so no translations occured. Try another MIB file.

Este foi o maior entrave pelo qual passei ao tentar implementar a integração. O SNMPTT, como premissa, necessita que a MIB passada como parâmetro de entrada possua a definição de um TRAP-TYPE ou um NOTIFICATION-TYPE. Já a MIB que vem com o RHQ não possui tais definições. Sendo assim, após uma série de tentativas e erros, alterei a MIB do RHQ, adicionando as linhas em destaque da listagem 5

trapGroup NOTIFICATION-GROUP
NOTIFICATIONS { coldStart }
STATUS current
DESCRIPTION "A collection of notifications for alerts"
::= { snmpMIBGroups 2 }
rhqTrapAlert TRAP-TYPE
STATUS current
ENTERPRISE jboss
DESCRIPTION "RHQ Notification: "
::= 1
END

Listagem 5 – Linhas adicionadas à MIB do RHQ

Após salvar as alterações, execute novamente o comando da listagem 4. Com isso, uma mensagem de sucesso deve ser perceptível, como a mensagem abaixo:

TRAP-TYPE: rhqTrapAlert
Looking up via snmptranslate: RHQ-MIB::rhqTrapAlert
OID: .1.3.6.1.4.1.18016.0.1
Done
Total translations: 1
Successful translations: 1
Failed translations: 0

A mensagem nos diz que foi criada a definição do TRAP no arquivo de saída (/etc/snmp/snmptt.conf.rhq), e que a definição está vinculada ao OID .1.3.6.1.4.1.18016.0.1 , que será o OID enviado pelo RHQ. Explico mais tarde o significado deste número.

Para finalizar a configuração do SNMPTT, deve ser adicionado esse novo arquivo (/etc/snmp/snmptt.conf.rhq) para a lista dos arquivos de configuração. Para isso, edite o arquivo /etc/snmp/snmptt.ini, e adicione a linha destacada em negrito na llistagem 6.

[...]
[TrapFiles]
snmptt_conf_files = <<END
/etc/snmp/snmptt.conf.rhq
END

Listagem 6 – Adição do arquivo de configuração do RHQ

Pronto, temos também o SNMPTT pronto para que a arquitetura da figura 1 funcione. Agora, seguindo a ordem inversa, vamos para a instalação e configuração do SNMPTRAPD.

O SNMPTRAPD
Esta ferramenta é o passo inicial para o recebimento do TRAP enviado pelo RHQ. É ela quem inicia um daemon que fica esperando pela chegada dos TRAPS. Normalmente, o SNMPTRAPD já vem instalado por default, como no meu caso no Fedora 15. Caso não seja a realidade do leitor, instale a ferramenta da forma padrão no seu S.O.

Fora a instalação, dois passos são necessários para configurar a ferramenta no cenário proposto da figura 1. Primeiramente, são adicionados os parâmetros da listagem 7 no arquivo /etc/snmp/snmptrapd.conf.

# envia o trap recebido para que o snmptt traduza-o
traphandle default /usr/sbin/snmptt
# desabilita autorização
disableAuthorization yes
# não loga os traps recebidos, deixa tudo com o snmptt
donotlogtraps yes

Listagem 7 – Configuração do snmptrapd

Já no segundo passo, é configurado que o SNMPTRAPD não deve traduzir o OID do TRAP, deixando-o no formato digital, e fazendo com que o SNMPTT realize o serviço. Para tanto, basta editar o arquivo /etc/rc.d/init.d/snmptrapd e adicionar a opção -On no parâmetro OPTION, como segue na listagem 8.

OPTIONS="-On -Lsd -p /var/run/snmptrapd.pid"
Listagem 8 – Acerto no script de serviço

Realizados os passos supramencionados, basta inicializar o serviço do SNMPTRAPD, que deve estar em /etc/init.d./snmptrapd. Entretanto, como sugestão, recomendo, inicialmente, que o snmptrapd seja inicializado no prṍprio terminal, podendo assim acompanhar os logs no caso de algum problema acontecer. O comando para iniciar é o da listagem 9:

/usr/sbin/snmptrapd -f -Lo 127.0.0.1 -On -Lsd -p /var/run/snmptrapd.pid
Listagem 9 – Comando para inicializar o snmptrapd

Com isso, temos o lado do Nagios totalmente preparado para receber os TRAPS do RHQ. Sendo assim, vamos para a configuração do próprio RHQ para o envio do TRAP.

O RHQ
Também fica fora do escopo deste post os passos da instalação básica do RHQ, visto que assume-se que o leitor interessado nesta integração já possua o conhecimento básico de ambas as ferramentas (RHQ e Nagios).

Assim, vamos direto a configuração do envio dos TRAPS. Primeiramente, vamos realizar as configurações básicas do RHQ para envio de TRAPS. Para tanto, basta acessar Administration -> System configuration -> Plugins, escolher a aba Server Plugins, clicar em Alert:SNMP e, na sequência, clicar em Configure Alert:SNMP.

Com isso, surgirá uma interface de configuração do Plugin do SNMP. Para este cenário, utilizei as configurações da figura 4.

Figura 4 - Configurações do SNMP

Aqui vai a explicação dos parâmetros que acho que valem a pena ser explicados: Agent Address é o endereço da máquina onde estiver rodando o SNMPTRAPD (no meu caso na mesma máquina =b ); Enterprise Oid é o OID que fará o macth no OID configurado na MIB do RHQ; Specific Id, é o id específico desta instância RHQ, fará o match com o trecho da MIB representado na listagem 10.

rhqTrapAlert TRAP-TYPE
STATUS current
ENTERPRISE jboss
DESCRIPTION "RHQ Notification: "
::= 1

Listagem 10 – Trecho do OID do TRAP

Muitos devem estar curioso sobre o porque do número 1.3.6.1.4.1.18016. Este número é o representativo da MIB do grupo JBoss, não sendo um n;umero aleatório qualquer. Existe um padrão estabelecido para estes números, sendo que as empresas/organizações “adquirem” determinadas sequências para seus produtos. Estes números são regulados pela IANA [4], e a sequência inicial, 1.3.6.1.4.1, representa que o OID é de alguma MIB privada, conforme nos mostra [5]. Já a sequência 18016, é um número adquirido pelo “grupo” JBoss, conforme nos mostra [6] (procure por JBoss no CTRL+F).

Tendo feita a configuração genérica do SNMP, vamos à configuração específica do alerta. Crie um alerta qualquer. No meu exemplo criei um alerta em cima do recurso de File System /tmp. Defini como condição do alerta que o Free Space seja maior que 10MB. Como essa condição é sempre verdadeira na minha máquina, e defini o intervalo de monitoramento em 1 minuto, o alerta será disparado todo minuto. Na figura 5 está a definição do alerta.

Figura 5 - Configurações do Alerta

Para este alerta, criei uma notificação que envia um SNMPTRAP, tendo a configuração descrita na figura 6.

Figura 6 - Configurações da Notificação SNMP

Explicando os parâmetros configurados: Host e Port representam o endereço onde o SNMPTRAPD está escutando (no meu caso, na mesma máquina). O Oid é a sequência de números que a MIB descreve. A sequência inicial, 1.3.6.1.4.1.18016, já expliquei anteriormente. Já a concatenação da sequência 2.1 serve para o envio do TRAP contendo todas as informações do alerta. Com um estudo mais aprofundado na especificação das MIB’s, perceberemos a seguinte sequência da MIB do RHQ. (listagem 11)

[...]
jboss OBJECT IDENTIFIER ::= {enterprises 18016 }
rhq OBJECT IDENTIFIER ::= {jboss 2 }
alert OBJECT IDENTIFIER ::= {rhq 1 }
alertName OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The name of the alert definition for this alert"
::= { alert 1 }
alertResourceName OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The name of the resource that triggered the alert"
::= { alert 2 }
[...]

Listagem 11 – Trecho da MIB do RHQ

Repare que ela define “jboss” como o padrão de números do “enterprise” (que é importado de uma MIB padrão) mais o valor 18016 (valor do grupo JBoss que comentei anteriormente). Como resultado, temos a sequência 1.3.6.1.4.1.18016. Depois, é declarado um objeto “rhq”, concatenando o resultado anterior ao valor 2. Logo após é declarado o objeto “alert”, que é o resultado anterior concatenado com o valor 1 (Pronto, valor 2.1 após o 18016 explicado). Assim, chegamos ao valor 1.3.6.1.4.1.18016.2.1 que foi informado em Oid. Repare que na MIB, debaixo do objeto “alert”, são definidos diversos objetos que fazem referência aos dados do alerta (alertName, alertURl, alertResourceName, etc). Estes serão os valores que irão ser enviados para o Nagios via TRAP.

Pronto, a partir de então temos tudo configurado. Se o agente do RHQ estiver em execução, os alertas serão lançados, as notificações farão o disparo do TRAP, o SNMPTRAPD receberá o TRAP, enviará o mesmo para a tradução do SNMPTT, que, por sua vez, enviará os dados do TRAP para o Nagios. Como resultados, teremos o Nagios acusando o TRAP recebido, como na figura 7.

Figura 7 - Nagios acusando o alerta do RHQ

Como último comentário, em uma das máquinas onde eu realizei esses passos, o snmptrapd acusou o seguinte erro quando na tentativa de enviar o TRAP para o snmptt:

Can't locate Config/IniFiles.pm in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at /usr/sbin/snmptt line 4026.
BEGIN failed--compilation aborted at /usr/sbin/snmptt line 4026

Este problema acontece pela falta de alguns módulos perl, base dos scripts snmptt. No meu caso tive de instalar, em primeiro lugar, o pacote do perl-Module-Build, que possui os módulos CPAN. Logo após, instalei manualmente o tal Config/IniFiles.pm através do comando:

perl -MCPAN -e 'install Config::IniFiles'

Segui estes passos em duas instalações distintas e obtive sucesso. Qualquer dúvida estou a disposição.
[]’s

Referências
[1] http://nagios.sourceforge.net/docs/3_0/quickstart-fedora.html
[2] http://sourceforge.net/projects/snmptt/files/snmptt/snmptt_1.3/
[3] http://snmptt.sourceforge.net/docs/snmptt.shtml#Installation-Overview
[4] http://www.iana.org/
[5] http://www.alvestrand.no/objectid/1.3.6.1.4.1.html
[6] http://www.iana.org/assignments/enterprise-numbers

Coding Horror

Um prato de informações tecnológicas com uma pitada de conhecimento aleatório.

InfoQ

Um prato de informações tecnológicas com uma pitada de conhecimento aleatório.

JBoss Developer Recent Posts

Um prato de informações tecnológicas com uma pitada de conhecimento aleatório.

JBossDivers

Mergulhando no Mundo JBoss

Seguir

Get every new post delivered to your Inbox.