108 lines
2.9 KiB
Python
108 lines
2.9 KiB
Python
import os.path
|
|
import socket
|
|
from pathlib import Path
|
|
from urllib.parse import urlparse
|
|
|
|
import frappe.utils
|
|
from frappe import get_conf
|
|
from frappe.exceptions import UrlSchemeNotSupported
|
|
|
|
REDIS_KEYS = ("redis_cache", "redis_queue")
|
|
|
|
|
|
def can_connect(sock: socket.socket, address: tuple[str, int] | str, timeout: int | float) -> bool:
|
|
"""
|
|
Check whether we can connect to a socket address.
|
|
|
|
Args:
|
|
sock: The socket object to use for the connection.
|
|
address: The address to connect to (tuple for network, string for unix).
|
|
timeout: Connection timeout in seconds.
|
|
|
|
Returns:
|
|
True if connection was successful, False otherwise.
|
|
"""
|
|
sock.settimeout(timeout)
|
|
try:
|
|
sock.connect(address)
|
|
sock.shutdown(socket.SHUT_RDWR)
|
|
return True
|
|
except OSError:
|
|
return False
|
|
finally:
|
|
sock.close()
|
|
|
|
|
|
def is_open(
|
|
scheme: str,
|
|
hostname: str | None,
|
|
port: int | str | None,
|
|
path: str | None,
|
|
timeout: int | float = 10,
|
|
) -> bool:
|
|
"""
|
|
Check if a service is reachable via socket connection.
|
|
|
|
Args:
|
|
scheme: The URL scheme (redis, mariadb, etc.) or 'unix'.
|
|
hostname: The remote host to connect to.
|
|
port: The port number to connect to.
|
|
path: The path to the unix socket (if scheme is 'unix').
|
|
timeout: Connection timeout in seconds.
|
|
|
|
Returns:
|
|
True if the service is reachable, False otherwise.
|
|
"""
|
|
if scheme in ("redis", "rediss", "postgres", "mariadb"):
|
|
if not hostname or not port:
|
|
return False
|
|
|
|
try:
|
|
addresses = socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
|
|
except (socket.gaierror, TypeError):
|
|
return False
|
|
|
|
# Try all addresses returned by getaddrinfo
|
|
for family, socket_type, proto, _, address in addresses:
|
|
s = socket.socket(family, socket_type, proto)
|
|
if can_connect(s, address, timeout):
|
|
return True
|
|
return False
|
|
|
|
if scheme == "unix":
|
|
if not path:
|
|
return False
|
|
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
return can_connect(s, path, timeout)
|
|
|
|
raise UrlSchemeNotSupported(scheme)
|
|
|
|
|
|
def check_database():
|
|
config = get_conf()
|
|
db_type = config.get("db_type", "mariadb")
|
|
if db_type == "sqlite":
|
|
db_path = Path(frappe.utils.get_site_path()) / "db" / f"{config.db_name}.db"
|
|
return {db_type: db_path.is_file() and os.access(db_path, os.R_OK | os.W_OK)}
|
|
if db_socket := config.get("db_socket"):
|
|
return {db_type: is_open("unix", None, None, db_socket)}
|
|
db_host = config.get("db_host", "127.0.0.1")
|
|
db_port = config.get("db_port", 3306 if db_type == "mariadb" else 5432)
|
|
return {db_type: is_open(db_type, db_host, db_port, None)}
|
|
|
|
|
|
def check_redis(redis_services=None):
|
|
config = get_conf()
|
|
services = redis_services or REDIS_KEYS
|
|
status = {}
|
|
for srv in services:
|
|
url = urlparse(config[srv])
|
|
status[srv] = is_open(url.scheme, url.hostname, url.port, url.path)
|
|
return status
|
|
|
|
|
|
def check_connection(redis_services=None):
|
|
service_status = {}
|
|
service_status.update(check_database())
|
|
service_status.update(check_redis(redis_services))
|
|
return service_status
|