diff options
-rw-r--r-- | README.md | 50 | ||||
-rw-r--r-- | doc/config-fmt.md | 16 | ||||
-rw-r--r-- | src/conf/py-sample/sample.jsonc | 6 | ||||
-rw-r--r-- | src/palhm/__init__.py | 41 |
4 files changed, 80 insertions, 33 deletions
@@ -202,7 +202,7 @@ reduce IO seek time. } ``` -On start, the objects in "root" and "http" groups will be built simultanesouly. +On start, the objects in "root" and "http" groups will be built simultaneously. On completion of all the objects in "http", the objects in the group "sql" and "ldap" will be built in order. @@ -216,24 +216,28 @@ or a rc.d script on SysVinit based systems. ```jsonc { "boot-report": { - // (REQUIRED) MUA for sending mail - /* stdout MUA - * For testing. Print contents to stdout. Doesn't actually send mail - */ + // (REQUIRED) MUA for sending mail + /* stdout MUA + * For testing. Print contents to stdout. Doesn't actually send mail + */ // "mua": "stdout", "mua": "mailx", // mailx command MUA - // (REQUIRED) List of recipients + // (REQUIRED) List of recipients "mail-to": [ "root" ], - // The mail subject (optional) + // The mail subject (optional) "subject": "Custom Boot Report Subject from {hostname}", - /* - * The mail body header(leading yaml comments). Use line break(\n) for - * multi-line header (optional) - */ + /* + * The mail body header(leading yaml comments). Use line break(\n) for + * multi-line header (optional) + */ "header": "Custom header content with {hostname} substitution.", "uptime-since": true, // Include output of `uptime --since` (optional) "uptime": true, // Include output of `uptime -p` (optional) - "bootid": true // Include kernel boot_id (optional) + "bootid": true, // Include kernel boot_id (optional) + // Wait for systemd to finish boot up process (optional) + "boot-wait": "systemd", + // Wait 5 seconds before sending mail + "delay": 5 } } ``` @@ -244,17 +248,17 @@ the `aws` module. ```jsonc { - "modules": [ "aws" ], - "boot-report": { - "mua": "aws-sns", - "mua-param": { - // "profile": "default", - // If the profile does not have the default region. - "region": "us-east-1" - }, - // Target ARNs. Any ARN recognised by the SNS can be used. - "mail-to": [ "arn:aws:sns:us-east-1:NNNNNNNNNNNN:topic-test" ] - } + "modules": [ "aws" ], + "boot-report": { + "mua": "aws-sns", + "mua-param": { + // "profile": "default", + // If the profile does not have the default region. + "region": "us-east-1" + }, + // Target ARNs. Any ARN recognised by the SNS can be used. + "mail-to": [ "arn:aws:sns:us-east-1:NNNNNNNNNNNN:topic-test" ] + } } ``` diff --git a/doc/config-fmt.md b/doc/config-fmt.md index 585f37a..36987bd 100644 --- a/doc/config-fmt.md +++ b/doc/config-fmt.md @@ -5,9 +5,9 @@ them to json by an external command. PALHM distinguishes between these two format by the file name extension. The conversion only occurs when the name of the config file ends with `.jsonc`. -To support the IEEE754 infinity, the accepated data types for some values are -both string and number. The former will be parsed by the relevant type class -before they are processed. +To support the IEEE754 infinity, the data types used for some values are both +string and number. The former will be parsed by the relevant type class before +they are processed. ## Structure The format of the object feature table. @@ -148,8 +148,8 @@ is required, simply increase the verbosity with the `-v` option. * C: comparator. One of <, <=, >, >= or ==. Defaults to == * Examples * ">=0": ignore exit code(always success) - * "<2" or "0-1": accept exit code 0 and 1 - * "1": accept exit code 1 only + * "<2" or "0-1": accept exit code 0 and 1 + * "1": accept exit code 1 only * "vl-stderr": verbosity level of stderr from the process. Defaults to 1 * "vl-stdout": verbosity level of stdout from the process. Defaults to 3 @@ -164,7 +164,7 @@ is required, simply increase the verbosity with the `-v` option. "argv": [ "/bin/pgp", "-e", "-r", "backup", "--compress-algo", "none" ], "env": { "LC_ALL": "C", - "GNUPGHOME": "~/gnupg" + "GNUPGHOME": "~/gnupg" }, "ec": "==0", "vl-stderr": 1, @@ -396,6 +396,10 @@ header contents of the yaml document for humans. * "uptime-since": include output of `uptime --since` * "uptime": include output of `uptime -p` * "bootid": include boot_id(`/proc/sys/kernel/random/boot_id`) +* "boot-wait": boot wait backend + * "systemd": wait for systemd to finish boot up +* "delay": the number of seconds to wait before sending mail. Finite float equal + to or greater than zero #### Content Substitution Variables * {hostname}: The hostname. See diff --git a/src/conf/py-sample/sample.jsonc b/src/conf/py-sample/sample.jsonc index b519fdf..2a349c1 100644 --- a/src/conf/py-sample/sample.jsonc +++ b/src/conf/py-sample/sample.jsonc @@ -10,12 +10,14 @@ "mua-param": { "int-opts": [ "smtp=localhost" ] }, - "mail-to": [ "root" ] + "mail-to": [ "root" ], // "subject": "Custom Boot Report Subject from {hostname}", // "header": "Custom header content with {hostname} substitution." // "uptime-since": true, // "uptime": true, - // "bootid": true + // "bootid": true, + // "boot-wait": "systemd", + "delay": 5 }, "tasks": [ { diff --git a/src/palhm/__init__.py b/src/palhm/__init__.py index 75b1b21..b9093fb 100644 --- a/src/palhm/__init__.py +++ b/src/palhm/__init__.py @@ -21,6 +21,7 @@ import platform import resource import sys import time +import math from .exceptions import InvalidConfigError import json @@ -178,7 +179,22 @@ class BootReport: "This is a boot report from {hostname}.\n" + "More details as follows.") + def _bootwait_systemd (): + argv = [ + "/usr/bin/systemctl", + "is-system-running", + "--wait" + ] + with subprocess.Popen( + argv, + stdin = subprocess.DEVNULL, + stdout = subprocess.DEVNULL) as p: + ec = p.wait() + if ec != 0: + raise ChildProcessError(p) + def __init__ (self, ctx: GlobalContext, jobj: dict): + def do_nothing(): pass self.yaml = import_module("yaml") self.mua = ctx.muas[jobj["mua"]](jobj.get("mua-param", {})) @@ -188,6 +204,20 @@ class BootReport: self.uptime_since = jobj.get("uptime-since", True) self.uptime = jobj.get("uptime", True) self.bootid = jobj.get("boot-id", True) + self.bootwait = jobj.get("boot-wait") + self.delay = float(jobj.get("delay", 0)) + + if self.bootwait is None: + self.bootwait_f = do_nothing + else: + if self.bootwait == "systemd": + self.bootwait_f = BootReport._bootwait_systemd + else: + raise KeyError(self.bootwait) + + if not math.isfinite(self.delay) or self.delay < 0: + raise ValueError(self.delay) + def get_subject (self) -> str: return BootReport._do_format(self.subject) @@ -231,6 +261,9 @@ class BootReport: yield self.yaml.dump(root_doc) def do_send (self, ctx: GlobalContext) -> int: + self.bootwait_f() + time.sleep(self.delay) + return self.mua.do_send( ctx = ctx, recipients = self.recipients, @@ -244,14 +277,18 @@ subject: {subject} header: {header} uptime_since: {uptime_since} uptime: {uptime} -bootid: {bootid}'''.format( +bootid: {bootid} +bootwait: {bootwait} +delay: {delay}'''.format( mua = str(self.mua).replace("\n", "\n\t"), recipients = "".join([ "\n\t- " + repr(i) for i in self.recipients]), subject = repr(self.subject), header = repr(self.header), uptime_since = self.uptime_since, uptime = self.uptime, - bootid = self.bootid) + bootid = self.bootid, + bootwait = self.bootwait, + delay = self.delay) class Runnable (ABC): @abstractmethod |