blob: 8fb6506a2be2c9b3e47fd31e578ada2e3083d1b1 [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 Spinlerf60ac272019-12-11 13:47:50 -060031
32 void changeHostState(bool newState)
33 {
Matt Spinler4aa23a12020-02-03 15:05:09 -060034 setHostUp(newState);
Matt Spinlerf60ac272019-12-11 13:47:50 -060035 }
36
37 void setHMCManaged(bool managed)
38 {
39 _hmcManaged = managed;
40 }
41};
42
43/**
44 * @brief The mock HostInterface class
Matt Spinler5342d9a2019-12-12 11:03:39 -060045 *
46 * This replaces the PLDM calls with a FIFO for the asynchronous
47 * responses.
Matt Spinlerf60ac272019-12-11 13:47:50 -060048 */
49class MockHostInterface : public HostInterface
50{
51 public:
Matt Spinler5342d9a2019-12-12 11:03:39 -060052 /**
53 * @brief Constructor
54 *
55 * @param[in] event - The sd_event object
56 * @param[in] dataIface - The DataInterface class
57 */
Matt Spinlerf60ac272019-12-11 13:47:50 -060058 MockHostInterface(sd_event* event, DataInterfaceBase& dataIface) :
59 HostInterface(event, dataIface)
60 {
Matt Spinler5342d9a2019-12-12 11:03:39 -060061 char templ[] = "/tmp/cmdfifoXXXXXX";
62 std::filesystem::path dir = mkdtemp(templ);
63 _fifo = dir / "fifo";
Matt Spinlerf60ac272019-12-11 13:47:50 -060064 }
65
Matt Spinler5342d9a2019-12-12 11:03:39 -060066 /**
67 * @brief Destructor
68 */
Matt Spinlerf60ac272019-12-11 13:47:50 -060069 virtual ~MockHostInterface()
70 {
Matt Spinler5342d9a2019-12-12 11:03:39 -060071 std::filesystem::remove_all(_fifo.parent_path());
Matt Spinlerf60ac272019-12-11 13:47:50 -060072 }
73
74 MOCK_METHOD(CmdStatus, sendNewLogCmd, (uint32_t, uint32_t), (override));
75
Matt Spinler5342d9a2019-12-12 11:03:39 -060076 /**
77 * @brief Cancels waiting for a command response
78 */
79 virtual void cancelCmd() override
80 {
81 _inProgress = false;
82 _source = nullptr;
83 }
84
85 /**
86 * @brief Returns the amount of time to wait before retrying after
87 * a failed send command.
88 *
89 * @return milliseconds - The amount of time to wait
90 */
91 virtual std::chrono::milliseconds getSendRetryDelay() const override
92 {
93 return std::chrono::milliseconds(2);
94 }
95
96 /**
97 * @brief Returns the amount of time to wait before retrying after
98 * a command receive.
99 *
100 * @return milliseconds - The amount of time to wait
101 */
102 virtual std::chrono::milliseconds getReceiveRetryDelay() const override
103 {
104 return std::chrono::milliseconds(2);
105 }
106
107 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600108 * @brief Returns the amount of time to wait before retrying if the
109 * host firmware's PEL storage was full and it can't store
110 * any more logs until it is freed up somehow.
111 *
112 * @return milliseconds - The amount of time to wait
113 */
114 virtual std::chrono::milliseconds getHostFullRetryDelay() const override
115 {
Matt Spinler6aae6a02020-02-07 12:46:07 -0600116 return std::chrono::milliseconds(400);
Matt Spinler41293cb2019-12-12 13:11:09 -0600117 }
118
119 /**
Matt Spinler5342d9a2019-12-12 11:03:39 -0600120 * @brief Returns the number of commands processed
121 */
122 size_t numCmdsProcessed() const
123 {
124 return _cmdsProcessed;
125 }
126
127 /**
128 * @brief Writes the data passed in to the FIFO
129 *
130 * @param[in] hostResponse - use a 0 to indicate success
131 *
132 * @return CmdStatus - success or failure
133 */
134 CmdStatus send(uint8_t hostResponse)
135 {
136 // Create a FIFO once.
137 if (!std::filesystem::exists(_fifo))
138 {
139 if (mkfifo(_fifo.c_str(), 0622))
140 {
141 ADD_FAILURE() << "Failed mkfifo " << _fifo << strerror(errno);
142 exit(-1);
143 }
144 }
145
146 // Open it and register the reponse callback to
147 // be used on FD activity.
148 int fd = open(_fifo.c_str(), O_NONBLOCK | O_RDWR);
149 EXPECT_TRUE(fd >= 0) << "Unable to open FIFO";
150
151 auto callback = [this](sdeventplus::source::IO& source, int fd,
152 uint32_t events) {
153 this->receive(source, fd, events);
154 };
155
156 try
157 {
158 _source = std::make_unique<sdeventplus::source::IO>(
159 _event, fd, EPOLLIN,
160 std::bind(callback, std::placeholders::_1,
161 std::placeholders::_2, std::placeholders::_3));
162 }
163 catch (std::exception& e)
164 {
165 ADD_FAILURE() << "Event exception: " << e.what();
166 close(fd);
167 return CmdStatus::failure;
168 }
169
170 // Write the fake host reponse to the FIFO
171 auto bytesWritten = write(fd, &hostResponse, sizeof(hostResponse));
172 EXPECT_EQ(bytesWritten, sizeof(hostResponse));
173
174 _inProgress = true;
175
176 return CmdStatus::success;
177 }
178
Matt Spinlerf60ac272019-12-11 13:47:50 -0600179 protected:
Matt Spinler5342d9a2019-12-12 11:03:39 -0600180 /**
181 * @brief Reads the data written to the fifo and then calls
182 * the subscriber's callback.
183 *
184 * Nonzero data indicates a command failure (for testing bad path).
185 *
186 * @param[in] source - The event source object
187 * @param[in] fd - The file descriptor used
188 * @param[in] events - The event bits
189 */
Matt Spinlerf60ac272019-12-11 13:47:50 -0600190 void receive(sdeventplus::source::IO& source, int fd,
191 uint32_t events) override
192 {
Matt Spinler5342d9a2019-12-12 11:03:39 -0600193 if (!(events & EPOLLIN))
194 {
195 return;
196 }
197
198 _inProgress = false;
199
200 int newFD = open(_fifo.c_str(), O_NONBLOCK | O_RDONLY);
201 ASSERT_TRUE(newFD >= 0) << "Failed to open FIFO";
202
203 // Read the host success/failure response from the FIFO.
204 uint8_t data;
205 auto bytesRead = read(newFD, &data, sizeof(data));
206 EXPECT_EQ(bytesRead, sizeof(data));
207
208 close(newFD);
209
210 ResponseStatus status = ResponseStatus::success;
211 if (data != 0)
212 {
213 status = ResponseStatus::failure;
214 }
215
216 if (_responseFunc)
217 {
218 (*_responseFunc)(status);
219 }
220
Matt Spinlerf60ac272019-12-11 13:47:50 -0600221 // Keep account of the number of commands responses for testing.
222 _cmdsProcessed++;
223 }
224
225 private:
Matt Spinler5342d9a2019-12-12 11:03:39 -0600226 /**
227 * @brief The event source for the fifo
228 */
229 std::unique_ptr<sdeventplus::source::IO> _source;
230
231 /**
232 * @brief the path to the fifo
233 */
234 std::filesystem::path _fifo;
235
236 /**
237 * @brief The number of commands processed
238 */
Matt Spinlerf60ac272019-12-11 13:47:50 -0600239 size_t _cmdsProcessed = 0;
Matt Spinler09d64002019-09-11 14:29:46 -0500240};
241
242} // namespace pels
243} // namespace openpower