diff options
author | Andrey V.Kosteltsev <AKosteltsev@IBS.RU> | 2022-07-15 10:36:51 +0300 |
---|---|---|
committer | Andrey V.Kosteltsev <AKosteltsev@IBS.RU> | 2022-07-15 10:36:51 +0300 |
commit | 0f4556fc2343dc0ade0bb1e0d1fc6f85770d77af (patch) | |
tree | e26b6cb64f8ac4625c45baa40834fe51dc25c4f8 /agent/sila | |
download | obmc-sila-snmp-0f4556fc2343dc0ade0bb1e0d1fc6f85770d77af.tar.xz |
First commit: Sila SNMP Sub Agent and configuration manager
Diffstat (limited to 'agent/sila')
-rw-r--r-- | agent/sila/inventory.cpp | 249 | ||||
-rw-r--r-- | agent/sila/inventory.hpp | 32 | ||||
-rw-r--r-- | agent/sila/powerstate.cpp | 140 | ||||
-rw-r--r-- | agent/sila/powerstate.hpp | 38 | ||||
-rw-r--r-- | agent/sila/sensors.cpp | 385 | ||||
-rw-r--r-- | agent/sila/sensors.hpp | 33 | ||||
-rw-r--r-- | agent/sila/sila_oid.hpp | 27 | ||||
-rw-r--r-- | agent/sila/software.cpp | 225 | ||||
-rw-r--r-- | agent/sila/software.hpp | 32 |
9 files changed, 1161 insertions, 0 deletions
diff --git a/agent/sila/inventory.cpp b/agent/sila/inventory.cpp new file mode 100644 index 0000000..233b8f8 --- /dev/null +++ b/agent/sila/inventory.cpp @@ -0,0 +1,249 @@ +/** + * @brief SILA inventory table implementation. + * + * This file is part of sila-snmp project. + * + * Copyright (c) 2022 SILA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "tracing.hpp" +#include "data/table.hpp" +#include "data/table/item.hpp" +#include "sila/sila_oid.hpp" +#include "snmptrap.hpp" + +namespace sila +{ +namespace inventory +{ +using OID = phosphor::snmp::agent::OID; +static const OID NOTIFY_OID = SILA_OID(0, 7); + +struct InventoryItem : public phosphor::snmp::data::table::Item< + std::string, std::string, std::string, std::string, + std::string, std::string, std::string, bool, bool> +{ + // Indexes of fields in tuble + enum Fields + { + FIELD_INVENTORY_PRETTY_NAME = 0, + FIELD_INVENTORY_MANUFACTURER, + FIELD_INVENTORY_BUILD_DATE, + FIELD_INVENTORY_MODEL, + FIELD_INVENTORY_PART_NUMBER, + FIELD_INVENTORY_SERIAL_NUMBER, + FIELD_INVENTORY_VERSION, + FIELD_INVENTORY_PRESENT, + FIELD_INVENTORY_FUNCTIONAL, + }; + + enum Columns + { + COLUMN_SILAINVENTORY_PATH = 1, + COLUMN_SILAINVENTORY_NAME, + COLUMN_SILAINVENTORY_MANUFACTURER, + COLUMN_SILAINVENTORY_BUILD_DATE, + COLUMN_SILAINVENTORY_MODEL, + COLUMN_SILAINVENTORY_PART_NUMBER, + COLUMN_SILAINVENTORY_SERIAL_NUMBER, + COLUMN_SILAINVENTORY_VERSION, + COLUMN_SILAINVENTORY_PRESENT, + COLUMN_SILAINVENTORY_FUNCTIONAL, + }; + + InventoryItem(const std::string& folder, const std::string& name) : + phosphor::snmp::data::table::Item<std::string, std::string, std::string, + std::string, std::string, std::string, + std::string, bool, bool>( + folder, name, + "", // Pretty Name + "", // Manufacturer + "", // Build date + "", // Model + "", // Part number + "", // Serial number + "", // Version + false, // Present + false) // Functional + { + phosphor::snmp::agent::make_oid( + _presentOid, ".1.3.6.1.4.1.49769.4.1.%lu.\"%s\"", + COLUMN_SILAINVENTORY_PRESENT, name.c_str()); + phosphor::snmp::agent::make_oid( + _functionalOid, ".1.3.6.1.4.1.49769.4.1.%lu.\"%s\"", + COLUMN_SILAINVENTORY_FUNCTIONAL, name.c_str()); + } + + void setFields(const fields_map_t& fields) override + { + bool isPresent = std::get<FIELD_INVENTORY_PRESENT>(data); + bool isFunctional = std::get<FIELD_INVENTORY_FUNCTIONAL>(data); + + setField<FIELD_INVENTORY_PRETTY_NAME>(fields, "PrettyName"); + setField<FIELD_INVENTORY_MANUFACTURER>(fields, "Manufacturer"); + setField<FIELD_INVENTORY_BUILD_DATE>(fields, "BuildDate"); + setField<FIELD_INVENTORY_MODEL>(fields, "Model"); + setField<FIELD_INVENTORY_PART_NUMBER>(fields, "PartNumber"); + setField<FIELD_INVENTORY_SERIAL_NUMBER>(fields, "SerialNumber"); + setField<FIELD_INVENTORY_VERSION>(fields, "Version"); + setField<FIELD_INVENTORY_PRESENT>(fields, "Present"); + setField<FIELD_INVENTORY_FUNCTIONAL>(fields, "Functional"); + + if (isPresent != std::get<FIELD_INVENTORY_PRESENT>(data) || + isFunctional != std::get<FIELD_INVENTORY_FUNCTIONAL>(data)) + { + DEBUGMSGTL(("sila:inventory", + "Inventory item '%s' at '%s': " + "present %d -> %d, function %d -> %d\n", + std::get<FIELD_INVENTORY_PRETTY_NAME>(data).c_str(), + name.c_str(), isPresent, + std::get<FIELD_INVENTORY_PRESENT>(data), isFunctional, + std::get<FIELD_INVENTORY_FUNCTIONAL>(data))); + + phosphor::snmp::agent::Trap trap(NOTIFY_OID); + trap.add_field(_presentOid, + std::get<FIELD_INVENTORY_PRESENT>(data)); + trap.add_field(_functionalOid, + std::get<FIELD_INVENTORY_FUNCTIONAL>(data)); + trap.send(); + } + } + + void get_snmp_reply(netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* request) const override + { + using namespace phosphor::snmp::agent; + + netsnmp_table_request_info* tinfo = netsnmp_extract_table_info(request); + + switch (tinfo->colnum) + { + case COLUMN_SILAINVENTORY_PATH: + VariableList::set(request->requestvb, name); + break; + + case COLUMN_SILAINVENTORY_NAME: + VariableList::set(request->requestvb, + std::get<FIELD_INVENTORY_PRETTY_NAME>(data)); + break; + + case COLUMN_SILAINVENTORY_MANUFACTURER: + VariableList::set(request->requestvb, + std::get<FIELD_INVENTORY_MANUFACTURER>(data)); + break; + + case COLUMN_SILAINVENTORY_BUILD_DATE: + VariableList::set(request->requestvb, + std::get<FIELD_INVENTORY_BUILD_DATE>(data)); + break; + + case COLUMN_SILAINVENTORY_MODEL: + VariableList::set(request->requestvb, + std::get<FIELD_INVENTORY_MODEL>(data)); + break; + + case COLUMN_SILAINVENTORY_PART_NUMBER: + VariableList::set(request->requestvb, + std::get<FIELD_INVENTORY_PART_NUMBER>(data)); + break; + + case COLUMN_SILAINVENTORY_SERIAL_NUMBER: + VariableList::set( + request->requestvb, + std::get<FIELD_INVENTORY_SERIAL_NUMBER>(data)); + break; + + case COLUMN_SILAINVENTORY_VERSION: + VariableList::set(request->requestvb, + std::get<FIELD_INVENTORY_VERSION>(data)); + break; + + case COLUMN_SILAINVENTORY_PRESENT: + VariableList::set(request->requestvb, + std::get<FIELD_INVENTORY_PRESENT>(data)); + break; + + case COLUMN_SILAINVENTORY_FUNCTIONAL: + VariableList::set(request->requestvb, + std::get<FIELD_INVENTORY_FUNCTIONAL>(data)); + break; + + default: + netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT); + break; + } + } + + void onCreate() override + { + DEBUGMSGTL( + ("sila:inventory", "Inventory item '%s' added.\n", name.c_str())); + } + + void onDestroy() override + { + DEBUGMSGTL(("sila:inventory", "Inventory item '%s' removed.\n", + name.c_str())); + if (std::get<FIELD_INVENTORY_PRESENT>(data) || + std::get<FIELD_INVENTORY_FUNCTIONAL>(data)) + { + phosphor::snmp::agent::Trap trap(NOTIFY_OID); + trap.add_field(_presentOid, false); + trap.add_field(_functionalOid, false); + trap.send(); + } + } + + OID _presentOid; + OID _functionalOid; +}; + +static phosphor::snmp::agent::OID inventoryTableOid = SILA_OID(4); + +static phosphor::snmp::data::Table<InventoryItem> + inventoryTable("/xyz/openbmc_project/inventory", + { + "xyz.openbmc_project.Inventory.Item", + "xyz.openbmc_project.Inventory.Decorator.Asset", + "xyz.openbmc_project.Inventory.Decorator.Revision", + "xyz.openbmc_project.State.Decorator.OperationalStatus", + }); + +/** + * @brief Initialize inventory table + */ +void init() +{ + DEBUGMSGTL(("sila:init", "Initialize silaInventoryTable\n")); + + inventoryTable.update(); + inventoryTable.init_mib("silaInventoryTable", inventoryTableOid.data(), + inventoryTableOid.size(), + InventoryItem::COLUMN_SILAINVENTORY_PATH, + InventoryItem::COLUMN_SILAINVENTORY_FUNCTIONAL); +} + +/** + * @brief Deinitialize inventory table + */ +void destroy() +{ + DEBUGMSGTL(("sila:shutdown", "Deinitialize silaInventoryTable\n")); + unregister_mib(inventoryTableOid.data(), inventoryTableOid.size()); +} + +} // namespace inventory +} // namespace sila diff --git a/agent/sila/inventory.hpp b/agent/sila/inventory.hpp new file mode 100644 index 0000000..ad8a3cc --- /dev/null +++ b/agent/sila/inventory.hpp @@ -0,0 +1,32 @@ +/** + * @brief SILA inventory table implementation. + * + * This file is part of sila-snmp project. + * + * Copyright (c) 2022 SILA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#pragma once + +namespace sila +{ +namespace inventory +{ + +void init(); +void destroy(); + +} // namespace inventory +} // namespace sila diff --git a/agent/sila/powerstate.cpp b/agent/sila/powerstate.cpp new file mode 100644 index 0000000..d82ad23 --- /dev/null +++ b/agent/sila/powerstate.cpp @@ -0,0 +1,140 @@ +/** + * @brief SILA host power state implementation. + * + * This file is part of sila-snmp project. + * + * Copyright (c) 2022 SILA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "data/scalar.hpp" +#include "sila/sila_oid.hpp" +#include "tracing.hpp" + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> + +#include "snmptrap.hpp" + +namespace sila +{ +namespace host +{ +namespace power +{ +namespace state +{ +using OID = phosphor::snmp::agent::OID; + +static const OID state_oid = SILA_OID(1, 1); +static const OID notify_oid = SILA_OID(0, 1); + +// Values specified in the MIB file. +constexpr int UNKNOWN = -1; +constexpr int OFF = 0; +constexpr int ON = 1; + +struct State : public phosphor::snmp::data::Scalar<std::string> +{ + static constexpr auto IFACE = "xyz.openbmc_project.State.Host"; + static constexpr auto PATH = "/xyz/openbmc_project/state/host0"; + + State() : + phosphor::snmp::data::Scalar<std::string>(PATH, IFACE, + "CurrentHostState", "") + { + } + + void setValue(value_t& var) + { + auto prev = getValue(); + phosphor::snmp::data::Scalar<std::string>::setValue(var); + + auto curr = getValue(); + if (prev != curr) + { + DEBUGMSGTL(("sila:powerstate", + "Host power state changed: '%s' -> '%s'\n", + prev.c_str(), curr.c_str())); + + phosphor::snmp::agent::Trap trap(notify_oid); + trap.add_field(state_oid, toSNMPValue()); + trap.send(); + } + } + + int toSNMPValue() const + { + auto value = getValue(); + if (value == "xyz.openbmc_project.State.Host.HostState.Running") + { + return ON; + } + if (value == "xyz.openbmc_project.State.Host.HostState.Off") + { + return OFF; + } + + return UNKNOWN; + } +}; + +static State state; + +/** @brief Handler for snmp requests */ +static int State_snmp_handler(netsnmp_mib_handler* /*handler*/, + netsnmp_handler_registration* /*reginfo*/, + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* requests) +{ + DEBUGMSGTL(("sila:handle", + "Processing request (%d) for silaHostPowerState\n", + reqinfo->mode)); + + switch (reqinfo->mode) + { + case MODE_GET: + for (netsnmp_request_info* request = requests; request; + request = request->next) + { + phosphor::snmp::agent::VariableList::set(request->requestvb, + state.toSNMPValue()); + } + break; + } + + return SNMP_ERR_NOERROR; +} + +void init() +{ + DEBUGMSGTL(("sila:init", "Initialize silaHostPowerState\n")); + + state.update(); + + netsnmp_register_read_only_instance(netsnmp_create_handler_registration( + "silaHostPowerState", State_snmp_handler, state_oid.data(), + state_oid.size(), HANDLER_CAN_RONLY)); +} +void destroy() +{ + DEBUGMSGTL(("sila:shutdown", "destroy silaHostPowerState\n")); + unregister_mib(const_cast<oid*>(state_oid.data()), state_oid.size()); +} + +} // namespace state +} // namespace power +} // namespace host +} // namespace sila diff --git a/agent/sila/powerstate.hpp b/agent/sila/powerstate.hpp new file mode 100644 index 0000000..937786d --- /dev/null +++ b/agent/sila/powerstate.hpp @@ -0,0 +1,38 @@ +/** + * @brief SILA host power state implementation. + * + * This file is part of sila-snmp project. + * + * Copyright (c) 2022 SILA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#pragma once + +namespace sila +{ +namespace host +{ +namespace power +{ +namespace state +{ + +void init(); +void destroy(); + +} // namespace state +} // namespace power +} // namespace host +} // namespace sila diff --git a/agent/sila/sensors.cpp b/agent/sila/sensors.cpp new file mode 100644 index 0000000..5cb8392 --- /dev/null +++ b/agent/sila/sensors.cpp @@ -0,0 +1,385 @@ +/** + * @brief SILA sensors tables implementation. + * + * This file is part of sila-snmp project. + * + * Copyright (c) 2022 SILA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "tracing.hpp" +#include "data/table.hpp" +#include "data/table/item.hpp" +#include "sila/sila_oid.hpp" +#include "snmptrap.hpp" + +#include <cmath> + +namespace sila +{ +namespace sensors +{ +/** + * @brief Sensor implementation. + */ +struct Sensor + : public phosphor::snmp::data::table::Item<double, double, bool, double, + bool, double, bool, double, bool> +{ + /** + * @brief State codes like in MIB file. + */ + enum state_t + { + E_DISABLED = 0, + E_NORMAL, + E_WARNING_LOW, + E_WARNING_HIGH, + E_CRITICAL_LOW, + E_CRITICAL_HIGH, + }; + + // SNMP table columns + enum Columns + { + COLUMN_SILASENSOR_NAME = 1, + COLUMN_SILASENSOR_VALUE, + COLUMN_SILASENSOR_WARNLOW, + COLUMN_SILASENSOR_WARNHIGH, + COLUMN_SILASENSOR_CRITLOW, + COLUMN_SILASENSOR_CRITHIGH, + COLUMN_SILASENSOR_STATE, + }; + + // Indexes of fields in tuple + enum Fields + { + FIELD_SENSOR_VALUE = 0, + FIELD_SENSOR_WARNLOW, + FIELD_SENSOR_WARNLOW_ALARM, + FIELD_SENSOR_WARNHI, + FIELD_SENSOR_WARNHI_ALARM, + FIELD_SENSOR_CRITLOW, + FIELD_SENSOR_CRITLOW_ALARM, + FIELD_SENSOR_CRITHI, + FIELD_SENSOR_CRITHI_ALARM, + }; + + // Sensor types (first letter in sensors folder name) + enum Types + { + ST_TEMPERATURE = 't', // temperature + ST_VOLTAGE = 'v', // voltage + ST_TACHOMETER = 'f', // fan_tach + ST_CURRENT = 'c', // current + ST_POWER = 'p', // power + }; + + /** + * @brief Object contructor. + */ + Sensor(const std::string& folder, const std::string& name) : + phosphor::snmp::data::table::Item<double, double, bool, double, bool, + double, bool, double, bool>( + folder, name, + .0, // Value + .0, false, // WarningLow + .0, false, // WarningHigh + .0, false, // CriticalLow + .0, false) // CriticalHigh + { + auto n = folder.rfind('/'); + if (n != std::string::npos) + { + // Prepare for send traps + switch (folder[n + 1]) + { + case ST_TEMPERATURE: + _notifyOid.assign(SILA_OID(0, 2)); + break; + + case ST_VOLTAGE: + _notifyOid.assign(SILA_OID(0, 3)); + break; + + case ST_TACHOMETER: + _notifyOid.assign(SILA_OID(0, 4)); + break; + + case ST_CURRENT: + _notifyOid.assign(SILA_OID(0, 5)); + break; + + case ST_POWER: + _notifyOid.assign(SILA_OID(0, 6)); + break; + } + + // Correct scale power + // Required for TEXTUAL-CONVENTION in SILA-MIB.txt + switch (folder[n + 1]) + { + case ST_TACHOMETER: + _power = 0; + break; + } + } + + if (!_notifyOid.empty()) + { + phosphor::snmp::agent::make_oid( + _stateOid, ".1.3.6.1.4.1.49769.1.%lu.1.7.\"%s\"", + _notifyOid.back(), name.c_str()); + } + } + + /** + * @brief Update fields with new values recieved from DBus. + */ + void setFields(const fields_map_t& fields) override + { + auto prevValue = getValue<FIELD_SENSOR_VALUE>(); + auto prevState = getState(); + + setField<FIELD_SENSOR_VALUE>(fields, "Value"); + setField<FIELD_SENSOR_WARNLOW>(fields, "WarningLow"); + setField<FIELD_SENSOR_WARNLOW_ALARM>(fields, "WarningAlarmLow"); + setField<FIELD_SENSOR_WARNHI>(fields, "WarningHigh"); + setField<FIELD_SENSOR_WARNHI_ALARM>(fields, "WarningAlarmHigh"); + setField<FIELD_SENSOR_CRITLOW>(fields, "CriticalLow"); + setField<FIELD_SENSOR_CRITLOW_ALARM>(fields, "CriticalAlarmLow"); + setField<FIELD_SENSOR_CRITHI>(fields, "CriticalHigh"); + setField<FIELD_SENSOR_CRITHI_ALARM>(fields, "CriticalAlarmHigh"); + + auto lastState = getState(); + + if (prevValue != getValue<FIELD_SENSOR_VALUE>() || + prevState != lastState) + { + DEBUGMSGTL(("sila:sensors", + "Sensor '%s' changed: %d -> %d, state: %d -> %d\n", + name.c_str(), prevValue, getValue<FIELD_SENSOR_VALUE>(), + prevState, lastState)); + } + + if (prevState != lastState) + { + send_notify(lastState); + } + } + + /** + * @brief Called when sensor added to table. + */ + void onCreate() override + { + DEBUGMSGTL(("sila:sensors", "Sensor '%s' added, state=%d\n", + name.c_str(), getState())); + send_notify(E_NORMAL); + } + + /** + * @brief Called when sensor removed from table. + */ + void onDestroy() override + { + DEBUGMSGTL(("sila:sensors", "Sensor '%s' removed, state=%d\n", + name.c_str(), getState())); + send_notify(E_DISABLED); + } + + /** + * @brief Send snmptrap about changed state. + * + * @param state - state value for sent with trap. + */ + void send_notify(state_t state) + { + if (!_notifyOid.empty() && !_stateOid.empty()) + { + phosphor::snmp::agent::Trap trap(_notifyOid); + trap.add_field(_stateOid.data(), _stateOid.size(), state); + trap.send(); + } + else + { + TRACE_ERROR("Notify is unsupported for sensor '%s'\n", + name.c_str()); + } + } + + /** + * @brief Get current state. + */ + state_t getState() const + { + if (std::get<FIELD_SENSOR_CRITHI_ALARM>(data)) + { + return E_CRITICAL_HIGH; + } + else if (std::get<FIELD_SENSOR_WARNHI_ALARM>(data)) + { + return E_WARNING_HIGH; + } + else if (std::get<FIELD_SENSOR_WARNLOW_ALARM>(data)) + { + return E_WARNING_LOW; + } + else if (std::get<FIELD_SENSOR_CRITLOW_ALARM>(data)) + { + return E_CRITICAL_LOW; + } + + return E_NORMAL; + } + + /** + * @brief snmp request handler. + */ + void get_snmp_reply(netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* request) const override + { + using namespace phosphor::snmp::agent; + + netsnmp_table_request_info* tinfo = netsnmp_extract_table_info(request); + + switch (tinfo->colnum) + { + case COLUMN_SILASENSOR_NAME: + VariableList::set(request->requestvb, name); + break; + + case COLUMN_SILASENSOR_VALUE: + VariableList::set(request->requestvb, + getValue<FIELD_SENSOR_VALUE>()); + break; + + case COLUMN_SILASENSOR_WARNLOW: + VariableList::set(request->requestvb, + getValue<FIELD_SENSOR_WARNLOW>()); + break; + + case COLUMN_SILASENSOR_WARNHIGH: + VariableList::set(request->requestvb, + getValue<FIELD_SENSOR_WARNHI>()); + break; + + case COLUMN_SILASENSOR_CRITLOW: + VariableList::set(request->requestvb, + getValue<FIELD_SENSOR_CRITLOW>()); + break; + + case COLUMN_SILASENSOR_CRITHIGH: + VariableList::set(request->requestvb, + getValue<FIELD_SENSOR_CRITHI>()); + break; + + case COLUMN_SILASENSOR_STATE: + VariableList::set(request->requestvb, getState()); + break; + + default: + netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT); + break; + } + } + + /** + * @brief Scale and round sensors value. + */ + template <size_t Idx> int getValue() const + { + return static_cast<int>( + std::round(std::get<Idx>(data) * powf(10.f, _power))); + } + + std::vector<oid> _notifyOid; + std::vector<oid> _stateOid; + int _power = 3; +}; + +struct SensorsTable : public phosphor::snmp::data::Table<Sensor> +{ + using OID = std::vector<oid>; + + SensorsTable(const std::string& folder, const std::string& tableName, + const OID& tableOID) : + phosphor::snmp::data::Table<Sensor>( + "/xyz/openbmc_project/sensors/" + folder, + { + "xyz.openbmc_project.Sensor.Value", + "xyz.openbmc_project.Sensor.Threshold.Warning", + "xyz.openbmc_project.Sensor.Threshold.Critical", + "xyz.openbmc_project.Sensor.Threshold.Fatal", + }), + tableName(tableName), tableOID(tableOID) + + { + } + + std::string tableName; + OID tableOID; +}; + +static std::array<SensorsTable, 5> sensors = { + SensorsTable{"temperature", "silaTempSensorsTable", SILA_OID(1, 2)}, + SensorsTable{"voltage", "silaVoltSensorsTable", SILA_OID(1, 3)}, + SensorsTable{"fan_tach", "silaTachSensorsTable", SILA_OID(1, 4)}, + SensorsTable{"current", "silaCurrSensorsTable", SILA_OID(1, 5)}, + SensorsTable{"power", "silaPowerSensorsTable", SILA_OID(1, 6)}, +}; + +/** + * @brief Update all sensors. + */ +void update() +{ + for (auto& s : sensors) + { + s.update(); + } +} + +/** + * @brief Initialize sensors. + */ +void init() +{ + DEBUGMSGTL(("sila:init", "Initialize silaSensors\n")); + + for (auto& s : sensors) + { + s.init_mib(s.tableName.c_str(), s.tableOID.data(), s.tableOID.size(), + Sensor::COLUMN_SILASENSOR_NAME, + Sensor::COLUMN_SILASENSOR_STATE); + s.update(); + } +} + +/** + * @brief Deinitialize sensors. + */ +void destroy() +{ + DEBUGMSGTL(("sila:shutdown", "Destroy silaSensors\n")); + + for (auto& s : sensors) + { + unregister_mib(s.tableOID.data(), s.tableOID.size()); + } +} + +} // namespace sensors +} // namespace sila diff --git a/agent/sila/sensors.hpp b/agent/sila/sensors.hpp new file mode 100644 index 0000000..6ce123c --- /dev/null +++ b/agent/sila/sensors.hpp @@ -0,0 +1,33 @@ +/** + * @brief SILA sensors tables implementation. + * + * This file is part of sila-snmp project. + * + * Copyright (c) 2022 SILA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#pragma once + +namespace sila +{ +namespace sensors +{ + +void init(); +void update(); +void destroy(); + +} // namespace sensors +} // namespace sila diff --git a/agent/sila/sila_oid.hpp b/agent/sila/sila_oid.hpp new file mode 100644 index 0000000..033a7e7 --- /dev/null +++ b/agent/sila/sila_oid.hpp @@ -0,0 +1,27 @@ +/** + * @brief SILA OID helper + * + * This file is part of yadro-snmp project. + * + * Copyright (c) 2022 SILA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#define SILA_OID(args...) \ + { \ + 1, 3, 6, 1, 4, 1, 49769, ##args \ + } diff --git a/agent/sila/software.cpp b/agent/sila/software.cpp new file mode 100644 index 0000000..bed1130 --- /dev/null +++ b/agent/sila/software.cpp @@ -0,0 +1,225 @@ +/** + * @brief SILA software table implementation. + * + * This file is part of sila-snmp project. + * + * Copyright (c) 2022 SILA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "tracing.hpp" +#include "data/table.hpp" +#include "data/table/item.hpp" +#include "data/enums.hpp" +#include "sila/sila_oid.hpp" +#include "snmpvars.hpp" + +#define INVALID_ENUM_VALUE 0xFF + +namespace sila +{ +namespace software +{ + +/** + * @brief DBus enum to byte converters. + */ +const phosphor::snmp::data::DBusEnum<uint8_t> + purpose = {"xyz.openbmc_project.Software.Version.VersionPurpose", + { + {"Unknown", 0}, + {"Other", 1}, + {"System", 2}, + {"BMC", 3}, + {"Host", 4}, + }, + INVALID_ENUM_VALUE}, + + activation = {"xyz.openbmc_project.Software.Activation.Activations", + { + {"NotReady", 0}, + {"Invalid", 1}, + {"Ready", 2}, + {"Activating", 3}, + {"Active", 4}, + {"Failed", 5}, + }, + INVALID_ENUM_VALUE}; + +/** + * @brief Software item implementation. + */ +struct Software : public phosphor::snmp::data::table::Item<std::string, uint8_t, + uint8_t, uint8_t> +{ + // Indexes of fields in tuple + enum Fields + { + FIELD_SOFTWARE_VERSION = 0, + FIELD_SOFTWARE_PURPOSE, + FIELD_SOFTWARE_ACTIVATION, + FIELD_SOFTWARE_PRIORITY, + }; + + /** + * @brief Object constructor. + */ + Software(const std::string& folder, const std::string& name) : + phosphor::snmp::data::table::Item<std::string, uint8_t, uint8_t, + uint8_t>( + folder, name, + "", // Version + INVALID_ENUM_VALUE, // Purpose + INVALID_ENUM_VALUE, // Activation + INVALID_ENUM_VALUE) // Priority + { + } + + /** + * @brief Set field value with DBus enum converter. + * + * @tparam Idx - Index of field + * @param fields - DBus fields map + * @param field - Name of field in DBus + * @param enumcvt - Enum converter + */ + template <size_t Idx> + void setFieldEnum(const fields_map_t& fields, const char* field, + const phosphor::snmp::data::DBusEnum<uint8_t>& enumcvt) + { + if (fields.find(field) != fields.end() && + std::holds_alternative<std::string>(fields.at(field))) + { + std::get<Idx>(data) = + enumcvt.get(std::get<std::string>(fields.at(field))); + } + } + + /** + * @brief Update fields with new values recieved from DBus. + */ + void setFields(const fields_map_t& fields) override + { + uint8_t prevActivation = std::get<FIELD_SOFTWARE_ACTIVATION>(data), + prevPriority = std::get<FIELD_SOFTWARE_PRIORITY>(data); + + setField<FIELD_SOFTWARE_VERSION>(fields, "Version"); + setFieldEnum<FIELD_SOFTWARE_PURPOSE>(fields, "Purpose", purpose); + setFieldEnum<FIELD_SOFTWARE_ACTIVATION>(fields, "Activation", + activation); + setField<FIELD_SOFTWARE_PRIORITY>(fields, "Priority"); + + if (prevActivation != std::get<FIELD_SOFTWARE_ACTIVATION>(data) || + prevPriority != std::get<FIELD_SOFTWARE_PRIORITY>(data)) + { + DEBUGMSGTL(("sila:software", + "Software '%s' version='%s', purpose=%d changed: " + "activation %d -> %d, priority %d -> %d\n", + name.c_str(), + std::get<FIELD_SOFTWARE_VERSION>(data).c_str(), + std::get<FIELD_SOFTWARE_PURPOSE>(data), prevActivation, + std::get<FIELD_SOFTWARE_ACTIVATION>(data), prevPriority, + std::get<FIELD_SOFTWARE_PRIORITY>(data))); + } + } + + enum Columns + { + COLUMN_SILASOFTWARE_HASH = 1, + COLUMN_SILASOFTWARE_VERSION = 2, + COLUMN_SILASOFTWARE_PURPOSE = 3, + COLUMN_SILASOFTWARE_ACTIVATION = 4, + COLUMN_SILASOFTWARE_PRIORITY = 5, + }; + + /** + * @brief snmp request handler. + */ + void get_snmp_reply(netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* request) const override + { + using namespace phosphor::snmp::agent; + + netsnmp_table_request_info* tinfo = netsnmp_extract_table_info(request); + + switch (tinfo->colnum) + { + case COLUMN_SILASOFTWARE_HASH: + VariableList::set(request->requestvb, name); + break; + + case COLUMN_SILASOFTWARE_VERSION: + VariableList::set(request->requestvb, + std::get<FIELD_SOFTWARE_VERSION>(data)); + break; + + case COLUMN_SILASOFTWARE_PURPOSE: + VariableList::set(request->requestvb, + std::get<FIELD_SOFTWARE_PURPOSE>(data)); + break; + + case COLUMN_SILASOFTWARE_ACTIVATION: + VariableList::set(request->requestvb, + std::get<FIELD_SOFTWARE_ACTIVATION>(data)); + break; + + case COLUMN_SILASOFTWARE_PRIORITY: + VariableList::set(request->requestvb, + std::get<FIELD_SOFTWARE_PRIORITY>(data)); + break; + + default: + netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT); + break; + } + } +}; + +constexpr oid softwareOid[] = SILA_OID(5); +constexpr auto SOFTWARE_FOLDER = "/xyz/openbmc_project/software"; + +static phosphor::snmp::data::Table<Software> + softwareTable("/xyz/openbmc_project/software", + { + "xyz.openbmc_project.Software.Version", + "xyz.openbmc_project.Software.Activation", + "xyz.openbmc_project.Software.RedundancyPriority", + }); + +/** + * @brief Initialize software table + */ +void init() +{ + DEBUGMSGTL(("sila:init", "Initialize silaSoftwareTable\n")); + + softwareTable.update(); + softwareTable.init_mib("silaSoftwareTable", softwareOid, + OID_LENGTH(softwareOid), + Software::COLUMN_SILASOFTWARE_HASH, + Software::COLUMN_SILASOFTWARE_PRIORITY); +} + +/** + * @brief Deinitialize software table + */ +void destroy() +{ + DEBUGMSGTL(("sila:shutdown", "Deinitialize silaSoftwareTable\n")); + unregister_mib(const_cast<oid*>(softwareOid), OID_LENGTH(softwareOid)); +} + +} // namespace software +} // namespace sila diff --git a/agent/sila/software.hpp b/agent/sila/software.hpp new file mode 100644 index 0000000..3cd1d87 --- /dev/null +++ b/agent/sila/software.hpp @@ -0,0 +1,32 @@ +/** + * @brief SILA software tables implementation. + * + * This file is part of sila-snmp project. + * + * Copyright (c) 2022 SILA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#pragma once + +namespace sila +{ +namespace software +{ + +void init(); +void destroy(); + +} // namespace software +} // namespace sila |