IPMIHost.cpp 7.73 KB
Newer Older
1
/*
2
 * IPMIHost.cpp
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 *  Created on: 18 Jan 2017
 *      Author: ottmi
 */

#include "IPMIHost.h"
#include "timestamp.h"

#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <exception>

#include <freeipmi/freeipmi.h>
#include <freeipmi/api/ipmi-api.h>
23
#include <freeipmi/sensor-read/ipmi-sensor-read.h>
24

25
26
#define RETRIES 2

27
28
29
30
IPMIHost::IPMIHost() {
	_ipmiCtx = nullptr;
	_sensorReadCtx = nullptr;
	_hostName = "";
31
32
	_userName = std::string("admin");
	_password = std::string("admin");
33
	_cache = "";
34
35
	_auth = IPMI_AUTHENTICATION_TYPE_MD5;
	_priv = IPMI_PRIVILEGE_LEVEL_ADMIN;
36
37
	_cipher = 3;
	_ipmiVersion = 1;
38
39
40
41
	_mqttPart = "";
	_retransmissionTimeout = 0;
	_sessionTimeout = 0;
	_strand = nullptr;
42
43
44
45
	_errorCount = 0;
	_delayNextReadUntil = 0;
}

46
47
48
49
50
IPMIHost::~IPMIHost() {
	if (_strand) {
		delete _strand;
	}
}
51
52

int IPMIHost::connect() {
53
54
55
56
	if (_ipmiCtx) {
		return 0;
	}
	
57
	if (!(_ipmiCtx = ipmi_ctx_create())) {
58
		_errorMsg = "Error creating IPMI context" + std::string(strerror(errno));
59
60
61
62
		return 1;
	}

	int workaround_flags = 0;
63
64
65
66
67
68
69
70
	int flags = IPMI_FLAGS_DEFAULT;
	int rc;
	if (_ipmiVersion == 1) {
		rc = ipmi_ctx_open_outofband(_ipmiCtx, _hostName.c_str(), _userName.c_str(), _password.c_str(), _auth, _priv, _sessionTimeout, _retransmissionTimeout, workaround_flags, flags); 
	} else {
		rc = ipmi_ctx_open_outofband_2_0(_ipmiCtx, _hostName.c_str(), _userName.c_str(), _password.c_str(), NULL, 0, _priv, _cipher, _sessionTimeout, _retransmissionTimeout, workaround_flags, flags);
	}
	if (rc < 0) {
71
		_errorMsg = "Error opening IPMI connection: " + std::string(ipmi_ctx_errormsg(_ipmiCtx));
72
73
74
75
76
77
78
79
80
81
		ipmi_ctx_close(_ipmiCtx);
		ipmi_ctx_destroy(_ipmiCtx);
		_ipmiCtx = NULL;
		return 2;
	}

	return 0;
}

int IPMIHost::disconnect() {
82
83
84
85
86
	if (_sensorReadCtx) {
		ipmi_sensor_read_ctx_destroy(_sensorReadCtx);
		_sensorReadCtx = NULL;
	}
	
87
88
89
90
91
92
93
94
95
	if (_ipmiCtx) {
		ipmi_ctx_close(_ipmiCtx);
		ipmi_ctx_destroy(_ipmiCtx);
		_ipmiCtx = NULL;
		return 0;
	}
	return 1;
}

