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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sysview-py
3
- Version: 0.3.7
3
+ Version: 0.4.0
4
4
  Summary: Add your description here
5
5
  Author: Michael Wilson
6
6
  Author-email: Michael Wilson <mw@1wilson.org>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sysview-py"
3
- version = "0.3.7"
3
+ version = "0.4.0"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -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."""
@@ -4,7 +4,7 @@ from contextlib import contextmanager
4
4
 
5
5
  @contextmanager
6
6
  def lock_file(file_path):
7
- f = open(file_path, 'w')
7
+ f = open(file_path, "w")
8
8
  fcntl.flock(f.fileno(), fcntl.LOCK_EX)
9
9
  try:
10
10
  yield f
@@ -4,7 +4,7 @@ import sys
4
4
  from .sysview import Sysview
5
5
  from .flock import lock_file
6
6
 
7
- LOCK_FILE=Path.home() / ".sysview.lock"
7
+ LOCK_FILE = Path.home() / ".sysview.lock"
8
8
 
9
9
 
10
10
  def main():
@@ -2,12 +2,8 @@ import hashlib
2
2
  from datetime import datetime, timedelta
3
3
 
4
4
 
5
- status_map = {
6
- "UNKNOWN": 3,
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
- if age > threshold:
117
- status = "UNKNOWN"
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((f"{self.report_id}_{self.command}").encode('utf-8')).hexdigest()
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, raw_report, hostname=None, date=None, report_type=None, items=None, report_id=None
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(raw_item, report_id=self.report_id, date=self.date)
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
- def __init__(self, db=DEFAULT_DB_PATH, hosts=[], reports=[], html_root=DEFAULT_HTML_PATH):
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
- autoescape=select_autoescape()
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, 'w') as f:
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(items=host.all_items, title=host.name, active="Hosts", path="../../", auto_refresh=True)
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, 'w') as f:
78
+ with open(html_path, "w") as f:
66
79
  f.write(html)
67
80
  for item in host.all_items:
68
- html = env.get_template("service.html").render(item=item, title=host.name, active="Services", path="../../", auto_refresh=False)
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, 'w') as f:
92
+ with open(html_path, "w") as f:
72
93
  f.write(html)
73
- with open(raw_path, 'w') as f:
94
+ with open(raw_path, "w") as f:
74
95
  f.write(item.raw_item)
75
- html = env.get_template("card_view.html").render(items=self.all_services, title="Services", active="Services", path="", auto_refresh=True)
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, 'w') as f:
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,8 +0,0 @@
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
- <pre>{{ item.raw_item }}</pre>
8
- {% include 'footer.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