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