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