systemd-watchdog 0.9.1__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.
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 Aaron D. Marasco
|
|
4
|
+
Copyright (c) 2019 Stig Otnes Kolstad
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: systemd-watchdog
|
|
3
|
+
Version: 0.9.1
|
|
4
|
+
Summary: sd_notify and sd_watchdog_enabled functionality for writing Python daemons under systemd
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: sd_notify,sd_watchdog_enabled,systemd,watchdog
|
|
7
|
+
Author: AaronDMarasco
|
|
8
|
+
Author-email: systemd-watchdog@marascos.net
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Project-URL: Homepage, http://github.com/AaronDMarasco/systemd-watchdog/
|
|
15
|
+
Project-URL: Issues, http://github.com/AaronDMarasco/systemd-watchdog/issues
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# systemd_watchdog
|
|
19
|
+
|
|
20
|
+

|
|
21
|
+

|
|
22
|
+

|
|
23
|
+

|
|
24
|
+

|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
sd_notify(3) and sd_watchdog_enabled(3) client functionality implemented in Python 3 for writing Python daemons
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
```
|
|
31
|
+
$ pip install systemd-watchdog
|
|
32
|
+
```
|
|
33
|
+
or
|
|
34
|
+
```
|
|
35
|
+
$ git clone ...
|
|
36
|
+
$ make install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
### `example.py`
|
|
41
|
+
```python
|
|
42
|
+
import systemd_watchdog
|
|
43
|
+
import time
|
|
44
|
+
|
|
45
|
+
wd = systemd_watchdog.watchdog()
|
|
46
|
+
if not wd.is_enabled:
|
|
47
|
+
# Then it's probably not running is systemd with watchdog enabled
|
|
48
|
+
raise Exception("Watchdog not enabled")
|
|
49
|
+
|
|
50
|
+
# Report a status message
|
|
51
|
+
wd.status("Starting my service...")
|
|
52
|
+
time.sleep(3)
|
|
53
|
+
|
|
54
|
+
# Report that the program init is complete
|
|
55
|
+
wd.ready()
|
|
56
|
+
wd.status("Init is complete...")
|
|
57
|
+
wd.notify()
|
|
58
|
+
time.sleep(3)
|
|
59
|
+
|
|
60
|
+
# Compute time between notifications
|
|
61
|
+
timeout_half_sec = int(float(wd.timeout) / 2e6) # Convert us->s and half that
|
|
62
|
+
wd.status("Sleeping and then notify x 3")
|
|
63
|
+
time.sleep(timeout_half_sec)
|
|
64
|
+
wd.notify()
|
|
65
|
+
time.sleep(timeout_half_sec)
|
|
66
|
+
wd.notify()
|
|
67
|
+
time.sleep(timeout_half_sec)
|
|
68
|
+
wd.notify()
|
|
69
|
+
wd.status("Spamming loop - should only see 2-3 notifications")
|
|
70
|
+
t = float(0)
|
|
71
|
+
while t <= 4*timeout_half_sec:
|
|
72
|
+
time.sleep(0.05)
|
|
73
|
+
wd.ping()
|
|
74
|
+
t += 0.05
|
|
75
|
+
|
|
76
|
+
# Report an error to the service manager
|
|
77
|
+
wd.notify_error("An irrecoverable error occured!")
|
|
78
|
+
# The service manager will probably kill the program here
|
|
79
|
+
time.sleep(3)
|
|
80
|
+
```
|
|
81
|
+
### Quick Example
|
|
82
|
+
In one terminal:
|
|
83
|
+
`socat unix-recv:/tmp/tempo.sock -`
|
|
84
|
+
|
|
85
|
+
In another terminal:
|
|
86
|
+
`NOTIFY_SOCKET=/tmp/tempo.sock WATCHDOG_USEC=5000000 python example.py`
|
|
87
|
+
|
|
88
|
+
Expected output (in first terminal):
|
|
89
|
+
```
|
|
90
|
+
STATUS=Starting my service...
|
|
91
|
+
READY=1
|
|
92
|
+
STATUS=Init is complete...
|
|
93
|
+
WATCHDOG=1
|
|
94
|
+
STATUS=Sleeping and then notify x 3
|
|
95
|
+
WATCHDOG=1
|
|
96
|
+
WATCHDOG=1
|
|
97
|
+
WATCHDOG=1
|
|
98
|
+
STATUS=Spamming loop - should only see 2-3 notifications
|
|
99
|
+
WATCHDOG=1
|
|
100
|
+
WATCHDOG=1
|
|
101
|
+
WATCHDOG=1
|
|
102
|
+
STATUS=An irrecoverable error occured!
|
|
103
|
+
WATCHDOG=trigger
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Public Interface
|
|
107
|
+
### `systemd_watchdog.watchdog` - commonly used properties and methods
|
|
108
|
+
#### `ping`
|
|
109
|
+
The only method required for the simplest implementation; combines `notify_due` with `notify()` to _only_ send "alive" notifications at reasonable intervals.
|
|
110
|
+
|
|
111
|
+
Returns boolean indicating if a message was sent or not.
|
|
112
|
+
|
|
113
|
+
#### `beat`
|
|
114
|
+
Alias for `ping()` if you prefer heartbeat terminology.
|
|
115
|
+
|
|
116
|
+
#### `ready()`
|
|
117
|
+
Report ready service state, _i.e._ completed init (only needed with `Type=notify`).
|
|
118
|
+
|
|
119
|
+
#### `status(msg)`
|
|
120
|
+
Send a service status message.
|
|
121
|
+
|
|
122
|
+
### `systemd_watchdog.watchdog` - less-used properties and methods
|
|
123
|
+
#### `is_enabled`
|
|
124
|
+
Boolean property stating whether watchdog capability is enabled.
|
|
125
|
+
|
|
126
|
+
#### `timeout`
|
|
127
|
+
Property reporting the number of microseconds (int) before process will be killed.
|
|
128
|
+
|
|
129
|
+
It is recommended that you call `notify()` once roughly half of this interval has passed (see `notify_due`).
|
|
130
|
+
|
|
131
|
+
#### `timeout_td`
|
|
132
|
+
Property that is the same as `timeout` but presented as `datetime.timedelta` for easier manipulation.
|
|
133
|
+
|
|
134
|
+
#### `notify_due`
|
|
135
|
+
Boolean property indicating more than half of the watchdog interval has passed since last update.
|
|
136
|
+
|
|
137
|
+
#### `notify()`
|
|
138
|
+
Report a healthy service state. Other calls, _e.g._ `status()` do *not* reset the watchdog.
|
|
139
|
+
|
|
140
|
+
#### `notify_error(msg=None)`
|
|
141
|
+
Report an error to the watchdog manager. This program will likely be killed upon receipt.
|
|
142
|
+
|
|
143
|
+
If `msg` is provided, it will be reported as a status message prior to the error.
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
## History
|
|
147
|
+
Aaron D. Marasco May 2020
|
|
148
|
+
* Forked from the sd-notify project <https://github.com/stigok/sd-notify>
|
|
149
|
+
* Additional contributors can be found in GitHub repository history
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
See `LICENSE` file
|
|
154
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
systemd_watchdog.py,sha256=856svx5UdOmq49Oi2CLIiiujC7XXx3pnPMhMR0Q3Pcg,3010
|
|
2
|
+
systemd_watchdog-0.9.1.dist-info/LICENSE,sha256=kNXlp3uKgbeOGdvGLoWJnhuOEh6gbTZC3twX7P5uTmI,1111
|
|
3
|
+
systemd_watchdog-0.9.1.dist-info/METADATA,sha256=xfsmkXQrkCT2JkbTJ0GqOaAkmR5KL1WYK9126-xrWAw,4531
|
|
4
|
+
systemd_watchdog-0.9.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
5
|
+
systemd_watchdog-0.9.1.dist-info/RECORD,,
|
systemd_watchdog.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
sd_notify(3) and sd_watchdog_enabled(3) client functionality implemented in Python 3 for writing Python daemons
|
|
3
|
+
|
|
4
|
+
See README.md and examples/daemon.py for usage
|
|
5
|
+
"""
|
|
6
|
+
from datetime import timedelta, datetime
|
|
7
|
+
import os
|
|
8
|
+
import socket
|
|
9
|
+
|
|
10
|
+
class watchdog():
|
|
11
|
+
_default_timeout = timedelta(hours=24) # Safe value to do math with; if disabled won't matter
|
|
12
|
+
def __init__(self, sock=None, addr=None):
|
|
13
|
+
self._address = addr or os.getenv("NOTIFY_SOCKET")
|
|
14
|
+
self._lastcall = datetime.fromordinal(1)
|
|
15
|
+
self._socket = sock or socket.socket(family=socket.AF_UNIX,
|
|
16
|
+
type=socket.SOCK_DGRAM)
|
|
17
|
+
self._timeout_td = self._default_timeout
|
|
18
|
+
|
|
19
|
+
# Note this fix is untested in a live system; https://unix.stackexchange.com/q/206386
|
|
20
|
+
if self._address and self._address[0] == '@':
|
|
21
|
+
self._address = '\0'+self._address[1:]
|
|
22
|
+
|
|
23
|
+
# Check for our timeout
|
|
24
|
+
if self._address:
|
|
25
|
+
wtime = os.getenv("WATCHDOG_USEC")
|
|
26
|
+
wpid = os.getenv("WATCHDOG_PID")
|
|
27
|
+
if wtime and wtime.isdigit():
|
|
28
|
+
if wpid is None or (wpid.isdigit() and (wpid == str(os.getpid()))):
|
|
29
|
+
self._timeout_td = timedelta(microseconds=int(wtime))
|
|
30
|
+
|
|
31
|
+
def _send(self, msg):
|
|
32
|
+
"""Send string as bytes on the notification socket"""
|
|
33
|
+
if self.is_enabled:
|
|
34
|
+
self._socket.sendto(msg.encode(), self._address)
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def is_enabled(self):
|
|
38
|
+
"""Property indicating whether watchdog is enabled"""
|
|
39
|
+
return bool(self._address)
|
|
40
|
+
|
|
41
|
+
def notify(self):
|
|
42
|
+
"""Send healthy notification to systemd"""
|
|
43
|
+
self._lastcall = datetime.now()
|
|
44
|
+
self._send("WATCHDOG=1\n")
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def notify_due(self):
|
|
48
|
+
return (datetime.now() - self._lastcall) >= (self._timeout_td / 2)
|
|
49
|
+
|
|
50
|
+
def notify_error(self, msg=None):
|
|
51
|
+
"""
|
|
52
|
+
Report a watchdog error. This program will likely be killed by the service manager.
|
|
53
|
+
|
|
54
|
+
If msg is provided, it will be reported as a status message prior to the error.
|
|
55
|
+
"""
|
|
56
|
+
if msg:
|
|
57
|
+
self.status(msg)
|
|
58
|
+
self._send("WATCHDOG=trigger\n")
|
|
59
|
+
|
|
60
|
+
def ping(self):
|
|
61
|
+
"""Send healthy notification to systemd if prudent to"""
|
|
62
|
+
if self.notify_due:
|
|
63
|
+
self.notify()
|
|
64
|
+
return True
|
|
65
|
+
return False
|
|
66
|
+
beat = ping # alias for ping if you prefer heartbeat terminology
|
|
67
|
+
|
|
68
|
+
def ready(self):
|
|
69
|
+
"""Report ready service state, i.e. completed init"""
|
|
70
|
+
self._send("READY=1\n")
|
|
71
|
+
|
|
72
|
+
def status(self, msg):
|
|
73
|
+
"""Send a free-form service status message"""
|
|
74
|
+
self._send("STATUS=%s\n" % (msg,))
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def timeout(self):
|
|
78
|
+
"""Report the watchdog window in microseconds as int (cf. sd_watchdog_enabled(3) )"""
|
|
79
|
+
return int(self._timeout_td/timedelta(microseconds=1))
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def timeout_td(self):
|
|
83
|
+
"""Report the watchdog window as datetime.timedelta (cf. sd_watchdog_enabled(3) )"""
|
|
84
|
+
return self._timeout_td
|