ducted 2.2__tar.gz → 2.2.1__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.
- {ducted-2.2 → ducted-2.2.1}/PKG-INFO +1 -1
- {ducted-2.2 → ducted-2.2.1}/duct/outputs/nats.py +2 -1
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/senml.py +23 -17
- {ducted-2.2 → ducted-2.2.1}/duct/tests/test_outputs.py +17 -1
- {ducted-2.2 → ducted-2.2.1}/duct/tests/test_protocol.py +17 -11
- {ducted-2.2 → ducted-2.2.1}/ducted.egg-info/PKG-INFO +1 -1
- {ducted-2.2 → ducted-2.2.1}/pyproject.toml +1 -1
- {ducted-2.2 → ducted-2.2.1}/LICENSE +0 -0
- {ducted-2.2 → ducted-2.2.1}/README.md +0 -0
- {ducted-2.2 → ducted-2.2.1}/docs/conf.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/docs/update_modules.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/__main__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/aggregators.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/configuration.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/ihateprotobuf/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/ihateprotobuf/proto_pb2.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/interfaces.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/logs/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/logs/follower.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/logs/parsers.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/objects.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/outputs/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/outputs/bosun.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/outputs/elasticsearch.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/outputs/graphite.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/outputs/influxdb3.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/outputs/logger.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/outputs/null.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/outputs/opentsdb.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/outputs/prometheus.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/outputs/riemann.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/elasticsearch.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/i2c.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/icmp.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/influxdb3.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/opentsdb.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/riemann.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/protocol/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/protocol/counters.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/protocol/flows.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/protocol/protocol.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/protocol/utils.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/protocol/xdr.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/server.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/protocol/ssh.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/service.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/apache.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/database/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/database/elasticsearch.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/database/memcache.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/database/postgresql.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/docker.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/generator.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/haproxy.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/linux/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/linux/basic.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/linux/ipsec.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/linux/process.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/linux/sensors.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/mail.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/media/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/media/libav.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/munin.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/nats.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/network.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/nginx.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/postfix.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/power/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/power/nut.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/power/raritan_px.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/python/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/python/uwsgi.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/rabbitmq.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/redis.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/riak.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/riemann.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/sensors/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/sensors/environment/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/sensors/environment/ds18b20.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/sensors/environment/mpl115.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/sflow.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/snmp.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/sources/unbound.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/tests/__init__.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/tests/globs.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/tests/helpers.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/tests/test_duct.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/tests/test_logs.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/tests/test_service.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/tests/test_sflow.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/tests/test_sources.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/tests/test_ssh.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/tests/test_utils.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/duct/utils.py +0 -0
- {ducted-2.2 → ducted-2.2.1}/ducted.egg-info/SOURCES.txt +0 -0
- {ducted-2.2 → ducted-2.2.1}/ducted.egg-info/dependency_links.txt +0 -0
- {ducted-2.2 → ducted-2.2.1}/ducted.egg-info/entry_points.txt +0 -0
- {ducted-2.2 → ducted-2.2.1}/ducted.egg-info/requires.txt +0 -0
- {ducted-2.2 → ducted-2.2.1}/ducted.egg-info/top_level.txt +0 -0
- {ducted-2.2 → ducted-2.2.1}/setup.cfg +0 -0
|
@@ -12,7 +12,7 @@ import nats
|
|
|
12
12
|
from nats.aio.client import Client as NATS
|
|
13
13
|
|
|
14
14
|
from duct.objects import Output, Event
|
|
15
|
-
from duct.protocol.senml import event_to_senml, event_to_senml_cbor, event_to_json
|
|
15
|
+
from duct.protocol.senml import event_to_senml, event_to_senml_cbor, event_to_senml_cbor_extended, event_to_json
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
log = logging.getLogger(__name__)
|
|
@@ -78,6 +78,7 @@ class Nats(Output):
|
|
|
78
78
|
self.transformers = {
|
|
79
79
|
"senml-json": event_to_senml,
|
|
80
80
|
"senml-cbor": event_to_senml_cbor,
|
|
81
|
+
"senml-cbor-extended": event_to_senml_cbor_extended,
|
|
81
82
|
"json": event_to_json,
|
|
82
83
|
}
|
|
83
84
|
|
|
@@ -12,7 +12,7 @@ import cbor2
|
|
|
12
12
|
from duct.objects import Event
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
def event_to_senml_pack(event: Event) -> list:
|
|
15
|
+
def event_to_senml_pack(event: Event, extended_attributes:bool=True) -> list:
|
|
16
16
|
"""Convert a Duct Event to a SenML record pack (list of dicts)."""
|
|
17
17
|
pack = [
|
|
18
18
|
{
|
|
@@ -22,20 +22,23 @@ def event_to_senml_pack(event: Event) -> list:
|
|
|
22
22
|
"v": event.metric,
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
25
|
+
|
|
26
|
+
if extended_attributes:
|
|
27
|
+
# We don't always want this extra overhead
|
|
28
|
+
if event.state:
|
|
29
|
+
pack.append({"n": event.service + "/state", "vs": event.state})
|
|
30
|
+
if event.description:
|
|
31
|
+
pack.append({"n": event.service + "/description", "vs": event.description})
|
|
32
|
+
if event.ttl:
|
|
33
|
+
pack.append({"n": event.service + "/ttl", "v": float(event.ttl)})
|
|
34
|
+
if event.tags:
|
|
35
|
+
pack.append({"n": event.service + "/tags", "vs": ",".join(event.tags)})
|
|
36
|
+
if event.attributes:
|
|
37
|
+
for k, v in event.attributes.items():
|
|
38
|
+
if isinstance(v, (int, float)):
|
|
39
|
+
pack.append({"n": f"{event.service}/attr/{k}", "v": float(v)})
|
|
40
|
+
else:
|
|
41
|
+
pack.append({"n": f"{event.service}/attr/{k}", "vs": str(v)})
|
|
39
42
|
return pack
|
|
40
43
|
|
|
41
44
|
|
|
@@ -101,9 +104,12 @@ def senml_to_event(data: bytes | str, default_ttl: float = 60.0) -> Event:
|
|
|
101
104
|
|
|
102
105
|
|
|
103
106
|
def event_to_senml_cbor(event: Event) -> bytes:
|
|
104
|
-
"""Serialise a Duct Event to SenML-CBOR bytes."""
|
|
105
|
-
return cbor2.dumps(event_to_senml_pack(event))
|
|
107
|
+
"""Serialise a Duct Event to SenML-CBOR bytes, only values."""
|
|
108
|
+
return cbor2.dumps(event_to_senml_pack(event, extended_attributes=False))
|
|
106
109
|
|
|
110
|
+
def event_to_senml_cbor_extended(event: Event) -> bytes:
|
|
111
|
+
"""Serialise a Duct Event to SenML-CBOR bytes with extended attributes."""
|
|
112
|
+
return cbor2.dumps(event_to_senml_pack(event))
|
|
107
113
|
|
|
108
114
|
def senml_cbor_to_event(data: bytes, default_ttl: float = 60.0) -> Event:
|
|
109
115
|
"""Deserialise SenML-CBOR bytes to a Duct Event."""
|
|
@@ -280,6 +280,22 @@ class TestNATSOutput:
|
|
|
280
280
|
|
|
281
281
|
assert out.nc.messages == []
|
|
282
282
|
|
|
283
|
+
@pytest.mark.asyncio
|
|
284
|
+
async def test_nats_output_cbor_ext(self, service, event):
|
|
285
|
+
nats_mod.nats = FakeNATS()
|
|
286
|
+
out = nats_mod.Nats({"format": "senml-cbor-extended"}, service)
|
|
287
|
+
await out.createClient()
|
|
288
|
+
await out.eventsReceived([event])
|
|
289
|
+
await out._tick()
|
|
290
|
+
|
|
291
|
+
topic, payload = out.nc.messages[0]
|
|
292
|
+
assert topic.endswith(".sky")
|
|
293
|
+
|
|
294
|
+
decoded = senml_cbor_to_event(payload)
|
|
295
|
+
assert decoded.service == "sky"
|
|
296
|
+
assert decoded.metric == pytest.approx(event.metric)
|
|
297
|
+
assert decoded.state == event.state
|
|
298
|
+
|
|
283
299
|
@pytest.mark.asyncio
|
|
284
300
|
async def test_nats_output_cbor(self, service, event):
|
|
285
301
|
nats_mod.nats = FakeNATS()
|
|
@@ -294,7 +310,7 @@ class TestNATSOutput:
|
|
|
294
310
|
decoded = senml_cbor_to_event(payload)
|
|
295
311
|
assert decoded.service == "sky"
|
|
296
312
|
assert decoded.metric == pytest.approx(event.metric)
|
|
297
|
-
assert decoded.state ==
|
|
313
|
+
assert decoded.state == 'ok'
|
|
298
314
|
|
|
299
315
|
@pytest.mark.asyncio
|
|
300
316
|
async def test_nats_output_json(self, service, event):
|
|
@@ -9,6 +9,7 @@ from duct.protocol.senml import (
|
|
|
9
9
|
event_to_senml,
|
|
10
10
|
senml_to_event,
|
|
11
11
|
event_to_senml_cbor,
|
|
12
|
+
event_to_senml_cbor_extended,
|
|
12
13
|
senml_cbor_to_event,
|
|
13
14
|
event_to_json,
|
|
14
15
|
json_to_event,
|
|
@@ -54,7 +55,7 @@ class TestSenMLPack:
|
|
|
54
55
|
def test_pack_secondary_records(self):
|
|
55
56
|
ev = _make_event(state="critical", description="high load",
|
|
56
57
|
tags=["prod"], hostname="h", service="cpu")
|
|
57
|
-
pack = event_to_senml_pack(ev)
|
|
58
|
+
pack = event_to_senml_pack(ev, True)
|
|
58
59
|
names = {r["n"] for r in pack}
|
|
59
60
|
assert "cpu/state" in names
|
|
60
61
|
assert "cpu/description" in names
|
|
@@ -63,39 +64,39 @@ class TestSenMLPack:
|
|
|
63
64
|
|
|
64
65
|
def test_pack_state_value(self):
|
|
65
66
|
ev = _make_event(state="warning", service="mem", hostname="h")
|
|
66
|
-
pack = event_to_senml_pack(ev)
|
|
67
|
+
pack = event_to_senml_pack(ev, True)
|
|
67
68
|
state_rec = next(r for r in pack if r.get("n") == "mem/state")
|
|
68
69
|
assert state_rec["vs"] == "warning"
|
|
69
70
|
|
|
70
71
|
def test_pack_tags_joined(self):
|
|
71
72
|
ev = _make_event(tags=["a", "b", "c"], service="s", hostname="h")
|
|
72
|
-
pack = event_to_senml_pack(ev)
|
|
73
|
+
pack = event_to_senml_pack(ev, True)
|
|
73
74
|
tags_rec = next(r for r in pack if r.get("n") == "s/tags")
|
|
74
75
|
assert tags_rec["vs"] == "a,b,c"
|
|
75
76
|
|
|
76
77
|
def test_pack_numeric_attribute(self):
|
|
77
78
|
ev = _make_event(attributes={"count": 5}, service="s", hostname="h")
|
|
78
|
-
pack = event_to_senml_pack(ev)
|
|
79
|
+
pack = event_to_senml_pack(ev, True)
|
|
79
80
|
attr_rec = next(r for r in pack if r.get("n") == "s/attr/count")
|
|
80
81
|
assert attr_rec.get("v") == 5.0
|
|
81
82
|
assert "vs" not in attr_rec
|
|
82
83
|
|
|
83
84
|
def test_pack_string_attribute(self):
|
|
84
85
|
ev = _make_event(attributes={"zone": "eu-west"}, service="s", hostname="h")
|
|
85
|
-
pack = event_to_senml_pack(ev)
|
|
86
|
+
pack = event_to_senml_pack(ev, True)
|
|
86
87
|
attr_rec = next(r for r in pack if r.get("n") == "s/attr/zone")
|
|
87
88
|
assert attr_rec.get("vs") == "eu-west"
|
|
88
89
|
assert "v" not in attr_rec
|
|
89
90
|
|
|
90
91
|
def test_pack_no_tags_when_empty(self):
|
|
91
92
|
ev = _make_event(tags=[], service="s", hostname="h")
|
|
92
|
-
pack = event_to_senml_pack(ev)
|
|
93
|
+
pack = event_to_senml_pack(ev, True)
|
|
93
94
|
names = [r.get("n") for r in pack]
|
|
94
95
|
assert "s/tags" not in names
|
|
95
96
|
|
|
96
97
|
def test_pack_no_attributes_when_none(self):
|
|
97
98
|
ev = _make_event(attributes=None, service="s", hostname="h")
|
|
98
|
-
pack = event_to_senml_pack(ev)
|
|
99
|
+
pack = event_to_senml_pack(ev, True)
|
|
99
100
|
names = [r.get("n") for r in pack]
|
|
100
101
|
assert not any("attr" in (n or "") for n in names)
|
|
101
102
|
|
|
@@ -106,7 +107,7 @@ class TestSenMLPack:
|
|
|
106
107
|
|
|
107
108
|
class TestSenMLRoundTrip:
|
|
108
109
|
def _roundtrip(self, ev):
|
|
109
|
-
return senml_pack_to_event(event_to_senml_pack(ev))
|
|
110
|
+
return senml_pack_to_event(event_to_senml_pack(ev, True))
|
|
110
111
|
|
|
111
112
|
def test_hostname_preserved(self):
|
|
112
113
|
ev = _make_event(hostname="myhost.local")
|
|
@@ -192,14 +193,19 @@ class TestSenMLCBOR:
|
|
|
192
193
|
ev = _make_event(service="temp.sensor")
|
|
193
194
|
assert senml_cbor_to_event(event_to_senml_cbor(ev)).service == "temp.sensor"
|
|
194
195
|
|
|
195
|
-
def
|
|
196
|
+
def test_roundtrip_full_extended(self):
|
|
196
197
|
ev = _make_event()
|
|
197
|
-
rt = senml_cbor_to_event(
|
|
198
|
+
rt = senml_cbor_to_event(event_to_senml_cbor_extended(ev))
|
|
198
199
|
assert rt.hostname == ev.hostname
|
|
199
200
|
assert rt.metric == pytest.approx(ev.metric)
|
|
200
201
|
assert rt.state == ev.state
|
|
201
|
-
assert rt.tags == ev.tags
|
|
202
202
|
|
|
203
|
+
def test_roundtrip_full(self):
|
|
204
|
+
ev = _make_event()
|
|
205
|
+
rt = senml_cbor_to_event(event_to_senml_cbor(ev))
|
|
206
|
+
assert rt.hostname == ev.hostname
|
|
207
|
+
assert rt.metric == pytest.approx(ev.metric)
|
|
208
|
+
assert rt.description == ""
|
|
203
209
|
|
|
204
210
|
class TestPlainJSON:
|
|
205
211
|
def test_returns_bytes(self):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|