IPMIHost.cpp 9.44 KB
Newer Older
1
2
3
//================================================================================
// Name        : IPMIHost.cpp
// Author      : Michael Ott
Micha Müller's avatar
Micha Müller committed
4
// Contact     : info@dcdb.it
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Copyright   : Leibniz Supercomputing Centre
// Description : Source file for IPMIHost class.
//================================================================================

//================================================================================
// This file is part of DCDB (DataCenter DataBase)
// Copyright (C) 2017-2019 Leibniz Supercomputing Centre
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//================================================================================
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

#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>
43
#include <freeipmi/sensor-read/ipmi-sensor-read.h>
44

45
46
#define RETRIES 2

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
IPMIHost::IPMIHost(const std::string& name) :
    EntityInterface(name),
    _ipmiCtx(nullptr),
    _sensorReadCtx(nullptr),
    _userName("admin"),
    _password("admin"),
    _cache(""),
    _auth(IPMI_AUTHENTICATION_TYPE_MD5),
    _priv(IPMI_PRIVILEGE_LEVEL_ADMIN),
    _cipher(3),
    _ipmiVersion(1),
    _sessionTimeout(0),
    _retransmissionTimeout(0),
    _errorCount(0),
    _delayNextReadUntil(0) {
62
63
}

64
65
66
67
68
69
70
71
72
73
74
75
IPMIHost::IPMIHost(const IPMIHost& other) :
    EntityInterface(other),
    _ipmiCtx(nullptr),
    _sensorReadCtx(nullptr),
    _userName(other._userName),
    _password(other._password),
    _cache(other._cache),
    _auth(other._auth),
    _priv(other._priv),
    _cipher(other._cipher),
    _ipmiVersion(other._ipmiVersion),
    _sessionTimeout(other._sessionTimeout),
76
    _retransmissionTimeout(other._retransmissionTimeout),
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
    _errorCount(0),
    _delayNextReadUntil(other._delayNextReadUntil) {
}

IPMIHost::~IPMIHost() {}

IPMIHost& IPMIHost::operator=(const IPMIHost& other) {
    EntityInterface::operator=(other);
    _ipmiCtx = nullptr;
    _sensorReadCtx = nullptr;
    _userName = other._userName;
    _password = other._password;
    _cache = other._cache;
    _auth = other._auth;
    _priv = other._priv;
    _cipher = other._cipher;
    _ipmiVersion = other._ipmiVersion;
    _sessionTimeout = other._sessionTimeout;
    _retransmissionTimeout = other._retransmissionTimeout;
    _errorCount = 0;
    _delayNextReadUntil = other._delayNextReadUntil;

    return *this;
100
}
101
102

int IPMIHost::connect() {
103
104
105
106
	if (_ipmiCtx) {
		return 0;
	}
	
107
	if (!(_ipmiCtx = ipmi_ctx_create())) {
108
		_errorMsg = "Error creating IPMI context" + std::string(strerror(errno));
109
110
111
112
		return 1;
	}

	int workaround_flags = 0;
113
114
115
	int flags = IPMI_FLAGS_DEFAULT;
	int rc;
	if (_ipmiVersion == 1) {
116
		rc = ipmi_ctx_open_outofband(_ipmiCtx, _name.c_str(), _userName.c_str(), _password.c_str(), _auth, _priv, _sessionTimeout, _retransmissionTimeout, workaround_flags, flags);
117
	} else {
118
		rc = ipmi_ctx_open_outofband_2_0(_ipmiCtx, _name.c_str(), _userName.c_str(), _password.c_str(), NULL, 0, _priv, _cipher, _sessionTimeout, _retransmissionTimeout, workaround_flags, flags);
119
120
	}
	if (rc < 0) {
121
		_errorMsg = "Error opening IPMI connection: " + std::string(ipmi_ctx_errormsg(_ipmiCtx));
122
123
124
125
126
127
128
129
130
131
		ipmi_ctx_close(_ipmiCtx);
		ipmi_ctx_destroy(_ipmiCtx);
		_ipmiCtx = NULL;
		return 2;
	}

	return 0;
}

int IPMIHost::disconnect() {
132
133
134
135
136
	if (_sensorReadCtx) {
		ipmi_sensor_read_ctx_destroy(_sensorReadCtx);
		_sensorReadCtx = NULL;
	}
	
137
138
139
140
141
142
143
144
145
	if (_ipmiCtx) {
		ipmi_ctx_close(_ipmiCtx);
		ipmi_ctx_destroy(_ipmiCtx);
		_ipmiCtx = NULL;
		return 0;
	}
	return 1;
}

