blob: bf35e79a429669bb3532f2abee24511d6fbe90b9 [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
Tomáš Peckaf206ab12023-05-11 20:42:10 +020063void processDsChanges(sysrepo::Session session, DatastoreChange& dsChange, const std::set<std::string>& ignoredPaths)
64{
Jan Kundrát82e41c32024-01-15 13:28:01 +010065 ValueMap changes;
Tomáš Peckaf206ab12023-05-11 20:42:10 +020066
67 for (const auto& change : session.getChanges()) {
68 if (ignoredPaths.contains(change.node.schema().path())) {
69 continue;
70 }
71
Jan Kundrátcdc899b2024-01-15 13:39:28 +010072 if (change.node.schema().nodeType() == libyang::NodeType::List) {
73 // any list will surely have some "nodes below", so let's not waste time printing the list entry itself
74 continue;
75 }
76 if (change.node.schema().nodeType() == libyang::NodeType::Container && !change.node.schema().asContainer().isPresence()) {
77 // non-presence containers are "always there", let's not clutter up the output
78 continue;
79 }
80
Tomáš Peckaf206ab12023-05-11 20:42:10 +020081 if (change.operation == sysrepo::ChangeOperation::Deleted) {
82 changes.emplace(change.node.path(), Deleted());
83 } else {
84 changes.emplace(change.node.path(), nodeAsString(change.node));
85 }
86 }
87
88 dsChange.change(changes);
89}
90
Tomáš Peckab0923ec2024-01-18 12:58:06 +010091struct AlarmInventory {
92 std::map<std::pair<std::string, std::string>, std::vector<std::string>> inventory;
93
94 void add(const std::string& alarmTypeId, const std::string& alarmTypeQualifier, const std::string& resource)
95 {
96 inventory[{alarmTypeId, alarmTypeQualifier}].push_back(resource);
97 }
98 bool contains(const std::string& alarmTypeId, const std::string& alarmTypeQualifier, const std::string& resource) const
99 {
100 if (auto it = inventory.find({alarmTypeId, alarmTypeQualifier}); it != inventory.end()) {
101 return std::find(it->second.begin(), it->second.end(), resource) != it->second.end();
102 }
103 return false;
104 }
105};
Tomáš Pecka2fdc8ce2024-01-18 12:28:37 +0100106
107#define COMPONENT(RESOURCE) "/ietf-hardware:hardware/component[name='" RESOURCE "']"
108
109#define REQUIRE_ALARM_INVENTORY_ADD_ALARM(ALARM_TYPE, IETF_HARDWARE_RESOURCE) \
110 REQUIRE_CALL(dsChangeAlarmInventory, change(ValueMap{ \
111 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='" ALARM_TYPE "'][alarm-type-qualifier='']/alarm-type-id", ALARM_TYPE}, \
112 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='" ALARM_TYPE "'][alarm-type-qualifier='']/alarm-type-qualifier", ""}, \
113 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='" ALARM_TYPE "'][alarm-type-qualifier='']/resource[1]", COMPONENT(IETF_HARDWARE_RESOURCE)}, \
Tomáš Peckab0923ec2024-01-18 12:58:06 +0100114 })).LR_SIDE_EFFECT(alarmInventory.add(ALARM_TYPE, "", COMPONENT(IETF_HARDWARE_RESOURCE)))
Tomáš Pecka2fdc8ce2024-01-18 12:28:37 +0100115
116#define REQUIRE_ALARM_INVENTORY_ADD_RESOURCE(ALARM_TYPE, IETF_HARDWARE_RESOURCE) \
117 REQUIRE_CALL(dsChangeAlarmInventory, change(ValueMap{ \
118 {"/ietf-alarms:alarms/alarm-inventory/alarm-type[alarm-type-id='" ALARM_TYPE "'][alarm-type-qualifier='']/resource[1]", COMPONENT(IETF_HARDWARE_RESOURCE)}, \
Tomáš Peckab0923ec2024-01-18 12:58:06 +0100119 })) \
120 .LR_SIDE_EFFECT(alarmInventory.add(ALARM_TYPE, "", COMPONENT(IETF_HARDWARE_RESOURCE)))
Tomáš Pecka2fdc8ce2024-01-18 12:28:37 +0100121
Tomáš Pecka22951ef2024-01-16 14:58:35 +0100122#define REQUIRE_ALARM_RPC(ALARM_TYPE_ID, IETF_HARDWARE_RESOURCE_KEY, SEVERITY, TEXT) \
123 REQUIRE_CALL(alarmEvents, event(std::map<std::string, std::string>{ \
124 {"/sysrepo-ietf-alarms:create-or-update-alarm", "(unprintable)"}, \
125 {"/sysrepo-ietf-alarms:create-or-update-alarm/alarm-text", TEXT}, \
126 {"/sysrepo-ietf-alarms:create-or-update-alarm/alarm-type-id", ALARM_TYPE_ID}, \
127 {"/sysrepo-ietf-alarms:create-or-update-alarm/alarm-type-qualifier", ""}, \
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100128 {"/sysrepo-ietf-alarms:create-or-update-alarm/resource", COMPONENT(IETF_HARDWARE_RESOURCE_KEY)}, \
Tomáš Pecka22951ef2024-01-16 14:58:35 +0100129 {"/sysrepo-ietf-alarms:create-or-update-alarm/severity", SEVERITY}, \
Tomáš Peckab0923ec2024-01-18 12:58:06 +0100130 })) \
131 .LR_WITH(alarmInventory.contains(ALARM_TYPE_ID, "", COMPONENT(IETF_HARDWARE_RESOURCE_KEY)))
Tomáš Pecka1b3c1732023-05-12 11:45:01 +0200132
Tomáš Pecka3f811962023-04-14 10:54:32 +0200133TEST_CASE("IETF Hardware with sysrepo")
134{
135 TEST_SYSREPO_INIT_LOGS;
136 TEST_SYSREPO_INIT;
137 TEST_SYSREPO_INIT_CLIENT;
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100138
139 srSess.sendRPC(srSess.getContext().newPath("/ietf-factory-default:factory-reset"));
140
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200141 auto alarmsClient = sysrepo::Connection{}.sessionStart(sysrepo::Datastore::Operational);
142
Tomáš Pecka3f811962023-04-14 10:54:32 +0200143 static const auto modulePrefix = "/ietf-hardware:hardware"s;
144
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200145 client.switchDatastore(sysrepo::Datastore::Operational);
146
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200147 DatastoreChange dsChangeHardware;
148 DatastoreChange dsChangeAlarmInventory;
Tomáš Pecka1b3c1732023-05-12 11:45:01 +0200149 AlarmEvent alarmEvents;
Tomáš Peckab0923ec2024-01-18 12:58:06 +0100150 AlarmInventory alarmInventory;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200151
Tomáš Pecka3f811962023-04-14 10:54:32 +0200152 trompeloeil::sequence seq1;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200153
Tomáš Pecka1b3c1732023-05-12 11:45:01 +0200154 auto alarmsRPC = alarmsClient.onRPCAction("/sysrepo-ietf-alarms:create-or-update-alarm", [&](auto, auto, auto, const libyang::DataNode input, auto, auto, auto) {
155 std::map<std::string, std::string> inputData;
156
157 for (const auto& node : input.childrenDfs()) {
158 inputData.emplace(node.path(), nodeAsString(node));
159 }
160
161 alarmEvents.event(inputData);
162
163 return sysrepo::ErrorCode::Ok;
164 });
165
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200166 auto directLeafNodeQuery = [&](const std::string& xpath) {
167 auto val = client.getData(xpath);
168 REQUIRE(val);
169 return std::string{val->findPath(xpath)->asTerm().valueStr()};
170 };
Tomáš Pecka3f811962023-04-14 10:54:32 +0200171
Tomáš Pecka3f811962023-04-14 10:54:32 +0200172 auto sysfsTempCpu = std::make_shared<FakeHWMon>();
Tomáš Pecka3f811962023-04-14 10:54:32 +0200173 auto sysfsPower = std::make_shared<FakeHWMon>();
Tomáš Pecka3f811962023-04-14 10:54:32 +0200174
Tomáš Pecka9af47392023-05-23 14:56:48 +0200175 using velia::ietf_hardware::OneThreshold;
176 using velia::ietf_hardware::Thresholds;
Tomáš Pecka3f811962023-04-14 10:54:32 +0200177 using velia::ietf_hardware::data_reader::SensorType;
178 using velia::ietf_hardware::data_reader::StaticData;
179 using velia::ietf_hardware::data_reader::SysfsValue;
Tomáš Peckae5366c62023-04-14 11:03:04 +0200180
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200181 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 +0200182 std::atomic<int64_t> psuSensorValue;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200183 std::atomic<int64_t> cpuTempValue;
184 std::atomic<int64_t> powerValue;
185
Tomáš Pecka3f811962023-04-14 10:54:32 +0200186 // register components into hw state
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200187 auto ietfHardware = std::make_shared<velia::ietf_hardware::IETFHardware>();
Tomáš Pecka3f811962023-04-14 10:54:32 +0200188 ietfHardware->registerDataReader(StaticData("ne", std::nullopt, {{"class", "iana-hardware:chassis"}, {"mfg-name", "CESNET"s}}));
Tomáš Peckae5366c62023-04-14 11:03:04 +0200189 ietfHardware->registerDataReader(SysfsValue<SensorType::Temperature>("ne:temperature-cpu", "ne", sysfsTempCpu, 1));
Tomáš Pecka9af47392023-05-23 14:56:48 +0200190 ietfHardware->registerDataReader(SysfsValue<SensorType::Power>("ne:power", "ne", sysfsPower, 1, Thresholds<int64_t>{
191 .criticalLow = OneThreshold<int64_t>{8'000'000, 500'000},
192 .warningLow = OneThreshold<int64_t>{10'000'000, 500'000},
193 .warningHigh = OneThreshold<int64_t>{20'000'000, 500'000},
194 .criticalHigh = OneThreshold<int64_t>{22'000'000, 500'000},
195 }));
Tomáš Pecka3f811962023-04-14 10:54:32 +0200196
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200197 /* 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
198 * data might not even be pushed (e.g. the child sensors).
199 * Since we push data into sysrepo we have to erase old data (that should no longer be present) from the sysrepo operational DS.
200 * We test such situation via the following data reader which returns data only when psuActive is set to true.
201 */
202 struct PsuDataReader {
203 const std::atomic<bool>& active;
Tomáš Pecka9af47392023-05-23 14:56:48 +0200204 const std::atomic<int64_t>& value;
Tomáš Pecka3f811962023-04-14 10:54:32 +0200205
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100206 velia::ietf_hardware::SensorPollData operator()()
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200207 {
Tomáš Pecka26b38212024-01-16 17:23:31 +0100208 velia::ietf_hardware::SideLoadedAlarm alarm;
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100209 velia::ietf_hardware::ThresholdsBySensorPath thr;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200210 velia::ietf_hardware::DataTree res = {
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100211 {COMPONENT("ne:psu") "/class", "iana-hardware:power-supply"},
212 {COMPONENT("ne:psu") "/parent", "ne"},
213 {COMPONENT("ne:psu") "/state/oper-state", "disabled"},
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200214 };
Tomáš Pecka3f811962023-04-14 10:54:32 +0200215
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200216 if (active) {
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100217 res[COMPONENT("ne:psu") "/state/oper-state"] = "enabled";
218 res[COMPONENT("ne:psu:child") "/class"] = "iana-hardware:sensor";
219 res[COMPONENT("ne:psu:child") "/parent"] = "ne:psu";
220 res[COMPONENT("ne:psu:child") "/state/oper-state"] = "enabled";
221 res[COMPONENT("ne:psu:child") "/sensor-data/oper-status"] = "ok";
222 res[COMPONENT("ne:psu:child") "/sensor-data/value"] = std::to_string(value);
223 res[COMPONENT("ne:psu:child") "/sensor-data/value-precision"] = "0";
224 res[COMPONENT("ne:psu:child") "/sensor-data/value-scale"] = "milli";
225 res[COMPONENT("ne:psu:child") "/sensor-data/value-type"] = "volts-DC";
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100226
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100227 thr[COMPONENT("ne:psu:child") "/sensor-data/value"] = Thresholds<int64_t>{
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100228 .criticalLow = std::nullopt,
229 .warningLow = OneThreshold<int64_t>{10000, 2000},
230 .warningHigh = OneThreshold<int64_t>{15000, 2000},
231 .criticalHigh = std::nullopt,
232 };
Tomáš Pecka26b38212024-01-16 17:23:31 +0100233
234 alarm = {"velia-alarms:sensor-missing-alarm", COMPONENT("ne:psu"), "cleared", "PSU missing."};
235 } else {
236 alarm = {"velia-alarms:sensor-missing-alarm", COMPONENT("ne:psu"), "critical", "PSU missing."};
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200237 }
Tomáš Pecka3f811962023-04-14 10:54:32 +0200238
Tomáš Pecka26b38212024-01-16 17:23:31 +0100239 return {res, thr, {alarm}};
Tomáš Pecka4886db22023-05-10 10:46:15 +0200240 }
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200241 };
Tomáš Pecka9af47392023-05-23 14:56:48 +0200242 ietfHardware->registerDataReader(PsuDataReader{psuActive, psuSensorValue});
Tomáš Pecka3f811962023-04-14 10:54:32 +0200243
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200244 /* Ensure that there are sane data after each sysrepo change callback (all the component subtrees are expected). */
245 auto changeSub = client.onModuleChange(
246 "ietf-hardware",
247 [&](sysrepo::Session session, auto, auto, auto, auto, auto) {
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200248 processDsChanges(session, dsChangeHardware, {"/ietf-hardware:hardware/last-change"});
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200249 return sysrepo::ErrorCode::Ok;
250 },
251 "/ietf-hardware:hardware/component",
252 0,
253 sysrepo::SubscribeOptions::DoneOnly);
Tomáš Peckacb089ac2023-04-21 14:54:26 +0200254
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200255 auto alarmsInvSub = alarmsClient.onModuleChange(
256 "ietf-alarms",
257 [&](sysrepo::Session session, auto, auto, auto, auto, auto) {
258 processDsChanges(session, dsChangeAlarmInventory, {});
259 return sysrepo::ErrorCode::Ok;
260 },
261 "/ietf-alarms:alarms/alarm-inventory",
262 0,
263 sysrepo::SubscribeOptions::DoneOnly);
264
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100265 SECTION("Disappearing sensor plugged from the beginning")
266 {
267 // first batch of values
268 cpuTempValue = 41800;
269 powerValue = 0;
270 psuActive = true;
271 psuSensorValue = 12000;
272 REQUIRE_CALL(*sysfsTempCpu, attribute("temp1_input")).LR_RETURN(cpuTempValue).TIMES(AT_LEAST(1));
273 REQUIRE_CALL(*sysfsPower, attribute("power1_input")).LR_RETURN(powerValue).TIMES(AT_LEAST(1));
Jan Kundrátcdc899b2024-01-15 13:39:28 +0100274 REQUIRE_ALARM_INVENTORY_ADD_ALARM("velia-alarms:sensor-low-value-alarm", "ne:power").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100275 REQUIRE_ALARM_INVENTORY_ADD_ALARM("velia-alarms:sensor-high-value-alarm", "ne:power").IN_SEQUENCE(seq1);
276 REQUIRE_ALARM_INVENTORY_ADD_ALARM("velia-alarms:sensor-missing-alarm", "ne:power").IN_SEQUENCE(seq1);
277 REQUIRE_ALARM_INVENTORY_ADD_ALARM("velia-alarms:sensor-nonoperational", "ne:power").IN_SEQUENCE(seq1);
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200278
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100279 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-low-value-alarm", "ne:psu:child").IN_SEQUENCE(seq1);
280 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-high-value-alarm", "ne:psu:child").IN_SEQUENCE(seq1);
281 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-missing-alarm", "ne:psu:child").IN_SEQUENCE(seq1);
282 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-nonoperational", "ne:psu:child").IN_SEQUENCE(seq1);
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200283
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100284 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-low-value-alarm", "ne:temperature-cpu").IN_SEQUENCE(seq1);
285 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-high-value-alarm", "ne:temperature-cpu").IN_SEQUENCE(seq1);
286 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-missing-alarm", "ne:temperature-cpu").IN_SEQUENCE(seq1);
287 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-nonoperational", "ne:temperature-cpu").IN_SEQUENCE(seq1);
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200288
Jan Kundrát82e41c32024-01-15 13:28:01 +0100289 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100290 {COMPONENT("ne") "/class", "iana-hardware:chassis"},
291 {COMPONENT("ne") "/mfg-name", "CESNET"},
292 {COMPONENT("ne") "/name", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100293 {COMPONENT("ne") "/state/oper-state", "enabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100294 {COMPONENT("ne:power") "/class", "iana-hardware:sensor"},
295 {COMPONENT("ne:power") "/name", "ne:power"},
296 {COMPONENT("ne:power") "/parent", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100297 {COMPONENT("ne:power") "/sensor-data/oper-status", "ok"},
298 {COMPONENT("ne:power") "/sensor-data/value", "0"},
299 {COMPONENT("ne:power") "/sensor-data/value-precision", "0"},
300 {COMPONENT("ne:power") "/sensor-data/value-scale", "micro"},
301 {COMPONENT("ne:power") "/sensor-data/value-type", "watts"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100302 {COMPONENT("ne:power") "/state/oper-state", "enabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100303 {COMPONENT("ne:psu") "/class", "iana-hardware:power-supply"},
304 {COMPONENT("ne:psu") "/name", "ne:psu"},
305 {COMPONENT("ne:psu") "/parent", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100306 {COMPONENT("ne:psu") "/state/oper-state", "enabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100307 {COMPONENT("ne:psu:child") "/class", "iana-hardware:sensor"},
308 {COMPONENT("ne:psu:child") "/name", "ne:psu:child"},
309 {COMPONENT("ne:psu:child") "/parent", "ne:psu"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100310 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", "ok"},
311 {COMPONENT("ne:psu:child") "/sensor-data/value", "12000"},
312 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", "0"},
313 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", "milli"},
314 {COMPONENT("ne:psu:child") "/sensor-data/value-type", "volts-DC"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100315 {COMPONENT("ne:psu:child") "/state/oper-state", "enabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100316 {COMPONENT("ne:temperature-cpu") "/class", "iana-hardware:sensor"},
317 {COMPONENT("ne:temperature-cpu") "/name", "ne:temperature-cpu"},
318 {COMPONENT("ne:temperature-cpu") "/parent", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100319 {COMPONENT("ne:temperature-cpu") "/sensor-data/oper-status", "ok"},
320 {COMPONENT("ne:temperature-cpu") "/sensor-data/value", "41800"},
321 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-precision", "0"},
322 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-scale", "milli"},
323 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-type", "celsius"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100324 {COMPONENT("ne:temperature-cpu") "/state/oper-state", "enabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100325 }))
326 .IN_SEQUENCE(seq1);
Tomáš Pecka26b38212024-01-16 17:23:31 +0100327 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-missing-alarm", "ne:psu").TIMES(AT_LEAST(1));
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100328 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 +0200329
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100330 auto ietfHardwareSysrepo = std::make_shared<velia::ietf_hardware::sysrepo::Sysrepo>(srSess, ietfHardware, 150ms);
331 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 +0200332
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100333 std::string lastChange = directLeafNodeQuery(modulePrefix + "/last-change");
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200334
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100335 // second batch of values, sensor data changed, PSU ejected
Jan Kundrát82e41c32024-01-15 13:28:01 +0100336 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100337 {COMPONENT("ne:psu:child") "/class", Deleted{}},
338 {COMPONENT("ne:psu:child") "/parent", Deleted{}},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100339 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", Deleted{}},
340 {COMPONENT("ne:psu:child") "/sensor-data/value", Deleted{}},
341 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", Deleted{}},
342 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", Deleted{}},
343 {COMPONENT("ne:psu:child") "/sensor-data/value-type", Deleted{}},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100344 {COMPONENT("ne:psu:child") "/state/oper-state", Deleted{}},
345 {COMPONENT("ne:power") "/sensor-data/value", "11222333"},
346 {COMPONENT("ne:psu") "/state/oper-state", "disabled"},
347 {COMPONENT("ne:temperature-cpu") "/sensor-data/value", "222"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100348 }))
349 .IN_SEQUENCE(seq1);
Tomáš Pecka26b38212024-01-16 17:23:31 +0100350 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu", "critical", "PSU missing.").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100351 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "cleared", "Sensor value crossed low threshold.").IN_SEQUENCE(seq1);
352 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu:child", "warning", "Sensor value not reported. Maybe the sensor was unplugged?").IN_SEQUENCE(seq1);
353 REQUIRE_CALL(*sysfsTempCpu, attribute("temp1_input")).LR_RETURN(cpuTempValue).TIMES(AT_LEAST(1));
354 REQUIRE_CALL(*sysfsPower, attribute("power1_input")).LR_RETURN(powerValue).TIMES(AT_LEAST(1));
355 cpuTempValue = 222;
356 powerValue = 11222333;
357 psuActive = false;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200358
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100359 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
360 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 +0200361
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100362 // third batch of changes, wild PSU appears with a warning
Jan Kundrát82e41c32024-01-15 13:28:01 +0100363 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100364 {COMPONENT("ne:psu") "/state/oper-state", "enabled"},
365 {COMPONENT("ne:psu:child") "/class", "iana-hardware:sensor"},
366 {COMPONENT("ne:psu:child") "/parent", "ne:psu"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100367 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", "ok"},
368 {COMPONENT("ne:psu:child") "/sensor-data/value", "50000"},
369 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", "0"},
370 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", "milli"},
371 {COMPONENT("ne:psu:child") "/sensor-data/value-type", "volts-DC"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100372 {COMPONENT("ne:psu:child") "/state/oper-state", "enabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100373 }))
374 .IN_SEQUENCE(seq1);
Tomáš Pecka26b38212024-01-16 17:23:31 +0100375 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu", "cleared", "PSU missing.").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100376 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu:child", "cleared", "Sensor value not reported. Maybe the sensor was unplugged?").IN_SEQUENCE(seq1);
377 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:psu:child", "warning", "Sensor value crossed high threshold.").IN_SEQUENCE(seq1);
378 psuSensorValue = 50000;
379 psuActive = true;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200380
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100381 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200382
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100383 // fourth round. We unplug with a warning
Jan Kundrát82e41c32024-01-15 13:28:01 +0100384 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100385 {COMPONENT("ne:psu:child") "/class", Deleted{}},
386 {COMPONENT("ne:psu:child") "/parent", Deleted{}},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100387 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", Deleted{}},
388 {COMPONENT("ne:psu:child") "/sensor-data/value", Deleted{}},
389 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", Deleted{}},
390 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", Deleted{}},
391 {COMPONENT("ne:psu:child") "/sensor-data/value-type", Deleted{}},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100392 {COMPONENT("ne:psu:child") "/state/oper-state", Deleted{}},
393 {COMPONENT("ne:psu") "/state/oper-state", "disabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100394 }))
395 .IN_SEQUENCE(seq1);
Tomáš Pecka26b38212024-01-16 17:23:31 +0100396 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu", "critical", "PSU missing.").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100397 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu:child", "warning", "Sensor value not reported. Maybe the sensor was unplugged?").IN_SEQUENCE(seq1);
398 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:psu:child", "cleared", "Sensor value crossed high threshold.").IN_SEQUENCE(seq1);
399 psuActive = false;
400 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200401
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100402 // 5+th round: test threshold crossings
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", "21000000"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100405 }))
406 .IN_SEQUENCE(seq1);
407 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "warning", "Sensor value crossed high threshold.").IN_SEQUENCE(seq1);
408 powerValue = 21'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", "24000000"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100413 }))
414 .IN_SEQUENCE(seq1);
415 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "critical", "Sensor value crossed high threshold.").IN_SEQUENCE(seq1);
416 powerValue = 24'000'000;
417 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200418
Jan Kundrát82e41c32024-01-15 13:28:01 +0100419 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100420 {COMPONENT("ne:power") "/sensor-data/value", "1"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100421 }))
422 .IN_SEQUENCE(seq1);
423 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "critical", "Sensor value crossed low threshold.").IN_SEQUENCE(seq1);
424 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "cleared", "Sensor value crossed high threshold.").IN_SEQUENCE(seq1);
425 powerValue = 1;
426 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200427
Jan Kundrát82e41c32024-01-15 13:28:01 +0100428 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100429 {COMPONENT("ne:power") "/sensor-data/value", "14000000"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100430 }))
431 .IN_SEQUENCE(seq1);
432 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "cleared", "Sensor value crossed low threshold.").IN_SEQUENCE(seq1);
433 powerValue = 14'000'000;
434 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200435
Tomáš Pecka5a4c0352023-12-12 12:29:28 +0100436
Jan Kundrát82e41c32024-01-15 13:28:01 +0100437 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100438 {COMPONENT("ne:power") "/sensor-data/value", "1000000000"},
439 {COMPONENT("ne:power") "/sensor-data/oper-status", "nonoperational"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100440 }))
441 .IN_SEQUENCE(seq1);
442 REQUIRE_ALARM_RPC("velia-alarms:sensor-nonoperational", "ne:power", "warning", "Sensor is nonoperational. The values it reports may not be relevant.").IN_SEQUENCE(seq1);
443 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "critical", "Sensor value crossed high threshold.").IN_SEQUENCE(seq1);
444 powerValue = 2'999'999'999;
445 waitForCompletionAndBitMore(seq1);
Tomáš Pecka5a4c0352023-12-12 12:29:28 +0100446
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100447 powerValue = 1'999'999'999;
448 waitForCompletionAndBitMore(seq1);
Tomáš Pecka5a4c0352023-12-12 12:29:28 +0100449
Jan Kundrát82e41c32024-01-15 13:28:01 +0100450 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100451 {COMPONENT("ne:power") "/sensor-data/value", "-1000000000"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100452 }))
453 .IN_SEQUENCE(seq1);
454 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "critical", "Sensor value crossed low threshold.").IN_SEQUENCE(seq1);
455 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "cleared", "Sensor value crossed high threshold.").IN_SEQUENCE(seq1);
456 powerValue = -2'999'999'999;
457 waitForCompletionAndBitMore(seq1);
Tomáš Pecka5a4c0352023-12-12 12:29:28 +0100458
Jan Kundrát82e41c32024-01-15 13:28:01 +0100459 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100460 {COMPONENT("ne:power") "/sensor-data/value", "-999999999"},
461 {COMPONENT("ne:power") "/sensor-data/oper-status", "ok"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100462 }))
463 .IN_SEQUENCE(seq1);
464 REQUIRE_ALARM_RPC("velia-alarms:sensor-nonoperational", "ne:power", "cleared", "Sensor is nonoperational. The values it reports may not be relevant.").IN_SEQUENCE(seq1);
465 powerValue = -999'999'999;
466 waitForCompletionAndBitMore(seq1);
467 }
468
469 SECTION("Disappearing sensor unplugged in the beginning")
470 {
471 cpuTempValue = 41800;
472 powerValue = 0;
473 psuActive = false;
474 psuSensorValue = 12000;
475 REQUIRE_CALL(*sysfsTempCpu, attribute("temp1_input")).LR_RETURN(cpuTempValue).TIMES(AT_LEAST(1));
476 REQUIRE_CALL(*sysfsPower, attribute("power1_input")).LR_RETURN(powerValue).TIMES(AT_LEAST(1));
Jan Kundrátcdc899b2024-01-15 13:39:28 +0100477 REQUIRE_ALARM_INVENTORY_ADD_ALARM("velia-alarms:sensor-low-value-alarm", "ne:power").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100478 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{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100488 {COMPONENT("ne") "/class", "iana-hardware:chassis"},
489 {COMPONENT("ne") "/mfg-name", "CESNET"},
490 {COMPONENT("ne") "/name", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100491 {COMPONENT("ne") "/state/oper-state", "enabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100492 {COMPONENT("ne:power") "/class", "iana-hardware:sensor"},
493 {COMPONENT("ne:power") "/name", "ne:power"},
494 {COMPONENT("ne:power") "/parent", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100495 {COMPONENT("ne:power") "/sensor-data/oper-status", "ok"},
496 {COMPONENT("ne:power") "/sensor-data/value", "0"},
497 {COMPONENT("ne:power") "/sensor-data/value-precision", "0"},
498 {COMPONENT("ne:power") "/sensor-data/value-scale", "micro"},
499 {COMPONENT("ne:power") "/sensor-data/value-type", "watts"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100500 {COMPONENT("ne:power") "/state/oper-state", "enabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100501 {COMPONENT("ne:psu") "/class", "iana-hardware:power-supply"},
502 {COMPONENT("ne:psu") "/name", "ne:psu"},
503 {COMPONENT("ne:psu") "/parent", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100504 {COMPONENT("ne:psu") "/state/oper-state", "disabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100505 {COMPONENT("ne:temperature-cpu") "/class", "iana-hardware:sensor"},
506 {COMPONENT("ne:temperature-cpu") "/name", "ne:temperature-cpu"},
507 {COMPONENT("ne:temperature-cpu") "/parent", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100508 {COMPONENT("ne:temperature-cpu") "/sensor-data/oper-status", "ok"},
509 {COMPONENT("ne:temperature-cpu") "/sensor-data/value", "41800"},
510 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-precision", "0"},
511 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-scale", "milli"},
512 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-type", "celsius"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100513 {COMPONENT("ne:temperature-cpu") "/state/oper-state", "enabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100514 }))
515 .IN_SEQUENCE(seq1);
Tomáš Pecka26b38212024-01-16 17:23:31 +0100516 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-missing-alarm", "ne:psu").TIMES(AT_LEAST(1));
517 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu", "critical", "PSU missing.").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100518 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "critical", "Sensor value crossed low threshold.").IN_SEQUENCE(seq1);
519
520 auto ietfHardwareSysrepo = std::make_shared<velia::ietf_hardware::sysrepo::Sysrepo>(srSess, ietfHardware, 150ms);
521 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
522 waitForCompletionAndBitMore(seq1);
523
524 std::string lastChange = directLeafNodeQuery(modulePrefix + "/last-change");
525
526 // PSU inserted
527 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-low-value-alarm", "ne:psu:child").IN_SEQUENCE(seq1);
528 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-high-value-alarm", "ne:psu:child").IN_SEQUENCE(seq1);
529 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-missing-alarm", "ne:psu:child").IN_SEQUENCE(seq1);
530 REQUIRE_ALARM_INVENTORY_ADD_RESOURCE("velia-alarms:sensor-nonoperational", "ne:psu:child").IN_SEQUENCE(seq1);
Jan Kundrát82e41c32024-01-15 13:28:01 +0100531 REQUIRE_CALL(dsChangeHardware, change(ValueMap{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100532 {COMPONENT("ne:psu") "/state/oper-state", "enabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100533 {COMPONENT("ne:psu:child") "/class", "iana-hardware:sensor"},
534 {COMPONENT("ne:psu:child") "/name", "ne:psu:child"},
535 {COMPONENT("ne:psu:child") "/parent", "ne:psu"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100536 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", "ok"},
537 {COMPONENT("ne:psu:child") "/sensor-data/value", "12000"},
538 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", "0"},
539 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", "milli"},
540 {COMPONENT("ne:psu:child") "/sensor-data/value-type", "volts-DC"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100541 {COMPONENT("ne:psu:child") "/state/oper-state", "enabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100542 }))
543 .IN_SEQUENCE(seq1);
Tomáš Pecka26b38212024-01-16 17:23:31 +0100544 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu", "cleared", "PSU missing.").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100545 psuActive = true;
546 waitForCompletionAndBitMore(seq1);
547
548 std::this_thread::sleep_for(1000ms); // last-change leaf resolution is in seconds, let's wait until the second increments
549 REQUIRE(directLeafNodeQuery(modulePrefix + "/last-change") > lastChange); // check that last-change leaf has timestamp that is greater than the previous one
550 }
Tomáš Pecka3f811962023-04-14 10:54:32 +0200551}