blob: e7635b064f8ebaca48e0dddd74ad4a68efc131a4 [file] [log] [blame]
/*
* Copyright (C) 2020 CESNET, https://photonics.cesnet.cz/
*
* Written by Tomáš Pecka <tomas.pecka@fit.cvut.cz>
*
*/
#include "trompeloeil_doctest.h"
#include "dbus-helpers/dbus_systemd_server.h"
#include "fake.h"
#include "health/inputs/DbusSystemdInput.h"
#include "test_log_setup.h"
#include "utils/log-init.h"
#include "utils/log.h"
TEST_CASE("Systemd monitor")
{
TEST_INIT_LOGS;
trompeloeil::sequence seq1;
// Create and setuo separate connections for both client and server. Could be done using a single connection but this way it is more generic
auto clientConnection = sdbus::createSessionBusConnection();
auto serverConnection = sdbus::createSessionBusConnection("cz.cesnet.systemd1");
clientConnection->enterEventLoopAsync();
serverConnection->enterEventLoopAsync();
auto mx = std::make_shared<FakeManager>();
auto server = DbusSystemdServer(*serverConnection);
// i1 gets constructed which means:
// - a registration is performed, along with an updateState call (State::OK)
// - i1's constructor queries the current state and performs updateState
REQUIRE_CALL(*mx, registerInput(ANY(void*), velia::health::State::OK)).LR_SIDE_EFFECT(mx->updateState(_1, _2)).IN_SEQUENCE(seq1);
REQUIRE_CALL(*mx, updateState(ANY(void*), velia::health::State::OK)).IN_SEQUENCE(seq1);
// create units. Unit2 and Unit3 are in states that we consider failed
// therefore the DbusSystemdInput will report ERROR after loading the second unit
// FailedUnits: {unit2, unit3} -> ERROR
server.createUnit(*serverConnection, "unit1.service", "/cz/cesnet/systemd1/unit/unit1", "active", "running");
server.createUnit(*serverConnection, "unit2.service", "/cz/cesnet/systemd1/unit/unit2", "activating", "auto-restart");
server.createUnit(*serverConnection, "unit3.service", "/cz/cesnet/systemd1/unit/unit3", "failed", "failed");
server.createUnit(*serverConnection, "unitIgnored.service", "/cz/cesnet/systemd1/unit/unitIgnored", "failed", "failed");
REQUIRE_CALL(*mx, updateState(ANY(void*), velia::health::State::OK)).IN_SEQUENCE(seq1);
REQUIRE_CALL(*mx, updateState(ANY(void*), velia::health::State::ERROR)).IN_SEQUENCE(seq1);
REQUIRE_CALL(*mx, updateState(ANY(void*), velia::health::State::ERROR)).IN_SEQUENCE(seq1);
auto i1 = std::make_shared<velia::health::DbusSystemdInput>(mx, std::set<std::string> {"unitIgnored.service"}, *clientConnection, "cz.cesnet.systemd1", "/cz/cesnet/systemd1", "cz.cesnet.systemd1.Manager", "cz.cesnet.systemd1.Unit");
// i1 now listens for dbus events, we can start the semaphore server
// FailedUnits: {unit3} -> ERROR
REQUIRE_CALL(*mx, updateState(i1.get(), velia::health::State::ERROR)).IN_SEQUENCE(seq1);
server.changeUnitState("/cz/cesnet/systemd1/unit/unit2", "active", "running");
// FailedUnits: {} -> OK
REQUIRE_CALL(*mx, updateState(i1.get(), velia::health::State::OK)).IN_SEQUENCE(seq1);
server.changeUnitState("/cz/cesnet/systemd1/unit/unit3", "active", "running");
// In case we obtain a notifications that unit changed state from (X,Y) to (X,Y), do not trigger any events.
server.changeUnitState("/cz/cesnet/systemd1/unit/unit3", "active", "running");
// add new unit with failed/failed, DbusSystemdInput should receive UnitNew signal and monitor this unit too
// FailedUnits: {unit4} -> OK
REQUIRE_CALL(*mx, updateState(i1.get(), velia::health::State::ERROR)).IN_SEQUENCE(seq1);
server.createUnit(*serverConnection, "unit4.service", "/cz/cesnet/systemd1/unit/unit4", "failed", "failed");
// unitIgnored is ignored by us, so it can change in any way but since we don't obtain the notifications, nothing will happen
server.changeUnitState("/cz/cesnet/systemd1/unit/unitIgnored", "failed", "failed");
server.changeUnitState("/cz/cesnet/systemd1/unit/unitIgnored", "active", "auto-restarting");
server.changeUnitState("/cz/cesnet/systemd1/unit/unitIgnored", "active", "running");
waitForCompletionAndBitMore(seq1);
// FailedUnits: {} -> OK
REQUIRE_CALL(*mx, updateState(i1.get(), velia::health::State::OK)).IN_SEQUENCE(seq1);
server.changeUnitState("/cz/cesnet/systemd1/unit/unit4", "active", "running");
waitForCompletionAndBitMore(seq1);
REQUIRE_CALL(*mx, unregisterInput(i1.get())).IN_SEQUENCE(seq1);
i1.reset();
}
#if 0
// Runs a StateManager with DbusSystemdInput connected to the development machine's systemd. Might be useful for debugging.
TEST_CASE("This machine's systemd monitor")
{
TEST_INIT_LOGS;
auto clientConnection = sdbus::createSystemBusConnection();
auto mx = std::make_shared<velia::health::StateManager>();
auto i1 = std::make_shared<velia::DbusSystemdInput>(mx, *clientConnection);
clientConnection->enterEventLoop();
}
#endif