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