255 lines
5.0 KiB
Python
Executable File
255 lines
5.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import json
|
|
import socket
|
|
import sys
|
|
|
|
|
|
PROTOCOL_VERSION = 1
|
|
VALID_OCF_RCS = {0, 1, 7, 8}
|
|
|
|
|
|
class TransportFailure(Exception):
|
|
pass
|
|
|
|
|
|
class ProtocolFailure(Exception):
|
|
pass
|
|
|
|
|
|
def send_request(
|
|
socket_path,
|
|
resource,
|
|
action,
|
|
parameters=None,
|
|
):
|
|
request = {
|
|
"protocol_version": PROTOCOL_VERSION,
|
|
"action": action,
|
|
"resource": resource,
|
|
}
|
|
|
|
if parameters is not None:
|
|
request["parameters"] = parameters
|
|
|
|
try:
|
|
sock = socket.socket(
|
|
socket.AF_UNIX,
|
|
socket.SOCK_STREAM,
|
|
)
|
|
sock.settimeout(2.0)
|
|
|
|
try:
|
|
sock.connect(socket_path)
|
|
|
|
sock.sendall(
|
|
json.dumps(
|
|
request,
|
|
separators=(",", ":"),
|
|
).encode("utf-8")
|
|
+ b"\n"
|
|
)
|
|
|
|
raw = b""
|
|
|
|
while b"\n" not in raw:
|
|
chunk = sock.recv(65536)
|
|
|
|
if not chunk:
|
|
break
|
|
|
|
raw += chunk
|
|
|
|
finally:
|
|
sock.close()
|
|
|
|
except (
|
|
socket.timeout,
|
|
ConnectionError,
|
|
OSError,
|
|
) as exc:
|
|
raise TransportFailure from exc
|
|
|
|
return raw
|
|
|
|
|
|
def decode_response(raw):
|
|
if not raw:
|
|
raise ProtocolFailure
|
|
|
|
try:
|
|
response = json.loads(
|
|
raw.decode("utf-8").strip()
|
|
)
|
|
|
|
except (
|
|
UnicodeDecodeError,
|
|
json.JSONDecodeError,
|
|
) as exc:
|
|
raise ProtocolFailure from exc
|
|
|
|
if not isinstance(response, dict):
|
|
raise ProtocolFailure
|
|
|
|
return response
|
|
|
|
|
|
def validate_response(response, resource, action):
|
|
if response.get("protocol_version") != PROTOCOL_VERSION:
|
|
raise ProtocolFailure
|
|
|
|
if response.get("ok") is not True:
|
|
raise ProtocolFailure
|
|
|
|
if response.get("resource") != resource:
|
|
raise ProtocolFailure
|
|
|
|
if response.get("action") != action:
|
|
raise ProtocolFailure
|
|
|
|
if "result" not in response:
|
|
raise ProtocolFailure
|
|
|
|
return response["result"]
|
|
|
|
|
|
def format_monitor_result(result):
|
|
if not isinstance(result, dict):
|
|
raise ProtocolFailure
|
|
|
|
if result.get("delegated") is True:
|
|
backend = result.get("backend")
|
|
runtime = result.get("container_runtime")
|
|
|
|
if backend != "container":
|
|
raise ProtocolFailure
|
|
|
|
if runtime not in ("podman", "docker"):
|
|
raise ProtocolFailure
|
|
|
|
return "|".join([
|
|
"delegate",
|
|
backend,
|
|
runtime,
|
|
])
|
|
|
|
ha = result.get("ha")
|
|
|
|
if not isinstance(ha, dict):
|
|
raise ProtocolFailure
|
|
|
|
try:
|
|
ocf_rc = int(ha.get("ocf_rc"))
|
|
|
|
except (
|
|
TypeError,
|
|
ValueError,
|
|
) as exc:
|
|
raise ProtocolFailure from exc
|
|
|
|
if ocf_rc not in VALID_OCF_RCS:
|
|
raise ProtocolFailure
|
|
|
|
receiver = result.get("wal_receiver") or {}
|
|
|
|
if not isinstance(receiver, dict):
|
|
raise ProtocolFailure
|
|
|
|
return "|".join([
|
|
str(ocf_rc),
|
|
str(ha.get("health", "unknown")),
|
|
str(ha.get("ha_role", "unknown")),
|
|
str(ha.get("state", "unknown")),
|
|
str(
|
|
ha.get(
|
|
"promotion_eligible",
|
|
"unknown",
|
|
)
|
|
).lower(),
|
|
str(result.get("role", "unknown")),
|
|
str(
|
|
result.get(
|
|
"streaming",
|
|
"unknown",
|
|
)
|
|
).lower(),
|
|
str(receiver.get("slot_name") or ""),
|
|
str(receiver.get("sender_host") or ""),
|
|
])
|
|
|
|
|
|
def format_result(action, result):
|
|
if action == "monitor":
|
|
return format_monitor_result(result)
|
|
|
|
return json.dumps(
|
|
result,
|
|
separators=(",", ":"),
|
|
sort_keys=True,
|
|
)
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) not in (6, 7):
|
|
raise SystemExit(11)
|
|
|
|
socket_path = sys.argv[1]
|
|
resource = sys.argv[2]
|
|
action = sys.argv[3]
|
|
request_parameters = None
|
|
|
|
if len(sys.argv) == 7:
|
|
try:
|
|
request_parameters = json.loads(sys.argv[6])
|
|
|
|
except (
|
|
TypeError,
|
|
json.JSONDecodeError,
|
|
):
|
|
raise SystemExit(11)
|
|
|
|
if not isinstance(request_parameters, dict):
|
|
raise SystemExit(11)
|
|
|
|
try:
|
|
transport_failure = int(sys.argv[4])
|
|
protocol_failure = int(sys.argv[5])
|
|
|
|
except ValueError:
|
|
raise SystemExit(11)
|
|
|
|
if not resource or not action:
|
|
raise SystemExit(protocol_failure)
|
|
|
|
try:
|
|
raw = send_request(
|
|
socket_path,
|
|
resource,
|
|
action,
|
|
request_parameters,
|
|
)
|
|
|
|
except TransportFailure:
|
|
raise SystemExit(transport_failure)
|
|
|
|
try:
|
|
response = decode_response(raw)
|
|
result = validate_response(
|
|
response,
|
|
resource,
|
|
action,
|
|
)
|
|
output = format_result(
|
|
action,
|
|
result,
|
|
)
|
|
|
|
except ProtocolFailure:
|
|
raise SystemExit(protocol_failure)
|
|
|
|
print(output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|