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