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