1 #include "extensions/openpower-pels/data_interface.hpp"
2 #include "extensions/openpower-pels/host_interface.hpp"
3 
4 #include <fcntl.h>
5 
6 #include <sdeventplus/source/io.hpp>
7 
8 #include <filesystem>
9 
10 #include <gmock/gmock.h>
11 
12 namespace openpower
13 {
14 namespace pels
15 {
16 
17 class MockDataInterface : public DataInterfaceBase
18 {
19   public:
20     MockDataInterface() {}
21     MOCK_METHOD(std::string, getMachineTypeModel, (), (const override));
22     MOCK_METHOD(std::string, getMachineSerialNumber, (), (const override));
23     MOCK_METHOD(std::string, getServerFWVersion, (), (const override));
24     MOCK_METHOD(std::string, getBMCFWVersion, (), (const override));
25     MOCK_METHOD(std::string, getBMCFWVersionID, (), (const override));
26     MOCK_METHOD(bool, getHostPELEnablement, (), (const override));
27     MOCK_METHOD(std::string, getBMCState, (), (const override));
28     MOCK_METHOD(std::string, getChassisState, (), (const override));
29     MOCK_METHOD(std::string, getHostState, (), (const override));
30     MOCK_METHOD(std::string, getMotherboardCCIN, (), (const override));
31     MOCK_METHOD(void, getHWCalloutFields,
32                 (const std::string&, std::string&, std::string&, std::string&),
33                 (const override));
34     MOCK_METHOD(std::string, getLocationCode, (const std::string&),
35                 (const override));
36     MOCK_METHOD(std::vector<std::string>, getSystemNames, (), (const override));
37     MOCK_METHOD(std::string, expandLocationCode, (const std::string&, uint16_t),
38                 (const override));
39     MOCK_METHOD(std::string, getInventoryFromLocCode,
40                 (const std::string&, uint16_t, bool), (const override));
41     MOCK_METHOD(void, assertLEDGroup, (const std::string&, bool),
42                 (const override));
43     MOCK_METHOD(void, setFunctional, (const std::string&, bool),
44                 (const override));
45     MOCK_METHOD(std::vector<uint8_t>, getSystemIMKeyword, (), (const override));
46     MOCK_METHOD(bool, getQuiesceOnError, (), (const override));
47     MOCK_METHOD(void, setCriticalAssociation, (const std::string&),
48                 (const override));
49     MOCK_METHOD(std::vector<bool>, checkDumpStatus,
50                 (const std::vector<std::string>&), (const override));
51     MOCK_METHOD(std::string, getBootState, (), (const override));
52     MOCK_METHOD(void, createGuardRecord,
53                 (const std::vector<uint8_t>&, const std::string&,
54                  const std::string&),
55                 (const override));
56     MOCK_METHOD(void, createProgressSRC,
57                 (const uint64_t&, const std::vector<uint8_t>&),
58                 (const override));
59     MOCK_METHOD(std::vector<uint32_t>, getLogIDWithHwIsolation, (),
60                 (const override));
61 
62     void changeHostState(bool newState)
63     {
64         setHostUp(newState);
65     }
66 
67     void setHMCManaged(bool managed)
68     {
69         _hmcManaged = managed;
70     }
71 };
72 
73 /**
74  * @brief The mock HostInterface class
75  *
76  * This replaces the PLDM calls with a FIFO for the asynchronous
77  * responses.
78  */
79 class MockHostInterface : public HostInterface
80 {
81   public:
82     /**
83      * @brief Constructor
84      *
85      * @param[in] event - The sd_event object
86      * @param[in] dataIface - The DataInterface class
87      */
88     MockHostInterface(sd_event* event, DataInterfaceBase& dataIface) :
89         HostInterface(event, dataIface)
90     {
91         char templ[] = "/tmp/cmdfifoXXXXXX";
92         std::filesystem::path dir = mkdtemp(templ);
93         _fifo = dir / "fifo";
94     }
95 
96     /**
97      * @brief Destructor
98      */
99     virtual ~MockHostInterface()
100     {
101         std::filesystem::remove_all(_fifo.parent_path());
102     }
103 
104     MOCK_METHOD(CmdStatus, sendNewLogCmd, (uint32_t, uint32_t), (override));
105 
106     /**
107      * @brief Cancels waiting for a command response
108      */
109     virtual void cancelCmd() override
110     {
111         _inProgress = false;
112         _source = nullptr;
113     }
114 
115     /**
116      * @brief Returns the amount of time to wait before retrying after
117      *        a failed send command.
118      *
119      * @return milliseconds - The amount of time to wait
120      */
121     virtual std::chrono::milliseconds getSendRetryDelay() const override
122     {
123         return std::chrono::milliseconds(2);
124     }
125 
126     /**
127      * @brief Returns the amount of time to wait before retrying after
128      *        a command receive.
129      *
130      * @return milliseconds - The amount of time to wait
131      */
132     virtual std::chrono::milliseconds getReceiveRetryDelay() const override
133     {
134         return std::chrono::milliseconds(2);
135     }
136 
137     /**
138      * @brief Returns the amount of time to wait before retrying if the
139      *        host firmware's PEL storage was full and it can't store
140      *        any more logs until it is freed up somehow.
141      *
142      * @return milliseconds - The amount of time to wait
143      */
144     virtual std::chrono::milliseconds getHostFullRetryDelay() const override
145     {
146         return std::chrono::milliseconds(400);
147     }
148 
149     /**
150      * @brief Returns the amount of time to wait after the host is up
151      *        before sending commands.
152      *
153      * @return milliseconds - The amount of time to wait
154      */
155     virtual std::chrono::milliseconds getHostUpDelay() const override
156     {
157         return std::chrono::milliseconds(0);
158     }
159 
160     /**
161      * @brief Returns the number of commands processed
162      */
163     size_t numCmdsProcessed() const
164     {
165         return _cmdsProcessed;
166     }
167 
168     /**
169      * @brief Writes the data passed in to the FIFO
170      *
171      * @param[in] hostResponse - use a 0 to indicate success
172      *
173      * @return CmdStatus - success or failure
174      */
175     CmdStatus send(uint8_t hostResponse)
176     {
177         // Create a FIFO once.
178         if (!std::filesystem::exists(_fifo))
179         {
180             if (mkfifo(_fifo.c_str(), 0622))
181             {
182                 ADD_FAILURE() << "Failed mkfifo " << _fifo << strerror(errno);
183                 exit(-1);
184             }
185         }
186 
187         // Open it and register the reponse callback to
188         // be used on FD activity.
189         int fd = open(_fifo.c_str(), O_NONBLOCK | O_RDWR);
190         EXPECT_TRUE(fd >= 0) << "Unable to open FIFO";
191 
192         auto callback = [this](sdeventplus::source::IO& source, int fd,
193                                uint32_t events) {
194             this->receive(source, fd, events);
195         };
196 
197         try
198         {
199             _source = std::make_unique<sdeventplus::source::IO>(
200                 _event, fd, EPOLLIN,
201                 std::bind(callback, std::placeholders::_1,
202                           std::placeholders::_2, std::placeholders::_3));
203         }
204         catch (const std::exception& e)
205         {
206             ADD_FAILURE() << "Event exception: " << e.what();
207             close(fd);
208             return CmdStatus::failure;
209         }
210 
211         // Write the fake host reponse to the FIFO
212         auto bytesWritten = write(fd, &hostResponse, sizeof(hostResponse));
213         EXPECT_EQ(bytesWritten, sizeof(hostResponse));
214 
215         _inProgress = true;
216 
217         return CmdStatus::success;
218     }
219 
220   protected:
221     /**
222      * @brief Reads the data written to the fifo and then calls
223      *        the subscriber's callback.
224      *
225      * Nonzero data indicates a command failure (for testing bad path).
226      *
227      * @param[in] source - The event source object
228      * @param[in] fd - The file descriptor used
229      * @param[in] events - The event bits
230      */
231     void receive(sdeventplus::source::IO& /*source*/, int /*fd*/,
232                  uint32_t events) override
233     {
234         if (!(events & EPOLLIN))
235         {
236             return;
237         }
238 
239         _inProgress = false;
240 
241         int newFD = open(_fifo.c_str(), O_NONBLOCK | O_RDONLY);
242         ASSERT_TRUE(newFD >= 0) << "Failed to open FIFO";
243 
244         // Read the host success/failure response from the FIFO.
245         uint8_t data;
246         auto bytesRead = read(newFD, &data, sizeof(data));
247         EXPECT_EQ(bytesRead, sizeof(data));
248 
249         close(newFD);
250 
251         ResponseStatus status = ResponseStatus::success;
252         if (data != 0)
253         {
254             status = ResponseStatus::failure;
255         }
256 
257         callResponseFunc(status);
258 
259         // Keep account of the number of commands responses for testing.
260         _cmdsProcessed++;
261     }
262 
263   private:
264     /**
265      * @brief The event source for the fifo
266      */
267     std::unique_ptr<sdeventplus::source::IO> _source;
268 
269     /**
270      * @brief the path to the fifo
271      */
272     std::filesystem::path _fifo;
273 
274     /**
275      * @brief The number of commands processed
276      */
277     size_t _cmdsProcessed = 0;
278 };
279 
280 } // namespace pels
281 } // namespace openpower
282