Dispatch parameterized daemon actions

This commit is contained in:
Lyncoln
2026-09-17 16:14:15 -04:00
parent 64cdc024b0
commit 6e00e80c5e
3 changed files with 158 additions and 4 deletions
+25 -4
View File
@@ -81,7 +81,13 @@ class Plugin:
+ ", ".join(missing)
)
def execute(self, action, config, runtime_state):
def execute(
self,
action,
config,
runtime_state,
request_parameters=None,
):
if action not in self.actions:
raise RegistryError(
f"plugin {self.name} does not support action {action}"
@@ -98,9 +104,18 @@ class Plugin:
# Backward-compatible generic state injection. Existing one-argument
# plugins continue to receive only config; state-aware plugins can
# declare a second positional parameter named runtime_state.
parameters = list(inspect.signature(handler).parameters.values())
handler_parameters = list(
inspect.signature(handler).parameters.values()
)
if len(parameters) >= 2:
if len(handler_parameters) >= 3:
return handler(
config,
runtime_state,
request_parameters or {},
)
if len(handler_parameters) >= 2:
return handler(config, runtime_state)
return handler(config)
@@ -178,7 +193,12 @@ class Registry:
in self.resources.items()
}
def execute(self, resource_id, action):
def execute(
self,
resource_id,
action,
request_parameters=None,
):
resource = self.resources.get(resource_id)
if resource is None:
@@ -190,6 +210,7 @@ class Registry:
action,
resource.config,
resource.runtime_state,
request_parameters,
)
return {
+23
View File
@@ -209,6 +209,20 @@ def handle_request(
"resources": registry.list_resources(),
})
request_parameters = req.get(
"parameters",
{},
)
if not isinstance(request_parameters, dict):
return protocol_response({
"ok": False,
"error_code": "invalid_request",
"error": "parameters must be a JSON object",
"resource": req.get("resource"),
"action": action,
})
resource = req.get("resource")
if not resource:
@@ -259,6 +273,15 @@ def handle_request(
"action": action,
})
if request_parameters:
return protocol_response(
registry.execute(
resource,
action,
request_parameters,
)
)
return protocol_response(
registry.execute(
resource,
+110
View File
@@ -0,0 +1,110 @@
#!/usr/bin/env python3
import sys
sys.path.insert(0, "daemon")
import server
class FakeRegistry:
def __init__(self):
self.resources = {"postgresql-ha": object()}
self.executions = []
def execute(
self,
resource,
action,
request_parameters=None,
):
self.executions.append(
(resource, action, request_parameters)
)
return {
"ok": True,
"resource": resource,
"plugin": "fake",
"action": action,
"result": {
"parameters": request_parameters or {},
},
}
def require(condition, message):
if not condition:
raise SystemExit("FAIL: " + message)
print("===== PARAMETERIZED ACTIONS =====")
registry = FakeRegistry()
server.registry = registry
server.pacemaker_resource_id = "postgresql-ha"
server.pacemaker_peer_uid = 65534
server.pacemaker_peer_gid = 65534
server.pacemaker_node_name = "david-prodesk"
peer = {
"pid": 1,
"uid": 65534,
"gid": 65534,
}
parameters = {
"type": "pre",
"operation": "demote",
"promote_uname": "david-eve",
"demote_uname": "david-prodesk",
}
response = server.handle_request(
{
"protocol_version": 1,
"resource": "postgresql-ha",
"action": "notify",
"parameters": parameters,
},
peer,
)
require(response["ok"] is True, "parameterized action was rejected")
require(
response["result"]["parameters"] == parameters,
"action parameters were not preserved",
)
require(
registry.executions
== [("postgresql-ha", "notify", parameters)],
"parameterized action was not dispatched exactly once",
)
print("parameterized_action dispatched")
response = server.handle_request(
{
"protocol_version": 1,
"resource": "postgresql-ha",
"action": "notify",
"parameters": [],
},
peer,
)
require(
response["ok"] is False,
"non-object parameters were accepted",
)
require(
response["error_code"] == "invalid_request",
"invalid parameters had the wrong classification",
)
require(
len(registry.executions) == 1,
"invalid parameters reached the registry",
)
print("invalid_parameters rejected")
print()
print("PARAMETERIZED_ACTION_TEST_PASS=1")