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.
Files changed (104) hide show
  1. {ducted-2.2 → ducted-2.2.1}/PKG-INFO +1 -1
  2. {ducted-2.2 → ducted-2.2.1}/duct/outputs/nats.py +2 -1
  3. {ducted-2.2 → ducted-2.2.1}/duct/protocol/senml.py +23 -17
  4. {ducted-2.2 → ducted-2.2.1}/duct/tests/test_outputs.py +17 -1
  5. {ducted-2.2 → ducted-2.2.1}/duct/tests/test_protocol.py +17 -11
  6. {ducted-2.2 → ducted-2.2.1}/ducted.egg-info/PKG-INFO +1 -1
  7. {ducted-2.2 → ducted-2.2.1}/pyproject.toml +1 -1
  8. {ducted-2.2 → ducted-2.2.1}/LICENSE +0 -0
  9. {ducted-2.2 → ducted-2.2.1}/README.md +0 -0
  10. {ducted-2.2 → ducted-2.2.1}/docs/conf.py +0 -0
  11. {ducted-2.2 → ducted-2.2.1}/docs/update_modules.py +0 -0
  12. {ducted-2.2 → ducted-2.2.1}/duct/__init__.py +0 -0
  13. {ducted-2.2 → ducted-2.2.1}/duct/__main__.py +0 -0
  14. {ducted-2.2 → ducted-2.2.1}/duct/aggregators.py +0 -0
  15. {ducted-2.2 → ducted-2.2.1}/duct/configuration.py +0 -0
  16. {ducted-2.2 → ducted-2.2.1}/duct/ihateprotobuf/__init__.py +0 -0
  17. {ducted-2.2 → ducted-2.2.1}/duct/ihateprotobuf/proto_pb2.py +0 -0
  18. {ducted-2.2 → ducted-2.2.1}/duct/interfaces.py +0 -0
  19. {ducted-2.2 → ducted-2.2.1}/duct/logs/__init__.py +0 -0
  20. {ducted-2.2 → ducted-2.2.1}/duct/logs/follower.py +0 -0
  21. {ducted-2.2 → ducted-2.2.1}/duct/logs/parsers.py +0 -0
  22. {ducted-2.2 → ducted-2.2.1}/duct/objects.py +0 -0
  23. {ducted-2.2 → ducted-2.2.1}/duct/outputs/__init__.py +0 -0
  24. {ducted-2.2 → ducted-2.2.1}/duct/outputs/bosun.py +0 -0
  25. {ducted-2.2 → ducted-2.2.1}/duct/outputs/elasticsearch.py +0 -0
  26. {ducted-2.2 → ducted-2.2.1}/duct/outputs/graphite.py +0 -0
  27. {ducted-2.2 → ducted-2.2.1}/duct/outputs/influxdb3.py +0 -0
  28. {ducted-2.2 → ducted-2.2.1}/duct/outputs/logger.py +0 -0
  29. {ducted-2.2 → ducted-2.2.1}/duct/outputs/null.py +0 -0
  30. {ducted-2.2 → ducted-2.2.1}/duct/outputs/opentsdb.py +0 -0
  31. {ducted-2.2 → ducted-2.2.1}/duct/outputs/prometheus.py +0 -0
  32. {ducted-2.2 → ducted-2.2.1}/duct/outputs/riemann.py +0 -0
  33. {ducted-2.2 → ducted-2.2.1}/duct/protocol/__init__.py +0 -0
  34. {ducted-2.2 → ducted-2.2.1}/duct/protocol/elasticsearch.py +0 -0
  35. {ducted-2.2 → ducted-2.2.1}/duct/protocol/i2c.py +0 -0
  36. {ducted-2.2 → ducted-2.2.1}/duct/protocol/icmp.py +0 -0
  37. {ducted-2.2 → ducted-2.2.1}/duct/protocol/influxdb3.py +0 -0
  38. {ducted-2.2 → ducted-2.2.1}/duct/protocol/opentsdb.py +0 -0
  39. {ducted-2.2 → ducted-2.2.1}/duct/protocol/riemann.py +0 -0
  40. {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/__init__.py +0 -0
  41. {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/protocol/__init__.py +0 -0
  42. {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/protocol/counters.py +0 -0
  43. {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/protocol/flows.py +0 -0
  44. {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/protocol/protocol.py +0 -0
  45. {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/protocol/utils.py +0 -0
  46. {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/protocol/xdr.py +0 -0
  47. {ducted-2.2 → ducted-2.2.1}/duct/protocol/sflow/server.py +0 -0
  48. {ducted-2.2 → ducted-2.2.1}/duct/protocol/ssh.py +0 -0
  49. {ducted-2.2 → ducted-2.2.1}/duct/service.py +0 -0
  50. {ducted-2.2 → ducted-2.2.1}/duct/sources/__init__.py +0 -0
  51. {ducted-2.2 → ducted-2.2.1}/duct/sources/apache.py +0 -0
  52. {ducted-2.2 → ducted-2.2.1}/duct/sources/database/__init__.py +0 -0
  53. {ducted-2.2 → ducted-2.2.1}/duct/sources/database/elasticsearch.py +0 -0
  54. {ducted-2.2 → ducted-2.2.1}/duct/sources/database/memcache.py +0 -0
  55. {ducted-2.2 → ducted-2.2.1}/duct/sources/database/postgresql.py +0 -0
  56. {ducted-2.2 → ducted-2.2.1}/duct/sources/docker.py +0 -0
  57. {ducted-2.2 → ducted-2.2.1}/duct/sources/generator.py +0 -0
  58. {ducted-2.2 → ducted-2.2.1}/duct/sources/haproxy.py +0 -0
  59. {ducted-2.2 → ducted-2.2.1}/duct/sources/linux/__init__.py +0 -0
  60. {ducted-2.2 → ducted-2.2.1}/duct/sources/linux/basic.py +0 -0
  61. {ducted-2.2 → ducted-2.2.1}/duct/sources/linux/ipsec.py +0 -0
  62. {ducted-2.2 → ducted-2.2.1}/duct/sources/linux/process.py +0 -0
  63. {ducted-2.2 → ducted-2.2.1}/duct/sources/linux/sensors.py +0 -0
  64. {ducted-2.2 → ducted-2.2.1}/duct/sources/mail.py +0 -0
  65. {ducted-2.2 → ducted-2.2.1}/duct/sources/media/__init__.py +0 -0
  66. {ducted-2.2 → ducted-2.2.1}/duct/sources/media/libav.py +0 -0
  67. {ducted-2.2 → ducted-2.2.1}/duct/sources/munin.py +0 -0
  68. {ducted-2.2 → ducted-2.2.1}/duct/sources/nats.py +0 -0
  69. {ducted-2.2 → ducted-2.2.1}/duct/sources/network.py +0 -0
  70. {ducted-2.2 → ducted-2.2.1}/duct/sources/nginx.py +0 -0
  71. {ducted-2.2 → ducted-2.2.1}/duct/sources/postfix.py +0 -0
  72. {ducted-2.2 → ducted-2.2.1}/duct/sources/power/__init__.py +0 -0
  73. {ducted-2.2 → ducted-2.2.1}/duct/sources/power/nut.py +0 -0
  74. {ducted-2.2 → ducted-2.2.1}/duct/sources/power/raritan_px.py +0 -0
  75. {ducted-2.2 → ducted-2.2.1}/duct/sources/python/__init__.py +0 -0
  76. {ducted-2.2 → ducted-2.2.1}/duct/sources/python/uwsgi.py +0 -0
  77. {ducted-2.2 → ducted-2.2.1}/duct/sources/rabbitmq.py +0 -0
  78. {ducted-2.2 → ducted-2.2.1}/duct/sources/redis.py +0 -0
  79. {ducted-2.2 → ducted-2.2.1}/duct/sources/riak.py +0 -0
  80. {ducted-2.2 → ducted-2.2.1}/duct/sources/riemann.py +0 -0
  81. {ducted-2.2 → ducted-2.2.1}/duct/sources/sensors/__init__.py +0 -0
  82. {ducted-2.2 → ducted-2.2.1}/duct/sources/sensors/environment/__init__.py +0 -0
  83. {ducted-2.2 → ducted-2.2.1}/duct/sources/sensors/environment/ds18b20.py +0 -0
  84. {ducted-2.2 → ducted-2.2.1}/duct/sources/sensors/environment/mpl115.py +0 -0
  85. {ducted-2.2 → ducted-2.2.1}/duct/sources/sflow.py +0 -0
  86. {ducted-2.2 → ducted-2.2.1}/duct/sources/snmp.py +0 -0
  87. {ducted-2.2 → ducted-2.2.1}/duct/sources/unbound.py +0 -0
  88. {ducted-2.2 → ducted-2.2.1}/duct/tests/__init__.py +0 -0
  89. {ducted-2.2 → ducted-2.2.1}/duct/tests/globs.py +0 -0
  90. {ducted-2.2 → ducted-2.2.1}/duct/tests/helpers.py +0 -0
  91. {ducted-2.2 → ducted-2.2.1}/duct/tests/test_duct.py +0 -0
  92. {ducted-2.2 → ducted-2.2.1}/duct/tests/test_logs.py +0 -0
  93. {ducted-2.2 → ducted-2.2.1}/duct/tests/test_service.py +0 -0
  94. {ducted-2.2 → ducted-2.2.1}/duct/tests/test_sflow.py +0 -0
  95. {ducted-2.2 → ducted-2.2.1}/duct/tests/test_sources.py +0 -0
  96. {ducted-2.2 → ducted-2.2.1}/duct/tests/test_ssh.py +0 -0
  97. {ducted-2.2 → ducted-2.2.1}/duct/tests/test_utils.py +0 -0
  98. {ducted-2.2 → ducted-2.2.1}/duct/utils.py +0 -0
  99. {ducted-2.2 → ducted-2.2.1}/ducted.egg-info/SOURCES.txt +0 -0
  100. {ducted-2.2 → ducted-2.2.1}/ducted.egg-info/dependency_links.txt +0 -0
  101. {ducted-2.2 → ducted-2.2.1}/ducted.egg-info/entry_points.txt +0 -0
  102. {ducted-2.2 → ducted-2.2.1}/ducted.egg-info/requires.txt +0 -0
  103. {ducted-2.2 → ducted-2.2.1}/ducted.egg-info/top_level.txt +0 -0
  104. {ducted-2.2 → ducted-2.2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ducted
3
- Version: 2.2
3
+ Version: 2.2.1
4
4
  Summary: A monitoring agent and event processor
5
5
  Author-email: Colin Alston <colin@tamvera.com>
6
6
  License: MIT
@@ -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
- if event.state:
26
- pack.append({"n": event.service + "/state", "vs": event.state})
27
- if event.description:
28
- pack.append({"n": event.service + "/description", "vs": event.description})
29
- if event.ttl:
30
- pack.append({"n": event.service + "/ttl", "v": float(event.ttl)})
31
- if event.tags:
32
- pack.append({"n": event.service + "/tags", "vs": ",".join(event.tags)})
33
- if event.attributes:
34
- for k, v in event.attributes.items():
35
- if isinstance(v, (int, float)):
36
- pack.append({"n": f"{event.service}/attr/{k}", "v": float(v)})
37
- else:
38
- pack.append({"n": f"{event.service}/attr/{k}", "vs": str(v)})
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 == event.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 test_roundtrip_full(self):
196
+ def test_roundtrip_full_extended(self):
196
197
  ev = _make_event()
197
- rt = senml_cbor_to_event(event_to_senml_cbor(ev))
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):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ducted
3
- Version: 2.2
3
+ Version: 2.2.1
4
4
  Summary: A monitoring agent and event processor
5
5
  Author-email: Colin Alston <colin@tamvera.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ducted"
7
- version = "2.2"
7
+ version = "2.2.1"
8
8
  description = "A monitoring agent and event processor"
9
9
  license = { text = "MIT" }
10
10
  authors = [{ name = "Colin Alston", email = "colin@tamvera.com" }]
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