blob: d32d02785721cd3b9a2f5e6c56ea79814e4c7e8c [file] [log] [blame]
Tomáš Pecka3f811962023-04-14 10:54:32 +02001#include "trompeloeil_doctest.h"
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +02002#include <iterator>
3#include <sysrepo-cpp/Enum.hpp>
4#include <trompeloeil.hpp>
Tomáš Pecka3f811962023-04-14 10:54:32 +02005#include "ietf-hardware/IETFHardware.h"
6#include "ietf-hardware/sysrepo/Sysrepo.h"
7#include "mock/ietf_hardware.h"
8#include "pretty_printers.h"
9#include "test_log_setup.h"
10#include "test_sysrepo_helpers.h"
11
12using namespace std::literals;
13
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020014std::string nodeAsString(const libyang::DataNode& node)
15{
16 switch (node.schema().nodeType()) {
17 case libyang::NodeType::Container:
18 return "(container)";
19 case libyang::NodeType::List:
20 return "(list instance)";
21 case libyang::NodeType::Leaf:
Tomáš Pecka2117ce52023-05-12 11:28:34 +020022 case libyang::NodeType::Leaflist:
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020023 return std::string(node.asTerm().valueStr());
24 default:
25 return "(unprintable)";
26 }
27};
28
29struct Deleted { };
30bool operator==(const Deleted&, const Deleted&) { return true; }
31
Jan Kundrát82e41c32024-01-15 13:28:01 +010032using ValueMap = std::map<std::string, std::variant<std::string, Deleted>>;
33
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020034namespace trompeloeil {
35template <>
Jan Kundrát82e41c32024-01-15 13:28:01 +010036struct printer<ValueMap> {
37 static void print(std::ostream& os, const ValueMap& map)
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020038 {
39 os << "{" << std::endl;
40 for (const auto& [key, value] : map) {
41 os << " \"" << key << "\": \""
42 << std::visit([](auto&& arg) -> std::string {
43 using T = std::decay_t<decltype(arg)>;
44 if constexpr (std::is_same_v<T, Deleted>)
45 return "Deleted()";
46 if constexpr (std::is_same_v<T, std::string>)
47 return arg; }, value)
48 << "\"," << std::endl;
49 }
50 os << "}";
51 }
52};
53}
54
55struct DatastoreChange {
Jan Kundrát82e41c32024-01-15 13:28:01 +010056 MAKE_CONST_MOCK1(change, void(const ValueMap&));
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020057};
58
Tomáš Pecka1b3c1732023-05-12 11:45:01 +020059struct AlarmEvent {
60 MAKE_CONST_MOCK1(event, void(const std::map<std::string, std::string>&));
61};
62
Jan Kundrátf85b5cb2024-01-15 13:11:51 +010063#define COMPONENT(RESOURCE) "/ietf-hardware:hardware/component[name='" RESOURCE "']"
64
Tomáš Pecka2117ce52023-05-12 11:28:34 +020065#define REQUIRE_ALARM_INVENTORY_ADD_ALARM(ALARM_TYPE, IETF_HARDWARE_RESOURCE) \
Jan Kundrát82e41c32024-01-15 13:28:01 +010066 REQUIRE_CALL(dsChangeAlarmInventory, change(ValueMap{ \
Tomáš Pecka2117ce52023-05-12 11:28:34 +020067 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='" ALARM_TYPE "'][alarm-type-qualifier='']", "(list instance)"}, \
68 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='" ALARM_TYPE "'][alarm-type-qualifier='']/alarm-type-id", ALARM_TYPE}, \
69 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='" ALARM_TYPE "'][alarm-type-qualifier='']/alarm-type-qualifier", ""}, \
Jan Kundrátf85b5cb2024-01-15 13:11:51 +010070 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='" ALARM_TYPE "'][alarm-type-qualifier='']/resource[1]", COMPONENT(IETF_HARDWARE_RESOURCE)}, \
Tomáš Pecka2117ce52023-05-12 11:28:34 +020071 }))
72
73#define REQUIRE_ALARM_INVENTORY_ADD_RESOURCE(ALARM_TYPE, IETF_HARDWARE_RESOURCE) \
Jan Kundrát82e41c32024-01-15 13:28:01 +010074 REQUIRE_CALL(dsChangeAlarmInventory, change(ValueMap{ \
Jan Kundrátf85b5cb2024-01-15 13:11:51 +010075 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='" ALARM_TYPE "'][alarm-type-qualifier='']/resource[1]", COMPONENT(IETF_HARDWARE_RESOURCE)}, \
Tomáš Pecka2117ce52023-05-12 11:28:34 +020076 }))
77
Tomáš Peckaf206ab12023-05-11 20:42:10 +020078void processDsChanges(sysrepo::Session session, DatastoreChange& dsChange, const std::set<std::string>& ignoredPaths)
79{
Jan Kundrát82e41c32024-01-15 13:28:01 +010080 ValueMap changes;
Tomáš Peckaf206ab12023-05-11 20:42:10 +020081
82 for (const auto& change : session.getChanges()) {
83 if (ignoredPaths.contains(change.node.schema().path())) {
84 continue;
85 }
86
87 if (change.operation == sysrepo::ChangeOperation::Deleted) {
88 changes.emplace(change.node.path(), Deleted());
89 } else {
90 changes.emplace(change.node.path(), nodeAsString(change.node));
91 }
92 }
93
94 dsChange.change(changes);
95}
96
Tomáš Pecka1b3c1732023-05-12 11:45:01 +020097#define REQUIRE_ALARM_RPC(ALARM_TYPE_ID, IETF_HARDWARE_RESOURCE_KEY, SEVERITY, TEXT) \
98 REQUIRE_CALL(alarmEvents, event(std::map<std::string, std::string>{ \
99 {"/sysrepo-ietf-alarms:create-or-update-alarm", "(unprintable)"}, \
100 {"/sysrepo-ietf-alarms:create-or-update-alarm/alarm-text", TEXT}, \
101 {"/sysrepo-ietf-alarms:create-or-update-alarm/alarm-type-id", ALARM_TYPE_ID}, \
102 {"/sysrepo-ietf-alarms:create-or-update-alarm/alarm-type-qualifier", ""}, \
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100103 {"/sysrepo-ietf-alarms:create-or-update-alarm/resource", COMPONENT(IETF_HARDWARE_RESOURCE_KEY)}, \
Tomáš Pecka1b3c1732023-05-12 11:45:01 +0200104 {"/sysrepo-ietf-alarms:create-or-update-alarm/severity", SEVERITY}, \
105 }))
106
Tomáš Pecka3f811962023-04-14 10:54:32 +0200107TEST_CASE("IETF Hardware with sysrepo")
108{
109 TEST_SYSREPO_INIT_LOGS;
110 TEST_SYSREPO_INIT;
111 TEST_SYSREPO_INIT_CLIENT;
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100112
113 srSess.sendRPC(srSess.getContext().newPath("/ietf-factory-default:factory-reset"));
114
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200115 auto alarmsClient = sysrepo::Connection{}.sessionStart(sysrepo::Datastore::Operational);
116
Tomáš Pecka3f811962023-04-14 10:54:32 +0200117 static const auto modulePrefix = "/ietf-hardware:hardware"s;
118
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200119 client.switchDatastore(sysrepo::Datastore::Operational);
120
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200121 DatastoreChange dsChangeHardware;
122 DatastoreChange dsChangeAlarmInventory;
Tomáš Pecka1b3c1732023-05-12 11:45:01 +0200123 AlarmEvent alarmEvents;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200124
Tomáš Pecka3f811962023-04-14 10:54:32 +0200125 trompeloeil::sequence seq1;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200126
Tomáš Pecka1b3c1732023-05-12 11:45:01 +0200127 auto alarmsRPC = alarmsClient.onRPCAction("/sysrepo-ietf-alarms:create-or-update-alarm", [&](auto, auto, auto, const libyang::DataNode input, auto, auto, auto) {
128 std::map<std::string, std::string> inputData;
129
130 for (const auto& node : input.childrenDfs()) {
131 inputData.emplace(node.path(), nodeAsString(node));
132 }
133
134 alarmEvents.event(inputData);
135
136 return sysrepo::ErrorCode::Ok;
137 });
138
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200139 auto directLeafNodeQuery = [&](const std::string& xpath) {
140 auto val = client.getData(xpath);
141 REQUIRE(val);
142 return std::string{val->findPath(xpath)->asTerm().valueStr()};
143 };
Tomáš Pecka3f811962023-04-14 10:54:32 +0200144
Tomáš Pecka3f811962023-04-14 10:54:32 +0200145 auto sysfsTempCpu = std::make_shared<FakeHWMon>();
Tomáš Pecka3f811962023-04-14 10:54:32 +0200146 auto sysfsPower = std::make_shared<FakeHWMon>();
Tomáš Pecka3f811962023-04-14 10:54:32 +0200147
Tomáš Pecka9af47392023-05-23 14:56:48 +0200148 using velia::ietf_hardware::OneThreshold;
149 using velia::ietf_hardware::Thresholds;
Tomáš Pecka3f811962023-04-14 10:54:32 +0200150 using velia::ietf_hardware::data_reader::SensorType;
151 using velia::ietf_hardware::data_reader::StaticData;
152 using velia::ietf_hardware::data_reader::SysfsValue;
Tomáš Peckae5366c62023-04-14 11:03:04 +0200153
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200154 std::atomic<bool> psuActive; // this needs to be destroyed after ietfHardware to avoid dangling reference (we are passing it as a ref to PsuDataReader)
Tomáš Pecka9af47392023-05-23 14:56:48 +0200155 std::atomic<int64_t> psuSensorValue;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200156 std::atomic<int64_t> cpuTempValue;
157 std::atomic<int64_t> powerValue;
158
Tomáš Pecka3f811962023-04-14 10:54:32 +0200159 // register components into hw state
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200160 auto ietfHardware = std::make_shared<velia::ietf_hardware::IETFHardware>();
Tomáš Pecka3f811962023-04-14 10:54:32 +0200161 ietfHardware->registerDataReader(StaticData("ne", std::nullopt, {{"class", "iana-hardware:chassis"}, {"mfg-name", "CESNET"s}}));
Tomáš Peckae5366c62023-04-14 11:03:04 +0200162 ietfHardware->registerDataReader(SysfsValue<SensorType::Temperature>("ne:temperature-cpu", "ne", sysfsTempCpu, 1));
Tomáš Pecka9af47392023-05-23 14:56:48 +0200163 ietfHardware->registerDataReader(SysfsValue<SensorType::Power>("ne:power", "ne", sysfsPower, 1, Thresholds<int64_t>{
164 .criticalLow = OneThreshold<int64_t>{8'000'000, 500'000},
165 .warningLow = OneThreshold<int64_t>{10'000'000, 500'000},
166 .warningHigh = OneThreshold<int64_t>{20'000'000, 500'000},
167 .criticalHigh = OneThreshold<int64_t>{22'000'000, 500'000},
168 }));
Tomáš Pecka3f811962023-04-14 10:54:32 +0200169
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200170 /* Some data readers (like our PSU reader, see the FspYhPsu test) may set oper-state to enabled/disabled depending on whether the device is present and Some
171 * data might not even be pushed (e.g. the child sensors).
172 * Since we push data into sysrepo we have to erase old data (that should no longer be present) from the sysrepo operational DS.
173 * We test such situation via the following data reader which returns data only when psuActive is set to true.
174 */
175 struct PsuDataReader {
176 const std::atomic<bool>& active;
Tomáš Pecka9af47392023-05-23 14:56:48 +0200177 const std::atomic<int64_t>& value;
Tomáš Pecka3f811962023-04-14 10:54:32 +0200178
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100179 velia::ietf_hardware::SensorPollData operator()()
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200180 {
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100181 velia::ietf_hardware::ThresholdsBySensorPath thr;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200182 velia::ietf_hardware::DataTree res = {
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100183 {COMPONENT("ne:psu") "/class", "iana-hardware:power-supply"},
184 {COMPONENT("ne:psu") "/parent", "ne"},
185 {COMPONENT("ne:psu") "/state/oper-state", "disabled"},
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200186 };
Tomáš Pecka3f811962023-04-14 10:54:32 +0200187
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200188 if (active) {
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100189 res[COMPONENT("ne:psu") "/state/oper-state"] = "enabled";
190 res[COMPONENT("ne:psu:child") "/class"] = "iana-hardware:sensor";
191 res[COMPONENT("ne:psu:child") "/parent"] = "ne:psu";
192 res[COMPONENT("ne:psu:child") "/state/oper-state"] = "enabled";
193 res[COMPONENT("ne:psu:child") "/sensor-data/oper-status"] = "ok";
194 res[COMPONENT("ne:psu:child") "/sensor-data/value"] = std::to_string(value);
195 res[COMPONENT("ne:psu:child") "/sensor-data/value-precision"] = "0";
196 res[COMPONENT("ne:psu:child") "/sensor-data/value-scale"] = "milli";
197 res[COMPONENT("ne:psu:child") "/sensor-data/value-type"] = "volts-DC";
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100198
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100199 thr[COMPONENT("ne:psu:child") "/sensor-data/value"] = Thresholds<int64_t>{
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100200 .criticalLow = std::nullopt,
201 .warningLow = OneThreshold<int64_t>{10000, 2000},
202 .warningHigh = OneThreshold<int64_t>{15000, 2000},
203 .criticalHigh = std::nullopt,
204 };
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200205 }
Tomáš Pecka3f811962023-04-14 10:54:32 +0200206
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100207 return {res, thr};
Tomáš Pecka4886db22023-05-10 10:46:15 +0200208 }
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200209 };
Tomáš Pecka9af47392023-05-23 14:56:48 +0200210 ietfHardware->registerDataReader(PsuDataReader{psuActive, psuSensorValue});
Tomáš Pecka3f811962023-04-14 10:54:32 +0200211
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200212 /* Ensure that there are sane data after each sysrepo change callback (all the component subtrees are expected). */
213 auto changeSub = client.onModuleChange(
214 "ietf-hardware",
215 [&](sysrepo::Session session, auto, auto, auto, auto, auto) {
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200216 processDsChanges(session, dsChangeHardware, {"/ietf-hardware:hardware/last-change"});
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200217 return sysrepo::ErrorCode::Ok;
218 },
219 "/ietf-hardware:hardware/component",
220 0,
221 sysrepo::SubscribeOptions::DoneOnly);
Tomáš Peckacb089ac2023-04-21 14:54:26 +0200222
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200223 auto alarmsInvSub = alarmsClient.onModuleChange(
224 "ietf-alarms",
225 [&](sysrepo::Session session, auto, auto, auto, auto, auto) {
226 processDsChanges(session, dsChangeAlarmInventory, {});
227 return sysrepo::ErrorCode::Ok;
228 },
229 "/ietf-alarms:alarms/alarm-inventory",
230 0,
231 sysrepo::SubscribeOptions::DoneOnly);
232
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100233 SECTION("Disappearing sensor plugged from the beginning")
234 {
235 // first batch of values
236 cpuTempValue = 41800;
237 powerValue = 0;
238 psuActive = true;
239 psuSensorValue = 12000;
240 REQUIRE_CALL(*sysfsTempCpu, attribute("temp1_input")).LR_RETURN(cpuTempValue).TIMES(AT_LEAST(1));
241 REQUIRE_CALL(*sysfsPower, attribute("power1_input")).LR_RETURN(powerValue).TIMES(AT_LEAST(1));
Jan Kundrát82e41c32024-01-15 13:28:01 +0100242 REQUIRE_CALL(dsChangeAlarmInventory, change(ValueMap{
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100243 {"/ietf-alarms:alarms", "(container)"},
244 {"/ietf-alarms:alarms/alarm-inventory", "(container)"},
245 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='velia-alarms:sensor-low-value-alarm'][alarm-type-qualifier='']", "(list instance)"},
246 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='velia-alarms:sensor-low-value-alarm'][alarm-type-qualifier='']/alarm-type-id", "velia-alarms:sensor-low-value-alarm"},
247 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='velia-alarms:sensor-low-value-alarm'][alarm-type-qualifier='']/alarm-type-qualifier", ""},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100248 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='velia-alarms:sensor-low-value-alarm'][alarm-type-qualifier='']/resource[1]", COMPONENT("ne:power")},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100249 }))
250 .IN_SEQUENCE(seq1); // the first alarm-inventory change also adds the two container leafs therefore I have not used the REQUIRE_ALARM_INVENTORY_ADD_ALARM macro
251 REQUIRE_ALARM_INVENTORY_ADD_ALARM("velia-alarms:sensor-high-value-alarm", "ne:power").IN_SEQUENCE(seq1);
252 REQUIRE_ALARM_INVENTORY_ADD_ALARM("velia-alarms:sensor-missing-alarm", "ne:power").IN_SEQUENCE(seq1);
253 REQUIRE_ALARM_INVENTORY_ADD_ALARM("velia-alarms:sensor-nonoperational", "ne:power").IN_SEQUENCE(seq1);
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200254
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100255 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-low-value-alarm", "ne:psu:child").IN_SEQUENCE(seq1);
256 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-high-value-alarm", "ne:psu:child").IN_SEQUENCE(seq1);
257 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-missing-alarm", "ne:psu:child").IN_SEQUENCE(seq1);
258 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-nonoperational", "ne:psu:child").IN_SEQUENCE(seq1);
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200259
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100260 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-low-value-alarm", "ne:temperature-cpu").IN_SEQUENCE(seq1);
261 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-high-value-alarm", "ne:temperature-cpu").IN_SEQUENCE(seq1);
262 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-missing-alarm", "ne:temperature-cpu").IN_SEQUENCE(seq1);
263 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-nonoperational", "ne:temperature-cpu").IN_SEQUENCE(seq1);
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200264
Jan Kundrát82e41c32024-01-15 13:28:01 +0100265 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100266 {"/ietf-hardware:hardware", "(container)"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100267 {COMPONENT("ne"), "(list instance)"},
268 {COMPONENT("ne") "/class", "iana-hardware:chassis"},
269 {COMPONENT("ne") "/mfg-name", "CESNET"},
270 {COMPONENT("ne") "/name", "ne"},
271 {COMPONENT("ne") "/state", "(container)"},
272 {COMPONENT("ne") "/state/oper-state", "enabled"},
273 {COMPONENT("ne:power"), "(list instance)"},
274 {COMPONENT("ne:power") "/class", "iana-hardware:sensor"},
275 {COMPONENT("ne:power") "/name", "ne:power"},
276 {COMPONENT("ne:power") "/parent", "ne"},
277 {COMPONENT("ne:power") "/sensor-data", "(container)"},
278 {COMPONENT("ne:power") "/sensor-data/oper-status", "ok"},
279 {COMPONENT("ne:power") "/sensor-data/value", "0"},
280 {COMPONENT("ne:power") "/sensor-data/value-precision", "0"},
281 {COMPONENT("ne:power") "/sensor-data/value-scale", "micro"},
282 {COMPONENT("ne:power") "/sensor-data/value-type", "watts"},
283 {COMPONENT("ne:power") "/state", "(container)"},
284 {COMPONENT("ne:power") "/state/oper-state", "enabled"},
285 {COMPONENT("ne:psu"), "(list instance)"},
286 {COMPONENT("ne:psu") "/class", "iana-hardware:power-supply"},
287 {COMPONENT("ne:psu") "/name", "ne:psu"},
288 {COMPONENT("ne:psu") "/parent", "ne"},
289 {COMPONENT("ne:psu") "/state", "(container)"},
290 {COMPONENT("ne:psu") "/state/oper-state", "enabled"},
291 {COMPONENT("ne:psu:child"), "(list instance)"},
292 {COMPONENT("ne:psu:child") "/class", "iana-hardware:sensor"},
293 {COMPONENT("ne:psu:child") "/name", "ne:psu:child"},
294 {COMPONENT("ne:psu:child") "/parent", "ne:psu"},
295 {COMPONENT("ne:psu:child") "/sensor-data", "(container)"},
296 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", "ok"},
297 {COMPONENT("ne:psu:child") "/sensor-data/value", "12000"},
298 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", "0"},
299 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", "milli"},
300 {COMPONENT("ne:psu:child") "/sensor-data/value-type", "volts-DC"},
301 {COMPONENT("ne:psu:child") "/state", "(container)"},
302 {COMPONENT("ne:psu:child") "/state/oper-state", "enabled"},
303 {COMPONENT("ne:temperature-cpu"), "(list instance)"},
304 {COMPONENT("ne:temperature-cpu") "/class", "iana-hardware:sensor"},
305 {COMPONENT("ne:temperature-cpu") "/name", "ne:temperature-cpu"},
306 {COMPONENT("ne:temperature-cpu") "/parent", "ne"},
307 {COMPONENT("ne:temperature-cpu") "/sensor-data", "(container)"},
308 {COMPONENT("ne:temperature-cpu") "/sensor-data/oper-status", "ok"},
309 {COMPONENT("ne:temperature-cpu") "/sensor-data/value", "41800"},
310 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-precision", "0"},
311 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-scale", "milli"},
312 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-type", "celsius"},
313 {COMPONENT("ne:temperature-cpu") "/state", "(container)"},
314 {COMPONENT("ne:temperature-cpu") "/state/oper-state", "enabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100315 }))
316 .IN_SEQUENCE(seq1);
317 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "critical", "Sensor value crossed low threshold.").IN_SEQUENCE(seq1);
Tomáš Pecka3f811962023-04-14 10:54:32 +0200318
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100319 auto ietfHardwareSysrepo = std::make_shared<velia::ietf_hardware::sysrepo::Sysrepo>(srSess, ietfHardware, 150ms);
320 std::this_thread::sleep_for(400ms); // let's wait until the bg polling thread is spawned; 400 ms is probably enough to spawn the thread and poll 2 or 3 times
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200321
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100322 std::string lastChange = directLeafNodeQuery(modulePrefix + "/last-change");
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200323
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100324 // second batch of values, sensor data changed, PSU ejected
Jan Kundrát82e41c32024-01-15 13:28:01 +0100325 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100326 {COMPONENT("ne:psu:child") "/class", Deleted{}},
327 {COMPONENT("ne:psu:child") "/parent", Deleted{}},
328 {COMPONENT("ne:psu:child") "/sensor-data", Deleted{}},
329 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", Deleted{}},
330 {COMPONENT("ne:psu:child") "/sensor-data/value", Deleted{}},
331 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", Deleted{}},
332 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", Deleted{}},
333 {COMPONENT("ne:psu:child") "/sensor-data/value-type", Deleted{}},
334 {COMPONENT("ne:psu:child") "/state", Deleted{}},
335 {COMPONENT("ne:psu:child") "/state/oper-state", Deleted{}},
336 {COMPONENT("ne:power") "/sensor-data/value", "11222333"},
337 {COMPONENT("ne:psu") "/state/oper-state", "disabled"},
338 {COMPONENT("ne:temperature-cpu") "/sensor-data/value", "222"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100339 }))
340 .IN_SEQUENCE(seq1);
341 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "cleared", "Sensor value crossed low threshold.").IN_SEQUENCE(seq1);
342 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu:child", "warning", "Sensor value not reported. Maybe the sensor was unplugged?").IN_SEQUENCE(seq1);
343 REQUIRE_CALL(*sysfsTempCpu, attribute("temp1_input")).LR_RETURN(cpuTempValue).TIMES(AT_LEAST(1));
344 REQUIRE_CALL(*sysfsPower, attribute("power1_input")).LR_RETURN(powerValue).TIMES(AT_LEAST(1));
345 cpuTempValue = 222;
346 powerValue = 11222333;
347 psuActive = false;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200348
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100349 std::this_thread::sleep_for(2000ms); // longer sleep here: last-change does not report milliseconds so this should increase last-change timestamp at least by one second
350 REQUIRE(directLeafNodeQuery(modulePrefix + "/last-change") > lastChange); // check that last-change leaf has timestamp that is greater than the previous one
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200351
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100352 // third batch of changes, wild PSU appears with a warning
Jan Kundrát82e41c32024-01-15 13:28:01 +0100353 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100354 {COMPONENT("ne:psu") "/state/oper-state", "enabled"},
355 {COMPONENT("ne:psu:child") "/class", "iana-hardware:sensor"},
356 {COMPONENT("ne:psu:child") "/parent", "ne:psu"},
357 {COMPONENT("ne:psu:child") "/sensor-data", "(container)"},
358 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", "ok"},
359 {COMPONENT("ne:psu:child") "/sensor-data/value", "50000"},
360 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", "0"},
361 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", "milli"},
362 {COMPONENT("ne:psu:child") "/sensor-data/value-type", "volts-DC"},
363 {COMPONENT("ne:psu:child") "/state", "(container)"},
364 {COMPONENT("ne:psu:child") "/state/oper-state", "enabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100365 }))
366 .IN_SEQUENCE(seq1);
367 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu:child", "cleared", "Sensor value not reported. Maybe the sensor was unplugged?").IN_SEQUENCE(seq1);
368 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:psu:child", "warning", "Sensor value crossed high threshold.").IN_SEQUENCE(seq1);
369 psuSensorValue = 50000;
370 psuActive = true;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200371
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100372 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200373
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100374 // fourth round. We unplug with a warning
Jan Kundrát82e41c32024-01-15 13:28:01 +0100375 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100376 {COMPONENT("ne:psu:child") "/class", Deleted{}},
377 {COMPONENT("ne:psu:child") "/parent", Deleted{}},
378 {COMPONENT("ne:psu:child") "/sensor-data", Deleted{}},
379 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", Deleted{}},
380 {COMPONENT("ne:psu:child") "/sensor-data/value", Deleted{}},
381 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", Deleted{}},
382 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", Deleted{}},
383 {COMPONENT("ne:psu:child") "/sensor-data/value-type", Deleted{}},
384 {COMPONENT("ne:psu:child") "/state", Deleted{}},
385 {COMPONENT("ne:psu:child") "/state/oper-state", Deleted{}},
386 {COMPONENT("ne:psu") "/state/oper-state", "disabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100387 }))
388 .IN_SEQUENCE(seq1);
389 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu:child", "warning", "Sensor value not reported. Maybe the sensor was unplugged?").IN_SEQUENCE(seq1);
390 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:psu:child", "cleared", "Sensor value crossed high threshold.").IN_SEQUENCE(seq1);
391 psuActive = false;
392 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200393
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100394 // 5+th round: test threshold crossings
Jan Kundrát82e41c32024-01-15 13:28:01 +0100395 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100396 {COMPONENT("ne:power") "/sensor-data/value", "21000000"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100397 }))
398 .IN_SEQUENCE(seq1);
399 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "warning", "Sensor value crossed high threshold.").IN_SEQUENCE(seq1);
400 powerValue = 21'000'000;
401 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200402
Jan Kundrát82e41c32024-01-15 13:28:01 +0100403 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100404 {COMPONENT("ne:power") "/sensor-data/value", "24000000"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100405 }))
406 .IN_SEQUENCE(seq1);
407 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "critical", "Sensor value crossed high threshold.").IN_SEQUENCE(seq1);
408 powerValue = 24'000'000;
409 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200410
Jan Kundrát82e41c32024-01-15 13:28:01 +0100411 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100412 {COMPONENT("ne:power") "/sensor-data/value", "1"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100413 }))
414 .IN_SEQUENCE(seq1);
415 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "critical", "Sensor value crossed low threshold.").IN_SEQUENCE(seq1);
416 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "cleared", "Sensor value crossed high threshold.").IN_SEQUENCE(seq1);
417 powerValue = 1;
418 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200419
Jan Kundrát82e41c32024-01-15 13:28:01 +0100420 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100421 {COMPONENT("ne:power") "/sensor-data/value", "14000000"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100422 }))
423 .IN_SEQUENCE(seq1);
424 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "cleared", "Sensor value crossed low threshold.").IN_SEQUENCE(seq1);
425 powerValue = 14'000'000;
426 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200427
Tomáš Pecka5a4c0352023-12-12 12:29:28 +0100428
Jan Kundrát82e41c32024-01-15 13:28:01 +0100429 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100430 {COMPONENT("ne:power") "/sensor-data/value", "1000000000"},
431 {COMPONENT("ne:power") "/sensor-data/oper-status", "nonoperational"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100432 }))
433 .IN_SEQUENCE(seq1);
434 REQUIRE_ALARM_RPC("velia-alarms:sensor-nonoperational", "ne:power", "warning", "Sensor is nonoperational. The values it reports may not be relevant.").IN_SEQUENCE(seq1);
435 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "critical", "Sensor value crossed high threshold.").IN_SEQUENCE(seq1);
436 powerValue = 2'999'999'999;
437 waitForCompletionAndBitMore(seq1);
Tomáš Pecka5a4c0352023-12-12 12:29:28 +0100438
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100439 powerValue = 1'999'999'999;
440 waitForCompletionAndBitMore(seq1);
Tomáš Pecka5a4c0352023-12-12 12:29:28 +0100441
Jan Kundrát82e41c32024-01-15 13:28:01 +0100442 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100443 {COMPONENT("ne:power") "/sensor-data/value", "-1000000000"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100444 }))
445 .IN_SEQUENCE(seq1);
446 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "critical", "Sensor value crossed low threshold.").IN_SEQUENCE(seq1);
447 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "cleared", "Sensor value crossed high threshold.").IN_SEQUENCE(seq1);
448 powerValue = -2'999'999'999;
449 waitForCompletionAndBitMore(seq1);
Tomáš Pecka5a4c0352023-12-12 12:29:28 +0100450
Jan Kundrát82e41c32024-01-15 13:28:01 +0100451 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100452 {COMPONENT("ne:power") "/sensor-data/value", "-999999999"},
453 {COMPONENT("ne:power") "/sensor-data/oper-status", "ok"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100454 }))
455 .IN_SEQUENCE(seq1);
456 REQUIRE_ALARM_RPC("velia-alarms:sensor-nonoperational", "ne:power", "cleared", "Sensor is nonoperational. The values it reports may not be relevant.").IN_SEQUENCE(seq1);
457 powerValue = -999'999'999;
458 waitForCompletionAndBitMore(seq1);
459 }
460
461 SECTION("Disappearing sensor unplugged in the beginning")
462 {
463 cpuTempValue = 41800;
464 powerValue = 0;
465 psuActive = false;
466 psuSensorValue = 12000;
467 REQUIRE_CALL(*sysfsTempCpu, attribute("temp1_input")).LR_RETURN(cpuTempValue).TIMES(AT_LEAST(1));
468 REQUIRE_CALL(*sysfsPower, attribute("power1_input")).LR_RETURN(powerValue).TIMES(AT_LEAST(1));
Jan Kundrát82e41c32024-01-15 13:28:01 +0100469 REQUIRE_CALL(dsChangeAlarmInventory, change(ValueMap{
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100470 {"/ietf-alarms:alarms", "(container)"},
471 {"/ietf-alarms:alarms/alarm-inventory", "(container)"},
472 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='velia-alarms:sensor-low-value-alarm'][alarm-type-qualifier='']", "(list instance)"},
473 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='velia-alarms:sensor-low-value-alarm'][alarm-type-qualifier='']/alarm-type-id", "velia-alarms:sensor-low-value-alarm"},
474 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='velia-alarms:sensor-low-value-alarm'][alarm-type-qualifier='']/alarm-type-qualifier", ""},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100475 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='velia-alarms:sensor-low-value-alarm'][alarm-type-qualifier='']/resource[1]", COMPONENT("ne:power")},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100476 }))
477 .IN_SEQUENCE(seq1); // the first alarm-inventory change also adds the two container leafs therefore I have not used the REQUIRE_ALARM_INVENTORY_ADD_ALARM macro
478 REQUIRE_ALARM_INVENTORY_ADD_ALARM("velia-alarms:sensor-high-value-alarm", "ne:power").IN_SEQUENCE(seq1);
479 REQUIRE_ALARM_INVENTORY_ADD_ALARM("velia-alarms:sensor-missing-alarm", "ne:power").IN_SEQUENCE(seq1);
480 REQUIRE_ALARM_INVENTORY_ADD_ALARM("velia-alarms:sensor-nonoperational", "ne:power").IN_SEQUENCE(seq1);
481
482 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-low-value-alarm", "ne:temperature-cpu").IN_SEQUENCE(seq1);
483 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-high-value-alarm", "ne:temperature-cpu").IN_SEQUENCE(seq1);
484 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-missing-alarm", "ne:temperature-cpu").IN_SEQUENCE(seq1);
485 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-nonoperational", "ne:temperature-cpu").IN_SEQUENCE(seq1);
486
Jan Kundrát82e41c32024-01-15 13:28:01 +0100487 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100488 {"/ietf-hardware:hardware", "(container)"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100489 {COMPONENT("ne"), "(list instance)"},
490 {COMPONENT("ne") "/class", "iana-hardware:chassis"},
491 {COMPONENT("ne") "/mfg-name", "CESNET"},
492 {COMPONENT("ne") "/name", "ne"},
493 {COMPONENT("ne") "/state", "(container)"},
494 {COMPONENT("ne") "/state/oper-state", "enabled"},
495 {COMPONENT("ne:power"), "(list instance)"},
496 {COMPONENT("ne:power") "/class", "iana-hardware:sensor"},
497 {COMPONENT("ne:power") "/name", "ne:power"},
498 {COMPONENT("ne:power") "/parent", "ne"},
499 {COMPONENT("ne:power") "/sensor-data", "(container)"},
500 {COMPONENT("ne:power") "/sensor-data/oper-status", "ok"},
501 {COMPONENT("ne:power") "/sensor-data/value", "0"},
502 {COMPONENT("ne:power") "/sensor-data/value-precision", "0"},
503 {COMPONENT("ne:power") "/sensor-data/value-scale", "micro"},
504 {COMPONENT("ne:power") "/sensor-data/value-type", "watts"},
505 {COMPONENT("ne:power") "/state", "(container)"},
506 {COMPONENT("ne:power") "/state/oper-state", "enabled"},
507 {COMPONENT("ne:psu"), "(list instance)"},
508 {COMPONENT("ne:psu") "/class", "iana-hardware:power-supply"},
509 {COMPONENT("ne:psu") "/name", "ne:psu"},
510 {COMPONENT("ne:psu") "/parent", "ne"},
511 {COMPONENT("ne:psu") "/state", "(container)"},
512 {COMPONENT("ne:psu") "/state/oper-state", "disabled"},
513 {COMPONENT("ne:temperature-cpu"), "(list instance)"},
514 {COMPONENT("ne:temperature-cpu") "/class", "iana-hardware:sensor"},
515 {COMPONENT("ne:temperature-cpu") "/name", "ne:temperature-cpu"},
516 {COMPONENT("ne:temperature-cpu") "/parent", "ne"},
517 {COMPONENT("ne:temperature-cpu") "/sensor-data", "(container)"},
518 {COMPONENT("ne:temperature-cpu") "/sensor-data/oper-status", "ok"},
519 {COMPONENT("ne:temperature-cpu") "/sensor-data/value", "41800"},
520 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-precision", "0"},
521 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-scale", "milli"},
522 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-type", "celsius"},
523 {COMPONENT("ne:temperature-cpu") "/state", "(container)"},
524 {COMPONENT("ne:temperature-cpu") "/state/oper-state", "enabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100525 }))
526 .IN_SEQUENCE(seq1);
527 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "critical", "Sensor value crossed low threshold.").IN_SEQUENCE(seq1);
528
529 auto ietfHardwareSysrepo = std::make_shared<velia::ietf_hardware::sysrepo::Sysrepo>(srSess, ietfHardware, 150ms);
530 std::this_thread::sleep_for(400ms); // let's wait until the bg polling thread is spawned; 400 ms is probably enough to spawn the thread and poll 2 or 3 times
531 waitForCompletionAndBitMore(seq1);
532
533 std::string lastChange = directLeafNodeQuery(modulePrefix + "/last-change");
534
535 // PSU inserted
536 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-low-value-alarm", "ne:psu:child").IN_SEQUENCE(seq1);
537 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-high-value-alarm", "ne:psu:child").IN_SEQUENCE(seq1);
538 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-missing-alarm", "ne:psu:child").IN_SEQUENCE(seq1);
539 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-nonoperational", "ne:psu:child").IN_SEQUENCE(seq1);
Jan Kundrát82e41c32024-01-15 13:28:01 +0100540 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100541 {COMPONENT("ne:psu") "/state/oper-state", "enabled"},
542 {COMPONENT("ne:psu:child"), "(list instance)"},
543 {COMPONENT("ne:psu:child") "/class", "iana-hardware:sensor"},
544 {COMPONENT("ne:psu:child") "/name", "ne:psu:child"},
545 {COMPONENT("ne:psu:child") "/parent", "ne:psu"},
546 {COMPONENT("ne:psu:child") "/sensor-data", "(container)"},
547 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", "ok"},
548 {COMPONENT("ne:psu:child") "/sensor-data/value", "12000"},
549 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", "0"},
550 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", "milli"},
551 {COMPONENT("ne:psu:child") "/sensor-data/value-type", "volts-DC"},
552 {COMPONENT("ne:psu:child") "/state", "(container)"},
553 {COMPONENT("ne:psu:child") "/state/oper-state", "enabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100554 }))
555 .IN_SEQUENCE(seq1);
556 psuActive = true;
557 waitForCompletionAndBitMore(seq1);
558
559 std::this_thread::sleep_for(1000ms); // last-change leaf resolution is in seconds, let's wait until the second increments
560 REQUIRE(directLeafNodeQuery(modulePrefix + "/last-change") > lastChange); // check that last-change leaf has timestamp that is greater than the previous one
561 }
Tomáš Pecka3f811962023-04-14 10:54:32 +0200562}