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