From c178dbd9b25dedd742af483ac2727728fc01491a Mon Sep 17 00:00:00 2001
From: KyMAN <0kyman0@gmail.com>
Date: Mon, 2 Mar 2026 06:59:40 +0100
Subject: [PATCH] #wip: Bash done. Python base done. Building SQL Server base.
---
.gitignore | 6 +
Bash/NucelarMonitor.debian.execute.sh | 2 +
Bash/NucelarMonitor.debian.script.sh | 160 ++++++
Public/index.html | 1 +
Python/NucelarMonitor.py | 738 ++++++++++++++++++++++++++
Python/run.py | 6 +
README.md | 47 +-
SQLServer/NucelarMonitor.server.sql | 609 +++++++++++++++++++++
version | 1 +
9 files changed, 1569 insertions(+), 1 deletion(-)
create mode 100644 .gitignore
create mode 100755 Bash/NucelarMonitor.debian.execute.sh
create mode 100755 Bash/NucelarMonitor.debian.script.sh
create mode 100644 Public/index.html
create mode 100644 Python/NucelarMonitor.py
create mode 100644 Python/run.py
create mode 100644 SQLServer/NucelarMonitor.server.sql
create mode 100644 version
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4441cf3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+/Data
+/Public/data
+__pycache__
+*.[Ss]ecrets.*
+*.[Ss]ecret.*
+/Python/pyodbc.py
\ No newline at end of file
diff --git a/Bash/NucelarMonitor.debian.execute.sh b/Bash/NucelarMonitor.debian.execute.sh
new file mode 100755
index 0000000..8d0bb2b
--- /dev/null
+++ b/Bash/NucelarMonitor.debian.execute.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+nohup ./tu_script.sh > /dev/null 2>&1 &
\ No newline at end of file
diff --git a/Bash/NucelarMonitor.debian.script.sh b/Bash/NucelarMonitor.debian.script.sh
new file mode 100755
index 0000000..0197c2d
--- /dev/null
+++ b/Bash/NucelarMonitor.debian.script.sh
@@ -0,0 +1,160 @@
+#!/bin/bash
+
+# Settings.
+key="kyman_9750h"
+candle_seconds=10
+candle_sleep_seconds=1
+execute_sleep_seconds=0
+show_json=true
+send_json=true
+url_server="http://192.168.1.131:13000/debian"
+# Settings.
+
+function get_net_data(){
+ echo $(cat /proc/net/dev|tail -n +3|awk '{
+
+ interface = $1;
+
+ gsub(/:/, "", interface);
+ array = "[\"" interface "\"," $2 "," $3 "," $4 "," $10 "," $11 "," $12 "]";
+
+ json = json == "" ? array : json "," array;
+
+ }END{
+ print "[" json "]";
+ }')
+}
+
+function execute(){
+
+ local domain=$(hostname -d)
+ local ips=$(ip -o addr|awk '{
+
+ is_ipv6 = $3 == "inet6" ? "true" : "false";
+
+ split($1, i, ":");
+ split($4, ip, "/");
+
+ array = "[" i[1] ",\"" $2 "\"," is_ipv6 ",\"" ip[1] "\"," ip[2] "]";
+ json = json == "" ? array : json "," array;
+
+ }END{
+ print "[" json "]";
+ }')
+ local hostnames=$(hostname -A|awk '{
+ json = json == "" ? "\"" $1 "\"" : json ",\"" $1 "\"";
+ }END{
+ print "[" json "]";
+ }')
+ local disks=$(lsblk -b -n -o name,size,fsavail,mountpoint|grep -E '└─|├─'|awk '{
+
+ device = $1;
+ total = $2;
+ available = $3 ~ /^\// || $3 == "" ? 0 : $3;
+ mountpoint = $4 != "" ? "\"" $4 "\"" : $3 ~ /^\// ? "null" : "\"" $3 "\"";
+
+ gsub(/├─|└─/, "", device);
+ array = "[\"" device "\"," total "," available "," mountpoint "]";
+
+ json = json == "" ? array : json "," array;
+
+ }END{
+ print "[" json "]";
+ }')
+ local iterations=0
+ local cpu_in=
+ local cpu_out=
+ local cpu_minimum=
+ local cpu_maximum=
+ local cpu_average=0
+ local memory_in=
+ local memory_out=
+ local memory_minimum=
+ local memory_maximum=
+ local memory_average=0
+ local memory_total=$(cat /proc/meminfo|grep MemTotal:|awk '{print $2 * 1024}')
+ local limit_seconds=$(($(date +%s) + $candle_seconds))
+ local json="["
+ local net_data="["
+ local candle_start=$(date +%s)
+ local candle_end=
+
+ net_data=$net_data$(get_net_data)
+
+ while true; do
+
+ cpu=$(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}')
+ # memory=$(cat /proc/meminfo|grep MemFree:|awk '{print $2 * 1024}')
+ memory=$(cat /proc/meminfo|grep MemAvailable:|awk '{print $2 * 1024}')
+
+ # cpu_average=$((cpu_average + cpu))
+ # memory_average=$((memory_average + memory))
+ cpu_average=$(echo "$cpu_average + $cpu"|bc -l)
+ memory_average=$(echo "$memory_average + $memory"|bc -l)
+
+ cpu=${cpu/,/.}
+
+ cpu_out=$cpu
+ memory_out=$memory
+ iterations=$((iterations + 1))
+
+ if [ -z "$cpu_in" ]; then
+ cpu_in=$cpu
+ memory_in=$memory
+ cpu_minimum=$cpu
+ cpu_maximum=$cpu
+ memory_minimum=$memory
+ memory_maximum=$memory
+ else
+
+ if [ "$(echo "$cpu < $cpu_minimum"|bc -l)" -eq 1 ];then cpu_minimum=$cpu;fi
+ if [ "$(echo "$cpu > $cpu_maximum"|bc -l)" -eq 1 ];then cpu_maximum=$cpu;fi
+ if [ "$(echo "$memory < $memory_minimum"|bc -l)" -eq 1 ];then memory_minimum=$memory;fi
+ if [ "$(echo "$memory > $memory_maximum"|bc -l)" -eq 1 ];then memory_maximum=$memory;fi
+ # cpu_minimum=$(echo -e "$cpu_minimum\n$cpu" | sort -n | head -1)
+ # cpu_maximum=$(echo -e "$cpu_maximum\n$cpu" | sort -n | tail -1)
+ # memory_minimum=$(echo -e "$memory_minimum\n$memory" | sort -n | head -1)
+ # memory_maximum=$(echo -e "$memory_maximum\n$memory" | sort -n | tail -1)
+
+ if [ $(date +%s) -ge $limit_seconds ]; then
+ break
+ fi
+
+ sleep 1
+ fi
+ done
+
+ if [ -z "$domain" ]; then
+ domain="null"
+ else
+ domain="\"$domain\""
+ fi
+
+ # cpu_average=$((cpu_average / iterations))
+ # memory_average=$((memory_average / iterations))
+ cpu_average=$(echo "scale=6; $cpu_average / $iterations"|bc|awk '{printf $1 + 0}')
+ memory_average=$(echo "scale=6; $memory_average / $iterations"|bc|awk '{printf $1 + 0}')
+
+ net_data="$net_data,$(get_net_data)]"
+ candle_end=$(date +%s)
+
+ json="$json$hostnames,$domain,$ips,$disks,$iterations"
+ json="$json,[$candle_start,$candle_end]"
+ json="$json,[${cpu_in//,/.},${cpu_out//,/.},${cpu_minimum//,/.},${cpu_maximum//,/.},$cpu_average]"
+ json="$json,[$memory_total,$memory_in,$memory_out,$memory_minimum,$memory_maximum,$memory_average]"
+ json="$json,$net_data"
+ json="$json]"
+
+ if [ "$show_json" = true ]; then echo "$json";fi
+ if [ "$send_json" = true ]; then
+ local response=$(echo "$json"|curl -s -X POST -H "Content-Type: application/json" -d @- "$url_server/$key")
+ fi
+
+}
+
+while true; do
+ execute
+ if [ $execute_sleep_seconds -gt 0 ]; then
+ sleep $execute_sleep_seconds
+ fi
+done
\ No newline at end of file
diff --git a/Public/index.html b/Public/index.html
new file mode 100644
index 0000000..dc09cc0
--- /dev/null
+++ b/Public/index.html
@@ -0,0 +1 @@
+
Funca
\ No newline at end of file
diff --git a/Python/NucelarMonitor.py b/Python/NucelarMonitor.py
new file mode 100644
index 0000000..0673506
--- /dev/null
+++ b/Python/NucelarMonitor.py
@@ -0,0 +1,738 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+import datetime
+from typing import Any, Optional, Sequence, Self, Callable
+from re import compile as re_compile, Pattern as REPattern, Match as REMatch, IGNORECASE as RE_IGNORE_CASE
+from pyodbc import connect as pyodbc_connect
+from socket import socket as Socket, AF_INET as ADDRESS_FAMILY_IPV4, SOCK_STREAM as SOCKET_STREAM, SOL_SOCKET as SOCKET_LAYER, SO_REUSEADDR as SOCKET_REUSE_ADDRESS
+from inspect import stack as get_stack, FrameInfo
+from traceback import format_stack as trace_format_stack, extract_tb as extract_traceback
+from threading import Thread
+from os.path import exists as path_exists, dirname as directory_name, abspath as absolute_path
+from json import loads as json_decode, dumps as json_encode
+from mimetypes import guess_type as get_mime_by_extension
+
+class NucelarMonitor:
+
+ DEFAULT_SETTINGS:dict[str, Any|None] = {
+ "autostart" : True,
+ "print_format" : "[{type}] {yyyy}{mm}{dd} {hh}{ii}{ss} [{line}]{file}({method}): {message}",
+ "exception_format" : " '[{line}]{file}({method})'{lines}\n\n{exception_message}",
+ "print_types" : [
+ ["unkn", "unknown"],
+ ["info", "information"],
+ ["warn", "warning"],
+ ["erro", "error", "wrong", "failure", "fail", "no"],
+ ["exce", "exception", "except"],
+ [" ok ", "ok", "success", "succeed", "yes"],
+ ["test", "debug"]
+ ],
+ "http_host" : "0.0.0.0",
+ "http_port" : 13000,
+ "http_cache_size" : 1024,
+ "http_maximum_connections" : 5,
+ "http_header_response" : (
+ "{http_protocol}/{http_version} {http_code} {http_message}\r\n" +
+ "Content-Type: {mime}\r\n" +
+ "Content-Length: {length}\r\n" +
+ "\r\n"
+ ),
+ "http_protocol" : "HTTP",
+ "http_version" : "1.1",
+ "http_code" : 200,
+ "http_message" : "OK",
+ "http_encoder" : "utf-8",
+ "index_files" : ("index.html", "index.htm"),
+ "sql_host" : "127.0.0.1",
+ "sql_port" : 1433,
+ "sql_user" : "sa",
+ "sql_password" : "password",
+ "sql_database" : "NucelarMonitor",
+ "default_controllers" : {
+ "get" : {
+ "/" : "/Public"
+ },
+ "post" : {
+ "/debian/{key}" : "debian"
+ }
+ },
+ "default_connections" : {}
+ }
+ DEFAULT_I18N:dict[str, dict[str, str|Sequence[str]]] = {
+ "english" : {}
+ }
+ ROOT:str = directory_name(absolute_path(__file__))
+ SLASH:str = "/" if "/" in ROOT else "\\\\"
+ SPECIAL_REGULAR_EXPRESSION_CHARACTERS:dict[str, str] = {
+ "\r" : "r",
+ "\n" : "n",
+ "\t" : "t"
+ }
+
+ RE_KEY:REPattern = re_compile(r'^[a-z_][a-z0-9_]*$', RE_IGNORE_CASE)
+ RE_STRING_VARIABLE:REPattern = re_compile(r'\{([a-z_][a-z0-9_]*)\}', RE_IGNORE_CASE)
+ RE_EXCEPTION:REPattern = re_compile(r'^\s*File "([^"]+)", line ([0-9]+), in ([^\n]+)(.*|[\r\n]*)*$')
+ RE_NEW_LINE:REPattern = re_compile(r'\r\n|[\r\n]')
+ RE_TO_SNAKE:REPattern = re_compile(r'[^a-zA-Z0-9]*([A-Z][A-Z0-9]*)|[^a-z0-9]+')
+ RE_HTTP_REQUEST:REPattern = re_compile(r'^([^\s]+)\s([^\s\?\#]+)(?:\?([^#]+))?(?:\#[^\s]+)?\s([^\/]+)\/([0-9\.]+)$')
+ RE_HEADER_LINE:REPattern = re_compile(r'^([^\:]+)\:(.+)$')
+ RE_HTTP_BLOCKS:REPattern = re_compile(r'((?:(?!(?:(?:\r\n){2}|\n{2}|\r{2}))(?:.|[\r\n]+))+)(?:(?:(?:\r\n){2}|\n{2}|\r{2})((?:.+|[\r\n]+)*))?')
+ RE_LAST_DIRECTORY:REPattern = re_compile(r'^(.*)[\/][^\/]*\/?$')
+ RE_SLASHES:REPattern = re_compile(r'[\\\/]+')
+ RE_TO_REGULAR_EXPRESSION:REPattern = re_compile(r'[\(\)\{\}\/\\\.\-\+\*\^\$\?\|\!\<\>\r\n\t]')
+ RE_ROUTE_KEY:REPattern = re_compile(r'\\\{([a-z_][a-z0-9_]*)\\\}', RE_IGNORE_CASE)
+
+ class Request:
+
+ def __init__(self:Self, data:bytes, encoder:str = "utf-8") -> None:
+
+ self.method:str
+ self.request:str
+ self.value_get:str|None
+ self.variables_get:dict[str, str]
+ self.variables_post:dict[str, str]
+ self.protocol:str
+ self.protocol_version:str
+ self.body:str
+ self.variables_uri:dict[str, str] = {}
+
+ header, body = (lambda header, body:(
+ NucelarMonitor.RE_NEW_LINE.split(str(header).strip()), body
+ ))(*NucelarMonitor.RE_HTTP_BLOCKS.match(data.decode(encoder)).groups())
+
+ (
+ self.method,
+ self.request,
+ self.value_get,
+ self.variables_get,
+ self.protocol,
+ self.protocol_version
+ ) = (lambda method, request, variables, protocol, protocol_version:(
+ str(method).lower(),
+ request,
+ variables,
+ self.parse_variables(variables),
+ protocol,
+ protocol_version
+ ))(*NucelarMonitor.RE_HTTP_REQUEST.match(header[0]).groups())
+ self.body = body
+ self.variables_post = self.parse_variables(body)
+
+ def set_uri_variables(self:Self, keys:list[str], matches:REMatch) -> None:
+
+ i:int
+ value:str
+
+ for i, value in enumerate(matches.groups()):
+ self.variables_uri[keys[i]] = value
+
+ def get(self:Self, keys:str|Sequence[str], default:Optional[Any] = None) -> Any|None:
+ return NucelarMonitor.get_value(keys, (
+ self.variables_uri, self.variables_get, self.variables_post
+ ), default)
+
+ @classmethod
+ def parse_variables(cls:type[Self], string:Optional[str]) -> dict[str, str]:
+ if not string:
+ return {}
+
+ variables:dict[str, str] = {}
+ pair:str
+
+ for pair in string.split("&"):
+ if "=" in pair:
+ key, value = pair.split("=", 1)
+ variables[cls.to_snake(key)] = value
+ else:
+ variables[cls.to_snake(pair)] = ""
+
+ return variables
+
+ @staticmethod
+ def to_snake(string:str) -> str:
+
+ def callback(matches:REMatch) -> str:
+
+ upper:str|None = matches.group(1)
+
+ return "_" + upper.lower() if upper else "_"
+
+ return NucelarMonitor.RE_TO_SNAKE.sub(callback, string).lower()
+
+ class Response:
+
+ def __init__(self:Self,
+ nucelar_monitor:type[Self],
+ response:Optional[Any] = None,
+ mime:Optional[str] = None,
+ code:Optional[int] = None,
+ message:Optional[str] = None
+ ) -> None:
+
+ default_code:int
+ default_message:str
+
+ self.nucelar_monitor:NucelarMonitor = nucelar_monitor
+ default_code, default_message = self.nucelar_monitor.get_http_default_code()
+ self.body:bytes = b""
+ self.mime:str
+ self.code:str = code or default_code
+ self.message:str = default_message if message is None else message
+
+ self.set_data(response, mime)
+
+ def set_data(self:Self, data:Any|None, mime:Optional[str] = None) -> None:
+ if isinstance(data, bytes):
+ self.body = data
+ self.mime = mime or "application/octet-stream"
+ elif isinstance(data, str):
+ self.body = data.encode(self.nucelar_monitor.get_encoder())
+ self.mime = mime or "text/plain;charset=" + self.nucelar_monitor.get_encoder()
+ elif isinstance(data, (dict, tuple, list)):
+ self.body = json_encode(data).encode(self.nucelar_monitor.get_encoder())
+ self.mime = mime or "application/json;charset=" + self.nucelar_monitor.get_encoder()
+ else:
+ self.body = str(data).encode(self.nucelar_monitor.get_encoder())
+ self.mime = mime or "text/plain;charset=" + self.nucelar_monitor.get_encoder()
+
+ def get_parameters(self:Self) -> dict[str, Any|None]:
+ return {
+ "http_code" : self.code,
+ "http_message" : self.message,
+ "mime" : self.mime,
+ "length" : len(self.body)
+ }
+
+ def __init__(self:Self, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
+
+ key:str
+
+ self.__inputs:dict[str, Any|None] = self.get_dictionary(inputs)
+ self.__sentences:dict[str, dict[str, str|Sequence[str]]] = self.DEFAULT_I18N
+ self.__language:str = self.get("language", None, "english")
+ self.__print_format:str = self.get("print_format")
+ self.__print_types:list[list[str]] = self.DEFAULT_SETTINGS["print_types"]
+ self.__exception_format:str = self.get("exception_format")
+ self.__controllers:dict[str, list[tuple[
+ REPattern,
+ tuple[tuple[str, ...]],
+ Callable[[NucelarMonitor.Request, NucelarMonitor.Response], None]|None,
+ str|None]
+ ]] = {}
+ self.__http_host:str = self.get("http_host")
+ self.__http_port:int = self.get("http_port")
+ self.__http_server:Socket
+ self.__http_buffer_size:int = self.get("http_cache_size")
+ self.__http_header_response:str = self.get("http_header_response")
+ self.__http_protocol:str = self.get("http_protocol")
+ self.__http_version:str = self.get("http_version")
+ self.__http_code:int = self.get("http_code")
+ self.__http_message:str = self.get("http_message")
+ self.__http_encoder:str = self.get("http_encoder")
+ self.__index_files:tuple[str, ...] = tuple(self.get("index_files"))
+ self.__started:bool = False
+ self.__working:bool = False
+ self.__root_paths:list[str] = ["", self.ROOT]
+
+ for _ in range(2):
+ self.__root_paths.append(self.RE_LAST_DIRECTORY.sub(r'\1', self.__root_paths[-1]))
+
+ for key in ("default_controllers", "controllers"):
+ self.add_controllers(self.get(key))
+
+ self.get("autostart") and self.start()
+
+ def start(self:Self) -> None:
+ if self.__started:
+ return
+ self.__started = True
+
+ self.__http_server = Socket(ADDRESS_FAMILY_IPV4, SOCKET_STREAM)
+ self.__working = True
+
+ try:
+
+ self.__http_server.setsockopt(SOCKET_LAYER, SOCKET_REUSE_ADDRESS, 1)
+ self.__http_server.bind((self.__http_host, self.__http_port))
+ self.__http_server.listen(self.get("http_maximum_connections"))
+
+ Thread(target = self.__listen).start()
+
+ except Exception as exception:
+ self.exception(exception, "http_server_start_exception", {
+ "host" : self.__http_host,
+ "port" : self.__http_port,
+ })
+ self.close()
+
+ def close(self:Self) -> None:
+ if not self.__started:
+ return
+
+ self.__started = False
+ self.__working = False
+
+ try:
+ self.__http_server.close()
+ except Exception as exception:
+ self.exception(exception, "http_server_close_exception", {
+ "host" : self.__http_host,
+ "port" : self.__http_port,
+ })
+
+ def get_print_type(self:Self, _type:str) -> str:
+
+ group:list[str]
+
+ for group in self.__print_types:
+ if _type in group:
+ return group[0].upper()
+ return self.__print_types[0][0].upper()
+
+ def print(self:Self,
+ _type:str,
+ message:str|Sequence[str],
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None,
+ i:int = 0
+ ) -> None:
+
+ date:datetime = datetime.datetime.now()
+ own:dict[str, Any|None] = {
+ "raw_type" : _type,
+ "type" : self.get_print_type(_type),
+ "i18n" : self.get_texts(message),
+ "message" : self.i18n(message, inputs),
+ **self.get_dictionary(inputs),
+ **self.get_action_data(i + 1)
+ }
+
+ for key in ("year", "month", "day", "hour", "minute", "second"):
+
+ k:str = "i" if key == "minute" else key[0]
+
+ own[k] = own[key] = getattr(date, key)
+ own[k + k] = ("00" + str(own[key]))[-2:]
+
+ own["yyyy"] = own["year"]
+
+ print(self.string_variables(self.__print_format, own) + (own["end"] if "end" in own else ""))
+
+ def exception(self:Self,
+ exception:Exception,
+ message:str|Sequence[str],
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None,
+ i:int = 0
+ ) -> None:
+
+ lines:list[str] = extract_traceback(exception.__traceback__).format()
+ matches:REMatch = self.RE_EXCEPTION.match(lines[-1])
+ data:dict[str, Any|None] = {
+ **self.get_dictionary(inputs),
+ "lines" : "",
+ "exception_message" : str(exception),
+ "method" : matches.group(3),
+ "line" : matches.group(2),
+ "file" : matches.group(1)
+ }
+ block:str
+ j:int
+
+ for j, block in enumerate(trace_format_stack()[:-2] + lines):
+ if block:
+ data["lines"] += "\n " + str(j) + " - " + self.RE_NEW_LINE.split(block.strip())[0]
+
+ data["end"] = self.string_variables(self.__exception_format, data)
+
+ message and self.print("exception", message, data, i + 2)
+
+ @classmethod
+ def fix_path(cls:type[Self], path:str) -> str:
+ return cls.RE_SLASHES.sub(cls.SLASH, path)
+
+ def get_absolute_path(self:Self, path:str) -> str|None:
+
+ root:str
+ absolute:str
+
+ for root in self.__root_paths:
+ absolute = self.fix_path(root + '/' + path)
+ if path_exists(absolute):
+ return absolute
+ return None
+
+ def load_file(self:Self, path:str, mode:str = "r") -> str|bytes|None:
+
+ path:str = self.get_absolute_path(path)
+
+ if path:
+ with open(path, mode) as file:
+ return file.read()
+ return None
+
+ def load_json(self:Self, data:str|dict[str, Any|None]|list[Any|None]) -> dict[str, Any|None]|list[Any|None]|None:
+ if isinstance(data, str):
+
+ json:list[Any|None]|dict[str, Any|None]|None
+
+ try:
+ json = json_decode(data)
+ except Exception as exception:
+ self.exception(exception, "load_json_exception", {
+ "data" : data,
+ "length" : len(data)
+ })
+
+ if json:
+ return json
+ try:
+ return json_decode(self.load_file(data))
+ except Exception as exception:
+ self.exception(exception, "load_json_by_file_exception", {
+ "path" : data
+ })
+ return None
+ elif isinstance(data, (dict, list)):
+ return data
+ return None
+
+ def get_encoder(self:Self) -> str:
+ return self.__http_encoder
+
+ def get_http_default_code(self:Self) -> tuple[int, str]:
+ return self.__http_code, self.__http_message
+
+ def get(self:Self,
+ keys:str|Sequence[str],
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None,
+ default:Optional[Any] = None
+ ) -> Any|None:
+ return self.get_value(keys, (inputs, self.__inputs, self.DEFAULT_SETTINGS), default)
+
+ def __get_text(self:Self, strings:str|Sequence[str]) -> str:
+
+ keys:list[str] = self.get_keys(strings := self.get_list(strings))
+
+ if len(keys):
+
+ language:str
+ used:list[str] = []
+
+ for language in [self.__language] + list(self.__sentences.keys()):
+ if language not in used and language in self.__sentences:
+
+ key:str
+
+ used.append(language)
+ for key in keys:
+ if key in self.__sentences[language]:
+ return self.__sentences[language][key]
+ return strings[0]
+
+ def i18n(self:Self,
+ strings:str|Sequence[str],
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None
+ ) -> str:
+ return self.string_variables(self.__get_text(strings), inputs)
+
+ def add_controllers(self:Self,
+ inputs:str|dict[str, dict[str, str|Callable[[Request], None]]]|Sequence[Any|None]
+ ) -> None:
+ if isinstance(inputs, dict):
+
+ method:str
+ controllers:dict[str, str|Callable[[NucelarMonitor.Request], None]]
+
+ for method, controllers in inputs.items():
+ if (method := method.lower()) not in self.__controllers:
+ self.__controllers[method] = []
+ if isinstance(controllers, dict):
+
+ request:str
+ target:str|Callable[[NucelarMonitor.Request], None]
+
+ for request, target in controllers.items():
+
+ controller:Callable[[NucelarMonitor.Request, NucelarMonitor.Response], None]|None = None
+ path:str|None = None
+
+ if isinstance(target, str) and (controller := getattr(self, target, None)) is None:
+ path = self.get_absolute_path(target)
+
+ if callable(controller) or path is not None:
+
+ variables:list[str] = []
+
+ def callback(matches:REMatch) -> str:
+
+ variables.append(matches.group(1))
+
+ return r'([^\/]+)'
+
+ self.__controllers[method].append((re_compile(r'^' + self.RE_ROUTE_KEY.sub(callback, self.to_regular_expression(
+ request[:-1] if request[-1] == "/" else request
+ )) + (r'' if path is None else r'(.*)') + r'\/?$'), tuple(variables), controller, path))
+
+ elif isinstance(inputs, (list, tuple)):
+
+ subinputs:Any|None
+
+ for subinputs in inputs:
+ self.add_controllers(subinputs)
+
+ elif isinstance(inputs, str):
+ self.add_controllers(self.load_json(inputs))
+
+ def __listen(self:Self) -> None:
+ while self.__working:
+ try:
+
+ client:Socket
+ address:str
+ port:int
+
+ client, (address, port) = self.__http_server.accept()
+
+ Thread(
+ target = self.__listen_client,
+ args = (client, address, port)
+ ).start()
+
+ except Exception as exception:
+ self.exception(exception, "http_server_listen_exception", {
+ "host" : self.__http_host,
+ "port" : self.__http_port,
+ })
+
+ def __listen_client(self:Self, client:Socket, address:str, port:int) -> None:
+
+ data:bytes = b""
+ route:str = ""
+ method:str = "UNKN"
+ response:NucelarMonitor.Response = NucelarMonitor.Response(self)
+
+ try:
+
+ request:NucelarMonitor.Request
+ variables:tuple[str, ...]
+ controller:Callable[[NucelarMonitor.Request], Any|None]|None
+ path:str|None
+ response_data:Any|None = None
+ pattern:REPattern
+ done:bool = False
+
+ while True:
+
+ buffer:bytes = client.recv(self.__http_buffer_size)
+
+ if not buffer:
+ break
+ data += buffer
+ if len(buffer) != self.__http_buffer_size:
+ break
+
+ for pattern, variables, controller, path in self.__controllers[
+ method := (request := self.Request(data, self.__http_encoder)).method
+ ]:
+
+ matches:REMatch = pattern.match(route := request.request)
+
+ if matches is not None:
+ request.set_uri_variables(variables, matches)
+ if done := path is not None:
+ for index in self.__index_files:
+
+ full_path = path + "/" + route + ("" if index == "" else "/" + index)
+
+ if done := (response_data := self.load_file(full_path, "rb")) is not None:
+ response.set_data(
+ response_data,
+ get_mime_by_extension(full_path)[0] or "application/octet-stream"
+ )
+ break
+
+ elif done := controller is not None:
+ controller(request, response)
+
+ if done:
+ break
+
+
+ if not done:
+ response.body = b"Not Found
"
+ response.mime = "text/html;charset=" + self.__http_encoder
+ response.code = "404"
+ response.message = "Not Found"
+
+ client.sendall(self.string_variables(self.__http_header_response, {
+ "http_protocol" : self.__http_protocol,
+ "http_version" : self.__http_version,
+ **response.get_parameters()
+ }).encode(self.__http_encoder) + response.body)
+ client.close()
+
+ except Exception as exception:
+ self.exception(exception, "http_server_client_exception", {
+ "host" : self.__http_host,
+ "port" : self.__http_port,
+ "client_address" : address,
+ "client_port" : port,
+ "length" : len(data),
+ "method" : method,
+ "route" : route,
+ "response_length" : len(response.body)
+ })
+
+ def debian(self:Self, request:Request, response:Response) -> None:
+
+ key:str = request.get("key")
+ hostnames:list[str]
+ domain:str|None
+ interfaces:list[list[int, str, bool, str, int]]
+ disks:list[list[str, int, int, str|None]]
+ iterations:int
+ candle_times:list[int, int]
+ cpu:list[float, float, float, float, float]
+ memory:list[int, int, int, int, int, float]
+ net_use:list[list[list[str, int, int, int, int, int, int]]]
+
+ hostnames, domain, interfaces, disks, iterations, candle_times, cpu, memory, net_use = json_decode(request.body)
+
+ @classmethod
+ def get_dictionary(cls:type[Self], *items:Sequence[Any|None]) -> dict[str, Any|None]:
+
+ dictionary:dict[str, Any|None] = {}
+ item:Any|None
+
+ for item in items:
+ if isinstance(item, dict):
+ dictionary.update(item)
+ elif isinstance(item, (list, tuple)):
+
+ subitem:Any|None
+
+ for subitem in item:
+ dictionary.update(cls.get_dictionary(subitem))
+
+ return dictionary
+
+ @classmethod
+ def get_keys(cls:type[Self], *items:Sequence[Any|None]) -> list[str]:
+
+ keys:list[str] = []
+ item:Any|None
+
+ for item in items:
+ if isinstance(item, str):
+ cls.RE_KEY.match(item) and keys.append(item)
+ elif isinstance(item, (list, tuple)):
+
+ subitem:Any|None
+
+ for subitem in item:
+ keys.extend(cls.get_keys(subitem))
+
+ return keys
+
+ @classmethod
+ def get_dictionaries(cls:type[Self], *items:Sequence[Any|None]) -> list[dict[str, Any|None]]:
+
+ dictionaries:list[dict[str, Any|None]] = []
+ item:Any|None
+
+ for item in items:
+ if isinstance(item, dict):
+ dictionaries.append(item)
+ elif isinstance(item, (list, tuple)):
+
+ subitem:Any|None
+
+ for subitem in item:
+ dictionaries.extend(cls.get_dictionaries(subitem))
+
+ return dictionaries
+
+ @classmethod
+ def get_value(cls:type[Self],
+ keys:str|Sequence[str],
+ inputs:dict[str, Any|None]|Sequence[Any|None],
+ default:Optional[Any] = None
+ ) -> Any|None:
+ if len(cls.get_keys(keys)):
+
+ dictionary:dict[str, Any|None]
+
+ for dictionary in cls.get_dictionaries(inputs):
+
+ key:str
+
+ for key in cls.get_keys(keys):
+ if key in dictionary:
+ return dictionary[key]
+ return default
+
+ @staticmethod
+ def get_list(item:Any|None) -> list[Any|None]:
+ return item if isinstance(item, (list, tuple)) else [item]
+
+ @classmethod
+ def string_variables(cls:type[Self],
+ string:str,
+ inputs:dict[str, Any|None]|Sequence[Any|None],
+ default:Optional[str] = None
+ ) -> str:
+
+ variables:dict[str, Any|None] = cls.get_dictionary(inputs)
+
+ def callback(matches:REMatch) -> str:
+
+ key:str = matches.group(1)
+
+ return (
+ str(variables[key]) if key in variables else
+ default if default is not None else
+ matches.group(0))
+
+ return cls.RE_STRING_VARIABLE.sub(callback, string)
+
+ @staticmethod
+ def get_texts(*items:list[Any|None]) -> list[str]:
+
+ texts:list[str] = []
+ item:Any|None
+
+ for item in items:
+ if isinstance(item, str):
+ texts.append(item)
+ elif isinstance(item, (list, tuple)):
+
+ subitem:Any|None
+
+ for subitem in item:
+ texts.extend(NucelarMonitor.get_texts(subitem))
+
+ return texts
+
+ @staticmethod
+ def get_action_data(i:int = 0) -> dict[str, str|int]:
+
+ stack:FrameInfo = get_stack()[i]
+
+ return {
+ "file" : stack.filename,
+ "method" : stack.function,
+ "line" : stack.lineno
+ }
+
+ @classmethod
+ def to_regular_expression(cls:type[Self], string:str) -> str:
+
+ def callback(matches:REMatch) -> str:
+
+ character:str = matches.group(0)
+
+ return "\\" + (
+ cls.SPECIAL_REGULAR_EXPRESSION_CHARACTERS[character] if character in cls.SPECIAL_REGULAR_EXPRESSION_CHARACTERS else
+ character)
+
+ return cls.RE_TO_REGULAR_EXPRESSION.sub(callback, string)
\ No newline at end of file
diff --git a/Python/run.py b/Python/run.py
new file mode 100644
index 0000000..df7d197
--- /dev/null
+++ b/Python/run.py
@@ -0,0 +1,6 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from NucelarMonitor import NucelarMonitor
+
+nucelar_monitor:NucelarMonitor = NucelarMonitor()
\ No newline at end of file
diff --git a/README.md b/README.md
index b18c6f6..4449d1b 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,48 @@
# NucelarMonitor
-NucelarMonitor is a very single project for monitoring OS and their Hardware and services in Stream via Web from server.
\ No newline at end of file
+NucelarMonitor is a very single project for monitoring OS and their Hardware and services in Stream via Web from server.
+
+# Notes
+
+Datos de envío Debian:
+
+0. `list[str]`: Hostnames.
+1. `str|None`: Domain.
+2. `list[list[int, str, bool, str, int]]`: Interfaces de red. Por cada uno:
+ 0. `int`: i. Identificador de posición.
+ 1. `str`: Nombre.
+ 2. `bool`: ¿Es IPv6?
+ 3. `str`: IP.
+ 4. `int`: Máscara.
+3. `list[list[str, int, int, str|None]]`: Discos. Por cada uno:
+ 0. `str`: Nombre.
+ 1. `int`: Capacidad.
+ 2. `int`: Espacio libre.
+ 3. `str|None`: Punto de montaje.
+4. `int`: Iteraciones o ciclos de conteo de las velas.
+5. `list[int, int]`: Timestamp de inicio y de fin de las velas.
+ 0. `int`: Timestamp de inicio.
+ 1. `int`: Timestamp de fin.
+6. `list[float, float, float, float, float]`: Vela de uso porcentual de la CPU:
+ 0. `float`: Entrada.
+ 1. `float`: Salida.
+ 2. `float`: Mínimo.
+ 3. `float`: Máximo.
+ 4. `float`: Media.
+7. `list[int, int, int, int, int, float]`: Vela de uso de la memoria RAM.
+ 0. `int`: Total de memoria.
+ 1. `int`: Entrada.
+ 2. `int`: Salida.
+ 3. `int`: Mínimo.
+ 4. `int`: Máximo.
+ 5. `float`: Media.
+8. `list[list[list[str, int, int, int, int, int, int]]]`: Uso de Red. Está diseñado en un proceso de diferencia Entrada/Salida. Es una lista de dos listas, entrada y salida, el segundo nivel tiene una lista por cada interfaz de red, las cuales tienen:
+ 0. `str`: Nombre. Identifica la interfaz de red.
+ 1. `int`: Bytes recepcionados actualmente.
+ 2. `int`: Paquetes recepcionados actualmente.
+ 3. `int`: Errores en recepción actualmente.
+ 4. `int`: Bytes enviados actualmente.
+ 5. `int`: Paquetes enviados actualmente.
+ 6. `int`: Errores en envío actualmente.
+
+La idea es procesar el uso de red en Python.
\ No newline at end of file
diff --git a/SQLServer/NucelarMonitor.server.sql b/SQLServer/NucelarMonitor.server.sql
new file mode 100644
index 0000000..f910665
--- /dev/null
+++ b/SQLServer/NucelarMonitor.server.sql
@@ -0,0 +1,609 @@
+if (select top 1 0 from sys.databases where name = 'NucelarMonitor') is null create database NucelarMonitor collate Latin1_General_100_CI_AS_SC_UTF8
+go
+use NucelarMonitor
+
+if object_id(N'dbo.tables_drop', N'P') is not null drop procedure dbo.tables_drop
+go
+create procedure dbo.tables_drop as begin
+
+ set nocount on
+
+ -- Level Plains.
+
+ -- Level 2.
+ if object_id(N'dbo.MachineInterfacesData', N'U') is not null drop table dbo.MachineInterfacesData
+ if object_id(N'dbo.MachineInterfacesTraffic', N'U') is not null drop table dbo.MachineInterfacesTraffic
+ if object_id(N'dbo.MachineDisksSpace', N'U') is not null drop table dbo.MachineDisksSpace
+ if object_id(N'dbo.Exceptions', N'U') is not null drop table dbo.Exceptions
+
+ -- Level 1.
+ if object_id(N'dbo.MachineInterfaces', N'U') is not null drop table dbo.MachineInterfaces
+ if object_id(N'dbo.MachineDisks', N'U') is not null drop table dbo.MachineDisks
+ if object_id(N'dbo.MachineRAM', N'U') is not null drop table dbo.MachineRAM
+ if object_id(N'dbo.MachineCPU', N'U') is not null drop table dbo.MachineCPU
+ if object_id(N'dbo.Procedures', N'U') is not null drop table dbo.Procedures
+
+ -- Level 0.
+ if object_id(N'dbo.CandlesTimes', N'U') is not null drop table dbo.CandlesTimes
+ if object_id(N'dbo.Interfaces', N'U') is not null drop table dbo.Interfaces
+ if object_id(N'dbo.Disks', N'U') is not null drop table dbo.Disks
+ if object_id(N'dbo.Hostnames', N'U') is not null drop table dbo.Hostnames
+ if object_id(N'dbo.Domains', N'U') is not null drop table dbo.Domains
+ if object_id(N'dbo.Machines', N'U') is not null drop table dbo.Machines
+ if object_id(N'dbo.Databases', N'U') is not null drop table dbo.Databases
+ if object_id(N'dbo.Messages', N'U') is not null drop table dbo.Messages
+ if object_id(N'dbo.BigData', N'U') is not null drop table dbo.BigData
+
+end
+go
+
+if object_id(N'dbo.tables_create', N'P') is not null drop procedure dbo.tables_create
+go
+create procedure dbo.tables_create as begin
+
+ set nocount on
+
+ -- Level 0.
+ if object_id(N'dbo.Machines', N'U') is null create table dbo.Machines(
+ id integer not null identity(1, 1),
+ [key] varchar(32) not null,
+ [description] varchar(512),
+ date_in datetime not null constraint machines_df_date_in default getdate(),
+ date_out datetime,
+ constraint machines_pk primary key clustered (id),
+ constraint machines_uk_key unique ([key]),
+ constraint machines_ck_key check ([key] like '[a-zA-Z0-9][a-zA-Z0-9_]{0,31}') with (fillfactor = 90)
+ )
+
+ if object_id(N'dbo.Domains', N'U') is null create table dbo.Domains(
+ id integer not null identity(1, 1),
+ domain varchar(64) not null,
+ [description] varchar(512),
+ date_in datetime not null constraint domains_df_date_in default getdate(),
+ date_out datetime,
+ constraint domains_pk primary key clustered (id),
+ constraint domains_uk_domain unique nonclustered (domain asc) with (fillfactor = 90),
+ constraint domains_ck_domain check ([domain] like '[a-zA-Z0-9_-.]{1,64}') with (fillfactor = 90)
+ )
+
+
+ if object_id(N'dbo.Hostnames', N'U') is null create table dbo.Hostnames(
+ id integer not null identity(1, 1),
+ [name] varchar(32) not null,
+ [description] varchar(512),
+ date_in datetime not null constraint hostnames_df_date_in default getdate(),
+ date_out datetime,
+ constraint hostnames_pk primary key clustered (id),
+ constraint hostnames_uk_name unique nonclustered ([name] asc) with (fillfactor = 90),
+ constraint hostnames_ck_name check ([name] like '[a-zA-Z0-9_-]{1,32}') with (fillfactor = 90)
+ )
+
+ if object_id(N'dbo.Disks', N'U') is null create table dbo.Disks(
+ id integer not null identity(1, 1),
+ [name] varchar(32) not null,
+ [size] bigint not null,
+ mountpoint varchar(128) not null,
+ [description] varchar(512),
+ date_in datetime not null constraint disks_df_date_in default getdate(),
+ date_out datetime,
+ constraint disks_pk primary key clustered (id),
+ constraint disks_uk_name unique nonclustered ([name] asc, mountpoint asc) with (fillfactor = 90),
+ constraint disks_ck_name check (
+ [name] like '[a-zA-Z0-9_-]{1,32}' or
+ mountpoint like '[A-Z]:'
+ ) with (fillfactor = 90),
+ constraint disks_ck_size check (size >= 0 and size < power(2, 53)) with (fillfactor = 90),
+ constraint disks_ck_mountpoint check (
+ mountpoint like '/%' or
+ mountpoint like '[a-zA-Z]:\%' or
+ mountpoint like '\\%'
+ ) with (fillfactor = 90)
+ )
+
+ if object_id(N'dbo.Interfaces', N'U') is null create table dbo.Interfaces(
+ id integer not null identity(1, 1),
+ [name] varchar(32) not null,
+ [description] varchar(512),
+ date_in datetime not null constraint interfaces_df_date_in default getdate(),
+ date_out datetime,
+ constraint interfaces_pk primary key clustered (id),
+ constraint interfaces_uk_machine_name unique nonclustered (machine asc, [name] asc) with (fillfactor = 90),
+ constraint interfaces_ck_name check ([name] like '[a-zA-Z0-9_-]{1,32}') with (fillfactor = 90)
+ )
+
+ if object_id(N'dbo.CandlesTimes', N'U') is null create table dbo.CandlesTimes(
+ id integer not null identity(1, 1),
+ [from] datetime not null,
+ [to] datetime not null,
+ date_in datetime not null constraint candles_times_df_date_in default getdate(),
+ date_out datetime,
+ constraint candles_times_pk primary key clustered (id),
+ constraint candles_times_uk_from_to unique nonclustered ([from] asc, [to] asc) with (fillfactor = 90),
+ constraint candles_times_ck_from_to check ([from] < [to]) with (fillfactor = 90)
+ )
+
+ if object_id(N'dbo.BigData', N'U') is null create table dbo.BigData(
+ id integer not null identity(1, 1),
+ [hash] binary(64) not null,
+ [value] varchar(max) not null,
+ date_in datetime not null constraint big_data_df_date_in default getdate(),
+ date_out datetime,
+ constraint big_data_pk primary key clustered (id),
+ constraint big_data_uk_hash unique nonclustered ([hash] asc) with (fillfactor = 90)
+ )
+
+ if object_id(N'dbo.Messages', N'U') is null create table dbo.Messages(
+ id integer not null identity(1, 1),
+ [key] varchar(128) not null,
+ date_in datetime not null constraint messages_df_date_in default getdate(),
+ date_out datetime,
+ constraint messages_pk primary key clustered (id),
+ constraint messages_ck_key check ([key] like '[a-zA-Z_][a-zA-Z0-9_]{0,127}') with (fillfactor = 90)
+ )
+
+ if object_id(N'dbo.Databases', N'U') is null create table dbo.Databases(
+ id integer not null identity(1, 1),
+ [name] varchar(64) not null,
+ date_in datetime not null constraint databases_df_date_in default getdate(),
+ date_out datetime,
+ constraint databases_pk primary key clustered (id),
+ constraint databases_uk_name unique nonclustered ([name] asc) with (fillfactor = 90),
+ constraint databases_ck_name check ([name] like '[a-zA-Z_][a-zA-Z0-9_]{0,63}') with (fillfactor = 90)
+ )
+
+ -- Level 1.
+ if object_id(N'dbo.MachineCPU', N'U') is null create table dbo.MachineCPU(
+ id integer not null identity(1, 1),
+ machine integer not null,
+ candle_time integer not null,
+ [in] float not null,
+ [out] float not null,
+ minimum float not null,
+ maximum float not null,
+ average float not null,
+ date_in datetime not null constraint machine_cpu_df_date_in default getdate(),
+ date_out datetime,
+ constraint machine_cpu_pk primary key clustered (id),
+ constraint machine_cpu_fk_machine foreign key (machine) references dbo.Machines(id)
+ on update no action
+ on delete no action,
+ constraint machine_cpu_fk_candle_time foreign key (candle_time) references dbo.CandlesTimes(id)
+ on update no action
+ on delete no action,
+ constraint machine_cpu_ck_cpu_in check (
+ [in] between 0 and 100 and
+ [in] between minimum and maximum
+ ) with (fillfactor = 90),
+ constraint machine_cpu_ck_cpu_out check (
+ [out] between 0 and 100 and
+ [out] between minimum and maximum
+ ) with (fillfactor = 90),
+ constraint machine_cpu_ck_cpu_minimum check (
+ minimum between 0 and 100 and
+ minimum <= maximum
+ ) with (fillfactor = 90),
+ constraint machine_cpu_ck_cpu_maximum check (
+ maximum between 0 and 100 and
+ maximum >= minimum
+ ) with (fillfactor = 90),
+ constraint machine_cpu_ck_cpu_average check (
+ average between 0 and 100 and
+ average between minimum and maximum
+ ) with (fillfactor = 90)
+ )
+
+ if object_id(N'dbo.MachineRAM', N'U') is null create table dbo.MachineRAM(
+ id integer not null identity(1, 1),
+ machine integer not null,
+ candle_time integer not null,
+ total bigint not null,
+ [in] bigint not null,
+ [out] bigint not null,
+ minimum bigint not null,
+ maximum bigint not null,
+ average bigint not null,
+ date_in datetime not null constraint machine_ram_df_date_in default getdate(),
+ date_out datetime,
+ constraint machine_ram_pk primary key clustered (id),
+ constraint machine_ram_fk_machine foreign key (machine) references dbo.Machines(id)
+ on update no action
+ on delete no action,
+ constraint machine_ram_fk_candle_time foreign key (candle_time) references dbo.CandlesTimes(id)
+ on update no action
+ on delete no action,
+ constraint machine_ram_ck_memory_total check (
+ total between 0 and power(2, 53) - 1
+ ) with (fillfactor = 90),
+ constraint machine_ram_ck_memory_in check (
+ [in] between 0 and power(2, 53) - 1 and
+ [in] between minimum and maximum and
+ [in] <= total
+ ) with (fillfactor = 90),
+ constraint machine_ram_ck_memory_out check (
+ [out] between 0 and power(2, 53) - 1 and
+ [out] between minimum and maximum and
+ [out] <= total
+ ) with (fillfactor = 90),
+ constraint machine_ram_ck_memory_minimum check (
+ minimum between 0 and power(2, 53) - 1 and
+ minimum <= total and
+ minimum <= maximum
+ ) with (fillfactor = 90),
+ constraint machine_ram_ck_memory_maximum check (
+ maximum between 0 and power(2, 53) - 1 and
+ maximum between total and minimum
+ ) with (fillfactor = 90),
+ constraint machine_ram_ck_memory_average check (
+ average between 0 and power(2, 53) - 1 and
+ average between minimum and maximum and
+ average <= total
+ ) with (fillfactor = 90)
+ )
+
+ if object_id(N'dbo.MachineDisks', N'U') is null create table dbo.MachineDisks(
+ id integer not null identity(1, 1),
+ machine integer not null,
+ [disk] integer not null,
+ belongs bit not null constraint machine_disks_df_belongs default 1,
+ mounted bit not null constraint machine_disks_df_mounted default 1,
+ date_in datetime not null constraint machine_disks_df_date_in default getdate(),
+ date_out datetime,
+ constraint machine_disks_pk primary key clustered (id),
+ constraint machine_disks_fk_machine foreign key (machine) references dbo.Machines(id)
+ on update no action
+ on delete no action,
+ constraint machine_disks_fk_disk foreign key ([disk]) references dbo.Disks(id)
+ on update no action
+ on delete no action
+ )
+
+ if object_id(N'dbo.MachineInterfaces', N'U') is null create table dbo.MachineInterfaces(
+ id integer not null identity(1, 1),
+ machine integer not null,
+ [interface] integer not null,
+ belongs bit not null constraint machine_interfaces_df_belongs default 1,
+ mounted bit not null constraint machine_interfaces_df_mounted default 1,
+ date_in datetime not null constraint machine_interfaces_df_date_in default getdate(),
+ date_out datetime,
+ constraint machine_interfaces_pk primary key clustered (id),
+ constraint machine_interfaces_fk_machine foreign key (machine) references dbo.Machines(id)
+ on update no action
+ on delete no action,
+ constraint machine_interfaces_fk_interface foreign key ([interface]) references dbo.Interfaces(id)
+ on update no action
+ on delete no action
+ )
+
+ if object_id(N'dbo.Procedures', N'U') is null create table dbo.Procedures(
+ id integer not null identity(1, 1),
+ [database] integer not null,
+ [name] varchar(64) not null,
+ date_in datetime not null constraint procedures_df_date_in default getdate(),
+ date_out datetime,
+ constraint procedures_pk primary key clustered (id),
+ constraint procedures_fk_database foreign key ([database]) references dbo.Databases(id)
+ on update no action
+ on delete no action,
+ constraint procedures_uk_name unique nonclustered ([database] asc, [name] asc) with (fillfactor = 90),
+ constraint procedures_ck_name check ([name] like '[a-zA-Z_][a-zA-Z0-9_]{0,63}') with (fillfactor = 90)
+ )
+
+ -- Level 2.
+ if object_id(N'dbo.MachineDisksSpace', N'U') is null create table dbo.MachineDisksSpace(
+ id integer not null identity(1, 1),
+ machine_disk integer not null,
+ candle_time integer not null,
+ free bigint not null,
+ date_in datetime not null constraint machine_disks_space_df_date_in default getdate(),
+ date_out datetime,
+ constraint machine_disks_space_pk primary key clustered (id),
+ constraint machine_disks_space_fk_machine_disk foreign key (machine_disk) references dbo.MachineDisks(id)
+ on update no action
+ on delete no action,
+ constraint machine_disks_space_fk_candle_time foreign key (candle_time) references dbo.CandlesTimes(id)
+ on update no action
+ on delete no action,
+ constraint machine_disks_space_ck_free check (
+ free between 0 and power(2, 53) - 1 and
+ free <= isnull((
+ select top 1 disks.[size]
+ from dbo.Disks disks
+ join dbo.MachineDisks machine_disks on machine_disks.[disk] = disks.id
+ where
+ disks.date_out is null and
+ machine_disks.date_out is null and
+ machine_disks.belongs = 1 and
+ machine_disks.id = machine_disk
+ ), 0)
+ ) with (fillfactor = 90)
+ )
+
+ if object_id(N'dbo.MachineInterfacesTraffic', N'U') is null create table dbo.MachineInterfacesTraffic(
+ id integer not null identity(1, 1),
+ machine_interface integer not null,
+ candle_time integer not null,
+ bytes bigint not null,
+ packages integer not null,
+ errors integer not null,
+ date_in datetime not null constraint machine_interfaces_traffic_df_date_in default getdate(),
+ date_out datetime,
+ constraint machine_interfaces_traffic_pk primary key clustered (id),
+ constraint machine_interfaces_traffic_fk_machine_interface foreign key (machine_interface) references dbo.MachineInterfaces(id)
+ on update no action
+ on delete no action,
+ constraint machine_interfaces_traffic_fk_candle_time foreign key (candle_time) references dbo.CandlesTimes(id)
+ on update no action
+ on delete no action,
+ constraint machine_interfaces_traffic_ck_bytes check (
+ bytes between 0 and power(2, 53) - 1
+ ) with (fillfactor = 90),
+ constraint machine_interfaces_traffic_ck_packages check (
+ packages between 0 and power(2, 31) - 1
+ ) with (fillfactor = 90),
+ constraint machine_interfaces_traffic_ck_errors check (
+ errors between 0 and power(2, 31) - 1
+ ) with (fillfactor = 90)
+ )
+
+ if object_id(N'dbo.MachineInterfacesData', N'U') is null create table dbo.MachineInterfacesData(
+ id integer not null identity(1, 1),
+ machine_interface integer not null,
+ is_ipv6 bit not null constraint machine_interfaces_data_df_is_ipv6 default 0,
+ [address] varchar(45) not null,
+ mask tinyint not null,
+ date_in datetime not null constraint machine_interfaces_data_df_date_in default getdate(),
+ date_out datetime,
+ constraint machine_interfaces_data_pk primary key clustered (id),
+ constraint machine_interfaces_data_fk_machine_interface foreign key (machine_interface) references dbo.MachineInterfaces(id)
+ on update no action
+ on delete no action,
+ constraint machine_interfaces_data_ck_is_ipv6 check (
+ (is_ipv6 = 0 and [address] like '[0-9]%.%.%.%') or
+ (is_ipv6 = 1 and [address] like '%:%')
+ ) with (fillfactor = 90),
+ constraint machine_interfaces_data_ck_address check (
+ ([address] like '[0-9]%.%.%.%' and mask between 0 and 32) or
+ ([address] like '%:%' and mask between 0 and 128)
+ ) with (fillfactor = 90),
+ constraint machine_interfaces_data_ck_mask check (
+ (mask between 0 and (case when is_ipv6 = 1 then 128 else 32 end))
+ ) with (fillfactor = 90)
+ )
+
+ if object_id(N'dbo.Exceptions', N'U') is null create table dbo.Exceptions(
+ id integer not null identity(1, 1),
+ [procedure] integer not null,
+ [message] integer not null,
+ parameters integer,
+ exception integer not null,
+ [status] varchar(16),
+ code integer,
+ date_in datetime not null constraint exceptions_df_date_in default getdate(),
+ date_out datetime,
+ constraint exceptions_pk primary key clustered (id),
+ constraint exceptions_fk_procedure foreign key ([procedure]) references dbo.Procedures(id)
+ on update no action
+ on delete no action,
+ constraint exceptions_fk_message foreign key ([message]) references dbo.Messages(id)
+ on update no action
+ on delete no action,
+ constraint exceptions_fk_parameters foreign key (parameters) references dbo.BigData(id)
+ on update no action
+ on delete no action,
+ constraint exceptions_fk_exception foreign key (exception) references dbo.BigData(id)
+ on update no action
+ on delete no action
+ )
+
+ -- Level Plains.
+ if object_id(N'dbo.MachineInterfacesPlain', N'U') is null create table dbo.MachineInterfacesPlain(
+ id integer not null identity(1, 1),
+ machine integer not null,
+ interface integer not null,
+ machine_interface integer not null,
+ candle_time integer not null,
+ bytes bigint not null,
+ packages integer not null,
+ errors integer not null,
+ is_ipv6 bit not null constraint machine_interfaces_data_df_is_ipv6 default 0,
+ [address] varchar(45) not null,
+ mask tinyint not null,
+ belongs bit not null constraint machine_interfaces_plain_df_belongs default 1,
+ date_in datetime not null constraint machine_interfaces_plain_df_date_in default getdate(),
+ date_out datetime,
+ constraint machine_interfaces_plain_pk primary key clustered (id),
+ constraint machine_interfaces_plain_fk_machine foreign key (machine) references dbo.Machines(id)
+ on update no action
+ on delete no action,
+ constraint machine_interfaces_plain_uk_machine_interface unique nonclustered (machine asc, [interface] asc) with (fillfactor = 90),
+ constraint machine_interfaces_plain_ck_interface check ([interface] like '[a-zA-Z0-9_-]{1,32}') with (fillfactor = 90)
+ )
+
+end
+go
+
+if object_id(N'dbo.tables_update', N'P') is not null drop procedure dbo.tables_update
+go
+create procedure dbo.tables_update as begin
+
+ set nocount on
+
+end
+go
+
+if object_id(N'dbo.tables_fill', N'P') is not null drop procedure dbo.tables_fill
+go
+create procedure dbo.tables_fill as begin
+
+ set nocount on
+
+end
+go
+
+execute dbo.tables_drop
+go
+execute dbo.tables_create
+go
+execute dbo.tables_update
+go
+execute dbo.tables_fill
+go
+
+if object_id(N'dbo.hash_big_data', N'FN') is not null drop function dbo.hash_big_data
+go
+create function dbo.hash_big_data (@value varchar(max)) returns binary(64) as begin
+ return select top 1 convert(binary(64), hashbytes('SHA2_256', convert(nvarchar(max), @value)))
+end
+go
+
+if object_id(N'dbo.get_procedure', N'P') is not null drop procedure dbo.get_procedure
+go
+create procedure dbo.get_procedure
+ @database varchar(64),
+ @procedure varchar(64),
+ @id integer output
+as begin
+
+ declare @database_id integer = (
+ select top 1 id from dbo.Databases where
+ date_out is null and
+ [name] = @database
+ )
+
+ set nocount on
+
+ if @database_id is null begin
+ insert into dbo.Databases([name]) values (@database)
+ set @database_id = scope_identity()
+ end
+
+ set @id = (
+ select top 1 id from dbo.Procedures where
+ date_out is null and
+ [database] = @database_id and
+ [name] = @procedure
+ )
+
+ if @id is null begin
+ insert into dbo.Procedures([database], [name]) values (@database_id, @procedure)
+ set @id = scope_identity()
+ end
+
+end
+go
+
+if object_id(N'dbo.get_big_data', N'P') is not null drop procedure dbo.get_big_data
+go
+create procedure dbo.get_big_data
+ @value varchar(max),
+ @id integer output
+as begin
+
+ declare @hash binary(64) = dbo.hash_big_data(@value)
+
+ set nocount on
+
+ set @id = (
+ select top 1 id from dbo.BigData where
+ date_out is null and
+ [hash] = @hash
+ )
+
+ if @id is null begin
+ insert into dbo.BigData([hash], [value]) values (@hash, @value)
+ set @id = scope_identity()
+ end
+
+end
+go
+
+if object_id(N'dbo.get_message', N'P') is not null drop procedure dbo.get_message
+go
+create procedure dbo.get_message
+ @message varchar(128),
+ @id integer output
+as begin
+
+ set nocount on
+
+ set @id = (
+ select top 1 id from dbo.Messages where
+ date_out is null and
+ [key] = @message
+ )
+
+ if @id is null begin
+ insert into dbo.Messages([key]) values (@message)
+ set @id = scope_identity()
+ end
+
+end
+go
+
+if object_id(N'dbo.set_exception', N'P') is not null drop procedure dbo.set_exception
+go
+create procedure dbo.set_exception
+ @database varchar(64),
+ @procedure varchar(64),
+ @message varchar(128),
+ @parameters varchar(max) = null
+as begin
+
+ declare @procedure_id integer
+ declare @message_id integer
+ declare @exception_message varchar(max) = error_message()
+ declare @exception_message_id integer
+ declare @parameters_id integer
+
+ set nocount on
+
+ execute dbo.get_procedure @database, @procedure, @procedure_id output
+ execute dbo.get_big_data @exception_message, @exception_message_id output
+ execute dbo.get_big_data @parameters, @parameters_id output
+ execute dbo.get_message @message, @message_id output
+
+ insert into dbo.Exceptions([procedure], [message], [parameters], exception, [status], code) values
+ (@procedure_id, @message_id, @parameters_id, @exception_message_id, error_severity(), error_number())
+
+end
+go
+
+if object_id(N'dbo.ProceduresView', N'V') is not null drop view dbo.ProceduresView
+go
+create view dbo.ProceduresView as select
+ procedures.id,
+ databases.[name] as [database],
+ procedures.[name] as [procedure],
+ procedures.date_in,
+ procedures.date_out
+from dbo.Procedures procedures
+join dbo.Databases databases on databases.id = procedures.[database]
+where
+ procedures.date_out is null and
+ databases.date_out is null
+go
+
+if object_id(N'dbo.ExceptionsView', N'V') is not null drop view dbo.ExceptionsView
+go
+create view dbo.ExceptionsView as select
+ exceptions.id,
+ procedures.[database] as [database],
+ procedures.[procedure] as [procedure],
+ messages.[key] as [message],
+ parameters.[value] as [parameters],
+ exception.[value] as [exception],
+ exceptions.[status],
+ exceptions.code,
+ exceptions.date_in
+from dbo.Exceptions exceptions
+join dbo.ProceduresView procedures on procedures.id = exceptions.[procedure]
+join dbo.Messages messages on messages.id = exceptions.[message]
+join dbo.BigData parameters on parameters.id = exceptions.parameters
+join dbo.BigData exception on exception.id = exceptions.exception
+where
+ exceptions.date_out is null and
+ procedures.date_out is null and
+ messages.date_out is null and
+ parameters.date_out is null and
+ exception.date_out is null
+go
\ No newline at end of file
diff --git a/version b/version
new file mode 100644
index 0000000..8acdd82
--- /dev/null
+++ b/version
@@ -0,0 +1 @@
+0.0.1