sysview-py 0.3.7__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sysview_py-0.3.7 → sysview_py-0.4.0}/PKG-INFO +1 -1
- {sysview_py-0.3.7 → sysview_py-0.4.0}/pyproject.toml +1 -1
- {sysview_py-0.3.7 → sysview_py-0.4.0}/src/sysview_py/db.py +32 -3
- {sysview_py-0.3.7 → sysview_py-0.4.0}/src/sysview_py/flock.py +1 -1
- {sysview_py-0.3.7 → sysview_py-0.4.0}/src/sysview_py/main.py +1 -1
- {sysview_py-0.3.7 → sysview_py-0.4.0}/src/sysview_py/report.py +22 -15
- {sysview_py-0.3.7 → sysview_py-0.4.0}/src/sysview_py/sysview.py +41 -19
- sysview_py-0.4.0/src/sysview_py/templates/service.html +21 -0
- sysview_py-0.3.7/src/sysview_py/templates/host.html +0 -19
- sysview_py-0.3.7/src/sysview_py/templates/index.html +0 -20
- sysview_py-0.3.7/src/sysview_py/templates/index.html.table +0 -22
- sysview_py-0.3.7/src/sysview_py/templates/service.html +0 -8
- sysview_py-0.3.7/src/sysview_py/templates/services.html +0 -22
- {sysview_py-0.3.7 → sysview_py-0.4.0}/README.md +0 -0
- {sysview_py-0.3.7 → sysview_py-0.4.0}/src/sysview_py/__init__.py +0 -0
- {sysview_py-0.3.7 → sysview_py-0.4.0}/src/sysview_py/static/simple.css +0 -0
- {sysview_py-0.3.7 → sysview_py-0.4.0}/src/sysview_py/static/sysview.css +0 -0
- {sysview_py-0.3.7 → sysview_py-0.4.0}/src/sysview_py/templates/card_view.html +0 -0
- {sysview_py-0.3.7 → sysview_py-0.4.0}/src/sysview_py/templates/footer.html +0 -0
- {sysview_py-0.3.7 → sysview_py-0.4.0}/src/sysview_py/templates/header.html +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import sqlite3
|
|
3
|
+
from .report import ReportItem, Report
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class SysviewDB:
|
|
@@ -8,8 +9,9 @@ class SysviewDB:
|
|
|
8
9
|
CREATE TABLE types(name, UNIQUE(name));
|
|
9
10
|
CREATE TABLE hosts_types(host, type, UNIQUE(host, type));
|
|
10
11
|
CREATE TABLE reports(hostname, type, date, raw_report);
|
|
11
|
-
CREATE TABLE items(command, status, status_text, additional_text, raw_item, report_id);
|
|
12
|
+
CREATE TABLE items(command, status, status_text, additional_text, raw_item, hostname, report_id);
|
|
12
13
|
"""
|
|
14
|
+
|
|
13
15
|
def __init__(self, path):
|
|
14
16
|
self.path = path
|
|
15
17
|
self._connection = None
|
|
@@ -50,7 +52,9 @@ class SysviewDB:
|
|
|
50
52
|
host_types = self.get_types(hostname)
|
|
51
53
|
for host_type in host_types:
|
|
52
54
|
sql = f"SELECT rowid,raw_report from reports WHERE type = '{host_type}' AND hostname= '{hostname}' ORDER BY rowid DESC LIMIT 1"
|
|
53
|
-
latest_report = self._cursor.execute(sql).fetchone()
|
|
55
|
+
report_id, latest_report = self._cursor.execute(sql).fetchone()
|
|
56
|
+
latest_report = Report(raw_report=latest_report, report_id=report_id)
|
|
57
|
+
latest_report.parse()
|
|
54
58
|
reports.append(latest_report)
|
|
55
59
|
return reports
|
|
56
60
|
|
|
@@ -75,14 +79,39 @@ class SysviewDB:
|
|
|
75
79
|
i.status_text,
|
|
76
80
|
i.additional_text,
|
|
77
81
|
i.raw_item,
|
|
82
|
+
report.hostname,
|
|
78
83
|
report_id,
|
|
79
84
|
)
|
|
80
85
|
for i in report.items
|
|
81
86
|
]
|
|
82
|
-
self._cursor.executemany("INSERT INTO items VALUES(?, ?, ?, ?, ?, ?)", items)
|
|
87
|
+
self._cursor.executemany("INSERT INTO items VALUES(?, ?, ?, ?, ?, ?, ?)", items)
|
|
83
88
|
|
|
84
89
|
self._connection.commit()
|
|
85
90
|
|
|
91
|
+
def get_last_changed_item(self, item):
|
|
92
|
+
sql = f"SELECT report_id,raw_item FROM items WHERE command = '{item.command}' AND hostname = '{item.host}' AND NOT status = '{item.status}' ORDER BY rowid DESC LIMIT 1"
|
|
93
|
+
last_changed_item = self._cursor.execute(sql).fetchone()
|
|
94
|
+
if last_changed_item is not None:
|
|
95
|
+
report_id = last_changed_item[0]
|
|
96
|
+
raw_item = last_changed_item[1]
|
|
97
|
+
last_changed_item = ReportItem(raw_item, report_id=report_id)
|
|
98
|
+
last_changed_item.parse()
|
|
99
|
+
sql = f"SELECT date FROM reports WHERE rowid = '{last_changed_item.report_id}'"
|
|
100
|
+
report_date = datetime.datetime.utcfromtimestamp(
|
|
101
|
+
self._cursor.execute(sql).fetchone()[0]
|
|
102
|
+
)
|
|
103
|
+
last_changed_item.date = report_date
|
|
104
|
+
return last_changed_item
|
|
105
|
+
|
|
106
|
+
def get_item_history(self, item, max_len=10):
|
|
107
|
+
history = []
|
|
108
|
+
last_item = item
|
|
109
|
+
while last_item is not None and len(history) < max_len:
|
|
110
|
+
last_item = self.get_last_changed_item(last_item)
|
|
111
|
+
if last_item is not None:
|
|
112
|
+
history.append(last_item)
|
|
113
|
+
return history
|
|
114
|
+
|
|
86
115
|
|
|
87
116
|
def adapt_date_iso(val):
|
|
88
117
|
"""Adapt datetime.date to ISO 8601 date."""
|
|
@@ -2,12 +2,8 @@ import hashlib
|
|
|
2
2
|
from datetime import datetime, timedelta
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
status_map = {
|
|
6
|
-
|
|
7
|
-
"CRITICAL": 2,
|
|
8
|
-
"WARNING": 1,
|
|
9
|
-
"OK": 0
|
|
10
|
-
}
|
|
5
|
+
status_map = {"UNKNOWN": 3, "CRITICAL": 2, "WARNING": 1, "OK": 0}
|
|
6
|
+
|
|
11
7
|
|
|
12
8
|
class Host:
|
|
13
9
|
def __init__(self, name, report_types=tuple(), reports=[]):
|
|
@@ -78,7 +74,7 @@ class ReportItem:
|
|
|
78
74
|
date=None,
|
|
79
75
|
host=None,
|
|
80
76
|
report_type=None,
|
|
81
|
-
report_id=None
|
|
77
|
+
report_id=None,
|
|
82
78
|
):
|
|
83
79
|
self.raw_item = raw_item
|
|
84
80
|
self.command = command
|
|
@@ -101,7 +97,7 @@ class ReportItem:
|
|
|
101
97
|
def name(self):
|
|
102
98
|
result = self.command
|
|
103
99
|
if len(result) > self.max_name_length:
|
|
104
|
-
result = result[:self.max_name_length] + "..."
|
|
100
|
+
result = result[: self.max_name_length] + "..."
|
|
105
101
|
return result
|
|
106
102
|
|
|
107
103
|
@property
|
|
@@ -110,11 +106,12 @@ class ReportItem:
|
|
|
110
106
|
|
|
111
107
|
@property
|
|
112
108
|
def status(self):
|
|
113
|
-
age = datetime.now() - self.date
|
|
114
|
-
threshold = timedelta(days=1)
|
|
115
109
|
status = self._status
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
threshold = timedelta(days=1)
|
|
111
|
+
if self.date is not None:
|
|
112
|
+
age = datetime.now() - self.date
|
|
113
|
+
if age > threshold:
|
|
114
|
+
status = "UNKNOWN"
|
|
118
115
|
return status
|
|
119
116
|
|
|
120
117
|
@status.setter
|
|
@@ -127,7 +124,9 @@ class ReportItem:
|
|
|
127
124
|
|
|
128
125
|
@property
|
|
129
126
|
def id(self):
|
|
130
|
-
return hashlib.sha256(
|
|
127
|
+
return hashlib.sha256(
|
|
128
|
+
(f"{self.report_id}_{self.command}").encode("utf-8")
|
|
129
|
+
).hexdigest()
|
|
131
130
|
|
|
132
131
|
def parse(self):
|
|
133
132
|
parsed_required_fields = False
|
|
@@ -153,7 +152,13 @@ class ReportItem:
|
|
|
153
152
|
|
|
154
153
|
class Report:
|
|
155
154
|
def __init__(
|
|
156
|
-
self,
|
|
155
|
+
self,
|
|
156
|
+
raw_report,
|
|
157
|
+
hostname=None,
|
|
158
|
+
date=None,
|
|
159
|
+
report_type=None,
|
|
160
|
+
items=None,
|
|
161
|
+
report_id=None,
|
|
157
162
|
):
|
|
158
163
|
self.raw_report = raw_report
|
|
159
164
|
self.hostname = hostname
|
|
@@ -199,7 +204,9 @@ class Report:
|
|
|
199
204
|
raw_item += f"{line}\n"
|
|
200
205
|
elif "---" == line:
|
|
201
206
|
if item_pointer > 0:
|
|
202
|
-
item = ReportItem(
|
|
207
|
+
item = ReportItem(
|
|
208
|
+
raw_item, report_id=self.report_id, date=self.date
|
|
209
|
+
)
|
|
203
210
|
item.parse()
|
|
204
211
|
self.items.append(item)
|
|
205
212
|
item_pointer = item_pointer + 1
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import shutil
|
|
2
2
|
import sys
|
|
3
|
+
import datetime
|
|
3
4
|
from jinja2 import Environment, PackageLoader, select_autoescape
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from .db import SysviewDB
|
|
@@ -10,10 +11,12 @@ DEFAULT_HTML_PATH = Path.home() / "sysview"
|
|
|
10
11
|
STATIC_FILES_PATH = Path(__file__).parent / "static"
|
|
11
12
|
|
|
12
13
|
|
|
13
|
-
|
|
14
14
|
class Sysview:
|
|
15
15
|
status_sort_order = ("UNKNOWN", "CRITICAL", "WARNING", "OK")
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self, db=DEFAULT_DB_PATH, hosts=[], reports=[], html_root=DEFAULT_HTML_PATH
|
|
19
|
+
):
|
|
17
20
|
self.db = db
|
|
18
21
|
self.hosts = hosts
|
|
19
22
|
self.reports = reports
|
|
@@ -37,6 +40,9 @@ class Sysview:
|
|
|
37
40
|
for raw_report in raw_reports:
|
|
38
41
|
report = Report(raw_report)
|
|
39
42
|
report.parse()
|
|
43
|
+
report.date = datetime.datetime.now()
|
|
44
|
+
for item in report.items:
|
|
45
|
+
item.date = report.date
|
|
40
46
|
self.db.add(report)
|
|
41
47
|
|
|
42
48
|
def read_hosts(self):
|
|
@@ -50,41 +56,57 @@ class Sysview:
|
|
|
50
56
|
self.html_hosts.mkdir(parents=True)
|
|
51
57
|
shutil.copytree(STATIC_FILES_PATH, self.html_root / STATIC_FILES_PATH.name)
|
|
52
58
|
env = Environment(
|
|
53
|
-
loader=PackageLoader("sysview_py"),
|
|
54
|
-
|
|
59
|
+
loader=PackageLoader("sysview_py"), autoescape=select_autoescape()
|
|
60
|
+
)
|
|
61
|
+
html = env.get_template("card_view.html").render(
|
|
62
|
+
items=self.hosts, title="Hosts", active="Hosts", path="", auto_refresh=True
|
|
55
63
|
)
|
|
56
|
-
html = env.get_template("card_view.html").render(items=self.hosts, title="Hosts", active="Hosts", path="", auto_refresh=True)
|
|
57
64
|
html_path = self.html_root / "index.html"
|
|
58
|
-
with open(html_path,
|
|
65
|
+
with open(html_path, "w") as f:
|
|
59
66
|
f.write(html)
|
|
60
67
|
for host in self.hosts:
|
|
61
|
-
html = env.get_template("card_view.html").render(
|
|
68
|
+
html = env.get_template("card_view.html").render(
|
|
69
|
+
items=host.all_items,
|
|
70
|
+
title=host.name,
|
|
71
|
+
active="Hosts",
|
|
72
|
+
path="../../",
|
|
73
|
+
auto_refresh=True,
|
|
74
|
+
)
|
|
62
75
|
host_dir = self.html_hosts / host.name
|
|
63
76
|
host_dir.mkdir()
|
|
64
77
|
html_path = host_dir / "host.html"
|
|
65
|
-
with open(html_path,
|
|
78
|
+
with open(html_path, "w") as f:
|
|
66
79
|
f.write(html)
|
|
67
80
|
for item in host.all_items:
|
|
68
|
-
|
|
81
|
+
item_history = self.db.get_item_history(item)
|
|
82
|
+
html = env.get_template("service.html").render(
|
|
83
|
+
item=item,
|
|
84
|
+
title=host.name,
|
|
85
|
+
active="Services",
|
|
86
|
+
path="../../",
|
|
87
|
+
auto_refresh=False,
|
|
88
|
+
item_history=item_history,
|
|
89
|
+
)
|
|
69
90
|
html_path = host_dir / f"{item.report_type}_{item.id}.html"
|
|
70
91
|
raw_path = host_dir / f"{item.report_type}_{item.id}.txt"
|
|
71
|
-
with open(html_path,
|
|
92
|
+
with open(html_path, "w") as f:
|
|
72
93
|
f.write(html)
|
|
73
|
-
with open(raw_path,
|
|
94
|
+
with open(raw_path, "w") as f:
|
|
74
95
|
f.write(item.raw_item)
|
|
75
|
-
html = env.get_template("card_view.html").render(
|
|
96
|
+
html = env.get_template("card_view.html").render(
|
|
97
|
+
items=self.all_services,
|
|
98
|
+
title="Services",
|
|
99
|
+
active="Services",
|
|
100
|
+
path="",
|
|
101
|
+
auto_refresh=True,
|
|
102
|
+
)
|
|
76
103
|
html_path = self.html_root / f"services.html"
|
|
77
|
-
with open(html_path,
|
|
104
|
+
with open(html_path, "w") as f:
|
|
78
105
|
f.write(html)
|
|
79
106
|
|
|
80
107
|
def get_latest_reports(self):
|
|
81
108
|
for host in self.hosts:
|
|
82
|
-
host.reports =
|
|
83
|
-
raw_reports = self.db.get_latest_reports(hostname=host.name)
|
|
84
|
-
for report_id, raw_report in raw_reports:
|
|
85
|
-
report = Report(raw_report, report_id=report_id)
|
|
86
|
-
report.parse()
|
|
87
|
-
host.reports.append(report)
|
|
109
|
+
host.reports = self.db.get_latest_reports(hostname=host.name)
|
|
88
110
|
host.reports.sort(key=lambda report: report.status_code, reverse=True)
|
|
89
111
|
self.hosts.sort(key=lambda host: host.status_code, reverse=True)
|
|
90
112
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{% include 'header.html' %}
|
|
2
|
+
<nav>
|
|
3
|
+
<a href="host.html">Show host</a>
|
|
4
|
+
|
|
|
5
|
+
<a href="{{ item.report_type }}_{{ item.id }}.txt">Raw report</a>
|
|
6
|
+
</nav>
|
|
7
|
+
</table>
|
|
8
|
+
<pre id="{{item.status}}">{{ item.status }}: {{ item.status_text }}</pre>
|
|
9
|
+
<i>Received: {{ item.date }}</i>
|
|
10
|
+
{% if item_history %}
|
|
11
|
+
<h3>History</h3>
|
|
12
|
+
<table>
|
|
13
|
+
{% for history_item in item_history %}
|
|
14
|
+
<tr>
|
|
15
|
+
<td id="{{ history_item.status }}">{{ history_item.date }}</td>
|
|
16
|
+
<td>{{ history_item.status_text }}</td>
|
|
17
|
+
</tr>
|
|
18
|
+
{% endfor %}
|
|
19
|
+
</table>
|
|
20
|
+
{% endif %}
|
|
21
|
+
{% include 'footer.html' %}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{% include 'header.html' %}
|
|
2
|
-
<table>
|
|
3
|
-
<tr>
|
|
4
|
-
<th>Command</th>
|
|
5
|
-
<th>Output</th>
|
|
6
|
-
<th>Last updated</th>
|
|
7
|
-
<th>Status</th>
|
|
8
|
-
</tr>
|
|
9
|
-
{% for item in host.all_items %}
|
|
10
|
-
|
|
11
|
-
<tr>
|
|
12
|
-
<td><a href="{{ item.report_type }}_{{ item.id }}.html">{{ item.command }}</a></td>
|
|
13
|
-
<td>{{ item.status_text }}</td>
|
|
14
|
-
<td>{{ item.date }}</td>
|
|
15
|
-
<td class="{{ item.status }}"></td>
|
|
16
|
-
</tr>
|
|
17
|
-
{% endfor %}
|
|
18
|
-
</table>
|
|
19
|
-
{% include 'footer.html' %}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{% include 'header.html' %}
|
|
2
|
-
<table>
|
|
3
|
-
<thead><tr>
|
|
4
|
-
<th>Hostname</th>
|
|
5
|
-
<th>Report types</th>
|
|
6
|
-
<th>Last updated</th>
|
|
7
|
-
<th>Status</th>
|
|
8
|
-
</tr></thead>
|
|
9
|
-
<tbody>
|
|
10
|
-
{% for host in hosts %}
|
|
11
|
-
<tr>
|
|
12
|
-
<td><a href="hosts/{{ host.name }}/host.html">{{ host.name }}</a></td>
|
|
13
|
-
<td>{{ host.report_types }}</td>
|
|
14
|
-
<td>{{ host.last_updated }}</td>
|
|
15
|
-
<td class="{{ host.status }}"></td>
|
|
16
|
-
</tr>
|
|
17
|
-
{% endfor %}
|
|
18
|
-
</tbody>
|
|
19
|
-
</table>
|
|
20
|
-
{% include 'footer.html' %}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
<html>
|
|
2
|
-
{% include 'header.html' %}
|
|
3
|
-
<table>
|
|
4
|
-
<thead><tr>
|
|
5
|
-
<th>Hostname</th>
|
|
6
|
-
<th>Report types</th>
|
|
7
|
-
<th>Last updated</th>
|
|
8
|
-
<th>Status</th>
|
|
9
|
-
</tr></thead>
|
|
10
|
-
<tbody>
|
|
11
|
-
{% for host in hosts %}
|
|
12
|
-
<tr>
|
|
13
|
-
<td><a href="hosts/{{ host.name }}/host.html">{{ host.name }}</a></td>
|
|
14
|
-
<td>{{ host.report_types }}</td>
|
|
15
|
-
<td>{{ host.last_updated }}</td>
|
|
16
|
-
<td class="{{ host.status }}"></td>
|
|
17
|
-
</tr>
|
|
18
|
-
{% endfor %}
|
|
19
|
-
</tbody>
|
|
20
|
-
</table>
|
|
21
|
-
{% include 'footer.html' %}
|
|
22
|
-
</html>
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
<html>
|
|
2
|
-
{% include 'header.html' %}
|
|
3
|
-
<table>
|
|
4
|
-
<tr>
|
|
5
|
-
<th>Hostname</th>
|
|
6
|
-
<th>Command</th>
|
|
7
|
-
<th>Output</th>
|
|
8
|
-
<th>Last updated</th>
|
|
9
|
-
<th>Status</th>
|
|
10
|
-
</tr>
|
|
11
|
-
{% for item in services %}
|
|
12
|
-
<tr>
|
|
13
|
-
<td>{{ item.host }}</td>
|
|
14
|
-
<td><a href="hosts/{{ item.host }}/{{ item.report_type}}_{{ item.id }}.html">{{ item.command }}</a></td>
|
|
15
|
-
<td>{{ item.status_text }}</td>
|
|
16
|
-
<td>{{ item.date }}</td>
|
|
17
|
-
<td id="{{ item.status }}"></td>
|
|
18
|
-
</tr>
|
|
19
|
-
{% endfor %}
|
|
20
|
-
</table>
|
|
21
|
-
{% include 'footer.html' %}
|
|
22
|
-
</html>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|