96
97
98
bool IPMIHost::getSdrRecord(uint16_t recordId, std::vector<uint8_t>& record) {
	ipmi_sdr_ctx_t	sdrCtx = ipmi_sdr_ctx_create();
	if (!sdrCtx) {
99
100
		throw std::runtime_error("Error creating SDR context: " + std::string(strerror(errno)));
		return false;
101
	}
102
	
103
	bool success = false;
104
	int retries = RETRIES;
105
106
107
108
109
110
111
112
113
114
115
116
117
118
	while (retries-- && !success) {
		if (connect() == 0) {
			if (ipmi_sdr_cache_open(sdrCtx, _ipmiCtx, _cache.c_str()) < 0) {
				if ((ipmi_sdr_ctx_errnum(sdrCtx) == IPMI_SDR_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST) || (ipmi_sdr_ctx_errnum(sdrCtx) == IPMI_SDR_ERR_CACHE_INVALID) || (ipmi_sdr_ctx_errnum(sdrCtx) == IPMI_SDR_ERR_CACHE_OUT_OF_DATE)) {
					if ((ipmi_sdr_ctx_errnum(sdrCtx) == IPMI_SDR_ERR_CACHE_INVALID) || (ipmi_sdr_ctx_errnum(sdrCtx) == IPMI_SDR_ERR_CACHE_OUT_OF_DATE)) {
						LOG(debug) << _hostName << "Deleting SDR cache " << _cache;
						ipmi_sdr_cache_close(sdrCtx);
						ipmi_sdr_cache_delete(sdrCtx, _cache.c_str());
					}
					if (ipmi_sdr_cache_create(sdrCtx, _ipmiCtx, _cache.c_str(), IPMI_SDR_CACHE_CREATE_FLAGS_DEFAULT, NULL, NULL) == 0) {
						LOG(debug) << _hostName << ": Created new SDR cache " << _cache;
					} else {
						LOG(debug) << _hostName << ": Error creating new SDR cache " << _cache;
					}
119
				} else {
120
					_errorMsg = "Error opening SDR cache: " + std::string(ipmi_sdr_ctx_errormsg(sdrCtx));
121
					increaseErrorCount();
122
					disconnect();
123
124
				}
			} else {
125
126
127
128
129
				int recordLength = 0;
				uint8_t recordBuf[IPMI_SDR_MAX_RECORD_LENGTH];
		
				if (ipmi_sdr_cache_search_record_id(sdrCtx, recordId) < 0) {
					_errorMsg = "Error searching SDR record: " + std::string(ipmi_sdr_ctx_errormsg(sdrCtx));
130
				} else {
131
132
133
134
135
136
					if ((recordLength = ipmi_sdr_cache_record_read(sdrCtx, recordBuf, IPMI_SDR_MAX_RECORD_LENGTH)) < 0) {
						_errorMsg = "Error reading SDR record: " + std::string(ipmi_sdr_ctx_errormsg(sdrCtx));
					} else {
						record.insert(record.end(), &recordBuf[0], &recordBuf[recordLength]);
						success = true;
					}
137
				}
138
				ipmi_sdr_cache_close(sdrCtx);
139
			}
140
141
142
		} else {
			increaseErrorCount();
			disconnect();
143
144
		}
	}
145
146
	
	ipmi_sdr_ctx_destroy(sdrCtx);
147
148

	if (!success) {
149
		throw std::runtime_error(_errorMsg);
150
151
	}
	return success;
152
153
}
	
154
int64_t IPMIHost::sendRawCmd(const std::vector<uint8_t>& rawCmd, uint8_t lsb, uint8_t msb) {
155
	uint8_t buf[256];
156
	int len = -1;
157
158
159
	int i;

	if (!IPMI_NET_FN_RQ_VALID(rawCmd[1])) {
160
		throw std::runtime_error("Error sending raw IPMI command: Invalid netfn value");
161
162
		return 0;
	}
163
	
164
	bool success = false;
165
	int retries = RETRIES;
166
167
168
169
	while (retries-- && !success) {
		if (connect() == 0) {
			if ((len = ipmi_cmd_raw(_ipmiCtx, rawCmd[0], rawCmd[1], &rawCmd[2],	rawCmd.size() - 2, buf, sizeof(buf))) < 0) {
				_errorMsg = "Error sending IPMI raw command: " + std::string(ipmi_ctx_errormsg(_ipmiCtx));
170
				increaseErrorCount();
171
172
173
				disconnect();
			} else {
				success = true;
174
			}
175
		} else {
176
177
			increaseErrorCount();
			disconnect();
178
		}
179
180
	}

181
#if 0
182
183
184
185
186
	std::cout << "IPMIHost::sendRawCmd() received " << len << " bytes: " << std::setw(2) << std::setfill('0') << std::hex;
	for (i = 0; i < len; i++) {
		std::cout << (unsigned) buf[i] << " ";
	}
	std::cout << std::dec << std::endl;
187
188
#endif

189
190
	if (!success) {
		throw std::runtime_error(_errorMsg);
191
		return 0;
192
	}
193
194

	std::stringstream ss;
195
	if (msb > len) {
196
197
198
199
200
		ss << "Error processing IPMI raw data: msb=" << msb << " > len=" << len;
	} else if (lsb > len) {
		ss << "Error processing IPMI raw data: lsb=" << lsb << " > len=" << len;
	}
	if (ss.gcount() > 0) {
201
202
203
204
205
206
		throw std::runtime_error(ss.str());
		return 0;
	}

	_errorCount = 0;
	
207
	int64_t val = 0;
208
209
210
	if (msb > lsb) {
		for (i = lsb; i <= msb; i++) {
			val |= ((int64_t) buf[i]) << (msb - i) * 8;
211
212
		}
	} else {
213
214
		for (i = lsb; i >= msb; i--) {
			val |= ((int64_t) buf[i]) << (i-msb) * 8;
215
		}
216
217
218
219
	}

	return val;
}
220
	
