From 8d23278251d27205033ffcc5cacfcbd97fc8c9c0 Mon Sep 17 00:00:00 2001 From: Joakim Hamren Date: Thu, 24 Jul 2014 15:37:55 +0200 Subject: [PATCH] Added filters to system-wide connections. --- CHANGELOG | 6 +- README.md | 2 +- psdash/__init__.py | 2 +- psdash/run.py | 2 +- psdash/static/js/psdash.js | 19 +++++ psdash/templates/network.html | 150 +++++++++++++++++++++++----------- psdash/web.py | 47 ++++++++++- tests/test_web.py | 4 + 8 files changed, 178 insertions(+), 54 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8032720..e6d18a4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ ---- v0.4.0 [Not released] --- +--- v0.4.0 [2014-07-24] --- * [NEW] Added a environment variables sub-page to the process view. @@ -12,6 +12,10 @@ * [NEW] Added Travis CI setup and test coverage. +* [NEW] Added filters to system-wide connections table + +* [NEW] Some rather large refactorings in general due to added tests. + --- v0.3.0 [2014-04-15] --- * [NEW] Added a PSDASH_URL_PREFIX configuration setting to allow psdash to be served from a sub-url rather than always on root. diff --git a/README.md b/README.md index 926f236..5c1e3cd 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Available command-line arguments: ``` usage: psdash [-h] [-l path] [-b host] [-p port] [-d] -psdash 0.3.0 - system information web dashboard +psdash 0.4.0 - system information web dashboard optional arguments: -h, --help show this help message and exit diff --git a/psdash/__init__.py b/psdash/__init__.py index f2b3589..49c37a8 100644 --- a/psdash/__init__.py +++ b/psdash/__init__.py @@ -1 +1 @@ -__version__ = "0.3.0" \ No newline at end of file +__version__ = "0.4.0" \ No newline at end of file diff --git a/psdash/run.py b/psdash/run.py index b0e1c57..0ff45e8 100644 --- a/psdash/run.py +++ b/psdash/run.py @@ -152,7 +152,7 @@ def update_net_io_counters(self): return self.app.psdash.net_io_counters.update() def run(self): - logger.info('Starting psdash v0.3.0') + logger.info('Starting psdash v0.4.0') self._setup_locale() self._setup_timers() diff --git a/psdash/static/js/psdash.js b/psdash/static/js/psdash.js index 4749fe0..331d3e1 100644 --- a/psdash/static/js/psdash.js +++ b/psdash/static/js/psdash.js @@ -108,8 +108,12 @@ function init_log() { scroll_down($el); } +var skip_updates = false; + function init_updater() { function update() { + if (skip_updates) return; + $.ajax({ url: location.href, cache: false, @@ -123,7 +127,22 @@ function init_updater() { setInterval(update, 3000); } +function init_connections_filter() { + var $content = $("#content"); + $content.on("change", "#connections-form select", function () { + $content.find("#connections-form").submit(); + }); + $content.on("focus", "#connections-form select", function () { + skip_updates = true; + }); + $content.on("blur", "#connections-form select", function () { + skip_updates = false; + }); +} + $(document).ready(function() { + init_connections_filter(); + if($("#log").length == 0) { init_updater(); } else { diff --git a/psdash/templates/network.html b/psdash/templates/network.html index 69adc27..f966d98 100644 --- a/psdash/templates/network.html +++ b/psdash/templates/network.html @@ -40,52 +40,108 @@
-
Connections
- - - - - - - - - - - - - - {% for c in net_connections %} - - - - - - - - - - {% endfor %} - -
FDPIDFamilyTypeLocal addressRemote addressStatus
- {{ c.fd if c.fd > 0 else "-" }} - - {% if c.pid %} - {{ c.pid }} - {% else %} - - - {% endif %} - {{ socket_families[c.family] }}{{ socket_types[c.type] }} - {% if c.laddr[0] and c.laddr[1] %} - {{ c.laddr[0] }}:{{ c.laddr[1] }} - {% else %} - - - {% endif %} - - {% if c.raddr[0] and c.raddr[1] %} - {{ c.raddr[0] }}:{{ c.raddr[1] }} - {% else %} - - - {% endif %} - {{ c.status }}
+
+ Connections + Listing {{ num_conns }} connections +
+
+ + + + + + + + + + + + + + + + + + + + + + + {% for c in net_connections %} + + + + + + + + + + {% endfor %} + +
FDPIDFamilyTypeLocal addressRemote addressState
+ + + + + + + + + + + +
+ {{ c.fd if c.fd > 0 else "-" }} + + {% if c.pid %} + {{ c.pid }} + {% else %} + - + {% endif %} + {{ socket_families[c.family] }}{{ socket_types[c.type] }} + {% if c.laddr[0] and c.laddr[1] %} + {{ c.laddr[0] }}:{{ c.laddr[1] }} + {% else %} + - + {% endif %} + + {% if c.raddr[0] and c.raddr[1] %} + {{ c.raddr[0] }}:{{ c.raddr[1] }} + {% else %} + - + {% endif %} + {{ c.status }}
+
{% endblock %} \ No newline at end of file diff --git a/psdash/web.py b/psdash/web.py index 3ce6d82..b3a773c 100755 --- a/psdash/web.py +++ b/psdash/web.py @@ -238,13 +238,48 @@ def process(pid, section): **context ) +def filter_connections(conns, filters): + for k, v in filters.iteritems(): + if not v: + continue + + if k in ('laddr', 'raddr'): + conns = [c for c in conns if getattr(c, k) and str(getattr(c, k)[0]) == v] + else: + conns = [c for c in conns if str(getattr(c, k)) == v] + + return conns @webapp.route('/network') def view_networks(): netifs = build_network_interfaces() netifs.sort(key=lambda x: x.get('bytes_sent'), reverse=True) + # {'key', 'default_value'} + # An empty string means that no filtering will take place on that key + form_keys = { + 'pid': '', + 'family': str(socket.AF_INET), + 'type': str(socket.SOCK_STREAM), + 'laddr': '', + 'raddr': '', + 'status': 'LISTEN' + } + + form_values = dict((k, request.args.get(k, default_val)) + for k, default_val in form_keys.iteritems()) + conns = psutil.net_connections() + + # populate filter dropdowns + pids = set(str(c.pid) for c in conns if c.pid) + local_addresses = set(c.laddr[0] for c in conns if c.laddr) + remote_addresses = set(c.raddr[0] for c in conns if c.raddr) + states = set(c.status for c in conns) + families = dict((k, str(v)) for k, v in socket_families.iteritems()) + types = dict((k, str(v)) for k, v in socket_types.iteritems()) + + conns = filter_connections(conns, form_values) conns.sort(key=lambda x: x.status) return render_template( @@ -252,9 +287,15 @@ def view_networks(): page='network', network_interfaces=netifs, net_connections=conns, - socket_families=socket_families, - socket_types=socket_types, - is_xhr=request.is_xhr + socket_families=families, + socket_types=types, + pids=pids, + local_addresses=local_addresses, + remote_addresses=remote_addresses, + states=states, + is_xhr=request.is_xhr, + num_conns=len(conns), + **form_values ) diff --git a/tests/test_web.py b/tests/test_web.py index 8df31d0..fb9926c 100644 --- a/tests/test_web.py +++ b/tests/test_web.py @@ -189,6 +189,10 @@ def test_non_existing(self): resp = self.client.get('/prettywronghuh') self.assertEqual(resp.status_code, httplib.NOT_FOUND) + def test_connection_filters(self): + resp = self.client.get('/network?laddr=127.0.0.1') + self.assertEqual(resp.status_code, httplib.OK) + class TestLogs(unittest2.TestCase): def _create_log_file(self):