xref: /openbmc/bmcweb/features/redfish/lib/metadata.hpp (revision 40e9b92ec19acffb46f83a6e55b18974da5d708e)
1*40e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
2*40e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3090ab8e1SEd Tanous #pragma once
4090ab8e1SEd Tanous 
5090ab8e1SEd Tanous #include "bmcweb_config.h"
6090ab8e1SEd Tanous 
7090ab8e1SEd Tanous #include "app.hpp"
8090ab8e1SEd Tanous #include "async_resp.hpp"
9090ab8e1SEd Tanous #include "http_request.hpp"
10090ab8e1SEd Tanous #include "persistent_data.hpp"
11090ab8e1SEd Tanous #include "query.hpp"
12090ab8e1SEd Tanous #include "registries/privilege_registry.hpp"
13090ab8e1SEd Tanous #include "utils/systemd_utils.hpp"
14090ab8e1SEd Tanous 
15090ab8e1SEd Tanous #include <tinyxml2.h>
16090ab8e1SEd Tanous 
17090ab8e1SEd Tanous #include <nlohmann/json.hpp>
18090ab8e1SEd Tanous 
19090ab8e1SEd Tanous namespace redfish
20090ab8e1SEd Tanous {
21090ab8e1SEd Tanous 
22090ab8e1SEd Tanous inline std::string
23090ab8e1SEd Tanous     getMetadataPieceForFile(const std::filesystem::path& filename)
24090ab8e1SEd Tanous {
25090ab8e1SEd Tanous     std::string xml;
26090ab8e1SEd Tanous     tinyxml2::XMLDocument doc;
27090ab8e1SEd Tanous     std::string pathStr = filename.string();
28090ab8e1SEd Tanous     if (doc.LoadFile(pathStr.c_str()) != tinyxml2::XML_SUCCESS)
29090ab8e1SEd Tanous     {
30090ab8e1SEd Tanous         BMCWEB_LOG_ERROR("Failed to open XML file {}", pathStr);
31090ab8e1SEd Tanous         return "";
32090ab8e1SEd Tanous     }
33090ab8e1SEd Tanous     xml += std::format("    <edmx:Reference Uri=\"/redfish/v1/schema/{}\">\n",
34090ab8e1SEd Tanous                        filename.filename().string());
35090ab8e1SEd Tanous     // std::string edmx = "{http://docs.oasis-open.org/odata/ns/edmx}";
36090ab8e1SEd Tanous     // std::string edm = "{http://docs.oasis-open.org/odata/ns/edm}";
37090ab8e1SEd Tanous     const char* edmx = "edmx:Edmx";
38090ab8e1SEd Tanous     for (tinyxml2::XMLElement* edmxNode = doc.FirstChildElement(edmx);
39090ab8e1SEd Tanous          edmxNode != nullptr; edmxNode = edmxNode->NextSiblingElement(edmx))
40090ab8e1SEd Tanous     {
41090ab8e1SEd Tanous         const char* dataServices = "edmx:DataServices";
42090ab8e1SEd Tanous         for (tinyxml2::XMLElement* node =
43090ab8e1SEd Tanous                  edmxNode->FirstChildElement(dataServices);
44090ab8e1SEd Tanous              node != nullptr; node = node->NextSiblingElement(dataServices))
45090ab8e1SEd Tanous         {
46090ab8e1SEd Tanous             BMCWEB_LOG_DEBUG("Got data service for {}", pathStr);
47090ab8e1SEd Tanous             const char* schemaTag = "Schema";
48090ab8e1SEd Tanous             for (tinyxml2::XMLElement* schemaNode =
49090ab8e1SEd Tanous                      node->FirstChildElement(schemaTag);
50090ab8e1SEd Tanous                  schemaNode != nullptr;
51090ab8e1SEd Tanous                  schemaNode = schemaNode->NextSiblingElement(schemaTag))
52090ab8e1SEd Tanous             {
53090ab8e1SEd Tanous                 std::string ns = schemaNode->Attribute("Namespace");
54090ab8e1SEd Tanous                 // BMCWEB_LOG_DEBUG("Found namespace {}", ns);
55090ab8e1SEd Tanous                 std::string alias;
56090ab8e1SEd Tanous                 if (std::string_view(ns).starts_with("RedfishExtensions"))
57090ab8e1SEd Tanous                 {
58090ab8e1SEd Tanous                     alias = " Alias=\"Redfish\"";
59090ab8e1SEd Tanous                 }
60090ab8e1SEd Tanous                 xml += std::format(
61090ab8e1SEd Tanous                     "        <edmx:Include Namespace=\"{}\"{}/>\n", ns, alias);
62090ab8e1SEd Tanous             }
63090ab8e1SEd Tanous         }
64090ab8e1SEd Tanous     }
65090ab8e1SEd Tanous     xml += "    </edmx:Reference>\n";
66090ab8e1SEd Tanous     return xml;
67090ab8e1SEd Tanous }
68090ab8e1SEd Tanous 
69090ab8e1SEd Tanous inline void
70090ab8e1SEd Tanous     handleMetadataGet(App& /*app*/, const crow::Request& /*req*/,
71090ab8e1SEd Tanous                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
72090ab8e1SEd Tanous {
73090ab8e1SEd Tanous     std::filesystem::path schema("/usr/share/www/redfish/v1/schema");
74090ab8e1SEd Tanous     std::error_code ec;
75090ab8e1SEd Tanous     auto iter = std::filesystem::directory_iterator(schema, ec);
76090ab8e1SEd Tanous     if (ec)
77090ab8e1SEd Tanous     {
78090ab8e1SEd Tanous         BMCWEB_LOG_ERROR("Failed to open XML folder {}", schema.string());
79090ab8e1SEd Tanous         asyncResp->res.result(
80090ab8e1SEd Tanous             boost::beast::http::status::internal_server_error);
81090ab8e1SEd Tanous         return;
82090ab8e1SEd Tanous     }
83090ab8e1SEd Tanous     std::string xml;
84090ab8e1SEd Tanous 
85090ab8e1SEd Tanous     xml += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
86090ab8e1SEd Tanous     xml +=
87090ab8e1SEd Tanous         "<edmx:Edmx xmlns:edmx=\"http://docs.oasis-open.org/odata/ns/edmx\" Version=\"4.0\">\n";
88090ab8e1SEd Tanous     for (const auto& dirEntry : iter)
89090ab8e1SEd Tanous     {
90090ab8e1SEd Tanous         std::string path = dirEntry.path().filename();
91090ab8e1SEd Tanous         if (!std::string_view(path).ends_with("_v1.xml"))
92090ab8e1SEd Tanous         {
93090ab8e1SEd Tanous             continue;
94090ab8e1SEd Tanous         }
95090ab8e1SEd Tanous         std::string metadataPiece = getMetadataPieceForFile(dirEntry.path());
96090ab8e1SEd Tanous         if (metadataPiece.empty())
97090ab8e1SEd Tanous         {
98090ab8e1SEd Tanous             asyncResp->res.result(
99090ab8e1SEd Tanous                 boost::beast::http::status::internal_server_error);
100090ab8e1SEd Tanous             return;
101090ab8e1SEd Tanous         }
102090ab8e1SEd Tanous         xml += metadataPiece;
103090ab8e1SEd Tanous     }
104090ab8e1SEd Tanous     xml += "    <edmx:DataServices>\n";
105090ab8e1SEd Tanous     xml +=
106090ab8e1SEd Tanous         "        <Schema xmlns=\"http://docs.oasis-open.org/odata/ns/edm\" Namespace=\"Service\">\n";
107090ab8e1SEd Tanous     xml +=
108090ab8e1SEd Tanous         "            <EntityContainer Name=\"Service\" Extends=\"ServiceRoot.v1_0_0.ServiceContainer\"/>\n";
109090ab8e1SEd Tanous     xml += "        </Schema>\n";
110090ab8e1SEd Tanous     xml += "    </edmx:DataServices>\n";
111090ab8e1SEd Tanous     xml += "</edmx:Edmx>\n";
112090ab8e1SEd Tanous 
113090ab8e1SEd Tanous     asyncResp->res.addHeader(boost::beast::http::field::content_type,
114090ab8e1SEd Tanous                              "application/xml");
115090ab8e1SEd Tanous     asyncResp->res.write(std::move(xml));
116090ab8e1SEd Tanous }
117090ab8e1SEd Tanous 
118090ab8e1SEd Tanous inline void requestRoutesMetadata(App& app)
119090ab8e1SEd Tanous {
1209008975eSEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/$metadata/")
121090ab8e1SEd Tanous         .methods(boost::beast::http::verb::get)(
122090ab8e1SEd Tanous             std::bind_front(handleMetadataGet, std::ref(app)));
123090ab8e1SEd Tanous }
124090ab8e1SEd Tanous 
125090ab8e1SEd Tanous } // namespace redfish
126