Add support for POST on TriggersCollection
Added POST method on /redfish/v1/TelemetryService/Triggers uri, which
creates new trigger in telemetry service, by using dbus call AddTrigger.
By DMTF, most of the properties are not required, and as such are
treated as optional. Some values can be deduced from others (like
'MetricType', depending on 'DiscreteTriggers' or 'NumericThresholds').
All properties provided in POST body by user will be verified against
each other, and errors will be raised. Few examples of such situations:
- 'MetricType' is set to 'Discrete' but 'NumericThresholds' was passed.
- 'MetricType' is set to 'Numeric' but "DiscreteTriggers' or
'DiscreteTriggerCondition' were passed
- 'DiscreteTriggerCondition' is set to 'Specified' but
'DiscreteTriggers' is an empty array or was not passed.
- 'DiscreteTriggerCondition' is set to 'Changed' but 'DiscreteTriggers'
is passed and is not an empty array.
Example 1 – Trigger with discrete values:
```
{
"Id": "TestTrigger",
"MetricType": "Discrete",
"TriggerActions": [
"RedfishEvent"
],
"DiscreteTriggerCondition": "Specified",
"DiscreteTriggers": [
{
"Value": "55.88",
"DwellTime": "PT0.001S",
"Severity": "Warning"
},
{
"Name": "My discrete trigger",
"Value": "55.88",
"DwellTime": "PT0.001S",
"Severity": "OK"
},
{
"Value": "55.88",
"DwellTime": "PT0.001S",
"Severity": "Critical"
}
],
"MetricProperties": [
"/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/0/Reading"
],
"Links": {
"MetricReportDefinitions": []
}
}
Example 2 – trigger with numeric threshold:
{
"Id": "TestTrigger2",
"Name": "My Numeric Trigger",
"MetricType": "Numeric",
"TriggerActions": [
"RedfishEvent",
"RedfishMetricReport"
],
"NumericThresholds": {
"UpperCritical": {
"Reading": 50,
"Activation": "Increasing",
"DwellTime": "PT0.001S"
},
"UpperWarning": {
"Reading": 48.1,
"Activation": "Increasing",
"DwellTime": "PT0.004S"
}
},
"MetricProperties": [
"/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/0/Reading",
"/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/17/Reading"
],
"Links": {
"MetricReportDefinitions": [
"/redfish/v1/TelemetryService/MetricReportDefinitions/PowerMetrics",
"/redfish/v1/TelemetryService/MetricReportDefinitions/PowerMetricStats",
"/redfish/v1/TelemetryService/MetricReportDefinitions/PlatformPowerUsage"
]
}
}
```
Tested:
- Triggers were successfully created with above example message bodies.
This can be checked by calling:
'busctl tree xyz.openbmc_project.Telemetry'.
- Expected errors were returned for messages with incorrect or mutually
exclusive properties and incorrect values.
- Redfish service validator is passing.
Signed-off-by: Szymon Dompke <szymon.dompke@intel.com>
Change-Id: Ief8c76de8aa660ae0d2dbe4610c26a28186a290a
diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp
index fc045f6..58ab1c9 100644
--- a/redfish-core/include/utils/telemetry_utils.hpp
+++ b/redfish-core/include/utils/telemetry_utils.hpp
@@ -34,6 +34,25 @@
return {triggersPath / id};
}
+inline std::optional<std::string>
+ getTriggerIdFromDbusPath(const std::string& dbusPath)
+{
+ sdbusplus::message::object_path converted(dbusPath);
+
+ if (converted.parent_path() !=
+ "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService")
+ {
+ return std::nullopt;
+ }
+
+ const std::string& id = converted.filename();
+ if (id.empty())
+ {
+ return std::nullopt;
+ }
+ return id;
+}
+
struct IncorrectMetricUri
{
std::string uri;
diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp
index 6a03b7f..fdd86d4 100644
--- a/redfish-core/lib/metric_report_definition.hpp
+++ b/redfish-core/lib/metric_report_definition.hpp
@@ -743,6 +743,44 @@
};
} // namespace telemetry
+inline void handleMetricReportDelete(
+ App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
+
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+
+ const std::string reportPath = telemetry::getDbusReportPath(id);
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp, id](const boost::system::error_code& ec) {
+ /*
+ * boost::system::errc and std::errc are missing value
+ * for EBADR error that is defined in Linux.
+ */
+ if (ec.value() == EBADR)
+ {
+ messages::resourceNotFound(asyncResp->res, "MetricReportDefinition",
+ id);
+ return;
+ }
+
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ asyncResp->res.result(boost::beast::http::status::no_content);
+ },
+ telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete",
+ "Delete");
+}
+
inline void requestRoutesMetricReportDefinitionCollection(App& app)
{
BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
@@ -859,42 +897,6 @@
"/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
.privileges(redfish::privileges::deleteMetricReportDefinitionCollection)
.methods(boost::beast::http::verb::delete_)(
- [&app](const crow::Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const std::string& id)
-
- {
- if (!redfish::setUpRedfishRoute(app, req, asyncResp))
- {
- return;
- }
-
- const std::string reportPath = telemetry::getDbusReportPath(id);
-
- crow::connections::systemBus->async_method_call(
- [asyncResp, id](const boost::system::error_code& ec) {
- /*
- * boost::system::errc and std::errc are missing value
- * for EBADR error that is defined in Linux.
- */
- if (ec.value() == EBADR)
- {
- messages::resourceNotFound(asyncResp->res,
- "MetricReportDefinition", id);
- return;
- }
-
- if (ec)
- {
- BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
- messages::internalError(asyncResp->res);
- return;
- }
-
- asyncResp->res.result(boost::beast::http::status::no_content);
- },
- telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete",
- "Delete");
- });
+ std::bind_front(handleMetricReportDelete, std::ref(app)));
}
} // namespace redfish
diff --git a/redfish-core/lib/trigger.hpp b/redfish-core/lib/trigger.hpp
index 68d6dd2..33e3bde 100644
--- a/redfish-core/lib/trigger.hpp
+++ b/redfish-core/lib/trigger.hpp
@@ -1,8 +1,12 @@
#pragma once
#include "app.hpp"
+#include "generated/enums/resource.hpp"
+#include "generated/enums/triggers.hpp"
#include "query.hpp"
#include "registries/privilege_registry.hpp"
+#include "sensors.hpp"
+#include "utility.hpp"
#include "utils/collection.hpp"
#include "utils/dbus_utils.hpp"
#include "utils/telemetry_utils.hpp"
@@ -31,6 +35,10 @@
using DiscreteThresholdParams =
std::tuple<std::string, std::string, uint64_t, std::string>;
+using TriggerThresholdParams =
+ std::variant<std::vector<NumericThresholdParams>,
+ std::vector<DiscreteThresholdParams>>;
+
using TriggerThresholdParamsExt =
std::variant<std::monostate, std::vector<NumericThresholdParams>,
std::vector<DiscreteThresholdParams>>;
@@ -43,48 +51,676 @@
TriggerSensorsParams, std::vector<std::string>,
std::vector<sdbusplus::message::object_path>>;
-inline std::optional<std::string>
- getRedfishFromDbusAction(const std::string& dbusAction)
+inline triggers::TriggerActionEnum
+ toRedfishTriggerAction(std::string_view dbusValue)
{
- std::optional<std::string> redfishAction = std::nullopt;
- if (dbusAction == "UpdateReport")
+ if (dbusValue ==
+ "xyz.openbmc_project.Telemetry.Trigger.TriggerAction.UpdateReport")
{
- redfishAction = "RedfishMetricReport";
+ return triggers::TriggerActionEnum::RedfishMetricReport;
}
- if (dbusAction == "LogToRedfishEventLog")
+ if (dbusValue ==
+ "xyz.openbmc_project.Telemetry.Trigger.TriggerAction.LogToRedfishEventLog")
{
- redfishAction = "RedfishEvent";
+ return triggers::TriggerActionEnum::RedfishEvent;
}
- if (dbusAction == "LogToJournal")
+ if (dbusValue ==
+ "xyz.openbmc_project.Telemetry.Trigger.TriggerAction.LogToJournal")
{
- redfishAction = "LogToLogService";
+ return triggers::TriggerActionEnum::LogToLogService;
}
- return redfishAction;
+ return triggers::TriggerActionEnum::Invalid;
}
-inline std::optional<std::vector<std::string>>
+inline std::string toDbusTriggerAction(std::string_view redfishValue)
+{
+ if (redfishValue == "RedfishMetricReport")
+ {
+ return "xyz.openbmc_project.Telemetry.Trigger.TriggerAction.UpdateReport";
+ }
+ if (redfishValue == "RedfishEvent")
+ {
+ return "xyz.openbmc_project.Telemetry.Trigger.TriggerAction.LogToRedfishEventLog";
+ }
+ if (redfishValue == "LogToLogService")
+ {
+ return "xyz.openbmc_project.Telemetry.Trigger.TriggerAction.LogToJournal";
+ }
+ return "";
+}
+
+inline std::string toDbusSeverity(std::string_view redfishValue)
+{
+ if (redfishValue == "OK")
+ {
+ return "xyz.openbmc_project.Telemetry.Trigger.Severity.OK";
+ }
+ if (redfishValue == "Warning")
+ {
+ return "xyz.openbmc_project.Telemetry.Trigger.Severity.Warning";
+ }
+ if (redfishValue == "Critical")
+ {
+ return "xyz.openbmc_project.Telemetry.Trigger.Severity.Critical";
+ }
+ return "";
+}
+
+inline resource::Health toRedfishSeverity(std::string_view dbusValue)
+{
+ if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Severity.OK")
+ {
+ return resource::Health::OK;
+ }
+ if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Severity.Warning")
+ {
+ return resource::Health::Warning;
+ }
+ if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Severity.Critical")
+ {
+ return resource::Health::Critical;
+ }
+ return resource::Health::Invalid;
+}
+
+inline std::string toDbusThresholdName(std::string_view redfishValue)
+{
+ if (redfishValue == "UpperCritical")
+ {
+ return "xyz.openbmc_project.Telemetry.Trigger.Type.UpperCritical";
+ }
+
+ if (redfishValue == "LowerCritical")
+ {
+ return "xyz.openbmc_project.Telemetry.Trigger.Type.LowerCritical";
+ }
+
+ if (redfishValue == "UpperWarning")
+ {
+ return "xyz.openbmc_project.Telemetry.Trigger.Type.UpperWarning";
+ }
+
+ if (redfishValue == "LowerWarning")
+ {
+ return "xyz.openbmc_project.Telemetry.Trigger.Type.LowerWarning";
+ }
+
+ return "";
+}
+
+inline std::string toRedfishThresholdName(std::string_view dbusValue)
+{
+ if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Type.UpperCritical")
+ {
+ return "UpperCritical";
+ }
+
+ if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Type.LowerCritical")
+ {
+ return "LowerCritical";
+ }
+
+ if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Type.UpperWarning")
+ {
+ return "UpperWarning";
+ }
+
+ if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Type.LowerWarning")
+ {
+ return "LowerWarning";
+ }
+
+ return "";
+}
+
+inline std::string toDbusActivation(std::string_view redfishValue)
+{
+ if (redfishValue == "Either")
+ {
+ return "xyz.openbmc_project.Telemetry.Trigger.Direction.Either";
+ }
+
+ if (redfishValue == "Decreasing")
+ {
+ return "xyz.openbmc_project.Telemetry.Trigger.Direction.Decreasing";
+ }
+
+ if (redfishValue == "Increasing")
+ {
+ return "xyz.openbmc_project.Telemetry.Trigger.Direction.Increasing";
+ }
+
+ return "";
+}
+
+inline triggers::ThresholdActivation
+ toRedfishActivation(std::string_view dbusValue)
+{
+ if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Direction.Either")
+ {
+ return triggers::ThresholdActivation::Either;
+ }
+
+ if (dbusValue ==
+ "xyz.openbmc_project.Telemetry.Trigger.Direction.Decreasing")
+ {
+ return triggers::ThresholdActivation::Decreasing;
+ }
+
+ if (dbusValue ==
+ "xyz.openbmc_project.Telemetry.Trigger.Direction.Increasing")
+ {
+ return triggers::ThresholdActivation::Increasing;
+ }
+
+ return triggers::ThresholdActivation::Invalid;
+}
+
+enum class MetricType
+{
+ Discrete,
+ Numeric
+};
+
+enum class DiscreteCondition
+{
+ Specified,
+ Changed
+};
+
+struct Context
+{
+ std::string id;
+ std::string name;
+ std::vector<std::string> actions;
+ std::vector<std::pair<sdbusplus::message::object_path, std::string>>
+ sensors;
+ std::vector<sdbusplus::message::object_path> reports;
+ TriggerThresholdParams thresholds;
+
+ std::optional<DiscreteCondition> discreteCondition;
+ std::optional<MetricType> metricType;
+ std::optional<std::vector<std::string>> metricProperties;
+};
+
+inline std::optional<sdbusplus::message::object_path>
+ getReportPathFromReportDefinitionUri(const std::string& uri)
+{
+ boost::urls::result<boost::urls::url_view> parsed =
+ boost::urls::parse_relative_ref(uri);
+
+ if (!parsed)
+ {
+ return std::nullopt;
+ }
+
+ std::string id;
+ if (!crow::utility::readUrlSegments(
+ *parsed, "redfish", "v1", "TelemetryService",
+ "MetricReportDefinitions", std::ref(id)))
+ {
+ return std::nullopt;
+ }
+
+ return sdbusplus::message::object_path(
+ "/xyz/openbmc_project/Telemetry/Reports") /
+ "TelemetryService" / id;
+}
+
+inline std::optional<MetricType> getMetricType(const std::string& metricType)
+{
+ if (metricType == "Discrete")
+ {
+ return MetricType::Discrete;
+ }
+ if (metricType == "Numeric")
+ {
+ return MetricType::Numeric;
+ }
+ return std::nullopt;
+}
+
+inline std::optional<DiscreteCondition>
+ getDiscreteCondition(const std::string& discreteTriggerCondition)
+{
+ if (discreteTriggerCondition == "Specified")
+ {
+ return DiscreteCondition::Specified;
+ }
+ if (discreteTriggerCondition == "Changed")
+ {
+ return DiscreteCondition::Changed;
+ }
+ return std::nullopt;
+}
+
+inline bool parseNumericThresholds(crow::Response& res,
+ nlohmann::json& numericThresholds,
+ Context& ctx)
+{
+ nlohmann::json::object_t* obj =
+ numericThresholds.get_ptr<nlohmann::json::object_t*>();
+ if (obj == nullptr)
+ {
+ messages::propertyValueTypeError(res, numericThresholds.dump(),
+ "NumericThresholds");
+ return false;
+ }
+
+ std::vector<NumericThresholdParams> parsedParams;
+ parsedParams.reserve(numericThresholds.size());
+
+ for (auto& key : *obj)
+ {
+ std::string dbusThresholdName = toDbusThresholdName(key.first);
+ if (dbusThresholdName.empty())
+ {
+ messages::propertyUnknown(res, key.first);
+ return false;
+ }
+
+ double reading = 0.0;
+ std::string activation;
+ std::string dwellTimeStr;
+
+ if (!json_util::readJson(key.second, res, "Reading", reading,
+ "Activation", activation, "DwellTime",
+ dwellTimeStr))
+ {
+ return false;
+ }
+
+ std::string dbusActivation = toDbusActivation(activation);
+
+ if (dbusActivation.empty())
+ {
+ messages::propertyValueIncorrect(res, "Activation", activation);
+ return false;
+ }
+
+ std::optional<std::chrono::milliseconds> dwellTime =
+ time_utils::fromDurationString(dwellTimeStr);
+ if (!dwellTime)
+ {
+ messages::propertyValueIncorrect(res, "DwellTime", dwellTimeStr);
+ return false;
+ }
+
+ parsedParams.emplace_back(dbusThresholdName,
+ static_cast<uint64_t>(dwellTime->count()),
+ dbusActivation, reading);
+ }
+
+ ctx.thresholds = std::move(parsedParams);
+ return true;
+}
+
+inline bool parseDiscreteTriggers(
+ crow::Response& res,
+ std::optional<std::vector<nlohmann::json>>& discreteTriggers, Context& ctx)
+{
+ std::vector<DiscreteThresholdParams> parsedParams;
+ if (!discreteTriggers)
+ {
+ ctx.thresholds = std::move(parsedParams);
+ return true;
+ }
+
+ parsedParams.reserve(discreteTriggers->size());
+ for (nlohmann::json& thresholdInfo : *discreteTriggers)
+ {
+ std::optional<std::string> name = "";
+ std::string value;
+ std::string dwellTimeStr;
+ std::string severity;
+
+ if (!json_util::readJson(thresholdInfo, res, "Name", name, "Value",
+ value, "DwellTime", dwellTimeStr, "Severity",
+ severity))
+ {
+ return false;
+ }
+
+ std::optional<std::chrono::milliseconds> dwellTime =
+ time_utils::fromDurationString(dwellTimeStr);
+ if (!dwellTime)
+ {
+ messages::propertyValueIncorrect(res, "DwellTime", dwellTimeStr);
+ return false;
+ }
+
+ std::string dbusSeverity = toDbusSeverity(severity);
+ if (dbusSeverity.empty())
+ {
+ messages::propertyValueIncorrect(res, "Severity", severity);
+ return false;
+ }
+
+ parsedParams.emplace_back(*name, dbusSeverity,
+ static_cast<uint64_t>(dwellTime->count()),
+ value);
+ }
+
+ ctx.thresholds = std::move(parsedParams);
+ return true;
+}
+
+inline bool parseTriggerThresholds(
+ crow::Response& res,
+ std::optional<std::vector<nlohmann::json>>& discreteTriggers,
+ std::optional<nlohmann::json>& numericThresholds, Context& ctx)
+{
+ if (discreteTriggers && numericThresholds)
+ {
+ messages::propertyValueConflict(res, "DiscreteTriggers",
+ "NumericThresholds");
+ messages::propertyValueConflict(res, "NumericThresholds",
+ "DiscreteTriggers");
+ return false;
+ }
+
+ if (ctx.discreteCondition)
+ {
+ if (numericThresholds)
+ {
+ messages::propertyValueConflict(res, "DiscreteTriggerCondition",
+ "NumericThresholds");
+ messages::propertyValueConflict(res, "NumericThresholds",
+ "DiscreteTriggerCondition");
+ return false;
+ }
+ }
+
+ if (ctx.metricType)
+ {
+ if (*ctx.metricType == MetricType::Discrete && numericThresholds)
+ {
+ messages::propertyValueConflict(res, "NumericThresholds",
+ "MetricType");
+ return false;
+ }
+ if (*ctx.metricType == MetricType::Numeric && discreteTriggers)
+ {
+ messages::propertyValueConflict(res, "DiscreteTriggers",
+ "MetricType");
+ return false;
+ }
+ if (*ctx.metricType == MetricType::Numeric && ctx.discreteCondition)
+ {
+ messages::propertyValueConflict(res, "DiscreteTriggers",
+ "DiscreteTriggerCondition");
+ return false;
+ }
+ }
+
+ if (discreteTriggers || ctx.discreteCondition ||
+ (ctx.metricType && *ctx.metricType == MetricType::Discrete))
+ {
+ if (ctx.discreteCondition)
+ {
+ if (*ctx.discreteCondition == DiscreteCondition::Specified &&
+ !discreteTriggers)
+ {
+ messages::createFailedMissingReqProperties(res,
+ "DiscreteTriggers");
+ return false;
+ }
+ if (discreteTriggers &&
+ ((*ctx.discreteCondition == DiscreteCondition::Specified &&
+ discreteTriggers->empty()) ||
+ (*ctx.discreteCondition == DiscreteCondition::Changed &&
+ !discreteTriggers->empty())))
+ {
+ messages::propertyValueConflict(res, "DiscreteTriggers",
+ "DiscreteTriggerCondition");
+ return false;
+ }
+ }
+ if (!parseDiscreteTriggers(res, discreteTriggers, ctx))
+ {
+ return false;
+ }
+ }
+ else if (numericThresholds)
+ {
+ if (!parseNumericThresholds(res, *numericThresholds, ctx))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ messages::createFailedMissingReqProperties(
+ res, "'DiscreteTriggers', 'NumericThresholds', "
+ "'DiscreteTriggerCondition' or 'MetricType'");
+ return false;
+ }
+ return true;
+}
+
+inline bool parseLinks(crow::Response& res, nlohmann::json& links, Context& ctx)
+{
+ if (links.empty())
+ {
+ return true;
+ }
+
+ std::optional<std::vector<std::string>> metricReportDefinitions;
+ if (!json_util::readJson(links, res, "MetricReportDefinitions",
+ metricReportDefinitions))
+ {
+ return false;
+ }
+
+ if (metricReportDefinitions)
+ {
+ ctx.reports.reserve(metricReportDefinitions->size());
+ for (std::string& reportDefinionUri : *metricReportDefinitions)
+ {
+ std::optional<sdbusplus::message::object_path> reportPath =
+ getReportPathFromReportDefinitionUri(reportDefinionUri);
+ if (!reportPath)
+ {
+ messages::propertyValueIncorrect(res, "MetricReportDefinitions",
+ reportDefinionUri);
+ return false;
+ }
+ ctx.reports.emplace_back(*reportPath);
+ }
+ }
+ return true;
+}
+
+inline bool parseMetricProperties(crow::Response& res, Context& ctx)
+{
+ if (!ctx.metricProperties)
+ {
+ return true;
+ }
+
+ ctx.sensors.reserve(ctx.metricProperties->size());
+
+ size_t uriIdx = 0;
+ for (const std::string& uriStr : *ctx.metricProperties)
+ {
+ boost::urls::result<boost::urls::url_view> uri =
+ boost::urls::parse_relative_ref(uriStr);
+ if (!uri)
+ {
+ messages::propertyValueIncorrect(
+ res, "MetricProperties/" + std::to_string(uriIdx), uriStr);
+ return false;
+ }
+ std::string chassisName;
+ std::string sensorName;
+ if (!crow::utility::readUrlSegments(*uri, "redfish", "v1", "Chassis",
+ std::ref(chassisName), "Sensors",
+ std::ref(sensorName)))
+ {
+ messages::propertyValueIncorrect(
+ res, "MetricProperties/" + std::to_string(uriIdx), uriStr);
+ return false;
+ }
+
+ std::pair<std::string, std::string> split =
+ splitSensorNameAndType(sensorName);
+ if (split.first.empty() || split.second.empty())
+ {
+ messages::propertyValueIncorrect(
+ res, "MetricProperties/" + std::to_string(uriIdx), uriStr);
+ return false;
+ }
+
+ std::string sensorPath = "/xyz/openbmc_project/sensors/" + split.first +
+ '/' + split.second;
+
+ ctx.sensors.emplace_back(sensorPath, uriStr);
+ uriIdx++;
+ }
+ return true;
+}
+
+inline bool parsePostTriggerParams(crow::Response& res,
+ const crow::Request& req, Context& ctx)
+{
+ std::optional<std::string> id = "";
+ std::optional<std::string> name = "";
+ std::optional<std::string> metricType;
+ std::optional<std::vector<std::string>> triggerActions;
+ std::optional<std::string> discreteTriggerCondition;
+ std::optional<std::vector<nlohmann::json>> discreteTriggers;
+ std::optional<nlohmann::json> numericThresholds;
+ std::optional<nlohmann::json> links;
+ if (!json_util::readJsonPatch(
+ req, res, "Id", id, "Name", name, "MetricType", metricType,
+ "TriggerActions", triggerActions, "DiscreteTriggerCondition",
+ discreteTriggerCondition, "DiscreteTriggers", discreteTriggers,
+ "NumericThresholds", numericThresholds, "MetricProperties",
+ ctx.metricProperties, "Links", links))
+ {
+ return false;
+ }
+
+ ctx.id = *id;
+ ctx.name = *name;
+
+ if (metricType)
+ {
+ if (!(ctx.metricType = getMetricType(*metricType)))
+ {
+ messages::propertyValueIncorrect(res, "MetricType", *metricType);
+ return false;
+ }
+ }
+
+ if (discreteTriggerCondition)
+ {
+ if (!(ctx.discreteCondition =
+ getDiscreteCondition(*discreteTriggerCondition)))
+ {
+ messages::propertyValueIncorrect(res, "DiscreteTriggerCondition",
+ *discreteTriggerCondition);
+ return false;
+ }
+ }
+
+ if (triggerActions)
+ {
+ ctx.actions.reserve(triggerActions->size());
+ for (const std::string& action : *triggerActions)
+ {
+ std::string dbusAction = toDbusTriggerAction(action);
+
+ if (dbusAction.empty())
+ {
+ messages::propertyValueNotInList(res, action, "TriggerActions");
+ return false;
+ }
+
+ ctx.actions.emplace_back(dbusAction);
+ }
+ }
+ if (!parseMetricProperties(res, ctx))
+ {
+ return false;
+ }
+
+ if (!parseTriggerThresholds(res, discreteTriggers, numericThresholds, ctx))
+ {
+ return false;
+ }
+
+ if (links)
+ {
+ if (!parseLinks(res, *links, ctx))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline void afterCreateTrigger(
+ const boost::system::error_code& ec, const std::string& dbusPath,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
+{
+ if (ec == boost::system::errc::file_exists)
+ {
+ messages::resourceAlreadyExists(asyncResp->res, "Trigger", "Id", id);
+ return;
+ }
+ if (ec == boost::system::errc::too_many_files_open)
+ {
+ messages::createLimitReachedForResource(asyncResp->res);
+ return;
+ }
+ if (ec)
+ {
+ messages::internalError(asyncResp->res);
+ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
+ return;
+ }
+
+ const std::optional<std::string>& triggerId =
+ getTriggerIdFromDbusPath(dbusPath);
+ if (!triggerId)
+ {
+ messages::internalError(asyncResp->res);
+ BMCWEB_LOG_ERROR << "Unknown data returned by "
+ "AddTrigger DBus method";
+ return;
+ }
+
+ messages::created(asyncResp->res);
+ boost::urls::url locationUrl = boost::urls::format(
+ "/redfish/v1/TelemetryService/Triggers/{}", *triggerId);
+ asyncResp->res.addHeader("Location", locationUrl.buffer());
+}
+
+inline std::optional<nlohmann::json::array_t>
getTriggerActions(const std::vector<std::string>& dbusActions)
{
- std::vector<std::string> triggerActions;
+ nlohmann::json::array_t triggerActions;
for (const std::string& dbusAction : dbusActions)
{
- std::optional<std::string> redfishAction =
- getRedfishFromDbusAction(dbusAction);
+ triggers::TriggerActionEnum redfishAction =
+ toRedfishTriggerAction(dbusAction);
- if (!redfishAction)
+ if (redfishAction == triggers::TriggerActionEnum::Invalid)
{
return std::nullopt;
}
- triggerActions.push_back(*redfishAction);
+ triggerActions.emplace_back(redfishAction);
}
- return {std::move(triggerActions)};
+ return triggerActions;
}
inline std::optional<nlohmann::json::array_t>
getDiscreteTriggers(const TriggerThresholdParamsExt& thresholdParams)
{
+ nlohmann::json::array_t triggers;
const std::vector<DiscreteThresholdParams>* discreteParams =
std::get_if<std::vector<DiscreteThresholdParams>>(&thresholdParams);
@@ -93,7 +729,6 @@
return std::nullopt;
}
- nlohmann::json::array_t triggers;
for (const auto& [name, severity, dwellTime, value] : *discreteParams)
{
std::optional<std::string> duration =
@@ -105,18 +740,19 @@
}
nlohmann::json::object_t trigger;
trigger["Name"] = name;
- trigger["Severity"] = severity;
+ trigger["Severity"] = toRedfishSeverity(severity);
trigger["DwellTime"] = *duration;
trigger["Value"] = value;
triggers.emplace_back(std::move(trigger));
}
- return {std::move(triggers)};
+ return triggers;
}
inline std::optional<nlohmann::json>
getNumericThresholds(const TriggerThresholdParamsExt& thresholdParams)
{
+ nlohmann::json::object_t thresholds;
const std::vector<NumericThresholdParams>* numericParams =
std::get_if<std::vector<NumericThresholdParams>>(&thresholdParams);
@@ -125,7 +761,6 @@
return std::nullopt;
}
- nlohmann::json::object_t thresholds;
for (const auto& [type, dwellTime, activation, reading] : *numericParams)
{
std::optional<std::string> duration =
@@ -135,13 +770,13 @@
{
return std::nullopt;
}
- nlohmann::json& threshold = thresholds[type];
+ nlohmann::json& threshold = thresholds[toRedfishThresholdName(type)];
threshold["Reading"] = reading;
- threshold["Activation"] = activation;
+ threshold["Activation"] = toRedfishActivation(activation);
threshold["DwellTime"] = *duration;
}
- return {std::move(thresholds)};
+ return thresholds;
}
inline std::optional<nlohmann::json> getMetricReportDefinitions(
@@ -208,7 +843,7 @@
if (triggerActions != nullptr)
{
- std::optional<std::vector<std::string>> redfishTriggerActions =
+ std::optional<nlohmann::json::array_t> redfishTriggerActions =
getTriggerActions(*triggerActions);
if (!redfishTriggerActions)
{
@@ -216,7 +851,7 @@
<< "Property TriggerActions is invalid in Trigger: " << id;
return false;
}
- json["TriggerActions"] = *triggerActions;
+ json["TriggerActions"] = *redfishTriggerActions;
}
if (reports != nullptr)
@@ -290,6 +925,32 @@
return true;
}
+inline void handleTriggerCollectionPost(
+ App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+
+ telemetry::Context ctx;
+ if (!telemetry::parsePostTriggerParams(asyncResp->res, req, ctx))
+ {
+ return;
+ }
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp, id = ctx.id](const boost::system::error_code& ec,
+ const std::string& dbusPath) {
+ afterCreateTrigger(ec, dbusPath, asyncResp, id);
+ },
+ service, "/xyz/openbmc_project/Telemetry/Triggers",
+ "xyz.openbmc_project.Telemetry.TriggerManager", "AddTrigger",
+ "TelemetryService/" + ctx.id, ctx.name, ctx.actions, ctx.sensors,
+ ctx.reports, ctx.thresholds);
+}
+
} // namespace telemetry
inline void requestRoutesTriggerCollection(App& app)
@@ -316,6 +977,11 @@
interfaces,
"/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
});
+
+ BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
+ .privileges(redfish::privileges::postTriggersCollection)
+ .methods(boost::beast::http::verb::post)(std::bind_front(
+ telemetry::handleTriggerCollectionPost, std::ref(app)));
}
inline void requestRoutesTrigger(App& app)