Skip to content

Commit 82c5b74

Browse files
authored
OEM Battery Concurrent Maintenance (#868) (#1219)
This commit is doing Battery Concurrent Maintenance with proper setting functional property after that. * Set Functional property after battery CM (#558) When bmcweb is told the RTC battery is replaced after a CM operation, set the Functional property on its assembly object path back to true before restarting the adcsensor daemon. This does two things: 1) Causes the fault LED to turn off 2) Sets the Redfish representation back to healthy so the web UI doesn't show it has unhealthy it after the CM operation. Of course if adcsensor detects an error again it would go back to unhealthy. Since this is handling the fault LED, the previous code that explicitly turned off the fault LED group was removed. * OEM Battery Concurrent Maintenance For Everest System (#549) The current implementation of this feature supports the redfish OEM ReadyToRemove property for the TOD battery on assembly. The TOD battery is put in the ReadyToRemove state by stopping the adcsensor application. The reverse occurs when the adcsensor application is restarted. The adcsensor is stopped and started by calling systemd. Note that this implementation only works if the adcsensor application handles one ADC sensor. The properties of an assembly have been extended to include `["Oem"]["OpenBMC"]["ReadyToRemove"]` The GET and PATCH methods to the route `/redfish/v1/Chassis/chassis/Assembly` now handle the property `["Oem"]["OpenBMC"]["ReadyToRemove"]` The following commands were tested: ``` curl -k -X GET https://${bmc}/redfish/v1/Chassis/chassis/Assembly ``` ``` curl -k -H "Content-Type: application/json" -X PATCH \ https://${bmc}/redfish/v1/Chassis/chassis/Assembly \ -d '{"Assemblies":[{"MemberId" : "35", "LocationIndicatorActive": true, "Oem": { "OpenBMC": { "ReadyToRemove": true }}}]}' curl -k -H "Content-Type: application/json" -X PATCH \ https://${bmc}/redfish/v1/Chassis/chassis/Assembly \ -d '{"Assemblies":[{"MemberId" : "35", "LocationIndicatorActive": false, "Oem": { "OpenBMC": { "ReadyToRemove": false }}}]}' ``` NOTE: The explicit `-H "Content-Type: application/json"` request header is needed for the OWASP security guideline by 1aa0c2b * The following modifications are included: 1) Currently there is only one assembly that contains an OEM entity. There must exist a method to identify the assembly that contains the TOD battery other than the presence of an OEM property, since more OEM properties could be added on other assemblies. Previously the memberId associated with the battery OEM assembly was used, but the memberId may change. This modification identifies the OEM battery assembly by its name on DBUS. 2)The memberId will not be required in the PATCH request when only the Oem/OpenBMC/ReadyToRemove property for the battery is contained in the PATCH request. 3) If the LocationIndicatorActive LED flag is included in the PATCH request with the Oem/OpenBMC/ReadyToRemove property or without the Oem/OpenBMC/ReadyToRemove property, the memberId will be required. 4) If the LocationIndicatorActive LED flag for any other assembly is included in the PATCH request the associated memberId is required. Signed-off-by: Tom Ippolito <[email protected]> Signed-off-by: Myung Bae <[email protected]>
1 parent 61de051 commit 82c5b74

File tree

5 files changed

+293
-5
lines changed

5 files changed

+293
-5
lines changed

redfish-core/lib/assembly.hpp

Lines changed: 208 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "http_response.hpp"
1010
#include "led.hpp"
1111
#include "logging.hpp"
12+
#include "query.hpp"
1213
#include "registries/privilege_registry.hpp"
1314
#include "utils/chassis_utils.hpp"
1415
#include "utils/dbus_utils.hpp"
@@ -18,8 +19,11 @@
1819
#include <boost/system/error_code.hpp>
1920
#include <boost/url/format.hpp>
2021
#include <nlohmann/json.hpp>
22+
#include <sdbusplus/asio/property.hpp>
2123
#include <sdbusplus/unpack_properties.hpp>
2224

25+
#include <algorithm>
26+
#include <array>
2327
#include <cstddef>
2428
#include <functional>
2529
#include <map>
@@ -133,6 +137,44 @@ void getAssemblyLocationCode(
133137
});
134138
}
135139

