Skip to content

Commit

Permalink
Add support for GetUnitFileState API
Browse files Browse the repository at this point in the history
- Adds support for GetUnitFileState API on controller and node
- Adds is-enabled command to bluechictl
- Adds integration test

Fixes: #934
Signed-off-by: Martin Perina <[email protected]>
  • Loading branch information
mwperina committed Sep 30, 2024
1 parent 34d9e91 commit 6e25ef1
Show file tree
Hide file tree
Showing 13 changed files with 226 additions and 9 deletions.
12 changes: 12 additions & 0 deletions data/org.eclipse.bluechi.Node.xml
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,18 @@
<arg name="unitfiles" type="a(ss)" direction="out" />
</method>

<!--
GetUnitFileState:
@file: The name of the unit file
@state: The current enablement status of the unit file
Get the current enablement status of specific unit file.
-->
<method name="GetUnitFileState">
<arg name="file" type="s" direction="in" />
<arg name="state" type="s" direction="out" />
</method>

<!--
Reload:
Expand Down
4 changes: 4 additions & 0 deletions data/org.eclipse.bluechi.internal.Agent.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@
<method name="ListUnitFiles">
<arg name="units" type="a(ss)" direction="out" />
</method>
<method name="GetUnitFileState">
<arg name="file" type="s" direction="in" />
<arg name="state" type="s" direction="out" />
</method>
<method name="Subscribe">
<arg name="unit" type="s" direction="in" />
</method>
Expand Down
6 changes: 5 additions & 1 deletion doc/man/bluechictl.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Performs one of the listed lifecycle operations on the given systemd unit for th

### **bluechictl** [*kill*] [*agent*] [*unit*]

Kills the processes of (i.e. sends a signal to) the specified unit on the chosen node.
Kills the processes of (i.e. sends a signal to) the specified unit on the chosen node.

**Options:**

