The configuration file holds the usual values of the script. In this step, command line options allow to override them for a single execution, i.e: to check one mount point with a lower threshold.
#!/usr/bin/env python3
"""
--------------------------------------------------------------------------------
@author : Luiggi Vercotti
@date : 2026-10-08
@version : 0.5.0
@description : Monitor filesystem usage
--------------------------------------------------------------------------------
Update:
0.1.0 2026-10-08 - Luiggi Vercotti - cre: Script creation
0.2.0 2026-10-08 - Luiggi Vercotti - evo: Several mount points, log levels
0.3.0 2026-10-08 - Luiggi Vercotti - evo: Monitoring exit status
0.4.0 2026-10-08 - Luiggi Vercotti - evo: Configuration file
0.5.0 2026-10-08 - Luiggi Vercotti - evo: Command line options
--------------------------------------------------------------------------------
List of mandatory configuration parameters:
@conf:filesystems|mount_points|str|false
@conf:thresholds|warning|int|false
@conf:thresholds|critical|int|false
--------------------------------------------------------------------------------
List of execution options and argument parameters:
@args:mount-point|str|Mount points to check (default: from configuration)|+||exclude||false|false
@args:exclude|str|Mount points to skip|+||||false|false
@args:warning|int|Warning threshold in percent (default: from configuration)|1||||false|false
@args:critical|int|Critical threshold in percent (default: from configuration)|1||||false|false
--------------------------------------------------------------------------------
Functioning:
---------------
Checks the usage of each mount point against a warning and a critical
threshold and exits with a monitoring compatible exit status:
0: OK, 1: WARNING, 2: CRITICAL
Configuration:
- filesystems::mount_points: Comma separated list of mount points
- thresholds::warning: Warning threshold in percent
- thresholds::critical: Critical threshold in percent
"""
# ------------------------------------------------------------------------------
# Initialization of the environment
# ------------------------------------------------------------------------------
import sys
import shutil
import scrippy_core
from scrippy_core import logger
OK, WARNING, CRITICAL = 0, 1, 2
STATUS = {OK: "OK", WARNING: "WARNING", CRITICAL: "CRITICAL"}
# ------------------------------------------------------------------------------
# Definition of functions and classes
# ------------------------------------------------------------------------------
def get_mount_points(config, args):
if args.mount_point:
return args.mount_point
mount_points = [mp.strip() for mp in config.get("filesystems", "mount_points").split(",")]
return [mp for mp in mount_points if mp not in (args.exclude or [])]
def get_thresholds(config, args):
warning = args.warning if args.warning is not None else config.get("thresholds", "warning", "int")
critical = args.critical if args.critical is not None else config.get("thresholds", "critical", "int")
return warning, critical
def get_usage(mount_point):
logger.debug(f"[+] Checking {mount_point}")
usage = shutil.disk_usage(mount_point)
return round(usage.used * 100 / usage.total, 1)
def check(mount_points, warning, critical):
worst = OK
for mount_point in mount_points:
usage = get_usage(mount_point)
if usage >= critical:
logger.error(f" '-> {mount_point}: {usage}% [CRITICAL]")
status = CRITICAL
elif usage >= warning:
logger.warning(f" '-> {mount_point}: {usage}% [WARNING]")
status = WARNING
else:
logger.info(f" '-> {mount_point}: {usage}% [OK]")
status = OK
worst = max(worst, status)
return worst
# ------------------------------------------------------------------------------
# Main processing
# ------------------------------------------------------------------------------
def main():
with scrippy_core.ScriptContext(retention=100) as _context:
config = _context.config
args = _context.args
warning, critical = get_thresholds(config, args)
logger.log_section("disk_watch", f"Thresholds: warning={warning}% critical={critical}%")
status = check(get_mount_points(config, args), warning, critical)
logger.info(f"[+] Status: {STATUS[status]}")
if status != OK:
sys.exit(status)
# ------------------------------------------------------------------------------
# Entry point
# ------------------------------------------------------------------------------
if __name__ == '__main__':
main()
What changed:
@args lines.get_mount_points() uses the --mount-point option when used, otherwise the configuration minus the --exclude mount points.get_thresholds() uses the --warning and --critical options when used, otherwise the configuration.The options are added to the help:
./disk_watch.py --help
usage: disk_watch.py [-h] [--version] [--source-code]
[--hist [NB_EXECUTION (default:10)]]
[--log [SESSION (default: last)]] [--debug] [--no-stdout]
[--no-log-file]
[--mount-point MOUNT_POINT [MOUNT_POINT ...]]
[--exclude EXCLUDE [EXCLUDE ...]] [--warning WARNING]
[--critical CRITICAL]
Author: Luiggi Vercotti
Date: 2026-10-08
Version: 0.5.0
Description: Monitor filesystem usage
options:
[...]
--mount-point MOUNT_POINT [MOUNT_POINT ...]
Mount points to check (default: from configuration)
--exclude EXCLUDE [EXCLUDE ...]
Mount points to skip
--warning WARNING Warning threshold in percent (default: from configuration)
--critical CRITICAL Critical threshold in percent (default: from configuration)
./disk_watch.py --exclude /var /home --warning 30
[...]
[20261008 19:11:53] [INFO ] [disk_watch] Thresholds: warning=30% critical=90%
[20261008 19:11:53] [WARNING ] '-> /: 34.9% [WARNING]
[20261008 19:11:53] [INFO ] [+] Status: WARNING
[...]
The options are checked before entering the execution context: A wrong usage stops the script with the exit status 2, before running any of your code:
./disk_watch.py --warning high
disk_watch.py: error: argument --warning: invalid int value: 'high'
./disk_watch.py --mount-point / --exclude /var
disk_watch.py: error: Params --mount-point conflicts with --exclude. Please remove --mount-point or --exclude
The options are available through _context.args, a standard argparse.Namespace. The value of the --mount-point option is args.mount_point: Dashes are replaced by underscores.
| Option | Not used | --warning 30 |
--mount-point / /home |
|---|---|---|---|
args.warning |
None |
30 |
None |
args.mount_point |
None |
None |
['/', '/home'] |
An option that is not used and has no default value is None. This is how get_thresholds() knows whether the configuration must be used.
An option is declared with an @args line made of 9 fields:
@args:<name>|<type>|<help>|<number of arguments>|<default value>|<conflicts>|<implies>|<required>|<secret>
| Field | Content | Mandatory |
|---|---|---|
name |
The option name, without the leading dashes. Letters, digits, - and _ |
Yes |
type |
str, int, float, bool or choice |
Yes |
help |
The help message of the option | Yes |
number of arguments |
1, any integer greater than 1, + (one or more) or ? (zero or one). Ignored for bool options |
Yes, except for bool |
default value |
The value used when the option is not used. For choice options, the list of accepted values |
No |
conflicts |
Comma separated list of options that can not be used with this option | No |
implies |
Comma separated list of options that must be used with this option | No |
required |
true if the option must be used, false otherwise |
Yes |
secret |
true if the value must be redacted from the logs and the history (see Secrets), false otherwise |
Yes |
Empty optional fields are left empty, but the 9 fields are always present. An invalid declaration is reported at startup with the reason:
[CRITICAL] [ScrippyCoreError]: Invalid @args declaration(s):
- @args:warning|integer|Warning threshold|1||||false|false: unknown type 'integer' (expected: str, int, float, choice, bool)
disk_watch.py options@args:mount-point|str|Mount points to check (default: from configuration)|+||exclude||false|false
@args:exclude|str|Mount points to skip|+||||false|false
@args:warning|int|Warning threshold in percent (default: from configuration)|1||||false|false
@args:critical|int|Critical threshold in percent (default: from configuration)|1||||false|false
--mount-point and --exclude accept one or more values (+) and are returned as lists.--mount-point conflicts with --exclude: Excluding mount points only makes sense when they come from the configuration file.--warning and --critical accept exactly one integer.| Type | Value |
|---|---|
str |
A string |
int |
An integer |
float |
A floating point number |
bool |
A flag that takes no argument (see below) |
choice |
A string among a list of accepted values (see below) |
With a number of arguments greater than 1, or +, the value is a list. With a fixed number of arguments greater than 1, the default value contains as many comma separated values:
@args:date|int|Day, month and year|3|1,1,2026|||false|false
bool optionsA bool option is a flag: It takes no argument and its number of arguments is ignored. Its default value is false when not set: The option is True when used, False otherwise.
@args:verbose|bool|Display details||false|||false|false
A bool option whose default value is true is False when used.
Note: A bool option can not be secret.
choice optionsFor a choice option, the default value field holds the comma separated list of accepted values. A choice option that is not used is None:
@args:format|choice|Report format|1|text,csv|||false|false
disk_watch.py: error: argument --format: invalid choice: 'pdf' (choose from text, csv)
disk_watch.py will use bool and choice options in the Workspace and environment step.
conflicts: The listed options can not be used together with this option.implies: When this option is used, the listed options must be used too.@args:format|choice|Report format|1|text,csv||report|false|false
disk_watch.py: error: Params --format implies --report. Please add --report
Both fields can reference the declared options and the debug, no-stdout and no-log-file automatic options.
The help, version, source-code, hist, log, debug, no-stdout and no-log-file options are automatically added to every script: Declaring one of them is an error. They are described in the options reference.
disk_watch.py will soon send alerts to a monitoring API, which requires an API token. In the next step, you will make sure this token never appears in the logs nor in the history.