221
222
223
224
225
double IPMIHost::readSensorRecord(std::vector<uint8_t>& record) {
	uint8_t rawReading = 0;
	double *reading = NULL;
	uint16_t eventBitmask = 0;

226
	bool success = false;
227
	int retries = RETRIES;
228
229
230
231
	while (retries-- && !success) {
		if (connect() == 0) {
			if (!_sensorReadCtx) {
				_sensorReadCtx = ipmi_sensor_read_ctx_create(_ipmiCtx);
232
			}
233
234
235
236
237
238
239
240
241
242
243
			
			if (_sensorReadCtx) {
				if (ipmi_sensor_read(_sensorReadCtx, &record[0], record.size(), 0, &rawReading, &reading, &eventBitmask) < 0) {
					_errorMsg = "Error reading IPMI record: " + std::string(ipmi_sensor_read_ctx_errormsg(_sensorReadCtx));
					increaseErrorCount();
					disconnect();
				} else {
					success = true;
				}
			} else {
				_errorMsg = "Error creating sensor context: " + std::string(ipmi_ctx_errormsg(_ipmiCtx));
244
245
			}
		} else {
246
247
			increaseErrorCount();
			disconnect();
248
		}
249
	}
250

251
	double ret = .0;
252
	if (success && reading) {
253
254
255
		_errorCount = 0;
		ret = *reading;
		free(reading);
256
257
	} else {
		throw std::runtime_error(_errorMsg);
258
	}
259
260
	
	return ret;
261
262
263
264
265
266
267
268
269
270
271
272
}

void IPMIHost::increaseErrorCount() {
	uint64_t now = getTimestamp();
	if (_errorCount < 25) {
		_errorCount++;
	}
	if (_errorCount > 5) {
		_delayNextReadUntil = now + MS_TO_NS(500 * (_errorCount - 5));
	}
}

Micha Mueller's avatar
Micha Mueller committed
273
void IPMIHost::initializeStrand(boost::asio::io_service& io) {
274
275
276
277
	if (!_strand) {
		_strand = new boost::asio::io_service::strand(io);
	}
}
Micha Mueller's avatar
Micha Mueller committed
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295

void IPMIHost::printConfig(LOG_LEVEL ll) {
  LOG_VAR(ll) << "  IPMIHost: " << _hostName;
  LOG_VAR(ll) << "   UserName:     " << _userName;
#ifdef DEBUG
  LOG_VAR(ll) << "   Password:     " << _password;
#else
  LOG_VAR(ll) << "   Password not shown";
#endif
  LOG_VAR(ll) << "   Cache:        " << _cache;
  LOG_VAR(ll) << "   Auth:         " << _auth;
  LOG_VAR(ll) << "   Priv:         " << _priv;
  LOG_VAR(ll) << "   Cipher:       " << _cipher;
  LOG_VAR(ll) << "   IPMI Version: " << _ipmiVersion;
  LOG_VAR(ll) << "   MQTT Part:    " << _mqttPart;
  LOG_VAR(ll) << "   Session Timeout:        " << _sessionTimeout;
  LOG_VAR(ll) << "   Retransmission Timeout: " << _retransmissionTimeout;
}