sched-host-tran: implement host transition process
Set the scheduled time and host transition to trigger power on/off.
Tested:
1. Check the state first
$ curl -k -H "X-Auth-Token: $token" https://$bmc/xyz/openbmc_project/state/host0
{
"data": {
"AttemptsLeft": 3,
"BootProgress": "xyz.openbmc_project.State.Boot.Progress.ProgressStages.Unspecified",
"CurrentHostState": "xyz.openbmc_project.State.Host.HostState.Off",
"OperatingSystemState": "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive",
"RequestedHostTransition": "xyz.openbmc_project.State.Host.Transition.Off",
"RequestedTransition": "xyz.openbmc_project.State.Host.Transition.On",
"ScheduledTime": 0
},
"message": "200 OK",
"status": "ok"
}
2. Set a time in future
# busctl set-property xyz.openbmc_project.State.ScheduledHostTransition \
/xyz/openbmc_project/state/host0 \
xyz.openbmc_project.State.ScheduledHostTransition ScheduledTime t 1582184830
# busctl get-property xyz.openbmc_project.State.ScheduledHostTransition \
/xyz/openbmc_project/state/host0 \
xyz.openbmc_project.State.ScheduledHostTransition ScheduledTime
t 1582184830
3. Check the state again after scheduled time
Jan 15 06:38:20 WS-Seq-FW-2 phosphor-host-state-manager[442]: Host State transaction request
$ curl -k -H "X-Auth-Token: $token" https://$bmc/xyz/openbmc_project/state/host0
{
"data": {
"AttemptsLeft": 3,
"BootProgress": "xyz.openbmc_project.State.Boot.Progress.ProgressStages.Unspecified",
"CurrentHostState": "xyz.openbmc_project.State.Host.HostState.Running",
"OperatingSystemState": "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive",
"RequestedHostTransition": "xyz.openbmc_project.State.Host.Transition.On",
"RequestedTransition": "xyz.openbmc_project.State.Host.Transition.On",
"ScheduledTime": 0
},
"message": "200 OK",
"status": "ok"
}
4. Set quested transition to off
# busctl set-property xyz.openbmc_project.State.ScheduledHostTransition \
/xyz/openbmc_project/state/host0 \
xyz.openbmc_project.State.ScheduledHostTransition RequestedTransition \
s "xyz.openbmc_project.State.Host.Transition.Off"
# busctl set-property xyz.openbmc_project.State.ScheduledHostTransition \
/xyz/openbmc_project/state/host0 \
xyz.openbmc_project.State.ScheduledHostTransition ScheduledTime t 1582250580
$ curl -k -H "X-Auth-Token: $token" https://$bmc/xyz/openbmc_project/state/host0
{
"data": {
"AttemptsLeft": 3,
"BootProgress": "xyz.openbmc_project.State.Boot.Progress.ProgressStages.Unspecified",
"CurrentHostState": "xyz.openbmc_project.State.Host.HostState.Off",
"OperatingSystemState": "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive",
"RequestedHostTransition": "xyz.openbmc_project.State.Host.Transition.Off"
"RequestedTransition": "xyz.openbmc_project.State.Host.Transition.Off",
"ScheduledTime": 0
},
"message": "200 OK",
"status": "ok"
}
Change-Id: Ib9f3a3984005d9187a9b98603ec1598d8992869e
Signed-off-by: Carol Wang <wangkair@cn.ibm.com>
diff --git a/scheduled_host_transition.cpp b/scheduled_host_transition.cpp
index 4d59ff9..b65d097 100644
--- a/scheduled_host_transition.cpp
+++ b/scheduled_host_transition.cpp
@@ -16,33 +16,50 @@
using namespace std::chrono;
using namespace phosphor::logging;
using namespace xyz::openbmc_project::ScheduledTime;
+using sdbusplus::exception::SdBusError;
using InvalidTimeError =
sdbusplus::xyz::openbmc_project::ScheduledTime::Error::InvalidTime;
using HostTransition =
sdbusplus::xyz::openbmc_project::State::server::ScheduledHostTransition;
+constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
+constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
+constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
+constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
+constexpr auto PROPERTY_TRANSITION = "RequestedHostTransition";
+
uint64_t ScheduledHostTransition::scheduledTime(uint64_t value)
{
if (value == 0)
{
// 0 means the function Scheduled Host Transition is disabled
- // to do: check timer, stop timer
- log<level::INFO>("The function Scheduled Host Transition is disabled.");
- return HostTransition::scheduledTime(value);
- }
+ // Stop the timer if it's running
+ if (timer.isEnabled())
+ {
+ timer.setEnabled(false);
+ }
- auto deltaTime = seconds(value) - getTime();
- if (deltaTime < seconds(0))
- {
- log<level::ERR>("Scheduled time is earlier than current time. Fail to "
- "do host transition.");
- elog<InvalidTimeError>(
- InvalidTime::REASON("Scheduled time is in the past"));
+ log<level::INFO>("The function Scheduled Host Transition is disabled.");
}
else
{
- // start timer
+ auto deltaTime = seconds(value) - getTime();
+ if (deltaTime < seconds(0))
+ {
+ log<level::ERR>(
+ "Scheduled time is earlier than current time. Fail to "
+ "schedule host transition.");
+ elog<InvalidTimeError>(
+ InvalidTime::REASON("Scheduled time is in the past"));
+ }
+ else
+ {
+ // Start a timer to do host transition at scheduled time
+ timer.restart(deltaTime);
+ }
}
+
+ // Set and return the scheduled time
return HostTransition::scheduledTime(value);
}
@@ -52,6 +69,78 @@
return duration_cast<seconds>(now.time_since_epoch());
}
+std::string getService(sdbusplus::bus::bus& bus, std::string path,
+ std::string interface)
+{
+ auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
+ MAPPER_INTERFACE, "GetObject");
+
+ mapper.append(path, std::vector<std::string>({interface}));
+
+ std::vector<std::pair<std::string, std::vector<std::string>>>
+ mapperResponse;
+
+ try
+ {
+ auto mapperResponseMsg = bus.call(mapper);
+
+ mapperResponseMsg.read(mapperResponse);
+ if (mapperResponse.empty())
+ {
+ log<level::ERR>("Error no matching service",
+ entry("PATH=%s", path.c_str()),
+ entry("INTERFACE=%s", interface.c_str()));
+ throw std::runtime_error("Error no matching service");
+ }
+ }
+ catch (const SdBusError& e)
+ {
+ log<level::ERR>("Error in mapper call", entry("ERROR=%s", e.what()),
+ entry("PATH=%s", path.c_str()),
+ entry("INTERFACE=%s", interface.c_str()));
+ throw;
+ }
+
+ return mapperResponse.begin()->first;
+}
+
+void setProperty(sdbusplus::bus::bus& bus, const std::string& path,
+ const std::string& interface, const std::string& property,
+ const std::string& value)
+{
+ sdbusplus::message::variant<std::string> variantValue = value;
+ std::string service = getService(bus, path, interface);
+
+ auto method = bus.new_method_call(service.c_str(), path.c_str(),
+ PROPERTY_INTERFACE, "Set");
+
+ method.append(interface, property, variantValue);
+ bus.call_noreply(method);
+
+ return;
+}
+
+void ScheduledHostTransition::hostTransition()
+{
+ auto hostPath = std::string{HOST_OBJPATH} + '0';
+
+ auto reqTrans = convertForMessage(HostTransition::scheduledTransition());
+
+ setProperty(bus, hostPath, HOST_BUSNAME, PROPERTY_TRANSITION, reqTrans);
+
+ log<level::INFO>("Set requestedTransition",
+ entry("REQUESTED_TRANSITION=%s", reqTrans.c_str()));
+}
+
+void ScheduledHostTransition::callback()
+{
+ // Stop timer, since we need to do host transition once only
+ timer.setEnabled(false);
+ hostTransition();
+ // Set scheduledTime to 0 to disable host transition
+ HostTransition::scheduledTime(0);
+}
+
} // namespace manager
} // namespace state
} // namespace phosphor