aprsd 3.3.3__py2.py3-none-any.whl → 3.4.0__py2.py3-none-any.whl

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.
Files changed (72) hide show
  1. aprsd/client.py +133 -20
  2. aprsd/clients/aprsis.py +6 -3
  3. aprsd/clients/fake.py +1 -1
  4. aprsd/clients/kiss.py +1 -1
  5. aprsd/cmds/completion.py +13 -27
  6. aprsd/cmds/fetch_stats.py +53 -57
  7. aprsd/cmds/healthcheck.py +32 -30
  8. aprsd/cmds/list_plugins.py +2 -2
  9. aprsd/cmds/listen.py +33 -17
  10. aprsd/cmds/send_message.py +2 -2
  11. aprsd/cmds/server.py +26 -9
  12. aprsd/cmds/webchat.py +34 -29
  13. aprsd/conf/common.py +46 -31
  14. aprsd/log/log.py +28 -6
  15. aprsd/main.py +20 -18
  16. aprsd/packets/__init__.py +3 -2
  17. aprsd/packets/collector.py +56 -0
  18. aprsd/packets/core.py +456 -321
  19. aprsd/packets/log.py +143 -0
  20. aprsd/packets/packet_list.py +83 -66
  21. aprsd/packets/seen_list.py +30 -19
  22. aprsd/packets/tracker.py +60 -62
  23. aprsd/packets/watch_list.py +64 -38
  24. aprsd/plugin.py +41 -16
  25. aprsd/plugins/email.py +35 -7
  26. aprsd/plugins/time.py +3 -2
  27. aprsd/plugins/version.py +4 -5
  28. aprsd/plugins/weather.py +0 -1
  29. aprsd/stats/__init__.py +20 -0
  30. aprsd/stats/app.py +46 -0
  31. aprsd/stats/collector.py +38 -0
  32. aprsd/threads/__init__.py +3 -2
  33. aprsd/threads/aprsd.py +67 -36
  34. aprsd/threads/keep_alive.py +55 -49
  35. aprsd/threads/log_monitor.py +46 -0
  36. aprsd/threads/rx.py +43 -24
  37. aprsd/threads/stats.py +44 -0
  38. aprsd/threads/tx.py +36 -17
  39. aprsd/utils/__init__.py +12 -0
  40. aprsd/utils/counter.py +6 -3
  41. aprsd/utils/json.py +20 -0
  42. aprsd/utils/objectstore.py +22 -17
  43. aprsd/web/admin/static/css/prism.css +4 -189
  44. aprsd/web/admin/static/js/charts.js +9 -7
  45. aprsd/web/admin/static/js/echarts.js +71 -9
  46. aprsd/web/admin/static/js/main.js +47 -6
  47. aprsd/web/admin/static/js/prism.js +11 -2246
  48. aprsd/web/admin/templates/index.html +18 -7
  49. aprsd/web/chat/static/js/gps.js +3 -1
  50. aprsd/web/chat/static/js/main.js +4 -3
  51. aprsd/web/chat/static/js/send-message.js +5 -2
  52. aprsd/web/chat/templates/index.html +1 -0
  53. aprsd/wsgi.py +62 -127
  54. {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/METADATA +14 -16
  55. {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/RECORD +60 -65
  56. {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/WHEEL +1 -1
  57. aprsd-3.4.0.dist-info/pbr.json +1 -0
  58. aprsd/plugins/query.py +0 -81
  59. aprsd/rpc/__init__.py +0 -14
  60. aprsd/rpc/client.py +0 -165
  61. aprsd/rpc/server.py +0 -99
  62. aprsd/stats.py +0 -266
  63. aprsd/threads/store.py +0 -30
  64. aprsd/utils/converters.py +0 -15
  65. aprsd/web/admin/static/json-viewer/jquery.json-viewer.css +0 -57
  66. aprsd/web/admin/static/json-viewer/jquery.json-viewer.js +0 -158
  67. aprsd/web/chat/static/json-viewer/jquery.json-viewer.css +0 -57
  68. aprsd/web/chat/static/json-viewer/jquery.json-viewer.js +0 -158
  69. aprsd-3.3.3.dist-info/pbr.json +0 -1
  70. {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/LICENSE +0 -0
  71. {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/entry_points.txt +0 -0
  72. {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/top_level.txt +0 -0
aprsd/stats.py DELETED
@@ -1,266 +0,0 @@
1
- import datetime
2
- import logging
3
- import threading
4
-
5
- from oslo_config import cfg
6
- import wrapt
7
-
8
- import aprsd
9
- from aprsd import packets, plugin, utils
10
-
11
-
12
- CONF = cfg.CONF
13
- LOG = logging.getLogger("APRSD")
14
-
15
-
16
- class APRSDStats:
17
-
18
- _instance = None
19
- lock = threading.Lock()
20
-
21
- start_time = None
22
- _aprsis_server = None
23
- _aprsis_keepalive = None
24
-
25
- _email_thread_last_time = None
26
- _email_tx = 0
27
- _email_rx = 0
28
-
29
- _mem_current = 0
30
- _mem_peak = 0
31
-
32
- _thread_info = {}
33
-
34
- _pkt_cnt = {
35
- "Packet": {
36
- "tx": 0,
37
- "rx": 0,
38
- },
39
- "AckPacket": {
40
- "tx": 0,
41
- "rx": 0,
42
- },
43
- "GPSPacket": {
44
- "tx": 0,
45
- "rx": 0,
46
- },
47
- "StatusPacket": {
48
- "tx": 0,
49
- "rx": 0,
50
- },
51
- "MicEPacket": {
52
- "tx": 0,
53
- "rx": 0,
54
- },
55
- "MessagePacket": {
56
- "tx": 0,
57
- "rx": 0,
58
- },
59
- "WeatherPacket": {
60
- "tx": 0,
61
- "rx": 0,
62
- },
63
- "ObjectPacket": {
64
- "tx": 0,
65
- "rx": 0,
66
- },
67
- }
68
-
69
- def __new__(cls, *args, **kwargs):
70
- if cls._instance is None:
71
- cls._instance = super().__new__(cls)
72
- # any init here
73
- cls._instance.start_time = datetime.datetime.now()
74
- cls._instance._aprsis_keepalive = datetime.datetime.now()
75
- return cls._instance
76
-
77
- @wrapt.synchronized(lock)
78
- @property
79
- def uptime(self):
80
- return datetime.datetime.now() - self.start_time
81
-
82
- @wrapt.synchronized(lock)
83
- @property
84
- def memory(self):
85
- return self._mem_current
86
-
87
- @wrapt.synchronized(lock)
88
- def set_memory(self, memory):
89
- self._mem_current = memory
90
-
91
- @wrapt.synchronized(lock)
92
- @property
93
- def memory_peak(self):
94
- return self._mem_peak
95
-
96
- @wrapt.synchronized(lock)
97
- def set_memory_peak(self, memory):
98
- self._mem_peak = memory
99
-
100
- @wrapt.synchronized(lock)
101
- def set_thread_info(self, thread_info):
102
- self._thread_info = thread_info
103
-
104
- @wrapt.synchronized(lock)
105
- @property
106
- def thread_info(self):
107
- return self._thread_info
108
-
109
- @wrapt.synchronized(lock)
110
- @property
111
- def aprsis_server(self):
112
- return self._aprsis_server
113
-
114
- @wrapt.synchronized(lock)
115
- def set_aprsis_server(self, server):
116
- self._aprsis_server = server
117
-
118
- @wrapt.synchronized(lock)
119
- @property
120
- def aprsis_keepalive(self):
121
- return self._aprsis_keepalive
122
-
123
- @wrapt.synchronized(lock)
124
- def set_aprsis_keepalive(self):
125
- self._aprsis_keepalive = datetime.datetime.now()
126
-
127
- def rx(self, packet):
128
- pkt_type = packet.__class__.__name__
129
- if pkt_type not in self._pkt_cnt:
130
- self._pkt_cnt[pkt_type] = {
131
- "tx": 0,
132
- "rx": 0,
133
- }
134
- self._pkt_cnt[pkt_type]["rx"] += 1
135
-
136
- def tx(self, packet):
137
- pkt_type = packet.__class__.__name__
138
- if pkt_type not in self._pkt_cnt:
139
- self._pkt_cnt[pkt_type] = {
140
- "tx": 0,
141
- "rx": 0,
142
- }
143
- self._pkt_cnt[pkt_type]["tx"] += 1
144
-
145
- @wrapt.synchronized(lock)
146
- @property
147
- def msgs_tracked(self):
148
- return packets.PacketTrack().total_tracked
149
-
150
- @wrapt.synchronized(lock)
151
- @property
152
- def email_tx(self):
153
- return self._email_tx
154
-
155
- @wrapt.synchronized(lock)
156
- def email_tx_inc(self):
157
- self._email_tx += 1
158
-
159
- @wrapt.synchronized(lock)
160
- @property
161
- def email_rx(self):
162
- return self._email_rx
163
-
164
- @wrapt.synchronized(lock)
165
- def email_rx_inc(self):
166
- self._email_rx += 1
167
-
168
- @wrapt.synchronized(lock)
169
- @property
170
- def email_thread_time(self):
171
- return self._email_thread_last_time
172
-
173
- @wrapt.synchronized(lock)
174
- def email_thread_update(self):
175
- self._email_thread_last_time = datetime.datetime.now()
176
-
177
- @wrapt.synchronized(lock)
178
- def stats(self):
179
- now = datetime.datetime.now()
180
- if self._email_thread_last_time:
181
- last_update = str(now - self._email_thread_last_time)
182
- else:
183
- last_update = "never"
184
-
185
- if self._aprsis_keepalive:
186
- last_aprsis_keepalive = str(now - self._aprsis_keepalive)
187
- else:
188
- last_aprsis_keepalive = "never"
189
-
190
- pm = plugin.PluginManager()
191
- plugins = pm.get_plugins()
192
- plugin_stats = {}
193
- if plugins:
194
- def full_name_with_qualname(obj):
195
- return "{}.{}".format(
196
- obj.__class__.__module__,
197
- obj.__class__.__qualname__,
198
- )
199
-
200
- for p in plugins:
201
- plugin_stats[full_name_with_qualname(p)] = {
202
- "enabled": p.enabled,
203
- "rx": p.rx_count,
204
- "tx": p.tx_count,
205
- "version": p.version,
206
- }
207
-
208
- wl = packets.WatchList()
209
- sl = packets.SeenList()
210
- pl = packets.PacketList()
211
-
212
- stats = {
213
- "aprsd": {
214
- "version": aprsd.__version__,
215
- "uptime": utils.strfdelta(self.uptime),
216
- "callsign": CONF.callsign,
217
- "memory_current": int(self.memory),
218
- "memory_current_str": utils.human_size(self.memory),
219
- "memory_peak": int(self.memory_peak),
220
- "memory_peak_str": utils.human_size(self.memory_peak),
221
- "threads": self._thread_info,
222
- "watch_list": wl.get_all(),
223
- "seen_list": sl.get_all(),
224
- },
225
- "aprs-is": {
226
- "server": str(self.aprsis_server),
227
- "callsign": CONF.aprs_network.login,
228
- "last_update": last_aprsis_keepalive,
229
- },
230
- "packets": {
231
- "total_tracked": int(pl.total_tx() + pl.total_rx()),
232
- "total_sent": int(pl.total_tx()),
233
- "total_received": int(pl.total_rx()),
234
- "by_type": self._pkt_cnt,
235
- },
236
- "messages": {
237
- "sent": self._pkt_cnt["MessagePacket"]["tx"],
238
- "received": self._pkt_cnt["MessagePacket"]["tx"],
239
- "ack_sent": self._pkt_cnt["AckPacket"]["tx"],
240
- },
241
- "email": {
242
- "enabled": CONF.email_plugin.enabled,
243
- "sent": int(self._email_tx),
244
- "received": int(self._email_rx),
245
- "thread_last_update": last_update,
246
- },
247
- "plugins": plugin_stats,
248
- }
249
- return stats
250
-
251
- def __str__(self):
252
- pl = packets.PacketList()
253
- return (
254
- "Uptime:{} Msgs TX:{} RX:{} "
255
- "ACK: TX:{} RX:{} "
256
- "Email TX:{} RX:{} LastLoop:{} ".format(
257
- self.uptime,
258
- pl.total_tx(),
259
- pl.total_rx(),
260
- self._pkt_cnt["AckPacket"]["tx"],
261
- self._pkt_cnt["AckPacket"]["rx"],
262
- self._email_tx,
263
- self._email_rx,
264
- self._email_thread_last_time,
265
- )
266
- )
aprsd/threads/store.py DELETED
@@ -1,30 +0,0 @@
1
- import logging
2
- import time
3
- from typing import List
4
-
5
- from oslo_config import cfg
6
-
7
- from aprsd.threads import APRSDThread
8
- from aprsd.utils import objectstore
9
-
10
-
11
- CONF = cfg.CONF
12
- LOG = logging.getLogger("APRSD")
13
-
14
-
15
- class APRSDStoreThread(APRSDThread):
16
- """save object store instances to disk periodically."""
17
-
18
- save_interval = 10
19
-
20
- def __init__(self, obj_list: List[objectstore.ObjectStoreMixin]):
21
- super().__init__("STORE")
22
- self.obj_list = obj_list
23
-
24
- def loop(self):
25
- if self.loop_interval % self.save_interval == 0:
26
- for obj in self.obj_list:
27
- LOG.debug(f"Saving {obj.__class__.__name__}")
28
- obj.save()
29
- time.sleep(1)
30
- return True
aprsd/utils/converters.py DELETED
@@ -1,15 +0,0 @@
1
- from datafiles import converters
2
- from datetime import datetime
3
-
4
-
5
- class MyDateTime(converters.Converter, datetime):
6
-
7
- @classmethod
8
- def to_preserialization_data(cls, python_value, **kwargs):
9
- # Convert `datetime` to a value that can be serialized
10
- return python_value.isoformat()
11
-
12
- @classmethod
13
- def to_python_value(cls, deserialized_data, **kwargs):
14
- # Convert file value back into a `datetime` object
15
- return datetime.fromisoformat(deserialized_data)
@@ -1,57 +0,0 @@
1
- /* Root element */
2
- .json-document {
3
- padding: 1em 2em;
4
- }
5
-
6
- /* Syntax highlighting for JSON objects */
7
- ul.json-dict, ol.json-array {
8
- list-style-type: none;
9
- margin: 0 0 0 1px;
10
- border-left: 1px dotted #ccc;
11
- padding-left: 2em;
12
- }
13
- .json-string {
14
- color: #0B7500;
15
- }
16
- .json-literal {
17
- color: #1A01CC;
18
- font-weight: bold;
19
- }
20
-
21
- /* Toggle button */
22
- a.json-toggle {
23
- position: relative;
24
- color: inherit;
25
- text-decoration: none;
26
- }
27
- a.json-toggle:focus {
28
- outline: none;
29
- }
30
- a.json-toggle:before {
31
- font-size: 1.1em;
32
- color: #c0c0c0;
33
- content: "\25BC"; /* down arrow */
34
- position: absolute;
35
- display: inline-block;
36
- width: 1em;
37
- text-align: center;
38
- line-height: 1em;
39
- left: -1.2em;
40
- }
41
- a.json-toggle:hover:before {
42
- color: #aaa;
43
- }
44
- a.json-toggle.collapsed:before {
45
- /* Use rotated down arrow, prevents right arrow appearing smaller than down arrow in some browsers */
46
- transform: rotate(-90deg);
47
- }
48
-
49
- /* Collapsable placeholder links */
50
- a.json-placeholder {
51
- color: #aaa;
52
- padding: 0 1em;
53
- text-decoration: none;
54
- }
55
- a.json-placeholder:hover {
56
- text-decoration: underline;
57
- }
@@ -1,158 +0,0 @@
1
- /**
2
- * jQuery json-viewer
3
- * @author: Alexandre Bodelot <alexandre.bodelot@gmail.com>
4
- * @link: https://github.com/abodelot/jquery.json-viewer
5
- */
6
- (function($) {
7
-
8
- /**
9
- * Check if arg is either an array with at least 1 element, or a dict with at least 1 key
10
- * @return boolean
11
- */
12
- function isCollapsable(arg) {
13
- return arg instanceof Object && Object.keys(arg).length > 0;
14
- }
15
-
16
- /**
17
- * Check if a string represents a valid url
18
- * @return boolean
19
- */
20
- function isUrl(string) {
21
- var urlRegexp = /^(https?:\/\/|ftps?:\/\/)?([a-z0-9%-]+\.){1,}([a-z0-9-]+)?(:(\d{1,5}))?(\/([a-z0-9\-._~:/?#[\]@!$&'()*+,;=%]+)?)?$/i;
22
- return urlRegexp.test(string);
23
- }
24
-
25
- /**
26
- * Transform a json object into html representation
27
- * @return string
28
- */
29
- function json2html(json, options) {
30
- var html = '';
31
- if (typeof json === 'string') {
32
- // Escape tags and quotes
33
- json = json
34
- .replace(/&/g, '&amp;')
35
- .replace(/</g, '&lt;')
36
- .replace(/>/g, '&gt;')
37
- .replace(/'/g, '&apos;')
38
- .replace(/"/g, '&quot;');
39
-
40
- if (options.withLinks && isUrl(json)) {
41
- html += '<a href="' + json + '" class="json-string" target="_blank">' + json + '</a>';
42
- } else {
43
- // Escape double quotes in the rendered non-URL string.
44
- json = json.replace(/&quot;/g, '\\&quot;');
45
- html += '<span class="json-string">"' + json + '"</span>';
46
- }
47
- } else if (typeof json === 'number') {
48
- html += '<span class="json-literal">' + json + '</span>';
49
- } else if (typeof json === 'boolean') {
50
- html += '<span class="json-literal">' + json + '</span>';
51
- } else if (json === null) {
52
- html += '<span class="json-literal">null</span>';
53
- } else if (json instanceof Array) {
54
- if (json.length > 0) {
55
- html += '[<ol class="json-array">';
56
- for (var i = 0; i < json.length; ++i) {
57
- html += '<li>';
58
- // Add toggle button if item is collapsable
59
- if (isCollapsable(json[i])) {
60
- html += '<a href class="json-toggle"></a>';
61
- }
62
- html += json2html(json[i], options);
63
- // Add comma if item is not last
64
- if (i < json.length - 1) {
65
- html += ',';
66
- }
67
- html += '</li>';
68
- }
69
- html += '</ol>]';
70
- } else {
71
- html += '[]';
72
- }
73
- } else if (typeof json === 'object') {
74
- var keyCount = Object.keys(json).length;
75
- if (keyCount > 0) {
76
- html += '{<ul class="json-dict">';
77
- for (var key in json) {
78
- if (Object.prototype.hasOwnProperty.call(json, key)) {
79
- html += '<li>';
80
- var keyRepr = options.withQuotes ?
81
- '<span class="json-string">"' + key + '"</span>' : key;
82
- // Add toggle button if item is collapsable
83
- if (isCollapsable(json[key])) {
84
- html += '<a href class="json-toggle">' + keyRepr + '</a>';
85
- } else {
86
- html += keyRepr;
87
- }
88
- html += ': ' + json2html(json[key], options);
89
- // Add comma if item is not last
90
- if (--keyCount > 0) {
91
- html += ',';
92
- }
93
- html += '</li>';
94
- }
95
- }
96
- html += '</ul>}';
97
- } else {
98
- html += '{}';
99
- }
100
- }
101
- return html;
102
- }
103
-
104
- /**
105
- * jQuery plugin method
106
- * @param json: a javascript object
107
- * @param options: an optional options hash
108
- */
109
- $.fn.jsonViewer = function(json, options) {
110
- // Merge user options with default options
111
- options = Object.assign({}, {
112
- collapsed: false,
113
- rootCollapsable: true,
114
- withQuotes: false,
115
- withLinks: true
116
- }, options);
117
-
118
- // jQuery chaining
119
- return this.each(function() {
120
-
121
- // Transform to HTML
122
- var html = json2html(json, options);
123
- if (options.rootCollapsable && isCollapsable(json)) {
124
- html = '<a href class="json-toggle"></a>' + html;
125
- }
126
-
127
- // Insert HTML in target DOM element
128
- $(this).html(html);
129
- $(this).addClass('json-document');
130
-
131
- // Bind click on toggle buttons
132
- $(this).off('click');
133
- $(this).on('click', 'a.json-toggle', function() {
134
- var target = $(this).toggleClass('collapsed').siblings('ul.json-dict, ol.json-array');
135
- target.toggle();
136
- if (target.is(':visible')) {
137
- target.siblings('.json-placeholder').remove();
138
- } else {
139
- var count = target.children('li').length;
140
- var placeholder = count + (count > 1 ? ' items' : ' item');
141
- target.after('<a href class="json-placeholder">' + placeholder + '</a>');
142
- }
143
- return false;
144
- });
145
-
146
- // Simulate click on toggle button when placeholder is clicked
147
- $(this).on('click', 'a.json-placeholder', function() {
148
- $(this).siblings('a.json-toggle').click();
149
- return false;
150
- });
151
-
152
- if (options.collapsed == true) {
153
- // Trigger click to collapse all nodes
154
- $(this).find('a.json-toggle').click();
155
- }
156
- });
157
- };
158
- })(jQuery);
@@ -1,57 +0,0 @@
1
- /* Root element */
2
- .json-document {
3
- padding: 1em 2em;
4
- }
5
-
6
- /* Syntax highlighting for JSON objects */
7
- ul.json-dict, ol.json-array {
8
- list-style-type: none;
9
- margin: 0 0 0 1px;
10
- border-left: 1px dotted #ccc;
11
- padding-left: 2em;
12
- }
13
- .json-string {
14
- color: #0B7500;
15
- }
16
- .json-literal {
17
- color: #1A01CC;
18
- font-weight: bold;
19
- }
20
-
21
- /* Toggle button */
22
- a.json-toggle {
23
- position: relative;
24
- color: inherit;
25
- text-decoration: none;
26
- }
27
- a.json-toggle:focus {
28
- outline: none;
29
- }
30
- a.json-toggle:before {
31
- font-size: 1.1em;
32
- color: #c0c0c0;
33
- content: "\25BC"; /* down arrow */
34
- position: absolute;
35
- display: inline-block;
36
- width: 1em;
37
- text-align: center;
38
- line-height: 1em;
39
- left: -1.2em;
40
- }
41
- a.json-toggle:hover:before {
42
- color: #aaa;
43
- }
44
- a.json-toggle.collapsed:before {
45
- /* Use rotated down arrow, prevents right arrow appearing smaller than down arrow in some browsers */
46
- transform: rotate(-90deg);
47
- }
48
-
49
- /* Collapsable placeholder links */
50
- a.json-placeholder {
51
- color: #aaa;
52
- padding: 0 1em;
53
- text-decoration: none;
54
- }
55
- a.json-placeholder:hover {
56
- text-decoration: underline;
57
- }