blob: 48d3880f2305231a52645cb8d7b685f6768d6c14 [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"
Tomáš Peckabaf289c2024-01-24 15:12:44 +01009#include "sysrepo-helpers/alarms.h"
Tomáš Pecka7eb64592024-01-24 14:10:47 +010010#include "sysrepo-helpers/datastore.h"
Tomáš Pecka3f811962023-04-14 10:54:32 +020011#include "test_log_setup.h"
Tomáš Peckac164ca62024-01-24 13:38:03 +010012#include "tests/sysrepo-helpers/common.h"
Jan Kundrát7935e812024-09-17 17:02:01 +020013#include "utils/libyang.h"
Tomáš Pecka3f811962023-04-14 10:54:32 +020014
15using namespace std::literals;
16
Tomáš Pecka2fdc8ce2024-01-18 12:28:37 +010017#define COMPONENT(RESOURCE) "/ietf-hardware:hardware/component[name='" RESOURCE "']"
Jan Kundrátc1511512024-01-15 13:47:28 +010018
Tomáš Peckabbfc1c32024-01-31 13:58:11 +010019#define VEC(...) (std::vector<std::string>{__VA_ARGS__})
20#define ALARMS(...) VEC(__VA_ARGS__)
21#define COMPONENTS(...) VEC(__VA_ARGS__)
22
Tomáš Pecka9bd61272024-01-31 15:08:13 +010023#define INTRODUCED_ALARM(ALARM_TYPE, DESCRIPTION) velia::alarms::AlarmInventoryEntry(ALARM_TYPE, DESCRIPTION)
24#define REQUIRE_ALARM_INVENTORY_ADD_ALARMS(...) REQUIRE_NEW_ALARM_INVENTORY_ENTRIES(alarmWatcher, (std::vector<velia::alarms::AlarmInventoryEntry>{__VA_ARGS__}))
Tomáš Peckabbfc1c32024-01-31 13:58:11 +010025#define REQUIRE_ALARM_INVENTORY_ADD_RESOURCES(ALARMS, COMPONENTS) REQUIRE_NEW_ALARM_INVENTORY_RESOURCES(alarmWatcher, ALARMS, COMPONENTS)
Jan Kundrátc1511512024-01-15 13:47:28 +010026
27#define REQUIRE_ALARM_RPC(ALARM_TYPE, RESOURCE, SEVERITY, TEXT) \
Tomáš Pecka4163c0c2024-01-31 13:21:03 +010028 REQUIRE_NEW_ALARM(alarmWatcher, ALARM_TYPE, COMPONENT(RESOURCE), SEVERITY, TEXT)
Tomáš Pecka1b3c1732023-05-12 11:45:01 +020029
Tomáš Pecka3f811962023-04-14 10:54:32 +020030TEST_CASE("IETF Hardware with sysrepo")
31{
32 TEST_SYSREPO_INIT_LOGS;
33 TEST_SYSREPO_INIT;
34 TEST_SYSREPO_INIT_CLIENT;
Tomáš Peckac0991ce2023-12-20 15:46:03 +010035
36 srSess.sendRPC(srSess.getContext().newPath("/ietf-factory-default:factory-reset"));
37
Tomáš Pecka2117ce52023-05-12 11:28:34 +020038 auto alarmsClient = sysrepo::Connection{}.sessionStart(sysrepo::Datastore::Operational);
39
Tomáš Pecka3f811962023-04-14 10:54:32 +020040 static const auto modulePrefix = "/ietf-hardware:hardware"s;
41
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020042 client.switchDatastore(sysrepo::Datastore::Operational);
43
Tomáš Pecka3f811962023-04-14 10:54:32 +020044 trompeloeil::sequence seq1;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020045
46 auto directLeafNodeQuery = [&](const std::string& xpath) {
47 auto val = client.getData(xpath);
48 REQUIRE(val);
Jan Kundrát8dd54de2024-09-17 17:04:00 +020049 return velia::utils::asString(*val->findPath(xpath));
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020050 };
Tomáš Pecka3f811962023-04-14 10:54:32 +020051
Tomáš Pecka3f811962023-04-14 10:54:32 +020052 auto sysfsTempCpu = std::make_shared<FakeHWMon>();
Tomáš Pecka3f811962023-04-14 10:54:32 +020053 auto sysfsPower = std::make_shared<FakeHWMon>();
Tomáš Pecka3f811962023-04-14 10:54:32 +020054
Tomáš Pecka9af47392023-05-23 14:56:48 +020055 using velia::ietf_hardware::OneThreshold;
56 using velia::ietf_hardware::Thresholds;
Tomáš Pecka3f811962023-04-14 10:54:32 +020057 using velia::ietf_hardware::data_reader::SensorType;
58 using velia::ietf_hardware::data_reader::StaticData;
59 using velia::ietf_hardware::data_reader::SysfsValue;
Tomáš Peckae5366c62023-04-14 11:03:04 +020060
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020061 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 +020062 std::atomic<int64_t> psuSensorValue;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020063 std::atomic<int64_t> cpuTempValue;
64 std::atomic<int64_t> powerValue;
65
Tomáš Pecka3f811962023-04-14 10:54:32 +020066 // register components into hw state
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020067 auto ietfHardware = std::make_shared<velia::ietf_hardware::IETFHardware>();
Tomáš Pecka3f811962023-04-14 10:54:32 +020068 ietfHardware->registerDataReader(StaticData("ne", std::nullopt, {{"class", "iana-hardware:chassis"}, {"mfg-name", "CESNET"s}}));
Tomáš Peckae5366c62023-04-14 11:03:04 +020069 ietfHardware->registerDataReader(SysfsValue<SensorType::Temperature>("ne:temperature-cpu", "ne", sysfsTempCpu, 1));
Jan Kundrátc1511512024-01-15 13:47:28 +010070 ietfHardware->registerDataReader(SysfsValue<SensorType::Power>(
71 "ne:power",
72 "ne",
73 sysfsPower,
74 1,
75 Thresholds<int64_t>{
76 .criticalLow = OneThreshold<int64_t>{8'000'000, 500'000},
77 .warningLow = OneThreshold<int64_t>{10'000'000, 500'000},
78 .warningHigh = OneThreshold<int64_t>{20'000'000, 500'000},
79 .criticalHigh = OneThreshold<int64_t>{22'000'000, 500'000},
80 }));
Tomáš Pecka3f811962023-04-14 10:54:32 +020081
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020082 /* 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
83 * data might not even be pushed (e.g. the child sensors).
84 * Since we push data into sysrepo we have to erase old data (that should no longer be present) from the sysrepo operational DS.
85 * We test such situation via the following data reader which returns data only when psuActive is set to true.
86 */
87 struct PsuDataReader {
88 const std::atomic<bool>& active;
Tomáš Pecka9af47392023-05-23 14:56:48 +020089 const std::atomic<int64_t>& value;
Tomáš Pecka3f811962023-04-14 10:54:32 +020090
Tomáš Peckac0991ce2023-12-20 15:46:03 +010091 velia::ietf_hardware::SensorPollData operator()()
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020092 {
Tomáš Pecka26b38212024-01-16 17:23:31 +010093 velia::ietf_hardware::SideLoadedAlarm alarm;
Tomáš Peckac0991ce2023-12-20 15:46:03 +010094 velia::ietf_hardware::ThresholdsBySensorPath thr;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020095 velia::ietf_hardware::DataTree res = {
Jan Kundrátf85b5cb2024-01-15 13:11:51 +010096 {COMPONENT("ne:psu") "/class", "iana-hardware:power-supply"},
97 {COMPONENT("ne:psu") "/parent", "ne"},
98 {COMPONENT("ne:psu") "/state/oper-state", "disabled"},
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +020099 };
Tomáš Pecka3f811962023-04-14 10:54:32 +0200100
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200101 if (active) {
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100102 res[COMPONENT("ne:psu") "/state/oper-state"] = "enabled";
103 res[COMPONENT("ne:psu:child") "/class"] = "iana-hardware:sensor";
104 res[COMPONENT("ne:psu:child") "/parent"] = "ne:psu";
105 res[COMPONENT("ne:psu:child") "/state/oper-state"] = "enabled";
106 res[COMPONENT("ne:psu:child") "/sensor-data/oper-status"] = "ok";
107 res[COMPONENT("ne:psu:child") "/sensor-data/value"] = std::to_string(value);
108 res[COMPONENT("ne:psu:child") "/sensor-data/value-precision"] = "0";
109 res[COMPONENT("ne:psu:child") "/sensor-data/value-scale"] = "milli";
110 res[COMPONENT("ne:psu:child") "/sensor-data/value-type"] = "volts-DC";
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100111
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100112 thr[COMPONENT("ne:psu:child") "/sensor-data/value"] = Thresholds<int64_t>{
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100113 .criticalLow = std::nullopt,
114 .warningLow = OneThreshold<int64_t>{10000, 2000},
115 .warningHigh = OneThreshold<int64_t>{15000, 2000},
116 .criticalHigh = std::nullopt,
117 };
Tomáš Pecka26b38212024-01-16 17:23:31 +0100118
119 alarm = {"velia-alarms:sensor-missing-alarm", COMPONENT("ne:psu"), "cleared", "PSU missing."};
120 } else {
121 alarm = {"velia-alarms:sensor-missing-alarm", COMPONENT("ne:psu"), "critical", "PSU missing."};
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200122 }
Tomáš Pecka3f811962023-04-14 10:54:32 +0200123
Tomáš Pecka26b38212024-01-16 17:23:31 +0100124 return {res, thr, {alarm}};
Tomáš Pecka4886db22023-05-10 10:46:15 +0200125 }
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200126 };
Tomáš Pecka9af47392023-05-23 14:56:48 +0200127 ietfHardware->registerDataReader(PsuDataReader{psuActive, psuSensorValue});
Tomáš Pecka3f811962023-04-14 10:54:32 +0200128
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200129 /* Ensure that there are sane data after each sysrepo change callback (all the component subtrees are expected). */
Tomáš Pecka7eb64592024-01-24 14:10:47 +0100130 DatastoreWatcher dsChangeHardware(client, "/ietf-hardware:hardware/component", {"/ietf-hardware:hardware/last-change"});
Tomáš Peckabaf289c2024-01-24 15:12:44 +0100131 AlarmWatcher alarmWatcher(client);
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200132
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100133 SECTION("Disappearing sensor plugged from the beginning")
134 {
135 // first batch of values
136 cpuTempValue = 41800;
137 powerValue = 0;
138 psuActive = true;
139 psuSensorValue = 12000;
140 REQUIRE_CALL(*sysfsTempCpu, attribute("temp1_input")).LR_RETURN(cpuTempValue).TIMES(AT_LEAST(1));
141 REQUIRE_CALL(*sysfsPower, attribute("power1_input")).LR_RETURN(powerValue).TIMES(AT_LEAST(1));
Tomáš Pecka2848fd02024-01-30 12:05:59 +0100142
Tomáš Pecka9bd61272024-01-31 15:08:13 +0100143 REQUIRE_ALARM_INVENTORY_ADD_ALARMS(
144 INTRODUCED_ALARM("velia-alarms:sensor-low-value-alarm", "Sensor value is below the low threshold."),
145 INTRODUCED_ALARM("velia-alarms:sensor-high-value-alarm", "Sensor value is above the high threshold."),
146 INTRODUCED_ALARM("velia-alarms:sensor-missing-alarm", "Sensor is missing."),
147 INTRODUCED_ALARM("velia-alarms:sensor-nonoperational", "Sensor is flagged as nonoperational."))
148 .IN_SEQUENCE(seq1);
Tomáš Pecka2848fd02024-01-30 12:05:59 +0100149
Tomáš Peckabbfc1c32024-01-31 13:58:11 +0100150 REQUIRE_ALARM_INVENTORY_ADD_RESOURCES(
151 ALARMS("velia-alarms:sensor-low-value-alarm",
152 "velia-alarms:sensor-high-value-alarm",
153 "velia-alarms:sensor-missing-alarm",
154 "velia-alarms:sensor-nonoperational"),
155 COMPONENTS(
156 COMPONENT("ne:power"),
157 COMPONENT("ne:psu:child"),
158 COMPONENT("ne:temperature-cpu")))
159 .IN_SEQUENCE(seq1);
Tomáš Pecka2117ce52023-05-12 11:28:34 +0200160
Tomáš Pecka7eb64592024-01-24 14:10:47 +0100161 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100162 {COMPONENT("ne") "/class", "iana-hardware:chassis"},
163 {COMPONENT("ne") "/mfg-name", "CESNET"},
164 {COMPONENT("ne") "/name", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100165 {COMPONENT("ne") "/state/oper-state", "enabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100166 {COMPONENT("ne:power") "/class", "iana-hardware:sensor"},
167 {COMPONENT("ne:power") "/name", "ne:power"},
168 {COMPONENT("ne:power") "/parent", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100169 {COMPONENT("ne:power") "/sensor-data/oper-status", "ok"},
170 {COMPONENT("ne:power") "/sensor-data/value", "0"},
171 {COMPONENT("ne:power") "/sensor-data/value-precision", "0"},
172 {COMPONENT("ne:power") "/sensor-data/value-scale", "micro"},
173 {COMPONENT("ne:power") "/sensor-data/value-type", "watts"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100174 {COMPONENT("ne:power") "/state/oper-state", "enabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100175 {COMPONENT("ne:psu") "/class", "iana-hardware:power-supply"},
176 {COMPONENT("ne:psu") "/name", "ne:psu"},
177 {COMPONENT("ne:psu") "/parent", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100178 {COMPONENT("ne:psu") "/state/oper-state", "enabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100179 {COMPONENT("ne:psu:child") "/class", "iana-hardware:sensor"},
180 {COMPONENT("ne:psu:child") "/name", "ne:psu:child"},
181 {COMPONENT("ne:psu:child") "/parent", "ne:psu"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100182 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", "ok"},
183 {COMPONENT("ne:psu:child") "/sensor-data/value", "12000"},
184 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", "0"},
185 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", "milli"},
186 {COMPONENT("ne:psu:child") "/sensor-data/value-type", "volts-DC"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100187 {COMPONENT("ne:psu:child") "/state/oper-state", "enabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100188 {COMPONENT("ne:temperature-cpu") "/class", "iana-hardware:sensor"},
189 {COMPONENT("ne:temperature-cpu") "/name", "ne:temperature-cpu"},
190 {COMPONENT("ne:temperature-cpu") "/parent", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100191 {COMPONENT("ne:temperature-cpu") "/sensor-data/oper-status", "ok"},
192 {COMPONENT("ne:temperature-cpu") "/sensor-data/value", "41800"},
193 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-precision", "0"},
194 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-scale", "milli"},
195 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-type", "celsius"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100196 {COMPONENT("ne:temperature-cpu") "/state/oper-state", "enabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100197 }))
198 .IN_SEQUENCE(seq1);
Tomáš Pecka11b49b82024-10-21 14:28:30 +0200199 REQUIRE_ALARM_INVENTORY_ADD_RESOURCES(ALARMS("velia-alarms:sensor-missing-alarm"), COMPONENTS(COMPONENT("ne:psu"))).TIMES(1);
Tomáš Pecka61212852024-09-30 14:36:50 +0200200 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "critical", "Sensor value crossed low threshold (0 < 8000000).").IN_SEQUENCE(seq1);
Tomáš Pecka3f811962023-04-14 10:54:32 +0200201
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100202 auto ietfHardwareSysrepo = std::make_shared<velia::ietf_hardware::sysrepo::Sysrepo>(srSess, ietfHardware, 150ms);
203 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 +0200204
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100205 std::string lastChange = directLeafNodeQuery(modulePrefix + "/last-change");
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200206
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100207 // second batch of values, sensor data changed, PSU ejected
Tomáš Pecka7eb64592024-01-24 14:10:47 +0100208 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100209 {COMPONENT("ne:psu:child") "/class", Deleted{}},
210 {COMPONENT("ne:psu:child") "/parent", Deleted{}},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100211 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", Deleted{}},
212 {COMPONENT("ne:psu:child") "/sensor-data/value", Deleted{}},
213 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", Deleted{}},
214 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", Deleted{}},
215 {COMPONENT("ne:psu:child") "/sensor-data/value-type", Deleted{}},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100216 {COMPONENT("ne:psu:child") "/state/oper-state", Deleted{}},
217 {COMPONENT("ne:power") "/sensor-data/value", "11222333"},
218 {COMPONENT("ne:psu") "/state/oper-state", "disabled"},
219 {COMPONENT("ne:temperature-cpu") "/sensor-data/value", "222"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100220 }))
221 .IN_SEQUENCE(seq1);
Tomáš Pecka26b38212024-01-16 17:23:31 +0100222 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu", "critical", "PSU missing.").IN_SEQUENCE(seq1);
Tomáš Pecka61212852024-09-30 14:36:50 +0200223 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "cleared", "Sensor value is within normal parameters.").IN_SEQUENCE(seq1);
Jan Kundrátc1511512024-01-15 13:47:28 +0100224 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu:child", "warning",
225 "Sensor value not reported. Maybe the sensor was unplugged?").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100226 REQUIRE_CALL(*sysfsTempCpu, attribute("temp1_input")).LR_RETURN(cpuTempValue).TIMES(AT_LEAST(1));
227 REQUIRE_CALL(*sysfsPower, attribute("power1_input")).LR_RETURN(powerValue).TIMES(AT_LEAST(1));
228 cpuTempValue = 222;
229 powerValue = 11222333;
230 psuActive = false;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200231
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100232 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
233 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 +0200234
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100235 // third batch of changes, wild PSU appears with a warning
Tomáš Pecka7eb64592024-01-24 14:10:47 +0100236 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100237 {COMPONENT("ne:psu") "/state/oper-state", "enabled"},
238 {COMPONENT("ne:psu:child") "/class", "iana-hardware:sensor"},
239 {COMPONENT("ne:psu:child") "/parent", "ne:psu"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100240 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", "ok"},
241 {COMPONENT("ne:psu:child") "/sensor-data/value", "50000"},
242 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", "0"},
243 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", "milli"},
244 {COMPONENT("ne:psu:child") "/sensor-data/value-type", "volts-DC"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100245 {COMPONENT("ne:psu:child") "/state/oper-state", "enabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100246 }))
247 .IN_SEQUENCE(seq1);
Tomáš Pecka26b38212024-01-16 17:23:31 +0100248 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu", "cleared", "PSU missing.").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100249 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu:child", "cleared", "Sensor value not reported. Maybe the sensor was unplugged?").IN_SEQUENCE(seq1);
Tomáš Pecka61212852024-09-30 14:36:50 +0200250 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:psu:child", "warning", "Sensor value crossed high threshold (50000 > 15000).").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100251 psuSensorValue = 50000;
252 psuActive = true;
Tomáš Pecka43ef7ba2023-04-13 15:56:48 +0200253
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100254 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200255
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100256 // fourth round. We unplug with a warning
Tomáš Pecka7eb64592024-01-24 14:10:47 +0100257 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100258 {COMPONENT("ne:psu:child") "/class", Deleted{}},
259 {COMPONENT("ne:psu:child") "/parent", Deleted{}},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100260 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", Deleted{}},
261 {COMPONENT("ne:psu:child") "/sensor-data/value", Deleted{}},
262 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", Deleted{}},
263 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", Deleted{}},
264 {COMPONENT("ne:psu:child") "/sensor-data/value-type", Deleted{}},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100265 {COMPONENT("ne:psu:child") "/state/oper-state", Deleted{}},
266 {COMPONENT("ne:psu") "/state/oper-state", "disabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100267 }))
268 .IN_SEQUENCE(seq1);
Tomáš Pecka26b38212024-01-16 17:23:31 +0100269 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu", "critical", "PSU missing.").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100270 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu:child", "warning", "Sensor value not reported. Maybe the sensor was unplugged?").IN_SEQUENCE(seq1);
Tomáš Pecka61212852024-09-30 14:36:50 +0200271 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:psu:child", "cleared", "Sensor value is within normal parameters.").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100272 psuActive = false;
273 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200274
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100275 // 5+th round: test threshold crossings
Tomáš Pecka7eb64592024-01-24 14:10:47 +0100276 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100277 {COMPONENT("ne:power") "/sensor-data/value", "21000000"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100278 }))
279 .IN_SEQUENCE(seq1);
Tomáš Pecka61212852024-09-30 14:36:50 +0200280 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "warning", "Sensor value crossed high threshold (21000000 > 20000000).").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100281 powerValue = 21'000'000;
282 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200283
Tomáš Pecka7eb64592024-01-24 14:10:47 +0100284 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100285 {COMPONENT("ne:power") "/sensor-data/value", "24000000"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100286 }))
287 .IN_SEQUENCE(seq1);
Tomáš Pecka61212852024-09-30 14:36:50 +0200288 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "critical", "Sensor value crossed high threshold (24000000 > 22000000).").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100289 powerValue = 24'000'000;
290 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200291
Tomáš Pecka7eb64592024-01-24 14:10:47 +0100292 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100293 {COMPONENT("ne:power") "/sensor-data/value", "1"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100294 }))
295 .IN_SEQUENCE(seq1);
Tomáš Pecka61212852024-09-30 14:36:50 +0200296 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "critical", "Sensor value crossed low threshold (1 < 8000000).").IN_SEQUENCE(seq1);
297 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "cleared", "Sensor value is within normal parameters.").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100298 powerValue = 1;
299 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200300
Tomáš Pecka7eb64592024-01-24 14:10:47 +0100301 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100302 {COMPONENT("ne:power") "/sensor-data/value", "14000000"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100303 }))
304 .IN_SEQUENCE(seq1);
Tomáš Pecka61212852024-09-30 14:36:50 +0200305 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "cleared", "Sensor value is within normal parameters.").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100306 powerValue = 14'000'000;
307 waitForCompletionAndBitMore(seq1);
Tomáš Pecka9af47392023-05-23 14:56:48 +0200308
Tomáš Pecka5a4c0352023-12-12 12:29:28 +0100309
Tomáš Pecka7eb64592024-01-24 14:10:47 +0100310 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100311 {COMPONENT("ne:power") "/sensor-data/value", "1000000000"},
312 {COMPONENT("ne:power") "/sensor-data/oper-status", "nonoperational"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100313 }))
314 .IN_SEQUENCE(seq1);
Jan Kundrátc1511512024-01-15 13:47:28 +0100315 REQUIRE_ALARM_RPC("velia-alarms:sensor-nonoperational", "ne:power", "warning",
316 "Sensor is nonoperational. The values it reports may not be relevant.").IN_SEQUENCE(seq1);
317 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "critical",
Tomáš Pecka61212852024-09-30 14:36:50 +0200318 "Sensor value crossed high threshold (1000000000 > 22000000).").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100319 powerValue = 2'999'999'999;
320 waitForCompletionAndBitMore(seq1);
Tomáš Pecka5a4c0352023-12-12 12:29:28 +0100321
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100322 powerValue = 1'999'999'999;
323 waitForCompletionAndBitMore(seq1);
Tomáš Pecka5a4c0352023-12-12 12:29:28 +0100324
Tomáš Pecka7eb64592024-01-24 14:10:47 +0100325 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100326 {COMPONENT("ne:power") "/sensor-data/value", "-1000000000"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100327 }))
328 .IN_SEQUENCE(seq1);
Tomáš Pecka61212852024-09-30 14:36:50 +0200329 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "critical", "Sensor value crossed low threshold (-1000000000 < 8000000).").IN_SEQUENCE(seq1);
330 REQUIRE_ALARM_RPC("velia-alarms:sensor-high-value-alarm", "ne:power", "cleared", "Sensor value is within normal parameters.").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100331 powerValue = -2'999'999'999;
332 waitForCompletionAndBitMore(seq1);
Tomáš Pecka5a4c0352023-12-12 12:29:28 +0100333
Tomáš Pecka7eb64592024-01-24 14:10:47 +0100334 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100335 {COMPONENT("ne:power") "/sensor-data/value", "-999999999"},
336 {COMPONENT("ne:power") "/sensor-data/oper-status", "ok"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100337 }))
338 .IN_SEQUENCE(seq1);
Jan Kundrátc1511512024-01-15 13:47:28 +0100339 REQUIRE_ALARM_RPC("velia-alarms:sensor-nonoperational", "ne:power", "cleared",
340 "Sensor is nonoperational. The values it reports may not be relevant.").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100341 powerValue = -999'999'999;
342 waitForCompletionAndBitMore(seq1);
343 }
344
345 SECTION("Disappearing sensor unplugged in the beginning")
346 {
347 cpuTempValue = 41800;
348 powerValue = 0;
349 psuActive = false;
350 psuSensorValue = 12000;
351 REQUIRE_CALL(*sysfsTempCpu, attribute("temp1_input")).LR_RETURN(cpuTempValue).TIMES(AT_LEAST(1));
352 REQUIRE_CALL(*sysfsPower, attribute("power1_input")).LR_RETURN(powerValue).TIMES(AT_LEAST(1));
Tomáš Pecka9bd61272024-01-31 15:08:13 +0100353 REQUIRE_ALARM_INVENTORY_ADD_ALARMS(
354 INTRODUCED_ALARM("velia-alarms:sensor-low-value-alarm", "Sensor value is below the low threshold."),
355 INTRODUCED_ALARM("velia-alarms:sensor-high-value-alarm", "Sensor value is above the high threshold."),
356 INTRODUCED_ALARM("velia-alarms:sensor-missing-alarm", "Sensor is missing."),
357 INTRODUCED_ALARM("velia-alarms:sensor-nonoperational", "Sensor is flagged as nonoperational."))
358 .IN_SEQUENCE(seq1);
Tomáš Pecka2848fd02024-01-30 12:05:59 +0100359
Tomáš Peckabbfc1c32024-01-31 13:58:11 +0100360 REQUIRE_ALARM_INVENTORY_ADD_RESOURCES(
361 ALARMS("velia-alarms:sensor-low-value-alarm",
362 "velia-alarms:sensor-high-value-alarm",
363 "velia-alarms:sensor-missing-alarm",
364 "velia-alarms:sensor-nonoperational"),
365 COMPONENTS(
366 COMPONENT("ne:power"),
367 COMPONENT("ne:temperature-cpu")))
368 .IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100369
Tomáš Pecka7eb64592024-01-24 14:10:47 +0100370 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100371 {COMPONENT("ne") "/class", "iana-hardware:chassis"},
372 {COMPONENT("ne") "/mfg-name", "CESNET"},
373 {COMPONENT("ne") "/name", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100374 {COMPONENT("ne") "/state/oper-state", "enabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100375 {COMPONENT("ne:power") "/class", "iana-hardware:sensor"},
376 {COMPONENT("ne:power") "/name", "ne:power"},
377 {COMPONENT("ne:power") "/parent", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100378 {COMPONENT("ne:power") "/sensor-data/oper-status", "ok"},
379 {COMPONENT("ne:power") "/sensor-data/value", "0"},
380 {COMPONENT("ne:power") "/sensor-data/value-precision", "0"},
381 {COMPONENT("ne:power") "/sensor-data/value-scale", "micro"},
382 {COMPONENT("ne:power") "/sensor-data/value-type", "watts"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100383 {COMPONENT("ne:power") "/state/oper-state", "enabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100384 {COMPONENT("ne:psu") "/class", "iana-hardware:power-supply"},
385 {COMPONENT("ne:psu") "/name", "ne:psu"},
386 {COMPONENT("ne:psu") "/parent", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100387 {COMPONENT("ne:psu") "/state/oper-state", "disabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100388 {COMPONENT("ne:temperature-cpu") "/class", "iana-hardware:sensor"},
389 {COMPONENT("ne:temperature-cpu") "/name", "ne:temperature-cpu"},
390 {COMPONENT("ne:temperature-cpu") "/parent", "ne"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100391 {COMPONENT("ne:temperature-cpu") "/sensor-data/oper-status", "ok"},
392 {COMPONENT("ne:temperature-cpu") "/sensor-data/value", "41800"},
393 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-precision", "0"},
394 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-scale", "milli"},
395 {COMPONENT("ne:temperature-cpu") "/sensor-data/value-type", "celsius"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100396 {COMPONENT("ne:temperature-cpu") "/state/oper-state", "enabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100397 }))
398 .IN_SEQUENCE(seq1);
Tomáš Peckabbfc1c32024-01-31 13:58:11 +0100399 REQUIRE_ALARM_INVENTORY_ADD_RESOURCES(ALARMS("velia-alarms:sensor-missing-alarm"), COMPONENTS(COMPONENT("ne:psu"))).TIMES(AT_LEAST(1));
Tomáš Pecka26b38212024-01-16 17:23:31 +0100400 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu", "critical", "PSU missing.").IN_SEQUENCE(seq1);
Tomáš Pecka61212852024-09-30 14:36:50 +0200401 REQUIRE_ALARM_RPC("velia-alarms:sensor-low-value-alarm", "ne:power", "critical", "Sensor value crossed low threshold (0 < 8000000).").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100402
403 auto ietfHardwareSysrepo = std::make_shared<velia::ietf_hardware::sysrepo::Sysrepo>(srSess, ietfHardware, 150ms);
404 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
405 waitForCompletionAndBitMore(seq1);
406
407 std::string lastChange = directLeafNodeQuery(modulePrefix + "/last-change");
408
409 // PSU inserted
Tomáš Peckabbfc1c32024-01-31 13:58:11 +0100410 REQUIRE_ALARM_INVENTORY_ADD_RESOURCES(
411 ALARMS("velia-alarms:sensor-low-value-alarm",
412 "velia-alarms:sensor-high-value-alarm",
413 "velia-alarms:sensor-missing-alarm",
414 "velia-alarms:sensor-nonoperational"),
415 COMPONENTS(COMPONENT("ne:psu:child")))
416 .IN_SEQUENCE(seq1);
Tomáš Pecka7eb64592024-01-24 14:10:47 +0100417 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100418 {COMPONENT("ne:psu") "/state/oper-state", "enabled"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100419 {COMPONENT("ne:psu:child") "/class", "iana-hardware:sensor"},
420 {COMPONENT("ne:psu:child") "/name", "ne:psu:child"},
421 {COMPONENT("ne:psu:child") "/parent", "ne:psu"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100422 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", "ok"},
423 {COMPONENT("ne:psu:child") "/sensor-data/value", "12000"},
424 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", "0"},
425 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", "milli"},
426 {COMPONENT("ne:psu:child") "/sensor-data/value-type", "volts-DC"},
Jan Kundrátf85b5cb2024-01-15 13:11:51 +0100427 {COMPONENT("ne:psu:child") "/state/oper-state", "enabled"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100428 }))
429 .IN_SEQUENCE(seq1);
Tomáš Pecka26b38212024-01-16 17:23:31 +0100430 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu", "cleared", "PSU missing.").IN_SEQUENCE(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100431 psuActive = true;
432 waitForCompletionAndBitMore(seq1);
433
434 std::this_thread::sleep_for(1000ms); // last-change leaf resolution is in seconds, let's wait until the second increments
435 REQUIRE(directLeafNodeQuery(modulePrefix + "/last-change") > lastChange); // check that last-change leaf has timestamp that is greater than the previous one
Tomáš Pecka11b49b82024-10-21 14:28:30 +0200436
437 // unplug again
438 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
439 {COMPONENT("ne:psu") "/state/oper-state", "disabled"},
440 {COMPONENT("ne:psu:child") "/class", Deleted{}},
441 {COMPONENT("ne:psu:child") "/parent", Deleted{}},
442 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", Deleted{}},
443 {COMPONENT("ne:psu:child") "/sensor-data/value", Deleted{}},
444 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", Deleted{}},
445 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", Deleted{}},
446 {COMPONENT("ne:psu:child") "/sensor-data/value-type", Deleted{}},
447 {COMPONENT("ne:psu:child") "/state/oper-state", Deleted{}},
448 }))
449 .IN_SEQUENCE(seq1);
450 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu", "critical", "PSU missing.").IN_SEQUENCE(seq1);
451 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu:child", "warning",
452 "Sensor value not reported. Maybe the sensor was unplugged?").IN_SEQUENCE(seq1);
453 psuActive = false;
454 waitForCompletionAndBitMore(seq1);
455
456
457 // PSU inserted again - no inventory-alarm updates expected
458 REQUIRE_DATASTORE_CHANGE(dsChangeHardware, (ValueChanges{
459 {COMPONENT("ne:psu") "/state/oper-state", "enabled"},
460 {COMPONENT("ne:psu:child") "/class", "iana-hardware:sensor"},
461 {COMPONENT("ne:psu:child") "/parent", "ne:psu"},
462 {COMPONENT("ne:psu:child") "/sensor-data/oper-status", "ok"},
463 {COMPONENT("ne:psu:child") "/sensor-data/value", "12000"},
464 {COMPONENT("ne:psu:child") "/sensor-data/value-precision", "0"},
465 {COMPONENT("ne:psu:child") "/sensor-data/value-scale", "milli"},
466 {COMPONENT("ne:psu:child") "/sensor-data/value-type", "volts-DC"},
467 {COMPONENT("ne:psu:child") "/state/oper-state", "enabled"},
468 }))
469 .IN_SEQUENCE(seq1);
470 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu", "cleared", "PSU missing.").IN_SEQUENCE(seq1);
471 REQUIRE_ALARM_RPC("velia-alarms:sensor-missing-alarm", "ne:psu:child", "cleared",
472 "Sensor value not reported. Maybe the sensor was unplugged?").IN_SEQUENCE(seq1);
473 psuActive = true;
474 waitForCompletionAndBitMore(seq1);
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100475 }
Tomáš Pecka3f811962023-04-14 10:54:32 +0200476}