diff --git a/srv/db-setup-mysql.sql b/srv/db-setup-mysql.sql index 3b9ef03..a802a79 100644 --- a/srv/db-setup-mysql.sql +++ b/srv/db-setup-mysql.sql @@ -47,5 +47,5 @@ create table ssvp_events ( eventDescription text, startTime datetime not null, endTime datetime, - severity integer + severity integer not null ); \ No newline at end of file diff --git a/srv/db-setup-sqlite3.sql b/srv/db-setup-sqlite3.sql new file mode 100644 index 0000000..fd8a044 --- /dev/null +++ b/srv/db-setup-sqlite3.sql @@ -0,0 +1,26 @@ +-- SPDX-License-Identifier: AGPL-3.0-or-later +-- +-- ssvp: server statistics viewer project +-- Copyright (C) 2023 Amy Parker +-- +-- This program is free software; you can redistribute it and/or modify it +-- under the terms of the GNU Affero General Public License as published +-- by the Free Software Foundation; either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, but +-- WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +-- See the GNU Affero General Public License for more details. +-- +-- You should have received a copy of the GNU Affero General Public License +-- along with this program; if not, write to the Free Software Foundation, Inc., +-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA or visit the +-- GNU Project at https:--gnu.org/licenses. The GNU Affero General Public +-- License version 3 is available at, for your convenience, +-- https:--www.gnu.org/licenses/agpl-3.0.en.html. + +create table ssvp_day_logs ( logDate date not null, serverName text not null, serverStatus integer not null ); +create table ssvp_interval_logs ( logDate datetime not null, serverName text not null, serverStatus boolean not null ); +create table ssvp_cached_stats ( monthlyUptime double not null, yearlyUptime double not null, allTimeUptime double not null, serverName text not null, currentStatus integer not null ); +create table ssvp_events ( eventID integer not null, serverName text not null, eventName text not null, eventDescription text, startTime datetime not null, endTime datetime, severity integer not null ); \ No newline at end of file diff --git a/srv/dbhandle.py b/srv/dbhandle.py index de2af5e..6a124d8 100644 --- a/srv/dbhandle.py +++ b/srv/dbhandle.py @@ -60,17 +60,39 @@ def update_cached_stats(self, srv: str, st: int) -> None: unimplemented() +# TODO: create a database of pre-treated SQL on initialization class DBAPIAbstracted(DBAbstract): def __init__(self, config: dict, prefix: str) -> None: super().__init__(config=config) self.p = prefix - def _get_ups(self, srv: str, conn) -> dict: + def _generate_connection(self): + unimplemented() + + def _treat_sql(self, sql: str) -> str: + return "" + + def _fetch_daily_data(self, srv: str) -> dict: + return self._fdd(srv=srv, conn=self._generate_connection()) + + def handle_daily_record(self, srv: str, st: int) -> int: + return self._hdr(srv=srv, st=st, conn=self._generate_connection()) + + def insert_interval_log(self, srv: str, status: int) -> None: + self._iil(srv=srv, status=status, conn=self._generate_connection()) + + def update_cached_stats(self, srv: str, st: int) -> None: + self._ucs(srv=srv, st=st, conn=self._generate_connection()) + + def get_uptime_stats(self, srv: str) -> dict: + return self._ups(srv=srv, conn=self._generate_connection()) + + def _ups(self, srv: str, conn) -> dict: curr = conn.cursor() sql = f"select monthlyUptime, yearlyUptime, allTimeUptime, currentStatus \ from {self.p}cached_stats \ where serverName = %s;" - curr.execute(sql, (srv,)) + curr.execute(self._treat_sql(sql), (srv,)) row = curr.fetchone() if row is None: curr.close() @@ -89,7 +111,7 @@ def _fdd(self, srv: str, conn) -> dict: sql = f"select logDate, serverStatus \ from {self.p}day_logs \ where logDate between (current_date() - interval 3 month) and current_date() and serverName = %s;" - curr.execute(sql, (srv,)) + curr.execute(self._treat_sql(sql), (srv,)) res = {} for row in curr.fetchall(): res[row[0]] = row[1] @@ -106,7 +128,7 @@ def _hdr(self, srv: str, st: int, conn) -> int: where serverName = %s \ and logDate = %s \ );" - curr.execute(sql, (srv, day)) + curr.execute(self._treat_sql(sql), (srv, day)) rc_exists = curr.fetchone()[0] sql = f"select max( \ select severity \ @@ -116,7 +138,7 @@ def _hdr(self, srv: str, st: int, conn) -> int: );" mx = None try: - curr.execute(sql, (srv,)) + curr.execute(self._treat_sql(sql), (srv,)) mx = max(st, curr.fetchone()[0]) # we can ignore the pep violation except: @@ -125,13 +147,13 @@ def _hdr(self, srv: str, st: int, conn) -> int: sql = f"insert into {self.p}day_logs \ (logDate, serverName, serverStatus) values \ (%s, %s, %s);" - curr.execute(sql, (day, srv, mx)) + curr.execute(self._treat_sql(sql), (day, srv, mx)) else: sql = f"update {self.p}day_logs \ set serverStatus = %s \ where logDate = %s \ and serverName = %s;" - curr.execute(sql, (mx, day, srv)) + curr.execute(self._treat_sql(sql), (mx, day, srv)) curr.fetchall() conn.commit() curr.close() @@ -143,7 +165,7 @@ def _iil(self, srv: str, status: int, conn) -> None: sql = f"insert into {self.p}interval_logs \ (logDate, serverName, serverStatus) values \ (%s, %s, %s);" - curr.execute(sql, (datetime.datetime.now(), srv, status)) + curr.execute(self._treat_sql(sql), (datetime.datetime.now(), srv, status)) curr.fetchall() conn.commit() curr.close() @@ -170,7 +192,7 @@ def _ucs(self, srv: str, st: int, conn) -> None: where serverName = %s), \ currentStatus = %s\ where serverName = %s;" - curr.execute(sql, (srv, srv, srv, st, srv)) + curr.execute(self._treat_sql(sql), (srv, srv, srv, st, srv)) curr.fetchall() conn.commit() curr.close() diff --git a/srv/getdb.py b/srv/getdb.py index b08cd52..20d347a 100644 --- a/srv/getdb.py +++ b/srv/getdb.py @@ -21,9 +21,11 @@ # https://www.gnu.org/licenses/agpl-3.0.en.html. import mysqlh +import sqliteh dblookup = { - "mysql": mysqlh.MySQLHandler + "mysql": mysqlh.MySQLHandler, + "sqlite3": sqliteh.SQLite3Handler } diff --git a/srv/mysqlh.py b/srv/mysqlh.py index d60b52f..8386928 100644 --- a/srv/mysqlh.py +++ b/srv/mysqlh.py @@ -32,24 +32,9 @@ def __init__(self, config: dict) -> None: self.login = config["username"] self.pw = config["password"] self.db = config["database"] - - def get_uptime_stats(self, srv: str) -> dict: - return self._get_ups(srv=srv, conn=self._generate_connection()) def _generate_connection(self): return mysql.connector.connect(user=self.login, password=self.pw, host=self.host, database=self.db) - - def _fetch_daily_data(self, srv: str) -> dict: - return self._fdd(srv=srv, conn=self._generate_connection()) - - def handle_daily_record(self, srv: str, st: int) -> int: - return self._hdr(srv=srv, st=st, conn=self._generate_connection()) - - def insert_interval_log(self, srv: str, status: int) -> None: - self._iil(srv=srv, status=status, conn=self._generate_connection()) - - def update_cached_stats(self, srv: str, st: int) -> None: - self._ucs(srv=srv, st=st, conn=self._generate_connection()) diff --git a/srv/sqliteh.py b/srv/sqliteh.py index 94271fd..368882f 100644 --- a/srv/sqliteh.py +++ b/srv/sqliteh.py @@ -22,15 +22,58 @@ import dbhandle import sqlite3 - import datetime -class SQLite3Handler(dbhandle.DBAbstract): +class SQLite3Handler(dbhandle.DBAPIAbstracted): def __init__(self, config: dict) -> None: - super().__init__(config=config) + super().__init__(config=config, prefix=config["prefix"]) self.filename = config["host"] - self.db = config["database"] - self.p = config["prefix"] - \ No newline at end of file + def _generate_connection(self): + # TODO: have filename search the current directory, not the cwd + return sqlite3.connect(self.filename) + + def _treat_sql(self, sql: str): + return sql.replace("%s", "?") + + def _fetch_daily_data(self, srv: str) -> dict: + conn = self._generate_connection() + curr = conn.cursor() + sql = f"select logDate, serverStatus \ + from {self.p}day_logs \ + where logDate > date('now', '-3 months')\ + and serverName = ?;" + curr.execute(sql, (srv,)) + res = {} + for row in curr.fetchall(): + res[datetime.datetime.strptime(row[0], "%Y-%m-%d").date()] = row[1] + curr.close() + conn.close() + return res + + def update_cached_stats(self, srv: str, st: int) -> None: + conn = self._generate_connection() + curr = conn.cursor() + sql = f"update {self.p}cached_stats \ + set monthlyUptime = \ + (select AVG(NOT serverStatus) \ + from {self.p}interval_logs \ + where logDate > date('now', '-1 month') \ + and serverName = %s), \ + yearlyUptime = \ + (select AVG(NOT serverStatus) \ + from {self.p}interval_logs \ + where logDate > date('now', '-1 year') \ + and serverName = %s), \ + allTimeUptime = \ + (select AVG(NOT serverStatus) \ + from {self.p}interval_logs \ + where serverName = %s), \ + currentStatus = %s\ + where serverName = %s;" + curr.execute(self._treat_sql(sql), (srv, srv, srv, st, srv)) + curr.fetchall() + conn.commit() + curr.close() + conn.close()