summaryrefslogtreecommitdiff
path: root/agent/sila
diff options
context:
space:
mode:
authorAndrey V.Kosteltsev <AKosteltsev@IBS.RU>2022-07-15 10:36:51 +0300
committerAndrey V.Kosteltsev <AKosteltsev@IBS.RU>2022-07-15 10:36:51 +0300
commit0f4556fc2343dc0ade0bb1e0d1fc6f85770d77af (patch)
treee26b6cb64f8ac4625c45baa40834fe51dc25c4f8 /agent/sila
downloadobmc-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.cpp249
-rw-r--r--agent/sila/inventory.hpp32
-rw-r--r--agent/sila/powerstate.cpp140
-rw-r--r--agent/sila/powerstate.hpp38
-rw-r--r--agent/sila/sensors.cpp385
-rw-r--r--agent/sila/sensors.hpp33
-rw-r--r--agent/sila/sila_oid.hpp27
-rw-r--r--agent/sila/software.cpp225
-rw-r--r--agent/sila/software.hpp32
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