blob: e221d96d4ad9202edbd3813edd6b4ba4b620a3f2 [file] [log] [blame]
/*
// Copyright (c) 2018 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 "error_messages.hpp"
#include "openbmc_dbus_rest.hpp"
#include "redfish_util.hpp"
#include <app.hpp>
#include <dbus_utility.hpp>
#include <registries/privilege_registry.hpp>
#include <sdbusplus/asio/property.hpp>
#include <utils/json_utils.hpp>
#include <utils/stl_utils.hpp>
#include <optional>
#include <variant>
namespace redfish
{
void getNTPProtocolEnabled(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp);
std::string getHostName();
const static std::array<std::pair<std::string, std::string>, 3> protocolToDBus{
{{"SSH", "dropbear"}, {"HTTPS", "bmcweb"}, {"IPMI", "phosphor-ipmi-net"}}};
inline void extractNTPServersAndDomainNamesData(
const dbus::utility::ManagedObjectType& dbusData,
std::vector<std::string>& ntpData, std::vector<std::string>& dnData)
{
for (const auto& obj : dbusData)
{
for (const auto& ifacePair : obj.second)
{
if (ifacePair.first !=
"xyz.openbmc_project.Network.EthernetInterface")
{
continue;
}
for (const auto& propertyPair : ifacePair.second)
{
if (propertyPair.first == "NTPServers")
{
const std::vector<std::string>* ntpServers =
std::get_if<std::vector<std::string>>(
&propertyPair.second);
if (ntpServers != nullptr)
{
ntpData = *ntpServers;
}
}
else if (propertyPair.first == "DomainName")
{
const std::vector<std::string>* domainNames =
std::get_if<std::vector<std::string>>(
&propertyPair.second);
if (domainNames != nullptr)
{
dnData = *domainNames;
}
}
}
}
}
}
template <typename CallbackFunc>
void getEthernetIfaceData(CallbackFunc&& callback)
{
crow::connections::systemBus->async_method_call(
[callback{std::forward<CallbackFunc>(callback)}](
const boost::system::error_code errorCode,
const dbus::utility::ManagedObjectType& dbusData) {
std::vector<std::string> ntpServers;
std::vector<std::string> domainNames;
if (errorCode)
{
callback(false, ntpServers, domainNames);
return;
}
extractNTPServersAndDomainNamesData(dbusData, ntpServers,
domainNames);
callback(true, ntpServers, domainNames);
},
"xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
"org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
}
inline void getNetworkData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const crow::Request& req)
{
asyncResp->res.jsonValue["@odata.type"] =
"#ManagerNetworkProtocol.v1_5_0.ManagerNetworkProtocol";
asyncResp->res.jsonValue["@odata.id"] =
"/redfish/v1/Managers/bmc/NetworkProtocol";
asyncResp->res.jsonValue["Id"] = "NetworkProtocol";
asyncResp->res.jsonValue["Name"] = "Manager Network Protocol";
asyncResp->res.jsonValue["Description"] = "Manager Network Service";
asyncResp->res.jsonValue["Status"]["Health"] = "OK";
asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
// HTTP is Mandatory attribute as per OCP Baseline Profile - v1.0.0,
// but from security perspective it is not recommended to use.
// Hence using protocolEnabled as false to make it OCP and security-wise
// compliant
asyncResp->res.jsonValue["HTTP"]["Port"] = 0;
asyncResp->res.jsonValue["HTTP"]["ProtocolEnabled"] = false;
std::string hostName = getHostName();
asyncResp->res.jsonValue["HostName"] = hostName;
getNTPProtocolEnabled(asyncResp);
getEthernetIfaceData([hostName, asyncResp](
const bool& success,
std::vector<std::string>& ntpServers,
const std::vector<std::string>& domainNames) {
if (!success)
{
messages::resourceNotFound(asyncResp->res, "ManagerNetworkProtocol",
"NetworkProtocol");
return;
}
stl_utils::removeDuplicate(ntpServers);
asyncResp->res.jsonValue["NTP"]["NTPServers"] = ntpServers;
if (!hostName.empty())
{
std::string fqdn = hostName;
if (!domainNames.empty())
{
fqdn += ".";
fqdn += domainNames[0];
}
asyncResp->res.jsonValue["FQDN"] = std::move(fqdn);
}
});
Privileges effectiveUserPrivileges =
redfish::getUserPrivileges(req.userRole);
// /redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates is
// something only ConfigureManager can access then only display when
// the user has permissions ConfigureManager
if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
effectiveUserPrivileges))
{
asyncResp->res.jsonValue["HTTPS"]["Certificates"] = {
{"@odata.id",
"/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"}};
}
for (const auto& protocol : protocolToDBus)
{
const std::string& protocolName = protocol.first;
const std::string& serviceName = protocol.second;
getPortStatusAndPath(
serviceName,
[asyncResp, protocolName](const boost::system::error_code ec,
const std::string& socketPath,
bool isProtocolEnabled) {
// If the service is not installed, that is not an error
if (ec == boost::system::errc::no_such_process)
{
asyncResp->res.jsonValue[protocolName]["Port"] =
nlohmann::detail::value_t::null;
asyncResp->res.jsonValue[protocolName]["ProtocolEnabled"] =
false;
return;
}
if (ec)
{
messages::internalError(asyncResp->res);
return;
}
asyncResp->res.jsonValue[protocolName]["ProtocolEnabled"] =
isProtocolEnabled;
getPortNumber(
socketPath,
[asyncResp, protocolName](
const boost::system::error_code ec, int portNumber) {
if (ec)
{
messages::internalError(asyncResp->res);
return;
}
asyncResp->res.jsonValue[protocolName]["Port"] =
portNumber;
});
});
}
} // namespace redfish
inline void handleNTPProtocolEnabled(
const bool& ntpEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
{
std::string timeSyncMethod;
if (ntpEnabled)
{
timeSyncMethod = "xyz.openbmc_project.Time.Synchronization.Method.NTP";
}
else
{
timeSyncMethod =
"xyz.openbmc_project.Time.Synchronization.Method.Manual";
}
crow::connections::systemBus->async_method_call(
[asyncResp](const boost::system::error_code errorCode) {
if (errorCode)
{
messages::internalError(asyncResp->res);
}
},
"xyz.openbmc_project.Settings", "/xyz/openbmc_project/time/sync_method",
"org.freedesktop.DBus.Properties", "Set",
"xyz.openbmc_project.Time.Synchronization", "TimeSyncMethod",
dbus::utility::DbusVariantType{timeSyncMethod});
}
inline void
handleNTPServersPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
std::vector<std::string>& ntpServers)
{
auto iter = stl_utils::firstDuplicate(ntpServers.begin(), ntpServers.end());
if (iter != ntpServers.end())
{
std::string pointer =
"NTPServers/" +
std::to_string(std::distance(ntpServers.begin(), iter));
messages::propertyValueIncorrect(asyncResp->res, pointer, *iter);
return;
}
crow::connections::systemBus->async_method_call(
[asyncResp,
ntpServers](boost::system::error_code ec,
const crow::openbmc_mapper::GetSubTreeType& subtree) {
if (ec)
{
BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "
<< ec.message();
messages::internalError(asyncResp->res);
return;
}
for (const auto& [objectPath, serviceMap] : subtree)
{
for (const auto& [service, interfaces] : serviceMap)
{
for (const auto& interface : interfaces)
{
if (interface !=
"xyz.openbmc_project.Network.EthernetInterface")
{
continue;
}
crow::connections::systemBus->async_method_call(
[asyncResp](const boost::system::error_code ec) {
if (ec)
{
messages::internalError(asyncResp->res);
return;
}
},
service, objectPath,
"org.freedesktop.DBus.Properties", "Set", interface,
"NTPServers",
dbus::utility::DbusVariantType{ntpServers});
}
}
}
},
"xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetSubTree",
"/xyz/openbmc_project", 0,
std::array<const char*, 1>{
"xyz.openbmc_project.Network.EthernetInterface"});
}
inline void
handleProtocolEnabled(const bool protocolEnabled,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string_view netBasePath)
{
crow::connections::systemBus->async_method_call(
[protocolEnabled, asyncResp,
netBasePath](const boost::system::error_code ec,
const crow::openbmc_mapper::GetSubTreeType& subtree) {
if (ec)
{
messages::internalError(asyncResp->res);
return;
}
for (const auto& entry : subtree)
{
if (boost::algorithm::starts_with(entry.first, netBasePath))
{
crow::connections::systemBus->async_method_call(
[asyncResp](const boost::system::error_code ec2) {
if (ec2)
{
messages::internalError(asyncResp->res);
return;
}
},
entry.second.begin()->first, entry.first,
"org.freedesktop.DBus.Properties", "Set",
"xyz.openbmc_project.Control.Service.Attributes",
"Running",
dbus::utility::DbusVariantType{protocolEnabled});
crow::connections::systemBus->async_method_call(
[asyncResp](const boost::system::error_code ec2) {
if (ec2)
{
messages::internalError(asyncResp->res);
return;
}
},
entry.second.begin()->first, entry.first,
"org.freedesktop.DBus.Properties", "Set",
"xyz.openbmc_project.Control.Service.Attributes",
"Enabled",
dbus::utility::DbusVariantType{protocolEnabled});
}
}
},
"xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetSubTree",
"/xyz/openbmc_project/control/service", 0,
std::array<const char*, 1>{
"xyz.openbmc_project.Control.Service.Attributes"});
}
inline std::string getHostName()
{
std::string hostName;
std::array<char, HOST_NAME_MAX> hostNameCStr{};
if (gethostname(hostNameCStr.data(), hostNameCStr.size()) == 0)
{
hostName = hostNameCStr.data();
}
return hostName;
}
inline void
getNTPProtocolEnabled(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
{
sdbusplus::asio::getProperty<std::string>(
*crow::connections::systemBus, "xyz.openbmc_project.Settings",
"/xyz/openbmc_project/time/sync_method",
"xyz.openbmc_project.Time.Synchronization", "TimeSyncMethod",
[asyncResp](const boost::system::error_code errorCode,
const std::string& timeSyncMethod) {
if (errorCode)
{
return;
}
if (timeSyncMethod ==
"xyz.openbmc_project.Time.Synchronization.Method.NTP")
{
asyncResp->res.jsonValue["NTP"]["ProtocolEnabled"] = true;
}
else if (timeSyncMethod ==
"xyz.openbmc_project.Time.Synchronization."
"Method.Manual")
{
asyncResp->res.jsonValue["NTP"]["ProtocolEnabled"] = false;
}
});
}
inline void requestRoutesNetworkProtocol(App& app)
{
BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/NetworkProtocol/")
.privileges(redfish::privileges::patchManagerNetworkProtocol)
.methods(
boost::beast::http::verb::
patch)([](const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
std::optional<std::string> newHostName;
std::optional<nlohmann::json> ntp;
std::optional<nlohmann::json> ipmi;
std::optional<nlohmann::json> ssh;
if (!json_util::readJsonPatch(req, asyncResp->res, "NTP", ntp,
"HostName", newHostName, "IPMI", ipmi,
"SSH", ssh))
{
return;
}
asyncResp->res.result(boost::beast::http::status::no_content);
if (newHostName)
{
messages::propertyNotWritable(asyncResp->res, "HostName");
return;
}
if (ntp)
{
std::optional<std::vector<std::string>> ntpServers;
std::optional<bool> ntpEnabled;
if (!json_util::readJson(*ntp, asyncResp->res, "NTPServers",
ntpServers, "ProtocolEnabled",
ntpEnabled))
{
return;
}
if (ntpEnabled)
{
handleNTPProtocolEnabled(*ntpEnabled, asyncResp);
}
if (ntpServers)
{
stl_utils::removeDuplicate(*ntpServers);
handleNTPServersPatch(asyncResp, *ntpServers);
}
}
if (ipmi)
{
std::optional<bool> ipmiProtocolEnabled;
if (!json_util::readJson(*ipmi, asyncResp->res,
"ProtocolEnabled",
ipmiProtocolEnabled))
{
return;
}
if (ipmiProtocolEnabled)
{
handleProtocolEnabled(
*ipmiProtocolEnabled, asyncResp,
"/xyz/openbmc_project/control/service/phosphor_2dipmi_2dnet_40");
}
}
if (ssh)
{
std::optional<bool> sshProtocolEnabled;
if (!json_util::readJson(*ssh, asyncResp->res,
"ProtocolEnabled", sshProtocolEnabled))
{
return;
}
if (sshProtocolEnabled)
{
handleProtocolEnabled(
*sshProtocolEnabled, asyncResp,
"/xyz/openbmc_project/control/service/dropbear");
}
}
});
BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/NetworkProtocol/")
.privileges(redfish::privileges::getManagerNetworkProtocol)
.methods(boost::beast::http::verb::get)(
[](const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
getNetworkData(asyncResp, req);
});
}
} // namespace redfish