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