Bash espera un ping exitoso

10

Estoy escribiendo en un script que reinicia varios servidores. Después del reinicio quiero "esperar" hasta que todos los servidores vuelvan a estar en línea. (Para simplificar las cosas, lo definí en línea = pingable)

Entonces, para cada servidor que hago

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

Lo que esperaría (y me gustaría) sería una salida como, por ejemplo,

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

donde los puntos ... aparecerían uno por uno.

Pero lo que realmente sucede es que primero solo hay

waiting for ServerXY ...

por un tiempo y cuando el servidor está de vuelta, obtengo el último punto y la última línea como

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

¿Por qué el bucle while solo se realiza dos veces como una vez con un error de ping y una vez con ping exitoso? ¿Qué debo cambiar para agregar más puntos en el ciclo while?

Hice la prueba también con una IP inexistente. Pero se quedó atascado con

waiting for NonExistentServer...

y nunca terminado por supuesto. Pero la misma pregunta ¿por qué no ........se agregan?

derHugo
fuente
Funciona bien para mí ...: /
Ravexina

Respuestas:

9

La cuestión

El problema es que lo has configurado -w 0.2. Cuando el valor es inferior a 1, se ignoran los valores de fecha límite ( -w) y tiempo de espera ( -W). Esto ha sido mencionado previamente en esta pregunta . Cuando lo usa -w 1, su script (que modifiqué un poco para eliminar bits inútiles) funciona correctamente:

$ ./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"

Solución

La solución obvia es usar -w 1. Si tiene la intención de utilizar un valor inferior a 1 segundo, el timeoutcomando debería ser mejor:

$ 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

Nuevamente, úselo con el !operador en el bucle:

#!/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"

Por supuesto, lo contrario se puede aplicar para mostrar el mensaje solo si el servidor está activo e informar cuando el servidor se cae, por ejemplo:

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

Sin embargo, tenga en cuenta que esto no es perfecto:

  • hacemos ping con solo 1 paquete por segundo. Ancho de banda bajo, conectividad deficiente, hardware defectuoso entre el servidor y el cliente que hace ping al servidor activará la salida del bucle y hará una notificación de falsos positivos

  • Confiamos en hacer ping, es decir, usar el eco ICMP. Los cortafuegos o incluso los servidores individuales bloquean las respuestas al eco de ping / ICMP. Usted podría utilizar ncde ncat(que es una versión mejorada del nc). Algo como en el bucle anterior funcionará bien en lugar de ping:

    nc -w5 -z 172.16.127.2 80

    Lo que esto hace es conectarse al servidor en 172.16.127.2 en el puerto 80. -zes evitar E / S, solo conéctese y desconecte. -wes esperar 5 segundos antes de informar una conexión fallida. Por supuesto, esto es bastante bueno para cuando tiene el servidor bajo su control y sabe que el puerto 80 está abierto. UPD se puede usar bien, pero si hay un firewall en su lugar, probablemente se prefiera TCP.

    Un beneficio oculto aquí es que si tiene algún servicio ejecutándose en un puerto específico (como HTTP en el puerto 80 o RTSP en 554), no conectarse al puerto puede servir como indicador de que su servicio necesita reiniciarse.

  • Por supuesto, ncy pingpuede ser un poco spam. La mejor manera sería hacer que el servidor se registre con otro servidor central, enviar un informe periódico, tal vez cada hora; de esa manera, si su servidor pierde un "tiempo de perforación", puede generar errores. La mejor manera es usar un servicio como Nagios, que hace eso. Pero en este punto estamos entrando en el ámbito de la informática de nivel empresarial con múltiples servidores. Si tienes algo como Raspberry Pi en casa, probablemente no necesites nada complejo.

Sergiy Kolodyazhnyy
fuente
Hola, muchas gracias por aclarar esa cosa. ¿Hay una forma diferente de hacerlo en la condición de bucle? Es perfecto para esperar un servidor, pero como mencioné, estoy esperando varios servidores más tarde. Haré algo como lo while (( $ServerA_W==1 || $ServerB_W==1 || .....))que se mantiene cuando cada servidor está de vuelta.
derHugo
Después, por ejemplo, un servidor está de vuelta esperando a los demás, no quiero hacer ping al que ya está de vuelta;)
derHugo
Sugeriría escribir eso como función e iniciar la instancia de cada función con la dirección IP como argumento en segundo plano. Pero recomendaría no imprimir los puntos, solo deje que cada función imprima el mensaje cuando aparezca el servidor. Avíseme si desea que escriba un ejemplo de dicha función
Sergiy Kolodyazhnyy
1
@Joanne Sí, es posible. Puedo actualizar mi respuesta más tarde hoy o mañana. Personalmente, no haría ping al servidor continuamente ya que es un poco spam, sin embargo
Sergiy Kolodyazhnyy
1
@Joanne Vea mis actualizaciones en la respuesta. Avíseme si eso ayuda o si hay alguna otra pregunta
Sergiy Kolodyazhnyy