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