24.09., 9:00 - 11:00: Due to updates GitLab will be unavailable for some minutes between 09:00 and 11:00.

IPMISensorGroup.cpp 7.31 KB
Newer Older
1 2 3
//================================================================================
// Name        : IPMISensorGroup.cpp
// Author      : Michael Ott, Micha Mueller
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 IPMI sensor group 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

#include "IPMISensorGroup.h"
#include "IPMIHost.h"
30
#include "LenovoXCC.h"
31 32

#include <chrono>
33 34
#include <exception>
#include <iostream>
35

36
IPMISensorGroup::IPMISensorGroup(const std::string &name)
37
    : SensorGroupTemplateEntity(name) {
38 39
}

40
IPMISensorGroup::IPMISensorGroup(const IPMISensorGroup &other)
41
    : SensorGroupTemplateEntity(other) {
42
}
43

44 45
IPMISensorGroup::~IPMISensorGroup() {}

46 47
IPMISensorGroup &IPMISensorGroup::operator=(const IPMISensorGroup &other) {
	SensorGroupTemplate::operator=(other);
48

49
	return *this;
50 51
}

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
float IPMISensorGroup::getMsgRate()  {
	float msgRate = 0;
	for (const auto &s : _sensors) {
		switch(s->getType()) {
			case IPMISensorBase::sensorType::xccDatastorePower:
			case IPMISensorBase::sensorType::xccBulkPower:
				msgRate+= 100.0f / _minValues;
				break;
			case IPMISensorBase::sensorType::xccBulkEnergy:
				msgRate+= 101.0f / _minValues;
				break;
			default:
				msgRate+= SensorGroupTemplate::getMsgRate();
		}
	}
	return msgRate;
}

70 71 72 73 74 75 76 77 78 79 80 81 82 83
uint64_t IPMISensorGroup::readRaw(const std::vector<uint8_t> &rawCmd, uint8_t lsb, uint8_t msb) {
	uint8_t buf[256];
	int     len = -1;

	try {
		len = _entity->sendRawCmd(&rawCmd[0], rawCmd.size(), (void *)buf, sizeof(buf));
	} catch (const std::exception &e) {
		throw e;
		return 0;
	}

	std::stringstream ss;
	if (msb > len) {
		ss << "Error processing IPMI raw data: msb=" << msb << " > len=" << len;
84
	} else if (lsb > len) {
85
		ss << "Error processing IPMI raw data: lsb=" << lsb << " > len=" << len;
86 87
	}
	if (ss.gcount() > 0) {
88 89
		throw std::runtime_error(ss.str());
		return 0;
90
	}
91

92
	int64_t val = 0;
93
	int     i;
94
	if (msb > lsb) {
95 96 97
		for (i = lsb; i <= msb; i++) {
			val |= ((int64_t)buf[i]) << (msb - i) * 8;
		}
98
	} else {
99 100 101
		for (i = lsb; i >= msb; i--) {
			val |= ((int64_t)buf[i]) << (i - msb) * 8;
		}
102
	}
103

104 105 106
	return val;
}