140+
inline void afterGetReadyToRemoveOfTodBattery(
141+
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
142+
std::size_t assemblyIndex, const boost::system::error_code& ec,
143+
const dbus::utility::MapperGetObject& /*unused*/)
144+
{
145+
nlohmann::json& assemblyArray = asyncResp->res.jsonValue["Assemblies"];
146+
if (ec)
147+
{
148+
if (ec.value() == boost::system::errc::io_error)
149+
{
150+
// Battery voltage is not on DBUS so ADCSensor is not
151+
// running.
152+
nlohmann::json& oemOpenBMC =
153+
assemblyArray.at(assemblyIndex)["Oem"]["OpenBMC"];
154+
oemOpenBMC["@odata.type"] = "#OpenBMCAssembly.v1_0_0.OpenBMC";
155+
oemOpenBMC["ReadyToRemove"] = true;
156+
return;
157+
}
158+
BMCWEB_LOG_ERROR("DBUS response error {}", ec.value());
159+
messages::internalError(asyncResp->res);
160+
return;
161+
}
162+
nlohmann::json& oemOpenBMC =
163+
assemblyArray.at(assemblyIndex)["Oem"]["OpenBMC"];
164+
oemOpenBMC["@odata.type"] = "#OpenBMCAssembly.v1_0_0.OpenBMC";
165+
oemOpenBMC["ReadyToRemove"] = false;
166+
}
167+
168+
inline void getReadyToRemoveOfTodBattery(
169+
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
170+
std::size_t assemblyIndex)
171+
{
172+
dbus::utility::getDbusObject(
173+
"/xyz/openbmc_project/sensors/voltage/Battery_Voltage", {},
174+
std::bind_front(afterGetReadyToRemoveOfTodBattery, asyncResp,
175+
assemblyIndex));
176+
}
177+
136178
/**
137179
* @brief Get properties for the assemblies associated to given chassis
138180
* @param[in] asyncResp - Shared pointer for asynchronous calls.
@@ -168,6 +210,16 @@ inline void getAssemblyProperties(
168210
tempArray.at(assemblyIndex)["Name"] =
169211
sdbusplus::message::object_path(assembly).filename();
170212

213+
// Handle special case for tod_battery assembly OEM ReadyToRemove
214+
// property NOTE: The following method for the special case of the
215+
// tod_battery ReadyToRemove property only works when there is only ONE
216+
// adcsensor handled by the adcsensor application.
217+
if (sdbusplus::message::object_path(assembly).filename() ==
218+
"tod_battery")
219+
{
220+
getReadyToRemoveOfTodBattery(asyncResp, assemblyIndex);
221+
}
222+
171223
dbus::utility::getDbusObject(
172224
assembly, chassisAssemblyInterfaces,
173225
[asyncResp, assemblyIndex,
@@ -263,6 +315,96 @@ inline void handleChassisAssemblyGet(
263315
});
264316
}
265317

318+
inline void startOrStopADCSensor(
319+
const bool start, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
320+
{
321+
std::string method{"StartUnit"};
322+
if (!start)
323+
{
324+
method = "StopUnit";
325+
}
326+
327+
crow::connections::systemBus->async_method_call(
328+
[asyncResp](const boost::system::error_code& ec) {
329+
if (ec)
330+
{
331+
BMCWEB_LOG_ERROR("Failed to start or stop ADCSensor:{}",
332+
ec.value());
333+
messages::internalError(asyncResp->res);
334+
return;
335+
}
336+
messages::success(asyncResp->res);
337+
},
338+
"org.freedesktop.systemd1", "/org/freedesktop/systemd1",
339+
"org.freedesktop.systemd1.Manager", method,
340+
"xyz.openbmc_project.adcsensor.service", "replace");
341+
}
342+
343+
inline void afterGetDbusObjectDoBatteryCM(
344+
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
345+
const std::string& assembly, const boost::system::error_code& ec,
346+
const dbus::utility::MapperGetObject& object)
347+
{
348+
if (ec)
349+
{
350+
BMCWEB_LOG_ERROR("DBUS response error {}", ec.value());
351+
messages::internalError(asyncResp->res);
352+
return;
353+
}
354+
355+
for (const auto& [serviceName, interfaceList] : object)
356+
{
357+
auto ifaceIt = std::ranges::find(
358+
interfaceList,
359+
"xyz.openbmc_project.State.Decorator.OperationalStatus");
360+
361+
if (ifaceIt == interfaceList.end())
362+
{
363+
continue;
364+
}
365+
366+
sdbusplus::asio::setProperty(
367+
*crow::connections::systemBus, serviceName, assembly,
368+
"xyz.openbmc_project.State.Decorator."
369+
"OperationalStatus",
370+
"Functional", true,
371+
[asyncResp, assembly](const boost::system::error_code& ec2) {
372+
if (ec2)
373+
{
374+
BMCWEB_LOG_ERROR(
375+
"Failed to set functional property on battery: {} ",
376+
ec2.value());
377+
messages::internalError(asyncResp->res);
378+
return;
379+
}
380+
startOrStopADCSensor(true, asyncResp);
381+
});
382+
return;
383+
}
384+
385+
BMCWEB_LOG_ERROR("No OperationalStatus interface on {}", assembly);
386+
messages::internalError(asyncResp->res);
387+
}
388+
389+
inline void doBatteryCM(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
390+
const std::string& assembly, const bool readyToRemove)
391+
{
392+
if (readyToRemove)
393+
{
394+
// Stop the adcsensor service so it doesn't monitor the battery
395+
startOrStopADCSensor(false, asyncResp);
396+
return;
397+
}
398+
399+
// Find the service that has the OperationalStatus iface, set the
400+
// Functional property back to true, and then start the adcsensor service.
401+
std::array<std::string_view, 1> interfaces = {
402+
"xyz.openbmc_project.State.Decorator.OperationalStatus"};
403+
dbus::utility::getDbusObject(
404+
assembly, interfaces,
405+
std::bind_front(afterGetDbusObjectDoBatteryCM, asyncResp, assembly));
406+
}
407+
266408
/**
267409
* @brief Set location indicator for the assemblies associated to given chassis
268410
* @param[in] req - The request data
@@ -295,15 +437,17 @@ inline void setAssemblyLocationIndicators(
295437

296438
std::vector<nlohmann::json> items = std::move(*assemblyData);
297439
std::map<std::string, bool> locationIndicatorActiveMap;
440+
std::map<std::string, nlohmann::json> oemIndicatorMap;
298441

299442
for (auto& item : items)
300443
{
301444
std::optional<std::string> memberId;
302445
std::optional<bool> locationIndicatorActive;
446+
std::optional<nlohmann::json> oem;
303447

304-
if (!json_util::readJson(item, asyncResp->res,
305-
"LocationIndicatorActive",
306-
locationIndicatorActive, "MemberId", memberId))
448+
if (!json_util::readJson(
449+
item, asyncResp->res, "LocationIndicatorActive",
450+
locationIndicatorActive, "MemberId", memberId, "Oem", oem))
307451
{
308452
return;
309453
}
@@ -322,6 +466,20 @@ inline void setAssemblyLocationIndicators(
322466
return;
323467
}
324468
}
469+
if (oem)
470+
{
471+
if (memberId)
472+
{
473+
oemIndicatorMap[*memberId] = *oem;
474+
}
475+
else
476+
{
477+
BMCWEB_LOG_WARNING(
478+
"Property Missing - MemberId must be included with Oem property");
479+
messages::propertyMissing(asyncResp->res, "MemberId");
480+
return;
481+
}
482+
}
325483
}
326484

327485
std::size_t assemblyIndex = 0;
@@ -334,15 +492,61 @@ inline void setAssemblyLocationIndicators(
334492
{
335493
setLocationIndicatorActive(asyncResp, assembly, iter->second);
336494
}
495+
496+
auto iter2 = oemIndicatorMap.find(std::to_string(assemblyIndex));
497+
498+
if (iter2 != oemIndicatorMap.end())
499+
{
500+
std::optional<bool> readytoremove;
501+
if (!json_util::readJson(iter2->second, asyncResp->res,
502+
"OpenBMC/ReadyToRemove", readytoremove))
503+
{
504+
BMCWEB_LOG_WARNING("Property Value Format Error ");
505+
messages::propertyValueFormatError(
506+
asyncResp->res, iter2->second, "OpenBMC/ReadyToRemove");
507+
return;
508+
}
509+
510+
if (!readytoremove)
511+
{
512+
BMCWEB_LOG_WARNING("Property Missing ");
513+
messages::propertyMissing(asyncResp->res,
514+
"OpenBMC/ReadyToRemove");
515+
return;
516+
}
517+
518+
// Handle special case for tod_battery assembly OEM ReadyToRemove
519+
// property. NOTE: The following method for the special case of the
520+
// tod_battery ReadyToRemove property only works when there is only
521+
// ONE adcsensor handled by the adcsensor application.
522+
if (sdbusplus::message::object_path(assembly).filename() ==
523+
"tod_battery")
524+
{
525+
doBatteryCM(asyncResp, assembly, readytoremove.value());
526+
}
527+
else
528+
{
529+
BMCWEB_LOG_WARNING(
530+
"Property Unknown: ReadyToRemove on Assembly with MemberID: {}",
531+
assemblyIndex);
532+
messages::propertyUnknown(asyncResp->res, "ReadyToRemove");
533+
return;
534+
}
535+
}
337536
assemblyIndex++;
338537
}
339538
}
340539

341540
inline void handleChassisAssemblyPatch(
342-
App& /*unused*/, const crow::Request& req,
541+
App& app, const crow::Request& req,
343542
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
344543
const std::string& chassisID)
345544
{
545+
if (!redfish::setUpRedfishRoute(app, req, asyncResp))
546+
{
547+
return;
548+
}
549+
346550
BMCWEB_LOG_DEBUG("Patch chassis path");
347551

348552
chassis_utils::getChassisAssembly(
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
3+
<edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Core.V1.xml">
4+
<edmx:Include Namespace="Org.OData.Core.V1" Alias="OData"/>
5+
</edmx:Reference>
6+
<edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/Assembly_v1.xml">
7+
<edmx:Include Namespace="Assembly"/>
8+
<edmx:Include Namespace="Assembly.v1_0_0"/>
9+
</edmx:Reference>
10+
<edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/RedfishExtensions_v1.xml">
11+
<edmx:Include Namespace="RedfishExtensions.v1_0_0" Alias="Redfish"/>
12+
</edmx:Reference>
13+
<edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/Resource_v1.xml">
14+
<edmx:Include Namespace="Resource"/>
15+
<edmx:Include Namespace="Resource.v1_0_0"/>
16+
</edmx:Reference>
17+
<edmx:DataServices>
18+
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="OpenBMCAssembly">
19+
<Annotation Term="Redfish.OwningEntity" String="OpenBMC"/>
20+
</Schema>
21+
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="OpenBMCAssembly.v1_0_0">
22+
<ComplexType Name="Oem" BaseType="Resource.OemObject">
23+
<Annotation Term="OData.AdditionalProperties" Bool="true"/>
24+
<Annotation Term="OData.Description" String="OpenBMCAssembly Oem properties."/>
25+
<Annotation Term="OData.AutoExpand"/>
26+
<Property Name="OpenBMC" Type="OpenBMCAssembly.v1_0_0.OpenBMC"/>
27+
</ComplexType>
28+
<ComplexType Name="OpenBMC" BaseType="Resource.OemObject">
29+
<Annotation Term="OData.AdditionalProperties" Bool="true"/>
30+
<Annotation Term="OData.Description" String="Oem properties for OpenBMC."/>
31+
<Annotation Term="OData.AutoExpand"/>
32+
<Property Name="ReadyToRemove" Type="Edm.Boolean"/>
33+
<Annotation Term="OData.Description" String="Indicates if the assembly is ready to be removed."/>
34+
<Annotation Term="OData.LongDescription" String="Indicates if the system is prepared for the assembly to be removed."/>
35+
</ComplexType>
36+
</Schema>
37+
</edmx:DataServices>
38+
</edmx:Edmx>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"$id": "https://github.com/ibm-openbmc/bmcweb/tree/HEAD/redfish-core/schema/oem/openbmc/json-schema/OpenBMCAssembly.json",
3+
"$schema": "http://redfish.dmtf.org/schemas/v1/redfish-schema-v1.json",
4+
"copyright": "Copyright 2025 OpenBMC.",
5+
"definitions": {},
6+
"owningEntity": "OpenBMC",
7+
"title": "#OpenBMCAssembly"
8+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"$id": "http://redfish.dmtf.org/schemas/v1/OpenBMCAssembly.v1_0_0.json",
3+
"$schema": "http://redfish.dmtf.org/schemas/v1/redfish-schema-v1.json",
4+
"copyright": "Copyright 2025 OpenBMC.",
5+
"definitions": {
6+
"OpenBMC": {
7+
"additionalProperties": false,
8+
"description": "An indication of whether the system is prepared for the assembly to be removed.",
9+
"parameters": {},
10+
"patternProperties": {
11+
"^([a-zA-Z_][a-zA-Z0-9_]*)?@(odata|Redfish|Message)\\.[a-zA-Z_][a-zA-Z0-9_]*$": {
12+
"description": "This property shall specify a valid odata or Redfish property.",
13+
"type": [
14+
"array",
15+
"boolean",
16+
"integer",
17+
"number",
18+
"null",
19+
"object",
20+
"string"
21+
]
22+
}
23+
},
24+
"properties": {
25+
"ReadyToRemove": {
26+
"description": "An indication of whether the system is prepared for an assembly to be removed.",
27+
"longDescription": "This property shall indicate whether the system is ready for the assembly to be changed. Setting the value to `true` shall cause the service to perform appropriate actions to allow the assembly to be removed. Setting the value to `false` shall cause the service to perform appropriate actions to allow the assembly to be installed.",
28+
"readonly": false,
29+
"type": ["boolean", "null"],
30+
"versionAdded": "v1_0_0"
31+
}
32+
},
33+
"type": "object"
34+
}
35+
},
36+
"OwningEntity": "OpenBMC",
37+
"title": "#OpenBMCAssembly.v1_0_0"
38+
}

redfish-core/schema/oem/openbmc/meson.build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ foreach option_key, schema : schemas
2323
endforeach
2424

2525
# Additional IBM schemas that should be installed
26-
ibm_schemas = ['OpenBMCMessage']
26+
ibm_schemas = ['OpenBMCAssembly', 'OpenBMCMessage']
2727

2828
foreach schema : ibm_schemas
2929
install_data(

0 commit comments

Comments
 (0)