This commit is contained in:
2026-02-25 23:16:46 +01:00
commit 216e216dee
10 changed files with 367 additions and 0 deletions

BIN
HBX2LV4aMAAstst.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

5
README Normal file
View File

@@ -0,0 +1,5 @@
Hi
<p align="center">
<img src="HBX2LV4aMAAstst.png" alt="">
</p>

75
cont/code/classes.py Normal file
View File

@@ -0,0 +1,75 @@
from pydantic import BaseModel
from sqlalchemy import Column, Date, Float, Integer, String, text
from datetime import datetime, timezone, timedelta
from typing import Optional, Annotated
from sqlalchemy.dialects.mysql import MEDIUMINT, TINYINT, TEXT, TIMESTAMP, FLOAT
from sqlalchemy.ext.declarative import declarative_base
# -----------
AlchemyBase = declarative_base()
# DATABASE "CLASSESS"--------------------------------------------
def sensorsqldata(table_name: Annotated[str, "Name of the table"]):
class SensorSQLDataClass(AlchemyBase):
__table_args__ = {'extend_existing': True}
__tablename__ = table_name
id = Column(MEDIUMINT(unsigned=True), primary_key=True, autoincrement=True)
time_stamp = Column(
TIMESTAMP,
server_default=text("CURRENT_TIMESTAMP"),
server_onupdate=text("CURRENT_TIMESTAMP"),
nullable=False,
)
TEMP = Column(TINYINT)
TEMP = Column(TEXT)
TEMP = Column(Float)
TEMP = Column(Float)
TEMP = Column(Float, nullable=True)
TEMP = Column(Float, nullable=True)
TEMP = Column(Float, nullable=True)
return SensorSQLDataClass
# Custom--------------------------------------------
class snmpValueToHost():
def __init__(self,
lastV: Annotated[str, "Last value\nNone when initializing"] = None,
remote: Annotated[str, "Needs to be a valid IP address"] = None,
nextV: Annotated[str, "Value to insert next\nNone when initializing"] = None,
lasttime: Annotated[float, "Last time since sending data\nSince Epoch"] = time.time(),
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.__remote = remote # Remote IP address of the SNMP client
self.__nextValue = nextV # Next value to send. Maybe will be used
self.__lastSentStatus = lastStatus # If previously sent new data, set to True.
self.__lastSentTime = lasttime # The time since the Epoch of the last sent data*
def ___str__():
return self.__lastValue
def __changeLastSentValue(slef):
self.__lastValue = self.__nextValue
return True
def __updateLastSentTime(self):
self.__lastSentTime = time.time()
return True
def __updateSentStatus(self):
if self.__lastSentStatus:
self.__lastSentStatus = False
return False
else:
self.__lastSentStatus = True
return True
def updateStats(self, nextV: Annotated[int, "Value that will be set as the new one"]):
...

123
cont/code/funct.py Normal file
View File

@@ -0,0 +1,123 @@
import influxdb_client, sqlalchemy, random, os
# from sqlmodel import Field, Session, SQLModel, create_engine, select
from sqlalchemy import create_engine, exc # , Column, Integer, String, Numeric
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from typing import Annotated, Final
from classes import *
from influxdb_client import InfluxDBClient, Point, WritePrecision
from influxdb_client.client.write_api import SYNCHRONOUS, ASYNCHRONOUS, WriteOptions
# import pymysql
# FluxQL ENV----------------------------------------
USEINFLUX: Final[bool] = os.getenv("USEINFLUX", True)
if USEINFLUX:
INFLXDBTOKEN: Final[str] = os.getenv("INFLXDBTOKEN", "123" )
INFLUXBCKT: Final[str] = os.getenv("INFLUXBCKT", "SNMPyth")
INFLUXORG: Final[str] = os.getenv("INFLUXORG", "staging")
INFLXDBURL: Final[str] = os.getenv("INFLXDBURL", "http://localhost:8086")
INFXLUXDB_MEASUEREMENT: Final[str] = os.getenv("INFXLUXDB_MEASUEREMENT", "SNMPyth-containrr")
INFLX_SEPARATE_POINTS: Final[float] = float(os.getenv("INFLX_SEPARATE_POINTS", 0.1))
# SQL ENV-------------------------------------------
USESQL: Final[bool] = os.getenv("USESQL", False)
if USESQL:
DBENGINE: Final[str] = os.getenv("DBENGINE", "mysql+pymysql")
DBADDR: Final[str] = os.getenv("DBADDR", "127.0.0.1")
DBUSR: Final[str] = os.getenv("DBUSR", "root")
DBPWD: Final[str] = os.getenv("DBPWD", "6767")
DBNAME: Final[str] = os.getenv("DBNAME", "TEMP_SENSR")
# DBprepare-------------------------------------------
# INFLUX
if USEINFLUX:
fluxdb_client = influxdb_client.InfluxDBClient(url=INFLXDBURL, token=INFLXDBTOKEN, org=INFLUXORG)
write_fluxdb_api = fluxdb_client.write_api(write_options=SYNCHRONOUS)
query_fluxdb_api = fluxdb_client.query_api()
# SQL
if USESQL:
engine = create_engine(
f"{DBENGINE}://{DBUSR}:{DBPWD}@{DBADDR}/{DBNAME}",
pool_pre_ping=True, # Check connection liveness before using and if needed, recconect
pool_recycle=3600 # recycle connections older than N (3600 in this case) seconds
)
Session = sessionmaker(bind=engine)
def tempsnsrIntoSQLDB(
tableInp: Annotated[int, "Name of the table"],
insertData: Annotated[str, "Data to insert"]
) -> int:
f"""
Insert {DBNAME} data into MariaDB
:tableInp: Name of the table
:return: 0 if succesfull, 1 if rolled back on error, 2 if SQL is not enabled 3 if no such table
"""
if not USESQL:
return 2
# Check if table exists
if not sqlalchemy.inspect(engine).has_table(tableInp):
return 3
with Session() as session:
try:
data = sensorsqldata(tableInp)
# print("2")
if inpayload:
# print("3")
session.add(data(
data = insertData,
))
# print("4")
session.commit() #Attempt to commit all the records
# print("good")
return 0
except Exception as e:
print(f"Error {e} when sending to mariadb")
session.rollback() #Rollback the changes on error
return 1
def tempsnsrIntoFluxQLDB(
inpayload: Annotated[SensorPayload, "Payload"]
) -> int:
"""
Insert temperature data into InfluxDB
:inpayload: Payload
:return: 0 if succesfull, 1 if general error, 2 if Influx is not enabled
"""
if not USEINFLUX:
return 2
# Prep InfluxDB data
inflxdb_Data_To_Send = (
influxdb_client.Point(f"{INFXLUXDB_MEASUEREMENT}")
.tag("PLACE", inpayload.name)
.tag("TEMP", sensorIDinp)
.tag("TEMP", whatTheSensor)
.field("TEMP", inpayload.temp)
.field("TEMP", inpayload.humid)
.field("TEMP", inpayload.hicc)
.field("TEMP", inpayload.presss)
.field("TEMP", inpayload.alttd)
)
# print(write_fluxdb_api.write(bucket=INFLUXBCKT, org=INFLUXORG, record=inflxdb_Data_To_Send))
# if write_fluxdb_api.write(bucket=INFLUXBCKT, org=INFLUXORG, record=inflxdb_Data_To_Send) == None:
if write_fluxdb_api.write(bucket=INFLUXBCKT, org=INFLUXORG, record=inflxdb_Data_To_Send):
# return {"STATUS": "succesfully inserted to InfluxDB"}
print("0")
return 0
else:
print("1")
return 1

68
cont/code/main.py Normal file
View File

@@ -0,0 +1,68 @@
# import pysnmp, sqlalchemy, ipaddress, os, influxdb_client, re, time
# # 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
from easysnmp import Session
# QoL
from typing import Annotated
# Program ENV---------------------------------------
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...")
# 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)
# Check if some neccesary ENVs are passed
if not USEINFLUX and not USESQL:
raise Exception("No database selected to store the data")
# if HOST_LIST empty, raise Exception No hosts passed
if not HOST_LIST:
raise Exception("No hosts passed\nExiting...")
# for host in HOST_LIST check if valid IP and create a list with strings
HOST_LIST_STR = []
for host in HOST_LIST:
try:
if not check_host_string(host):
ip = str(ipaddress.IPv4Address(host))
HOST_LIST_STR.append(ip)
except ValueError:
raise Exception("Malformed IP address.\nExiting...")
# Checking if DNS IP is a valid one
# Done under "Program ENV" on line ~18
# Code-------------------------------------------
# Create connections for MySQL and InfluxDB
# functions for inserting data into two DBs
# DNS lookup if the user passed a hostname
# if not given a specific IP for DNS server, fallback to 1.1.1.1
# maybe a class?
# store last value and check if it wasn't sent already to the database

40
cont/conf/Dockerfile Normal file
View File

@@ -0,0 +1,40 @@
FROM alpine:latest
# https://docs.docker.com/reference/dockerfile/#environment-replacement
# ENV PUID=1600
# ENV USER=itsme
# VOLUME [""]
RUN apk update && \
apk add python3 py3-pip su-exec curl && \
mkdir -p /app/snmpython/
WORKDIR /app/snmpython/
COPY ../code/someMain.py /app/snmpython/
# COPY ./entrypoint.sh /entrypoint.sh
# RUN chmod +x /entrypoint.sh
# Add user to run under
# RUN adduser --disabled-password -u "${PUID}" "${USER}"
# RUN chown -R "${USER}" /app/snmpython
COPY ./requirements.txt /app/snmpython/
RUN python3 -m venv venv && \
venv/bin/python3 -m pip install --upgrade pip && \
venv/bin/pip3 install -r requirements.txt
# venv/bin/pip3 install -r requirements.txt
# Set user. No need for root past this point
# USER "${USER}"
# ENTRYPOINT ["/entrypoint.sh"]
# HEALTHCHECK --interval=3s --timeout=3s --retries=3 \
# CMD curl --fail http://127.0.0.1:8181/health || exit 1
CMD ["/app/snmpython/someMain.py"]

3
cont/conf/build.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
docker buildx build --platform linux/amd64 --tag tea.shupogaki.org/yuruc3/snmpython:v0 --debug --push .

View File

@@ -0,0 +1,16 @@
---
services:
mdfanchanger:
container_name: snmpython-dev
user: 1600:1600
image: shupotea/yuruc3/snmpython:dev
build:
dockerfile: ./Dockerfile
environment:
- INFLUXTOKEN: "12345678"
- INFLUXHOST: ""
- HOST_LIST: ["192.168.0.1", "192.168.0.1", "192.168.0.1", "192.168.0.1"]
restart: unless-stopped

25
cont/conf/entrypoint.sh Normal file
View File

@@ -0,0 +1,25 @@
#!/bin/sh
set -e
# Default to 1600 if not provided
PUID="${PUID:-1000}"
PGID="${PGID:-1000}"
USER="${USER:-pythusr}"
GROUP="${USER:-pythusr}"
CHOWNPATH="/app/snmpython"
# Create group if missing
if ! getent group "$GROUP" >/dev/null 2>&1; then
addgroup -g "$PGID" "$GROUP"
fi
# Create user if missing
if ! id -u "$USER" >/dev/null 2>&1; then
adduser -D -u "$PUID" -G "$GROUP" "$USER"
fi
# Fix permissions (only do this on /app/API)
chown -R "$PUID:$PGID" "$CHOWNPATH"
# Drop privileges & run command
exec su-exec "$PUID:$PGID" "$@"

View File

@@ -0,0 +1,12 @@
# mysql-connector-python==9.2.0
PyMySQL==1.1.1
sqlmodel==0.0.24
pydantic==2.10.6
pydantic_core==2.27.2
annotated-types==0.7.0
typing==3.7.4.3
pysnmp==7.1.22
# requests==2.32.3
influxdb-client==1.49.0
# influxdb3-python==0.18.0