IPMIHost.cpp 6.53 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
38
39
	_mqttPart = "";
	_retransmissionTimeout = 0;
	_sessionTimeout = 0;
	_strand = nullptr;
40
41
42
43
	_errorCount = 0;
	_delayNextReadUntil = 0;
}

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

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

	int workaround_flags = 0;
	int flags = 0;
62
63
	if (ipmi_ctx_open_outofband(_ipmiCtx, _hostName.c_str(), _userName.c_str(),	_password.c_str(), _auth, _priv, _sessionTimeout,	_retransmissionTimeout, workaround_flags, flags) < 0) {
		_errorMsg = "Error opening IPMI connection: " + std::string(ipmi_ctx_errormsg(_ipmiCtx));
64
65
66
67
68
69
70
71
72
73
		ipmi_ctx_close(_ipmiCtx);
		ipmi_ctx_destroy(_ipmiCtx);
		_ipmiCtx = NULL;
		return 2;
	}

	return 0;
}

int IPMIHost::disconnect() {
74
75
76
77
78
	if (_sensorReadCtx) {
		ipmi_sensor_read_ctx_destroy(_sensorReadCtx);
		_sensorReadCtx = NULL;
	}
	
79
80
81
82
83
84
85
86
87
	if (_ipmiCtx) {
		ipmi_ctx_close(_ipmiCtx);
		ipmi_ctx_destroy(_ipmiCtx);
		_ipmiCtx = NULL;
		return 0;
	}
	return 1;
}

88
89
90
bool IPMIHost::getSdrRecord(uint16_t recordId, std::vector<uint8_t>& record) {
	ipmi_sdr_ctx_t	sdrCtx = ipmi_sdr_ctx_create();
	if (!sdrCtx) {
91
92
		throw std::runtime_error("Error creating SDR context: " + std::string(strerror(errno)));
		return false;
93
	}
94
	
95
	bool success = false;
96
	int retries = RETRIES;
97
98
99
100
101
102
103
104
105
106
107
108
109
110
	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;
					}
111
				} else {
112
					_errorMsg = "Error opening SDR cache: " + std::string(ipmi_sdr_ctx_errormsg(sdrCtx));
113
					increaseErrorCount();
114
					disconnect();
115
116
				}
			} else {
117
118
119
120
121
				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));
122
				} else {
123
124
125
126
127
128
					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;
					}
129
				}
130
				ipmi_sdr_cache_close(sdrCtx);
131
			}
132
133
134
		} else {
			increaseErrorCount();
			disconnect();
135
136
		}
	}
137
138
	
	ipmi_sdr_ctx_destroy(sdrCtx);
139
140

	if (!success) {
141
		throw std::runtime_error(_errorMsg);
142
143
	}
	return success;
144
145
}
	
146
int64_t IPMIHost::sendRawCmd(const std::vector<uint8_t>& rawCmd,
147
148
		uint16_t start, uint16_t stop) {
	uint8_t buf[256];
149
	int len = -1;
150
151
152
	int i;

	if (!IPMI_NET_FN_RQ_VALID(rawCmd[1])) {
153
		throw std::runtime_error("Error sending raw IPMI command: Invalid netfn value");
154
155
		return 0;
	}
156
	
157
	bool success = false;
158
	int retries = RETRIES;
159
160
161
162
	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));
163
				increaseErrorCount();
164
165
166
				disconnect();
			} else {
				success = true;
167
			}
168
		} else {
169
170
			increaseErrorCount();
			disconnect();
171
		}
172
173
	}

174
#if 0
175
176
177
178
179
	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;
180
181
#endif

182
183
	if (!success) {
		throw std::runtime_error(_errorMsg);
184
		return 0;
185
	}
186
	
187
188
189
190
191
192
193
194
195
	if (stop > len) {
		std::stringstream ss;
		ss << "Error processing IPMI raw data: stop=" << stop << " > len=" << len;
		throw std::runtime_error(ss.str());
		return 0;
	}

	_errorCount = 0;
	
196
	int64_t val = 0;
197
	for (i = start; i <= stop; i++) {
198
		val |= ((int64_t) buf[i]) << (stop - i) * 8;
199
200
201
202
	}

	return val;
}
203
	
204
205
206
207
208
double IPMIHost::readSensorRecord(std::vector<uint8_t>& record) {
	uint8_t rawReading = 0;
	double *reading = NULL;
	uint16_t eventBitmask = 0;

209
	bool success = false;
210
	int retries = RETRIES;
211
212
213
214
	while (retries-- && !success) {
		if (connect() == 0) {
			if (!_sensorReadCtx) {
				_sensorReadCtx = ipmi_sensor_read_ctx_create(_ipmiCtx);
215
			}
216
217
218
219
220
221
222
223
224
225
226
			
			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));
227
228
			}
		} else {
229
230
			increaseErrorCount();
			disconnect();
231
		}
232
	}
233

234
	double ret = .0;
235
	if (success && reading) {
236
237
238
		_errorCount = 0;
		ret = *reading;
		free(reading);
239
240
	} else {
		throw std::runtime_error(_errorMsg);
241
	}
242
243
	
	return ret;
244
245
246
247
248
249
250
251
252
253
254
255
}

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
256
void IPMIHost::initializeStrand(boost::asio::io_service& io) {
257
258
259
260
	if (!_strand) {
		_strand = new boost::asio::io_service::strand(io);
	}
}