blob: e7635b064f8ebaca48e0dddd74ad4a68efc131a4 [file] [log] [blame]
Tomáš Peckaccd80c32020-06-22 14:44:32 +02001/*
2 * Copyright (C) 2020 CESNET, https://photonics.cesnet.cz/
3 *
4 * Written by Tomáš Pecka <tomas.pecka@fit.cvut.cz>
5 *
6*/
7
8#include "trompeloeil_doctest.h"
9#include "dbus-helpers/dbus_systemd_server.h"
10#include "fake.h"
Tomáš Pecka261c8862020-11-05 11:23:08 +010011#include "health/inputs/DbusSystemdInput.h"
Tomáš Peckaccd80c32020-06-22 14:44:32 +020012#include "test_log_setup.h"
13#include "utils/log-init.h"
14#include "utils/log.h"
15
16TEST_CASE("Systemd monitor")
17{
18 TEST_INIT_LOGS;
19 trompeloeil::sequence seq1;
20
21 // Create and setuo separate connections for both client and server. Could be done using a single connection but this way it is more generic
22 auto clientConnection = sdbus::createSessionBusConnection();
23 auto serverConnection = sdbus::createSessionBusConnection("cz.cesnet.systemd1");
24 clientConnection->enterEventLoopAsync();
25 serverConnection->enterEventLoopAsync();
26
27 auto mx = std::make_shared<FakeManager>();
28 auto server = DbusSystemdServer(*serverConnection);
29
30 // i1 gets constructed which means:
31 // - a registration is performed, along with an updateState call (State::OK)
32 // - i1's constructor queries the current state and performs updateState
Tomáš Pecka261c8862020-11-05 11:23:08 +010033 REQUIRE_CALL(*mx, registerInput(ANY(void*), velia::health::State::OK)).LR_SIDE_EFFECT(mx->updateState(_1, _2)).IN_SEQUENCE(seq1);
34 REQUIRE_CALL(*mx, updateState(ANY(void*), velia::health::State::OK)).IN_SEQUENCE(seq1);
Tomáš Peckaccd80c32020-06-22 14:44:32 +020035
36 // create units. Unit2 and Unit3 are in states that we consider failed
37 // therefore the DbusSystemdInput will report ERROR after loading the second unit
38 // FailedUnits: {unit2, unit3} -> ERROR
Tomáš Peckaf2391d62020-11-06 14:02:00 +010039 server.createUnit(*serverConnection, "unit1.service", "/cz/cesnet/systemd1/unit/unit1", "active", "running");
40 server.createUnit(*serverConnection, "unit2.service", "/cz/cesnet/systemd1/unit/unit2", "activating", "auto-restart");
41 server.createUnit(*serverConnection, "unit3.service", "/cz/cesnet/systemd1/unit/unit3", "failed", "failed");
42 server.createUnit(*serverConnection, "unitIgnored.service", "/cz/cesnet/systemd1/unit/unitIgnored", "failed", "failed");
Tomáš Peckaccd80c32020-06-22 14:44:32 +020043
Tomáš Pecka261c8862020-11-05 11:23:08 +010044 REQUIRE_CALL(*mx, updateState(ANY(void*), velia::health::State::OK)).IN_SEQUENCE(seq1);
45 REQUIRE_CALL(*mx, updateState(ANY(void*), velia::health::State::ERROR)).IN_SEQUENCE(seq1);
46 REQUIRE_CALL(*mx, updateState(ANY(void*), velia::health::State::ERROR)).IN_SEQUENCE(seq1);
47 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");
Tomáš Peckaccd80c32020-06-22 14:44:32 +020048 // i1 now listens for dbus events, we can start the semaphore server
49
50 // FailedUnits: {unit3} -> ERROR
Tomáš Pecka261c8862020-11-05 11:23:08 +010051 REQUIRE_CALL(*mx, updateState(i1.get(), velia::health::State::ERROR)).IN_SEQUENCE(seq1);
Tomáš Peckaccd80c32020-06-22 14:44:32 +020052 server.changeUnitState("/cz/cesnet/systemd1/unit/unit2", "active", "running");
53
54 // FailedUnits: {} -> OK
Tomáš Pecka261c8862020-11-05 11:23:08 +010055 REQUIRE_CALL(*mx, updateState(i1.get(), velia::health::State::OK)).IN_SEQUENCE(seq1);
Tomáš Peckaccd80c32020-06-22 14:44:32 +020056 server.changeUnitState("/cz/cesnet/systemd1/unit/unit3", "active", "running");
57
Tomáš Pecka8ce88352020-11-04 19:14:13 +010058 // In case we obtain a notifications that unit changed state from (X,Y) to (X,Y), do not trigger any events.
59 server.changeUnitState("/cz/cesnet/systemd1/unit/unit3", "active", "running");
60
Tomáš Peckaccd80c32020-06-22 14:44:32 +020061 // add new unit with failed/failed, DbusSystemdInput should receive UnitNew signal and monitor this unit too
62 // FailedUnits: {unit4} -> OK
Tomáš Pecka261c8862020-11-05 11:23:08 +010063 REQUIRE_CALL(*mx, updateState(i1.get(), velia::health::State::ERROR)).IN_SEQUENCE(seq1);
Tomáš Peckaf2391d62020-11-06 14:02:00 +010064 server.createUnit(*serverConnection, "unit4.service", "/cz/cesnet/systemd1/unit/unit4", "failed", "failed");
65
66 // unitIgnored is ignored by us, so it can change in any way but since we don't obtain the notifications, nothing will happen
67 server.changeUnitState("/cz/cesnet/systemd1/unit/unitIgnored", "failed", "failed");
68 server.changeUnitState("/cz/cesnet/systemd1/unit/unitIgnored", "active", "auto-restarting");
69 server.changeUnitState("/cz/cesnet/systemd1/unit/unitIgnored", "active", "running");
Tomáš Peckaccd80c32020-06-22 14:44:32 +020070
71 waitForCompletionAndBitMore(seq1);
72
73 // FailedUnits: {} -> OK
Tomáš Pecka261c8862020-11-05 11:23:08 +010074 REQUIRE_CALL(*mx, updateState(i1.get(), velia::health::State::OK)).IN_SEQUENCE(seq1);
Tomáš Peckaccd80c32020-06-22 14:44:32 +020075 server.changeUnitState("/cz/cesnet/systemd1/unit/unit4", "active", "running");
76
77 waitForCompletionAndBitMore(seq1);
78
79 REQUIRE_CALL(*mx, unregisterInput(i1.get())).IN_SEQUENCE(seq1);
80 i1.reset();
81}
82
83#if 0
84// Runs a StateManager with DbusSystemdInput connected to the development machine's systemd. Might be useful for debugging.
85TEST_CASE("This machine's systemd monitor")
86{
87 TEST_INIT_LOGS;
88
89 auto clientConnection = sdbus::createSystemBusConnection();
90
Tomáš Pecka261c8862020-11-05 11:23:08 +010091 auto mx = std::make_shared<velia::health::StateManager>();
Tomáš Peckaccd80c32020-06-22 14:44:32 +020092 auto i1 = std::make_shared<velia::DbusSystemdInput>(mx, *clientConnection);
93
94 clientConnection->enterEventLoop();
95}
96#endif