blob: 578123384cb535110f51be95df5057adbc12e3fd [file] [log] [blame]
Matt Spinler09d64002019-09-11 14:29:46 -05001#include "extensions/openpower-pels/data_interface.hpp"
Matt Spinlerf60ac272019-12-11 13:47:50 -06002#include "extensions/openpower-pels/host_interface.hpp"
3
4#include <fcntl.h>
5
6#include <filesystem>
7#include <sdeventplus/source/io.hpp>
Matt Spinler09d64002019-09-11 14:29:46 -05008
9#include <gmock/gmock.h>
10
11namespace openpower
12{
13namespace pels
14{
15
16class MockDataInterface : public DataInterfaceBase
17{
18 public:
19 MockDataInterface()
20 {
21 }
Matt Spinlerf60ac272019-12-11 13:47:50 -060022 MOCK_METHOD(std::string, getMachineTypeModel, (), (const override));
23 MOCK_METHOD(std::string, getMachineSerialNumber, (), (const override));
24 MOCK_METHOD(std::string, getServerFWVersion, (), (const override));
25 MOCK_METHOD(std::string, getBMCFWVersion, (), (const override));
Matt Spinler677381b2020-01-23 10:04:29 -060026 MOCK_METHOD(std::string, getBMCFWVersionID, (), (const override));
Matt Spinler24a85582020-01-27 16:40:21 -060027 MOCK_METHOD(bool, getHostPELEnablement, (), (const override));
Matt Spinler4aa23a12020-02-03 15:05:09 -060028 MOCK_METHOD(std::string, getBMCState, (), (const override));
29 MOCK_METHOD(std::string, getChassisState, (), (const override));
30 MOCK_METHOD(std::string, getHostState, (), (const override));
Matt Spinler075e5ba2020-02-21 15:46:00 -060031 MOCK_METHOD(std::string, getMotherboardCCIN, (), (const override));
Matt Spinlerf60ac272019-12-11 13:47:50 -060032
33 void changeHostState(bool newState)
34 {
Matt Spinler4aa23a12020-02-03 15:05:09 -060035 setHostUp(newState);
Matt Spinlerf60ac272019-12-11 13:47:50 -060036 }
37
38 void setHMCManaged(bool managed)
39 {
40 _hmcManaged = managed;
41 }
42};
43
44/**
45 * @brief The mock HostInterface class
Matt Spinler5342d9a2019-12-12 11:03:39 -060046 *
47 * This replaces the PLDM calls with a FIFO for the asynchronous
48 * responses.
Matt Spinlerf60ac272019-12-11 13:47:50 -060049 */
50class MockHostInterface : public HostInterface
51{
52 public:
Matt Spinler5342d9a2019-12-12 11:03:39 -060053 /**
54 * @brief Constructor
55 *
56 * @param[in] event - The sd_event object
57 * @param[in] dataIface - The DataInterface class
58 */
Matt Spinlerf60ac272019-12-11 13:47:50 -060059 MockHostInterface(sd_event* event, DataInterfaceBase& dataIface) :
60 HostInterface(event, dataIface)
61 {
Matt Spinler5342d9a2019-12-12 11:03:39 -060062 char templ[] = "/tmp/cmdfifoXXXXXX";
63 std::filesystem::path dir = mkdtemp(templ);
64 _fifo = dir / "fifo";
Matt Spinlerf60ac272019-12-11 13:47:50 -060065 }
66
Matt Spinler5342d9a2019-12-12 11:03:39 -060067 /**
68 * @brief Destructor
69 */
Matt Spinlerf60ac272019-12-11 13:47:50 -060070 virtual ~MockHostInterface()
71 {
Matt Spinler5342d9a2019-12-12 11:03:39 -060072 std::filesystem::remove_all(_fifo.parent_path());
Matt Spinlerf60ac272019-12-11 13:47:50 -060073 }
74
75 MOCK_METHOD(CmdStatus, sendNewLogCmd, (uint32_t, uint32_t), (override));
76
Matt Spinler5342d9a2019-12-12 11:03:39 -060077 /**
78 * @brief Cancels waiting for a command response
79 */
80 virtual void cancelCmd() override
81 {
82 _inProgress = false;
83 _source = nullptr;
84 }
85
86 /**
87 * @brief Returns the amount of time to wait before retrying after
88 * a failed send command.
89 *
90 * @return milliseconds - The amount of time to wait
91 */
92 virtual std::chrono::milliseconds getSendRetryDelay() const override
93 {
94 return std::chrono::milliseconds(2);
95 }
96
97 /**
98 * @brief Returns the amount of time to wait before retrying after
99 * a command receive.
100 *
101 * @return milliseconds - The amount of time to wait
102 */
103 virtual std::chrono::milliseconds getReceiveRetryDelay() const override
104 {
105 return std::chrono::milliseconds(2);
106 }
107
108 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600109 * @brief Returns the amount of time to wait before retrying if the
110 * host firmware's PEL storage was full and it can't store
111 * any more logs until it is freed up somehow.
112 *
113 * @return milliseconds - The amount of time to wait
114 */
115 virtual std::chrono::milliseconds getHostFullRetryDelay() const override
116 {
Matt Spinler6aae6a02020-02-07 12:46:07 -0600117 return std::chrono::milliseconds(400);
Matt Spinler41293cb2019-12-12 13:11:09 -0600118 }
119
120 /**
Matt Spinler5342d9a2019-12-12 11:03:39 -0600121 * @brief Returns the number of commands processed
122 */
123 size_t numCmdsProcessed() const
124 {
125 return _cmdsProcessed;
126 }
127
128 /**
129 * @brief Writes the data passed in to the FIFO
130 *
131 * @param[in] hostResponse - use a 0 to indicate success
132 *
133 * @return CmdStatus - success or failure
134 */
135 CmdStatus send(uint8_t hostResponse)
136 {
137 // Create a FIFO once.
138 if (!std::filesystem::exists(_fifo))
139 {
140 if (mkfifo(_fifo.c_str(), 0622))
141 {
142 ADD_FAILURE() << "Failed mkfifo " << _fifo << strerror(errno);
143 exit(-1);
144 }
145 }
146
147 // Open it and register the reponse callback to
148 // be used on FD activity.
149 int fd = open(_fifo.c_str(), O_NONBLOCK | O_RDWR);
150 EXPECT_TRUE(fd >= 0) << "Unable to open FIFO";
151
152 auto callback = [this](sdeventplus::source::IO& source, int fd,
153 uint32_t events) {
154 this->receive(source, fd, events);
155 };
156
157 try
158 {
159 _source = std::make_unique<sdeventplus::source::IO>(
160 _event, fd, EPOLLIN,
161 std::bind(callback, std::placeholders::_1,
162 std::placeholders::_2, std::placeholders::_3));
163 }
164 catch (std::exception& e)
165 {
166 ADD_FAILURE() << "Event exception: " << e.what();
167 close(fd);
168 return CmdStatus::failure;
169 }
170
171 // Write the fake host reponse to the FIFO
172 auto bytesWritten = write(fd, &hostResponse, sizeof(hostResponse));
173 EXPECT_EQ(bytesWritten, sizeof(hostResponse));
174
175 _inProgress = true;
176
177 return CmdStatus::success;
178 }
179
Matt Spinlerf60ac272019-12-11 13:47:50 -0600180 protected:
Matt Spinler5342d9a2019-12-12 11:03:39 -0600181 /**
182 * @brief Reads the data written to the fifo and then calls
183 * the subscriber's callback.
184 *
185 * Nonzero data indicates a command failure (for testing bad path).
186 *
187 * @param[in] source - The event source object
188 * @param[in] fd - The file descriptor used
189 * @param[in] events - The event bits
190 */
Matt Spinlerf60ac272019-12-11 13:47:50 -0600191 void receive(sdeventplus::source::IO& source, int fd,
192 uint32_t events) override
193 {
Matt Spinler5342d9a2019-12-12 11:03:39 -0600194 if (!(events & EPOLLIN))
195 {
196 return;
197 }
198
199 _inProgress = false;
200
201 int newFD = open(_fifo.c_str(), O_NONBLOCK | O_RDONLY);
202 ASSERT_TRUE(newFD >= 0) << "Failed to open FIFO";
203
204 // Read the host success/failure response from the FIFO.
205 uint8_t data;
206 auto bytesRead = read(newFD, &data, sizeof(data));
207 EXPECT_EQ(bytesRead, sizeof(data));
208
209 close(newFD);
210
211 ResponseStatus status = ResponseStatus::success;
212 if (data != 0)
213 {
214 status = ResponseStatus::failure;
215 }
216
Matt Spinlera44efe42020-03-03 10:30:16 -0600217 callResponseFunc(status);
Matt Spinler5342d9a2019-12-12 11:03:39 -0600218
Matt Spinlerf60ac272019-12-11 13:47:50 -0600219 // Keep account of the number of commands responses for testing.
220 _cmdsProcessed++;
221 }
222
223 private:
Matt Spinler5342d9a2019-12-12 11:03:39 -0600224 /**
225 * @brief The event source for the fifo
226 */
227 std::unique_ptr<sdeventplus::source::IO> _source;
228
229 /**
230 * @brief the path to the fifo
231 */
232 std::filesystem::path _fifo;
233
234 /**
235 * @brief The number of commands processed
236 */
Matt Spinlerf60ac272019-12-11 13:47:50 -0600237 size_t _cmdsProcessed = 0;
Matt Spinler09d64002019-09-11 14:29:46 -0500238};
239
240} // namespace pels
241} // namespace openpower