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