Init commit for standalone enodebd
Change-Id: I88eeef5135dd7ba8551ddd9fb6a0695f5325337b
diff --git a/common/sdwatchdog.py b/common/sdwatchdog.py
new file mode 100644
index 0000000..2eb4e52
--- /dev/null
+++ b/common/sdwatchdog.py
@@ -0,0 +1,101 @@
+"""
+Copyright 2020 The Magma Authors.
+
+This source code is licensed under the BSD-style license found in the
+LICENSE file in the root directory of this source tree.
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+# pylint: disable=W0223
+
+import asyncio
+import logging
+import os
+import time
+from typing import List, Optional, Set, cast
+
+import systemd.daemon
+from common.job import Job
+
+
+class SDWatchdogTask(Job):
+ pass
+
+
+class SDWatchdog(object):
+ """
+ This is a task that utilizes systemd watchdog functionality.
+
+ SDWatchdog() task is started automatically in run in common/service.run(),
+ where it will look at every task in the loop to see if it is a subclass
+ of SDWatchdogTask
+
+ To enable systemd watchdog, add "WatchdogSec=60" in the [Service] section
+ of the systemd service file.
+ """
+
+ def __init__(
+ self,
+ tasks: Optional[List[SDWatchdogTask]],
+ update_status: bool = False, # update systemd status field
+ period: float = 30,
+ ) -> None:
+ """
+ coroutine that will check each task's time_last_completed_loop to
+ ensure that it was updated every in the last timeout_s seconds.
+
+ Perform check of each service every period seconds.
+ """
+
+ self.tasks = cast(Set[SDWatchdogTask], set())
+ self.update_status = update_status
+ self.period = period
+
+ if tasks:
+ for t in tasks:
+ if not issubclass(type(t), SDWatchdogTask):
+ logging.warning(
+ "'%s' is not a 'SDWatchdogTask', skipping", repr(t),
+ )
+ else:
+ self.tasks.add(t)
+
+ @staticmethod
+ def has_notify() -> bool:
+ return os.getenv("NOTIFY_SOCKET") is not None
+
+ async def run(self) -> None:
+ """
+ check tasks every self.period seconds to see if they have completed
+ a loop within the last 'timeout' seconds. If so, sd notify WATCHDOG=1
+ """
+ if not self.has_notify():
+ logging.warning("Missing 'NOTIFY_SOCKET' for SDWatchdog, skipping")
+ return
+ logging.info("Starting SDWatchdog...")
+ while True:
+ current_time = time.time()
+ anyStuck = False
+ for task in self.tasks:
+ if task.not_completed(current_time):
+ errmsg = "SDWatchdog service '%s' has not completed %s" % (
+ repr(task), time.asctime(time.gmtime(current_time)),
+ )
+ if self.update_status:
+ systemd.daemon.notify("STATUS=%s\n" % errmsg)
+ logging.info(errmsg)
+ anyStuck = True
+
+ if not anyStuck:
+ systemd.daemon.notify(
+ 'STATUS=SDWatchdog success %s\n' %
+ time.asctime(time.gmtime(current_time)),
+ )
+ systemd.daemon.notify("WATCHDOG=1")
+ systemd.daemon.notify("READY=1") # only active if Type=notify
+
+ await asyncio.sleep(self.period)