blob: b2e249f1681a1734a0dc7b958cfc0d09f984d9d4 [file] [log] [blame]
Tomáš Peckacb7a5f82021-01-20 15:12:00 +01001/*
2 * Copyright (C) 2021 CESNET, https://photonics.cesnet.cz/
3 *
4 * Written by Tomáš Pecka <tomas.pecka@cesnet.cz>
5 *
6 */
7
Tomáš Pecka594a6762021-01-29 11:06:08 +01008#include "Firmware.h"
Tomáš Peckacb7a5f82021-01-20 15:12:00 +01009#include "utils/log.h"
10#include "utils/sysrepo.h"
11
12using namespace std::literals;
13
14namespace {
15
16const auto CZECHLIGHT_SYSTEM_MODULE_NAME = "czechlight-system"s;
17const auto CZECHLIGHT_SYSTEM_FIRMWARE_MODULE_PREFIX = "/"s + CZECHLIGHT_SYSTEM_MODULE_NAME + ":firmware/"s;
Tomáš Pecka92b14e42021-01-27 17:33:11 +010018const auto FIRMWARE_SLOTS = {"rootfs.0"s, "rootfs.1"s};
Tomáš Peckacb7a5f82021-01-20 15:12:00 +010019
20}
21
22namespace velia::system {
23
Tomáš Peckad51d4cb2021-02-03 14:15:49 +010024Firmware::Firmware(std::shared_ptr<::sysrepo::Connection> srConn, sdbus::IConnection& dbusConnectionSignals, sdbus::IConnection& dbusConnectionMethods)
Tomáš Pecka92b14e42021-01-27 17:33:11 +010025 : m_rauc(std::make_shared<RAUC>(
26 dbusConnectionSignals,
27 dbusConnectionMethods,
28 [this](const std::string& operation) {
29 if (operation == "installing") {
30 std::lock_guard<std::mutex> lck(m_mtx);
Tomáš Pecka0eefdf12021-02-17 19:28:13 +010031 m_installMessage = "";
Tomáš Pecka92b14e42021-01-27 17:33:11 +010032 m_installStatus = "in-progress";
33 }
34 },
35 [this](int32_t perc, const std::string& msg) {
36 std::map<std::string, std::string> data = {
37 {CZECHLIGHT_SYSTEM_FIRMWARE_MODULE_PREFIX + "installation/update/message", msg},
38 {CZECHLIGHT_SYSTEM_FIRMWARE_MODULE_PREFIX + "installation/update/progress", std::to_string(perc)},
39 };
40
41 libyang::S_Data_Node dataNode;
42 auto session = std::make_shared<::sysrepo::Session>(m_srConn);
43
Tomáš Pecka498e91c2021-03-02 17:46:47 +010044 utils::valuesToYang(data, {}, session, dataNode);
Tomáš Pecka92b14e42021-01-27 17:33:11 +010045 session->event_notif_send(dataNode);
46 },
47 [this](int32_t retVal, const std::string& lastError) {
48 auto lock = updateSlotStatus();
49 m_installStatus = retVal == 0 ? "succeeded" : "failed";
50 m_installMessage = lastError;
51 }))
52 , m_log(spdlog::get("system"))
53 , m_srConn(std::move(srConn))
Tomáš Peckaaf8f0632021-01-27 16:45:55 +010054 , m_srSessionOps(std::make_shared<::sysrepo::Session>(m_srConn))
55 , m_srSessionRPC(std::make_shared<::sysrepo::Session>(m_srConn))
56 , m_srSubscribeOps(std::make_shared<::sysrepo::Subscribe>(m_srSessionOps))
57 , m_srSubscribeRPC(std::make_shared<::sysrepo::Subscribe>(m_srSessionRPC))
Tomáš Peckacb7a5f82021-01-20 15:12:00 +010058{
Tomáš Peckab78372f2021-04-22 11:09:20 +020059 utils::ensureModuleImplemented(m_srSessionOps, "czechlight-system", "2021-01-13");
60
Tomáš Peckacb7a5f82021-01-20 15:12:00 +010061 {
62 auto raucOperation = m_rauc->operation();
63 auto raucLastError = m_rauc->lastError();
Tomáš Peckaaf8f0632021-01-27 16:45:55 +010064
Tomáš Pecka92b14e42021-01-27 17:33:11 +010065 auto lock = updateSlotStatus();
Tomáš Peckaaf8f0632021-01-27 16:45:55 +010066
67 m_installMessage = raucLastError;
Tomáš Peckacb7a5f82021-01-20 15:12:00 +010068
69 if (raucOperation == "installing") {
Tomáš Peckaaf8f0632021-01-27 16:45:55 +010070 m_installStatus = "in-progress";
Tomáš Peckacb7a5f82021-01-20 15:12:00 +010071 } else if (!raucLastError.empty()) {
Tomáš Peckaaf8f0632021-01-27 16:45:55 +010072 m_installStatus = "failed";
Tomáš Peckacb7a5f82021-01-20 15:12:00 +010073 } else {
Tomáš Peckaaf8f0632021-01-27 16:45:55 +010074 m_installStatus = "none";
Tomáš Peckacb7a5f82021-01-20 15:12:00 +010075 }
Tomáš Peckacb7a5f82021-01-20 15:12:00 +010076 }
77
Tomáš Peckaaf8f0632021-01-27 16:45:55 +010078 m_srSubscribeRPC->rpc_subscribe(
Tomáš Peckacb7a5f82021-01-20 15:12:00 +010079 (CZECHLIGHT_SYSTEM_FIRMWARE_MODULE_PREFIX + "installation/install").c_str(),
Jan Kundrátef2b3802021-02-18 09:57:05 +010080 [this](auto session, auto, auto input, auto, auto, [[maybe_unused]] auto output) {
Tomáš Pecka92b14e42021-01-27 17:33:11 +010081 auto lock = updateSlotStatus();
82
Tomáš Peckacb7a5f82021-01-20 15:12:00 +010083 try {
84 std::string source = input->val(0)->val_to_string();
85 m_rauc->install(source);
86 } catch (sdbus::Error& e) {
87 m_log->warn("RAUC install error: '{}'", e.what());
88 session->set_error(e.getMessage().c_str(), nullptr);
89 return SR_ERR_OPERATION_FAILED;
90 }
91 return SR_ERR_OK;
92 },
93 0,
94 SR_SUBSCR_CTX_REUSE);
Tomáš Peckaaf8f0632021-01-27 16:45:55 +010095
96 m_srSubscribeOps->oper_get_items_subscribe(
97 CZECHLIGHT_SYSTEM_MODULE_NAME.c_str(),
Jan Kundrátef2b3802021-02-18 09:57:05 +010098 [this](auto session, auto, auto, auto, auto, auto& parent) {
Tomáš Peckaaf8f0632021-01-27 16:45:55 +010099 std::map<std::string, std::string> data;
Tomáš Pecka92b14e42021-01-27 17:33:11 +0100100
Tomáš Peckaaf8f0632021-01-27 16:45:55 +0100101 {
Tomáš Pecka92b14e42021-01-27 17:33:11 +0100102 auto lock = updateSlotStatus();
103
104 data.insert(m_slotStatusCache.begin(), m_slotStatusCache.end());
105 data[CZECHLIGHT_SYSTEM_FIRMWARE_MODULE_PREFIX + "installation/status"] = m_installStatus;
106 data[CZECHLIGHT_SYSTEM_FIRMWARE_MODULE_PREFIX + "installation/message"] = m_installMessage;
Tomáš Peckaaf8f0632021-01-27 16:45:55 +0100107 }
108
Tomáš Pecka498e91c2021-03-02 17:46:47 +0100109 utils::valuesToYang(data, {}, session, parent);
Tomáš Peckaaf8f0632021-01-27 16:45:55 +0100110 return SR_ERR_OK;
111 },
112 (CZECHLIGHT_SYSTEM_FIRMWARE_MODULE_PREFIX + "*").c_str(),
113 SR_SUBSCR_PASSIVE | SR_SUBSCR_OPER_MERGE | SR_SUBSCR_CTX_REUSE);
Tomáš Peckacb7a5f82021-01-20 15:12:00 +0100114}
Tomáš Pecka92b14e42021-01-27 17:33:11 +0100115
116/** @brief Updates the slot status cache with the new data obtained via RAUC
117 *
118 * Gets current slot status data from RAUC and updates the local slot status cache if new data are available.
119 * The methods manipulates with the local cache which is shared among multiple thread.
120 *
121 * @return an unique_lock (in locked state) that can be further used to manipulate with the local cache
122 * */
123std::unique_lock<std::mutex> Firmware::updateSlotStatus()
124{
125 std::map<std::string, velia::system::RAUC::SlotProperties> slotStatus;
126
127 try {
128 slotStatus = m_rauc->slotStatus();
129 } catch (sdbus::Error& e) {
130 m_log->warn("Could not fetch RAUC slot status data: {}", e.getMessage());
131 }
132
133 std::unique_lock<std::mutex> lck(m_mtx);
134
135 for (const auto& slotName : FIRMWARE_SLOTS) {
136 if (auto it = slotStatus.find(slotName); it != slotStatus.end()) { // if there is an update for the slot "slotName"
137 const auto& props = it->second;
Tomáš Pecka825542f2021-02-24 14:00:10 +0100138 std::string xpathPrefix;
Tomáš Pecka92b14e42021-01-27 17:33:11 +0100139
Tomáš Pecka825542f2021-02-24 14:00:10 +0100140 // Better be defensive about provided properties. If somebody removes /slot.raucs, RAUC doesn't provide all the data (at least bundle.version and installed.timestamp).
141 if (auto pit = props.find("bootname"); pit != props.end()) {
142 xpathPrefix = CZECHLIGHT_SYSTEM_FIRMWARE_MODULE_PREFIX + "firmware-slot[name='" + std::get<std::string>(pit->second) + "']/";
143 } else {
144 m_log->error("RAUC didn't provide 'bootname' property for slot '{}'. Skipping update for that slot.");
145 continue;
146 }
147
148 for (const auto& [yangKey, raucKey] : {std::pair{"state", "state"}, {"boot-status", "boot-status"}, {"version", "bundle.version"}, {"installed", "installed.timestamp"}}) {
149 if (auto pit = props.find(raucKey); pit != props.end()) {
150 m_slotStatusCache[xpathPrefix + yangKey] = std::get<std::string>(pit->second);
151 } else {
152 m_log->warn("RAUC didn't provide '{}' property for slot '{}'.", raucKey, slotName);
153 }
154 }
Tomáš Pecka92b14e42021-01-27 17:33:11 +0100155 }
156 }
157
158 return lck;
159}
Tomáš Peckacb7a5f82021-01-20 15:12:00 +0100160}