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