5416af2fb1bebb847af55f8522b9edfb27232624
[kiosk.git] / health_renderer.py
1 #!/usr/bin/env python3
2
3 import logging
4 import os
5 import time
6 from typing import Dict
7
8 import constants
9 import file_writer
10 import renderer
11 import utils
12
13
14 logger = logging.getLogger(__file__)
15
16
17 class periodic_health_renderer(renderer.abstaining_renderer):
18     def __init__(self, name_to_timeout_dict: Dict[str, int]) -> None:
19         super().__init__(name_to_timeout_dict)
20
21     def periodic_render(self, key: str) -> bool:
22         with file_writer.file_writer("periodic-health_6_300.html") as f:
23             timestamps = "/timestamps/"
24             days = constants.seconds_per_day
25             hours = constants.seconds_per_hour
26             mins = constants.seconds_per_minute
27             minutes = mins
28             limits = {
29                 timestamps + "last_http_probe_wannabe_house": mins * 10,
30                 timestamps + "last_http_probe_meerkat_cabin": mins * 10,
31                 timestamps + "last_http_probe_dns_house": mins * 10,
32                 timestamps + "last_http_probe_rpi_cabin": mins * 10,
33                 timestamps + "last_http_probe_rpi_house": mins * 10,
34                 timestamps + "last_http_probe_therm_house": mins * 10,
35                 timestamps + "last_rsnapshot_hourly": hours * 24,
36                 timestamps + "last_rsnapshot_daily": days * 3,
37                 timestamps + "last_rsnapshot_weekly": days * 14,
38                 timestamps + "last_rsnapshot_monthly": days * 70,
39                 timestamps + "last_zfssnapshot_hourly": hours * 5,
40                 timestamps + "last_zfssnapshot_daily": hours * 36,
41                 timestamps + "last_zfssnapshot_weekly": days * 9,
42                 timestamps + "last_zfssnapshot_monthly": days * 70,
43                 timestamps + "last_zfssnapshot_cleanup": hours * 24,
44                 timestamps + "last_zfs_scrub": days * 9,
45                 timestamps + "last_backup_zfs_scrub": days * 9,
46                 timestamps + "last_cabin_zfs_scrub": days * 9,
47                 timestamps + "last_zfsxfer_backup.house": hours * 36,
48                 timestamps + "last_zfsxfer_ski.dyn.guru.org": days * 7,
49                 timestamps + "last_photos_sync": hours * 8,
50                 timestamps + "last_disk_selftest_short": days * 14,
51                 timestamps + "last_disk_selftest_long": days * 31,
52                 timestamps + "last_backup_disk_selftest_short": days * 14,
53                 timestamps + "last_backup_disk_selftest_long": days * 31,
54                 timestamps + "last_cabin_disk_selftest_short": days * 14,
55                 timestamps + "last_cabin_disk_selftest_long": days * 31,
56                 timestamps + "last_cabin_rpi_ping": mins * 20,
57                 timestamps + "last_healthy_wifi": mins * 10,
58                 timestamps + "last_healthy_network": mins * 10,
59                 timestamps + "last_scott_sync": days * 2,
60             }
61             self.write_header(f)
62
63             now = time.time()
64             n = 0
65             for filepath, limit_sec in sorted(limits.items()):
66                 ts = os.stat(filepath).st_mtime
67                 age = now - ts
68                 logger.debug(f"{filepath} -- age: {age}, limit {limit_sec}")
69                 if age < limits[filepath]:
70                     # OK
71                     f.write(
72                         '<TD BGCOLOR="#007010" HEIGHT=100 WIDTH=33% STYLE="text-size:70%; vertical-align: middle;">\n'
73                     )
74                     txt_color="#ffffff"
75                 else:
76                     # BAD!
77                     f.write(
78                         '<TD BGCOLOR="#990000" HEIGHT=100 WIDTH=33% CLASS="invalid" STYLE="text-size:70%; vertical-align:middle;">\n'
79                     )
80                     txt_color="#000000"
81                 f.write(f"  <CENTER><FONT SIZE=-1 COLOR={txt_color}>\n")
82
83                 name = filepath.replace(timestamps, "")
84                 name = name.replace("last_", "")
85                 name = name.replace("_", "&nbsp;")
86                 duration = utils.describe_duration_briefly(int(age))
87
88                 logger.debug(f"{name} is {duration} old.")
89                 f.write(f"{name}<BR>\n<B>{duration}</B> old.\n")
90                 f.write("</FONT></CENTER>\n</TD>\n\n")
91                 n += 1
92                 if n % 3 == 0:
93                     f.write("</TR>\n<TR>\n<!-- ------------------- -->\n")
94             self.write_footer(f)
95         return True
96
97     def write_header(self, f: file_writer.file_writer) -> None:
98         f.write(
99             """
100 <HTML>
101 <HEAD>
102 <STYLE>
103 @-webkit-keyframes invalid {
104   from { background-color: #ff6400; }
105   to { background-color: #ff0000; }
106   padding-right: 25px;
107   padding-left: 25px;
108 }
109 @-moz-keyframes invalid {
110   from { background-color: #ff6400; }
111   to { background-color: #ff0000; }
112   padding-right: 25px;
113   padding-left: 25px;
114 }
115 @-o-keyframes invalid {
116   from { background-color: #ff6400; }
117   to { background-color: #ff0000; }
118   padding-right: 25px;
119   padding-left: 25px;
120 }
121 @keyframes invalid {
122   from { background-color: #ff6400; }
123   to { background-color: #ff0000; }
124   padding-right: 25px;
125   padding-left: 25px;
126 }
127 .invalid {
128   -webkit-animation: invalid 1s infinite; /* Safari 4+ */
129   -moz-animation:    invalid 1s infinite; /* Fx 5+ */
130   -o-animation:      invalid 1s infinite; /* Opera 12+ */
131   animation:         invalid 1s infinite; /* IE 10+ */
132 }
133 </STYLE>
134 <meta http-equiv="cache-control" content="max-age=0" />
135 <meta http-equiv="cache-control" content="no-cache" />
136 <meta http-equiv="expires" content="0" />
137 <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
138 <meta http-equiv="pragma" content="no-cache" />
139 </HEAD>
140 <BODY>
141 <H1>Periodic Cronjob Health Report</H1>
142 <HR>
143 <CENTER>
144 <TABLE BORDER=0 WIDTH=99% style="font-size:16pt">
145 <TR>
146 """
147         )
148
149     def write_footer(self, f: file_writer.file_writer) -> None:
150         f.write(
151             """
152 </TR>
153 </TABLE>
154 </BODY>
155 </HTML>"""
156         )
157
158
159 #test = periodic_health_renderer({"Test", 123})
160 #test.periodic_render("Test")