Expand Down Expand Up @@ -80,6 +80,10 @@ Fetches information about all systemd unit files on the bluechi-agents. If [blue
**--filter**
Use glob filter for the unit file path

### **bluechictl** *is-enabled* [*agent*] [*unit*]

Fetches the current enablement status of the specific unit file on the specific `bluechi-agent`.

### **bluechictl** *list-units* [*agent*]

Fetches information about all systemd units on the bluechi-agents. If [bluechi-agent] is not specified, all agents are queried.
Expand Down
1 change: 1 addition & 0 deletions src/agent/agent.c
Original file line number Diff line number Diff line change
Expand Up @@ -1763,6 +1763,7 @@ static const sd_bus_vtable internal_agent_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("ListUnits", "", UNIT_INFO_STRUCT_ARRAY_TYPESTRING, agent_method_list_units, 0),
SD_BUS_METHOD("ListUnitFiles", "", UNIT_FILE_INFO_STRUCT_ARRAY_TYPESTRING, agent_method_list_unit_files, 0),
SD_BUS_METHOD("GetUnitFileState", "s", "s", agent_method_passthrough_to_systemd, 0),
SD_BUS_METHOD("GetUnitProperties", "ss", "a{sv}", agent_method_get_unit_properties, 0),
SD_BUS_METHOD("GetUnitProperty", "sss", "v", agent_method_get_unit_property, 0),
SD_BUS_METHOD("SetUnitProperties", "sba(sv)", "", agent_method_set_unit_properties, 0),
Expand Down
2 changes: 2 additions & 0 deletions src/client/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "method-enable-disable.h"
#include "method-freeze-thaw.h"
#include "method-help.h"
#include "method-is-enabled.h"
#include "method-kill.h"
#include "method-list-unit-files.h"
#include "method-list-units.h"
Expand Down Expand Up @@ -43,6 +44,7 @@ int method_version(UNUSED Command *command, UNUSED void *userdata) {
const Method methods[] = {
{ "help", 0, 0, OPT_NONE, method_help, usage_bluechi },
{ "list-unit-files", 0, 1, OPT_FILTER, method_list_unit_files, usage_method_list_unit_files },
{ "is-enabled", 2, 2, OPT_NONE, method_is_enabled, usage_method_is_enabled },
{ "list-units", 0, 1, OPT_FILTER, method_list_units, usage_method_list_units },
{ "start", 2, 2, OPT_NONE, method_start, usage_method_lifecycle },
{ "stop", 2, 2, OPT_NONE, method_stop, usage_method_lifecycle },
Expand Down
1 change: 1 addition & 0 deletions src/client/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ client_src = [
'main.c',
'client.c',
'method-help.c',
'method-is-enabled.c',
'method-loglevel.c',
'method-list-unit-files.c',
'method-list-units.c',
Expand Down
66 changes: 66 additions & 0 deletions src/client/method-is-enabled.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright Contributors to the Eclipse BlueChi project
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include <stdlib.h>
#include <string.h>

#include "client.h"
#include "method-is-enabled.h"
#include "usage.h"

#include "libbluechi/common/opt.h"

static int method_is_enabled_on(Client *client, char *node_name, char *unit_file) {
int r = 0;
_cleanup_sd_bus_error_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_sd_bus_message_ sd_bus_message *result = NULL;
const char *state = NULL;

r = assemble_object_path_string(NODE_OBJECT_PATH_PREFIX, node_name, &client->object_path);
if (r < 0) {
return r;
}

r = sd_bus_call_method(
client->api_bus,
BC_INTERFACE_BASE_NAME,
client->object_path,
NODE_INTERFACE,
"GetUnitFileState",
&error,
&result,
"s",
unit_file);
if (r < 0) {
fprintf(stderr, "Failed to issue method call: %s\n", error.message);
return r;
}

r = sd_bus_message_read(result, "s", &state);
if (r < 0) {
fprintf(stderr, "Failed to read result of method call: %s\n", error.message);
return r;
}

printf("%s\n", state);

if (strcmp(state, "enabled") == 0 || strcmp(state, "enabled-runtime") == 0 ||
strcmp(state, "static") == 0 || strcmp(state, "alias") == 0 || strcmp(state, "indirect") == 0 ||
strcmp(state, "generated") == 0) {
return 0;
}

return -1;
}

int method_is_enabled(Command *command, void *userdata) {
return method_is_enabled_on(userdata, command->opargv[0], command->opargv[1]);
}

void usage_method_is_enabled() {
usage_print_header();
usage_print_description("Check whether unit file is enabled");
usage_print_usage("bluechictl is-enabled [nodename] [unit]");
}
13 changes: 13 additions & 0 deletions src/client/method-is-enabled.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright Contributors to the Eclipse BlueChi project
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once

#include <stddef.h>

#include "libbluechi/cli/command.h"

int method_is_enabled(Command *command, void *userdata);
void usage_method_is_enabled();
1 change: 1 addition & 0 deletions src/controller/node.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ static const sd_bus_vtable node_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("ListUnits", "", UNIT_INFO_STRUCT_ARRAY_TYPESTRING, node_method_list_units, 0),
SD_BUS_METHOD("ListUnitFiles", "", UNIT_FILE_INFO_STRUCT_ARRAY_TYPESTRING, node_method_list_unit_files, 0),
SD_BUS_METHOD("GetUnitFileState", "s", "s", node_method_passthrough_to_agent, 0),
SD_BUS_METHOD("StartUnit", "ss", "o", node_method_start_unit, 0),
SD_BUS_METHOD("StopUnit", "ss", "o", node_method_stop_unit, 0),
SD_BUS_METHOD("FreezeUnit", "s", "", node_method_passthrough_to_agent, 0),
Expand Down
19 changes: 19 additions & 0 deletions tests/bluechi_test/bluechictl.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,25 @@ def list_unit_files(
expected_result,
)

def is_enabled(
self,
node_name: str,
unit_name: str,
check_result: bool = False,
expected_result: int = 0,
) -> Tuple[Optional[int], Union[Iterator[bytes], Any, Tuple[bytes, bytes]]]:
# track started units to stop and reset failures on cleanup
if node_name not in self.tracked_services:
self.tracked_services[node_name] = []
self.tracked_services[node_name].append(unit_name)

return self._run(
f"Fetching enablement status of unit '{unit_name}' on node '{node_name}'",
f"is-enabled {node_name} {unit_name}",
check_result,
expected_result,
)

def get_unit_status(
self,
node_name: str,
Expand Down
37 changes: 29 additions & 8 deletions tests/bluechi_test/systemctl.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,21 +117,42 @@ def is_unit_in_state(self, unit_name: str, expected_state: str) -> bool:
LOGGER.info(f"Got state '{latest_state}' for unit {unit_name}")
return latest_state == expected_state

def is_unit_enabled(
def enable_unit(
self, unit_name: str, check_result: bool = True, expected_result: int = 0
) -> bool:
_, output = self._do_operation_on_unit(
) -> Tuple[Optional[int], Union[Iterator[bytes], Any, Tuple[bytes, bytes]]]:
self.tracked_services.append(unit_name)

return self._do_operation_on_unit(
unit_name, "enable", check_result, expected_result
)

def disable_unit(
self, unit_name: str, check_result: bool = True, expected_result: int = 0
) -> Tuple[Optional[int], Union[Iterator[bytes], Any, Tuple[bytes, bytes]]]:
self.tracked_services.append(unit_name)

return self._do_operation_on_unit(
unit_name, "disable", check_result, expected_result
)

def is_enabled(
self, unit_name: str, check_result: bool = False, expected_result: int = 0
) -> str:
return self._do_operation_on_unit(
unit_name, "is-enabled", check_result, expected_result
)
return output == "enabled"

def is_unit_enabled(
self, unit_name: str, check_result: bool = True, expected_result: int = 0
) -> bool:
_, out = self.is_enabled(unit_name=unit_name)
return "enabled" == out

def is_unit_disabled(
self, unit_name: str, check_result: bool = True, expected_result: int = 0
) -> bool:
_, output = self._do_operation_on_unit(
unit_name, "is-enabled", check_result, expected_result
)
return output == "disabled"
_, out = self.is_enabled(unit_name=unit_name)
return "disabled" == out

def service_is_active(self, unit_name: str) -> bool:
result, _ = self.client.exec_run(
Expand Down
3 changes: 3 additions & 0 deletions tests/tests/tier0/bluechictl-is-enabled/main.fmf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
summary: Test if bluechictl is-enabled returns the same enablement status of a specific
node as running systemctl is-enabled on the node for all unit files
id: 387cdca2-fa98-4c35-8bd0-352572a7e206
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#
# Copyright Contributors to the Eclipse BlueChi project
#
# SPDX-License-Identifier: LGPL-2.1-or-later

from typing import Dict

from bluechi_test.config import BluechiAgentConfig, BluechiControllerConfig
from bluechi_test.machine import BluechiAgentMachine, BluechiControllerMachine
from bluechi_test.systemd_lists import (
RegexPattern,
SystemdUnitFile,
parse_bluechictl_list_output,
)
from bluechi_test.test import BluechiTest

node_foo_name = "node-foo"


def check_execs(
ctrl: BluechiControllerMachine,
node: BluechiAgentMachine,
unit_name: str,
check_output: bool = True,
):
bc_res, bc_out = ctrl.bluechictl.is_enabled(
node_name=node_foo_name, unit_name=unit_name
)
sc_res, sc_out = node.systemctl.is_enabled(unit_name=unit_name)

assert bc_res == sc_res
if check_output:
assert bc_out == sc_out


def exec(ctrl: BluechiControllerMachine, nodes: Dict[str, BluechiAgentMachine]):
node_foo = nodes[node_foo_name]

# Traversing over existing unit files is the easiest way to cover all existing enablement statuses
all_res, all_out = ctrl.bluechictl.list_unit_files(node_name=node_foo_name)
assert all_res == 0
all_unit_files = parse_bluechictl_list_output(
content=all_out,
line_pattern=RegexPattern.BLUECHICTL_LIST_UNIT_FILES,
item_class=SystemdUnitFile,
)
for unit in all_unit_files[node_foo_name].values():
check_execs(ctrl=ctrl, node=node_foo, unit_name=unit.key)

# Error message from bluechictl is not completely the same as from systemctl for non-existent service
check_execs(
ctrl=ctrl, node=node_foo, unit_name="non-existent.service", check_output=False
)


def test_bluechi_list_unit_files_on_a_node(
bluechi_test: BluechiTest,
bluechi_ctrl_default_config: BluechiControllerConfig,
bluechi_node_default_config: BluechiAgentConfig,
):

node_foo_cfg = bluechi_node_default_config.deep_copy()
node_foo_cfg.node_name = node_foo_name

bluechi_ctrl_default_config.allowed_node_names = [node_foo_name]

bluechi_test.set_bluechi_controller_config(bluechi_ctrl_default_config)
bluechi_test.add_bluechi_agent_config(node_foo_cfg)

bluechi_test.run(exec)

0 comments on commit 6e25ef1

Please sign in to comment.