Compare commits
6 Commits
be895664d1
...
9bad35fcd5
| Author | SHA1 | Date | |
|---|---|---|---|
| 9bad35fcd5 | |||
| b421086235 | |||
| 8ef830e1ad | |||
| 3dd849e515 | |||
| 09a63770d7 | |||
| fe51d046f6 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
cont/code/__pycache__
|
||||||
|
cont/code/venv
|
||||||
20
MIBs.md
20
MIBs.md
@@ -26,6 +26,23 @@ input voltage for PSU(s)
|
|||||||
|
|
||||||
Divide by 2 to get power usage
|
Divide by 2 to get power usage
|
||||||
|
|
||||||
|
### To get per PSU power in watts
|
||||||
|
|
||||||
|
Multiply Amperage by Voltage:
|
||||||
|
|
||||||
|
.1.3.6.1.4.1.674.10892.5.4.600.30.1.6 (amperageProbeReading)
|
||||||
|
|
||||||
|
1.1 is PSU1 and 1.2 is PSU2
|
||||||
|
|
||||||
|
Amperage is in hundreds of miliamps. So if amperageProbeReading read 2, it means that PSUx is drawing 0.2 Amps
|
||||||
|
|
||||||
|
|
||||||
|
.1.3.6.1.4.1.674.10892.5.4.600.20.1.6 (voltageProbeReading)
|
||||||
|
|
||||||
|
1.31 is PSU1 and 1.32 is PSU2
|
||||||
|
|
||||||
|
Here the voltage i given without deicmal point (240000) so decimal point needs to be moved (240.000) or just convert to int (240)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## other
|
## other
|
||||||
@@ -34,3 +51,6 @@ Divide by 2 to get power usage
|
|||||||
|
|
||||||
Get the CPU name(s)
|
Get the CPU name(s)
|
||||||
|
|
||||||
|
.1.3.6.1.4.1.674.10892.5.2.5.0 (systemPowerUpTime)
|
||||||
|
|
||||||
|
Uptime in seconds
|
||||||
13500
cont/code/MIBs/iDRAC-SMIv1.mib
Normal file
13500
cont/code/MIBs/iDRAC-SMIv1.mib
Normal file
File diff suppressed because it is too large
Load Diff
15282
cont/code/MIBs/iDRAC-SMIv2.mib
Normal file
15282
cont/code/MIBs/iDRAC-SMIv2.mib
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@ from datetime import datetime, timezone, timedelta
|
|||||||
from typing import Optional, Annotated
|
from typing import Optional, Annotated
|
||||||
from sqlalchemy.dialects.mysql import MEDIUMINT, TINYINT, TEXT, TIMESTAMP, FLOAT
|
from sqlalchemy.dialects.mysql import MEDIUMINT, TINYINT, TEXT, TIMESTAMP, FLOAT
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
# -----------
|
# -----------
|
||||||
|
|
||||||
@@ -41,21 +42,23 @@ class snmpValueToHost():
|
|||||||
lastV: Annotated[str, "Last value\nNone when initializing"] = None,
|
lastV: Annotated[str, "Last value\nNone when initializing"] = None,
|
||||||
remote: Annotated[str, "Needs to be a valid IP address"] = None,
|
remote: Annotated[str, "Needs to be a valid IP address"] = None,
|
||||||
nextV: Annotated[str, "Value to insert next\nNone when initializing"] = None,
|
nextV: Annotated[str, "Value to insert next\nNone when initializing"] = None,
|
||||||
lasttime: Annotated[float, "Last time since sending data\nSince Epoch"] = time.time(),
|
lasttime: Annotated[float, "Last time since sending data\nSince Epoch, so time.time()"] = None,
|
||||||
lastStatus: Annotated[bool, "True if value newest value was inserted\nFalse if data is pending to be sent"] = False
|
lastStatus: Annotated[bool, "True if value newest value was inserted\nFalse if data is pending to be sent"] = False
|
||||||
):
|
):
|
||||||
self.__lastValue = lastV # Save last sent value
|
self.__lastValue = lastV # Save last sent value
|
||||||
self.__remote = remote # Remote IP address of the SNMP client
|
self.__remote = remote # Remote IP address of the SNMP client
|
||||||
self.__nextValue = nextV # Next value to send. Maybe will be used
|
self.__nextValue = nextV # Next value to send. Maybe will be used
|
||||||
self.__lastSentStatus = lastStatus # If previously sent new data, set to True.
|
self.__lastSentStatus = lastStatus # If previously sent new data, set to True.
|
||||||
self.__lastSentTime = lasttime # The time since the Epoch of the last sent data*
|
self.__lastSentTime = lasttime if lasttime else time.time() # The time since the Epoch of the last sent data*
|
||||||
|
|
||||||
|
|
||||||
def ___str__():
|
def ___str__(self):
|
||||||
return self.__lastValue
|
return self.__lastValue
|
||||||
|
|
||||||
def __changeLastSentValue(slef):
|
def __changeLastSentValue(self, newValue: int) -> bool:
|
||||||
|
self.__nextValue == newValue
|
||||||
self.__lastValue = self.__nextValue
|
self.__lastValue = self.__nextValue
|
||||||
|
self.__nextValue == None
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def __updateLastSentTime(self):
|
def __updateLastSentTime(self):
|
||||||
@@ -71,5 +74,23 @@ class snmpValueToHost():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def updateStats(self, nextV: Annotated[int, "Value that will be set as the new one"]):
|
def updateStats(self, nextV: Annotated[int, "Value that will be set as the new one"]):
|
||||||
|
__changeLastSentValue(nextV)
|
||||||
|
__updateLastSentTime()
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# Dataclasses--------------------------------------------
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class snmpPyData():
|
||||||
|
"""Dataclass for snmp data"""
|
||||||
|
hostname: str
|
||||||
|
powerDrawPSU1: int
|
||||||
|
powerDrawPSU2: int
|
||||||
|
voltagePSU1: int
|
||||||
|
voltagePSU2: int
|
||||||
|
inletTemp: float
|
||||||
|
exhaustTemp: float
|
||||||
|
uptime: int
|
||||||
|
|
||||||
|
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
import influxdb_client, sqlalchemy, random, os
|
import influxdb_client, sqlalchemy, random, os
|
||||||
# from sqlmodel import Field, Session, SQLModel, create_engine, select
|
# from sqlmodel import Field, Session, SQLModel, create_engine, select
|
||||||
from sqlalchemy import create_engine, exc # , Column, Integer, String, Numeric
|
from sqlalchemy import create_engine, exc # , Column, Integer, String, Numeric
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
# from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
from typing import Annotated, Final
|
|
||||||
from classes import *
|
from classes import *
|
||||||
|
from typing import Annotated, Final
|
||||||
|
|
||||||
from influxdb_client import InfluxDBClient, Point, WritePrecision
|
from influxdb_client import InfluxDBClient, Point, WritePrecision
|
||||||
from influxdb_client.client.write_api import SYNCHRONOUS, ASYNCHRONOUS, WriteOptions
|
from influxdb_client.client.write_api import SYNCHRONOUS, ASYNCHRONOUS, WriteOptions
|
||||||
@@ -68,12 +67,12 @@ def tempsnsrIntoSQLDB(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
data = sensorsqldata(tableInp)
|
sqlData = sensorsqldata(tableInp)
|
||||||
# print("2")
|
# print("2")
|
||||||
|
|
||||||
if inpayload:
|
if inpayload:
|
||||||
# print("3")
|
# print("3")
|
||||||
session.add(data(
|
session.add(sqlData(
|
||||||
data = insertData,
|
data = insertData,
|
||||||
))
|
))
|
||||||
# print("4")
|
# print("4")
|
||||||
@@ -88,7 +87,7 @@ def tempsnsrIntoSQLDB(
|
|||||||
|
|
||||||
|
|
||||||
def tempsnsrIntoFluxQLDB(
|
def tempsnsrIntoFluxQLDB(
|
||||||
inpayload: Annotated[SensorPayload, "Payload"]
|
inpayload: Annotated[snmpPyData, "Payload"]
|
||||||
) -> int:
|
) -> int:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,32 +1,61 @@
|
|||||||
# import pysnmp, sqlalchemy, ipaddress, os, influxdb_client, re, time
|
import ipaddress, os, re, time, funct, classes
|
||||||
# # SQL
|
|
||||||
# from sqlalchemy import create_engine, exc # , Column, Integer, String, Numeric
|
|
||||||
# from sqlalchemy.ext.declarative import declarative_base
|
|
||||||
# from sqlalchemy.orm import sessionmaker
|
|
||||||
# # InfluxDB
|
|
||||||
# from influxdb_client import InfluxDBClient, Point, WritePrecision
|
|
||||||
# from influxdb_client.client.write_api import SYNCHRONOUS, ASYNCHRONOUS, WriteOptions
|
|
||||||
import pysnmp, ipaddress, os, re, time
|
|
||||||
#SNMP
|
#SNMP
|
||||||
from easysnmp import Session
|
# from easysnmp import Session
|
||||||
# QoL
|
# QoL
|
||||||
from typing import Annotated
|
from typing import Annotated, Final
|
||||||
|
|
||||||
|
|
||||||
# Program ENV---------------------------------------
|
# Program ENV---------------------------------------
|
||||||
HOST_LIST: Final[list] = os.getenv("HOST_LIST")
|
HOST_LIST: Final[list] = os.getenv("HOST_LIST")
|
||||||
try:
|
|
||||||
RESOLV_ADDR: Final[str] = ipaddress(os.getenv("RESOLV_ADDR", "1.1.1.1"))
|
|
||||||
except ValueError:
|
|
||||||
raise Exception("Malformed DNS IP address.\nExiting...")
|
|
||||||
|
|
||||||
|
SNMPUSER: Final[str] = os.getenv("SNMPUSER", None)
|
||||||
|
SNMPPRIVKEY: Final[str] = os.getenv("SNMPPRIVKEY", None)
|
||||||
|
SNMPAUTHKEY: Final[str] = os.getenv("SNMPAUTHKEY", None)
|
||||||
|
|
||||||
|
SNMPAUTHPROTO: Final[str] = os.getenv("SNMPAUTHPROTO", "SHA")
|
||||||
|
SNMPPRIVPROTO: Final[str] = os.getenv("SNMPPRIVPROTO", "SHA")
|
||||||
|
SNMPORT: Final[int] = os.getenv("SNMPORT", 161)
|
||||||
|
|
||||||
# Flightchecks-------------------------------------------
|
# Flightchecks-------------------------------------------
|
||||||
# Helper functions
|
|
||||||
def check_host_string(input_text: Annotated[str, "Text to check"]):
|
|
||||||
pattern = re.compile(r"^[A-Za-z].*$", re.IGNORECASE)
|
|
||||||
return pattern.match(input_text)
|
|
||||||
|
|
||||||
|
if SNMPAUTHPROTO and SNMPPRIVPROTO == "SHA":
|
||||||
|
USMDATA = UsmUserData(
|
||||||
|
userName=SNMPUSER,
|
||||||
|
authKey=SNMPPRIVKEY,
|
||||||
|
privKey=SNMPAUTHKEY,
|
||||||
|
authProtocol=usmHMACSHAAuthProtocol,
|
||||||
|
privProtocol=usmHMACSHAAuthProtocol,
|
||||||
|
)
|
||||||
|
elif SNMPAUTHPROTO and SNMPPRIVPROTO == "AES128":
|
||||||
|
USMDATA = UsmUserData(
|
||||||
|
userName=SNMPUSER,
|
||||||
|
authKey=SNMPPRIVKEY,
|
||||||
|
privKey=SNMPAUTHKEY,
|
||||||
|
authProtocol=usmAesCfb128Protocol,
|
||||||
|
privProtocol=usmAesCfb128Protocol,
|
||||||
|
)
|
||||||
|
elif SNMPAUTHPROTO == "SHA" and SNMPPRIVPROTO == "AES128":
|
||||||
|
USMDATA = UsmUserData(
|
||||||
|
userName=SNMPUSER,
|
||||||
|
authKey=SNMPPRIVKEY,
|
||||||
|
privKey=SNMPAUTHKEY,
|
||||||
|
authProtocol=usmHMACSHAAuthProtocol,
|
||||||
|
privProtocol=usmAesCfb128Protocol,
|
||||||
|
)
|
||||||
|
elif SNMPAUTHPROTO == "AES128" and SNMPPRIVPROTO == "SHA":
|
||||||
|
USMDATA = UsmUserData(
|
||||||
|
userName=SNMPUSER,
|
||||||
|
authKey=SNMPPRIVKEY,
|
||||||
|
privKey=SNMPAUTHKEY,
|
||||||
|
authProtocol=usmAesCfb128Protocol,
|
||||||
|
privProtocol=usmHMACSHAAuthProtocol,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise Exception(f"No PrivAuth option like {SNMPPRIVPROTO}")
|
||||||
|
|
||||||
|
# Helper functions
|
||||||
|
def wattageCalc(amperageInp: float, voltageInp: float) -> float:
|
||||||
|
return amperageInp * voltageInp
|
||||||
|
|
||||||
# Check if some neccesary ENVs are passed
|
# Check if some neccesary ENVs are passed
|
||||||
if not USEINFLUX and not USESQL:
|
if not USEINFLUX and not USESQL:
|
||||||
@@ -37,25 +66,67 @@ if not HOST_LIST:
|
|||||||
raise Exception("No hosts passed\nExiting...")
|
raise Exception("No hosts passed\nExiting...")
|
||||||
|
|
||||||
# for host in HOST_LIST check if valid IP and create a list with strings
|
# for host in HOST_LIST check if valid IP and create a list with strings
|
||||||
HOST_LIST_STR = []
|
|
||||||
for host in HOST_LIST:
|
for host in HOST_LIST:
|
||||||
try:
|
try:
|
||||||
if not check_host_string(host):
|
ip = str(ipaddress.IPv4Address(host))
|
||||||
ip = str(ipaddress.IPv4Address(host))
|
|
||||||
HOST_LIST_STR.append(ip)
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise Exception("Malformed IP address.\nExiting...")
|
raise Exception(f" IP {ip} is invalid.\nExiting...")
|
||||||
|
|
||||||
# Checking if DNS IP is a valid one
|
|
||||||
# Done under "Program ENV" on line ~18
|
# Done under "Program ENV" on line ~18
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Code-------------------------------------------
|
# Code-------------------------------------------
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from pysnmp.hlapi.v3arch.asyncio import *
|
||||||
|
|
||||||
|
|
||||||
|
async def poolRemote(remoteIP: str):
|
||||||
|
snmpEngine = SnmpEngine()
|
||||||
|
|
||||||
|
iterator = get_cmd(
|
||||||
|
snmpEngine,
|
||||||
|
UsmUserData(
|
||||||
|
userName=SNMPUSER,
|
||||||
|
authKey=SNMPPRIVKEY,
|
||||||
|
privKey=SNMPAUTHKEY,
|
||||||
|
authProtocol=usmHMACSHAAuthProtocol,
|
||||||
|
privProtocol=usmHMACSHAAuthProtocol,
|
||||||
|
),
|
||||||
|
await UdpTransportTarget.create((remoteIP, SNMPORT)),
|
||||||
|
ContextData(),
|
||||||
|
ObjectType(ObjectIdentity("SNMPv2-MIB", "sysDescr", 0)),
|
||||||
|
)
|
||||||
|
|
||||||
|
errorIndication, errorStatus, errorIndex, varBinds = await iterator
|
||||||
|
|
||||||
|
if errorIndication:
|
||||||
|
print(errorIndication)
|
||||||
|
|
||||||
|
elif errorStatus:
|
||||||
|
print(
|
||||||
|
"{} at {}".format(
|
||||||
|
errorStatus.prettyPrint(),
|
||||||
|
errorIndex and varBinds[int(errorIndex) - 1][0] or "?",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
for varBind in varBinds:
|
||||||
|
print(" = ".join([x.prettyPrint() for x in varBind]))
|
||||||
|
|
||||||
|
snmpEngine.close_dispatcher()
|
||||||
|
|
||||||
|
|
||||||
|
# tasks = [poolRemote(ip) for ip in HOST_LIST]
|
||||||
|
# results = await asyncio.gather(*tasks)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Create connections for MySQL and InfluxDB
|
# Create connections for MySQL and InfluxDB
|
||||||
|
|
||||||
# functions for inserting data into two DBs
|
# functions for inserting data into two DBs
|
||||||
|
|||||||
8
cont/conf/contLogs.sh
Normal file
8
cont/conf/contLogs.sh
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
docker container logs snmpython-dev -f
|
||||||
|
|
||||||
|
# Added to have time to ctrl+C
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
@@ -6,7 +6,6 @@ pydantic_core==2.27.2
|
|||||||
annotated-types==0.7.0
|
annotated-types==0.7.0
|
||||||
typing==3.7.4.3
|
typing==3.7.4.3
|
||||||
pysnmp==7.1.22
|
pysnmp==7.1.22
|
||||||
|
|
||||||
# requests==2.32.3
|
# requests==2.32.3
|
||||||
influxdb-client==1.49.0
|
influxdb-client==1.49.0
|
||||||
# influxdb3-python==0.18.0
|
# influxdb3-python==0.18.0
|
||||||
43
tables.sql
Normal file
43
tables.sql
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
-- Set correct timezone
|
||||||
|
SET GLOBAL time_zone = 'Europe/Stockholm';
|
||||||
|
|
||||||
|
SET NAMES utf8;
|
||||||
|
SET time_zone = '+01:00';
|
||||||
|
-- SET foreign_key_checks = 0;
|
||||||
|
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
|
||||||
|
|
||||||
|
-- DELIMITER ;;
|
||||||
|
|
||||||
|
-- DROP EVENT IF EXISTS `Prune_old_entries_staging_sensr`;;
|
||||||
|
|
||||||
|
-- CREATE EVENT `Prune_old_entries_staging_sensr`
|
||||||
|
-- ON SCHEDULE EVERY 1 DAY STARTS '2024-07-17 22:00:00'
|
||||||
|
-- ON COMPLETION
|
||||||
|
-- NOT PRESERVE
|
||||||
|
-- DISABLE ON SLAVE DO
|
||||||
|
-- DELETE FROM staging_sensr
|
||||||
|
-- WHERE time_stamp < NOW() - INTERVAL 2 MONTH;;
|
||||||
|
|
||||||
|
-- DELIMITER ;
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- New universal table type
|
||||||
|
CREATE TABLE newUniversalSensorTable (
|
||||||
|
id MEDIUMINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
time_stamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
hostname TEXT(30),
|
||||||
|
powerDrawPSU1 TINYINT(100),
|
||||||
|
powerDrawPSU2 TINYINT(100) NULL,
|
||||||
|
voltagePSU1 TINYINT(100),
|
||||||
|
voltagePSU2 TINYINT(100) NULL,
|
||||||
|
inletTemp FLOAT,
|
||||||
|
exhaustTemp FLOAT,
|
||||||
|
uptime INT NULL,
|
||||||
|
pressure FLOAT NULL,
|
||||||
|
altitude FLOAT NULL
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
Reference in New Issue
Block a user