Script en Python que comprueba el estado de un sitio web y devuelve los valores necesarios para pasar el estado al sistema de monitorización Pandora FMS. El script Python permite obtener el estado o bien mediante su código devuelto o bien mediante la comparación con algún valor devuelto en alguna de sus cabeceras (headers). Generamos un ejecutable portable de la aplicación Python tanto para Windows como para Linux.
- Código fuente completo del script Python EstadoSitio.py para comprobar el estado de un sitio web.
- Generar ejecutable portable EstadoSitio.exe a partir de EstadoSitio.py en Windows.
- Generar ejecutable portable EstadoSitio a partir de EstadoSitio.py en Linux.
- Ejemplo de uso de la aplicación EstadoSitio.py.
- Descarga del código fuente Python y de los ejecutables para Windows y para Linux EstadoSitio.py.
Código fuente completo del script Python EstadoSitio.py para comprobar el estado de un sitio web
A continuación, mostramos él código fuente completo en Python para obtener el estado de un sitio web:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# ProyectoA.com Aplicación Python para obtener el estado de un sitio web y devolver los datos en formato tentacle para Pandora FMS # Versión 2.0 import argparse import http.client import ssl # Clase para conectar con sitio web, devuelve en atributos el resultado de la conexión y el objeto conexionHTTP class ConectarSitioWeb: urlRaiz = "" urlCompleta = "" puerto = None resultadoConexion = None conexionHTTP = None mensaje = "" # Constructor def __init__(self, urlRaiz, urlCompleta, puerto): self.urlRaiz = urlRaiz self.urlCompleta = urlCompleta self.puerto = puerto # Setters def setURLRaiz(self, url): self.urlRaiz = url def setURLCompleta(self, url): self.urlCompleta = url def setPuerto(self, puerto): self.puerto = puerto # Getters def getResultadoConexion (self): return self.resultadoConexion def getConexionHTTP (self): return self.conexionHTTP def getMensaje (self): return self.mensaje # Método para conectar con sitio web (devolverá True si la conexión se ha realizado) def Conectar(self): # Para desactivar los mensajes de aviso de certificado para HTTPS ssl._create_default_https_context = ssl._create_unverified_context try: if self.puerto != None: conexionHTTP = http.client.HTTPSConnection(self.urlRaiz, self.puerto) else: conexionHTTP = http.client.HTTPSConnection(self.urlRaiz) conexionHTTP.request("GET", self.urlCompleta) resultadoConexion = conexionHTTP.getresponse() # Pasamos los valores devueltos a los atributos de la clase self.conexionHTTP = conexionHTTP self.resultadoConexion = resultadoConexion return True except Exception as ex: # Si se produce un error, mostraremos el error en la descripción del módulo self.mensaje = "Error: {0}".format(getattr(ex, 'message', str(ex))) return False # Mostrar resultado en consola con formato tentacle de Pandora FMS def MostrarResultado(resultado, url, nombreModulo, mensaje): print("<module>") print("<name><![CDATA[{0}]]></name>".format(nombreModulo)) print("<type><![CDATA[generic_proc]]></type>") print("<data><![CDATA[{0}]]></data>".format(resultado)) print("<description><![CDATA[Comprobar estado {0}. {1}]]></description>".format(url, mensaje)) # print("<unit>%</unit>") # print("<min_critical>95</min_critical>") # print("<max_critical>100</max_critical>") print("</module>") # Mostrar y preparar los argumentos que admite el programa por la línea de comandos def MostrarArgumentos(): # Iniciamos el programa, obteniendo los argumentos pasados por la línea de comandos # Conformamos los argumentos que admitirá el programa por la línea de comandos parser = argparse.ArgumentParser() parser.add_argument("-ur", "--urlraiz", type=str, required=True, help="URL raíz a analizar (sin http://)") parser.add_argument("-uc", "--urlcompleta", type=str, required=True, help="URL completa a analizar (con http o https://)") parser.add_argument("-p", "--puerto", type=int, required=False, help="Puerto de conexión") parser.add_argument("-m", "--modulo", type=str, required=True, help="Nombre del módulo") parser.add_argument("-v", "--version", action="store_true", required=False, help="Muestra por pantalla la versión de OpenSSL") parser.add_argument("-e", "--ComprobarPorEncabezado", action="store_true", required=False, help="Para obtener web activa usa un encabezado, necesita los parámetros -EncabezadoNombre -EncabezadoValor") parser.add_argument("-c", "--ComprobarPorCodigoRespuesta", action="store_true", required=False, help="Para obtener web activa usa un encabezado, necesita los parámetros -EncabezadoNombre -EncabezadoValor") parser.add_argument("-en", "--EncabezadoNombre", type=str, required=False, help="Nombre del encabezado para obtener su valor y comprobar si es igual. Por ejemplo 'X-Redirect-By'") parser.add_argument("-ev", "--EncabezadoValor", type=str, required=False, help="Valor del encabezado indicado en parámetro -en para comprobar si es igual, si lo es la web se marca como activa") parser.add_argument("-me", "--MostrarEncabezados", action="store_true", required=False, help="Devuelve todos los encabezados del sitio web y sus valores") return parser.parse_args() # Comprobar el estado de la web por código de respuesta def ComprobarEstadoWebCodigo(resultadoConexion, conexionHTTP, urlRaiz, modulo): # Códigos de estado que se consideran correctos # 200 OK # 201 Created # 202 Accepted # 203 Non-Authoritative Information # 204 No Content # 205 Reset Content # 206 Partial Content # 207 Multi-Status (WebDAV) # 208 Already Reported (WebDAV) # 226 IM Used (HTTP Delta encoding) # 301 Moved Permanently # NOTA: si la web pasar por un WAF y no especificamos una URL con un fichero, tipo .../index.php, puede # que devuelva el estado 403 Forbidden try: if resultadoConexion.status in (200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 301): resultado = 1 else: resultado = 0 # Establecemos el código de estado devuelto para mostrarlo en la descripción del módulo mensaje = "Codigo_Estado: {0}, Motivo: {1}".format( resultadoConexion.status, resultadoConexion.reason) MostrarResultado(resultado, urlRaiz, modulo, mensaje) # NOTA: si la web pasar por un WAF y no especificamos una URL con un fichero, tipo .../index.php, puede # que devuelva el estado 403 Forbidden conexionHTTP.close except Exception as ex: # Si se produce un error, mostraremos el error en la descripción del módulo MostrarResultado(0, urlRaiz, modulo, "Error: {0}".format(getattr(ex, 'message', str(ex)))) # Comprobar estado de la web por valor de cabecera: Date, Server, Upgrade, Connection, X-Powered-By, # Expires, Cache-Control, X-Redirect-By, Location, Content-Length, Content-Type, etc. def ComprobarEstadoWebCabecera(resultadoConexion, conexionHTTP, urlRaiz, modulo, cabecera, valorCabecera): try: # Obtenemos el valor de la cabecera pasada por parámetro valorCabeceraDevuelto = resultadoConexion.getheader(cabecera) resultado = 0 # Si no existe la cabecera, devolvemos error en el sitio web y pasamos en la descripción el motivo if valorCabeceraDevuelto is None: resultado = 0 mensaje = "No se ha encontrado la cabecera: {0}".format(cabecera) conexionHTTP.close else: # Comprobamos si el valor de la cabecera obtenida del sitio web es igual al pasado por parámetro # Si es igual, el estado será correcto (1) compEncabezados = valorCabeceraDevuelto.upper() == valorCabecera.upper() if compEncabezados: resultado = 1 else: resultado = 0 conexionHTTP.close # Establecemos el valor de la cabecera devuelto para mostrarlo en la descripción del módulo mensaje = "Cabecera: {0}, Comparar: {1}, Devuelto: {2}".format( cabecera, valorCabecera, valorCabeceraDevuelto) # todasLasCabeceras = resultadoConexion.getheaders() # print(todasLasCabeceras) MostrarResultado(resultado, urlRaiz, modulo, mensaje) except Exception as ex: # Si se produce un error, mostraremos el error en la descripción del módulo MostrarResultado(0, urlRaiz, modulo, "Error: {0}".format(getattr(ex, 'message', str(ex)))) # Mostrar todos los encabezados (headers) del sitio web y sus valores def ObtenerEncabezadosSitioWeb(resultadoConexion, conexionHTTP): try: # Obtenemos todos los encabezados (headers) return resultadoConexion.getheaders() except Exception as ex: return "" # Procedimiento que ejecuta el resto de procedimientos para iniciar el programa def IniciarPrograma(): # Preparamos y mostramos los argumentos (si se indica) args = MostrarArgumentos() if args.version: print("Versión OpenSSL: {0}".format(ssl.OPENSSL_VERSION)) if args.urlraiz and args.urlcompleta: urlRaiz = args.urlraiz urlCompleta = args.urlcompleta puerto = args.puerto # Instanciamos la clase ConectarSitioWeb conexion = ConectarSitioWeb(urlRaiz, urlCompleta, puerto) if conexion.Conectar(): # Si se ha pasado el parámetro de mostrar encabezados -me # Mostramos los encabezados Headers del sitio web y su valor if args.MostrarEncabezados: print(ObtenerEncabezadosSitioWeb(conexion.getResultadoConexion(), conexion.getConexionHTTP())) else: # Si no se ha pasado el parámetro -me, comprobamos estado sitio web # Comprobamos que se haya pasado uno de los dos argumentos excluyentes: # -e (comprobar por Encabezado) o -c (Comprobar por código) comprobarCodigoRespuesta = False comprobarEncabezado = False if args.ComprobarPorCodigoRespuesta: comprobarCodigoRespuesta = True if args.ComprobarPorEncabezado: comprobarEncabezado = True continuar = False # Si no se ha pasado ninguno de los dos parámetros de tipo de comprobación if not comprobarEncabezado and not comprobarCodigoRespuesta: continuar = False print("Debe añadir uno de los dos argumentos, o bien -e (comprobar por encabezado) o bien -c (comprobar por código)") if comprobarEncabezado or comprobarCodigoRespuesta: continuar = True # No se admiten los dos parámetros -e o -c a la vez if comprobarEncabezado and comprobarCodigoRespuesta: continuar = False print("Debe indicar solo uno de los dos argumento, o bien -e o bien -c") if continuar: try: # Si se comprueba si la web está activa por código de respuesta if args.ComprobarPorCodigoRespuesta: ComprobarEstadoWebCodigo(conexion.getResultadoConexion(), conexion.getConexionHTTP(), urlRaiz, args.modulo) # Si se comprueba si la web está activa por código de respuesta if args.ComprobarPorEncabezado: continuar = True # Si no se ha indicado el nombre del encabezado o el valor del encabezado, salir y avisar if args.EncabezadoNombre is None: print("Ha indicado la comprobación por encabezado (argumento -e) pero no ha indicado el nombre del encabezado (argumento -en)") continuar = False if args.EncabezadoValor is None: print("Ha indicado la comprobación por encabezado (argumento -e) pero no ha indicado el valor del encabezado (argumento -ev)") continuar = False if continuar: ComprobarEstadoWebCabecera(conexion.getResultadoConexion(), conexion.getConexionHTTP(), urlRaiz, args.modulo, args.EncabezadoNombre, args.EncabezadoValor) except Exception as ex: # Si se produce un error, mostraremos el error en la descripción del módulo MostrarResultado(0, urlRaiz, args.modulo, "Error: {0}".format(getattr(ex, 'message', str(ex)))) else: MostrarResultado(0, urlRaiz, args.modulo, conexion.getMensaje()) else: print("No ha indicado la URL raíz (parámetro -ur) o la URL completa (parámetro -uc)") # Iniciamos el programa IniciarPrograma() |
Las opciones del script Python EstadoSitio.py:
- -h, –help: muestra la ayuda del comando, con los posibles parámetros y su descripción.
- –ur URLRAIZ, –urlraiz URLRAIZ: URL raíz a analizar (sin http://).
- -uc URLCOMPLETA, –urlcompleta URLCOMPLETA: URL completa a analizar (con http o https://).
- -p PUERTO, –puerto PUERTO: puerto de conexión (80, 443, etc.).
- -m MODULO, –modulo MODULO: nombre del módulo para Pandora FMS.
- -v, –version: muestra por pantalla la versión de OpenSSL usada.
- -e, –ComprobarPorEncabezado: para obtener si la web está OK usará un encabezado (header) y su valor, el nombre del encabezado a comparar se le debe pasar por el parámetro -en y el valor a comparar por el parámetro -ev.
- -c, –ComprobarPorCodigoRespuesta: para obtener si la web está OK mediante el código de estado devuelto por el servidor.
- -en ENCABEZADONOMBRE, –EncabezadoNombre ENCABEZADONOMBRE: si usamos el parámetro -e, deberemos añadir este parámetro para indicar el nombre del encabezado para obtener su valor y comprobar si es igual. Por ejemplo ‘X-Redirect-By’. El valor del encabezado se indica en el parámetro -ev.
- -ev ENCABEZADOVALOR, –EncabezadoValor ENCABEZADOVALOR: si hemos usado el parámetro -e, indicaremos en este parámetro el valor del encabezado (indicando en -en) a comparar.
- -me, –MostrarEncabezados: muestra todos los encabezados (headers) del sitio web y su valor.
Generar ejecutable portable EstadoSitio.exe a partir de EstadoSitio.py en Windows
Para generar el ejecutable portable del script Python anterior, lo guardaremos en un fichero con el nombre EstadoSitio.py y lo colocaremos en una carpeta del equipo Windows. Este equipo tendrá instalado Python y pyinstaller, como indicamos en este tutorial:
Aunque lo explicamos para Linux, el proceso para Windows es exactamente igual, instalando pyinstaller en Python con:
1 |
pip3 install pyinstaller |
Una vez instalado pyinstaller, abriremos una ventana de MS-DOS (cmd o símbolo de sistema) y accederemos a la carpeta donde se encuentre el script Python EstadoSitio.py, en nuestro caso en la carpeta D:\Mis documentos\ProyectoA\Python\EstadoSitioWeb:
1 |
D:\Mis documentos\ProyectoA\Python\EstadoSitioWeb |
Ejecutaremos el siguiente comando para generar el fichero ejecutable portable EstadoSitio.exe que podemos llevarnos a cualquier otro equipo Windows. Funcionará sin necesidad de instalar ningún software adicional:
1 |
pyinstaller.exe EstadoSitio.py -F |
La herramienta pyinstaller habrá generado un fichero ejecutable con el nombre EstadoSitio.exe en la carpeta dist. Podremos ejecutarlo con el parámetro -h para mostrar la ayuda:
1 |
EstadoSitio.exe -h |
Este fichero está listo para poder ser ejecutando en cualquier equipo Windows, como mostramos más adelante.
A la hora de generar el ejecutable con pyinstaller, si nos muestra este error:
«pyinstaller.exe» no se reconoce como un comando interno o externo, programa o archivo por lotes ejecutable.
Se debe a que no tenemos añadida la ruta del ejecutable pyinstaller.exe en la variable de entorno PATH del equipo. Podemos solucionar este problema buscando dónde tenemos el ejecutable pyinstaller.exe. que en el caso de Windows 11 y una instalación estándar de Python 3.12, el ejecutable de pyinstaller se encuentra en:
C:\Users\usuario\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\Scripts
Para agregar la ruta a la variable de entorno PATH, abriremos las propiedades del sistema (tecla de Windows + Pausa). Pulsaremos en «Configuración avanzada del sistema»:
Pulsaremos en «Variables de entorno» en la pestaña «Opciones avanzadas»:
Seleccionaremos la variable de entorno «Path» y pulsaremos en «Editar»:
Añadiremos una nueva línea con la ruta donde se encuentre el fichero pyinstaller.exe y pulsaremos «Aceptar»:
De esta forma, funcionará el comando pyinstaller.exe ejecutado desde una ventana de MS-DOS o Símbolo de sistema (cmd).
Generar ejecutable portable EstadoSitio a partir de EstadoSitio.py en Linux
De la misma forma que hemos hecho en un equipo Windows, en Linux es similar. Copiaremos el código fuente Python del script EstadoSitio.py en un fichero y, ubicándonos en la ruta donde se encuentre dicho fichero, ejecutaremos:
1 |
pyinstaller EstadoSitio.py -F |
Ejemplo de uso de la aplicación Python EstadoSitio.py
Desde un equipo Windows, con el fichero ejecutable EstadoSitio.exe, desde una ventana de MS-DOS (Símbolo de sistema o cmd), accederemos a la carpeta donde tengamos el ejecutable EstadoSitio.exe y. Para comprobar si el sitio web proyectoa.com está activo, comparando el valor de la cabecera «Server» con «HTTPd», ejecutaremos el siguiente comando:
1 |
EstadoSitioWeb.exe -ur proyectoa.com -uc https://proyectoa.com/index.php -m "Web_OK_ProyectoA" -e -en "Server" -ev "HTTPd" |
El comando devolverá el siguiente texto por consola si el sitio web está activo, en este caso, si el valor devuelto en la cabecera «Server» es «HTTPd»:
1 2 3 4 5 6 |
<module> <name><![CDATA[Web_OK_ProyectoA]]></name> <type><![CDATA[generic_proc]]></type> <data><![CDATA[1]]></data> <description><![CDATA[Comprobar estado proyectoa.com. Cabecera: Server, Comparar: HTTPd, Devuelto: HTTPd]]></description> </module> |
En el caso de que el sitio web no esté activo o haya habido algún error en su comprobación,. Por ejemplo, si el valor devuelto de la cabecera Server es diferente al consultado (lo hemos cambiado a «HTTPdd» para forzar el error), devolverá:
1 2 3 4 5 6 |
<module> <name><![CDATA[Web_OK_ProyectoA]]></name> <type><![CDATA[generic_proc]]></type> <data><![CDATA[0]]></data> <description><![CDATA[Comprobar estado proyectoa.com. Cabecera: Server, Comparar: HTTPdd, Devuelto: HTTPd]]></description> </module> |
Y si se ha producido algún error al comprobar el sitio web, devolverá el error en «description»:
1 2 3 4 5 6 |
<module> <name><![CDATA[Web_OK_ProyectoA]]></name> <type><![CDATA[generic_proc]]></type> <data><![CDATA[0]]></data> <description><![CDATA[Comprobar estado proyectoa.com. Error: [Errno 11001] getaddrinfo failed]]></description> </module> |
Puesto que el formato de salida es para el sistema de monitorización Pandora FMS, mostrará en «data», el valor «1» si el sitio web es correcto o «0» si no lo es. Y siempre devolverá el formato admitido por los sensores de Pandora FMS.
En este ejemplo, hemos usado el método de comprobar el valor de una cabecera. También podemos usar el comprobar el estado devuelto por el servidor. Por ejemplo, para comprobar si el sitio web proyectoa.com es correcto, con el código de estado devuelto, ejecutaremos:
1 2 3 4 5 6 |
<module> <name><![CDATA[Web_OK_Proyectoa]]></name> <type><![CDATA[generic_proc]]></type> <data><![CDATA[1]]></data> <description><![CDATA[Comprobar estado proyectoa.com. Codigo_Estado: 200, Motivo: OK]]></description> </module> |
El programa Python admite como estado correcto los siguientes códigos de estado:
- 200 OK
- 201 Created
- 202 Accepted
- 203 Non-Authoritative Information
- 204 No Content
- 205 Reset Content
- 206 Partial Content
- 207 Multi-Status (WebDAV)
- 208 Already Reported (WebDAV)
- 226 IM Used (HTTP Delta encoding)
- 301 Moved Permanently
Hay que tener en cuenta que si el sitio web está protegido por un WAF (Web Application Firewall), el intento de obtención del estado podría considerarlo un ataque, devolviendo el código: 403 Forbidden. En este caso, es recomendable usar el método de obtener el valor de alguna cabecera (que hemos mostrado al principio de este punto).
En el caso de un equipo Linux, el script EstadoSitio.py funciona exactamente igual, colocándonos donde se encuentre el ejecutable EstadoSitio, ejecutaremos el siguiente comando para comprobar si el sitio web proyectoa.com está activo (mediante la comparación del valor de la cabecera «Server» a «HTTHd»):
1 |
./EstadoSitio -ur proyectoa.com -uc https://proyectoa.com/index.php -m "Web_OK_ProyectoA" -e -en "Server" -ev "HTTPd" |
Podremos comprobar que devuelve, exactamente, los mismos valores que si lo ejecutásemos en Windows.
El programa permite obtener todas las cabeceras de un sitio web y su valor, por si necesitamos usar el método de comprobación de cabecera, para ello añadiremos el parámetro -me:
1 |
EstadoSitioWeb.exe -ur proyectoa.com -uc https://proyectoa.com/index.php -m Web_OK -p 443 -me |
Devolverá algo así como:
1 |
[('Date', 'Sat, 10 Feb 2024 13:11:06 GMT'), ('Content-Type', 'text/html; charset=iso-8859-1'), ('Transfer-Encoding', 'chunked'), ('Connection', 'keep-alive'), ('Vary', 'Accept-Encoding'), ('Age', '0'), ('Server', 'HTTPd')] |
Podremos usar cualquiera de los encabezados devueltos: Date, Content-Type, Transfer-Encoding, Connection, Vary, Age, Server, etc. para comprobar si el sitio web está OK.
Descarga del código fuente Python y de los ejecutables para Windows y para Linux EstadoSitio.py
En el siguiente enlace podréis descargar el código fuente completo del script Python EstadoSitio.py, así como los ejecutables para Windows y para Linux: