Bash espera por um sucesso de ping

10

Estou escrevendo em um script reiniciando vários servidores. Após a reinicialização, quero "esperar" até que todos os servidores estejam novamente online. (Para simplificar, eu defini para mim on-line = pingável)

Então, para cada servidor que eu faço

ServerXY_W=1
echo -n "waiting for ServerXY ..."
while (($ServerXY_W == 1))
do
   if ping -c 1 -w 0.2 192.168.123.123 &> /dev/null
   then
      echo "ServerXY is back online!"
      ServerXY_W=0
   else
      echo -n "."
   fi
done

O que eu esperaria (e gostaria) seria uma saída como por exemplo

waiting for ServerXY .................
ServerXY is back online!

onde os pontos .... apareceriam um por um.

Mas o que realmente acontece é primeiro, há apenas

waiting for ServerXY ...

por um tempo e quando o servidor está de volta eu recebo o último ponto e a última linha como

waiting for ServerXY ....
ServerXY is back online!

Por que o loop while é executado apenas duas vezes como uma vez com falha no ping e uma vez com êxito no ping? O que preciso alterar para adicionar mais pontos no loop while?

Eu fiz o teste também com um IP inexistente. Mas ficou preso com

waiting for NonExistentServer...

e nunca terminou, é claro. Mas a mesma pergunta, por que não ........são adicionados?

derHugo
fonte
Funciona bem para mim ...: /
Ravexina

Respostas:

9

O problema

O problema é que você definiu -w 0.2. Quando o valor estiver abaixo de 1, os valores de prazo ( -w) e timeout ( -W) serão ignorados. Isso foi mencionado anteriormente nesta pergunta . Quando você usa -w 1, seu script (que eu modifiquei levemente para remover bits inúteis) funciona corretamente:

$ ./ping_server.sh                                                 
waiting for ServerXY ....................
Server is back online

$ cat ./ping_server.sh
#!/bin/bash
printf "%s" "waiting for ServerXY ..."
while ! ping -c 1 -n -w 1 147.153.237.192 &> /dev/null
do
    printf "%c" "."
done
printf "\n%s\n"  "Server is back online"

Solução

Solução óbvia é usar -w 1. Se você pretende usar um valor menor que 1 segundo, o timeoutcomando deve ser melhor:

$ timeout 0.2 ping -c 1 147.153.237.192                            
PING 147.153.237.192 (147.153.237.192) 56(84) bytes of data.
64 bytes from 147.153.237.192: icmp_seq=1 ttl=124 time=2.61 ms

--- 147.153.237.192 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 2.612/2.612/2.612/0.000 ms

Novamente, use-o com o !operador no loop:

#!/bin/bash
printf "%s" "waiting for ServerXY ..."
while ! timeout 0.2 ping -c 1 -n 147.153.237.192 &> /dev/null
do
    printf "%c" "."
done
printf "\n%s\n"  "Server is back online"

Obviamente, o oposto pode ser aplicado para mostrar a mensagem apenas se o servidor estiver ativo e reportar quando o servidor estiver inativo, por exemplo:

$ while ping -q -c 1 172.16.127.2 >/dev/null ; do sleep 1; done ; echo "Server stopped responding"
Server stopped responding

Observe no entanto, isso não é perfeito:

  • estamos fazendo ping com apenas 1 pacote a cada segundo. Baixa largura de banda, conectividade ruim, hardware ruim entre o servidor e o cliente que executa ping no servidor acionará o loop para sair e fará uma notificação de falso positivo

  • Estamos confiando no ping, que está usando o eco ICMP. Firewalls ou mesmo servidores individuais bloqueiam as respostas ao eco de ping / ICMP. Você poderia usar ncde ncat(que é uma versão melhorada do nc). Algo como no loop acima funcionará bem em vez de ping:

    nc -w5 -z 172.16.127.2 80

    O que isso faz é conectar-se ao servidor na 172.16.127.2 na porta 80. -zé evitar a E / S - basta conectar e desconectar. -wé aguardar 5 segundos antes de relatar falha na conexão. Claro que isso é muito bom para quando você tem um servidor sob seu controle e sabe que a porta 80 está aberta. O UPD pode ser usado bem, mas se houver firewall, provavelmente o TCP é o preferido.

    Um benefício oculto aqui é que, se você tiver algum serviço em execução em uma porta específica (como HTTP na porta 80 ou RTSP em 554), a falha na conexão à porta poderá servir como indicador de que seu serviço precisa ser reiniciado.

  • Claro, nce pingpode ser um pouco spam. A melhor maneira seria fazer o check-in do servidor com outro servidor central, enviar um relatório periódico, talvez a cada hora; Dessa forma, se o servidor perder um "tempo de espera", você poderá gerar erros. A melhor maneira é usar um serviço como o Nagios, que faz isso. Mas neste momento estamos entrando no domínio da computação em nível corporativo com vários servidores. Se você tem algo como Raspberry Pi em casa, provavelmente não precisa de nada complexo.

Sergiy Kolodyazhnyy
fonte
Oi, muito obrigado por limpar essa coisa -w! Existe uma maneira diferente de fazê-lo na condição de loop? É perfeito para aguardar um servidor, mas, como mencionado, estou aguardando vários servidores mais tarde. Farei algo parecido com o while (( $ServerA_W==1 || $ServerB_W==1 || .....))que ocorre quando todos os servidores estão de volta.
DerHugo 28/06
Depois de, por exemplo, um servidor está de volta à espera para os outros que eu não quero fazer ping o que está de volta já;)
derHugo
Eu sugiro escrever isso como função e iniciar instância de cada função com endereço IP como argumento em segundo plano. Mas eu recomendo que não imprima os pontos, deixe cada função imprimir a mensagem quando o servidor chegar. Deixe-me saber se você deseja que eu escreva um exemplo dessa função #
Sergiy Kolodyazhnyy
1
@ Joanne Sim, é possível. Posso atualizar minha resposta mais tarde hoje ou amanhã. Pessoalmente, eu não
efetuaria
1
@ Joanne Veja minhas atualizações na resposta. Deixe-me saber se isso ajuda ou se há quaisquer outras perguntas
Sergiy Kolodyazhnyy