107 108
void IPMISensorGroup::read() {
	reading_t reading;
Michael Ott's avatar
Michael Ott committed
109
	reading.value = 0;
110 111
	reading.timestamp = getTimestamp();

112 113 114 115 116
	//TODO with SensorGroup refactor we lost the ability to sleep until
	// delayNextRead. Instead we are now checking every time if we can read
	// again. Overhead acceptable? General delayNextReadUntil implementation in
	// SensorGroupTemplate required?
	if (reading.timestamp < _entity->getDelayNextReadUntil()) {
117
		return;
118 119
	}

120
	for (const auto &s : _sensors) {
121
		try {
122 123 124 125
			switch (s->getType()) {
				case IPMISensorBase::sensorType::xccDatastorePower: {
					std::vector<reading_t> readings;
					LenovoXCC xcc(_entity);
126
					if (_entity->getXCC()->getDatastorePower(readings) == 0) {
127
						for (unsigned int i=0; i<readings.size(); i++) {
128 129
							s->storeReading(readings[i], s->getFactor());
						}
Michael Ott's avatar
Michael Ott committed
130
						reading = readings.back();
131 132
					}
					break;
133
				}
Michael Ott's avatar
Michael Ott committed
134
				case IPMISensorBase::sensorType::xccSingleEnergy: {
135
					if (_entity->getXCC()->getSingleEnergy(reading) == 0) {
Michael Ott's avatar
Michael Ott committed
136 137 138 139
						s->storeReading(reading, s->getFactor());
					}
					break;
				}
140 141
				case IPMISensorBase::sensorType::xccBulkPower: {
					std::vector<reading_t> readings;
142
					if (_entity->getXCC()->getBulkPower(readings) == 0) {
143 144 145
						for (unsigned int i=0; i<readings.size(); i++) {
							s->storeReading(readings[i], s->getFactor());
						}
Michael Ott's avatar
Michael Ott committed
146
						reading = readings.back();
147 148 149
					}
					break;
				}
150 151
				case IPMISensorBase::sensorType::xccBulkEnergy: {
					std::vector<reading_t> readings;
152
					if (_entity->getXCC()->getBulkEnergy(readings) == 0) {
153 154 155
						for (unsigned int i=0; i<readings.size(); i++) {
							s->storeReading(readings[i], s->getFactor());
						}
Michael Ott's avatar
Michael Ott committed
156
						reading = readings.back();
157 158 159
					}
					break;
				}
160
				case IPMISensorBase::sensorType::sdr: {
161 162 163 164 165 166
					std::vector<uint8_t> sdrRecord = s->getSdrRecord();
					if (sdrRecord.size() == 0) {
						_entity->getSdrRecord(s->getRecordId(), sdrRecord);
						s->setSdrRecord(sdrRecord);
					}
					reading.value = _entity->readSensorRecord(sdrRecord);
167 168 169 170
					s->storeReading(reading, s->getFactor());
					break;
				}
				case IPMISensorBase::sensorType::raw: {
171
					reading.value = readRaw(s->getRawCmd(), s->getLsb(), s->getMsb());
172 173
					s->storeReading(reading, s->getFactor());
					break;
174
				}
175 176 177 178
				default:
					LOG(error) << _groupName << "::" << s->getName() << " undefined sensor type";
					break;
			}
179
#ifdef DEBUG
Michael Ott's avatar
Michael Ott committed
180
			LOG(debug) << s->getName() << " reading: ts=" << prettyPrintTimestamp(reading.timestamp) << " val=" << reading.value;
181
#endif
182
		} catch (const std::exception &e) {
183
			LOG(error) << _groupName << "::" << s->getName() << " could not read value: " << e.what();
184
			continue;
185 186 187
		}
	}
}
188 189

uint64_t IPMISensorGroup::nextReadingTime() {
190
	if ((_sensors.size() == 1) && (_sensors.front()->getType() == IPMISensorBase::sensorType::xccDatastorePower)) {
191
		reading_t r = _sensors.front()->getLatestValue();
192 193
		uint64_t  now = getTimestamp();

194 195 196 197 198 199 200 201 202 203 204 205 206 207
		if (r.timestamp < now - S_TO_NS(35)) {
			// There was no reading yet or it is too old, so schedule next in 30s
			return now + S_TO_NS(30);
		} else {
			// The first reading of the next 30s block is 10ms after the last reading in the current block.
			// A block becomes available 30s after its first reading, adding 2s grace period
			return r.timestamp + MS_TO_NS(32010);
		}
	}
	return SensorGroupInterface::nextReadingTime();
}

bool IPMISensorGroup::checkConfig() {
	if (_sensors.size() > 1) {
208
		for (const auto &s : _sensors) {
209
			if (s->getType() == IPMISensorBase::sensorType::xccDatastorePower) {
210 211
				LOG(error) << _groupName << " contains an XCC sensor among others, this is not possible";
				return false;
212 213 214 215 216 217 218 219
			} 
		}
	}
	
	for (const auto &s : _sensors) {
		if (s->getType() == IPMISensorBase::sensorType::undefined) {
			LOG(error) << _groupName << "::" << s->getName() << " has an undefined sensor type";
			return false;
220 221 222 223
		}
	}
	return true;
}
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240

void IPMISensorGroup::execOnInit() {
	for (const auto &s : _sensors) {
		switch(s->getType()) {
			case IPMISensorBase::sensorType::xccDatastorePower:
			case IPMISensorBase::sensorType::xccSingleEnergy:
			case IPMISensorBase::sensorType::xccBulkPower:
			case IPMISensorBase::sensorType::xccBulkEnergy:
				if (_entity->getXCC() == nullptr) {
					_entity->setXCC(new LenovoXCC(_entity));
				}
				break;
			default:
				break;
		}
	}
}