146
147
148
bool IPMIHost::getSdrRecord(uint16_t recordId, std::vector<uint8_t>& record) {
	ipmi_sdr_ctx_t	sdrCtx = ipmi_sdr_ctx_create();
	if (!sdrCtx) {
149
150
		throw std::runtime_error("Error creating SDR context: " + std::string(strerror(errno)));
		return false;
151
	}
152
	
153
	bool success = false;
154
	int retries = RETRIES;
155
156
157
158
159
	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)) {
160
						LOG(debug) << _name << "Deleting SDR cache " << _cache;
161
162
163
164
						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) {
165
						LOG(debug) << _name << ": Created new SDR cache " << _cache;
166
					} else {
167
						LOG(debug) << _name << ": Error creating new SDR cache " << _cache;
168
					}
169
				} else {
170
					_errorMsg = "Error opening SDR cache: " + std::string(ipmi_sdr_ctx_errormsg(sdrCtx));
171
					increaseErrorCount();
172
					disconnect();
173
174
				}
			} else {
175
176
177
178
179
				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));
180
				} else {
181
182
183
184
185
186
					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;
					}
187
				}
188
				ipmi_sdr_cache_close(sdrCtx);
189
			}
190
191
192
		} else {
			increaseErrorCount();
			disconnect();
193
194
		}
	}
195
196
	
	ipmi_sdr_ctx_destroy(sdrCtx);
197
198

	if (!success) {
199
		throw std::runtime_error(_errorMsg);
200
201
	}
	return success;
202
203
}
	
204
int IPMIHost::sendRawCmd(const uint8_t* rawCmd, unsigned int rawCmdLen, void* buf, unsigned int bufLen) {
205
	int len = -1;
206
207
208
	int i;

	if (!IPMI_NET_FN_RQ_VALID(rawCmd[1])) {
209
		throw std::runtime_error("Error sending raw IPMI command: Invalid netfn value");
210
211
		return 0;
	}
212
	
213
	bool success = false;
214
	int retries = RETRIES;
215
216
	while (retries-- && !success) {
		if (connect() == 0) {
217
			if ((len = ipmi_cmd_raw(_ipmiCtx, rawCmd[0], rawCmd[1], &rawCmd[2], rawCmdLen - 2, buf, bufLen)) < 0) {
218
				_errorMsg = "Error sending IPMI raw command: " + std::string(ipmi_ctx_errormsg(_ipmiCtx));
219
				increaseErrorCount();
220
221
222
				disconnect();
			} else {
				success = true;
223
			}
224
		} else {
225
226
			increaseErrorCount();
			disconnect();
227
		}
228
229
	}

230
#if 0
231
232
233
234
235
	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;
236
237
#endif

238
239
	if (!success) {
		throw std::runtime_error(_errorMsg);
240
		return -1;
241
	}
242
243
244
    
        _errorCount = 0;
        return len;
245
}
246
	
247
248
249
250
251
double IPMIHost::readSensorRecord(std::vector<uint8_t>& record) {
	uint8_t rawReading = 0;
	double *reading = NULL;
	uint16_t eventBitmask = 0;

252
	bool success = false;
253
	int retries = RETRIES;
254
255
256
257
	while (retries-- && !success) {
		if (connect() == 0) {
			if (!_sensorReadCtx) {
				_sensorReadCtx = ipmi_sensor_read_ctx_create(_ipmiCtx);
258
			}
259
260
261
262
263
264
265
266
267
268
269
			
			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));
270
271
			}
		} else {
272
273
			increaseErrorCount();
			disconnect();
274
		}
275
	}
276

277
	double ret = .0;
278
	if (success && reading) {
279
	    //TODO should _delayNextReadUntil be reset here?
280
281
282
		_errorCount = 0;
		ret = *reading;
		free(reading);
283
284
	} else {
		throw std::runtime_error(_errorMsg);
285
	}
286
287
	
	return ret;
288
289
290
291
292
293
294
295
296
297
298
299
}

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

300
void IPMIHost::printEntityConfig(LOG_LEVEL ll) {
301
302
    LOG_VAR(ll) << eInd << "IPMIHost: " << getHostName();
    LOG_VAR(ll) << eInd << "UserName:     " << getUserName();
Micha Mueller's avatar
Micha Mueller committed
303
#ifdef DEBUG
304
    LOG_VAR(ll) << eInd << "Password:     " << getPassword();
Micha Mueller's avatar
Micha Mueller committed
305
#else
306
    LOG_VAR(ll) << eInd << "Password not shown";
Micha Mueller's avatar
Micha Mueller committed
307
#endif
308
309
310
311
312
313
314
    LOG_VAR(ll) << eInd << "Cache:        " << getCache();
    LOG_VAR(ll) << eInd << "Auth:         " << getAuth();
    LOG_VAR(ll) << eInd << "Priv:         " << getPriv();
    LOG_VAR(ll) << eInd << "Cipher:       " << getCipher();
    LOG_VAR(ll) << eInd << "IPMI Version: " << getIpmiVersion();
    LOG_VAR(ll) << eInd << "Session Timeout:        " << _sessionTimeout;
    LOG_VAR(ll) << eInd << "Retransmission Timeout: " << _retransmissionTimeout;
Micha Mueller's avatar
Micha Mueller committed
315
}