Big init
This commit is contained in:
BIN
HBX2LV4aMAAstst.png
Normal file
BIN
HBX2LV4aMAAstst.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
5
README
Normal file
5
README
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Hi
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="HBX2LV4aMAAstst.png" alt="">
|
||||||
|
</p>
|
||||||
75
cont/code/classes.py
Normal file
75
cont/code/classes.py
Normal 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
123
cont/code/funct.py
Normal 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
68
cont/code/main.py
Normal 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
40
cont/conf/Dockerfile
Normal 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
3
cont/conf/build.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
docker buildx build --platform linux/amd64 --tag tea.shupogaki.org/yuruc3/snmpython:v0 --debug --push .
|
||||||
16
cont/conf/docker-compose.dev.yaml
Normal file
16
cont/conf/docker-compose.dev.yaml
Normal 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
25
cont/conf/entrypoint.sh
Normal 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" "$@"
|
||||||
12
cont/conf/requirements.txt
Normal file
12
cont/conf/requirements.txt
Normal 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
|
||||||
Reference in New Issue
Block a user