Browse Source

It's not that pretty but it seems to work

master
Zack Marvel 2 years ago
parent
commit
807318bbf5
  1. 2
      src/__init__.py
  2. 11
      src/monitor.py
  3. 52
      src/package_list.py
  4. 31
      src/static/packages.js
  5. 16
      src/static/style.css
  6. 15
      src/templates/index.html
  7. 39
      src/templates/index.html.j2
  8. 6
      src/web.py

2
src/__init__.py

@ -4,8 +4,6 @@ from .monitor import Monitor
DEFAULT_CONFIG_PATH = 'apt-monitor.ini'
_config = Config(DEFAULT_CONFIG_PATH)
_monitor = Monitor(_config)

11
src/monitor.py

@ -5,6 +5,8 @@ import asyncssh
import asyncio
from pathlib import Path
from .package_list import PackageList
# 5 minutes
DEFAULT_INTERVAL = 5*60
@ -28,6 +30,9 @@ class Monitor(threading.Thread):
self._done = False
self._status = {}
for host in self.config.get_hosts():
if host not in ['DEFAULTS', 'apt-monitor']:
self._status[host] = PackageList()
def run(self):
while not self._done:
@ -48,8 +53,7 @@ class Monitor(threading.Thread):
tasks.append(self.check_host(host))
results = await asyncio.gather(*tasks, return_exceptions=True)
for (host, stdout, stderr) in results:
self._status[host] = list(
filter(lambda line: line != '', stdout))
self._status[host] = PackageList(stdout)
print('HOST {} => {}'.format(host, stderr))
async def check_host(self, host):
@ -74,7 +78,8 @@ class Monitor(threading.Thread):
upgradable_list = conn.run(cmd)
completed = await upgradable_list
if completed.exit_status == 0:
return (host, completed.stdout.splitlines(),
return (host, list(filter(lambda line: line != '',
completed.stdout.splitlines())),
completed.stderr.splitlines())
else:
raise ConnectionError(host)

52
src/package_list.py

@ -0,0 +1,52 @@
import re
pattern = re.compile('([A-Za-z0-9\\-_]+)\\/([A-Za-z]+) ([0-9\\.\\-]+) '
'([a-z0-9]+) \\[upgradable from: (.*)\\]')
class PackageList():
def __init__(self, packages=[]):
self.packages = self.parse_packages(packages)
def parse_packages(self, packages):
package_list = []
for line in packages:
match = pattern.match(line)
if match is not None:
# ignore arch for now
package_list.append(Package(match.group(1), match.group(2),
match.group(3), match.group(5)))
return package_list
class Package():
def __init__(self, name, source, new, old):
self.name = name
self.source = source
self.new = new
self.old = old
def test():
case = ['aptitude/stable 0.8.7-1 i386 [upgradable from: 0.6.11-1+b1]']
match = pattern.match(case[0])
assert match is not None
print(match)
assert match.group(1) == 'aptitude'
assert match.group(2) == 'stable'
assert match.group(3) == '0.8.7-1'
assert match.group(4) == 'i386'
assert match.group(5) == '0.6.11-1+b1'
package_list = PackageList(case)
assert len(package_list.packages) == 1
pkg = package_list.packages[0]
assert pkg.name == 'aptitude'
assert pkg.source == 'stable'
assert pkg.new == '0.8.7-1'
assert pkg.old == '0.6.11-1+b1'
if __name__ == '__main__':
test()

31
src/static/packages.js

@ -0,0 +1,31 @@
document.addEventListener('DOMContentLoaded', registerAnchorCallbacks, false);
document.addEventListener('DOMContentLoaded', hideTables, false);
function registerAnchorCallbacks() {
const anchors = document.getElementsByClassName('host-expand');
Array.prototype.forEach.call(anchors, function (anchor) {
anchor.onclick = expandTable;
});
}
function hideTables() {
const tables = document.getElementsByClassName('host-packages');
Array.prototype.forEach.call(tables, function (table) {
table.style = 'visibility: collapse;';
});
}
function expandTable(e) {
const table = this.nextSibling.nextSibling
if (table) {
console.log('toggle', table);
if (table.visible) {
table.style = 'visibility: collapse;';
} else {
table.style = 'visibility: visible;';
}
table.visible = !table.visible
}
}

16
src/static/style.css

@ -0,0 +1,16 @@
a.host-expand {
font-weight: bold;
}
table.host-packages {
width: 100%;
}
table.host-packages thead th {
border-bottom: 1px solid black;
}
body {
font-family: sans-serif;
}

15
src/templates/index.html

@ -1,15 +0,0 @@
<!doctype html>
<html>
<head>
<title>apt-monitor</title>
</head>
<body>
<h1>apt-monitor</h1>
<h2>Hosts</h2>
<ul>
{% for host in config.get_hosts() %}
<li><b>{{ host }}</b>: {{ monitor.get_status(host) }}</li>
{% endfor %}
</ul>
</body>
</html>

39
src/templates/index.html.j2

@ -0,0 +1,39 @@
<!doctype html>
<html>
<head>
<title>apt-monitor</title>
<link rel="stylesheet" type="text/css" href="{{ stylesheet }}">
<script src="{{ script }}"></script>
</head>
<body>
<h1>apt-monitor</h1>
<h2>Hosts</h2>
<ul>
{% for host in config.get_hosts() %}
<li>
<a href="#" class="host-expand">{{ host }}</a>: {{ monitor.get_status(host).packages|length }} upgradable
<table class="host-packages">
<thead>
<tr>
<th scope='col'>Package</th>
<th scope='col'>Source</th>
<th scope='col'>Upgradable From</th>
<th scope='col'>Upgradable To</th>
</tr>
</thead>
<tbody>
{% for pkg in monitor.get_status(host).packages %}
<tr>
<th scope='row'>{{ pkg.name }}</th>
<td>{{ pkg.source }}</td>
<td>{{ pkg.old }}</td>
<td>{{ pkg.new }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</li>
{% endfor %}
</ul>
</body>
</html>

6
src/web.py

@ -1,5 +1,5 @@
from flask import Flask, render_template
from flask import Flask, render_template, url_for
from . import _monitor, _config
@ -9,4 +9,6 @@ app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html', monitor=_monitor, config=_config)
return render_template('index.html.j2', monitor=_monitor, config=_config,
stylesheet=url_for('static', filename='style.css'),
script=url_for('static', filename='packages.js'))
Loading…
Cancel
Save