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