diff --git a/scripts/policy/frameworks/management/agent/api.zeek b/scripts/policy/frameworks/management/agent/api.zeek index 6607c27144..f29b05e7cc 100644 --- a/scripts/policy/frameworks/management/agent/api.zeek +++ b/scripts/policy/frameworks/management/agent/api.zeek @@ -66,7 +66,11 @@ export { ## The controller sends this to every agent to request a dispatch (the ## execution of a pre-implemented activity) to all cluster nodes. This ## is the generic controller-agent "back-end" implementation of explicit - ## client-controller "front-end" interactions. + ## client-controller "front-end" interactions, including: + ## + ## - :zeek:see:`Management::Controller::API::get_id_value_request`: two + ## arguments, the first being "get_id_value" and the second the name + ## of the ID to look up. ## ## reqid: a request identifier string, echoed in the response event. ## diff --git a/scripts/policy/frameworks/management/agent/main.zeek b/scripts/policy/frameworks/management/agent/main.zeek index 2dafd0414d..004b874888 100644 --- a/scripts/policy/frameworks/management/agent/main.zeek +++ b/scripts/policy/frameworks/management/agent/main.zeek @@ -321,6 +321,10 @@ event Management::Node::API::node_dispatch_response(reqid: string, result: Manag # confirm their type here based on the requested dispatch command. switch req$node_dispatch_state$action[0] { + case "get_id_value": + if ( result?$data ) + result$data = result$data as string; + break; default: Management::Log::error(fmt("unexpected dispatch command %s", req$node_dispatch_state$action[0])); diff --git a/scripts/policy/frameworks/management/controller/api.zeek b/scripts/policy/frameworks/management/controller/api.zeek index 9b374d54f4..e9bdff1893 100644 --- a/scripts/policy/frameworks/management/controller/api.zeek +++ b/scripts/policy/frameworks/management/controller/api.zeek @@ -80,6 +80,31 @@ export { result: Management::ResultVec); + ## zeek-client sends this event to retrieve the current value of a + ## variable in Zeek's global namespace, referenced by the given + ## identifier (i.e., variable name). The controller asks all agents + ## to retrieve this value from each cluster node, accumulates the + ## returned responses, and responds with a get_id_value_response + ## event back to the client. + ## + ## reqid: a request identifier string, echoed in the response event. + ## + ## id: the name of the variable whose value to retrieve. + global get_id_value_request: event(reqid: string, id: string); + + ## Response to a get_id_value_request event. The controller sends this + ## back to the client. + ## + ## reqid: the request identifier used in the request event. + ## + ## result: a :zeek:type:`vector` of :zeek:see:`Management::Result` + ## records. Each record covers one Zeek cluster node. Each record's + ## data field contains a string with the JSON rendering (as produced + ## by :zeek:id:`to_json`, including the error strings it potentially + ## returns). + global get_id_value_response: event(reqid: string, result: Management::ResultVec); + + # Testing events. These don't provide operational value but expose # internal functionality, triggered by test cases. diff --git a/scripts/policy/frameworks/management/controller/main.zeek b/scripts/policy/frameworks/management/controller/main.zeek index d7a292d7f4..2f2ad42ff4 100644 --- a/scripts/policy/frameworks/management/controller/main.zeek +++ b/scripts/policy/frameworks/management/controller/main.zeek @@ -42,7 +42,11 @@ export { ## and report their outcomes. See ## :zeek:see:`Management::Agent::API::node_dispatch_request` and ## :zeek:see:`Management::Agent::API::node_dispatch_response` for the - ## agent/controller interaction. + ## agent/controller interaction, and + ## :zeek:see:`Management::Controller::API::get_id_value_request` and + ## :zeek:see:`Management::Controller::API::get_id_value_response` + ## for an example of a specific API the controller generalizes into + ## a dispatch. type NodeDispatchState: record { ## The dispatched action. The first string is a command, ## any remaining strings its arguments. @@ -598,6 +602,10 @@ event Management::Agent::API::node_dispatch_response(reqid: string, results: Man # type "any": confirm their (known) type here. switch req$node_dispatch_state$action[0] { + case "get_id_value": + if ( results[i]?$data ) + results[i]$data = results[i]$data as string; + break; default: Management::Log::error(fmt("unexpected dispatch command %s", req$node_dispatch_state$action[0])); @@ -620,6 +628,12 @@ event Management::Agent::API::node_dispatch_response(reqid: string, results: Man # Send response event to the client based upon the dispatch type. switch req$node_dispatch_state$action[0] { + case "get_id_value": + Management::Log::info(fmt( + "tx Management::Controller::API::get_id_value_response %s", + Management::Request::to_string(req))); + event Management::Controller::API::get_id_value_response(req$id, req$results); + break; default: Management::Log::error(fmt("unexpected dispatch command %s", req$node_dispatch_state$action[0])); @@ -629,6 +643,42 @@ event Management::Agent::API::node_dispatch_response(reqid: string, results: Man Management::Request::finish(req$id); } +event Management::Controller::API::get_id_value_request(reqid: string, id: string) + { + Management::Log::info(fmt("rx Management::Controller::API::get_id_value_request %s %s", reqid, id)); + + # Special case: if we have no instances, respond right away. + if ( |g_instances| == 0 ) + { + Management::Log::info(fmt("tx Management::Controller::API::get_id_value_response %s", reqid)); + event Management::Controller::API::get_id_value_response(reqid, vector( + Management::Result($reqid=reqid, $success=F, $error="no instances connected"))); + return; + } + + local action = vector("get_id_value", id); + local req = Management::Request::create(reqid); + req$node_dispatch_state = NodeDispatchState($action=action); + + for ( name in g_instances ) + { + if ( name !in g_instances_ready ) + next; + + local agent_topic = Management::Agent::topic_prefix + "/" + name; + local areq = Management::Request::create(); + + areq$parent_id = req$id; + add req$node_dispatch_state$requests[areq$id]; + + Management::Log::info(fmt( + "tx Management::Agent::API::node_dispatch_request %s %s to %s", + areq$id, action, name)); + + Broker::publish(agent_topic, Management::Agent::API::node_dispatch_request, areq$id, action); + } + } + event Management::Request::request_expired(req: Management::Request::Request) { # Various handlers for timed-out request state. We use the state members @@ -665,6 +715,12 @@ event Management::Request::request_expired(req: Management::Request::Request) switch req$node_dispatch_state$action[0] { + case "get_id_value": + Management::Log::info(fmt( + "tx Management::Controller::API::get_id_value_response %s", + Management::Request::to_string(req))); + event Management::Controller::API::get_id_value_response(req$id, req$results); + break; default: Management::Log::error(fmt("unexpected dispatch command %s", req$node_dispatch_state$action[0])); @@ -717,6 +773,7 @@ event zeek_init() Management::Controller::API::get_instances_response, Management::Controller::API::set_configuration_response, Management::Controller::API::get_nodes_response, + Management::Controller::API::get_id_value_response, Management::Controller::API::test_timeout_response ]; diff --git a/scripts/policy/frameworks/management/node/main.zeek b/scripts/policy/frameworks/management/node/main.zeek index 68749c11b5..7bc321c869 100644 --- a/scripts/policy/frameworks/management/node/main.zeek +++ b/scripts/policy/frameworks/management/node/main.zeek @@ -19,7 +19,36 @@ redef Management::Log::role = Management::NODE; ## provided result record. type DispatchCallback: function(args: vector of string, res: Management::Result); +## Implementation of the "get_id_value" dispatch. Its only argument is the name +## of the ID to look up. +function dispatch_get_id_value(args: vector of string, res: Management::Result) + { + if ( |args| == 0 ) + { + res$success = F; + res$error = "get_id_value expects name of global identifier"; + return; + } + + local val = lookup_ID(args[0]); + + # The following lookup_ID() result strings indicate errors: + if ( type_name(val) == "string" ) + { + local valstr: string = val; + if ( valstr == "" || valstr == "" ) + { + res$success = F; + res$error = valstr[1:-1]; + } + } + + if ( res$success ) + res$data = to_json(val); + } + global g_dispatch_table: table[string] of DispatchCallback = { + ["get_id_value"] = dispatch_get_id_value, }; event Management::Node::API::node_dispatch_request(reqid: string, action: vector of string)