xref: /openbmc/bmcweb/features/redfish/lib/metadata.hpp (revision 090ab8e1042e14f7e5e02572ae2a2102677f1f00)
1*090ab8e1SEd Tanous #pragma once
2*090ab8e1SEd Tanous 
3*090ab8e1SEd Tanous #include "bmcweb_config.h"
4*090ab8e1SEd Tanous 
5*090ab8e1SEd Tanous #include "app.hpp"
6*090ab8e1SEd Tanous #include "async_resp.hpp"
7*090ab8e1SEd Tanous #include "http_request.hpp"
8*090ab8e1SEd Tanous #include "persistent_data.hpp"
9*090ab8e1SEd Tanous #include "query.hpp"
10*090ab8e1SEd Tanous #include "registries/privilege_registry.hpp"
11*090ab8e1SEd Tanous #include "utils/systemd_utils.hpp"
12*090ab8e1SEd Tanous 
13*090ab8e1SEd Tanous #include <tinyxml2.h>
14*090ab8e1SEd Tanous 
15*090ab8e1SEd Tanous #include <nlohmann/json.hpp>
16*090ab8e1SEd Tanous 
17*090ab8e1SEd Tanous namespace redfish
18*090ab8e1SEd Tanous {
19*090ab8e1SEd Tanous 
20*090ab8e1SEd Tanous inline std::string
21*090ab8e1SEd Tanous     getMetadataPieceForFile(const std::filesystem::path& filename)
22*090ab8e1SEd Tanous {
23*090ab8e1SEd Tanous     std::string xml;
24*090ab8e1SEd Tanous     tinyxml2::XMLDocument doc;
25*090ab8e1SEd Tanous     std::string pathStr = filename.string();
26*090ab8e1SEd Tanous     if (doc.LoadFile(pathStr.c_str()) != tinyxml2::XML_SUCCESS)
27*090ab8e1SEd Tanous     {
28*090ab8e1SEd Tanous         BMCWEB_LOG_ERROR("Failed to open XML file {}", pathStr);
29*090ab8e1SEd Tanous         return "";
30*090ab8e1SEd Tanous     }
31*090ab8e1SEd Tanous     xml += std::format("    <edmx:Reference Uri=\"/redfish/v1/schema/{}\">\n",
32*090ab8e1SEd Tanous                        filename.filename().string());
33*090ab8e1SEd Tanous     // std::string edmx = "{http://docs.oasis-open.org/odata/ns/edmx}";
34*090ab8e1SEd Tanous     // std::string edm = "{http://docs.oasis-open.org/odata/ns/edm}";
35*090ab8e1SEd Tanous     const char* edmx = "edmx:Edmx";
36*090ab8e1SEd Tanous     for (tinyxml2::XMLElement* edmxNode = doc.FirstChildElement(edmx);
37*090ab8e1SEd Tanous          edmxNode != nullptr; edmxNode = edmxNode->NextSiblingElement(edmx))
38*090ab8e1SEd Tanous     {
39*090ab8e1SEd Tanous         const char* dataServices = "edmx:DataServices";
40*090ab8e1SEd Tanous         for (tinyxml2::XMLElement* node =
41*090ab8e1SEd Tanous                  edmxNode->FirstChildElement(dataServices);
42*090ab8e1SEd Tanous              node != nullptr; node = node->NextSiblingElement(dataServices))
43*090ab8e1SEd Tanous         {
44*090ab8e1SEd Tanous             BMCWEB_LOG_DEBUG("Got data service for {}", pathStr);
45*090ab8e1SEd Tanous             const char* schemaTag = "Schema";
46*090ab8e1SEd Tanous             for (tinyxml2::XMLElement* schemaNode =
47*090ab8e1SEd Tanous                      node->FirstChildElement(schemaTag);
48*090ab8e1SEd Tanous                  schemaNode != nullptr;
49*090ab8e1SEd Tanous                  schemaNode = schemaNode->NextSiblingElement(schemaTag))
50*090ab8e1SEd Tanous             {
51*090ab8e1SEd Tanous                 std::string ns = schemaNode->Attribute("Namespace");
52*090ab8e1SEd Tanous                 // BMCWEB_LOG_DEBUG("Found namespace {}", ns);
53*090ab8e1SEd Tanous                 std::string alias;
54*090ab8e1SEd Tanous                 if (std::string_view(ns).starts_with("RedfishExtensions"))
55*090ab8e1SEd Tanous                 {
56*090ab8e1SEd Tanous                     alias = " Alias=\"Redfish\"";
57*090ab8e1SEd Tanous                 }
58*090ab8e1SEd Tanous                 xml += std::format(
59*090ab8e1SEd Tanous                     "        <edmx:Include Namespace=\"{}\"{}/>\n", ns, alias);
60*090ab8e1SEd Tanous             }
61*090ab8e1SEd Tanous         }
62*090ab8e1SEd Tanous     }
63*090ab8e1SEd Tanous     xml += "    </edmx:Reference>\n";
64*090ab8e1SEd Tanous     return xml;
65*090ab8e1SEd Tanous }
66*090ab8e1SEd Tanous 
67*090ab8e1SEd Tanous inline void
68*090ab8e1SEd Tanous     handleMetadataGet(App& /*app*/, const crow::Request& /*req*/,
69*090ab8e1SEd Tanous                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
70*090ab8e1SEd Tanous {
71*090ab8e1SEd Tanous     std::filesystem::path schema("/usr/share/www/redfish/v1/schema");
72*090ab8e1SEd Tanous     std::error_code ec;
73*090ab8e1SEd Tanous     auto iter = std::filesystem::directory_iterator(schema, ec);
74*090ab8e1SEd Tanous     if (ec)
75*090ab8e1SEd Tanous     {
76*090ab8e1SEd Tanous         BMCWEB_LOG_ERROR("Failed to open XML folder {}", schema.string());
77*090ab8e1SEd Tanous         asyncResp->res.result(
78*090ab8e1SEd Tanous             boost::beast::http::status::internal_server_error);
79*090ab8e1SEd Tanous         return;
80*090ab8e1SEd Tanous     }
81*090ab8e1SEd Tanous     std::string xml;
82*090ab8e1SEd Tanous 
83*090ab8e1SEd Tanous     xml += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
84*090ab8e1SEd Tanous     xml +=
85*090ab8e1SEd Tanous         "<edmx:Edmx xmlns:edmx=\"http://docs.oasis-open.org/odata/ns/edmx\" Version=\"4.0\">\n";
86*090ab8e1SEd Tanous     for (const auto& dirEntry : iter)
87*090ab8e1SEd Tanous     {
88*090ab8e1SEd Tanous         std::string path = dirEntry.path().filename();
89*090ab8e1SEd Tanous         if (!std::string_view(path).ends_with("_v1.xml"))
90*090ab8e1SEd Tanous         {
91*090ab8e1SEd Tanous             continue;
92*090ab8e1SEd Tanous         }
93*090ab8e1SEd Tanous         std::string metadataPiece = getMetadataPieceForFile(dirEntry.path());
94*090ab8e1SEd Tanous         if (metadataPiece.empty())
95*090ab8e1SEd Tanous         {
96*090ab8e1SEd Tanous             asyncResp->res.result(
97*090ab8e1SEd Tanous                 boost::beast::http::status::internal_server_error);
98*090ab8e1SEd Tanous             return;
99*090ab8e1SEd Tanous         }
100*090ab8e1SEd Tanous         xml += metadataPiece;
101*090ab8e1SEd Tanous     }
102*090ab8e1SEd Tanous     xml += "    <edmx:DataServices>\n";
103*090ab8e1SEd Tanous     xml +=
104*090ab8e1SEd Tanous         "        <Schema xmlns=\"http://docs.oasis-open.org/odata/ns/edm\" Namespace=\"Service\">\n";
105*090ab8e1SEd Tanous     xml +=
106*090ab8e1SEd Tanous         "            <EntityContainer Name=\"Service\" Extends=\"ServiceRoot.v1_0_0.ServiceContainer\"/>\n";
107*090ab8e1SEd Tanous     xml += "        </Schema>\n";
108*090ab8e1SEd Tanous     xml += "    </edmx:DataServices>\n";
109*090ab8e1SEd Tanous     xml += "</edmx:Edmx>\n";
110*090ab8e1SEd Tanous 
111*090ab8e1SEd Tanous     asyncResp->res.addHeader(boost::beast::http::field::content_type,
112*090ab8e1SEd Tanous                              "application/xml");
113*090ab8e1SEd Tanous     asyncResp->res.write(std::move(xml));
114*090ab8e1SEd Tanous }
115*090ab8e1SEd Tanous 
116*090ab8e1SEd Tanous inline void requestRoutesMetadata(App& app)
117*090ab8e1SEd Tanous {
118*090ab8e1SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/$metadata")
119*090ab8e1SEd Tanous         .methods(boost::beast::http::verb::get)(
120*090ab8e1SEd Tanous             std::bind_front(handleMetadataGet, std::ref(app)));
121*090ab8e1SEd Tanous }
122*090ab8e1SEd Tanous 
123*090ab8e1SEd Tanous } // namespace redfish
124