summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces
diff options
context:
space:
mode:
authorJason M. Bills <jason.m.bills@linux.intel.com>2020-08-31 23:56:28 +0300
committerJason M. Bills <jason.m.bills@linux.intel.com>2020-09-02 00:21:46 +0300
commitf99301c1a626951ee7feee081a1494e795d0e243 (patch)
treeca75379d317be9cc1757a00e0352a048b5d3200b /meta-openbmc-mods/meta-common/recipes-phosphor/interfaces
parent40108db4434d8c2e0a1ad2d1dd3f5ae34b17352c (diff)
downloadopenbmc-f99301c1a626951ee7feee081a1494e795d0e243.tar.xz
Update to internal 0.74
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/interfaces')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-Remove-QueryString.patch621
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-bmcweb-handle-device-or-resource-busy-exception.patch219
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-EventService-https-client-support.patch405
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-VirtualMedia-fixes-for-Redfish-Service-Validator.patch122
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0006-Fix-Image-and-ImageName-values-in-schema.patch38
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch913
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch594
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-DELETE-in-MetricReportDefinitions-st.patch68
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-OnRequest-in-MetricReportDefinition.patch169
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Add-support-for-MetricDefinition-scheme.patch535
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Fix-MetricReport-timestamp-for-EventService.patch78
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README21
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend29
13 files changed, 3810 insertions, 2 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-Remove-QueryString.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-Remove-QueryString.patch
new file mode 100644
index 000000000..238fb83c7
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-Remove-QueryString.patch
@@ -0,0 +1,621 @@
+From f8749b5898403ee04a623a5bc534bd939865e221 Mon Sep 17 00:00:00 2001
+From: James Feist <james.feist@linux.intel.com>
+Date: Wed, 22 Jul 2020 09:08:38 -0700
+Subject: [PATCH 1/1] Remove QueryString
+
+QueryString is an error-prone library that was
+leftover from crow. Replace it with boost::url,
+a header only library based and written by the
+one of the authors of boost beast.
+
+Tested: Verified logging paging still worked
+as expected
+
+Change-Id: I47c225089aa7d0f7d2299142f91806294f879381
+Signed-off-by: James Feist <james.feist@linux.intel.com>
+---
+ CMakeLists.txt | 2 +
+ CMakeLists.txt.in | 10 +
+ http/http_connection.h | 21 +-
+ http/http_request.h | 5 +-
+ http/query_string.h | 421 -----------------------------
+ redfish-core/lib/event_service.hpp | 7 +-
+ redfish-core/lib/log_services.hpp | 20 +-
+ 7 files changed, 45 insertions(+), 441 deletions(-)
+ delete mode 100644 http/query_string.h
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 2886438..50483ad 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -280,6 +280,8 @@ add_definitions (-DBOOST_ALL_NO_LIB)
+ add_definitions (-DBOOST_NO_RTTI)
+ add_definitions (-DBOOST_NO_TYPEID)
+ add_definitions (-DBOOST_COROUTINES_NO_DEPRECATION_WARNING)
++add_definitions (-DBOOST_URL_STANDALONE)
++add_definitions (-DBOOST_URL_HEADER_ONLY)
+
+ # sdbusplus
+ if (NOT ${YOCTO_DEPENDENCIES})
+diff --git a/CMakeLists.txt.in b/CMakeLists.txt.in
+index d14910f..5cd73f6 100644
+--- a/CMakeLists.txt.in
++++ b/CMakeLists.txt.in
+@@ -53,3 +53,13 @@ externalproject_add (
+ cp -r "${CMAKE_BINARY_DIR}/nlohmann-json-src/include/nlohmann"
+ "${CMAKE_BINARY_DIR}/prefix/include"
+ )
++
++externalproject_add (
++ Boost-URL GIT_REPOSITORY "https://github.com/CPPAlliance/url.git" GIT_TAG
++ a56ae0df6d3078319755fbaa67822b4fa7fd352b SOURCE_DIR
++ "${CMAKE_BINARY_DIR}/boost-url-src" BINARY_DIR
++ "${CMAKE_BINARY_DIR}/boost-url-build" CONFIGURE_COMMAND "" BUILD_COMMAND
++ "" INSTALL_COMMAND mkdir -p "${CMAKE_BINARY_DIR}/prefix/include" &&
++ cp -r "${CMAKE_BINARY_DIR}/boost-url-src/include/boost"
++ "${CMAKE_BINARY_DIR}/prefix/include"
++)
+diff --git a/http/http_connection.h b/http/http_connection.h
+index 35bf99c..8dba3d6 100644
+--- a/http/http_connection.h
++++ b/http/http_connection.h
+@@ -728,13 +728,9 @@ class Connection :
+ return;
+ }
+
+- // Compute the url parameters for the request
+- req->url = req->target();
+- std::size_t index = req->url.find("?");
+- if (index != std::string_view::npos)
+- {
+- req->url = req->url.substr(0, index);
+- }
++ req->urlView = boost::urls::url_view(req->target());
++ req->url = req->urlView.encoded_path();
++
+ crow::authorization::authenticate(*req, res, session);
+
+ bool loggedIn = req && req->session;
+@@ -743,7 +739,16 @@ class Connection :
+ startDeadline(loggedInAttempts);
+ BMCWEB_LOG_DEBUG << "Starting slow deadline";
+
+- req->urlParams = QueryString(std::string(req->target()));
++ req->urlParams = req->urlView.params();
++
++#ifdef BMCWEB_ENABLE_DEBUG
++ std::string paramList = "";
++ for (const auto param : req->urlParams)
++ {
++ paramList += param->key() + " " + param->value() + " ";
++ }
++ BMCWEB_LOG_DEBUG << "QueryParams: " << paramList;
++#endif
+ }
+ else
+ {
+diff --git a/http/http_request.h b/http/http_request.h
+index 0691465..95f88c7 100644
+--- a/http/http_request.h
++++ b/http/http_request.h
+@@ -1,7 +1,6 @@
+ #pragma once
+
+ #include "common.h"
+-#include "query_string.h"
+
+ #include "sessions.hpp"
+
+@@ -9,6 +8,7 @@
+ #include <boost/beast/http.hpp>
+ #include <boost/beast/ssl/ssl_stream.hpp>
+ #include <boost/beast/websocket.hpp>
++#include <boost/url/url_view.hpp>
+
+ namespace crow
+ {
+@@ -24,7 +24,8 @@ struct Request
+ boost::beast::http::request<boost::beast::http::string_body>& req;
+ boost::beast::http::fields& fields;
+ std::string_view url{};
+- QueryString urlParams{};
++ boost::urls::url_view urlView{};
++ boost::urls::url_view::params_type urlParams{};
+ bool isSecure{false};
+
+ const std::string& body;
+diff --git a/http/query_string.h b/http/query_string.h
+deleted file mode 100644
+index e980280..0000000
+--- a/http/query_string.h
++++ /dev/null
+@@ -1,421 +0,0 @@
+-#pragma once
+-
+-#include <cstdio>
+-#include <cstring>
+-#include <iostream>
+-#include <string>
+-#include <vector>
+-
+-namespace crow
+-{
+-// ----------------------------------------------------------------------------
+-// qs_parse (modified)
+-// https://github.com/bartgrantham/qs_parse
+-// ----------------------------------------------------------------------------
+-/* Similar to strncmp, but handles URL-encoding for either string */
+-int qsStrncmp(const char* s, const char* qs, size_t n);
+-
+-/* Finds the beginning of each key/value pair and stores a pointer in qs_kv.
+- * Also decodes the value portion of the k/v pair *in-place*. In a future
+- * enhancement it will also have a compile-time option of sorting qs_kv
+- * alphabetically by key. */
+-size_t qsParse(char* qs, char* qs_kv[], size_t qs_kv_size);
+-
+-/* Used by qs_parse to decode the value portion of a k/v pair */
+-int qsDecode(char* qs);
+-
+-/* Looks up the value according to the key on a pre-processed query string
+- * A future enhancement will be a compile-time option to look up the key
+- * in a pre-sorted qs_kv array via a binary search. */
+-// char * qs_k2v(const char * key, char * qs_kv[], int qs_kv_size);
+-char* qsK2v(const char* key, char* const* qs_kv, int qs_kv_size, int nth);
+-
+-/* Non-destructive lookup of value, based on key. User provides the
+- * destinaton string and length. */
+-char* qsScanvalue(const char* key, const char* qs, char* val, size_t val_len);
+-
+-// TODO: implement sorting of the qs_kv array; for now ensure it's not compiled
+-#undef _qsSORTING
+-
+-// isxdigit _is_ available in <ctype.h>, but let's avoid another header instead
+-#define BMCWEB_QS_ISHEX(x) \
+- ((((x) >= '0' && (x) <= '9') || ((x) >= 'A' && (x) <= 'F') || \
+- ((x) >= 'a' && (x) <= 'f')) \
+- ? 1 \
+- : 0)
+-#define BMCWEB_QS_HEX2DEC(x) \
+- (((x) >= '0' && (x) <= '9') \
+- ? (x)-48 \
+- : ((x) >= 'A' && (x) <= 'F') \
+- ? (x)-55 \
+- : ((x) >= 'a' && (x) <= 'f') ? (x)-87 : 0)
+-#define BMCWEB_QS_ISQSCHR(x) \
+- ((((x) == '=') || ((x) == '#') || ((x) == '&') || ((x) == '\0')) ? 0 : 1)
+-
+-inline int qsStrncmp(const char* s, const char* qs, size_t n)
+-{
+- int i = 0;
+- char u1, u2;
+- char unyb, lnyb;
+-
+- while (n-- > 0)
+- {
+- u1 = *s++;
+- u2 = *qs++;
+-
+- if (!BMCWEB_QS_ISQSCHR(u1))
+- {
+- u1 = '\0';
+- }
+- if (!BMCWEB_QS_ISQSCHR(u2))
+- {
+- u2 = '\0';
+- }
+-
+- if (u1 == '+')
+- {
+- u1 = ' ';
+- }
+- if (u1 == '%') // easier/safer than scanf
+- {
+- unyb = static_cast<char>(*s++);
+- lnyb = static_cast<char>(*s++);
+- if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb))
+- {
+- u1 = static_cast<char>((BMCWEB_QS_HEX2DEC(unyb) * 16) +
+- BMCWEB_QS_HEX2DEC(lnyb));
+- }
+- else
+- {
+- u1 = '\0';
+- }
+- }
+-
+- if (u2 == '+')
+- {
+- u2 = ' ';
+- }
+- if (u2 == '%') // easier/safer than scanf
+- {
+- unyb = static_cast<char>(*qs++);
+- lnyb = static_cast<char>(*qs++);
+- if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb))
+- {
+- u2 = static_cast<char>((BMCWEB_QS_HEX2DEC(unyb) * 16) +
+- BMCWEB_QS_HEX2DEC(lnyb));
+- }
+- else
+- {
+- u2 = '\0';
+- }
+- }
+-
+- if (u1 != u2)
+- {
+- return u1 - u2;
+- }
+- if (u1 == '\0')
+- {
+- return 0;
+- }
+- i++;
+- }
+- if (BMCWEB_QS_ISQSCHR(*qs))
+- {
+- return -1;
+- }
+- else
+- {
+- return 0;
+- }
+-}
+-
+-inline size_t qsParse(char* qs, char* qs_kv[], size_t qs_kv_size)
+-{
+- size_t i;
+- size_t j;
+- char* substrPtr;
+-
+- for (i = 0; i < qs_kv_size; i++)
+- {
+- qs_kv[i] = nullptr;
+- }
+-
+- // find the beginning of the k/v substrings or the fragment
+- substrPtr = qs + strcspn(qs, "?#");
+- if (substrPtr[0] != '\0')
+- {
+- substrPtr++;
+- }
+- else
+- {
+- return 0; // no query or fragment
+- }
+-
+- i = 0;
+- while (i < qs_kv_size)
+- {
+- qs_kv[i++] = substrPtr;
+- j = strcspn(substrPtr, "&");
+- if (substrPtr[j] == '\0')
+- {
+- break;
+- }
+- substrPtr += j + 1;
+- }
+-
+- // we only decode the values in place, the keys could have '='s in them
+- // which will hose our ability to distinguish keys from values later
+- for (j = 0; j < i; j++)
+- {
+- substrPtr = qs_kv[j] + strcspn(qs_kv[j], "=&#");
+- if (substrPtr[0] == '&' || substrPtr[0] == '\0')
+- { // blank value: skip decoding
+- substrPtr[0] = '\0';
+- }
+- else
+- {
+- qsDecode(++substrPtr);
+- }
+- }
+-
+-#ifdef _qsSORTING
+-// TODO: qsort qs_kv, using qs_strncmp() for the comparison
+-#endif
+-
+- return i;
+-}
+-
+-inline int qsDecode(char* qs)
+-{
+- int i = 0, j = 0;
+-
+- while (BMCWEB_QS_ISQSCHR(qs[j]))
+- {
+- if (qs[j] == '+')
+- {
+- qs[i] = ' ';
+- }
+- else if (qs[j] == '%') // easier/safer than scanf
+- {
+- if (!BMCWEB_QS_ISHEX(qs[j + 1]) || !BMCWEB_QS_ISHEX(qs[j + 2]))
+- {
+- qs[i] = '\0';
+- return i;
+- }
+- qs[i] = static_cast<char>((BMCWEB_QS_HEX2DEC(qs[j + 1]) * 16) +
+- BMCWEB_QS_HEX2DEC(qs[j + 2]));
+- j += 2;
+- }
+- else
+- {
+- qs[i] = qs[j];
+- }
+- i++;
+- j++;
+- }
+- qs[i] = '\0';
+-
+- return i;
+-}
+-
+-inline char* qsK2v(const char* key, char* const* qs_kv, int qs_kv_size,
+- int nth = 0)
+-{
+- int i;
+- size_t keyLen, skip;
+-
+- keyLen = strlen(key);
+-
+-#ifdef _qsSORTING
+-// TODO: binary search for key in the sorted qs_kv
+-#else // _qsSORTING
+- for (i = 0; i < qs_kv_size; i++)
+- {
+- // we rely on the unambiguous '=' to find the value in our k/v pair
+- if (qsStrncmp(key, qs_kv[i], keyLen) == 0)
+- {
+- skip = strcspn(qs_kv[i], "=");
+- if (qs_kv[i][skip] == '=')
+- {
+- skip++;
+- }
+- // return (zero-char value) ? ptr to trailing '\0' : ptr to value
+- if (nth == 0)
+- {
+- return qs_kv[i] + skip;
+- }
+- else
+- {
+- --nth;
+- }
+- }
+- }
+-#endif // _qsSORTING
+-
+- return nullptr;
+-}
+-
+-inline char* qsScanvalue(const char* key, const char* qs, char* val,
+- size_t val_len)
+-{
+- size_t i, keyLen;
+- const char* tmp;
+-
+- // find the beginning of the k/v substrings
+- if ((tmp = strchr(qs, '?')) != nullptr)
+- {
+- qs = tmp + 1;
+- }
+-
+- keyLen = strlen(key);
+- while (qs[0] != '#' && qs[0] != '\0')
+- {
+- if (qsStrncmp(key, qs, keyLen) == 0)
+- {
+- break;
+- }
+- qs += strcspn(qs, "&") + 1;
+- }
+-
+- if (qs[0] == '\0')
+- {
+- return nullptr;
+- }
+-
+- qs += strcspn(qs, "=&#");
+- if (qs[0] == '=')
+- {
+- qs++;
+- i = strcspn(qs, "&=#");
+- strncpy(val, qs, (val_len - 1) < (i + 1) ? (val_len - 1) : (i + 1));
+- qsDecode(val);
+- }
+- else
+- {
+- if (val_len > 0)
+- {
+- val[0] = '\0';
+- }
+- }
+-
+- return val;
+-}
+-} // namespace crow
+-// ----------------------------------------------------------------------------
+-
+-namespace crow
+-{
+-class QueryString
+-{
+- public:
+- static const size_t maxKeyValuePairsCount = 256;
+-
+- QueryString() = default;
+-
+- QueryString(const QueryString& qs) : url(qs.url)
+- {
+- for (auto p : qs.keyValuePairs)
+- {
+- keyValuePairs.push_back(
+- const_cast<char*>(p - qs.url.c_str() + url.c_str()));
+- }
+- }
+-
+- QueryString& operator=(const QueryString& qs)
+- {
+- if (this == &qs)
+- {
+- return *this;
+- }
+-
+- url = qs.url;
+- keyValuePairs.clear();
+- for (auto p : qs.keyValuePairs)
+- {
+- keyValuePairs.push_back(
+- const_cast<char*>(p - qs.url.c_str() + url.c_str()));
+- }
+- return *this;
+- }
+-
+- QueryString& operator=(QueryString&& qs)
+- {
+- keyValuePairs = std::move(qs.keyValuePairs);
+- auto* oldData = const_cast<char*>(qs.url.c_str());
+- url = std::move(qs.url);
+- for (auto& p : keyValuePairs)
+- {
+- p += const_cast<char*>(url.c_str()) - oldData;
+- }
+- return *this;
+- }
+-
+- explicit QueryString(std::string newUrl) : url(std::move(newUrl))
+- {
+- if (url.empty())
+- {
+- return;
+- }
+-
+- keyValuePairs.resize(maxKeyValuePairsCount);
+-
+- size_t count =
+- qsParse(&url[0], &keyValuePairs[0], maxKeyValuePairsCount);
+- keyValuePairs.resize(count);
+- }
+-
+- void clear()
+- {
+- keyValuePairs.clear();
+- url.clear();
+- }
+-
+- friend std::ostream& operator<<(std::ostream& os, const QueryString& qs)
+- {
+- os << "[ ";
+- for (size_t i = 0; i < qs.keyValuePairs.size(); ++i)
+- {
+- if (i != 0u)
+- {
+- os << ", ";
+- }
+- os << qs.keyValuePairs[i];
+- }
+- os << " ]";
+- return os;
+- }
+-
+- char* get(const std::string& name) const
+- {
+- char* ret = qsK2v(name.c_str(), keyValuePairs.data(),
+- static_cast<int>(keyValuePairs.size()));
+- return ret;
+- }
+-
+- std::vector<char*> getList(const std::string& name) const
+- {
+- std::vector<char*> ret;
+- std::string plus = name + "[]";
+- char* element = nullptr;
+-
+- int count = 0;
+- while (true)
+- {
+- element = qsK2v(plus.c_str(), keyValuePairs.data(),
+- static_cast<int>(keyValuePairs.size()), count++);
+- if (element == nullptr)
+- {
+- break;
+- }
+- ret.push_back(element);
+- }
+- return ret;
+- }
+-
+- private:
+- std::string url;
+- std::vector<char*> keyValuePairs;
+-};
+-
+-} // namespace crow
+diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
+index b27c6e0..8bd30f5 100644
+--- a/redfish-core/lib/event_service.hpp
++++ b/redfish-core/lib/event_service.hpp
+@@ -445,13 +445,16 @@ class EventServiceSSE : public Node
+ subValue->protocol = "Redfish";
+ subValue->retryPolicy = "TerminateAfterRetries";
+
+- char* filters = req.urlParams.get("$filter");
+- if (filters == nullptr)
++ boost::urls::url_view::params_type::iterator it =
++ req.urlParams.find("$filter");
++ if (it == req.urlParams.end())
+ {
+ subValue->eventFormatType = "Event";
+ }
++
+ else
+ {
++ std::string filters = it->value();
+ // Reading from query params.
+ bool status = readSSEQueryParams(
+ filters, subValue->eventFormatType, subValue->registryMsgIds,
+diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp
+index bee1a92..590243c 100644
+--- a/redfish-core/lib/log_services.hpp
++++ b/redfish-core/lib/log_services.hpp
+@@ -218,12 +218,14 @@ static bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp)
+ static bool getSkipParam(crow::Response& res, const crow::Request& req,
+ uint64_t& skip)
+ {
+- char* skipParam = req.urlParams.get("$skip");
+- if (skipParam != nullptr)
++ boost::urls::url_view::params_type::iterator it =
++ req.urlParams.find("$skip");
++ if (it != req.urlParams.end())
+ {
++ std::string skipParam = it->value();
+ char* ptr = nullptr;
+- skip = std::strtoul(skipParam, &ptr, 10);
+- if (*skipParam == '\0' || *ptr != '\0')
++ skip = std::strtoul(skipParam.c_str(), &ptr, 10);
++ if (skipParam.empty() || *ptr != '\0')
+ {
+
+ messages::queryParameterValueTypeError(res, std::string(skipParam),
+@@ -238,12 +240,14 @@ static constexpr const uint64_t maxEntriesPerPage = 1000;
+ static bool getTopParam(crow::Response& res, const crow::Request& req,
+ uint64_t& top)
+ {
+- char* topParam = req.urlParams.get("$top");
+- if (topParam != nullptr)
++ boost::urls::url_view::params_type::iterator it =
++ req.urlParams.find("$top");
++ if (it != req.urlParams.end())
+ {
++ std::string topParam = it->value();
+ char* ptr = nullptr;
+- top = std::strtoul(topParam, &ptr, 10);
+- if (*topParam == '\0' || *ptr != '\0')
++ top = std::strtoul(topParam.c_str(), &ptr, 10);
++ if (topParam.empty() || *ptr != '\0')
+ {
+ messages::queryParameterValueTypeError(res, std::string(topParam),
+ "$top");
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-bmcweb-handle-device-or-resource-busy-exception.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-bmcweb-handle-device-or-resource-busy-exception.patch
new file mode 100644
index 000000000..761caabb7
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-bmcweb-handle-device-or-resource-busy-exception.patch
@@ -0,0 +1,219 @@
+From 5fa2fb4bd766b9c74b9edff4701408a002466b2a Mon Sep 17 00:00:00 2001
+From: Karol Wachowski <karol.wachowski@intel.com>
+Date: Fri, 10 Jul 2020 09:54:06 +0000
+Subject: [PATCH] bmcweb handle device or resource busy exception
+
+Use async_method_call_timed() for mount/unmount dbus oprations.
+Long mount/unmount times are supported by VirtualMedia service,
+this works because of settable timeout property, available for each block
+device.
+Default dbus calls will timeout when mount/unmount timeout is long enough.
+
+Get mount/unmount timeout property and use it for mount/unmount calls.
+Add handling of device or resource busy exception (EBUSY) that
+can be thrown by VirtualMedia service during Mount/Unmount dbus operations.
+
+Tested: Verified that after mounting non-existing HTTPS resource
+ in proxy mode, VirtualMedia recovers restoring ready state
+ and returns EBUSY during that transition.
+ Verfied that resources can be mounted/unmounted in both legacy
+ and proxy mode.
+Signed-off-by: Karol Wachowski <karol.wachowski@intel.com>
+Change-Id: Ica62c34db0cce24c4c6169fc661edfde49e948d0
+---
+ redfish-core/lib/virtual_media.hpp | 144 ++++++++++++++++++++++-------
+ 1 file changed, 110 insertions(+), 34 deletions(-)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 0b5eb1a..a0c63ad 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -23,6 +23,8 @@
+ // for GetObjectType and ManagedObjectType
+ #include <account_service.hpp>
+
++#include <chrono>
++
+ namespace redfish
+
+ {
+@@ -109,6 +111,26 @@ static void vmParseInterfaceObject(const DbusInterfaceType& interface,
+ }
+ }
+
++/**
++ * @brief parses Timeout property and converts to microseconds
++ */
++static std::optional<uint64_t>
++ vmParseTimeoutProperty(const std::variant<int>& timeoutProperty)
++{
++ const int* timeoutValue = std::get_if<int>(&timeoutProperty);
++ if (timeoutValue)
++ {
++ constexpr int timeoutMarginSeconds = 10;
++ return std::chrono::duration_cast<std::chrono::microseconds>(
++ std::chrono::seconds(*timeoutValue + timeoutMarginSeconds))
++ .count();
++ }
++ else
++ {
++ return std::nullopt;
++ }
++}
++
+ /**
+ * @brief Fill template for Virtual Media Item.
+ */
+@@ -806,22 +828,54 @@ class VirtualMediaActionInsertMedia : public Node
+ }
+
+ crow::connections::systemBus->async_method_call(
+- [asyncResp, secretPipe](const boost::system::error_code ec,
+- bool success) {
++ [asyncResp, service, name, imageUrl, rw, unixFd,
++ secretPipe](const boost::system::error_code ec,
++ const std::variant<int> timeoutProperty) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
+ messages::internalError(asyncResp->res);
++ return;
+ }
+- else if (!success)
++
++ auto timeout = vmParseTimeoutProperty(timeoutProperty);
++ if (timeout == std::nullopt)
+ {
+- BMCWEB_LOG_ERROR << "Service responded with error";
+- messages::generalError(asyncResp->res);
++ BMCWEB_LOG_ERROR << "Timeout property is empty.";
++ messages::internalError(asyncResp->res);
++ return;
+ }
++
++ crow::connections::systemBus->async_method_call_timed(
++ [asyncResp, secretPipe](const boost::system::error_code ec,
++ bool success) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Bad D-Bus request error: "
++ << ec;
++ if (ec ==
++ boost::system::errc::device_or_resource_busy)
++ {
++ messages::resourceInUse(asyncResp->res);
++ }
++ else
++ {
++ messages::internalError(asyncResp->res);
++ }
++ }
++ else if (!success)
++ {
++ BMCWEB_LOG_ERROR << "Service responded with error";
++ messages::generalError(asyncResp->res);
++ }
++ },
++ service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
++ "xyz.openbmc_project.VirtualMedia.Legacy", "Mount",
++ *timeout, imageUrl, rw, unixFd);
+ },
+ service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
+- "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw,
+- unixFd);
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.VirtualMedia.MountPoint", "Timeout");
+ }
+ };
+
+@@ -955,38 +1009,60 @@ class VirtualMediaActionEjectMedia : public Node
+ const std::string& service, const std::string& name,
+ bool legacy)
+ {
+-
+- // Legacy mount requires parameter with image
++ std::string objectPath = "/xyz/openbmc_project/VirtualMedia/";
++ std::string ifaceName = "xyz.openbmc_project.VirtualMedia";
+ if (legacy)
+ {
+- crow::connections::systemBus->async_method_call(
+- [asyncResp](const boost::system::error_code ec) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
+-
+- messages::internalError(asyncResp->res);
+- return;
+- }
+- },
+- service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
+- "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
++ objectPath += "Legacy/";
++ ifaceName += ".Legacy";
+ }
+- else // proxy
++ else
+ {
+- crow::connections::systemBus->async_method_call(
+- [asyncResp](const boost::system::error_code ec) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
+-
+- messages::internalError(asyncResp->res);
+- return;
+- }
+- },
+- service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
+- "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
++ objectPath += "Proxy/";
++ ifaceName += ".Proxy";
+ }
++ objectPath += name;
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, service, name, objectPath,
++ ifaceName](const boost::system::error_code ec,
++ const std::variant<int> timeoutProperty) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ auto timeout = vmParseTimeoutProperty(timeoutProperty);
++ if (timeout == std::nullopt)
++ {
++ BMCWEB_LOG_ERROR << "Timeout property is empty.";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ crow::connections::systemBus->async_method_call_timed(
++ [asyncResp](const boost::system::error_code ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Bad D-Bus request error: "
++ << ec;
++ if (ec ==
++ boost::system::errc::device_or_resource_busy)
++ {
++ messages::resourceInUse(asyncResp->res);
++ }
++ else
++ {
++ messages::internalError(asyncResp->res);
++ }
++ return;
++ }
++ },
++ service, objectPath, ifaceName, "Unmount", *timeout);
++ },
++ service, objectPath, "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.VirtualMedia.MountPoint", "Timeout");
+ }
+ };
+
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-EventService-https-client-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-EventService-https-client-support.patch
new file mode 100644
index 000000000..274dd044a
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-EventService-https-client-support.patch
@@ -0,0 +1,405 @@
+From f388587781c3d874b13b50ad39e8674f0bc08049 Mon Sep 17 00:00:00 2001
+From: AppaRao Puli <apparao.puli@linux.intel.com>
+Date: Mon, 25 May 2020 16:14:39 +0530
+Subject: [PATCH] EventService: https client support
+
+Add https client support for push style
+eventing. Using this BMC can push the event
+logs/telemetry data to event listener over
+secure http channel.
+
+Tested:
+ - Created subscription with https destination
+ url. Using SubmitTestEvent action set the
+ event and can see event on event listener.
+ - Validator passed.
+
+Change-Id: I44c3918b39baa2eb5fddda9d635f99aa280a422a
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+---
+ http/http_client.hpp | 270 +++++++++++++++++--------
+ redfish-core/include/event_service_manager.hpp | 2 +-
+ 2 files changed, 186 insertions(+), 86 deletions(-)
+
+diff --git a/http/http_client.hpp b/http/http_client.hpp
+index e6a7db1..27d2af3 100644
+--- a/http/http_client.hpp
++++ b/http/http_client.hpp
+@@ -17,6 +17,7 @@
+ #include <boost/asio/strand.hpp>
+ #include <boost/beast/core.hpp>
+ #include <boost/beast/http.hpp>
++#include <boost/beast/ssl.hpp>
+ #include <boost/beast/version.hpp>
+
+ #include <cstdlib>
+@@ -49,7 +50,10 @@ enum class ConnState
+ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ {
+ private:
+- boost::beast::tcp_stream conn;
++ boost::asio::io_context& ioc;
++ boost::asio::ssl::context ctx{boost::asio::ssl::context::tlsv12_client};
++ std::shared_ptr<boost::beast::ssl_stream<boost::beast::tcp_stream>> sslConn;
++ std::shared_ptr<boost::beast::tcp_stream> conn;
+ boost::asio::steady_timer timer;
+ boost::beast::flat_buffer buffer;
+ boost::beast::http::request<boost::beast::http::string_body> req;
+@@ -62,14 +66,37 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ std::string host;
+ std::string port;
+ std::string uri;
++ bool useSsl;
+ uint32_t retryCount;
+ uint32_t maxRetryAttempts;
+ uint32_t retryIntervalSecs;
+ std::string retryPolicyAction;
+ bool runningTimer;
+
++ inline boost::beast::tcp_stream& getConn()
++ {
++ if (useSsl)
++ {
++ return (boost::beast::get_lowest_layer(*sslConn));
++ }
++ else
++ {
++ return (*conn);
++ }
++ }
++
+ void doConnect()
+ {
++ if (useSsl)
++ {
++ sslConn = std::make_shared<
++ boost::beast::ssl_stream<boost::beast::tcp_stream>>(ioc, ctx);
++ }
++ else
++ {
++ conn = std::make_shared<boost::beast::tcp_stream>(ioc);
++ }
++
+ if (state == ConnState::connectInProgress)
+ {
+ return;
+@@ -77,25 +104,53 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ state = ConnState::connectInProgress;
+
+ BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":" << port;
+- // Set a timeout on the operation
+- conn.expires_after(std::chrono::seconds(30));
+- conn.async_connect(endpoint, [self(shared_from_this())](
+- const boost::beast::error_code& ec,
+- const boost::asio::ip::tcp::resolver::
+- results_type::endpoint_type& ep) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "Connect " << ep
+- << " failed: " << ec.message();
+- self->state = ConnState::connectFailed;
+- self->checkQueue();
+- return;
+- }
+- self->state = ConnState::connected;
+- BMCWEB_LOG_DEBUG << "Connected to: " << ep;
+
+- self->checkQueue();
+- });
++ auto respHandler =
++ [self(shared_from_this())](const boost::beast::error_code& ec,
++ const boost::asio::ip::tcp::resolver::
++ results_type::endpoint_type& ep) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Connect " << ep
++ << " failed: " << ec.message();
++ self->state = ConnState::connectFailed;
++ self->checkQueue();
++ return;
++ }
++ BMCWEB_LOG_DEBUG << "Connected to: " << ep;
++ if (self->useSsl)
++ {
++ self->performHandshake();
++ }
++ else
++ {
++ self->state = ConnState::connected;
++ self->checkQueue();
++ }
++ };
++
++ getConn().expires_after(std::chrono::seconds(30));
++ getConn().async_connect(endpoint, std::move(respHandler));
++ }
++
++ void performHandshake()
++ {
++ sslConn->async_handshake(
++ boost::asio::ssl::stream_base::client,
++ [self(shared_from_this())](const boost::beast::error_code& ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "SSL handshake failed: "
++ << ec.message();
++ self->state = ConnState::connectFailed;
++ self->doCloseAndCheckQueue();
++ return;
++ }
++ self->state = ConnState::connected;
++ BMCWEB_LOG_DEBUG << "SSL Handshake successfull \n";
++
++ self->checkQueue();
++ });
+ }
+
+ void sendMessage(const std::string& data)
+@@ -108,7 +163,10 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+
+ BMCWEB_LOG_DEBUG << __FUNCTION__ << "(): " << host << ":" << port;
+
+- req.version(static_cast<int>(11)); // HTTP 1.1
++ req = {};
++ res = {};
++
++ req.version(11); // HTTP 1.1
+ req.target(uri);
+ req.method(boost::beast::http::verb::post);
+
+@@ -123,83 +181,121 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ req.body() = data;
+ req.prepare_payload();
+
+- // Set a timeout on the operation
+- conn.expires_after(std::chrono::seconds(30));
++ auto respHandler = [self(shared_from_this())](
++ const boost::beast::error_code& ec,
++ const std::size_t& bytesTransferred) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message();
++ self->state = ConnState::sendFailed;
++ self->doCloseAndCheckQueue();
++ return;
++ }
++ BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
++ << bytesTransferred;
++ boost::ignore_unused(bytesTransferred);
+
+- // Send the HTTP request to the remote host
+- boost::beast::http::async_write(
+- conn, req,
+- [self(shared_from_this())](const boost::beast::error_code& ec,
+- const std::size_t& bytesTransferred) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "sendMessage() failed: "
+- << ec.message();
+- self->state = ConnState::sendFailed;
+- self->checkQueue();
+- return;
+- }
+- BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
+- << bytesTransferred;
+- boost::ignore_unused(bytesTransferred);
++ self->recvMessage();
++ };
+
+- self->recvMessage();
+- });
++ getConn().expires_after(std::chrono::seconds(30));
++ if (useSsl)
++ {
++ boost::beast::http::async_write(*sslConn, req,
++ std::move(respHandler));
++ }
++ else
++ {
++ boost::beast::http::async_write(*conn, req, std::move(respHandler));
++ }
+ }
+
+ void recvMessage()
+ {
+- // Receive the HTTP response
+- boost::beast::http::async_read(
+- conn, buffer, res,
+- [self(shared_from_this())](const boost::beast::error_code& ec,
+- const std::size_t& bytesTransferred) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "recvMessage() failed: "
+- << ec.message();
+- self->state = ConnState::recvFailed;
+- self->checkQueue();
+- return;
+- }
+- BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
+- << bytesTransferred;
+- boost::ignore_unused(bytesTransferred);
++ auto respHandler = [self(shared_from_this())](
++ const boost::beast::error_code& ec,
++ const std::size_t& bytesTransferred) {
++ if (ec && ec != boost::beast::http::error::partial_message)
++ {
++ BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message();
++ self->state = ConnState::recvFailed;
++ self->doCloseAndCheckQueue();
++ return;
++ }
++ BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
++ << bytesTransferred;
++ boost::ignore_unused(bytesTransferred);
+
+- // Discard received data. We are not interested.
+- BMCWEB_LOG_DEBUG << "recvMessage() data: " << self->res;
++ // Discard received data. We are not interested.
++ BMCWEB_LOG_DEBUG << "recvMessage() data: " << self->res;
+
+- // Send is successful, Lets remove data from queue
+- // check for next request data in queue.
+- self->requestDataQueue.pop();
+- self->state = ConnState::idle;
+- self->checkQueue();
+- });
++ // Send is successful, Lets remove data from queue
++ // check for next request data in queue.
++ self->requestDataQueue.pop();
++ self->state = ConnState::idle;
++
++ if (ec == boost::beast::http::error::partial_message)
++ {
++ // Least bothered about recv message. Partial
++ // message means, already data is sent. Lets close
++ // connection and let next request open connection
++ // to avoid truncated stream.
++ self->state = ConnState::closed;
++ self->doCloseAndCheckQueue();
++ return;
++ }
++
++ self->checkQueue();
++ };
++
++ getConn().expires_after(std::chrono::seconds(30));
++ if (useSsl)
++ {
++ boost::beast::http::async_read(*sslConn, buffer, res,
++ std::move(respHandler));
++ }
++ else
++ {
++ boost::beast::http::async_read(*conn, buffer, res,
++ std::move(respHandler));
++ }
+ }
+
+- void doClose()
++ void doCloseAndCheckQueue()
+ {
+ boost::beast::error_code ec;
+- conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
++ getConn().cancel();
++ getConn().expires_after(std::chrono::seconds(30));
++ getConn().socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both,
++ ec);
+
+- state = ConnState::closed;
+- // not_connected happens sometimes so don't bother reporting it.
+- if (ec && ec != boost::beast::errc::not_connected)
++ if (ec && ec != boost::asio::error::eof)
+ {
+- BMCWEB_LOG_ERROR << "shutdown failed: " << ec.message();
+- return;
++ // Many https server closes connection abruptly
++ // i.e witnout close_notify. More details are at
++ // https://github.com/boostorg/beast/issues/824
++ if (ec == boost::asio::ssl::error::stream_truncated)
++ {
++ BMCWEB_LOG_DEBUG
++ << "doCloseAndCheckQueue(): Connection closed by server.";
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR << "doCloseAndCheckQueue() failed: "
++ << ec.message();
++ }
+ }
++
++ getConn().close();
+ BMCWEB_LOG_DEBUG << "Connection closed gracefully";
++ checkQueue();
++ return;
+ }
+
+ void checkQueue(const bool newRecord = false)
+ {
+ if (requestDataQueue.empty())
+ {
+- // TODO: Having issue in keeping connection alive. So lets close if
+- // nothing to be transferred.
+- doClose();
+-
+ BMCWEB_LOG_DEBUG << "requestDataQueue is empty\n";
+ return;
+ }
+@@ -257,17 +353,20 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ BMCWEB_LOG_DEBUG << "Attempt retry after " << retryIntervalSecs
+ << " seconds. RetryCount = " << retryCount;
+ timer.expires_after(std::chrono::seconds(retryIntervalSecs));
+- timer.async_wait([self = shared_from_this()](
+- const boost::system::error_code& ec) {
+- self->runningTimer = false;
+- self->connStateCheck();
+- });
++ timer.async_wait(
++ [self = shared_from_this()](boost::system::error_code) {
++ self->runningTimer = false;
++ self->connStateCheck();
++ });
+ return;
+ }
+ else
+ {
+- // reset retry count.
+- retryCount = 0;
++ if (state == ConnState::idle)
++ {
++ // State idle means, previous attempt is successful.
++ retryCount = 0;
++ }
+ }
+ connStateCheck();
+
+@@ -310,10 +409,11 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ public:
+ explicit HttpClient(boost::asio::io_context& ioc, const std::string& id,
+ const std::string& destIP, const std::string& destPort,
+- const std::string& destUri) :
+- conn(ioc),
++ const std::string& destUri,
++ const bool inUseSsl = true) :
++ ioc(ioc),
+ timer(ioc), subId(id), host(destIP), port(destPort), uri(destUri),
+- retryCount(0), maxRetryAttempts(5),
++ useSsl(inUseSsl), retryCount(0), maxRetryAttempts(5),
+ retryPolicyAction("TerminateAfterRetries"), runningTimer(false)
+ {
+ boost::asio::ip::tcp::resolver resolver(ioc);
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index 6362112..3ab2605 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -383,7 +383,7 @@ class Subscription
+ {
+ conn = std::make_shared<crow::HttpClient>(
+ crow::connections::systemBus->get_io_context(), id, host, port,
+- path);
++ path, (uriProto == "https" ? true : false));
+ }
+
+ Subscription(const std::shared_ptr<crow::Request::Adaptor>& adaptor) :
+--
+2.7.4
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-VirtualMedia-fixes-for-Redfish-Service-Validator.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-VirtualMedia-fixes-for-Redfish-Service-Validator.patch
new file mode 100644
index 000000000..52ff4e531
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-VirtualMedia-fixes-for-Redfish-Service-Validator.patch
@@ -0,0 +1,122 @@
+From 49dc25100ab8a4220f81bc8f9b54808850fe1267 Mon Sep 17 00:00:00 2001
+From: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
+Date: Wed, 8 Jul 2020 15:17:31 +0200
+Subject: [PATCH] VirtualMedia fixes for Redfish Service Validator
+
+Removes all warnings and errors for VirtualMedia
+- rework for OemVirtualMedia
+- minor adjustments for jsons
+
+Tested:
+Redfish Service Validator ran with no errors and/or warnings
+
+Change-Id: Ic027166153a807a8bd3a6c04f042969f16e0dc6a
+Signed-off-by: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
+---
+ redfish-core/lib/virtual_media.hpp | 4 +--
+ .../v1/JsonSchemas/OemVirtualMedia/index.json | 28 +++----------------
+ .../redfish/v1/schema/OemVirtualMedia_v1.xml | 12 ++++----
+ 3 files changed, 12 insertions(+), 32 deletions(-)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 552e255..183abbe 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -129,7 +129,7 @@ static nlohmann::json vmItemTemplate(const std::string& name,
+ item["MediaTypes"] = {"CD", "USBStick"};
+ item["TransferMethod"] = "Stream";
+ item["TransferProtocolType"] = nullptr;
+- item["Oem"]["OpenBmc"]["WebSocketEndpoint"] = nullptr;
++ item["Oem"]["OpenBMC"]["WebSocketEndpoint"] = nullptr;
+ item["Oem"]["OpenBMC"]["@odata.type"] =
+ "#OemVirtualMedia.v1_0_0.VirtualMedia";
+
+@@ -1039,7 +1039,7 @@ class VirtualMediaCollection : public Node
+ "#VirtualMediaCollection.VirtualMediaCollection";
+ res.jsonValue["Name"] = "Virtual Media Services";
+ res.jsonValue["@odata.id"] =
+- "/redfish/v1/Managers/" + name + "/VirtualMedia/";
++ "/redfish/v1/Managers/" + name + "/VirtualMedia";
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp, name](const boost::system::error_code ec,
+diff --git a/static/redfish/v1/JsonSchemas/OemVirtualMedia/index.json b/static/redfish/v1/JsonSchemas/OemVirtualMedia/index.json
+index 78bd8b7..9ae641a 100644
+--- a/static/redfish/v1/JsonSchemas/OemVirtualMedia/index.json
++++ b/static/redfish/v1/JsonSchemas/OemVirtualMedia/index.json
+@@ -3,9 +3,10 @@
+ "$schema": "http://redfish.dmtf.org/schemas/v1/redfish-schema-v1.json",
+ "copyright": "Copyright 2014-2019 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright",
+ "definitions": {
+- "OpenBmc": {
+- "additionalProperties": true,
+- "description": "Oem properties for OpenBmc.",
++ "VirtualMedia": {
++ "additionalProperties": false,
++ "description": "OEM Extension for VirtualMedia",
++ "longDescription": "OEM Extension for VirtualMedia to support Proxy mode.",
+ "patternProperties": {
+ "^([a-zA-Z_][a-zA-Z0-9_]*)?@(odata|Redfish|Message)\\.[a-zA-Z_][a-zA-Z0-9_]*$": {
+ "description": "This property shall specify a valid odata or Redfish property.",
+@@ -32,27 +33,6 @@
+ }
+ },
+ "type": "object"
+- },
+- "VirtualMedia": {
+- "additionalProperties": false,
+- "description": "OEM Extension for VirtualMedia",
+- "longDescription": "OEM Extension for VirtualMedia to support Proxy mode.",
+- "patternProperties": {
+- "^([a-zA-Z_][a-zA-Z0-9_]*)?@(odata|Redfish|Message)\\.[a-zA-Z_][a-zA-Z0-9_]*$": {
+- "description": "This property shall specify a valid odata or Redfish property.",
+- "type": [
+- "array",
+- "boolean",
+- "integer",
+- "number",
+- "null",
+- "object",
+- "string"
+- ]
+- }
+- },
+- "properties": {},
+- "type": "object"
+ }
+ },
+ "owningEntity": "OpenBMC",
+diff --git a/static/redfish/v1/schema/OemVirtualMedia_v1.xml b/static/redfish/v1/schema/OemVirtualMedia_v1.xml
+index 2b03a67..84afe73 100644
+--- a/static/redfish/v1/schema/OemVirtualMedia_v1.xml
++++ b/static/redfish/v1/schema/OemVirtualMedia_v1.xml
+@@ -25,20 +25,20 @@
+ <Annotation Term="Redfish.OwningEntity" String="OpenBMC"/>
+ <Annotation Term="Redfish.Release" String="1.0"/>
+
+- <ComplexType Name="OpenBmc" BaseType="Resource.OemObject">
+- <Annotation Term="OData.AdditionalProperties" Bool="true"/>
+- <Annotation Term="OData.Description" String="Oem properties for OpenBmc." />
+-
+- <Property Name="WebSocketEndpoint" Type="Edm.String">
++ <Property Name="WebSocketEndpoint" Type="Edm.String">
+ <Annotation Term="OData.Permissions" EnumMember="OData.Permission/Read"/>
+ <Annotation Term="OData.Description" String="Indicates endpoint socket name and location."/>
+ <Annotation Term="OData.LongDescription" String="The value of this property shall be a structure ring indicating location and name of the socket used to communicate with nbd server."/>
+ </Property>
+- </ComplexType>
+
+ <EntityType Name="VirtualMedia" BaseType="Resource.OemObject" Abstract="true">
+ <Annotation Term="OData.Description" String="OEM Extension for VirtualMedia"/>
+ <Annotation Term="OData.LongDescription" String="OEM Extension for VirtualMedia to support Proxy mode."/>
++ <Property Name="WebSocketEndpoint" Type="Edm.String">
++ <Annotation Term="OData.Permissions" EnumMember="OData.Permission/Read"/>
++ <Annotation Term="OData.Description" String="Indicates endpoint socket name and location."/>
++ <Annotation Term="OData.LongDescription" String="The value of this property shall be a structure ring indicating location and name of the socket used to communicate with nbd server."/>
++ </Property>
+ </EntityType>
+ </Schema>
+
+--
+2.25.0
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0006-Fix-Image-and-ImageName-values-in-schema.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0006-Fix-Image-and-ImageName-values-in-schema.patch
new file mode 100644
index 000000000..c182822a6
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0006-Fix-Image-and-ImageName-values-in-schema.patch
@@ -0,0 +1,38 @@
+From 15305d3a9db371af924482e5a6959bbf7812cf6c Mon Sep 17 00:00:00 2001
+From: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
+Date: Wed, 29 Jul 2020 15:56:57 +0200
+Subject: [PATCH] Fix Image and ImageName values in schema
+
+According to design document and schema Image shall contain URL of
+image location and ImageName only name of the image.
+
+Change-Id: Ie1a906c66aa2a10113c307eb1e7d2d7da2810fbd
+Signed-off-by: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
+---
+ redfish-core/lib/virtual_media.hpp | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 183abbe..0345e7b 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -97,7 +97,15 @@ static void vmParseInterfaceObject(const DbusInterfaceType& interface,
+ std::get_if<std::string>(&imageUrlProperty->second);
+ if (imageUrlValue && !imageUrlValue->empty())
+ {
+- aResp->res.jsonValue["ImageName"] = *imageUrlValue;
++ std::size_t lastIndex = imageUrlValue->rfind("/");
++ if (lastIndex == std::string::npos)
++ {
++ aResp->res.jsonValue["ImageName"] = *imageUrlValue;
++ }
++
++ aResp->res.jsonValue["ImageName"] =
++ imageUrlValue->substr(lastIndex + 1);
++ aResp->res.jsonValue["Image"] = *imageUrlValue;
+ aResp->res.jsonValue["Inserted"] = *activeValue;
+ if (*activeValue == true)
+ {
+--
+2.25.0
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch
new file mode 100644
index 000000000..3850c8fa8
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch
@@ -0,0 +1,913 @@
+From 7820421433349df28bd393e8d610d1848af0f1c8 Mon Sep 17 00:00:00 2001
+From: "Wludzik, Jozef" <jozef.wludzik@intel.com>
+Date: Mon, 27 Apr 2020 17:24:15 +0200
+Subject: [PATCH 1/5] Redfish TelemetryService schema implementation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Added TelemetryService, MetricReports, MetricReportCollection,
+MetricReportDefinition and MetricReportDefinitionCollection schemas
+with GET method support. Added TelemetryService URI to root service.
+Implemented communication with backend - MonitoringService.
+Added schemes attributes that are supported by MonitoringService
+design. User is able to fetch basic information about reports if
+MonitoringService is present in OpenBMC.
+
+Tested:
+ - Succesfully passed RedfishServiceValidator.py
+ - Validated conversion to duration format using whole
+ range of uint32_t type
+ - Validated assigning value to JSON response using different
+ closures and std::functions types
+
+Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
+Signed-off-by: Adrian Ambrożewicz <adrian.ambrozewicz@linux.intel.com>
+Change-Id: Ie6b0b49f4ef5eeaef07d1209b6c349270c04d570
+---
+ include/dbus_utility.hpp | 21 +++
+ redfish-core/include/redfish.hpp | 10 ++
+ redfish-core/include/utils/json_utils.hpp | 101 +++++++++++++
+ redfish-core/include/utils/telemetry_utils.hpp | 100 +++++++++++++
+ redfish-core/include/utils/time_utils.hpp | 97 +++++++++++++
+ redfish-core/lib/metric_report.hpp | 149 +++++++++++++++++++
+ redfish-core/lib/metric_report_definition.hpp | 193 +++++++++++++++++++++++++
+ redfish-core/lib/service_root.hpp | 2 +
+ redfish-core/lib/telemetry_service.hpp | 92 ++++++++++++
+ 9 files changed, 765 insertions(+)
+ create mode 100644 redfish-core/include/utils/telemetry_utils.hpp
+ create mode 100644 redfish-core/include/utils/time_utils.hpp
+ create mode 100644 redfish-core/lib/metric_report.hpp
+ create mode 100644 redfish-core/lib/metric_report_definition.hpp
+ create mode 100644 redfish-core/lib/telemetry_service.hpp
+
+diff --git a/include/dbus_utility.hpp b/include/dbus_utility.hpp
+index e1360f7..3df88d8 100644
+--- a/include/dbus_utility.hpp
++++ b/include/dbus_utility.hpp
+@@ -109,5 +109,26 @@ inline void checkDbusPathExists(const std::string& path, Callback&& callback)
+ std::array<std::string, 0>());
+ }
+
++template <typename Array, typename Callback>
++inline void getSubTreePaths(Callback&& callback, const std::string& path,
++ int depth, Array& interfaces)
++{
++ crow::connections::systemBus->async_method_call(
++ callback, "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", path, depth,
++ interfaces);
++}
++
++template <typename Callback>
++inline void getAllProperties(Callback&& callback, const std::string& service,
++ const std::string& path,
++ const std::string& interface)
++{
++ crow::connections::systemBus->async_method_call(
++ callback, service, path, "org.freedesktop.DBus.Properties", "GetAll",
++ interface);
++}
++
+ } // namespace utility
+ } // namespace dbus
+diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
+index cc98e1a..3d4c117 100644
+--- a/redfish-core/include/redfish.hpp
++++ b/redfish-core/include/redfish.hpp
+@@ -25,6 +25,8 @@
+ #include "../lib/log_services.hpp"
+ #include "../lib/managers.hpp"
+ #include "../lib/message_registries.hpp"
++#include "../lib/metric_report.hpp"
++#include "../lib/metric_report_definition.hpp"
+ #include "../lib/network_protocol.hpp"
+ #include "../lib/pcie.hpp"
+ #include "../lib/power.hpp"
+@@ -35,6 +37,7 @@
+ #include "../lib/storage.hpp"
+ #include "../lib/systems.hpp"
+ #include "../lib/task.hpp"
++#include "../lib/telemetry_service.hpp"
+ #include "../lib/thermal.hpp"
+ #include "../lib/update_service.hpp"
+ #ifdef BMCWEB_ENABLE_VM_NBDPROXY
+@@ -202,6 +205,13 @@ class RedfishService
+ nodes.emplace_back(std::make_unique<HypervisorInterface>(app));
+ nodes.emplace_back(std::make_unique<HypervisorSystem>(app));
+
++ nodes.emplace_back(std::make_unique<TelemetryService>(app));
++ nodes.emplace_back(
++ std::make_unique<MetricReportDefinitionCollection>(app));
++ nodes.emplace_back(std::make_unique<MetricReportDefinition>(app));
++ nodes.emplace_back(std::make_unique<MetricReportCollection>(app));
++ nodes.emplace_back(std::make_unique<MetricReport>(app));
++
+ for (const auto& node : nodes)
+ {
+ node->initPrivileges();
+diff --git a/redfish-core/include/utils/json_utils.hpp b/redfish-core/include/utils/json_utils.hpp
+index d578de4..fbb259d 100644
+--- a/redfish-core/include/utils/json_utils.hpp
++++ b/redfish-core/include/utils/json_utils.hpp
+@@ -13,15 +13,19 @@
+ // See the License for the specific language governing permissions and
+ // limitations under the License.
+ */
++
+ #pragma once
+
+ #include <http_request.h>
+ #include <http_response.h>
+
++#include <boost/container/flat_map.hpp>
+ #include <error_messages.hpp>
+ #include <nlohmann/json.hpp>
+
+ #include <bitset>
++#include <string>
++#include <variant>
+
+ namespace redfish
+ {
+@@ -436,5 +440,102 @@ bool getValueFromJsonObject(nlohmann::json& jsonData, const std::string& key,
+ return details::unpackValue(jsonValue, key, value);
+ }
+
++template <class T>
++struct IsStdFunction
++{
++ static constexpr bool value = false;
++};
++
++template <class T>
++struct IsStdFunction<std::function<T>>
++{
++ static constexpr bool value = true;
++};
++
++template <class T>
++constexpr bool is_std_function_v = IsStdFunction<T>::value;
++
++/**
++ * @brief Assign dbus property to http response attribute if property is stored
++ * on the map.
++ */
++template <typename T, typename S, typename... V>
++bool assignIfPresent(
++ const boost::container::flat_map<std::string, std::variant<V...>>& ret,
++ const char* propertyName, nlohmann::json& attribute, const S& convert)
++{
++ if constexpr (is_std_function_v<S>)
++ {
++ if (!convert)
++ {
++ BMCWEB_LOG_ERROR << "Passed empty target as convert argument";
++ return false;
++ }
++ }
++
++ auto found = ret.find(propertyName);
++ if (found != ret.end())
++ {
++ auto property = std::get_if<T>(&found->second);
++ if (property)
++ {
++ attribute = convert(*property);
++ return true;
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR << "Variant does not contain this type";
++ }
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR << "Element not found in map";
++ }
++
++ return false;
++}
++
++template <typename T, typename... V>
++bool assignIfPresent(
++ const boost::container::flat_map<std::string, std::variant<V...>>& ret,
++ const char* propertyName, nlohmann::json& attribute)
++{
++ return assignIfPresent<T>(ret, propertyName, attribute,
++ [](const T& v) -> T { return v; });
++}
++
++template <typename T, typename... V>
++bool assignIfPresent(
++ const boost::container::flat_map<std::string, std::variant<V...>>& ret,
++ const char* attributeName, crow::Response& res)
++{
++ return assignIfPresent<T>(ret, attributeName, res.jsonValue[attributeName]);
++}
++
++/**
++ * @brief Translate dbusPaths received from ObjectMapper into Redfish
++ * collection members and fill http response with those information.
++ */
++inline void dbusPathsToMembersArray(crow::Response& res,
++ const std::vector<std::string>& reports,
++ const char* path)
++{
++ nlohmann::json& members = res.jsonValue["Members"];
++ members = nlohmann::json::array();
++
++ for (const std::string& objpath : reports)
++ {
++ std::size_t lastPos = objpath.rfind("/");
++ if (lastPos == std::string::npos)
++ {
++ BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath;
++ continue;
++ }
++ members.push_back({{"@odata.id", path + objpath.substr(lastPos + 1)}});
++ }
++
++ res.jsonValue["Members@odata.count"] = members.size();
++}
++
+ } // namespace json_util
+ } // namespace redfish
+diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp
+new file mode 100644
+index 0000000..05ed00f
+--- /dev/null
++++ b/redfish-core/include/utils/telemetry_utils.hpp
+@@ -0,0 +1,100 @@
++/*
++// Copyright (c) 2018-2020 Intel Corporation
++//
++// Licensed under the Apache License, Version 2.0 (the "License");
++// you may not use this file except in compliance with the License.
++// You may obtain a copy of the License at
++//
++// http://www.apache.org/licenses/LICENSE-2.0
++//
++// Unless required by applicable law or agreed to in writing, software
++// distributed under the License is distributed on an "AS IS" BASIS,
++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++// See the License for the specific language governing permissions and
++// limitations under the License.
++*/
++
++#pragma once
++
++namespace redfish
++{
++
++namespace telemetry
++{
++
++static constexpr const char* metricReportDefinitionUri =
++ "/redfish/v1/TelemetryService/MetricReportDefinitions/";
++static constexpr const char* metricReportUri =
++ "/redfish/v1/TelemetryService/MetricReports/";
++static constexpr const char* reportInterface =
++ "xyz.openbmc_project.MonitoringService.Report";
++static constexpr const char* telemetryPath =
++ "/xyz/openbmc_project/MonitoringService/Reports/TelemetryService";
++
++static void getReportCollection(const std::shared_ptr<AsyncResp>& asyncResp,
++ const char* uri)
++{
++ const std::array<const char*, 1> interfaces = {reportInterface};
++
++ dbus::utility::getSubTreePaths(
++ [asyncResp, uri](const boost::system::error_code ec,
++ const std::vector<std::string>& reports) {
++ if (ec == boost::system::errc::no_such_file_or_directory)
++ {
++ asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
++ asyncResp->res.jsonValue["Members@odata.count"] = 0;
++ return;
++ }
++
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
++ return;
++ }
++
++ json_util::dbusPathsToMembersArray(asyncResp->res, reports, uri);
++ },
++ telemetryPath, 1, interfaces);
++}
++
++template <typename Callback>
++static void getReport(const std::shared_ptr<AsyncResp>& asyncResp,
++ const std::string& id, const char* schemaType,
++ const Callback&& callback)
++{
++ const std::array<const char*, 1> interfaces = {reportInterface};
++
++ dbus::utility::getSubTreePaths(
++ [asyncResp, id, schemaType,
++ callback](const boost::system::error_code ec,
++ const std::vector<std::string>& reports) {
++ if (ec == boost::system::errc::no_such_file_or_directory)
++ {
++ messages::resourceNotFound(asyncResp->res, schemaType, id);
++ return;
++ }
++
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
++ return;
++ }
++
++ const std::string target = "/xyz/openbmc_project/"
++ "MonitoringService/Reports/"
++ "TelemetryService/" +
++ id;
++ auto path = std::find(reports.begin(), reports.end(), target);
++ if (path == std::end(reports))
++ {
++ messages::resourceNotFound(asyncResp->res, schemaType, id);
++ return;
++ }
++ callback(asyncResp, *path, id);
++ },
++ telemetryPath, 1, interfaces);
++}
++} // namespace telemetry
++} // namespace redfish
+diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp
+new file mode 100644
+index 0000000..0256b3f
+--- /dev/null
++++ b/redfish-core/include/utils/time_utils.hpp
+@@ -0,0 +1,97 @@
++/*
++// Copyright (c) 2020 Intel Corporation
++//
++// Licensed under the Apache License, Version 2.0 (the "License");
++// you may not use this file except in compliance with the License.
++// You may obtain a copy of the License at
++//
++// http://www.apache.org/licenses/LICENSE-2.0
++//
++// Unless required by applicable law or agreed to in writing, software
++// distributed under the License is distributed on an "AS IS" BASIS,
++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++// See the License for the specific language governing permissions and
++// limitations under the License.
++*/
++
++#pragma once
++
++#include <boost/algorithm/string/trim.hpp>
++
++#include <chrono>
++#include <cstdint>
++#include <string>
++#include <type_traits>
++
++namespace redfish
++{
++
++namespace time_utils
++{
++
++namespace details
++{
++
++template <typename T>
++std::string toDurationFormatItem(std::chrono::milliseconds& duration,
++ const char* postfix)
++{
++ const auto t = std::chrono::duration_cast<T>(duration);
++ if (t.count() == 0)
++ {
++ return "";
++ }
++
++ std::stringstream ss;
++ if constexpr (std::is_same<T, std::chrono::milliseconds>::value)
++ {
++ ss << static_cast<float>(t.count()) /
++ static_cast<float>(std::chrono::milliseconds::period::den);
++ }
++ else
++ {
++ ss << t.count();
++ }
++ ss << postfix;
++ duration -= t;
++ return ss.str();
++}
++
++} // namespace details
++
++/**
++ * @brief Convert time value into duration format that is based on ISO 8601.
++ * Pattern: "-?P(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+(.\\d+)?S)?)?"
++ * Reference: "Redfish Telemetry White Paper".
++ */
++std::string toDurationFormat(const uint32_t ms)
++{
++ std::chrono::milliseconds duration(ms);
++ if (duration.count() == 0)
++ {
++ return "PT0S";
++ }
++
++ std::string fmt;
++ fmt.reserve(sizeof("PxxxDTxxHxxMxx.xxxxxxS"));
++
++ using Days = std::chrono::duration<int, std::ratio<24 * 60 * 60>>;
++
++ fmt += "P";
++ fmt += details::toDurationFormatItem<Days>(duration, "D");
++ if (duration.count() == 0)
++ {
++ return fmt;
++ }
++
++ fmt += "T";
++ fmt += details::toDurationFormatItem<std::chrono::hours>(duration, "H");
++ fmt += details::toDurationFormatItem<std::chrono::minutes>(duration, "M");
++ fmt +=
++ details::toDurationFormatItem<std::chrono::milliseconds>(duration, "S");
++
++ return fmt;
++}
++
++} // namespace time_utils
++} // namespace redfish
+diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp
+new file mode 100644
+index 0000000..a52d680
+--- /dev/null
++++ b/redfish-core/lib/metric_report.hpp
+@@ -0,0 +1,149 @@
++/*
++// Copyright (c) 2018-2020 Intel Corporation
++//
++// Licensed under the Apache License, Version 2.0 (the "License");
++// you may not use this file except in compliance with the License.
++// You may obtain a copy of the License at
++//
++// http://www.apache.org/licenses/LICENSE-2.0
++//
++// Unless required by applicable law or agreed to in writing, software
++// distributed under the License is distributed on an "AS IS" BASIS,
++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++// See the License for the specific language governing permissions and
++// limitations under the License.
++*/
++
++#pragma once
++
++#include "node.hpp"
++#include "utils/telemetry_utils.hpp"
++
++#include <boost/container/flat_map.hpp>
++
++#include <system_error>
++#include <variant>
++
++namespace redfish
++{
++
++class MetricReportCollection : public Node
++{
++ public:
++ MetricReportCollection(CrowApp& app) : Node(app, telemetry::metricReportUri)
++ {
++ entityPrivileges = {
++ {boost::beast::http::verb::get, {{"Login"}}},
++ {boost::beast::http::verb::head, {{"Login"}}},
++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
++ }
++
++ private:
++ void doGet(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>& params) override
++ {
++ res.jsonValue["@odata.type"] =
++ "#MetricReportCollection.MetricReportCollection";
++ res.jsonValue["@odata.id"] =
++ "/redfish/v1/TelemetryService/MetricReports";
++ res.jsonValue["Name"] = "Metric Report Collection";
++
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ telemetry::getReportCollection(asyncResp, telemetry::metricReportUri);
++ }
++};
++
++class MetricReport : public Node
++{
++ public:
++ MetricReport(CrowApp& app) :
++ Node(app, std::string(telemetry::metricReportUri) + "<str>/",
++ std::string())
++ {
++ entityPrivileges = {
++ {boost::beast::http::verb::get, {{"Login"}}},
++ {boost::beast::http::verb::head, {{"Login"}}},
++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
++ }
++
++ private:
++ void doGet(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>& params) override
++ {
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++
++ if (params.size() != 1)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ const std::string& id = params[0];
++ telemetry::getReport(asyncResp, id, schemaType, getReportProperties);
++ }
++
++ using Readings =
++ std::vector<std::tuple<std::string, std::string, double, int32_t>>;
++ using MetricValues = std::vector<std::map<std::string, std::string>>;
++
++ static MetricValues toMetricValues(const Readings& readings)
++ {
++ MetricValues metricValues;
++
++ for (auto& [id, metadata, sensorValue, timestamp] : readings)
++ {
++ metricValues.push_back({
++ {"MetricId", id},
++ {"MetricProperty", metadata},
++ {"MetricValue", std::to_string(sensorValue)},
++ {"Timestamp", crow::utility::getDateTime(timestamp)},
++ });
++ }
++
++ return metricValues;
++ }
++
++ static void getReportProperties(const std::shared_ptr<AsyncResp> asyncResp,
++ const std::string& reportPath,
++ const std::string& id)
++ {
++ asyncResp->res.jsonValue["@odata.type"] = schemaType;
++ asyncResp->res.jsonValue["@odata.id"] = telemetry::metricReportUri + id;
++ asyncResp->res.jsonValue["Id"] = id;
++ asyncResp->res.jsonValue["Name"] = id;
++ asyncResp->res.jsonValue["MetricReportDefinition"]["@odata.id"] =
++ telemetry::metricReportDefinitionUri + id;
++
++ dbus::utility::getAllProperties(
++ [asyncResp](
++ const boost::system::error_code ec,
++ const boost::container::flat_map<
++ std::string, std::variant<Readings, int32_t>>& ret) {
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
++ return;
++ }
++
++ json_util::assignIfPresent<int32_t>(
++ ret, "Timestamp", asyncResp->res.jsonValue["Timestamp"],
++ crow::utility::getDateTime);
++ json_util::assignIfPresent<Readings>(
++ ret, "Readings", asyncResp->res.jsonValue["MetricValues"],
++ toMetricValues);
++ },
++ "xyz.openbmc_project.MonitoringService", reportPath,
++ "xyz.openbmc_project.MonitoringService.Report");
++ }
++
++ static constexpr const char* schemaType =
++ "#MetricReport.v1_3_0.MetricReport";
++};
++} // namespace redfish
+diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp
+new file mode 100644
+index 0000000..d82ae59
+--- /dev/null
++++ b/redfish-core/lib/metric_report_definition.hpp
+@@ -0,0 +1,193 @@
++/*
++// Copyright (c) 2018-2020 Intel Corporation
++//
++// Licensed under the Apache License, Version 2.0 (the "License");
++// you may not use this file except in compliance with the License.
++// You may obtain a copy of the License at
++//
++// http://www.apache.org/licenses/LICENSE-2.0
++//
++// Unless required by applicable law or agreed to in writing, software
++// distributed under the License is distributed on an "AS IS" BASIS,
++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++// See the License for the specific language governing permissions and
++// limitations under the License.
++*/
++
++#pragma once
++
++#include "node.hpp"
++#include "utils/telemetry_utils.hpp"
++#include "utils/time_utils.hpp"
++
++#include <boost/container/flat_map.hpp>
++
++#include <system_error>
++#include <variant>
++
++namespace redfish
++{
++
++class MetricReportDefinitionCollection : public Node
++{
++ public:
++ MetricReportDefinitionCollection(CrowApp& app) :
++ Node(app, telemetry::metricReportDefinitionUri)
++ {
++ entityPrivileges = {
++ {boost::beast::http::verb::get, {{"Login"}}},
++ {boost::beast::http::verb::head, {{"Login"}}},
++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
++ }
++
++ private:
++ void doGet(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>& params) override
++ {
++ res.jsonValue["@odata.type"] = "#MetricReportDefinitionCollection."
++ "MetricReportDefinitionCollection";
++ res.jsonValue["@odata.id"] =
++ "/redfish/v1/TelemetryService/MetricReportDefinitions";
++ res.jsonValue["Name"] = "Metric Definition Collection";
++
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ telemetry::getReportCollection(asyncResp,
++ telemetry::metricReportDefinitionUri);
++ }
++};
++
++class MetricReportDefinition : public Node
++{
++ public:
++ MetricReportDefinition(CrowApp& app) :
++ Node(app, std::string(telemetry::metricReportDefinitionUri) + "<str>/",
++ std::string())
++ {
++ entityPrivileges = {
++ {boost::beast::http::verb::get, {{"Login"}}},
++ {boost::beast::http::verb::head, {{"Login"}}},
++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
++ }
++
++ private:
++ void doGet(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>& params) override
++ {
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++
++ if (params.size() != 1)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ const std::string& id = params[0];
++
++ telemetry::getReport(asyncResp, id, schemaType,
++ getReportDefinitonProperties);
++ }
++
++ static std::vector<std::string>
++ toReportActions(const std::vector<std::string>& actions)
++ {
++ const boost::container::flat_map<std::string, std::string>
++ reportActions = {
++ {"Event", "RedfishEvent"},
++ {"Log", "LogToMetricReportsCollection"},
++ };
++
++ std::vector<std::string> out;
++ for (auto& action : actions)
++ {
++ auto found = reportActions.find(action);
++ if (found != reportActions.end())
++ {
++ out.emplace_back(found->second);
++ }
++ }
++ return out;
++ }
++
++ using ReadingParameters =
++ std::vector<std::tuple<std::vector<sdbusplus::message::object_path>,
++ std::string, std::string, std::string>>;
++ using Metrics = std::vector<std::map<
++ std::string, std::variant<std::string, std::vector<std::string>>>>;
++
++ static Metrics toMetrics(const ReadingParameters& params)
++ {
++ Metrics metrics;
++
++ for (auto& [sensorPaths, operationType, id, metadata] : params)
++ {
++ metrics.push_back({
++ {"MetricId", id},
++ {"MetricProperties", std::vector<std::string>() = {metadata}},
++ });
++ }
++
++ return metrics;
++ }
++
++ static void
++ getReportDefinitonProperties(const std::shared_ptr<AsyncResp> asyncResp,
++ const std::string& reportPath,
++ const std::string& id)
++ {
++ asyncResp->res.jsonValue["@odata.type"] = schemaType;
++ asyncResp->res.jsonValue["@odata.id"] =
++ telemetry::metricReportDefinitionUri + id;
++ asyncResp->res.jsonValue["Id"] = id;
++ asyncResp->res.jsonValue["Name"] = id;
++ asyncResp->res.jsonValue["MetricReport"]["@odata.id"] =
++ telemetry::metricReportUri + id;
++ asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
++
++ dbus::utility::getAllProperties(
++ [asyncResp](const boost::system::error_code ec,
++ const boost::container::flat_map<
++ std::string,
++ std::variant<std::string, std::vector<std::string>,
++ uint32_t, ReadingParameters>>& ret) {
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
++ return;
++ }
++
++ json_util::assignIfPresent<std::vector<std::string>>(
++ ret, "ReportAction",
++ asyncResp->res.jsonValue["ReportActions"], toReportActions);
++ auto assigned = json_util::assignIfPresent<std::string>(
++ ret, "ReportingType",
++ asyncResp->res.jsonValue["MetricReportDefinitionType"]);
++ if (assigned &&
++ asyncResp->res.jsonValue["MetricReportDefinitionType"] ==
++ "Periodic")
++ {
++ json_util::assignIfPresent<uint32_t>(
++ ret, "ScanPeriod",
++ asyncResp->res
++ .jsonValue["Schedule"]["RecurrenceInterval"],
++ time_utils::toDurationFormat);
++ }
++ json_util::assignIfPresent<ReadingParameters>(
++ ret, "ReadingParameters",
++ asyncResp->res.jsonValue["Metrics"], toMetrics);
++ },
++ "xyz.openbmc_project.MonitoringService", reportPath,
++ "xyz.openbmc_project.MonitoringService.Report");
++ }
++
++ public:
++ static constexpr const char* schemaType =
++ "#MetricReportDefinition.v1_3_0.MetricReportDefinition";
++};
++} // namespace redfish
+diff --git a/redfish-core/lib/service_root.hpp b/redfish-core/lib/service_root.hpp
+index b6bd6e0..3302390 100644
+--- a/redfish-core/lib/service_root.hpp
++++ b/redfish-core/lib/service_root.hpp
+@@ -69,6 +69,8 @@ class ServiceRoot : public Node
+ res.jsonValue["Tasks"] = {{"@odata.id", "/redfish/v1/TaskService"}};
+ res.jsonValue["EventService"] = {
+ {"@odata.id", "/redfish/v1/EventService"}};
++ res.jsonValue["TelemetryService"] = {
++ {"@odata.id", "/redfish/v1/TelemetryService"}};
+ res.end();
+ }
+
+diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp
+new file mode 100644
+index 0000000..a410700
+--- /dev/null
++++ b/redfish-core/lib/telemetry_service.hpp
+@@ -0,0 +1,92 @@
++/*
++// Copyright (c) 2018-2020 Intel Corporation
++//
++// Licensed under the Apache License, Version 2.0 (the "License");
++// you may not use this file except in compliance with the License.
++// You may obtain a copy of the License at
++//
++// http://www.apache.org/licenses/LICENSE-2.0
++//
++// Unless required by applicable law or agreed to in writing, software
++// distributed under the License is distributed on an "AS IS" BASIS,
++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++// See the License for the specific language governing permissions and
++// limitations under the License.
++*/
++
++#pragma once
++
++#include "node.hpp"
++#include "utils/time_utils.hpp"
++
++#include <boost/container/flat_map.hpp>
++
++#include <variant>
++
++namespace redfish
++{
++
++class TelemetryService : public Node
++{
++ public:
++ TelemetryService(CrowApp& app) : Node(app, "/redfish/v1/TelemetryService/")
++ {
++ entityPrivileges = {
++ {boost::beast::http::verb::get, {{"Login"}}},
++ {boost::beast::http::verb::head, {{"Login"}}},
++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
++ }
++
++ private:
++ void doGet(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>& params) override
++ {
++ res.jsonValue["@odata.type"] =
++ "#TelemetryService.v1_2_0.TelemetryService";
++ res.jsonValue["@odata.id"] = "/redfish/v1/TelemetryService";
++ res.jsonValue["Id"] = "TelemetryService";
++ res.jsonValue["Name"] = "Telemetry Service";
++
++ res.jsonValue["LogService"]["@odata.id"] =
++ "/redfish/v1/Managers/bmc/LogServices/Journal";
++ res.jsonValue["MetricReportDefinitions"]["@odata.id"] =
++ "/redfish/v1/TelemetryService/MetricReportDefinitions";
++ res.jsonValue["MetricReports"]["@odata.id"] =
++ "/redfish/v1/TelemetryService/MetricReports";
++
++ getMonitoringServiceProperties(res);
++ }
++
++ void getMonitoringServiceProperties(crow::Response& res)
++ {
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ dbus::utility::getAllProperties(
++ [asyncResp](
++ const boost::system::error_code ec,
++ const boost::container::flat_map<std::string,
++ std::variant<uint32_t>>& ret) {
++ if (ec)
++ {
++ asyncResp->res.jsonValue["Status"]["State"] = "Absent";
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
++ return;
++ }
++
++ asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
++
++ json_util::assignIfPresent<uint32_t>(ret, "MaxReports",
++ asyncResp->res);
++ json_util::assignIfPresent<uint32_t>(
++ ret, "PollRateResolution",
++ asyncResp->res.jsonValue["MinCollectionInterval"],
++ time_utils::toDurationFormat);
++ },
++ "xyz.openbmc_project.MonitoringService",
++ "/xyz/openbmc_project/MonitoringService/Reports",
++ "xyz.openbmc_project.MonitoringService.ReportsManagement");
++ }
++};
++} // namespace redfish
+--
+2.16.6
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch
new file mode 100644
index 000000000..8a8690bf3
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch
@@ -0,0 +1,594 @@
+From 941be2c7d819b4a55d5a8b67948e53658d907789 Mon Sep 17 00:00:00 2001
+From: "Wludzik, Jozef" <jozef.wludzik@intel.com>
+Date: Mon, 18 May 2020 11:56:57 +0200
+Subject: [PATCH 2/5] Add support for POST in MetricReportDefinitions
+
+Added POST action in MetricReportDefinitions node to allow user
+to add new MetricReportDefinition. Using minimal set of
+MetricReportDefinition parameters from user bmcweb converts it to
+DBus call "AddReport" to MonitoringService that serves as a backend
+for TelemetryService.
+
+Tested:
+ - Succesfully passed RedfishServiceValidator.py
+ - Validated good cases with different parameters for POST action
+ - Validated bad cases with different parameters for POST action
+ - Validated fromDurationFormat() with range of arguments starting
+ from PT0.0S up to P49D (it is an upper limit for uint32_t)
+
+Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
+Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Change-Id: I2fed96848594451e22fde686f8c066d7770cc65a
+---
+ redfish-core/include/utils/time_utils.hpp | 49 +++
+ .../include/utils/validate_params_length.hpp | 109 +++++++
+ redfish-core/lib/metric_report_definition.hpp | 347 +++++++++++++++++++++
+ 3 files changed, 505 insertions(+)
+ create mode 100644 redfish-core/include/utils/validate_params_length.hpp
+
+diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp
+index 0256b3f..c365585 100644
+--- a/redfish-core/include/utils/time_utils.hpp
++++ b/redfish-core/include/utils/time_utils.hpp
+@@ -57,6 +57,32 @@ std::string toDurationFormatItem(std::chrono::milliseconds& duration,
+ return ss.str();
+ }
+
++template <typename T>
++static long long fromDurationFormatItem(std::string_view& fmt,
++ const char* postfix)
++{
++ auto pos = fmt.find(postfix);
++ if (pos == std::string::npos)
++ {
++ return 0;
++ }
++
++ long out;
++ if constexpr (std::is_same<T, std::chrono::milliseconds>::value)
++ {
++ /* Half point is added to avoid numeric error on rounding */
++ out = static_cast<long>(std::strtof(fmt.data(), nullptr) *
++ std::chrono::milliseconds::period::den +
++ 0.5f);
++ }
++ else
++ {
++ out = std::strtol(fmt.data(), nullptr, 10);
++ }
++ fmt.remove_prefix(pos + 1);
++ return std::chrono::milliseconds(T(out)).count();
++}
++
+ } // namespace details
+
+ /**
+@@ -93,5 +119,28 @@ std::string toDurationFormat(const uint32_t ms)
+ return fmt;
+ }
+
++static uint32_t fromDurationFormat(std::string_view fmt)
++{
++ if (fmt.empty() || fmt[0] != 'P')
++ {
++ return 0;
++ }
++ using Days = std::chrono::duration<int, std::ratio<24 * 60 * 60>>;
++
++ fmt.remove_prefix(1);
++ auto out = details::fromDurationFormatItem<Days>(fmt, "D");
++ if (fmt[0] != 'T')
++ {
++ return static_cast<uint32_t>(out);
++ }
++
++ fmt.remove_prefix(1);
++ out += details::fromDurationFormatItem<std::chrono::hours>(fmt, "H");
++ out += details::fromDurationFormatItem<std::chrono::minutes>(fmt, "M");
++ out += details::fromDurationFormatItem<std::chrono::milliseconds>(fmt, "S");
++
++ return static_cast<uint32_t>(out);
++}
++
+ } // namespace time_utils
+ } // namespace redfish
+diff --git a/redfish-core/include/utils/validate_params_length.hpp b/redfish-core/include/utils/validate_params_length.hpp
+new file mode 100644
+index 0000000..c4e0569
+--- /dev/null
++++ b/redfish-core/include/utils/validate_params_length.hpp
+@@ -0,0 +1,109 @@
++#pragma once
++
++namespace redfish
++{
++namespace detail
++{
++template <class Limits, size_t... Seq>
++bool validateParamsLength(crow::Response& res, Limits&& limits,
++ std::index_sequence<Seq...>)
++{
++ return (... && std::get<Seq>(limits).validate(res));
++}
++} // namespace detail
++
++template <class T>
++struct ItemSizeValidator
++{
++ ItemSizeValidator(const T&& item, std::string_view name, size_t limit) :
++ item(std::forward<const T>(item)), name(name), limit(limit)
++ {}
++
++ bool validate(crow::Response& res) const
++ {
++ return ItemSizeValidator<T>::validateItem(res, item, name, limit);
++ }
++
++ private:
++ static bool validateItem(crow::Response& res, size_t item,
++ std::string_view name, size_t limit)
++ {
++ if (item > static_cast<size_t>(limit))
++ {
++ messages::stringValueTooLong(res, std::string(name),
++ static_cast<int>(limit));
++ return false;
++ }
++ return true;
++ }
++
++ static bool validateItem(crow::Response& res, std::string_view item,
++ std::string_view name, size_t limit)
++ {
++ return validateItem(res, item.size(), name, limit);
++ }
++
++ static bool validateItem(crow::Response& res, const std::string& item,
++ std::string_view name, size_t limit)
++ {
++ return validateItem(res, item.size(), name, limit);
++ }
++
++ static bool validateItem(crow::Response& res,
++ const sdbusplus::message::object_path& item,
++ std::string_view name, size_t limit)
++ {
++ return validateItem(res, item.str.size(), name, limit);
++ }
++
++ T item;
++ std::string_view name;
++ size_t limit;
++};
++
++template <class T>
++ItemSizeValidator(const T&, std::string_view, size_t)
++ -> ItemSizeValidator<const T&>;
++
++ItemSizeValidator(const char*, std::string_view, size_t)
++ ->ItemSizeValidator<std::string_view>;
++
++template <class ContainerT>
++struct ArrayItemsValidator
++{
++ ArrayItemsValidator(const ContainerT& item, std::string_view name,
++ size_t limit) :
++ item(item),
++ name(name), limit(limit)
++ {}
++
++ bool validate(crow::Response& res) const
++ {
++ return std::all_of(
++ item.begin(), item.end(), [&res, this](const auto& item) {
++ return ItemSizeValidator(item, name, limit).validate(res);
++ });
++ }
++
++ private:
++ const ContainerT& item;
++ std::string_view name;
++ size_t limit;
++};
++
++template <class T>
++bool validateParamLength(crow::Response& res, T&& item, std::string_view name,
++ size_t limit)
++{
++ return ItemSizeValidator(std::forward<T>(item), name, limit).validate(res);
++}
++
++template <class Limits>
++bool validateParamsLength(crow::Response& res, Limits&& limits)
++{
++ return detail::validateParamsLength(
++ res, std::forward<Limits>(limits),
++ std::make_index_sequence<std::tuple_size_v<std::decay_t<Limits>>>());
++}
++
++} // namespace redfish
+diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp
+index d82ae59..ecbab0c 100644
+--- a/redfish-core/lib/metric_report_definition.hpp
++++ b/redfish-core/lib/metric_report_definition.hpp
+@@ -17,16 +17,29 @@
+ #pragma once
+
+ #include "node.hpp"
++#include "sensors.hpp"
+ #include "utils/telemetry_utils.hpp"
+ #include "utils/time_utils.hpp"
++#include "utils/validate_params_length.hpp"
+
++#include <boost/algorithm/string/join.hpp>
++#include <boost/algorithm/string/split.hpp>
+ #include <boost/container/flat_map.hpp>
+
++#include <regex>
+ #include <system_error>
++#include <tuple>
+ #include <variant>
+
+ namespace redfish
+ {
++static constexpr size_t maxShortParamLength = 255;
++static constexpr size_t maxLongParamLength = 1024;
++static constexpr size_t maxDbusNameLength = 255;
++static constexpr size_t maxArraySize = 100;
++static constexpr size_t maxReportIdLen =
++ maxDbusNameLength - std::string_view(telemetry::telemetryPath).size() -
++ std::string_view("/").size();
+
+ class MetricReportDefinitionCollection : public Node
+ {
+@@ -57,6 +70,339 @@ class MetricReportDefinitionCollection : public Node
+ telemetry::getReportCollection(asyncResp,
+ telemetry::metricReportDefinitionUri);
+ }
++
++ using ChassisSensorNode = std::pair<std::string, std::string>;
++ using DbusSensor = sdbusplus::message::object_path;
++ using DbusSensors = std::vector<DbusSensor>;
++ using MetricParam =
++ std::tuple<DbusSensors, std::string, std::string, std::string>;
++ using MetricParams = std::vector<MetricParam>;
++ /*
++ * AddReportArgs misses "Domain" parameter because it is constant for
++ * TelemetryService and equals "TelemetryService".
++ */
++ using AddReportArgs =
++ std::tuple<std::string, std::string, std::vector<std::string>, uint32_t,
++ MetricParams>;
++
++ void doPost(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>& params) override
++ {
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ AddReportArgs addReportArgs;
++ if (!getUserParameters(res, req, addReportArgs))
++ {
++ return;
++ }
++
++ boost::container::flat_set<ChassisSensorNode> chassisSensorSet;
++ auto unmatched = getChassisSensorNode(
++ std::get<MetricParams>(addReportArgs), chassisSensorSet);
++ if (unmatched)
++ {
++ messages::resourceNotFound(asyncResp->res, "MetricProperties",
++ *unmatched);
++ return;
++ }
++
++ auto addReportReq =
++ std::make_shared<AddReport>(addReportArgs, asyncResp);
++ for (auto& [chassis, sensorType] : chassisSensorSet)
++ {
++ retrieveUriToDbusMap(
++ chassis, sensorType,
++ [asyncResp, addReportReq](
++ const boost::beast::http::status status,
++ const boost::container::flat_map<std::string, std::string>&
++ uriToDbus) { *addReportReq += uriToDbus; });
++ }
++ }
++
++ static std::optional<std::string>
++ replaceReportActions(std::vector<std::string>& actions)
++ {
++ const boost::container::flat_map<std::string, std::string>
++ reportActions = {
++ {"RedfishEvent", "Event"},
++ {"LogToMetricReportsCollection", "Log"},
++ };
++
++ for (auto& action : actions)
++ {
++ auto found = reportActions.find(action);
++ if (found == reportActions.end())
++ {
++ return action;
++ }
++ action = found->second;
++ }
++ return std::nullopt;
++ }
++
++ static constexpr const std::array<const char*, 2> supportedDefinitionType =
++ {"Periodic", "OnRequest"};
++
++ static bool getUserParameters(crow::Response& res, const crow::Request& req,
++ AddReportArgs& params)
++ {
++ std::vector<nlohmann::json> metrics;
++ std::optional<nlohmann::json> schedule;
++ auto& [name, reportingType, reportActions, scanPeriod, metricParams] =
++ params;
++ if (!json_util::readJson(req, res, "Id", name, "Metrics", metrics,
++ "MetricReportDefinitionType", reportingType,
++ "ReportActions", reportActions, "Schedule",
++ schedule))
++ {
++ return false;
++ }
++
++ auto limits = std::make_tuple(
++ ItemSizeValidator(name, "Id", maxReportIdLen),
++ ItemSizeValidator(reportingType, "MetricReportDefinitionType",
++ maxShortParamLength),
++ ItemSizeValidator(reportActions.size(), "ReportActions.size()",
++ maxArraySize),
++ ArrayItemsValidator(reportActions, "ReportActions",
++ maxShortParamLength),
++ ItemSizeValidator(metrics.size(), "Metrics.size()", maxArraySize));
++
++ if (!validateParamsLength(res, std::move(limits)))
++ {
++ return false;
++ }
++
++ constexpr const char* allowedCharactersInName =
++ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
++ "_";
++ if (name.empty() || name.find_first_not_of(allowedCharactersInName) !=
++ std::string::npos)
++ {
++ BMCWEB_LOG_ERROR << "Failed to match " << name
++ << " with allowed character "
++ << allowedCharactersInName;
++ messages::propertyValueFormatError(res, name, "Id");
++ return false;
++ }
++
++ if (!std::any_of(
++ supportedDefinitionType.begin(), supportedDefinitionType.end(),
++ [reportingType](auto& x) { return reportingType == x; }))
++ {
++ messages::propertyValueNotInList(res, reportingType,
++ "MetricReportDefinitionType");
++ return false;
++ }
++
++ auto unmatched = replaceReportActions(reportActions);
++ if (unmatched)
++ {
++ messages::propertyValueNotInList(res, *unmatched, "ReportActions");
++ return false;
++ }
++
++ if (reportingType == "Periodic")
++ {
++ if (!schedule)
++ {
++ messages::createFailedMissingReqProperties(res, "Schedule");
++ return false;
++ }
++
++ std::string interval;
++ if (!json_util::readJson(*schedule, res, "RecurrenceInterval",
++ interval))
++ {
++ return false;
++ }
++
++ if (!validateParamLength(res, interval, "RecurrenceInterval",
++ maxShortParamLength))
++ {
++ return false;
++ }
++
++ constexpr const char* durationPattern =
++ "-?P(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+(.\\d+)?S)?)?";
++ if (!std::regex_match(interval, std::regex(durationPattern)))
++ {
++ messages::propertyValueFormatError(res, interval,
++ "RecurrenceInterval");
++ return false;
++ }
++
++ scanPeriod = time_utils::fromDurationFormat(interval);
++ }
++
++ return fillMetricParams(metrics, res, metricParams);
++ }
++
++ static bool fillMetricParams(std::vector<nlohmann::json>& metrics,
++ crow::Response& res,
++ MetricParams& metricParams)
++ {
++ metricParams.reserve(metrics.size());
++ for (auto& m : metrics)
++ {
++ std::string metricId;
++ std::vector<std::string> metricProperties;
++ if (!json_util::readJson(m, res, "MetricId", metricId,
++ "MetricProperties", metricProperties))
++ {
++ return false;
++ }
++
++ auto limits = std::make_tuple(
++ ItemSizeValidator(metricId, "MetricId", maxLongParamLength),
++ ItemSizeValidator(metricProperties.size(),
++ "MetricProperties.size()", maxArraySize),
++ ArrayItemsValidator(metricProperties, "MetricProperties",
++ maxLongParamLength));
++
++ if (!validateParamsLength(res, std::move(limits)))
++ {
++ return false;
++ }
++
++ DbusSensors dbusSensors;
++ dbusSensors.reserve(metricProperties.size());
++ std::for_each(
++ metricProperties.begin(), metricProperties.end(),
++ [&dbusSensors](auto& x) { dbusSensors.emplace_back(x); });
++
++ metricParams.emplace_back(
++ dbusSensors, "SINGLE", metricId,
++ boost::algorithm::join(metricProperties, ", "));
++ }
++ return true;
++ }
++
++ static std::optional<std::string> getChassisSensorNode(
++ const MetricParams& metricParams,
++ boost::container::flat_set<ChassisSensorNode>& matched)
++ {
++ for (const auto& metricParam : metricParams)
++ {
++ const auto& sensors = std::get<DbusSensors>(metricParam);
++ for (const auto& sensor : sensors)
++ {
++ /*
++ * Support only following paths:
++ * "/redfish/v1/Chassis/<chassis>/Power#/..."
++ * "/redfish/v1/Chassis/<chassis>/Sensors/..."
++ * "/redfish/v1/Chassis/<chassis>/Thermal#/..."
++ */
++ constexpr const char* uriPattern =
++ "\\/redfish\\/v1\\/Chassis\\/(\\w+)\\/"
++ "(Power|Sensors|Thermal)[#]?\\/.*";
++ std::smatch m;
++ if (!std::regex_match(sensor.str, m, std::regex(uriPattern)) ||
++ m.size() != 3)
++ {
++ BMCWEB_LOG_ERROR << "Failed to match " << sensor.str
++ << " with pattern " << uriPattern;
++ return sensor;
++ }
++
++ BMCWEB_LOG_DEBUG << "Chassis=" << m[1] << ", Type=" << m[2];
++ matched.emplace(m[1], m[2]);
++ }
++ }
++ return std::nullopt;
++ }
++
++ static std::optional<std::string> replaceUriWithDbus(
++ const boost::container::flat_map<std::string, std::string>& uriToDbus,
++ MetricParams& metricParams)
++ {
++ for (auto& metricParam : metricParams)
++ {
++ auto& dbusSensors = std::get<DbusSensors>(metricParam);
++ for (auto& uri : dbusSensors)
++ {
++ auto dbus = uriToDbus.find(uri);
++ if (dbus == uriToDbus.end())
++ {
++ BMCWEB_LOG_ERROR << "Failed to find DBus sensor "
++ "corresponding to URI "
++ << uri.str;
++ return uri;
++ }
++ uri = dbus->second;
++ }
++ }
++ return std::nullopt;
++ }
++
++ static void addReport(std::shared_ptr<AsyncResp> asyncResp,
++ AddReportArgs args)
++ {
++ constexpr const char* domain = "TelemetryService";
++ auto& [name, reportingType, reportActions, scanPeriod, metricParams] =
++ args;
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, name](const boost::system::error_code ec,
++ const std::string ret) {
++ if (ec == boost::system::errc::file_exists)
++ {
++ messages::resourceAlreadyExists(
++ asyncResp->res, "MetricReportDefinition", "Id", name);
++ 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;
++ }
++
++ messages::created(asyncResp->res);
++ },
++ "xyz.openbmc_project.MonitoringService",
++ "/xyz/openbmc_project/MonitoringService/Reports",
++ "xyz.openbmc_project.MonitoringService.ReportsManagement",
++ "AddReport", name, domain, reportingType, reportActions, scanPeriod,
++ metricParams);
++ }
++
++ class AddReport
++ {
++ public:
++ AddReport(AddReportArgs& args, std::shared_ptr<AsyncResp>& asyncResp) :
++ asyncResp{asyncResp}, addReportArgs{args}
++ {}
++ ~AddReport()
++ {
++ auto unmatched = replaceUriWithDbus(
++ uriToDbus, std::get<MetricParams>(addReportArgs));
++ if (unmatched)
++ {
++ messages::resourceNotFound(asyncResp->res, "MetricProperties",
++ *unmatched);
++ return;
++ }
++
++ addReport(asyncResp, addReportArgs);
++ }
++
++ AddReport& operator+=(
++ const boost::container::flat_map<std::string, std::string>& rhs)
++ {
++ this->uriToDbus.insert(rhs.begin(), rhs.end());
++ return *this;
++ }
++
++ private:
++ std::shared_ptr<AsyncResp> asyncResp;
++ AddReportArgs addReportArgs;
++ boost::container::flat_map<std::string, std::string> uriToDbus{};
++ };
+ };
+
+ class MetricReportDefinition : public Node
+@@ -148,6 +494,7 @@ class MetricReportDefinition : public Node
+ asyncResp->res.jsonValue["MetricReport"]["@odata.id"] =
+ telemetry::metricReportUri + id;
+ asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
++ asyncResp->res.jsonValue["ReportUpdates"] = "Overwrite";
+
+ dbus::utility::getAllProperties(
+ [asyncResp](const boost::system::error_code ec,
+--
+2.16.6
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-DELETE-in-MetricReportDefinitions-st.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-DELETE-in-MetricReportDefinitions-st.patch
new file mode 100644
index 000000000..4c49b0cd3
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-DELETE-in-MetricReportDefinitions-st.patch
@@ -0,0 +1,68 @@
+From 8b2f4a6fe57bf2410cdb22f8c3c695e98d583040 Mon Sep 17 00:00:00 2001
+From: "Wludzik, Jozef" <jozef.wludzik@intel.com>
+Date: Mon, 18 May 2020 12:40:15 +0200
+Subject: [PATCH 3/5] Add support for DELETE in MetricReportDefinitions/<str>
+
+Added support for DELETE action in MetricReportDefinitions/<str>
+node. It allows user to remove MetricReportDefinition together
+with MetricReport connected to it.
+
+Tested:
+ - Succesfully passed RedfishServiceValidator.py
+ - Validated DELETE action by removing exisiting
+ MetricReportDefinitions from MonitoringService
+ - Validated DELETE action with negative cases when
+ MetricReportDefinition does not exist
+
+Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
+Change-Id: Iffde9f7bbf2955376e9714ac8d833967bd25eaa3
+---
+ redfish-core/lib/metric_report_definition.hpp | 32 +++++++++++++++++++++++++++
+ 1 file changed, 32 insertions(+)
+
+diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp
+index ecbab0c..8e04ac8 100644
+--- a/redfish-core/lib/metric_report_definition.hpp
++++ b/redfish-core/lib/metric_report_definition.hpp
+@@ -533,6 +533,38 @@ class MetricReportDefinition : public Node
+ "xyz.openbmc_project.MonitoringService.Report");
+ }
+
++ void doDelete(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>& params) override
++ {
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ if (params.size() != 1)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ const std::string& id = params[0];
++ telemetry::getReport(asyncResp, id, schemaType, deleteReport);
++ }
++
++ static void deleteReport(const std::shared_ptr<AsyncResp>& asyncResp,
++ const std::string& path, const std::string& id)
++ {
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ asyncResp->res.result(boost::beast::http::status::no_content);
++ },
++ "xyz.openbmc_project.MonitoringService", path,
++ "xyz.openbmc_project.Object.Delete", "Delete");
++ }
++
+ public:
+ static constexpr const char* schemaType =
+ "#MetricReportDefinition.v1_3_0.MetricReportDefinition";
+--
+2.16.6
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-OnRequest-in-MetricReportDefinition.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-OnRequest-in-MetricReportDefinition.patch
new file mode 100644
index 000000000..e996ac585
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-OnRequest-in-MetricReportDefinition.patch
@@ -0,0 +1,169 @@
+From 9fc7d722b3192df9940062185b40ebb0fabad518 Mon Sep 17 00:00:00 2001
+From: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Date: Mon, 8 Jun 2020 15:16:10 +0200
+Subject: [PATCH 4/5] Add support for "OnRequest" in MetricReportDefinition
+
+Added support for "OnRequest" of ReportingType property in
+MetricReportDefinition node. Now user is able to create
+MetricReportDefinition that is updated on every GET request
+on MetricReport.
+
+Tested:
+ - Succesfully passed RedfishServiceValidator.py
+ - Manually tested via curl
+
+Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Change-Id: I1cdfe47e56fdc5ec9753558145d0bf3645160aaf
+---
+ include/dbus_utility.hpp | 30 +++++++++++++++
+ redfish-core/include/utils/telemetry_utils.hpp | 8 ++--
+ redfish-core/lib/metric_report.hpp | 53 +++++++++++++++++++++++++-
+ 3 files changed, 87 insertions(+), 4 deletions(-)
+
+diff --git a/include/dbus_utility.hpp b/include/dbus_utility.hpp
+index 3df88d8..029d8d8 100644
+--- a/include/dbus_utility.hpp
++++ b/include/dbus_utility.hpp
+@@ -17,6 +17,7 @@
+
+ #include <sdbusplus/message.hpp>
+
++#include <functional>
+ #include <regex>
+
+ namespace dbus
+@@ -130,5 +131,34 @@ inline void getAllProperties(Callback&& callback, const std::string& service,
+ interface);
+ }
+
++template <typename T>
++static void getProperty(
++ std::function<void(const boost::system::error_code&, const T&)> callback,
++ const std::string& service, const std::string& path,
++ const std::string& interface, const std::string& property)
++{
++ crow::connections::systemBus->async_method_call(
++ [callback](const boost::system::error_code ec,
++ const std::variant<T>& value) {
++ if (ec)
++ {
++ callback(ec, T{});
++ return;
++ }
++
++ if (auto v = std::get_if<T>(&value))
++ {
++ callback(ec, *v);
++ return;
++ }
++
++ callback(boost::system::errc::make_error_code(
++ boost::system::errc::io_error),
++ T{});
++ },
++ service, path, "org.freedesktop.DBus.Properties", "Get", interface,
++ property);
++}
++
+ } // namespace utility
+ } // namespace dbus
+diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp
+index 05ed00f..6c4e810 100644
+--- a/redfish-core/include/utils/telemetry_utils.hpp
++++ b/redfish-core/include/utils/telemetry_utils.hpp
+@@ -26,6 +26,8 @@ static constexpr const char* metricReportDefinitionUri =
+ "/redfish/v1/TelemetryService/MetricReportDefinitions/";
+ static constexpr const char* metricReportUri =
+ "/redfish/v1/TelemetryService/MetricReports/";
++static constexpr const char* monitoringService =
++ "xyz.openbmc_project.MonitoringService";
+ static constexpr const char* reportInterface =
+ "xyz.openbmc_project.MonitoringService.Report";
+ static constexpr const char* telemetryPath =
+@@ -66,9 +68,9 @@ static void getReport(const std::shared_ptr<AsyncResp>& asyncResp,
+ const std::array<const char*, 1> interfaces = {reportInterface};
+
+ dbus::utility::getSubTreePaths(
+- [asyncResp, id, schemaType,
+- callback](const boost::system::error_code ec,
+- const std::vector<std::string>& reports) {
++ [asyncResp, id, schemaType, callback = std::move(callback)](
++ const boost::system::error_code ec,
++ const std::vector<std::string>& reports) {
+ if (ec == boost::system::errc::no_such_file_or_directory)
+ {
+ messages::resourceNotFound(asyncResp->res, schemaType, id);
+diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp
+index a52d680..877e7f1 100644
+--- a/redfish-core/lib/metric_report.hpp
++++ b/redfish-core/lib/metric_report.hpp
+@@ -85,7 +85,7 @@ class MetricReport : public Node
+ }
+
+ const std::string& id = params[0];
+- telemetry::getReport(asyncResp, id, schemaType, getReportProperties);
++ telemetry::getReport(asyncResp, id, schemaType, updateReportIfRequired);
+ }
+
+ using Readings =
+@@ -143,6 +143,57 @@ class MetricReport : public Node
+ "xyz.openbmc_project.MonitoringService.Report");
+ }
+
++ template <typename Callback>
++ static void updateReport(Callback&& callback,
++ const std::shared_ptr<AsyncResp>& asyncResp,
++ const std::string& path)
++ {
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, callback{std::move(callback)}](
++ const boost::system::error_code& ec) {
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ callback();
++ },
++ telemetry::monitoringService, path, telemetry::reportInterface,
++ "Update");
++ }
++
++ static void
++ updateReportIfRequired(const std::shared_ptr<AsyncResp> asyncResp,
++ const std::string& reportPath,
++ const std::string& id)
++ {
++ dbus::utility::getProperty<std::string>(
++ [asyncResp, id, reportPath](const boost::system::error_code& ec,
++ const std::string& reportingType) {
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ if (reportingType == "OnRequest")
++ {
++ updateReport(
++ [asyncResp, reportPath, id] {
++ getReportProperties(asyncResp, reportPath, id);
++ },
++ asyncResp, reportPath);
++ }
++ else
++ {
++ getReportProperties(asyncResp, reportPath, id);
++ }
++ },
++ telemetry::monitoringService, reportPath,
++ telemetry::reportInterface, "ReportingType");
++ }
++
+ static constexpr const char* schemaType =
+ "#MetricReport.v1_3_0.MetricReport";
+ };
+--
+2.16.6
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Add-support-for-MetricDefinition-scheme.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Add-support-for-MetricDefinition-scheme.patch
new file mode 100644
index 000000000..f7da8a556
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Add-support-for-MetricDefinition-scheme.patch
@@ -0,0 +1,535 @@
+From b1da8901b5985d6a77b63ca9eb0570b46528f0bd Mon Sep 17 00:00:00 2001
+From: "Wludzik, Jozef" <jozef.wludzik@intel.com>
+Date: Mon, 8 Jun 2020 17:15:54 +0200
+Subject: [PATCH 5/5] Add support for MetricDefinition scheme
+
+Added MetricDefinition node to redfish core. Now user is able to
+get all possible metrics that are present in system and are
+supported by TelemetryService.
+
+Tested:
+ - Succesfully passed RedfishServiceValidator.py
+ - Validated a presence of MetricDefinition members
+
+Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
+Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Change-Id: I3086e1302e1ba2e5442d1367939fd5507a0cbc00
+---
+ redfish-core/include/redfish.hpp | 3 +
+ redfish-core/include/utils/telemetry_utils.hpp | 2 +
+ redfish-core/lib/metric_definition.hpp | 300 +++++++++++++++++++++++++
+ redfish-core/lib/metric_report.hpp | 65 +++++-
+ redfish-core/lib/sensors.hpp | 43 +++-
+ redfish-core/lib/telemetry_service.hpp | 2 +
+ 6 files changed, 402 insertions(+), 13 deletions(-)
+ create mode 100644 redfish-core/lib/metric_definition.hpp
+
+diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
+index 3d4c117..2a12bf9 100644
+--- a/redfish-core/include/redfish.hpp
++++ b/redfish-core/include/redfish.hpp
+@@ -25,6 +25,7 @@
+ #include "../lib/log_services.hpp"
+ #include "../lib/managers.hpp"
+ #include "../lib/message_registries.hpp"
++#include "../lib/metric_definition.hpp"
+ #include "../lib/metric_report.hpp"
+ #include "../lib/metric_report_definition.hpp"
+ #include "../lib/network_protocol.hpp"
+@@ -206,6 +207,8 @@ class RedfishService
+ nodes.emplace_back(std::make_unique<HypervisorSystem>(app));
+
+ nodes.emplace_back(std::make_unique<TelemetryService>(app));
++ nodes.emplace_back(std::make_unique<MetricDefinitionCollection>(app));
++ nodes.emplace_back(std::make_unique<MetricDefinition>(app));
+ nodes.emplace_back(
+ std::make_unique<MetricReportDefinitionCollection>(app));
+ nodes.emplace_back(std::make_unique<MetricReportDefinition>(app));
+diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp
+index 6c4e810..bb747c4 100644
+--- a/redfish-core/include/utils/telemetry_utils.hpp
++++ b/redfish-core/include/utils/telemetry_utils.hpp
+@@ -22,6 +22,8 @@ namespace redfish
+ namespace telemetry
+ {
+
++static constexpr const char* metricDefinitionUri =
++ "/redfish/v1/TelemetryService/MetricDefinitions/";
+ static constexpr const char* metricReportDefinitionUri =
+ "/redfish/v1/TelemetryService/MetricReportDefinitions/";
+ static constexpr const char* metricReportUri =
+diff --git a/redfish-core/lib/metric_definition.hpp b/redfish-core/lib/metric_definition.hpp
+new file mode 100644
+index 0000000..837a068
+--- /dev/null
++++ b/redfish-core/lib/metric_definition.hpp
+@@ -0,0 +1,300 @@
++/*
++// Copyright (c) 2018-2020 Intel Corporation
++//
++// Licensed under the Apache License, Version 2.0 (the "License");
++// you may not use this file except in compliance with the License.
++// You may obtain a copy of the License at
++//
++// http://www.apache.org/licenses/LICENSE-2.0
++//
++// Unless required by applicable law or agreed to in writing, software
++// distributed under the License is distributed on an "AS IS" BASIS,
++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++// See the License for the specific language governing permissions and
++// limitations under the License.
++*/
++
++#pragma once
++
++#include "node.hpp"
++#include "sensors.hpp"
++#include "utils/telemetry_utils.hpp"
++
++namespace redfish
++{
++
++namespace chassis
++{
++template <typename F>
++static inline void getChassisNames(F&& callback)
++{
++ const std::array<const char*, 2> interfaces = {
++ "xyz.openbmc_project.Inventory.Item.Board",
++ "xyz.openbmc_project.Inventory.Item.Chassis"};
++
++ dbus::utility::getSubTreePaths(
++ [callback{std::move(callback)}](const boost::system::error_code ec,
++ std::vector<std::string>& chassisList) {
++ if (ec)
++ {
++ return;
++ }
++
++ std::vector<std::string> chassisNames;
++ chassisNames.reserve(chassisList.size());
++ for (auto& chassisPath : chassisList)
++ {
++ auto pos = chassisPath.rfind("/");
++ if (pos == std::string::npos)
++ {
++ continue;
++ }
++ chassisNames.push_back(chassisPath.substr(pos + 1));
++ }
++
++ callback(chassisNames);
++ },
++ "/xyz/openbmc_project/inventory", 0, interfaces);
++}
++} // namespace chassis
++
++class MetricDefinitionCollection : public Node
++{
++ public:
++ MetricDefinitionCollection(CrowApp& app) :
++ Node(app, "/redfish/v1/TelemetryService/MetricDefinitions")
++ {
++ entityPrivileges = {
++ {boost::beast::http::verb::get, {{"Login"}}},
++ {boost::beast::http::verb::head, {{"Login"}}},
++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
++ }
++
++ private:
++ void doGet(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>& params) override
++ {
++ res.jsonValue["@odata.type"] = "#MetricDefinitionCollection."
++ "MetricDefinitionCollection";
++ res.jsonValue["@odata.id"] =
++ "/redfish/v1/TelemetryService/MetricDefinitions";
++ res.jsonValue["Name"] = "Metric Definition Collection";
++ res.jsonValue["Members"] = nlohmann::json::array();
++ res.jsonValue["Members@odata.count"] = sensors::dbus::types.size();
++
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ auto collectionReduce = std::make_shared<CollectionGather>(asyncResp);
++ chassis::getChassisNames(
++ [asyncResp,
++ collectionReduce](const std::vector<std::string>& chassisNames) {
++ for (auto& chassisName : chassisNames)
++ {
++ for (auto& [sensorNode, _] : sensors::dbus::types)
++ {
++ BMCWEB_LOG_INFO << "Chassis: " << chassisName
++ << " sensor: " << sensorNode;
++ retrieveUriToDbusMap(
++ chassisName, sensorNode.data(),
++ [asyncResp, collectionReduce](
++ const boost::beast::http::status status,
++ const boost::container::flat_map<
++ std::string, std::string>& uriToDbus) {
++ *collectionReduce += uriToDbus;
++ });
++ }
++ }
++ });
++ }
++
++ class CollectionGather
++ {
++ public:
++ CollectionGather(const std::shared_ptr<AsyncResp>& asyncResp) :
++ asyncResp{asyncResp}
++ {
++ dbusTypes.reserve(sensors::dbus::paths.size());
++ }
++
++ ~CollectionGather()
++ {
++ json_util::dbusPathsToMembersArray(
++ asyncResp->res,
++ std::vector<std::string>(dbusTypes.begin(), dbusTypes.end()),
++ telemetry::metricDefinitionUri);
++ }
++
++ CollectionGather& operator+=(
++ const boost::container::flat_map<std::string, std::string>& rhs)
++ {
++ for (auto& [_, dbusSensor] : rhs)
++ {
++ auto pos = dbusSensor.rfind("/");
++ if (pos == std::string::npos)
++ {
++ BMCWEB_LOG_ERROR << "Received invalid DBus Sensor Path = "
++ << dbusSensor;
++ continue;
++ }
++
++ this->dbusTypes.insert(dbusSensor.substr(0, pos));
++ }
++ return *this;
++ }
++
++ private:
++ const std::shared_ptr<AsyncResp> asyncResp;
++ boost::container::flat_set<std::string> dbusTypes;
++ };
++};
++
++class MetricDefinition : public Node
++{
++ public:
++ MetricDefinition(CrowApp& app) :
++ Node(app, std::string(telemetry::metricDefinitionUri) + "<str>/",
++ std::string())
++ {
++ entityPrivileges = {
++ {boost::beast::http::verb::get, {{"Login"}}},
++ {boost::beast::http::verb::head, {{"Login"}}},
++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
++ }
++
++ private:
++ void doGet(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>& params) override
++ {
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ if (params.size() != 1)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ const std::string& id = params[0];
++
++ size_t sensorIndex = 0;
++ for (auto& name : sensors::dbus::names)
++ {
++ if (name == id)
++ {
++ break;
++ }
++ sensorIndex++;
++ }
++ if (sensorIndex >= sensors::dbus::max)
++ {
++ messages::resourceNotFound(asyncResp->res, schemaType, id);
++ return;
++ }
++
++ auto definitionGather =
++ std::make_shared<DefinitionGather>(asyncResp, id);
++ chassis::getChassisNames(
++ [asyncResp, definitionGather,
++ sensorIndex](const std::vector<std::string>& chassisNames) {
++ for (auto& chassisName : chassisNames)
++ {
++ for (auto& [sensorNode, dbusPaths] : sensors::dbus::types)
++ {
++ auto found =
++ std::find(dbusPaths.begin(), dbusPaths.end(),
++ sensors::dbus::paths[sensorIndex]);
++ if (found == dbusPaths.end())
++ {
++ continue;
++ }
++
++ retrieveUriToDbusMap(
++ chassisName, sensorNode.data(),
++ [asyncResp, definitionGather](
++ const boost::beast::http::status status,
++ const boost::container::flat_map<
++ std::string, std::string>& uriToDbus) {
++ *definitionGather += uriToDbus;
++ });
++ }
++ }
++ });
++ }
++
++ class DefinitionGather
++ {
++ public:
++ DefinitionGather(const std::shared_ptr<AsyncResp>& asyncResp,
++ const std::string& id) :
++ id(id),
++ asyncResp{asyncResp}
++ {}
++ ~DefinitionGather()
++ {
++ if (redfishSensors.empty())
++ {
++ messages::resourceNotFound(asyncResp->res, schemaType, id);
++ return;
++ }
++
++ asyncResp->res.jsonValue["MetricProperties"] =
++ nlohmann::json::array();
++ auto& members = asyncResp->res.jsonValue["MetricProperties"];
++ for (auto& redfishSensor : redfishSensors)
++ {
++ members.push_back(redfishSensor);
++ }
++
++ asyncResp->res.jsonValue["Id"] = id;
++ asyncResp->res.jsonValue["Name"] = id;
++ asyncResp->res.jsonValue["@odata.id"] =
++ telemetry::metricDefinitionUri + id;
++ asyncResp->res.jsonValue["@odata.type"] = schemaType;
++ asyncResp->res.jsonValue["MetricDataType"] = "Decimal";
++ asyncResp->res.jsonValue["MetricType"] = "Numeric";
++ asyncResp->res.jsonValue["Implementation"] = "PhysicalSensor";
++ asyncResp->res.jsonValue["IsLinear"] = true;
++ asyncResp->res.jsonValue["TimestampAccuracy"] = "PT0.1S";
++ auto unit = sensorUnits.find(id);
++ if (unit != sensorUnits.end())
++ {
++ asyncResp->res.jsonValue["Units"] = unit->second;
++ }
++ }
++
++ DefinitionGather& operator+=(
++ const boost::container::flat_map<std::string, std::string>& rhs)
++ {
++ for (auto& [redfishSensor, dbusSensor] : rhs)
++ {
++ if (dbusSensor.find(id) != std::string::npos)
++ {
++ this->redfishSensors.push_back(redfishSensor);
++ }
++ }
++ return *this;
++ }
++
++ const std::string id;
++
++ private:
++ const std::shared_ptr<AsyncResp> asyncResp;
++ std::vector<std::string> redfishSensors;
++ const boost::container::flat_map<std::string, std::string> sensorUnits =
++ {{sensors::dbus::names[sensors::dbus::voltage], "V"},
++ {sensors::dbus::names[sensors::dbus::power], "W"},
++ {sensors::dbus::names[sensors::dbus::current], "A"},
++ {sensors::dbus::names[sensors::dbus::fan_tach], "RPM"},
++ {sensors::dbus::names[sensors::dbus::temperature], "Cel"},
++ {sensors::dbus::names[sensors::dbus::utilization], "%"},
++ {sensors::dbus::names[sensors::dbus::fan_pwm], "Duty cycle"}};
++ };
++
++ static constexpr const char* schemaType =
++ "#MetricDefinition.v1_0_3.MetricDefinition";
++};
++
++} // namespace redfish
+diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp
+index 877e7f1..be72b18 100644
+--- a/redfish-core/lib/metric_report.hpp
++++ b/redfish-core/lib/metric_report.hpp
+@@ -91,6 +91,9 @@ class MetricReport : public Node
+ using Readings =
+ std::vector<std::tuple<std::string, std::string, double, int32_t>>;
+ using MetricValues = std::vector<std::map<std::string, std::string>>;
++ using ReadingParameters =
++ std::vector<std::tuple<std::vector<sdbusplus::message::object_path>,
++ std::string, std::string, std::string>>;
+
+ static MetricValues toMetricValues(const Readings& readings)
+ {
+@@ -109,6 +112,49 @@ class MetricReport : public Node
+ return metricValues;
+ }
+
++ static void addMetricDefinition(nlohmann::json& metrics,
++ const ReadingParameters& params)
++ {
++ for (auto& metric : metrics)
++ {
++ if (!metric.contains("MetricId"))
++ {
++ continue;
++ }
++
++ auto& id = metric["MetricId"].get_ref<std::string&>();
++ auto param =
++ std::find_if(params.begin(), params.end(), [id](const auto& x) {
++ return id == std::get<2>(x);
++ });
++ if (param == params.end())
++ {
++ continue;
++ }
++
++ auto& dbusPaths =
++ std::get<std::vector<sdbusplus::message::object_path>>(*param);
++ if (dbusPaths.size() > 1)
++ {
++ continue;
++ }
++
++ auto dbusPath = dbusPaths.begin();
++ for (size_t i = 0; i < sensors::dbus::paths.size(); i++)
++ {
++ if (dbusPath->str.find(sensors::dbus::paths[i]) ==
++ std::string::npos)
++ {
++ continue;
++ }
++ metric["MetricDefinition"]["@odata.id"] =
++ telemetry::metricDefinitionUri +
++ std::string(sensors::dbus::names[i]);
++ break;
++ }
++ }
++ }
++
+ static void getReportProperties(const std::shared_ptr<AsyncResp> asyncResp,
+ const std::string& reportPath,
+ const std::string& id)
+@@ -124,7 +170,8 @@ class MetricReport : public Node
+ [asyncResp](
+ const boost::system::error_code ec,
+ const boost::container::flat_map<
+- std::string, std::variant<Readings, int32_t>>& ret) {
++ std::string,
++ std::variant<Readings, int32_t, ReadingParameters>>& ret) {
+ if (ec)
+ {
+ messages::internalError(asyncResp->res);
+@@ -138,6 +185,22 @@ class MetricReport : public Node
+ json_util::assignIfPresent<Readings>(
+ ret, "Readings", asyncResp->res.jsonValue["MetricValues"],
+ toMetricValues);
++
++ auto found = ret.find("ReadingParameters");
++ if (found != ret.end())
++ {
++ auto params =
++ std::get_if<ReadingParameters>(&found->second);
++ if (params)
++ {
++ auto& jsonValue = asyncResp->res.jsonValue;
++ if (jsonValue.contains("MetricValues"))
++ {
++ addMetricDefinition(jsonValue["MetricValues"],
++ *params);
++ }
++ }
++ }
+ },
+ "xyz.openbmc_project.MonitoringService", reportPath,
+ "xyz.openbmc_project.MonitoringService.Report");
+diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp
+index f12bbe0..1fa1009 100644
+--- a/redfish-core/lib/sensors.hpp
++++ b/redfish-core/lib/sensors.hpp
+@@ -53,20 +53,39 @@ static constexpr std::string_view thermal = "Thermal";
+
+ namespace dbus
+ {
++
++enum Index
++{
++ voltage = 0,
++ power,
++ current,
++ fan_tach,
++ temperature,
++ fan_pwm,
++ utilization,
++ max
++};
++
++static constexpr std::array<const char*, max> names = {
++ "voltage", "power", "current", "fan_tach",
++ "temperature", "fan_pwm", "utilization"};
++
++static constexpr std::array<const char*, max> paths = {
++ "/xyz/openbmc_project/sensors/voltage",
++ "/xyz/openbmc_project/sensors/power",
++ "/xyz/openbmc_project/sensors/current",
++ "/xyz/openbmc_project/sensors/fan_tach",
++ "/xyz/openbmc_project/sensors/temperature",
++ "/xyz/openbmc_project/sensors/fan_pwm",
++ "/xyz/openbmc_project/sensors/utilization"};
++
+ static const boost::container::flat_map<std::string_view,
+ std::vector<const char*>>
+- types = {{node::power,
+- {"/xyz/openbmc_project/sensors/voltage",
+- "/xyz/openbmc_project/sensors/power"}},
+- {node::sensors,
+- {"/xyz/openbmc_project/sensors/power",
+- "/xyz/openbmc_project/sensors/current",
+- "/xyz/openbmc_project/sensors/utilization"}},
+- {node::thermal,
+- {"/xyz/openbmc_project/sensors/fan_tach",
+- "/xyz/openbmc_project/sensors/temperature",
+- "/xyz/openbmc_project/sensors/fan_pwm"}}};
+-}
++ types = {
++ {node::power, {paths[voltage], paths[power]}},
++ {node::sensors, {paths[power], paths[current], paths[utilization]}},
++ {node::thermal, {paths[fan_tach], paths[temperature], paths[fan_pwm]}}};
++} // namespace dbus
+ } // namespace sensors
+
+ /**
+diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp
+index a410700..79e4154 100644
+--- a/redfish-core/lib/telemetry_service.hpp
++++ b/redfish-core/lib/telemetry_service.hpp
+@@ -52,6 +52,8 @@ class TelemetryService : public Node
+
+ res.jsonValue["LogService"]["@odata.id"] =
+ "/redfish/v1/Managers/bmc/LogServices/Journal";
++ res.jsonValue["MetricDefinitions"]["@odata.id"] =
++ "/redfish/v1/TelemetryService/MetricDefinitions";
+ res.jsonValue["MetricReportDefinitions"]["@odata.id"] =
+ "/redfish/v1/TelemetryService/MetricReportDefinitions";
+ res.jsonValue["MetricReports"]["@odata.id"] =
+--
+2.16.6
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Fix-MetricReport-timestamp-for-EventService.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Fix-MetricReport-timestamp-for-EventService.patch
new file mode 100644
index 000000000..75d49b6d6
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Fix-MetricReport-timestamp-for-EventService.patch
@@ -0,0 +1,78 @@
+From b71f087a173c36a16526156fa34581673e2b860c Mon Sep 17 00:00:00 2001
+From: "Wludzik, Jozef" <jozef.wludzik@intel.com>
+Date: Fri, 24 Jul 2020 17:05:38 +0200
+Subject: [PATCH 6/6] Fix MetricReport timestamp for EventService
+
+Changed MetricReport timestamp type from std::string to int32_t.
+
+Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
+Change-Id: I0a52b6963e7bedda89a216256f64764cd8799bf1
+---
+ redfish-core/include/event_service_manager.hpp | 23 +++++++++++++----------
+ 1 file changed, 13 insertions(+), 10 deletions(-)
+
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index d2f4f2a..dc04ccb 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -38,7 +38,7 @@ namespace redfish
+ {
+
+ using ReadingsObjType =
+- std::vector<std::tuple<std::string, std::string, double, std::string>>;
++ std::vector<std::tuple<std::string, std::string, double, int32_t>>;
+ using EventServiceConfig = std::tuple<bool, uint32_t, uint32_t>;
+
+ static constexpr const char* eventFormatType = "Event";
+@@ -532,10 +532,12 @@ class Subscription
+ metricValuesArray.push_back({});
+ nlohmann::json& entry = metricValuesArray.back();
+
+- entry = {{"MetricId", std::get<0>(it)},
+- {"MetricProperty", std::get<1>(it)},
+- {"MetricValue", std::to_string(std::get<2>(it))},
+- {"Timestamp", std::get<3>(it)}};
++ auto& [id, property, value, timestamp] = it;
++
++ entry = {{"MetricId", id},
++ {"MetricProperty", property},
++ {"MetricValue", value},
++ {"Timestamp", crow::utility::getDateTime(timestamp)}};
+ }
+
+ nlohmann::json msg = {
+@@ -1266,7 +1268,7 @@ class EventServiceManager
+ [idStr{std::move(idStr)}](
+ const boost::system::error_code ec,
+ boost::container::flat_map<
+- std::string, std::variant<std::string, ReadingsObjType>>&
++ std::string, std::variant<int32_t, ReadingsObjType>>&
+ resp) {
+ if (ec)
+ {
+@@ -1275,8 +1277,8 @@ class EventServiceManager
+ return;
+ }
+
+- const std::string* timestampPtr =
+- std::get_if<std::string>(&resp["Timestamp"]);
++ const int32_t* timestampPtr =
++ std::get_if<int32_t>(&resp["Timestamp"]);
+ if (!timestampPtr)
+ {
+ BMCWEB_LOG_DEBUG << "Failed to Get timestamp.";
+@@ -1303,8 +1305,9 @@ class EventServiceManager
+ std::shared_ptr<Subscription> entry = it.second;
+ if (entry->eventFormatType == metricReportFormatType)
+ {
+- entry->filterAndSendReports(idStr, *timestampPtr,
+- *readingsPtr);
++ entry->filterAndSendReports(
++ idStr, crow::utility::getDateTime(*timestampPtr),
++ *readingsPtr);
+ }
+ }
+ },
+--
+2.16.6
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README
new file mode 100644
index 000000000..2929b0aec
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README
@@ -0,0 +1,21 @@
+These patches are mirror of upstream TelemetryService implementation.
+Until change is integrated they will be manually merged here to enable feature in Intel builds.
+
+Current revisions:
+- Redfish TelemetryService schema implementation
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/31692/29
+
+- Add support for POST in MetricReportDefinitions
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/32536/24
+
+- Add support for DELETE in MetricReportDefinitions/<str>
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/32537/23
+
+- Add support for "OnRequest" in MetricReportDefinition
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/33358/17
+
+- Add support for MetricDefinition scheme
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/33363/20
+
+- Temporary patch for EventService because of change in design
+ 0006-Fix-MetricReport-timestamp-for-EventService.patch
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend
index 5c70f25d7..5a44eec78 100644
--- a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend
@@ -1,6 +1,8 @@
# todo(james) remove nobranch
SRC_URI = "git://github.com/openbmc/bmcweb.git"
-SRCREV = "a502de3d661acf95613d4e4d27c9611f2c8148ea"
+SRCREV = "6964c9820ad101d6fc30badd1ae353efea3dd094"
+
+DEPENDS += "boost-url"
FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
@@ -13,14 +15,37 @@ GROUPADD_PARAM_${PN} = "web; redfish "
SRC_URI += "file://0001-Firmware-update-support-for-StandBySpare.patch \
file://0002-Use-chip-id-based-UUID-for-Service-Root.patch \
file://0003-bmcweb-changes-for-setting-ApplyOptions-ClearCfg.patch \
+ file://0004-Remove-QueryString.patch \
+ file://0004-bmcweb-handle-device-or-resource-busy-exception.patch \
+ file://0005-EventService-https-client-support.patch \
+ file://0005-VirtualMedia-fixes-for-Redfish-Service-Validator.patch \
+ file://0006-Fix-Image-and-ImageName-values-in-schema.patch \
+"
+
+# Temporary downstream mirror of upstream patches, see telemetry\README for details
+SRC_URI += "file://telemetry/0001-Redfish-TelemetryService-schema-implementation.patch \
+ file://telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch \
+ file://telemetry/0003-Add-support-for-DELETE-in-MetricReportDefinitions-st.patch \
+ file://telemetry/0004-Add-support-for-OnRequest-in-MetricReportDefinition.patch \
+ file://telemetry/0005-Add-support-for-MetricDefinition-scheme.patch \
+ file://telemetry/0006-Fix-MetricReport-timestamp-for-EventService.patch \
"
+# Temporary fix: Move it to service file
+do_install_append() {
+ install -d ${D}/var/lib/bmcweb
+}
+
# Enable PFR support
EXTRA_OECMAKE += "${@bb.utils.contains('IMAGE_FSTYPES', 'intel-pfr', '-DBMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE=ON', '', d)}"
-# Enable NBD_PROXY
+# Enable NBD proxy embedded in bmcweb
EXTRA_OECMAKE += " -DBMCWEB_ENABLE_VM_NBDPROXY=ON"
+# Disable dependency on external nbd-proxy application
+EXTRA_OECMAKE += " -DBMCWEB_ENABLE_VM_WEBSOCKET=OFF"
+RDEPENDS_${PN}_remove += "jsnbd"
+
# Enable Validation unsecure based on IMAGE_FEATURES
EXTRA_OECMAKE += "${@bb.utils.contains('EXTRA_IMAGE_FEATURES', 'validation-unsecure', '-DBMCWEB_ENABLE_VALIDATION_UNSECURE_FEATURE=ON', '', d)}"