├── .github └── workflows │ ├── publish-to-pypi.yml │ └── tests.yml ├── .gitignore ├── .python-version ├── .readthedocs.yaml ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.rst ├── README_template.rst ├── check_systemd.py ├── contrib └── icinga2 │ └── command.conf ├── docs ├── conf.py ├── index.rst ├── requirements.in └── requirements.txt ├── poetry.lock ├── pyproject.toml ├── tests ├── __init__.py ├── _test_gi.py ├── _test_unmocked.py ├── cli_output │ ├── systemctl-list-timers_1.txt │ ├── systemctl-list-timers_2.txt │ ├── systemctl-list-timers_all-n-a.txt │ ├── systemctl-list-timers_ok.txt │ ├── systemctl-list-units_3units.txt │ ├── systemctl-list-units_failed.txt │ ├── systemctl-list-units_inactive.txt │ ├── systemctl-list-units_multiple-failure.txt │ ├── systemctl-list-units_ok.txt │ ├── systemctl-list-units_regexp-excludes.txt │ ├── systemctl-list-units_v246.txt │ ├── systemctl-show-ansible-pull_inactive.txt │ ├── systemctl-show-nginx_active.txt │ ├── systemd-analyze_12.345.txt │ └── systemd-analyze_not-finished.txt ├── helper.py ├── test_argparse.py ├── test_boot_not_finised.py ├── test_data_acquisition_units.py ├── test_data_source_cli.py ├── test_dbus.py ├── test_option_exclude.py ├── test_option_ignore_inactive_state.py ├── test_option_unit.py ├── test_performance_data.py ├── test_scope_startup_time.py ├── test_scope_timers.py ├── test_scope_units.py ├── test_table_parser.py ├── test_type_checkers.py ├── test_unit.py ├── test_version246.py └── unit-files │ ├── example-service.service │ ├── example-timer.service │ └── example-timer.timer └── tox.ini /.github/workflows/publish-to-pypi.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/ 3 | name: Build and Publish to PyPI 4 | 5 | on: 6 | push: 7 | # Pattern matched against refs/tags 8 | tags: 9 | - '*' # Push events to every tag not containing / 10 | 11 | jobs: 12 | build-and-publish: 13 | name: Build and Publish to PyPI 14 | runs-on: ubuntu-latest 15 | steps: 16 | # https://github.com/marketplace/actions/checkout 17 | - name: Checkout git repository 18 | uses: actions/checkout@v4 19 | # https://github.com/marketplace/actions/setup-python 20 | - name: Setup Python 21 | uses: actions/setup-python@v5 22 | with: 23 | python-version: "3.x" 24 | 25 | # Build #################################################################### 26 | 27 | # https://github.com/marketplace/actions/publish-python-poetry-package 28 | - name: Publish to PyPI 29 | # https://github.com/JRubics/poetry-publish/issues/39 30 | uses: JRubics/poetry-publish@v2.0 31 | with: 32 | # https://pypi.org/manage/account/token/ 33 | # https://github.com///settings/secrets/actions/new 34 | pypi_token: ${{ secrets.PYPI_API_TOKEN }} 35 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') 36 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions 3 | name: Tests 4 | 5 | on: 6 | push: 7 | branches: ['main'] 8 | pull_request: 9 | branches: ['main'] 10 | workflow_dispatch: 11 | 12 | jobs: 13 | tests: 14 | name: Python ${{ matrix.python-version }} 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] 20 | 21 | steps: 22 | # https://github.com/marketplace/actions/checkout 23 | - uses: actions/checkout@v4 24 | # https://github.com/marketplace/actions/setup-python 25 | - uses: actions/setup-python@v5 26 | with: 27 | python-version: ${{ matrix.python-version }} 28 | - name: Install dependencies 29 | run: | 30 | python -m pip install --upgrade pip setuptools wheel virtualenv tox tox-gh-actions 31 | 32 | # https://github.com/ymyzk/tox-gh-actions 33 | - name: Run tox targets for ${{ matrix.python-version }} 34 | run: python -m tox 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Based on https://github.com/github/gitignore/blob/main/Python.gitignore 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | 7 | # Distribution / packaging 8 | dist/ 9 | MANIFEST 10 | *.egg-info/ 11 | *.venv/ 12 | 13 | # Unit test / coverage reports 14 | .tox/ 15 | nosetests.xml 16 | .pytest_cache/ 17 | 18 | # Sphinx documentation 19 | docs/_build/ 20 | 21 | # pyenv 22 | # .python-version 23 | 24 | # mypy 25 | .mypy_cache/ 26 | 27 | *.pickle 28 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.8 2 | 3.9 3 | 3.10 4 | 3.11 5 | 3.13 6 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 2 | 3 | # https://docs.readthedocs.io/en/stable/config-file/v2.html 4 | version: 2 5 | 6 | # https://docs.readthedocs.io/en/stable/config-file/v2.html#build 7 | build: 8 | os: "ubuntu-22.04" 9 | tools: 10 | python: "3.10" 11 | 12 | # https://docs.readthedocs.io/en/stable/config-file/v2.html#sphinx 13 | sphinx: 14 | configuration: docs/conf.py 15 | 16 | # https://docs.readthedocs.io/en/stable/config-file/v2.html#python 17 | python: 18 | install: 19 | - requirements: docs/requirements.txt 20 | - method: pip 21 | path: . 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/__pycache__/": true, 4 | "**/.mypy_cache/": true, 5 | "**/.pytest_cache/": true, 6 | "**/.tox/": true, 7 | "**/*.egg-info": true, 8 | "**/*.py[cod]": true, 9 | "**/*.ruff_cache/": true, 10 | "**/*.venv/": true, 11 | "**/dist/": true, 12 | "**/doc/html/": true, 13 | "**/docs/_build/": true, 14 | "**/MANIFEST": true 15 | }, 16 | "[python]": { 17 | "editor.rulers": [ 18 | 79, 19 | 80, 20 | 88 21 | ], 22 | "editor.defaultFormatter": "charliermarsh.ruff" 23 | }, 24 | "python.defaultInterpreterPath": ".venv/bin/python", 25 | "python.testing.pytestEnabled": true, 26 | "python.testing.pytestArgs": [ 27 | "--capture=tee-sys", 28 | "-vv", 29 | "--rootdir", 30 | "${workspaceFolder}/tests" 31 | ], 32 | "mypy.dmypyExecutable": "${workspaceFolder}/.venv/bin/dmypy" 33 | } 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | [Compare with latest](https://github.com/Josef-Friedrich/check_systemd/compare/v5.0.0...HEAD) 11 | 12 | ## [v5.0.0] - 2025-02-09 13 | 14 | [Compare with v0.4.1](https://github.com/Josef-Friedrich/check_systemd/compare/v4.1.1...v5.0.0) 15 | 16 | ### Changed 17 | 18 | - Drop support for Python 3.8 because of its end of life 19 | 20 | ### Fixed 21 | 22 | - Fix the --user option when checking a single user service (#42) 23 | 24 | ## [v4.1.1] - 2024-11-02 25 | 26 | [Compare with v0.4.0](https://github.com/Josef-Friedrich/check_systemd/compare/v4.1.0...v4.1.1) 27 | 28 | ### Added 29 | 30 | - Add support for Python 3.13 31 | 32 | ### Changed 33 | 34 | - Updated `contrib/icinga2/command.conf` to support latest commands and harmonize with Icinga 2 ITL upstream. 35 | The two variables `systemd_dead_timer_warning` and `systemd_dead_timer_critical` were renamed to `systemd_dead_timers_warning` and `systemd_dead_timers_critical`, respectively. 36 | - Fix typos in the help output 37 | 38 | ## [v4.1.0] - 2024-03-01 39 | 40 | [Compare with v0.4.0](https://github.com/Josef-Friedrich/check_systemd/compare/v4.0.0...v4.1.0) 41 | 42 | ### Added 43 | 44 | - Add basic debug support (--debug) ([f371f78](https://github.com/Josef-Friedrich/check_systemd/commit/f371f78256bd0a407156abf7a507a3bdd4fcf328) by Josef Friedrich). 45 | 46 | ### Fixed 47 | 48 | - Fix check response for stopped units and units selected with --unit ([dd79ced](https://github.com/Josef-Friedrich/check_systemd/commit/dd79ceddcb23b7924b52c38e916aeea40a2d058e) by Josef Friedrich). 49 | 50 | ## [v4.0.0] - 2024-02-20 51 | 52 | [Compare with v3.1.0](https://github.com/Josef-Friedrich/check_systemd/compare/v3.1.0...v4.0.0) 53 | 54 | ### Changed 55 | 56 | - Switch the testing framework to pytest ([a0aa63c](https://github.com/Josef-Friedrich/check_systemd/commit/a0aa63c0ff0aad038d9cc7f484cd7eee50744db5) by Josef Friedrich) 57 | 58 | ### Fixed 59 | 60 | - Fix the readthedocs site ([a7cf474](https://github.com/Josef-Friedrich/check_systemd/commit/a7cf4741500a981773d48d5f065ee5379828462e) by Josef Friedrich). 61 | - Fix some type hint issues in the tests ([1e2b135](https://github.com/Josef-Friedrich/check_systemd/commit/1e2b135e92f919135eb7d937320d7878822ba99f) by Josef Friedrich). 62 | 63 | ### Removed 64 | 65 | - Remove the perfdata `data_source` ([62fd828](https://github.com/Josef-Friedrich/check_systemd/commit/62fd828177c9372a5234314e354ca9a442e726bb) by Josef Friedrich). 66 | 67 | ## [v3.1.0] - 2024-01-04 68 | 69 | ### Added 70 | 71 | - (Re)add the options `-i`, `--ignore-inactive-state` (#31). 72 | 73 | ## [v3.0.0] - 2023-11-18 74 | 75 | ### Added 76 | 77 | - The options `--include`, `--include-unit`, `--include-type`, 78 | `--exclude`, `--exclude-unit`, `--exclude-type` have been added to 79 | have better control over which units should be selected for testing. 80 | - A new entry was added to the performance data: `data_source=cli` or 81 | `data_source=dbus` 82 | - Add example `icinga2` configuration for the plugin (#23) 83 | - New option to query user units (`--user`) (#22) 84 | - New option `--required` to set the state that the systemd unit must have (#17) 85 | 86 | ### Changed 87 | 88 | - The options `--dead-timers`, `--dead-timers-warning` and 89 | `--dead-timers-critical` have been renamed to `--timers`, 90 | `--timers-warning` and `--timers-critical`. 91 | - In the command line help, the options have been grouped according to 92 | their monitoring scope. 93 | - Always return perfdata for `startup_time` even with `-n` (#27) 94 | - Exit `Unknown` when importing `nagiosplugin` fails (#24) 95 | 96 | ### Removed 97 | 98 | - The options `-i`, `--ignore-inactive-state` have been removed. 99 | - Drop support for 3.6 and 3.7 100 | 101 | ## [v2.3.1] - 2021-03-03 102 | 103 | ### Fixed 104 | 105 | - Clean up the documentation 106 | 107 | ### Removed 108 | 109 | - Remove the systemd source to avoid confusion (#13) 110 | 111 | ## [v2.3.0] - 2021-01-15 112 | 113 | ### Added 114 | 115 | - Add option `--ignore-inactive-state` to prevent `check_systemd` from triggering a critical state 116 | 117 | ## [v2.2.1] - 2020-10-27 118 | 119 | ### Fixed 120 | 121 | - Make the plugin compatible with systemd 246 (#10) 122 | 123 | ## [v2.2.0] - 2020-05-27 124 | 125 | ### Added 126 | 127 | - New option `-n, --no-startup-time` to disable the startup time check (#7). 128 | 129 | ## [v2.1.0] - 2020-05-16 130 | 131 | ### Added 132 | 133 | - Exclude units using regular expressions 134 | 135 | ## [v2.0.11] - 2020-03-20 136 | 137 | ### Fixed 138 | 139 | - Versioning 140 | - Documentation (README) 141 | 142 | ## [v2.0.10] - 2020-03-19 143 | 144 | ### Fixed 145 | 146 | - Fix KeyError 'not-found' 147 | 148 | ## [v2.0.9] - 2019-08-05 149 | 150 | ### Fixed 151 | 152 | - Parse milliseconds in timespan strings (#3) 153 | 154 | ## [v2.0.8] - 2019-05-26 155 | 156 | ### Fixed 157 | 158 | - Boot not finished 159 | 160 | ## [v2.0.7] - 2019-05-26 161 | 162 | ## Added 163 | 164 | - Tests to test bootup not finisched 165 | 166 | ## [v2.0.6] - 2019-04-13 167 | 168 | ### Added 169 | 170 | - Documentation 171 | 172 | ### Fixed 173 | 174 | - Version number 175 | 176 | ## [v2.0.5] - 2019-04-13 177 | 178 | ### Fixed 179 | 180 | - `start_time` performance data 181 | 182 | ## [v2.0.4] - 2019-04-08 183 | 184 | ### Fixed 185 | 186 | - startup time on raspbian 187 | 188 | ## [v2.0.3] - 2019-04-07 189 | 190 | ### Added 191 | 192 | - New option `--version` 193 | - Check startup time 194 | - performance data 195 | 196 | ## [v2.0.2] - 2019-04-06 197 | 198 | ### Fixed 199 | 200 | - Fix setup.py 201 | 202 | ## [v2.0.1] - 2019-04-06 203 | 204 | ### Fixed 205 | 206 | - Fix setup.py 207 | 208 | ## [v2.0.0] - 2019-04-06 209 | 210 | ### Added 211 | 212 | - Added an --exclude option to exclude some systemd units from the checks 213 | 214 | ## [v1.2.0](https://github.com/Josef-Friedrich/check_systemd/releases/tag/v1.2.0) - 2018-07-30 215 | 216 | [Compare with V1.1.1](https://github.com/Josef-Friedrich/check_systemd/compare/V1.1.1...v1.2.0) 217 | 218 | ### Added 219 | 220 | - Added custom Summary class to handle status line output Added verbosity flag ([79d4a3b](https://github.com/Josef-Friedrich/check_systemd/commit/79d4a3b4c314a250d461473b5766b8d0217fcbf2) by spike). 221 | 222 | ## [v1.1.1](https://github.com/Josef-Friedrich/check_systemd/releases/tag/V1.1.1) - 2017-07-14 223 | 224 | [Compare with v1.1.0](https://github.com/Josef-Friedrich/check_systemd/compare/v1.1.0...V1.1.1) 225 | 226 | ### Removed 227 | 228 | - Remove global service variable ([daeb929](https://github.com/Josef-Friedrich/check_systemd/commit/daeb92963238c88e4e8ffa471d1da80fd9a5a0a4) by Adam Cécile). 229 | 230 | ## [v1.1.0](https://github.com/Josef-Friedrich/check_systemd/releases/tag/v1.1.0) - 2016-09-26 231 | 232 | [Compare with v1.0.4](https://github.com/Josef-Friedrich/check_systemd/compare/v1.0.4...v1.1.0) 233 | 234 | ### Added 235 | 236 | - Added Parsing option for testing a single Systemd-service ([a1d2af0](https://github.com/Josef-Friedrich/check_systemd/commit/a1d2af0a78015684b554c83604d572ce43b6b283) by Felix Buehler). 237 | 238 | ### Changed 239 | 240 | - changed from optionparse to argparse ([0ef5153](https://github.com/Josef-Friedrich/check_systemd/commit/0ef5153c4c38067b0ebdfe15d76d1cedc8b1316b) by Felix Buehler). 241 | 242 | ## [v1.0.4](https://github.com/Josef-Friedrich/check_systemd/releases/tag/v1.0.4) - 2016-08-31 243 | 244 | [Compare with v1.0.2](https://github.com/Josef-Friedrich/check_systemd/compare/v1.0.2...v1.0.4) 245 | 246 | ### Added 247 | 248 | - add execute permissions ([3f4df49](https://github.com/Josef-Friedrich/check_systemd/commit/3f4df496610edd37ba617f3879352cca5069a78f) by Felix Buehler). 249 | 250 | ### Fixed 251 | 252 | - Fix python path ([2c9e835](https://github.com/Josef-Friedrich/check_systemd/commit/2c9e8353d6a8dac730b95e3750c11e9f3e893a03) by Andrea Briganti). 253 | 254 | ## [v1.0.2](https://github.com/Josef-Friedrich/check_systemd/releases/tag/v1.0.2) - 2015-07-30 255 | 256 | [Compare with v1.0.1](https://github.com/Josef-Friedrich/check_systemd/compare/v1.0.1...v1.0.2) 257 | 258 | ## [v1.0.1](https://github.com/Josef-Friedrich/check_systemd/releases/tag/v1.0.1) - 2015-07-15 259 | 260 | [Compare with first commit](https://github.com/Josef-Friedrich/check_systemd/compare/9dbe9d1332fb82a65fdfb98bb81c44f48c9be680...v1.0.1) 261 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 489 | USA 490 | 491 | Also add information on how to contact you by electronic and paper mail. 492 | 493 | You should also get your employer (if you work as a programmer) or your 494 | school, if any, to sign a "copyright disclaimer" for the library, if 495 | necessary. Here is a sample; alter the names: 496 | 497 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 498 | library `Frob' (a library for tweaking knobs) written by James Random 499 | Hacker. 500 | 501 | , 1 April 1990 502 | Ty Coon, President of Vice 503 | 504 | That's all there is to it! 505 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | poetry run tox 3 | 4 | test_unmocked: 5 | pytest -vvv --capture tee-sys tests/_test_unmocked.py 6 | 7 | install: update 8 | 9 | # https://github.com/python-poetry/poetry/issues/34#issuecomment-1054626460 10 | install_editable: 11 | pip install -e . 12 | 13 | update: 14 | poetry lock 15 | poetry install 16 | 17 | build: 18 | poetry build 19 | 20 | publish: 21 | poetry build 22 | poetry publish 23 | 24 | format: 25 | poetry run tox -e format 26 | 27 | docs: 28 | poetry run tox -e docs 29 | xdg-open docs/_build/index.html > /dev/null 2>&1 30 | 31 | readme: 32 | poetry run tox -e readme 33 | 34 | lint: 35 | poetry run tox -e lint 36 | 37 | pin_docs_requirements: 38 | pipx install pip-tools 39 | pip-compile --output-file=docs/requirements.txt docs/requirements.in pyproject.toml 40 | 41 | copy_example_systemd_units: 42 | sudo cp -r tests/unit-files/* /etc/systemd/system/ 43 | 44 | .PHONY: test install install_editable update build publish format docs readme lint pin_docs_requirements 45 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: http://img.shields.io/pypi/v/check-systemd.svg 2 | :target: https://pypi.org/project/check-systemd 3 | :alt: This package on the Python Package Index 4 | 5 | .. image:: https://github.com/Josef-Friedrich/check_systemd/actions/workflows/tests.yml/badge.svg 6 | :target: https://github.com/Josef-Friedrich/check_systemd/actions/workflows/tests.yml 7 | :alt: Tests 8 | 9 | .. image:: https://readthedocs.org/projects/check-systemd/badge/?version=latest 10 | :target: https://check-systemd.readthedocs.io/en/latest/?badge=latest 11 | :alt: Documentation Status 12 | 13 | check_systemd 14 | ============= 15 | 16 | ``check_systemd`` is a `Nagios `__ / 17 | `Icinga `__ monitoring plugin to check 18 | `systemd `__. This Python script will report a 19 | degraded system to your monitoring solution. It can also be used to 20 | monitor individual systemd services (with the ``-u, --unit`` parameter) 21 | and timers units (with the ``-t, --dead-timers`` parameter). The only 22 | dependency the plugin needs is the Python library 23 | `nagiosplugin `__. 24 | 25 | Installation 26 | ------------ 27 | 28 | :: 29 | 30 | pip install check_systemd 31 | 32 | Packages 33 | -------- 34 | 35 | ``check_systemd`` on `repology.org `__. 36 | 37 | - archlinux 38 | (`package `__, 39 | `source 40 | code `__): 41 | ``yaourt -S check_systemd`` 42 | - Ubuntu 43 | (`package `__, 44 | `source 45 | code `__): 46 | ``apt install monitoring-plugins-systemd`` 47 | - Debian 48 | (`package `__, 49 | `source 50 | code `__): 51 | ``apt install monitoring-plugins-systemd`` 52 | - NixOS 53 | (`package `__, 54 | `source 55 | code `__): 56 | ``nix-env -iA nixos.check_systemd`` 57 | - Fedora 58 | (`package `__, 59 | `source code `__): 60 | ``dnf install nagios-plugins-systemd`` 61 | - OracleLinux9 / RHEL9 62 | (`package `__, 63 | `source code `__, 64 | `binary `__): 65 | This package includes one single binary compiled with the Python compiler Nuitka, including all dependencies. 66 | The package is built via GitLab CI as a nightly release and is considered experimental. 67 | ``curl -L -o check_systemd-1.0-1.x86_64.rpm "https://gitlab.com/msfgitlab/check_systemd_build_rpm/-/jobs/artifacts/main/raw/output/check_systemd-1.0-1.x86_64.rpm?job=release_rpm" && sudo dnf install -y ./check_systemd-1.0-1.x86_64.rpm`` 68 | 69 | Command line interface 70 | ---------------------- 71 | 72 | :: 73 | 74 | usage: check_systemd [-h] [-v] [-d] [-V] [-i] [-I REGEXP] [-u UNIT_NAME] 75 | [--include-type UNIT_TYPE [UNIT_TYPE ...]] [-e REGEXP] 76 | [--exclude-unit UNIT_NAME [UNIT_NAME ...]] 77 | [--exclude-type UNIT_TYPE] 78 | [--state {active,reloading,inactive,failed,activating,deactivating}] 79 | [-t] [-W SECONDS] [-C SECONDS] [-n] [-w SECONDS] 80 | [-c SECONDS] [--dbus | --cli] [--user] [-P | -p] 81 | 82 | Copyright (c) 2014-18 Andrea Briganti 83 | Copyright (c) 2019-25 Josef Friedrich 84 | 85 | Nagios / Icinga monitoring plugin to check systemd. 86 | 87 | options: 88 | -h, --help show this help message and exit 89 | -v, --verbose Increase output verbosity (use up to 3 times). 90 | -d, --debug Increase debug verbosity (use up to 2 times): -d: info 91 | -dd: debug. 92 | -V, --version show program's version number and exit 93 | 94 | Options related to unit selection: 95 | By default all systemd units are checked. Use the option '-e' to exclude units 96 | by a regular expression. Use the option '-u' to check only one unit. 97 | 98 | -i, --ignore-inactive-state 99 | Ignore an inactive state on a specific unit. Oneshot 100 | services for example are only active while running and 101 | not enabled. The rest of the time they are inactive. 102 | This option has only an affect if it is used with the 103 | option -u. 104 | -I REGEXP, --include REGEXP 105 | Include systemd units to the checks. This option can be 106 | applied multiple times, for example: -I mnt-data.mount 107 | -I task.service. Regular expressions can be used to 108 | include multiple units at once, for example: -i 109 | 'user@\d+\.service'. For more informations see the 110 | Python documentation about regular expressions 111 | (https://docs.python.org/3/library/re.html). 112 | -u UNIT_NAME, --unit UNIT_NAME, --include-unit UNIT_NAME 113 | Name of the systemd unit that is being tested. 114 | --include-type UNIT_TYPE [UNIT_TYPE ...] 115 | One or more unit types (for example: 'service', 'timer') 116 | -e REGEXP, --exclude REGEXP 117 | Exclude a systemd unit from the checks. This option can 118 | be applied multiple times, for example: -e mnt- 119 | data.mount -e task.service. Regular expressions can be 120 | used to exclude multiple units at once, for example: -e 121 | 'user@\d+\.service'. For more informations see the 122 | Python documentation about regular expressions 123 | (https://docs.python.org/3/library/re.html). 124 | --exclude-unit UNIT_NAME [UNIT_NAME ...] 125 | Name of the systemd unit that is being tested. 126 | --exclude-type UNIT_TYPE 127 | One or more unit types (for example: 'service', 'timer') 128 | --state {active,reloading,inactive,failed,activating,deactivating}, --required {active,reloading,inactive,failed,activating,deactivating}, --expected-state {active,reloading,inactive,failed,activating,deactivating} 129 | Specify the active state that the systemd unit must have 130 | (for example: active, inactive) 131 | 132 | Timers related options: 133 | -t, --timers, --dead-timers 134 | Detect dead / inactive timers. See the corresponding 135 | options '-W, --dead-timer-warning' and '-C, --dead- 136 | timers-critical'. Dead timers are detected by parsing 137 | the output of 'systemctl list-timers'. Dead timer rows 138 | displaying 'n/a' in the NEXT and LEFT columns and the 139 | time span in the column PASSED exceeds the values 140 | specified with the options '-W, --dead-timer-warning' 141 | and '-C, --dead-timers-critical'. 142 | -W SECONDS, --timers-warning SECONDS, --dead-timers-warning SECONDS 143 | Time ago in seconds for dead / inactive timers to 144 | trigger a warning state (by default 6 days). 145 | -C SECONDS, --timers-critical SECONDS, --dead-timers-critical SECONDS 146 | Time ago in seconds for dead / inactive timers to 147 | trigger a critical state (by default 7 days). 148 | 149 | Startup time related options: 150 | -n, --no-startup-time 151 | Don’t check the startup time. Using this option the 152 | options '-w, --warning' and '-c, --critical' have no 153 | effect. Performance data about the startup time is 154 | collected, but no critical, warning etc. states are 155 | triggered. 156 | -w SECONDS, --warning SECONDS 157 | Startup time in seconds to result in a warning status. 158 | The default is 60 seconds. 159 | -c SECONDS, --critical SECONDS 160 | Startup time in seconds to result in a critical status. 161 | The default is 120 seconds. 162 | 163 | Monitoring data acquisition: 164 | --dbus Use the systemd’s D-Bus API instead of parsing the text 165 | output of various systemd related command line 166 | interfaces to monitor systemd. At the moment the D-Bus 167 | backend of this plugin is only partially implemented. 168 | --cli Use the text output of serveral systemd command line 169 | interface (cli) binaries to gather the required data for 170 | the monitoring process. 171 | --user Also show user (systemctl --user) units. 172 | 173 | Performance data: 174 | By default performance data is attached. 175 | 176 | -P, --performance-data 177 | Attach performance data to the plugin output. 178 | -p, --no-performance-data 179 | Attach no performance data to the plugin output. 180 | 181 | Performance data: 182 | - count_units 183 | - startup_time 184 | - units_activating 185 | - units_active 186 | - units_failed 187 | - units_inactive 188 | 189 | Project pages 190 | ------------- 191 | 192 | - on `github.com `__ 193 | - on 194 | `icinga.com `__ 195 | - on 196 | `nagios.org `__ 197 | 198 | Behind the scenes 199 | ----------------- 200 | 201 | dbus 202 | ^^^^ 203 | 204 | - ``gi``: Python 3 bindings for gobject-introspection libraries 205 | GObject is an abstraction layer that allows programming with an object 206 | paradigm that is compatible with many languages. It is a part of Glib, 207 | the core library used to build GTK+ and GNOME. 208 | `Website `__ 209 | `Repo `__ 210 | `PyPI (PyGObject) `__ 211 | `Stubs `__ 212 | `Ubuntu (python3-gi) `__ 213 | `Debian (python3-gi) `__ 214 | 215 | - ``dbus``: simple interprocess messaging system (Python 3 interface) 216 | D-Bus is a message bus, used for sending messages between applications. 217 | Conceptually, it fits somewhere in between raw sockets and CORBA in 218 | terms of complexity. 219 | `Website `__ 220 | `Repo `__ 221 | `PyPI (dbus-python) `__ 222 | `Ubuntu (python3-dbus) `__ 223 | `Debian (python3-dbus) `__ 224 | 225 | Command line interface (cli) parsing: 226 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 227 | 228 | To detect failed units this monitoring script runs: 229 | 230 | .. code:: sh 231 | 232 | systemctl list-units --all 233 | 234 | To get the startup time it executes: 235 | 236 | .. code:: sh 237 | 238 | systemd-analyze 239 | 240 | To find dead timers this plugin launches: 241 | 242 | .. code:: sh 243 | 244 | systemctl list-timers --all 245 | 246 | To learn how ``systemd`` produces the text output on the command line, 247 | it is worthwhile to take a look at ``systemd``\ ’s source code. Files 248 | relevant for text output are: 249 | `basic/time-util.c `__, 250 | `analyze/analyze.c `__. 251 | 252 | Testing 253 | ------- 254 | 255 | :: 256 | 257 | pyenv install 3.6.12 258 | pyenv install 3.7.9 259 | pyenv local 3.6.12 3.7.9 260 | pip3 install tox 261 | tox 262 | 263 | Test a single test case: 264 | 265 | :: 266 | 267 | tox -e py38 -- test/test_scope_timers.py:TestScopeTimers.test_all_n_a 268 | 269 | Deploying 270 | --------- 271 | 272 | Edit the version number in check_systemd.py (without ``v``). Use the 273 | ``-s`` option to sign the tag (required for the Debian package). 274 | 275 | :: 276 | 277 | git tag -s v2.0.11 278 | git push --tags 279 | -------------------------------------------------------------------------------- /README_template.rst: -------------------------------------------------------------------------------- 1 | {{ badge.pypi }} 2 | 3 | {{ badge.github_workflow() }} 4 | 5 | {{ badge.readthedocs }} 6 | 7 | check_systemd 8 | ============= 9 | 10 | ``check_systemd`` is a `Nagios `__ / 11 | `Icinga `__ monitoring plugin to check 12 | `systemd `__. This Python script will report a 13 | degraded system to your monitoring solution. It can also be used to 14 | monitor individual systemd services (with the ``-u, --unit`` parameter) 15 | and timers units (with the ``-t, --dead-timers`` parameter). The only 16 | dependency the plugin needs is the Python library 17 | `nagiosplugin `__. 18 | 19 | Installation 20 | ------------ 21 | 22 | :: 23 | 24 | pip install check_systemd 25 | 26 | Packages 27 | -------- 28 | 29 | ``check_systemd`` on `repology.org `__. 30 | 31 | - archlinux 32 | (`package `__, 33 | `source 34 | code `__): 35 | ``yaourt -S check_systemd`` 36 | - Ubuntu 37 | (`package `__, 38 | `source 39 | code `__): 40 | ``apt install monitoring-plugins-systemd`` 41 | - Debian 42 | (`package `__, 43 | `source 44 | code `__): 45 | ``apt install monitoring-plugins-systemd`` 46 | - NixOS 47 | (`package `__, 48 | `source 49 | code `__): 50 | ``nix-env -iA nixos.check_systemd`` 51 | - Fedora 52 | (`package `__, 53 | `source code `__): 54 | ``dnf install nagios-plugins-systemd`` 55 | - OracleLinux9 / RHEL9 56 | (`package `__, 57 | `source code `__, 58 | `binary `__): 59 | This package includes one single binary compiled with the Python compiler Nuitka, including all dependencies. 60 | The package is built via GitLab CI as a nightly release and is considered experimental. 61 | ``curl -L -o check_systemd-1.0-1.x86_64.rpm "https://gitlab.com/msfgitlab/check_systemd_build_rpm/-/jobs/artifacts/main/raw/output/check_systemd-1.0-1.x86_64.rpm?job=release_rpm" && sudo dnf install -y ./check_systemd-1.0-1.x86_64.rpm`` 62 | 63 | 64 | Command line interface 65 | ---------------------- 66 | 67 | {{ cli('check_systemd --help') | literal }} 68 | 69 | Project pages 70 | ------------- 71 | 72 | - on `github.com `__ 73 | - on 74 | `icinga.com `__ 75 | - on 76 | `nagios.org `__ 77 | 78 | Behind the scenes 79 | ----------------- 80 | 81 | dbus 82 | ^^^^ 83 | 84 | - ``gi``: Python 3 bindings for gobject-introspection libraries 85 | GObject is an abstraction layer that allows programming with an object 86 | paradigm that is compatible with many languages. It is a part of Glib, 87 | the core library used to build GTK+ and GNOME. 88 | `Website `__ 89 | `Repo `__ 90 | `PyPI (PyGObject) `__ 91 | `Stubs `__ 92 | `Ubuntu (python3-gi) `__ 93 | `Debian (python3-gi) `__ 94 | 95 | - ``dbus``: simple interprocess messaging system (Python 3 interface) 96 | D-Bus is a message bus, used for sending messages between applications. 97 | Conceptually, it fits somewhere in between raw sockets and CORBA in 98 | terms of complexity. 99 | `Website `__ 100 | `Repo `__ 101 | `PyPI (dbus-python) `__ 102 | `Ubuntu (python3-dbus) `__ 103 | `Debian (python3-dbus) `__ 104 | 105 | Command line interface (cli) parsing: 106 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 107 | 108 | To detect failed units this monitoring script runs: 109 | 110 | .. code:: sh 111 | 112 | systemctl list-units --all 113 | 114 | To get the startup time it executes: 115 | 116 | .. code:: sh 117 | 118 | systemd-analyze 119 | 120 | To find dead timers this plugin launches: 121 | 122 | .. code:: sh 123 | 124 | systemctl list-timers --all 125 | 126 | To learn how ``systemd`` produces the text output on the command line, 127 | it is worthwhile to take a look at ``systemd``\ ’s source code. Files 128 | relevant for text output are: 129 | `basic/time-util.c `__, 130 | `analyze/analyze.c `__. 131 | 132 | Testing 133 | ------- 134 | 135 | :: 136 | 137 | pyenv install 3.6.12 138 | pyenv install 3.7.9 139 | pyenv local 3.6.12 3.7.9 140 | pip3 install tox 141 | tox 142 | 143 | Test a single test case: 144 | 145 | :: 146 | 147 | tox -e py38 -- test/test_scope_timers.py:TestScopeTimers.test_all_n_a 148 | 149 | Deploying 150 | --------- 151 | 152 | Edit the version number in check_systemd.py (without ``v``). Use the 153 | ``-s`` option to sign the tag (required for the Debian package). 154 | 155 | :: 156 | 157 | git tag -s v2.0.11 158 | git push --tags 159 | -------------------------------------------------------------------------------- /contrib/icinga2/command.conf: -------------------------------------------------------------------------------- 1 | object CheckCommand "systemd_jf" { 2 | command = [ PluginDir + "/check_systemd" ] 3 | 4 | arguments = { 5 | /* General options */ 6 | "-v" = { 7 | set_if = {{ macro("$systemd_verbose_level$") == 1 }} 8 | description = "Increase verbosity level (Accepted values: `1`, `2` or `3`). Defaults to none." 9 | } 10 | "-vv" = { 11 | set_if = {{ macro("$systemd_verbose_level$") == 2 }} 12 | } 13 | "-vvv" = { 14 | set_if = {{ macro("$systemd_verbose_level$") == 3 }} 15 | } 16 | 17 | /* Options related to unit selection */ 18 | "--ignore-inactive-state" = { 19 | value = "$systemd_ignore_inactive_state$" 20 | description = {{{Ignore an inactive state on a specific unit. Oneshot 21 | services for example are only active while running and 22 | not enabled. The rest of the time they are inactive. 23 | This option has only an affect if it is used with the 24 | option -u.}}} 25 | } 26 | "--include" = { 27 | value = "$systemd_include$" 28 | description = {{{Include systemd units to the checks. This option can be 29 | applied multiple times, for example: -I mnt-data.mount 30 | -I task.service. Regular expressions can be used to 31 | include multiple units at once, for example: -i 32 | 'user@\d+\.service'. For more informations see the 33 | Python documentation about regular expressions 34 | (https://docs.python.org/3/library/re.html).}}} 35 | repeat_key = true 36 | } 37 | "--unit" = { 38 | value = "$systemd_unit$" 39 | description = "Name of the systemd unit that is being tested." 40 | } 41 | "--include-type" = { 42 | value = "$systemd_include_type$" 43 | description = "One or more unit types (for example: 'service', 'timer')" 44 | } 45 | "--exclude" = { 46 | value = "$systemd_exclude$" 47 | description = {{{Exclude a systemd unit from the checks. This option can 48 | be applied multiple times, for example: -e mnt- 49 | data.mount -e task.service. Regular expressions can be 50 | used to exclude multiple units at once, for example: -e 51 | 'user@\d+\.service'. For more informations see the 52 | Python documentation about regular expressions 53 | (https://docs.python.org/3/library/re.html).}}} 54 | repeat_key = true 55 | } 56 | "--exclude-unit" = { 57 | value = "$systemd_exclude_unit$" 58 | description = "Name of the systemd unit that is being tested." 59 | } 60 | "--exclude-type" = { 61 | value = "$systemd_exclude_type$" 62 | description = "One or more unit types (for example: 'service', 'timer')" 63 | } 64 | "--state" = { 65 | value = "$systemd_state$" 66 | description = {{{Specify the active state that the systemd unit must have 67 | (for example: active, inactive)}}} 68 | } 69 | 70 | /* Timers related options */ 71 | "--dead-timers" = { 72 | value = "$systemd_dead_timers$" 73 | description = {{{Detect dead / inactive timers. See the corresponding 74 | options '-W, --dead-timer-warning' and '-C, --dead- 75 | timers-critical'. Dead timers are detected by parsing 76 | the output of 'systemctl list-timers'. Dead timer rows 77 | displaying 'n/a' in the NEXT and LEFT columns and the 78 | time span in the column PASSED exceeds the values 79 | specified with the options '-W, --dead-timer-warning' 80 | and '-C, --dead-timers-critical'.}}} 81 | } 82 | "--timers-warning" = { 83 | value = "$systemd_dead_timers_warning$" 84 | description = {{{Time ago in seconds for dead / inactive timers to 85 | trigger a warning state (by default 6 days).}}} 86 | } 87 | "--timers-critical" = { 88 | value = "$systemd_dead_timers_critical$" 89 | description = {{{Time ago in seconds for dead / inactive timers to 90 | trigger a critical state (by default 7 days).}}} 91 | } 92 | 93 | /* Startup time related options */ 94 | "--no-startup-time" = { 95 | value = "$systemd_no_startup_time$" 96 | description = {{{Don’t check the startup time. Using this option the 97 | options '-w, --warning' and '-c, --critical' have no 98 | effect. Performance data about the startup time is 99 | collected, but no critical, warning etc. states are 100 | triggered.}}} 101 | } 102 | "--warning" = { 103 | value = "$systemd_warning$" 104 | description = {{{Startup time in seconds to result in a warning status. 105 | The default is 60 seconds.}}} 106 | } 107 | "--critical" = { 108 | value = "$systemd_critical$" 109 | description = {{{Startup time in seconds to result in a critical status. 110 | The default is 120 seconds.}}} 111 | } 112 | 113 | /* Monitoring data acquisition */ 114 | "--dbus" = { 115 | value = "$systemd_dbus$" 116 | description = {{{Use the systemd’s D-Bus API instead of parsing the text 117 | output of various systemd related command line 118 | interfaces to monitor systemd. At the moment the D-Bus 119 | backend of this plugin is only partially implemented.}}} 120 | } 121 | "--cli" = { 122 | value = "$systemd_cli$" 123 | description = {{{Use the text output of serveral systemd command line 124 | interface (cli) binaries to gather the required data for 125 | the monitoring process.}}} 126 | } 127 | "--user" = { 128 | value = "$systemd_user$" 129 | description = "Also show user (systemctl --user) units." 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from importlib.metadata import version as get_version 3 | 4 | html_theme = "sphinx_rtd_theme" 5 | 6 | extensions = [ 7 | "sphinx.ext.autodoc", 8 | "sphinx.ext.viewcode", 9 | ] 10 | templates_path = ["_templates"] 11 | source_suffix = ".rst" 12 | 13 | master_doc = "index" 14 | 15 | project = "check_systemd" 16 | copyright: str = f"2019-{datetime.now().year}, Josef Friedrich" 17 | author = "Josef Friedrich" 18 | version: str = get_version("check_systemd") 19 | release: str = get_version("check_systemd") 20 | language = "en" 21 | exclude_patterns = ["_build"] 22 | pygments_style = "sphinx" 23 | htmlhelp_basename = "checksystemddoc" 24 | 25 | # https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_default_options 26 | autodoc_default_options = { 27 | # If set, autodoc will generate documention for the members of the target module, class or exception. 28 | "members": True, 29 | # This value selects if automatically documented members are sorted alphabetical (value 'alphabetical'), by member type (value 'groupwise') or by source order (value 'bysource'). The default is alphabetical. 30 | "member-order": "bysource", 31 | # If set, autodoc will also generate document for the members not having docstrings: 32 | "undoc-members": False, 33 | # If set, autodoc will also generate document for the private members (that is, those named like _private or __private) 34 | "private-members": False, 35 | # If set, autodoc will also generate document for the special members (that is, those named like __special__): 36 | "special-members": False, 37 | # For classes and exceptions, members inherited from base classes will be left out when documenting all members, unless you give the inherited-members option, in addition to members 38 | "inherited-members": False, 39 | # The automodule, autoclass and autoexception directives also support a flag option called show-inheritance. When given, a list of base classes will be inserted just below the class signature (when used with automodule, this will be inserted for every class that is documented in the module). 40 | "show-inheritance": False, 41 | # For modules, __all__ will be respected when looking for members unless you give the ignore-module-all flag option. Without ignore-module-all, the order of the members will also be the order in __all__. 42 | "ignore-module-all": False, 43 | # In an automodule directive with the members option set, only module members whose __module__ attribute is equal to the module name as given to automodule will be documented. This is to prevent documentation of imported classes or functions. Set the imported-members option if you want to prevent this behavior and document all available members. Note that attributes from imported modules will not be documented, because attribute documentation is discovered by parsing the source file of the current module. 44 | # "imported-members": False, 45 | # The directives supporting member documentation also have a exclude-members option that can be used to exclude single member names from documentation, if all members are to be documented. 46 | "exclude-members": True, 47 | # autoclass also recognizes the class-doc-from option that can be used to override the global value of autoclass_content. 48 | # autoclass_content 49 | # This value selects what content will be inserted into the main body of an autoclass directive. The possible values are: 50 | # "class" Only the class’ docstring is inserted. This is the default. You can still document __init__ as a separate method using automethod or the members option to autoclass. 51 | # "both" Both the class’ and the __init__ method’s docstring are concatenated and inserted. 52 | # "init" Only the __init__ method’s docstring is inserted. 53 | "class-doc-from": "both", 54 | # The no-value option can be used instead of a blank annotation to show the type hint but not the value: 55 | "no-value": True, 56 | } 57 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to check_systemd's documentation! 2 | ========================================= 3 | 4 | .. automodule:: check_systemd 5 | :members: 6 | :private-members: 7 | :special-members: 8 | 9 | Indices and tables 10 | ================== 11 | 12 | * :ref:`genindex` 13 | * :ref:`modindex` 14 | * :ref:`search` 15 | -------------------------------------------------------------------------------- /docs/requirements.in: -------------------------------------------------------------------------------- 1 | sphinx==7.2.6 2 | sphinx_rtd_theme==2.0.0 3 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.12 3 | # by the following command: 4 | # 5 | # pip-compile --output-file=docs/requirements.txt docs/requirements.in pyproject.toml 6 | # 7 | alabaster==0.7.13 8 | # via sphinx 9 | babel==2.14.0 10 | # via sphinx 11 | certifi==2023.11.17 12 | # via requests 13 | charset-normalizer==3.3.2 14 | # via requests 15 | docutils==0.20.1 16 | # via 17 | # sphinx 18 | # sphinx-rtd-theme 19 | idna==3.6 20 | # via requests 21 | imagesize==1.4.1 22 | # via sphinx 23 | jinja2==3.1.2 24 | # via sphinx 25 | markupsafe==2.1.3 26 | # via jinja2 27 | nagiosplugin==1.3.3 28 | # via check_systemd (pyproject.toml) 29 | packaging==23.2 30 | # via sphinx 31 | pygments==2.17.2 32 | # via sphinx 33 | requests==2.31.0 34 | # via sphinx 35 | snowballstemmer==2.2.0 36 | # via sphinx 37 | sphinx==7.2.6 38 | # via 39 | # -r docs/requirements.in 40 | # sphinx-rtd-theme 41 | # sphinxcontrib-applehelp 42 | # sphinxcontrib-devhelp 43 | # sphinxcontrib-htmlhelp 44 | # sphinxcontrib-jquery 45 | # sphinxcontrib-qthelp 46 | # sphinxcontrib-serializinghtml 47 | sphinx-rtd-theme==2.0.0 48 | # via -r docs/requirements.in 49 | sphinxcontrib-applehelp==1.0.7 50 | # via sphinx 51 | sphinxcontrib-devhelp==1.0.5 52 | # via sphinx 53 | sphinxcontrib-htmlhelp==2.0.4 54 | # via sphinx 55 | sphinxcontrib-jquery==4.1 56 | # via sphinx-rtd-theme 57 | sphinxcontrib-jsmath==1.0.1 58 | # via sphinx 59 | sphinxcontrib-qthelp==1.0.6 60 | # via sphinx 61 | sphinxcontrib-serializinghtml==1.1.9 62 | # via sphinx 63 | urllib3==2.1.0 64 | # via requests 65 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "check_systemd" 3 | version = "5.0.0" 4 | description = "Nagios / Icinga monitoring plugin to check systemd for failed units." 5 | authors = ["Josef Friedrich "] 6 | readme = "README.rst" 7 | license = "LGPL-2.1-only" 8 | repository = "https://github.com/Josef-Friedrich/check_systemd" 9 | classifiers = [ 10 | "Development Status :: 5 - Production/Stable", 11 | "Environment :: Plugins", 12 | "Intended Audience :: System Administrators", 13 | "License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.9", 17 | "Programming Language :: Python :: 3.10", 18 | "Programming Language :: Python :: 3.11", 19 | "Programming Language :: Python :: 3.12", 20 | "Programming Language :: Python :: 3.13", 21 | "Operating System :: POSIX :: Linux", 22 | "Topic :: System :: Networking :: Monitoring", 23 | ] 24 | 25 | [tool.poetry.dependencies] 26 | python = "^3.9" 27 | nagiosplugin = "^1.2" 28 | 29 | [tool.poetry.group.dev.dependencies] 30 | nagiosplugin-stubs = "^0.6.4" 31 | # PyGObject = "^3" # Slow tests on GitHub Actions and publishing to PyPI failed 32 | pygobject-stubs = "^2" 33 | pytest = "^8" 34 | readme-patcher = "^0" 35 | ruff = "^0" 36 | Sphinx = "^7" 37 | sphinx-rtd-theme = "^2" 38 | tox = "^4" 39 | twine = "^5" 40 | mypy = "^1" 41 | 42 | [build-system] 43 | requires = ["poetry-core>=1.0.0"] 44 | build-backend = "poetry.core.masonry.api" 45 | 46 | [tool.poetry.scripts] 47 | check_systemd = "check_systemd:main" 48 | 49 | [tool.pyright] 50 | reportMissingModuleSource = false 51 | reportPrivateImportUsage = "none" 52 | reportUnusedImport = "information" 53 | reportUnusedExpression = "information" 54 | reportImportCycles = "information" 55 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Josef-Friedrich/check_systemd/0704f6aa243590e7120bfe52ed8d960acd09fc9d/tests/__init__.py -------------------------------------------------------------------------------- /tests/_test_gi.py: -------------------------------------------------------------------------------- 1 | """Run this test in an unmocked environment.""" 2 | 3 | from __future__ import annotations 4 | 5 | from time import sleep 6 | 7 | from gi.repository.Gio import BusType, DBusProxy, DBusProxyFlags 8 | 9 | # print(proxy.GetAll("(ss)", "org.freedesktop.systemd1.Timer", "NextElapseUSecMonotonic")) # type: ignore 10 | 11 | 12 | while True: 13 | proxy = DBusProxy.new_for_bus_sync( 14 | BusType.SESSION, 15 | DBusProxyFlags.DO_NOT_LOAD_PROPERTIES, 16 | None, 17 | "org.freedesktop.systemd1", 18 | "/org/freedesktop/systemd1/unit/example_2dtimer_2etimer", 19 | "org.freedesktop.DBus.Properties", 20 | None, 21 | ) 22 | print(proxy.Get("(ss)", "org.freedesktop.systemd1.Timer", "NextElapseUSecRealtime")) # type: ignore 23 | sleep(10) 24 | -------------------------------------------------------------------------------- /tests/_test_unmocked.py: -------------------------------------------------------------------------------- 1 | """Run this test in an unmocked environment.""" 2 | 3 | from __future__ import annotations 4 | 5 | import pytest 6 | 7 | from check_systemd import CliSource, GiSource, Source 8 | 9 | 10 | @pytest.fixture 11 | def cli() -> CliSource: 12 | return CliSource() 13 | 14 | 15 | @pytest.fixture 16 | def gi() -> GiSource: 17 | return GiSource() 18 | 19 | 20 | class TestPropertyAllUnits: 21 | def test_cli(self, cli: Source) -> None: 22 | assert cli.units.count > 0 23 | 24 | def test_cli_user(self, cli: Source) -> None: 25 | cli.set_user(True) 26 | assert cli.units.count > 0 27 | 28 | def test_compare(self, cli: Source, gi: Source) -> None: 29 | assert cli.units.count == gi.units.count 30 | 31 | def test_compare_user(self, cli: Source, gi: GiSource) -> None: 32 | cli.set_user(True) 33 | gi.set_user(True) 34 | assert cli.units.count == gi.units.count 35 | 36 | 37 | class TestGetUnit: 38 | def test_cli(self, cli: Source) -> None: 39 | unit = cli.get_unit("ssh.service") 40 | assert unit.name == "ssh.service" 41 | assert unit.active_state == "active" 42 | assert unit.sub_state == "running" 43 | assert unit.load_state == "loaded" 44 | 45 | def test_gi(self, gi: Source) -> None: 46 | unit = gi.get_unit("ssh.service") 47 | assert unit.name == "ssh.service" 48 | assert unit.active_state == "active" 49 | assert unit.sub_state == "running" 50 | assert unit.load_state == "loaded" 51 | 52 | 53 | class TestPropertyStartupTime: 54 | def assert_startup_time(self, source: Source) -> None: 55 | startup_time = source.startup_time 56 | assert startup_time 57 | assert startup_time > 0 58 | 59 | def test_cli(self, cli: Source) -> None: 60 | self.assert_startup_time(cli) 61 | 62 | def test_gi(self, gi: Source) -> None: 63 | self.assert_startup_time(gi) 64 | 65 | def test_compare(self, cli: Source, gi: Source) -> None: 66 | assert cli.startup_time == gi.startup_time 67 | 68 | 69 | class TestPropertyTimers: 70 | def test_gi(self, gi: Source) -> None: 71 | for timer in gi.timers: 72 | assert timer.name 73 | 74 | # @pytest.mark.skip(reason="Fix later") 75 | def test_compare(self, cli: Source, gi: Source) -> None: 76 | assert cli.timers.count == gi.timers.count 77 | assert list(gi.timers) == list(cli.timers) 78 | 79 | 80 | class TestClassGiSource: 81 | class TestClassUnitProxy: 82 | unit = GiSource.UnitProxy(name="ssh.service") 83 | 84 | def test_property_object_path(self) -> None: 85 | assert ( 86 | self.unit.object_path == "/org/freedesktop/systemd1/unit/ssh_2eservice" 87 | ) 88 | 89 | def test_property_interface_name(self) -> None: 90 | assert self.unit.interface_name == "org.freedesktop.systemd1.Unit" 91 | 92 | def test_property_active_state(self) -> None: 93 | assert self.unit.active_state == "active" 94 | 95 | def test_property_sub_state(self) -> None: 96 | assert self.unit.sub_state == "running" 97 | 98 | def test_property_load_state(self) -> None: 99 | assert self.unit.load_state == "loaded" 100 | 101 | def test_active_enter_timestamp_monotonic(self) -> None: 102 | assert self.unit.active_enter_timestamp_monotonic > 0 103 | 104 | class TestClassManagerProxy: 105 | manager = GiSource.ManagerProxy() 106 | 107 | def test_property_object_path(self) -> None: 108 | assert self.manager.object_path == "/org/freedesktop/systemd1" 109 | 110 | def test_property_interface_name(self) -> None: 111 | assert self.manager.interface_name == "org.freedesktop.systemd1.Manager" 112 | 113 | def test_property_default_target(self) -> None: 114 | assert self.manager.default_target == "graphical.target" 115 | 116 | def test_property_userspace_timestamp_monotonic(self) -> None: 117 | assert self.manager.userspace_timestamp_monotonic > 0 118 | 119 | def test_method_get_object_path(self) -> None: 120 | assert ( 121 | self.manager.get_object_path("ssh.service") 122 | == "/org/freedesktop/systemd1/unit/ssh_2eservice" 123 | ) 124 | 125 | def test_method_list_units(self) -> None: 126 | assert isinstance(self.manager.units[0][0], str) 127 | -------------------------------------------------------------------------------- /tests/cli_output/systemctl-list-timers_1.txt: -------------------------------------------------------------------------------- 1 | NEXT LEFT LAST PASSED UNIT ACTIVATES 2 | Sat 2020-05-16 23:34:57 CEST 20h left Fri 2020-05-15 23:34:57 CEST 3h 39min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service 3 | n/a n/a Thu 2020-03-12 03:09:01 CET 2 months 4 days ago phpsessionclean.timer phpsessionclean.service 4 | 5 | 2 timers listed. 6 | -------------------------------------------------------------------------------- /tests/cli_output/systemctl-list-timers_2.txt: -------------------------------------------------------------------------------- 1 | NEXT LEFT LAST PASSED UNIT ACTIVATES 2 | Sat 2020-05-16 11:26:47 CEST 1min 26s left Sat 2020-05-16 11:24:21 CEST 58s ago dyndns-update-script_nuernberg_dev-eth0_ipv4.timer dyndns-update-script_nuernberg_dev-eth0_ipv4.service 3 | Sat 2020-05-16 11:27:01 CEST 1min 40s left Sat 2020-05-16 11:23:44 CEST 1min 36s ago nsca-localhost.timer nsca-localhost.service 4 | Sat 2020-05-16 11:27:39 CEST 2min 19s left Sat 2020-05-16 11:25:14 CEST 6s ago dyndns-update-script_nrasp_dev-eth0_ipvboth.timer dyndns-update-script_nrasp_dev-eth0_ipvboth.service 5 | Sat 2020-05-16 11:30:00 CEST 4min 39s left Sat 2020-05-16 09:00:00 CEST 2h 25min ago turn-on.timer turn-on.service 6 | Sat 2020-05-16 11:46:11 CEST 20min left Sat 2020-05-16 10:41:47 CEST 43min ago unattended-upgrades-frequent.timer unattended-upgrades-frequent.service 7 | Sat 2020-05-16 12:01:43 CEST 36min left Sat 2020-05-16 11:01:06 CEST 24min ago dfm-auto-root.timer dfm-auto-root.service 8 | Sat 2020-05-16 12:02:32 CEST 37min left Sat 2020-05-16 11:00:42 CEST 24min ago anacron.timer anacron.service 9 | Sat 2020-05-16 16:30:13 CEST 5h 4min left Fri 2020-05-15 16:30:13 CEST 18h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service 10 | Sat 2020-05-16 18:52:48 CEST 7h left Sat 2020-05-16 10:28:05 CEST 57min ago apt-daily.timer apt-daily.service 11 | Sun 2020-05-17 00:00:00 CEST 12h left Sat 2020-05-16 00:00:00 CEST 11h ago gitserver-manager.timer gitserver-manager.service 12 | Sun 2020-05-17 00:00:00 CEST 12h left Sat 2020-05-16 00:00:00 CEST 11h ago oh-my-zsh_jf.timer oh-my-zsh_jf.service 13 | Sun 2020-05-17 00:00:00 CEST 12h left Sat 2020-05-16 00:00:00 CEST 11h ago oh-my-zsh_root.timer oh-my-zsh_root.service 14 | Sun 2020-05-17 06:23:17 CEST 18h left Sat 2020-05-16 06:16:55 CEST 5h 8min ago apt-daily-upgrade.timer apt-daily-upgrade.service 15 | n/a n/a Tue 2020-04-07 05:49:00 CEST 1 months 1 days ago dfm-auto-jf.timer dfm-auto-jf.service 16 | n/a n/a Tue 2020-04-07 04:17:32 CEST 1 months 2 days ago rsync.timer rsync.service 17 | 18 | 15 timers listed. 19 | -------------------------------------------------------------------------------- /tests/cli_output/systemctl-list-timers_all-n-a.txt: -------------------------------------------------------------------------------- 1 | NEXT LEFT LAST PASSED UNIT ACTIVATES 2 | Mon 2022-01-03 10:58:01 CET 5min left n/a n/a systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service 3 | n/a n/a n/a n/a systemd-readahead-done.timer systemd-readahead-done.service 4 | -------------------------------------------------------------------------------- /tests/cli_output/systemctl-list-timers_ok.txt: -------------------------------------------------------------------------------- 1 | NEXT LEFT LAST PASSED UNIT ACTIVATES 2 | Sat 2020-05-16 14:37:33 CEST 1min 16s left Sat 2020-05-16 14:32:27 CEST 3min 49s ago update-motd.timer update-motd.service 3 | Sat 2020-05-16 14:50:58 CEST 14min left Sat 2020-05-16 14:31:56 CEST 4min 20s ago rsync_nnas_data-shares-jf-maps_wnas-data-shares-jf-maps.timer rsync_nnas_data-shares-jf-maps_wnas-data-shares-jf-maps.service 4 | Sat 2020-05-16 15:03:13 CEST 26min left Sat 2020-05-16 14:31:56 CEST 4min 20s ago anacron.timer anacron.service 5 | Sat 2020-05-16 15:11:15 CEST 34min left Sat 2020-05-16 14:31:56 CEST 4min 20s ago apt-daily.timer apt-daily.service 6 | 7 | 5 timers listed. 8 | -------------------------------------------------------------------------------- /tests/cli_output/systemctl-list-units_3units.txt: -------------------------------------------------------------------------------- 1 | UNIT LOAD ACTIVE SUB DESCRIPTION 2 | sockets.target loaded active active Sockets 3 | sound.target loaded active active Sound Card 4 | swap.target loaded active active Swap 5 | 6 | LOAD = Reflects whether the unit definition was properly loaded. 7 | ACTIVE = The high-level unit activation state, i.e. generalization of SUB. 8 | SUB = The low-level unit activation state, values depend on unit type. 9 | 10 | xxx loaded units listed. Pass --all to see loaded but inactive units, too. 11 | To show all installed unit files use 'systemctl list-unit-files'. 12 | -------------------------------------------------------------------------------- /tests/cli_output/systemctl-list-units_failed.txt: -------------------------------------------------------------------------------- 1 | UNIT LOAD ACTIVE SUB DESCRIPTION 2 | rtkit-daemon.service loaded inactive running RealtimeKit Scheduling Policy Service 3 | setvtrgb.service loaded active exited Set console scheme 4 | smartd.service masked failed dead smartd.service 5 | 6 | LOAD = Reflects whether the unit definition was properly loaded. 7 | ACTIVE = The high-level unit activation state, i.e. generalization of SUB. 8 | SUB = The low-level unit activation state, values depend on unit type. 9 | 10 | xxx loaded units listed. Pass --all to see loaded but inactive units, too. 11 | To show all installed unit files use 'systemctl list-unit-files'. 12 | -------------------------------------------------------------------------------- /tests/cli_output/systemctl-list-units_inactive.txt: -------------------------------------------------------------------------------- 1 | UNIT LOAD ACTIVE SUB DESCRIPTION 2 | ansible-pull.service loaded inactive dead system configuration upgrade 3 | ansible-pull.timer loaded active waiting system configuration upgrade 4 | 5 | LOAD = Reflects whether the unit definition was properly loaded. 6 | ACTIVE = The high-level unit activation state, i.e. generalization of SUB. 7 | SUB = The low-level unit activation state, values depend on unit type. 8 | 9 | xxx loaded units listed. Pass --all to see loaded but inactive units, too. 10 | To show all installed unit files use 'systemctl list-unit-files'. 11 | -------------------------------------------------------------------------------- /tests/cli_output/systemctl-list-units_multiple-failure.txt: -------------------------------------------------------------------------------- 1 | UNIT LOAD ACTIVE SUB DESCRIPTION 2 | rtkit-daemon.service loaded failed running RealtimeKit Scheduling Policy Service 3 | setvtrgb.service loaded active exited Set console scheme 4 | smartd.service masked failed dead smartd.service 5 | 6 | LOAD = Reflects whether the unit definition was properly loaded. 7 | ACTIVE = The high-level unit activation state, i.e. generalization of SUB. 8 | SUB = The low-level unit activation state, values depend on unit type. 9 | 10 | xxx loaded units listed. Pass --all to see loaded but inactive units, too. 11 | To show all installed unit files use 'systemctl list-unit-files'. 12 | -------------------------------------------------------------------------------- /tests/cli_output/systemctl-list-units_regexp-excludes.txt: -------------------------------------------------------------------------------- 1 | UNIT LOAD ACTIVE SUB DESCRIPTION 2 | rtkit-daemon.service loaded inactive running RealtimeKit Scheduling Policy Service 3 | setvtrgb.service loaded active exited Set console scheme 4 | user@123.service loaded failed failed User Manager for UID 123 5 | user@234.service loaded failed failed User Manager for UID 234 6 | user@345.service loaded failed failed User Manager for UID 345 7 | 8 | LOAD = Reflects whether the unit definition was properly loaded. 9 | ACTIVE = The high-level unit activation state, i.e. generalization of SUB. 10 | SUB = The low-level unit activation state, values depend on unit type. 11 | 12 | xxx loaded units listed. Pass --all to see loaded but inactive units, too. 13 | To show all installed unit files use 'systemctl list-unit-files'. 14 | -------------------------------------------------------------------------------- /tests/cli_output/systemctl-list-units_v246.txt: -------------------------------------------------------------------------------- 1 | UNIT LOAD ACTIVE SUB DESCRIPTION 2 | dev-block-254:0.device loaded active plugged /dev/block/254:0 3 | dev-block-254:1.device loaded active plugged /dev/block/254:1 4 | dev-disk-by\x2did-ata\x2dHITACHI_HTS723232A7A364_E3834563K5NVSN.device loaded active plugged HITACHI_HTS723232A7A364 5 | dev-disk-by\x2did-ata\x2dHITACHI_HTS723232A7A364_E3834563K5NVSN\x2dpart1.device loaded active plugged HITACHI_HTS723232A7A364 Réservé_au_système 6 | dev-disk-by\x2did-ata\x2dHITACHI_HTS723232A7A364_E3834563K5NVSN\x2dpart2.device loaded active plugged HITACHI_HTS723232A7A364 2 7 | dev-disk-by\x2did-ata\x2dHITACHI_HTS723232A7A364_E3834563K5NVSN\x2dpart4.device loaded active plugged HITACHI_HTS723232A7A364 4 8 | dev-disk-by\x2did-ata\x2dTOSHIBA_MQ01ABD100_Z6M7P2Q0T.device loaded active plugged TOSHIBA_MQ01ABD100 9 | dev-disk-by\x2did-ata\x2dTOSHIBA_MQ01ABD100_Z6M7P2Q0T\x2dpart1.device loaded active plugged TOSHIBA_MQ01ABD100 1 10 | dev-disk-by\x2did-dm\x2dname\x2dcleartera.device loaded active plugged /dev/disk/by-id/dm-name-cleartera 11 | dev-disk-by\x2did-dm\x2dname\x2dcleartext.device loaded active plugged /dev/disk/by-id/dm-name-cleartext 12 | dev-disk-by\x2did-dm\x2dname\x2dlvm\x2droot.device loaded active plugged /dev/disk/by-id/dm-name-lvm-root 13 | dev-disk-by\x2did-dm\x2dname\x2dlvm\x2dswap.device loaded active plugged /dev/disk/by-id/dm-name-lvm-swap 14 | dev-disk-by\x2did-dm\x2dname\x2dteradisk\x2darchive.device loaded active plugged /dev/disk/by-id/dm-name-teradisk-archive 15 | dev-disk-by\x2did-dm\x2dname\x2dteradisk\x2droot\x2d\x2dmirror.device loaded active plugged /dev/disk/by-id/dm-name-teradisk-root--mirror 16 | dev-disk-by\x2did-dm\x2duuid\x2dCRYPT\x2dLUKS1\x2dfc78a5931403454294b5a12bd668726d\x2dcleartext.device loaded active plugged /dev/disk/by-id/dm-uuid-CRYPT-LUKS1-fc78a5931403454294b5a12bd668726d-cleartext 17 | dev-disk-by\x2did-dm\x2duuid\x2dCRYPT\x2dLUKS2\x2d90a57b32d83d45ada696d054f442cc4b\x2dcleartera.device loaded active plugged /dev/disk/by-id/dm-uuid-CRYPT-LUKS2-90a57b32d83d45ada696d054f442cc4b-cleartera 18 | dev-disk-by\x2did-dm\x2duuid\x2dLVM\x2d8n8ccXWKi0IXDGaz6FWiJegeVk4056Bidkjto74u7XIcfmwSqykRL6ZDqgWbLlid.device loaded active plugged /dev/disk/by-id/dm-uuid-LVM-8n8ccXWKi0IXDGaz6FWiJegeVk4056Bidkjto74u7XIcfmwSqykRL6ZDqgWbLlid 19 | dev-disk-by\x2did-dm\x2duuid\x2dLVM\x2d8n8ccXWKi0IXDGaz6FWiJegeVk4056BiuK6cAvrVBMHRbOxIuoMMHpNQzaNNt5aH.device loaded active plugged /dev/disk/by-id/dm-uuid-LVM-8n8ccXWKi0IXDGaz6FWiJegeVk4056BiuK6cAvrVBMHRbOxIuoMMHpNQzaNNt5aH 20 | dev-disk-by\x2did-dm\x2duuid\x2dLVM\x2dOcI4YLGWmw1IljsHTC2U2SVsay4GYYfWAajqhctTQol4FzKCyPdkRJvsAxJaZgmd.device loaded active plugged /dev/disk/by-id/dm-uuid-LVM-OcI4YLGWmw1IljsHTC2U2SVsay4GYYfWAajqhctTQol4FzKCyPdkRJvsAxJaZgmd 21 | dev-disk-by\x2did-dm\x2duuid\x2dLVM\x2dOcI4YLGWmw1IljsHTC2U2SVsay4GYYfWkszJfX4DjIxOLR9TOPZtOaukQzW2zZ2f.device loaded active plugged /dev/disk/by-id/dm-uuid-LVM-OcI4YLGWmw1IljsHTC2U2SVsay4GYYfWkszJfX4DjIxOLR9TOPZtOaukQzW2zZ2f 22 | dev-disk-by\x2did-lvm\x2dpv\x2duuid\x2dcy3zIM\x2dqNKi\x2dEmnR\x2dTLV6\x2dFPsB\x2dTlOF\x2dLJt6g5.device loaded active plugged /dev/disk/by-id/lvm-pv-uuid-cy3zIM-qNKi-EmnR-TLV6-FPsB-TlOF-LJt6g5 23 | dev-disk-by\x2did-lvm\x2dpv\x2duuid\x2dkZk45h\x2djp6P\x2dYI7W\x2dyQjp\x2dzlOa\x2dYusA\x2dA9349g.device loaded active plugged /dev/disk/by-id/lvm-pv-uuid-kZk45h-jp6P-YI7W-yQjp-zlOa-YusA-A9349g 24 | dev-disk-by\x2did-wwn\x2d0x50000397724881e9.device loaded active plugged TOSHIBA_MQ01ABD100 25 | dev-disk-by\x2did-wwn\x2d0x50000397724881e9\x2dpart1.device loaded active plugged TOSHIBA_MQ01ABD100 1 26 | dev-disk-by\x2did-wwn\x2d0x5000cca61decdaaf.device loaded active plugged HITACHI_HTS723232A7A364 27 | dev-disk-by\x2did-wwn\x2d0x5000cca61decdaaf\x2dpart1.device loaded active plugged HITACHI_HTS723232A7A364 Réservé_au_système 28 | dev-disk-by\x2did-wwn\x2d0x5000cca61decdaaf\x2dpart2.device loaded active plugged HITACHI_HTS723232A7A364 2 29 | dev-disk-by\x2did-wwn\x2d0x5000cca61decdaaf\x2dpart4.device loaded active plugged HITACHI_HTS723232A7A364 4 30 | dev-disk-by\x2dlabel-R\xc3\xa9serv\xc3\xa9\x5cx20au\x5cx20syst\xc3\xa8me.device loaded active plugged HITACHI_HTS723232A7A364 Réservé_au_système 31 | dev-disk-by\x2dpartuuid-50afadcc\x2d6ce4\x2d4b4f\x2da592\x2dc5a2b240e579.device loaded active plugged TOSHIBA_MQ01ABD100 1 32 | dev-disk-by\x2dpartuuid-94f13043\x2d01.device loaded active plugged HITACHI_HTS723232A7A364 Réservé_au_système 33 | dev-disk-by\x2dpartuuid-94f13043\x2d02.device loaded active plugged HITACHI_HTS723232A7A364 2 34 | dev-disk-by\x2dpartuuid-94f13043\x2d04.device loaded active plugged HITACHI_HTS723232A7A364 4 35 | dev-disk-by\x2dpath-pci\x2d0000:00:1f.2\x2data\x2d1.0.device loaded active plugged HITACHI_HTS723232A7A364 36 | dev-disk-by\x2dpath-pci\x2d0000:00:1f.2\x2data\x2d1.0\x2dpart1.device loaded active plugged HITACHI_HTS723232A7A364 Réservé_au_système 37 | dev-disk-by\x2dpath-pci\x2d0000:00:1f.2\x2data\x2d1.0\x2dpart2.device loaded active plugged HITACHI_HTS723232A7A364 2 38 | dev-disk-by\x2dpath-pci\x2d0000:00:1f.2\x2data\x2d1.0\x2dpart4.device loaded active plugged HITACHI_HTS723232A7A364 4 39 | dev-disk-by\x2dpath-pci\x2d0000:00:1f.2\x2data\x2d1.device loaded active plugged HITACHI_HTS723232A7A364 40 | dev-disk-by\x2dpath-pci\x2d0000:00:1f.2\x2data\x2d1\x2dpart1.device loaded active plugged HITACHI_HTS723232A7A364 Réservé_au_système 41 | dev-disk-by\x2dpath-pci\x2d0000:00:1f.2\x2data\x2d1\x2dpart2.device loaded active plugged HITACHI_HTS723232A7A364 2 42 | dev-disk-by\x2dpath-pci\x2d0000:00:1f.2\x2data\x2d1\x2dpart4.device loaded active plugged HITACHI_HTS723232A7A364 4 43 | dev-disk-by\x2dpath-pci\x2d0000:00:1f.2\x2data\x2d2.0.device loaded active plugged TOSHIBA_MQ01ABD100 44 | dev-disk-by\x2dpath-pci\x2d0000:00:1f.2\x2data\x2d2.0\x2dpart1.device loaded active plugged TOSHIBA_MQ01ABD100 1 45 | dev-disk-by\x2dpath-pci\x2d0000:00:1f.2\x2data\x2d2.device loaded active plugged TOSHIBA_MQ01ABD100 46 | dev-disk-by\x2dpath-pci\x2d0000:00:1f.2\x2data\x2d2\x2dpart1.device loaded active plugged TOSHIBA_MQ01ABD100 1 47 | dev-disk-by\x2duuid-1881f70f\x2dfbf5\x2d4044\x2dbfe8\x2dd7a6300c6818.device loaded active plugged /dev/disk/by-uuid/1881f70f-fbf5-4044-bfe8-d7a6300c6818 48 | dev-disk-by\x2duuid-50A020EFA020DCEA.device loaded active plugged HITACHI_HTS723232A7A364 Réservé_au_système 49 | dev-disk-by\x2duuid-680b789b\x2d3bd6\x2d4b1c\x2d89e9\x2d660035df92dc.device loaded active plugged /dev/disk/by-uuid/680b789b-3bd6-4b1c-89e9-660035df92dc 50 | dev-disk-by\x2duuid-882C22A02C228970.device loaded active plugged HITACHI_HTS723232A7A364 2 51 | dev-disk-by\x2duuid-90a57b32\x2dd83d\x2d45ad\x2da696\x2dd054f442cc4b.device loaded active plugged TOSHIBA_MQ01ABD100 1 52 | dev-disk-by\x2duuid-d996bd3a\x2dc6fe\x2d4455\x2d9726\x2d2171e0ad8c6c.device loaded active plugged /dev/disk/by-uuid/d996bd3a-c6fe-4455-9726-2171e0ad8c6c 53 | dev-disk-by\x2duuid-fc78a593\x2d1403\x2d4542\x2d94b5\x2da12bd668726d.device loaded active plugged HITACHI_HTS723232A7A364 4 54 | dev-dm\x2d0.device loaded active plugged /dev/dm-0 55 | dev-dm\x2d1.device loaded active plugged /dev/dm-1 56 | dev-dm\x2d2.device loaded active plugged /dev/dm-2 57 | dev-dm\x2d3.device loaded active plugged /dev/dm-3 58 | dev-dm\x2d4.device loaded active plugged /dev/dm-4 59 | dev-dm\x2d5.device loaded active plugged /dev/dm-5 60 | dev-input-mice.device loaded active plugged /dev/input/mice 61 | dev-lvm-root.device loaded active plugged /dev/lvm/root 62 | dev-lvm-swap.device loaded active plugged /dev/lvm/swap 63 | dev-mapper-cleartera.device loaded active plugged /dev/mapper/cleartera 64 | dev-mapper-cleartext.device loaded active plugged /dev/mapper/cleartext 65 | dev-mapper-lvm\x2droot.device loaded active plugged /dev/mapper/lvm-root 66 | dev-mapper-lvm\x2dswap.device loaded active plugged /dev/mapper/lvm-swap 67 | dev-mapper-teradisk\x2darchive.device loaded active plugged /dev/mapper/teradisk-archive 68 | dev-mapper-teradisk\x2droot\x2d\x2dmirror.device loaded active plugged /dev/mapper/teradisk-root--mirror 69 | dev-net-tun.device loaded active plugged /dev/net/tun 70 | dev-ram0.device loaded active plugged /dev/ram0 71 | dev-ram1.device loaded active plugged /dev/ram1 72 | dev-ram10.device loaded active plugged /dev/ram10 73 | dev-ram11.device loaded active plugged /dev/ram11 74 | dev-ram12.device loaded active plugged /dev/ram12 75 | dev-ram13.device loaded active plugged /dev/ram13 76 | dev-ram14.device loaded active plugged /dev/ram14 77 | dev-ram15.device loaded active plugged /dev/ram15 78 | dev-ram2.device loaded active plugged /dev/ram2 79 | dev-ram3.device loaded active plugged /dev/ram3 80 | dev-ram4.device loaded active plugged /dev/ram4 81 | dev-ram5.device loaded active plugged /dev/ram5 82 | dev-ram6.device loaded active plugged /dev/ram6 83 | dev-ram7.device loaded active plugged /dev/ram7 84 | dev-ram8.device loaded active plugged /dev/ram8 85 | dev-ram9.device loaded active plugged /dev/ram9 86 | dev-rfkill.device loaded active plugged /dev/rfkill 87 | dev-sda.device loaded active plugged TOSHIBA_MQ01ABD100 88 | dev-sda1.device loaded active plugged TOSHIBA_MQ01ABD100 1 89 | dev-sdb.device loaded active plugged HITACHI_HTS723232A7A364 90 | dev-sdb1.device loaded active plugged HITACHI_HTS723232A7A364 Réservé_au_système 91 | dev-sdb2.device loaded active plugged HITACHI_HTS723232A7A364 2 92 | dev-sdb4.device loaded active plugged HITACHI_HTS723232A7A364 4 93 | dev-teradisk-archive.device loaded active plugged /dev/teradisk/archive 94 | dev-teradisk-root\x2dmirror.device loaded active plugged /dev/teradisk/root-mirror 95 | dev-ttyS0.device loaded active plugged /dev/ttyS0 96 | dev-ttyS1.device loaded active plugged /dev/ttyS1 97 | dev-ttyS2.device loaded active plugged /dev/ttyS2 98 | dev-ttyS3.device loaded active plugged /dev/ttyS3 99 | dev-zram0.device loaded active plugged /dev/zram0 100 | sys-devices-pci0000:00-0000:00:02.0-backlight-acpi_video0.device loaded active plugged /sys/devices/pci0000:00/0000:00:02.0/backlight/acpi_video0 101 | sys-devices-pci0000:00-0000:00:02.0-drm-card0-card0\x2dLVDS\x2d1-intel_backlight.device loaded active plugged /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-LVDS-1/intel_backlight 102 | sys-devices-pci0000:00-0000:00:19.0-net-enp0s25.device loaded active plugged 82579LM Gigabit Network Connection (Lewisville) (ThinkPad T520) 103 | sys-devices-pci0000:00-0000:00:1b.0-sound-card0.device loaded active plugged 6 Series/C200 Series Chipset Family High Definition Audio Controller 104 | sys-devices-pci0000:00-0000:00:1c.1-0000:03:00.0-net-wlp3s0.device loaded active plugged Centrino Advanced-N 6205 [Taylor Peak] (Centrino Advanced-N 6205 (802.11a/b/g/n)) 105 | sys-devices-pci0000:00-0000:00:1f.2-ata1-host0-target0:0:0-0:0:0:0-block-sdb-sdb1.device loaded active plugged HITACHI_HTS723232A7A364 Réservé_au_système 106 | sys-devices-pci0000:00-0000:00:1f.2-ata1-host0-target0:0:0-0:0:0:0-block-sdb-sdb2.device loaded active plugged HITACHI_HTS723232A7A364 2 107 | sys-devices-pci0000:00-0000:00:1f.2-ata1-host0-target0:0:0-0:0:0:0-block-sdb-sdb4.device loaded active plugged HITACHI_HTS723232A7A364 4 108 | sys-devices-pci0000:00-0000:00:1f.2-ata1-host0-target0:0:0-0:0:0:0-block-sdb.device loaded active plugged HITACHI_HTS723232A7A364 109 | sys-devices-pci0000:00-0000:00:1f.2-ata2-host1-target1:0:0-1:0:0:0-block-sda-sda1.device loaded active plugged TOSHIBA_MQ01ABD100 1 110 | sys-devices-pci0000:00-0000:00:1f.2-ata2-host1-target1:0:0-1:0:0:0-block-sda.device loaded active plugged TOSHIBA_MQ01ABD100 111 | sys-devices-platform-serial8250-tty-ttyS0.device loaded active plugged /sys/devices/platform/serial8250/tty/ttyS0 112 | sys-devices-platform-serial8250-tty-ttyS1.device loaded active plugged /sys/devices/platform/serial8250/tty/ttyS1 113 | sys-devices-platform-serial8250-tty-ttyS2.device loaded active plugged /sys/devices/platform/serial8250/tty/ttyS2 114 | sys-devices-platform-serial8250-tty-ttyS3.device loaded active plugged /sys/devices/platform/serial8250/tty/ttyS3 115 | sys-devices-virtual-block-dm\x2d0.device loaded active plugged /sys/devices/virtual/block/dm-0 116 | sys-devices-virtual-block-dm\x2d1.device loaded active plugged /sys/devices/virtual/block/dm-1 117 | sys-devices-virtual-block-dm\x2d2.device loaded active plugged /sys/devices/virtual/block/dm-2 118 | sys-devices-virtual-block-dm\x2d3.device loaded active plugged /sys/devices/virtual/block/dm-3 119 | sys-devices-virtual-block-dm\x2d4.device loaded active plugged /sys/devices/virtual/block/dm-4 120 | sys-devices-virtual-block-dm\x2d5.device loaded active plugged /sys/devices/virtual/block/dm-5 121 | sys-devices-virtual-block-ram0.device loaded active plugged /sys/devices/virtual/block/ram0 122 | sys-devices-virtual-block-ram1.device loaded active plugged /sys/devices/virtual/block/ram1 123 | sys-devices-virtual-block-ram10.device loaded active plugged /sys/devices/virtual/block/ram10 124 | sys-devices-virtual-block-ram11.device loaded active plugged /sys/devices/virtual/block/ram11 125 | sys-devices-virtual-block-ram12.device loaded active plugged /sys/devices/virtual/block/ram12 126 | sys-devices-virtual-block-ram13.device loaded active plugged /sys/devices/virtual/block/ram13 127 | sys-devices-virtual-block-ram14.device loaded active plugged /sys/devices/virtual/block/ram14 128 | sys-devices-virtual-block-ram15.device loaded active plugged /sys/devices/virtual/block/ram15 129 | sys-devices-virtual-block-ram2.device loaded active plugged /sys/devices/virtual/block/ram2 130 | sys-devices-virtual-block-ram3.device loaded active plugged /sys/devices/virtual/block/ram3 131 | sys-devices-virtual-block-ram4.device loaded active plugged /sys/devices/virtual/block/ram4 132 | sys-devices-virtual-block-ram5.device loaded active plugged /sys/devices/virtual/block/ram5 133 | sys-devices-virtual-block-ram6.device loaded active plugged /sys/devices/virtual/block/ram6 134 | sys-devices-virtual-block-ram7.device loaded active plugged /sys/devices/virtual/block/ram7 135 | sys-devices-virtual-block-ram8.device loaded active plugged /sys/devices/virtual/block/ram8 136 | sys-devices-virtual-block-ram9.device loaded active plugged /sys/devices/virtual/block/ram9 137 | sys-devices-virtual-block-zram0.device loaded active plugged /sys/devices/virtual/block/zram0 138 | sys-devices-virtual-input-mice.device loaded active plugged /sys/devices/virtual/input/mice 139 | sys-devices-virtual-misc-rfkill.device loaded active plugged /sys/devices/virtual/misc/rfkill 140 | sys-devices-virtual-misc-tun.device loaded active plugged /sys/devices/virtual/misc/tun 141 | sys-module-fuse.device loaded active plugged /sys/module/fuse 142 | sys-module-tun.device loaded active plugged /sys/module/tun 143 | sys-subsystem-net-devices-enp0s25.device loaded active plugged 82579LM Gigabit Network Connection (Lewisville) (ThinkPad T520) 144 | sys-subsystem-net-devices-wlp3s0.device loaded active plugged Centrino Advanced-N 6205 [Taylor Peak] (Centrino Advanced-N 6205 (802.11a/b/g/n)) 145 | -.mount loaded active mounted Root Mount 146 | archive.mount loaded active mounted /archive 147 | boot.mount loaded active mounted /boot 148 | dev-hugepages.mount loaded active mounted Huge Pages File System 149 | dev-mqueue.mount loaded active mounted POSIX Message Queue File System 150 | nix-store.mount loaded active mounted /nix/store 151 | run-keys.mount loaded active mounted /run/keys 152 | run-user-1000-gvfs.mount loaded active mounted /run/user/1000/gvfs 153 | run-user-1000.mount loaded active mounted /run/user/1000 154 | run-wrappers.mount loaded active mounted /run/wrappers 155 | sys-fs-fuse-connections.mount loaded active mounted FUSE Control File System 156 | sys-kernel-config.mount loaded inactive dead Kernel Configuration File System 157 | sys-kernel-debug-tracing.mount loaded active mounted /sys/kernel/debug/tracing 158 | sys-kernel-debug.mount loaded active mounted Kernel Debug File System 159 | ● tmp.mount not-found inactive dead tmp.mount 160 | systemd-ask-password-console.path loaded inactive dead Dispatch Password Requests to Console Directory Watch 161 | systemd-ask-password-plymouth.path loaded active running Forward Password Requests to Plymouth Directory Watch 162 | systemd-ask-password-wall.path loaded active waiting Forward Password Requests to Wall Directory Watch 163 | init.scope loaded active running System and Service Manager 164 | session-2.scope loaded active running Session 2 of user symphorien 165 | accounts-daemon.service loaded active running Accounts Service 166 | ● acpid.service not-found inactive dead acpid.service 167 | alsa-store.service loaded active exited Store Sound Card State 168 | audit.service loaded active exited Kernel Auditing 169 | ● auditd.service not-found inactive dead auditd.service 170 | batterycheck.service loaded inactive dead Hibernate on low battery 171 | borgbackup-job-nlaptop.service loaded inactive dead BorgBackup job nlaptop 172 | btrbk-home.service loaded inactive dead Takes BTRFS snapshots and maintains retention policies. 173 | btrbk-remote.service loaded inactive dead Takes BTRFS snapshots and maintains retention policies. 174 | btrfs-autobalance.service loaded inactive dead keep some unallocated space on root btrfs 175 | ● btrfs-defrag.service not-found inactive dead btrfs-defrag.service 176 | ● btrfs-scrub-.service not-found inactive dead btrfs-scrub-.service 177 | cups.service loaded active running CUPS Scheduler 178 | dbus.service loaded active running D-Bus System Message Bus 179 | display-manager.service loaded active running X11 Server 180 | earlyoom.service loaded active running Early OOM Daemon for Linux 181 | emergency.service loaded inactive dead Emergency Shell 182 | ● fcoe.service not-found inactive dead fcoe.service 183 | firewall.service loaded active exited Firewall 184 | getty@tty1.service loaded active running Getty on tty1 185 | getty@tty7.service loaded inactive dead Getty on tty7 186 | gpm.service loaded active running Console Mouse Daemon 187 | hdapsd@sda.service loaded active running hdapsd hard drive active protection system daemon - sda 188 | hdapsd@sdb.service loaded active running hdapsd hard drive active protection system daemon - sdb 189 | ● iscsi.service not-found inactive dead iscsi.service 190 | kmod-static-nodes.service loaded active exited Create list of static device nodes for the current kernel 191 | lvm2-activation-early.service loaded inactive dead LVM direct activation of logical volumes 192 | lvm2-activation-net.service loaded inactive dead LVM direct activation of logical volumes 193 | lvm2-activation.service loaded inactive dead LVM direct activation of logical volumes 194 | lvm2-pvscan@254:0.service loaded active exited LVM event activation on device 254:0 195 | lvm2-pvscan@254:1.service loaded active exited LVM event activation on device 254:1 196 | ● modprobe@drm.service not-found inactive dead modprobe@drm.service 197 | network-local-commands.service loaded active exited Extra networking commands. 198 | network-really-online.service loaded inactive dead wait network to be really online 199 | network-setup.service loaded active exited Networking Setup 200 | NetworkManager-dispatcher.service loaded inactive dead Network Manager Script Dispatcher Service 201 | NetworkManager-wait-online.service loaded active exited Network Manager Wait Online 202 | NetworkManager.service loaded active running Network Manager 203 | nix-daemon.service loaded active running Nix Daemon 204 | ● nixos-upgrade.service not-found inactive dead nixos-upgrade.service 205 | ● nm-wait-online.service loaded failed failed wait for network manager to connect to something 206 | ● openvswitch.service not-found inactive dead openvswitch.service 207 | plymouth-quit-wait.service loaded inactive dead Hold until boot process finishes up 208 | plymouth-quit.service loaded inactive dead Terminate Plymouth Boot Screen 209 | plymouth-read-write.service loaded inactive dead Tell Plymouth To Write Out Runtime Data 210 | plymouth-start.service loaded inactive dead Show Plymouth Boot Screen 211 | polkit.service loaded active running Authorization Manager 212 | post-resume.service loaded inactive dead Post-Resume Actions 213 | powertop.service loaded active exited Powertop tunings 214 | pre-sleep.service loaded inactive dead Pre-Sleep Actions 215 | ● rbdmap.service not-found inactive dead rbdmap.service 216 | ● rc-local.service not-found inactive dead rc-local.service 217 | rescue.service loaded inactive dead Rescue Shell 218 | resolvconf.service loaded active exited resolvconf update 219 | rtkit-daemon.service loaded active running RealtimeKit Scheduling Policy Service 220 | save-hwclock.service loaded inactive dead Save Hardware Clock 221 | sshd.service loaded active running SSH Daemon 222 | ● sssd.service not-found inactive dead sssd.service 223 | ● syslog.service not-found inactive dead syslog.service 224 | systemd-ask-password-console.service loaded inactive dead Dispatch Password Requests to Console 225 | systemd-ask-password-plymouth.service loaded active running Forward Password Requests to Plymouth 226 | systemd-ask-password-wall.service loaded inactive dead Forward Password Requests to Wall 227 | systemd-backlight@backlight:acpi_video0.service loaded active exited Load/Save Screen Backlight Brightness of backlight:acpi_video0 228 | systemd-backlight@backlight:intel_backlight.service loaded active exited Load/Save Screen Backlight Brightness of backlight:intel_backlight 229 | systemd-fsck-root.service loaded inactive dead File System Check on Root Device 230 | systemd-fsck@dev-disk-by\x2duuid-680b789b\x2d3bd6\x2d4b1c\x2d89e9\x2d660035df92dc.service loaded active exited File System Check on /dev/disk/by-uuid/680b789b-3bd6-4b1c-89e9-660035df92dc 231 | systemd-hibernate.service loaded inactive dead Hibernate 232 | ● systemd-hwdb-update.service not-found inactive dead systemd-hwdb-update.service 233 | systemd-hybrid-sleep.service loaded inactive dead Hybrid Suspend+Hibernate 234 | systemd-journal-catalog-update.service loaded inactive dead Rebuild Journal Catalog 235 | systemd-journal-flush.service loaded active exited Flush Journal to Persistent Storage 236 | systemd-journald.service loaded active running Journal Service 237 | systemd-logind.service loaded active running User Login Management 238 | systemd-machined.service loaded inactive dead Virtual Machine and Container Registration Service 239 | systemd-modules-load.service loaded active exited Load Kernel Modules 240 | ● systemd-quotacheck.service not-found inactive dead systemd-quotacheck.service 241 | systemd-random-seed.service loaded active exited Load/Save Random Seed 242 | systemd-remount-fs.service loaded active exited Remount Root and Kernel File Systems 243 | systemd-suspend.service loaded inactive dead Suspend 244 | systemd-sysctl.service loaded active exited Apply Kernel Variables 245 | ● systemd-sysusers.service not-found inactive dead systemd-sysusers.service 246 | systemd-timesyncd.service loaded active running Network Time Synchronization 247 | systemd-tmpfiles-clean.service loaded inactive dead Cleanup of Temporary Directories 248 | systemd-tmpfiles-setup-dev.service loaded active exited Create Static Device Nodes in /dev 249 | systemd-tmpfiles-setup.service loaded active exited Create Volatile Files and Directories 250 | systemd-udev-settle.service loaded active exited Wait for udev To Complete Device Initialization 251 | systemd-udev-trigger.service loaded active exited Coldplug All udev Devices 252 | systemd-udevd.service loaded active running Rule-based Manager for Device Events and Files 253 | systemd-update-done.service loaded inactive dead Update is Completed 254 | systemd-update-utmp.service loaded active exited Update UTMP about System Boot/Shutdown 255 | systemd-user-sessions.service loaded active exited Permit User Sessions 256 | systemd-vconsole-setup.service loaded inactive dead Setup Virtual Console 257 | thinkfan.service loaded active running Thinkfan 258 | tlp-sleep.service loaded inactive dead TLP suspend/resume 259 | tlp.service loaded active exited TLP system startup/shutdown 260 | udisks2.service loaded active running Disk Manager 261 | ● update-locatedb.service not-found inactive dead update-locatedb.service 262 | upower.service loaded active running Daemon for power management 263 | user-runtime-dir@1000.service loaded active exited User Runtime Directory /run/user/1000 264 | user@1000.service loaded active running User Manager for UID 1000 265 | wpa_supplicant.service loaded active running WPA supplicant 266 | zram-init-zram0.service loaded active exited Init swap on zram-based device zram0 267 | zram-reloader.service loaded active exited Reload zram kernel module when number of devices changes 268 | -.slice loaded active active Root Slice 269 | machine.slice loaded inactive dead Virtual Machine and Container Slice 270 | system-getty.slice loaded active active system-getty.slice 271 | system-hdapsd.slice loaded active active system-hdapsd.slice 272 | system-lvm2\x2dpvscan.slice loaded active active system-lvm2\x2dpvscan.slice 273 | system-systemd\x2dbacklight.slice loaded active active system-systemd\x2dbacklight.slice 274 | system-systemd\x2dcoredump.slice loaded active active system-systemd\x2dcoredump.slice 275 | system-systemd\x2dfsck.slice loaded active active system-systemd\x2dfsck.slice 276 | system.slice loaded active active System Slice 277 | user-1000.slice loaded active active user-1000.slice 278 | user-78.slice loaded active active user-78.slice 279 | user.slice loaded active active User and Session Slice 280 | cups.socket loaded active running CUPS Scheduler 281 | dbus.socket loaded active running D-Bus System Message Bus Socket 282 | nix-daemon.socket loaded active running Nix Daemon Socket 283 | syslog.socket loaded inactive dead Syslog Socket 284 | systemd-coredump.socket loaded active listening Process Core Dump Socket 285 | systemd-journald-audit.socket loaded active running Journal Audit Socket 286 | systemd-journald-dev-log.socket loaded active running Journal Socket (/dev/log) 287 | systemd-journald.socket loaded active running Journal Socket 288 | ● systemd-rfkill.socket masked inactive dead systemd-rfkill.socket 289 | systemd-udevd-control.socket loaded active running udev Control Socket 290 | systemd-udevd-kernel.socket loaded active running udev Kernel Socket 291 | dev-zram0.swap loaded active active /dev/zram0 292 | basic.target loaded active active Basic System 293 | ● blockdev@dev-disk-by\x2duuid-1881f70f\x2dfbf5\x2d4044\x2dbfe8\x2dd7a6300c6818.target not-found inactive dead blockdev@dev-disk-by\x2duuid-1881f70f\x2dfbf5\x2d4044\x2dbfe8\x2dd7a6300c6818.target 294 | ● blockdev@dev-disk-by\x2duuid-680b789b\x2d3bd6\x2d4b1c\x2d89e9\x2d660035df92dc.target not-found inactive dead blockdev@dev-disk-by\x2duuid-680b789b\x2d3bd6\x2d4b1c\x2d89e9\x2d660035df92dc.target 295 | ● blockdev@dev-disk-by\x2duuid-d996bd3a\x2dc6fe\x2d4455\x2d9726\x2d2171e0ad8c6c.target not-found inactive dead blockdev@dev-disk-by\x2duuid-d996bd3a\x2dc6fe\x2d4455\x2d9726\x2d2171e0ad8c6c.target 296 | ● blockdev@dev-dm\x2d3.target not-found inactive dead blockdev@dev-dm\x2d3.target 297 | ● blockdev@dev-mapper-lvm\x2dswap.target not-found inactive dead blockdev@dev-mapper-lvm\x2dswap.target 298 | ● blockdev@dev-mapper-teradisk\x2darchive.target not-found inactive dead blockdev@dev-mapper-teradisk\x2darchive.target 299 | ● blockdev@dev-zram0.target not-found inactive dead blockdev@dev-zram0.target 300 | cryptsetup.target loaded active active Local Encrypted Volumes 301 | emergency.target loaded inactive dead Emergency Mode 302 | getty-pre.target loaded inactive dead Login Prompts (Pre) 303 | getty.target loaded active active Login Prompts 304 | graphical.target loaded active active Graphical Interface 305 | hibernate.target loaded inactive dead Hibernate 306 | hybrid-sleep.target loaded inactive dead Hybrid Suspend+Hibernate 307 | ● initrd-switch-root.target not-found inactive dead initrd-switch-root.target 308 | local-fs-pre.target loaded active active Local File Systems (Pre) 309 | local-fs.target loaded active active Local File Systems 310 | machines.target loaded active active Containers 311 | multi-user.target loaded active active Multi-User System 312 | network-interfaces.target loaded active active All Network Interfaces (deprecated) 313 | network-online.target loaded active active Network is Online 314 | network-pre.target loaded active active Network (Pre) 315 | network.target loaded active active Network 316 | nss-lookup.target loaded active active Host and Network Name Lookups 317 | nss-user-lookup.target loaded active active User and Group Name Lookups 318 | paths.target loaded active active Paths 319 | post-resume.target loaded inactive dead Post-Resume Actions 320 | remote-fs-pre.target loaded inactive dead Remote File Systems (Pre) 321 | remote-fs.target loaded active active Remote File Systems 322 | rescue.target loaded inactive dead Rescue Mode 323 | shutdown.target loaded inactive dead Shutdown 324 | sleep.target loaded inactive dead Sleep 325 | slices.target loaded active active Slices 326 | sockets.target loaded active active Sockets 327 | sound.target loaded active active Sound Card 328 | suspend.target loaded inactive dead Suspend 329 | swap.target loaded active active Swap 330 | sysinit.target loaded active active System Initialization 331 | ● time-set.target not-found inactive dead time-set.target 332 | time-sync.target loaded active active System Time Synchronized 333 | timers.target loaded active active Timers 334 | umount.target loaded inactive dead Unmount All Filesystems 335 | batterycheck.timer loaded active waiting Update timer for locate database 336 | borgbackup-job-nlaptop.timer loaded active waiting borgbackup-job-nlaptop.timer 337 | btrbk-home.timer loaded active waiting Timer to take BTRFS snapshots and maintain retention policies. 338 | btrbk-remote.timer loaded active waiting Timer to take BTRFS snapshots and maintain retention policies. 339 | btrfs-autobalance.timer loaded active waiting btrfs-autobalance.timer 340 | systemd-tmpfiles-clean.timer loaded active waiting Daily Cleanup of Temporary Directories 341 | 342 | LOAD = Reflects whether the unit definition was properly loaded. 343 | ACTIVE = The high-level unit activation state, i.e. generalization of SUB. 344 | SUB = The low-level unit activation state, values depend on unit type. 345 | 346 | xxx loaded units listed. Pass --all to see loaded but inactive units, too. 347 | To show all installed unit files use 'systemctl list-unit-files'. 348 | -------------------------------------------------------------------------------- /tests/cli_output/systemctl-show-ansible-pull_inactive.txt: -------------------------------------------------------------------------------- 1 | Id=ansible-pull.service 2 | ActiveState=inactive 3 | SubState=running 4 | LoadState=loaded 5 | -------------------------------------------------------------------------------- /tests/cli_output/systemctl-show-nginx_active.txt: -------------------------------------------------------------------------------- 1 | Id=nginx.service 2 | LoadState=loaded 3 | ActiveState=active 4 | SubState=running 5 | -------------------------------------------------------------------------------- /tests/cli_output/systemd-analyze_12.345.txt: -------------------------------------------------------------------------------- 1 | Startup finished in 5.081s (kernel) + 34min 41.211s (userspace) = 34min 46.292s 2 | graphical.target reached after 12.345s in userspace 3 | -------------------------------------------------------------------------------- /tests/cli_output/systemd-analyze_not-finished.txt: -------------------------------------------------------------------------------- 1 | Bootup is not yet finished. Please try again later. 2 | -------------------------------------------------------------------------------- /tests/helper.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import io 4 | import os 5 | import typing 6 | from contextlib import redirect_stderr, redirect_stdout 7 | from os import path 8 | from unittest import TestCase, mock 9 | from unittest.mock import Mock 10 | 11 | import check_systemd 12 | 13 | test: TestCase = TestCase() 14 | test.maxDiff = None 15 | 16 | 17 | class AddBin(object): 18 | """ 19 | :param string bin_path: Path relative to the test folder. 20 | """ 21 | 22 | bin_path: str 23 | 24 | old_path: str 25 | 26 | def __init__(self, bin_path: str) -> None: 27 | self.bin_path = bin_path 28 | self.old_path = os.environ["PATH"] 29 | 30 | def __enter__(self) -> None: 31 | BIN = os.path.abspath(os.path.join(os.path.dirname(__file__), self.bin_path)) 32 | os.environ["PATH"] = BIN + ":" + os.environ["PATH"] 33 | 34 | def __exit__(self) -> None: 35 | os.environ["PATH"] = self.old_path 36 | 37 | 38 | def get_cli_output_path(file_name: str) -> str: 39 | return path.join(path.dirname(path.abspath(__file__)), "cli_output", file_name) 40 | 41 | 42 | def convert_to_bytes(file_name_or_str: str) -> bytes: 43 | """Read a text file as bytes or convert a string into bytes. 44 | 45 | :param file_name_or_str: The name of a text file which is placed in the 46 | folder ``cli_output`` or a string that is converted to bytes. 47 | 48 | :return: The text in byte format. 49 | """ 50 | output_path = get_cli_output_path(file_name_or_str) 51 | if os.path.exists(output_path): 52 | in_file = open(output_path, "rb") 53 | data = in_file.read() 54 | in_file.close() 55 | return data 56 | else: 57 | return file_name_or_str.encode() 58 | 59 | 60 | def MPopen( 61 | returncode: int = 0, stdout: str | None = None, stderr: str | None = None 62 | ) -> Mock: 63 | """A mocked version of ``subprocess.POpen``.""" 64 | mock = Mock() 65 | mock.returncode = returncode 66 | stdout_bytes = None 67 | if stdout: 68 | stdout_bytes = convert_to_bytes(stdout) 69 | 70 | stderr_bytes = None 71 | if stderr: 72 | stderr_bytes = convert_to_bytes(stderr) 73 | mock.communicate.return_value = (stdout_bytes, stderr_bytes) 74 | return mock 75 | 76 | 77 | def get_mocks_for_popen(*stdout: str) -> list[Mock]: 78 | """ 79 | Create multiple mock objects which are suitable to mimic multiple calls of 80 | ``subprocess.Popen()``. 81 | 82 | Assign the result of this function to the attribute ``side_effect``: 83 | ``Popen.side_effect = result_of_is_function`` 84 | 85 | :param stdout: Multiple strings or multiple file names of text files inside 86 | the folder ``cli_ouput``. 87 | 88 | :return: A list of mock objects for the class ``subprocess.Popen()``. 89 | """ 90 | mocks: list[Mock] = [] 91 | for out in stdout: 92 | mocks.append(MPopen(stdout=out)) 93 | return mocks 94 | 95 | 96 | class MockResult: 97 | """A class to collect all results of a mocked execution of the main 98 | function.""" 99 | 100 | __sys_exit: Mock 101 | __stdout: str | None 102 | __stderr: str | None 103 | 104 | def __init__(self, sys_exit_mock: Mock, stdout: str, stderr: str) -> None: 105 | self.__sys_exit = sys_exit_mock 106 | self.__stdout = stdout 107 | self.__stderr = stderr 108 | 109 | @property 110 | def stdout(self) -> str | None: 111 | """The function ``redirect_stdout()`` is used to capture the ``stdout`` 112 | output.""" 113 | if self.__stdout: 114 | return self.__stdout 115 | return None 116 | 117 | @property 118 | def stderr(self) -> str | None: 119 | """The function ``redirect_stderr()`` is used to capture the ``stderr`` 120 | output.""" 121 | if self.__stderr: 122 | return self.__stderr 123 | return None 124 | 125 | @property 126 | def output(self) -> str: 127 | """A combined string of the captured stdout, stderr and the print 128 | calls. Somehow the whole stdout couldn’t be read. The help text could 129 | be read, but not the plugin output using the function 130 | ``redirect_stdout()``.""" 131 | out: str = "" 132 | 133 | if self.__stdout: 134 | out += self.__stdout 135 | 136 | if self.__stderr: 137 | out += self.__stderr 138 | 139 | return out 140 | 141 | @property 142 | def exitcode(self) -> int: 143 | """The captured exit code""" 144 | return int(self.__sys_exit.call_args[0][0]) 145 | 146 | def assert_exitcode(self, exitcode: int) -> None: 147 | assert self.exitcode == exitcode 148 | 149 | def assert_ok(self) -> None: 150 | self.assert_exitcode(0) 151 | 152 | def assert_warn(self) -> None: 153 | self.assert_exitcode(1) 154 | 155 | def assert_critical(self) -> None: 156 | self.assert_exitcode(2) 157 | 158 | def assert_unknown(self) -> None: 159 | self.assert_exitcode(3) 160 | 161 | @property 162 | def first_line(self) -> str | None: 163 | """The first line of the stdout output without a newline break at the 164 | end as a string. 165 | """ 166 | if self.output: 167 | return self.output.split("\n", 1)[0] 168 | return None 169 | 170 | def assert_first_line(self, first_line: str) -> None: 171 | assert self.first_line == first_line 172 | 173 | 174 | def execute_main( 175 | argv: list[str] = ["check_systemd.py"], 176 | stdout: list[str] = [ 177 | "systemctl-list-units_ok.txt", 178 | "systemd-analyze_12.345.txt", 179 | ], 180 | popen: typing.Iterable[Mock] | None = None, 181 | ) -> MockResult: 182 | """Execute the main function with a lot of patched functions and classes. 183 | 184 | :param argv: A list of command line arguments, e. g. 185 | ``argv=['-u', 'nginx.service']`` 186 | 187 | :param stdout: A list of file names of files in the directory 188 | ``test/cli_output``. You have to specify as many text files as there 189 | are calls of the function ``subprocess.Popen``: 190 | 191 | * Line 334 192 | ``p = subprocess.Popen(['systemctl', 'list-units', '--all']``, 193 | ``SystemctlListUnitsResource`` 194 | * Line 460 195 | ``p = subprocess.Popen(['systemd-analyze']``, 196 | ``SystemdAnalyseResource`` 197 | * Line 576 198 | ``p = subprocess.Popen(['systemctl', 'list-timers', '--all']``, 199 | ``SystemctlListTimersResource`` 200 | * Line 656 201 | ``p = subprocess.Popen(['systemctl', 'is-active', self.unit]``, 202 | ``SystemctlIsActiveResource`` 203 | 204 | :param popen: Some mocked Popen classes. 205 | 206 | :param int analyze_returncode: The first call `systemctl analyze` to check 207 | if the startup process is finished 208 | 209 | :return: The results are assembled in the class ``MockResult`` 210 | """ 211 | if not argv or argv[0] != "check_systemd.py": 212 | argv.insert(0, "check_systemd.py") 213 | with ( 214 | mock.patch("sys.exit") as sys_exit, 215 | mock.patch("check_systemd.subprocess.Popen") as Popen, 216 | mock.patch("sys.argv", argv), 217 | ): 218 | if popen: 219 | Popen.side_effect = popen 220 | else: 221 | Popen.side_effect = get_mocks_for_popen(*stdout) 222 | 223 | file_stdout: io.StringIO = io.StringIO() 224 | file_stderr: io.StringIO = io.StringIO() 225 | with redirect_stdout(file_stdout), redirect_stderr(file_stderr): 226 | check_systemd.main() # type: ignore 227 | 228 | return MockResult( 229 | sys_exit_mock=sys_exit, 230 | stdout=file_stdout.getvalue(), 231 | stderr=file_stderr.getvalue(), 232 | ) 233 | 234 | 235 | class Expected: 236 | startup_time = "startup_time=12.345;60;120" 237 | """``startup_time=12.345;60;120``""" 238 | -------------------------------------------------------------------------------- /tests/test_argparse.py: -------------------------------------------------------------------------------- 1 | """Test the command line interface. The CLI interface is implemented with 2 | argparse.""" 3 | 4 | from __future__ import annotations 5 | 6 | import io 7 | import subprocess 8 | from contextlib import redirect_stderr 9 | 10 | import pytest 11 | 12 | import check_systemd 13 | from check_systemd import get_argparser 14 | from tests.helper import execute_main 15 | 16 | 17 | class TestFromFunction: 18 | def test_default(self) -> None: 19 | opts = get_argparser().parse_args([]) 20 | assert "cli" == opts.data_source 21 | 22 | def test_dbus(self) -> None: 23 | opts = get_argparser().parse_args(["--dbus"]) 24 | assert "dbus" == opts.data_source 25 | 26 | def test_cli(self) -> None: 27 | opts = get_argparser().parse_args(["--cli"]) 28 | assert "cli" == opts.data_source 29 | 30 | def test_exclusive_cli_dbus(self) -> None: 31 | dev_null = io.StringIO() 32 | with pytest.raises(SystemExit) as cm, redirect_stderr(dev_null): 33 | get_argparser().parse_args(["--cli", "--dbus"]) 34 | assert cm.value.code == 2 35 | 36 | 37 | class TestWithMocking: 38 | def test_without_arguments(self) -> None: 39 | result = execute_main() 40 | result.assert_ok() 41 | 42 | def test_help_short(self) -> None: 43 | result = execute_main(argv=["-h"]) 44 | assert "usage: check_systemd" in result.output 45 | 46 | def test_help_long(self) -> None: 47 | result = execute_main(argv=["--help"]) 48 | assert "usage: check_systemd" in result.output 49 | 50 | def test_version_short(self) -> None: 51 | result = execute_main(argv=["-V"]) 52 | assert "check_systemd " + check_systemd.__version__ in result.output 53 | 54 | def test_version_long(self) -> None: 55 | result = execute_main(argv=["--version"]) 56 | assert "check_systemd " + check_systemd.__version__ in result.output 57 | 58 | 59 | class TestWithSubprocess: 60 | def test_help(self) -> None: 61 | process = subprocess.run( 62 | ["./check_systemd.py", "--help"], encoding="utf-8", stdout=subprocess.PIPE 63 | ) 64 | assert process.returncode == 0 65 | assert "usage: check_systemd" in process.stdout 66 | 67 | def test_version(self) -> None: 68 | process = subprocess.run( 69 | ["./check_systemd.py", "--version"], 70 | encoding="utf-8", 71 | stdout=subprocess.PIPE, 72 | ) 73 | assert process.returncode == 0 74 | assert "check_systemd " + check_systemd.__version__ in process.stdout 75 | 76 | def test_exclusive_cli_dbus(self) -> None: 77 | process = subprocess.run( 78 | ["./check_systemd.py", "--cli", "--dbus"], 79 | encoding="utf-8", 80 | stderr=subprocess.PIPE, 81 | ) 82 | assert process.returncode == 2 83 | assert ( 84 | "error: argument --dbus: not allowed with argument --cli" in process.stderr 85 | ) 86 | 87 | def test_entry_point(self) -> None: 88 | process = subprocess.run( 89 | ["check_systemd", "--help"], encoding="utf-8", stdout=subprocess.PIPE 90 | ) 91 | assert process.returncode == 0 92 | assert "check_systemd" in process.stdout 93 | -------------------------------------------------------------------------------- /tests/test_boot_not_finised.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from tests.helper import MPopen, execute_main 4 | 5 | 6 | class TestBootupNotFinished: 7 | def test_bootup_not_finished(self) -> None: 8 | result = execute_main( 9 | argv=["--no-performance-data"], 10 | popen=( 11 | MPopen(stdout="systemctl-list-units_ok.txt"), 12 | MPopen(returncode=1, stderr="systemd-analyze_not-finished.txt"), 13 | ), 14 | ) 15 | result.assert_ok() 16 | result.assert_first_line("SYSTEMD OK - all") 17 | 18 | def test_bootup_not_finished_verbose(self) -> None: 19 | self.maxDiff = None 20 | result = execute_main( 21 | argv=["--verbose"], 22 | popen=( 23 | MPopen(stdout="systemctl-list-units_ok.txt"), 24 | MPopen(returncode=1, stderr="systemd-analyze_not-finished.txt"), 25 | ), 26 | ) 27 | 28 | result.assert_ok() 29 | assert "SYSTEMD OK - all\n" in result.output 30 | -------------------------------------------------------------------------------- /tests/test_data_acquisition_units.py: -------------------------------------------------------------------------------- 1 | """Tests related to data acquisition of systemd units.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import Sequence 6 | 7 | from check_systemd import Source 8 | 9 | Unit = Source.Unit 10 | Cache = Source.Cache 11 | NameFilter = Source.NameFilter 12 | 13 | unit_modem_manager = Unit( 14 | name="ModemManager.service", 15 | active_state="active", 16 | sub_state="running", 17 | load_state="loaded", 18 | ) 19 | unit_mongod = Unit( 20 | name="mongod.service", 21 | active_state="failed", 22 | sub_state="running", 23 | load_state="loaded", 24 | ) 25 | unit_mysql = Unit( 26 | name="mysql.service", 27 | active_state="active", 28 | sub_state="running", 29 | load_state="loaded", 30 | ) 31 | unit_named = Unit( 32 | name="named.service", 33 | active_state="active", 34 | sub_state="running", 35 | load_state="loaded", 36 | ) 37 | unit_networking = Unit( 38 | name="networking.mount", 39 | active_state="active", 40 | sub_state="mounting-done", 41 | load_state="loaded", 42 | ) 43 | unit_nginx = Unit( 44 | name="nginx.service", 45 | active_state="active", 46 | sub_state="running", 47 | load_state="loaded", 48 | ) 49 | unit_nmdb = Unit( 50 | name="nmbd.timer", active_state="active", sub_state="running", load_state="loaded" 51 | ) 52 | unit_php = Unit( 53 | name="php7.4-fpm.service", 54 | active_state="active", 55 | sub_state="running", 56 | load_state="loaded", 57 | ) 58 | 59 | 60 | class TestClassUnit: 61 | def test_initialization(self) -> None: 62 | unit = Unit( 63 | name="test.service", 64 | active_state="active", 65 | sub_state="running", 66 | load_state="loaded", 67 | ) 68 | assert "test.service" == unit.name 69 | assert "active" == unit.active_state 70 | assert "running" == unit.sub_state 71 | assert "loaded" == unit.load_state 72 | assert "active" == unit.active_state 73 | 74 | 75 | class TestClassCache: 76 | unit_cache: Source.Cache[Unit] 77 | 78 | def setup_method(self) -> None: 79 | self.unit_cache = Source.Cache[Unit]() 80 | self.unit_cache.add(unit_modem_manager.name, unit_modem_manager) 81 | self.unit_cache.add(unit_mongod.name, unit_mongod) 82 | self.unit_cache.add(unit_mysql.name, unit_mysql) 83 | self.unit_cache.add(unit_named.name, unit_named) 84 | self.unit_cache.add(unit_networking.name, unit_networking) 85 | self.unit_cache.add(unit_nginx.name, unit_nginx) 86 | self.unit_cache.add(unit_modem_manager.name, unit_modem_manager) 87 | self.unit_cache.add(unit_nmdb.name, unit_nmdb) 88 | self.unit_cache.add(unit_php.name, unit_php) 89 | 90 | def filter( 91 | self, 92 | include: str | Sequence[str] | None = None, 93 | exclude: str | Sequence[str] | None = None, 94 | ) -> list[str]: 95 | units: list[str] = [] 96 | for unit in self.unit_cache.filter(include=include, exclude=exclude): 97 | units.append(unit.name) 98 | return units 99 | 100 | def test_iterator(self) -> None: 101 | units = list(self.unit_cache) 102 | assert units[0].name == "ModemManager.service" 103 | 104 | def test_method_get(self) -> None: 105 | unit = self.unit_cache.get(name="ModemManager.service") 106 | assert unit 107 | assert "ModemManager.service" == unit.name 108 | 109 | def test_method_list(self) -> None: 110 | units = self.filter() 111 | assert 8 == len(units) 112 | 113 | def test_method_list_include(self) -> None: 114 | units = self.filter(include="XXX") 115 | assert 0 == len(units) 116 | 117 | units = self.filter(include="named.service") 118 | assert 1 == len(units) 119 | 120 | units = self.filter(include="n.*") 121 | assert 4 == len(units) 122 | 123 | def test_method_list_include_multiple(self) -> None: 124 | units = self.filter(include=("n.*", "p.*")) 125 | assert 5 == len(units) 126 | 127 | def test_method_list_exclude(self) -> None: 128 | units = self.filter(exclude="named.service") 129 | assert 7 == len(units) 130 | 131 | units = self.filter(exclude=r".*\.(mount|timer)") 132 | assert 6 == len(units) 133 | 134 | def test_method_list_exclude_multiple(self) -> None: 135 | units = self.filter(exclude=("named.service", "nmbd.timer")) 136 | assert 6 == len(units) 137 | 138 | def test_method_count_by_states(self) -> None: 139 | counter = self.unit_cache.count_by_states( 140 | ("active_state:active", "active_state:failed") 141 | ) 142 | assert counter["active_state:active"] == 7 143 | assert counter["active_state:failed"] == 1 144 | 145 | 146 | class TestClassNameFilter: 147 | __filter: NameFilter 148 | 149 | def setup_method(self) -> None: 150 | self.__filter = NameFilter() 151 | self.__filter.add("php7.4-fpm.service") 152 | self.__filter.add("ModemManager.service") 153 | self.__filter.add("mongod.service") 154 | self.__filter.add("mysql.service") 155 | self.__filter.add("named.service") 156 | self.__filter.add("networking.mount") 157 | self.__filter.add("nginx.service") 158 | self.__filter.add("nmbd.timer") 159 | 160 | def filter( 161 | self, 162 | include: str | Sequence[str] | None = None, 163 | exclude: str | Sequence[str] | None = None, 164 | ) -> list[str]: 165 | unit_names: list[str] = [] 166 | for unit_name in self.__filter.filter(include=include, exclude=exclude): 167 | unit_names.append(unit_name) 168 | return unit_names 169 | 170 | def test_initialization_with_arg(self) -> None: 171 | filter = NameFilter(["test1.service", "test2.service"]) 172 | assert 2 == len(filter.get()) 173 | 174 | def test_iterator(self) -> None: 175 | names: list[str] = [] 176 | for name in self.__filter: 177 | names.append(name) 178 | assert names == [ 179 | "ModemManager.service", 180 | "mongod.service", 181 | "mysql.service", 182 | "named.service", 183 | "networking.mount", 184 | "nginx.service", 185 | "nmbd.timer", 186 | "php7.4-fpm.service", 187 | ] 188 | 189 | def test_method_list(self) -> None: 190 | units = self.filter() 191 | assert 8 == len(units) 192 | 193 | def test_method_list_include(self) -> None: 194 | units = self.filter(include="XXX") 195 | assert 0 == len(units) 196 | 197 | units = self.filter(include="named.service") 198 | assert 1 == len(units) 199 | 200 | units = self.filter(include="n.*") 201 | assert 4 == len(units) 202 | 203 | def test_method_list_include_multiple(self) -> None: 204 | units = self.filter(include=("n.*", "p.*")) 205 | assert 5 == len(units) 206 | 207 | def test_method_list_exclude(self) -> None: 208 | units = self.filter(exclude="named.service") 209 | assert 7 == len(units) 210 | 211 | units = self.filter(exclude=r".*\.(mount|timer)") 212 | assert 6 == len(units) 213 | 214 | def test_method_list_exclude_multiple(self) -> None: 215 | units = self.filter(exclude=("named.service", "nmbd.timer")) 216 | assert 6 == len(units) 217 | 218 | def test_method_list_include_exclude_empty_list(self) -> None: 219 | units = self.filter(include=[], exclude=[]) 220 | assert 8 == len(units) 221 | -------------------------------------------------------------------------------- /tests/test_data_source_cli.py: -------------------------------------------------------------------------------- 1 | """Unit tests""" 2 | 3 | from __future__ import annotations 4 | 5 | from unittest.mock import patch 6 | 7 | from check_systemd import CliSource 8 | from tests.helper import get_mocks_for_popen 9 | 10 | 11 | def test_collect_properties() -> None: 12 | with patch("check_systemd.subprocess.Popen") as Popen: 13 | Popen.return_value = get_mocks_for_popen("systemctl-show-nginx_active.txt")[0] 14 | unit = CliSource().get_unit("nginx.service") 15 | assert unit.name == "nginx.service" 16 | assert unit.active_state == "active" 17 | assert unit.sub_state == "running" 18 | assert unit.load_state == "loaded" 19 | -------------------------------------------------------------------------------- /tests/test_dbus.py: -------------------------------------------------------------------------------- 1 | """Test the D-Bus API as a data source.""" 2 | 3 | from __future__ import annotations 4 | 5 | from unittest.mock import patch 6 | 7 | import check_systemd 8 | 9 | 10 | class TestDbus: 11 | def test_mocking(self) -> None: 12 | with ( 13 | patch("sys.exit"), 14 | patch("check_systemd.is_dbus"), 15 | patch("sys.argv", ["check_systemd.py", "--dbus"]), 16 | ): 17 | check_systemd.main() # type: ignore 18 | -------------------------------------------------------------------------------- /tests/test_option_exclude.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from tests.helper import MockResult, execute_main 4 | 5 | 6 | def execute_with_opt_e(argv: list[str], unit_suffix: str = "failed") -> MockResult: 7 | return execute_main( 8 | argv=argv, 9 | stdout=[ 10 | "systemctl-list-units_{}.txt".format(unit_suffix), 11 | "systemd-analyze_12.345.txt", 12 | ], 13 | ) 14 | 15 | 16 | class TestOptionExclude: 17 | def test_known_service(self) -> None: 18 | result = execute_with_opt_e( 19 | argv=["-e", "smartd.service", "--no-performance-data"], 20 | unit_suffix="failed", 21 | ) 22 | result.assert_ok() 23 | result.assert_first_line("SYSTEMD OK - all") 24 | 25 | def test_unknown_service(self) -> None: 26 | result = execute_with_opt_e( 27 | argv=["-e", "testX.service", "--no-performance-data"], 28 | unit_suffix="failed", 29 | ) 30 | result.assert_critical() 31 | result.assert_first_line("SYSTEMD CRITICAL - smartd.service: failed") 32 | 33 | def test_regexp(self) -> None: 34 | result = execute_with_opt_e( 35 | argv=["-e", "user@\\d+\\.service", "--no-performance-data"], 36 | unit_suffix="regexp-excludes", 37 | ) 38 | result.assert_ok() 39 | result.assert_first_line("SYSTEMD OK - all") 40 | 41 | def test_regexp_dot(self) -> None: 42 | result = execute_with_opt_e( 43 | argv=["-e", ".*", "--no-performance-data"], 44 | unit_suffix="regexp-excludes", 45 | ) 46 | result.assert_unknown() 47 | 48 | def test_invalid_regexp(self) -> None: 49 | result = execute_with_opt_e( 50 | argv=["-e", "*service"], 51 | unit_suffix="ok", 52 | ) 53 | result.assert_unknown() 54 | result.assert_first_line( 55 | "SYSTEMD UNKNOWN: check_systemd.CheckSystemdRegexpError: " 56 | "Invalid regular expression: '*service'" 57 | ) 58 | -------------------------------------------------------------------------------- /tests/test_option_ignore_inactive_state.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from tests.helper import execute_main 4 | 5 | 6 | class TestOptionIgnoreInactiveState: 7 | def test_with(self) -> None: 8 | result = execute_main( 9 | argv=["--unit", "ansible-pull.service"], 10 | stdout=[ 11 | "systemctl-list-units_inactive.txt", 12 | "systemctl-show-ansible-pull_inactive.txt", 13 | "systemd-analyze_12.345.txt", 14 | ], 15 | ) 16 | result.assert_ok() 17 | result.assert_first_line( 18 | "SYSTEMD OK - ansible-pull.service: inactive | " 19 | "count_units=2 startup_time=12.3;60;120 " 20 | "units_activating=0 units_active=1 units_failed=0 units_inactive=1" 21 | ) 22 | 23 | def test_ok(self) -> None: 24 | result = execute_main( 25 | argv=["--ignore-inactive-state", "--unit", "ansible-pull.service"], 26 | stdout=[ 27 | "systemctl-list-units_inactive.txt", 28 | "systemctl-show-ansible-pull_inactive.txt", 29 | "systemd-analyze_12.345.txt", 30 | ], 31 | ) 32 | result.assert_ok() 33 | result.assert_first_line( 34 | "SYSTEMD OK - ansible-pull.service: inactive | " 35 | "count_units=2 startup_time=12.3;60;120 " 36 | "units_activating=0 units_active=1 units_failed=0 units_inactive=1" 37 | ) 38 | -------------------------------------------------------------------------------- /tests/test_option_unit.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from tests.helper import execute_main 4 | 5 | 6 | def execute_with_opt_u(argv: list[str], list_units: str = "ok"): 7 | if "--no-performance-data" not in argv: 8 | argv.append("--no-performance-data") 9 | return execute_main( 10 | argv=argv, 11 | stdout=[ 12 | "systemctl-list-units_{}.txt".format(list_units), 13 | "systemctl-show-nginx_active.txt", 14 | "systemd-analyze_12.345.txt", 15 | ], 16 | ) 17 | 18 | 19 | class TestOptionUnit: 20 | def test_ok(self) -> None: 21 | result = execute_with_opt_u(argv=["--unit", "nginx.service"], list_units="ok") 22 | result.assert_ok() 23 | result.assert_first_line("SYSTEMD OK - nginx.service: active") 24 | 25 | def test_failed(self) -> None: 26 | result = execute_with_opt_u( 27 | argv=["--unit", "smartd.service"], list_units="failed" 28 | ) 29 | result.assert_critical() 30 | result.assert_first_line("SYSTEMD CRITICAL - smartd.service: failed") 31 | 32 | def test_different_unit_name(self) -> None: 33 | result = execute_with_opt_u(argv=["--unit", "XXXXX.service"], list_units="ok") 34 | result.assert_unknown() 35 | result.assert_first_line( 36 | "SYSTEMD UNKNOWN: ValueError: Please verify your --include-* and " 37 | "--exclude-* options. No units have been added for testing." 38 | ) 39 | -------------------------------------------------------------------------------- /tests/test_performance_data.py: -------------------------------------------------------------------------------- 1 | from tests.helper import execute_main 2 | 3 | 4 | class TestPerformanceData: 5 | def test_ok(self) -> None: 6 | result = execute_main(argv=["--performance-data"]) 7 | result.assert_ok() 8 | result.assert_first_line( 9 | "SYSTEMD OK - all " 10 | "| count_units=386 startup_time=12.3;60;120 " 11 | "units_activating=0 " 12 | "units_active=275 units_failed=0 units_inactive=111" 13 | ) 14 | 15 | def test_dead_timers(self) -> None: 16 | result = execute_main( 17 | argv=["--timers"], 18 | stdout=[ 19 | "systemctl-list-units_3units.txt", 20 | "systemd-analyze_12.345.txt", 21 | "systemctl-list-timers_1.txt", 22 | ], 23 | ) 24 | result.assert_critical() 25 | result.assert_first_line( 26 | "SYSTEMD CRITICAL - phpsessionclean.timer " 27 | "| count_units=3 startup_time=12.3;60;120 " 28 | "units_activating=0 " 29 | "units_active=3 units_failed=0 units_inactive=0" 30 | ) 31 | 32 | def test_options_exclude(self) -> None: 33 | result = execute_main( 34 | argv=["-e", "testX.service"], 35 | stdout=[ 36 | "systemctl-list-units_failed.txt", 37 | "systemd-analyze_12.345.txt", 38 | ], 39 | ) 40 | result.assert_critical() 41 | result.assert_first_line( 42 | "SYSTEMD CRITICAL - smartd.service: failed | count_units=3 " 43 | "startup_time=12.3;60;120 " 44 | "units_activating=0 units_active=1 units_failed=1 " 45 | "units_inactive=1" 46 | ) 47 | -------------------------------------------------------------------------------- /tests/test_scope_startup_time.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from tests.helper import execute_main 4 | 5 | 6 | class TestScopeStartupTime: 7 | def test_option_critical(self) -> None: 8 | result = execute_main(argv=["-c", "1", "--no-performance-data"]) 9 | result.assert_critical() 10 | result.assert_first_line( 11 | "SYSTEMD CRITICAL - startup_time is 12.3 (outside range 0:1)" 12 | ) 13 | 14 | def test_option_warning(self) -> None: 15 | result = execute_main(argv=["-w", "2", "--no-performance-data"]) 16 | result.assert_warn() 17 | result.assert_first_line( 18 | "SYSTEMD WARNING - startup_time is 12.3 (outside range 0:2)" 19 | ) 20 | 21 | def test_option_no_startup_time_long(self) -> None: 22 | result = execute_main( 23 | argv=["-c", "1", "--no-startup-time", "--no-performance-data"] 24 | ) 25 | result.assert_ok() 26 | result.assert_first_line("SYSTEMD OK - all") 27 | 28 | def test_option_no_startup_time_short(self) -> None: 29 | result = execute_main(argv=["-c", "1", "-n"]) 30 | result.assert_ok() 31 | -------------------------------------------------------------------------------- /tests/test_scope_timers.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Optional 4 | 5 | import pytest 6 | 7 | from tests.helper import MockResult, execute_main 8 | 9 | 10 | def execute_with_opt_t( 11 | additional_argv: Optional[list[str]] = None, 12 | stdout_timers_suffix: str = "1", 13 | warning: Optional[int] = None, 14 | critical: Optional[int] = None, 15 | ) -> MockResult: 16 | argv = ["--timers", "--no-performance-data"] 17 | if warning: 18 | argv += ["--timers-warning", str(warning)] 19 | if critical: 20 | argv += ["--timers-critical", str(critical)] 21 | 22 | if additional_argv: 23 | argv += additional_argv 24 | 25 | return execute_main( 26 | argv=argv, 27 | stdout=[ 28 | "systemctl-list-units_3units.txt", 29 | "systemd-analyze_12.345.txt", 30 | "systemctl-list-timers_{}.txt".format(stdout_timers_suffix), 31 | ], 32 | ) 33 | 34 | 35 | class TestScopeTimers: 36 | def test_dead_timers_1(self) -> None: 37 | result = execute_with_opt_t() 38 | result.assert_critical() 39 | assert "SYSTEMD CRITICAL - phpsessionclean.timer" == result.first_line 40 | 41 | def test_dead_timers_2(self) -> None: 42 | result = execute_with_opt_t(stdout_timers_suffix="2") 43 | result.assert_critical() 44 | result.assert_first_line("SYSTEMD CRITICAL - dfm-auto-jf.timer, rsync.timer") 45 | 46 | @pytest.mark.skip(reason="use freezegun") 47 | def test_dead_timers_2_ok(self) -> None: 48 | result = execute_with_opt_t( 49 | stdout_timers_suffix="2", warning=2764801, critical=2764802 50 | ) 51 | result.assert_ok() 52 | 53 | @pytest.mark.skip(reason="use freezegun") 54 | def test_dead_timers_2_warning(self) -> None: 55 | result = execute_with_opt_t( 56 | stdout_timers_suffix="2", warning=2764799, critical=2764802 57 | ) 58 | result.assert_warn() 59 | 60 | @pytest.mark.skip(reason="use freezegun") 61 | def test_dead_timers_2_warning_equal(self) -> None: 62 | result = execute_with_opt_t( 63 | stdout_timers_suffix="2", warning=2764800, critical=2764802 64 | ) 65 | result.assert_warn() 66 | 67 | def test_dead_timers_ok(self) -> None: 68 | result = execute_with_opt_t(stdout_timers_suffix="ok") 69 | result.assert_ok() 70 | result.assert_first_line("SYSTEMD OK - all") 71 | 72 | def test_dead_timers_exclude(self) -> None: 73 | result = execute_with_opt_t( 74 | stdout_timers_suffix="2", additional_argv=["-e", "dfm-auto-jf.timer"] 75 | ) 76 | result.assert_critical() 77 | result.assert_first_line("SYSTEMD CRITICAL - rsync.timer") 78 | 79 | def test_dead_timers_exclude_multiple(self) -> None: 80 | result = execute_with_opt_t( 81 | stdout_timers_suffix="2", 82 | additional_argv=["-e", "dfm-auto-jf.timer", "-e", "rsync.timer"], 83 | ) 84 | result.assert_ok() 85 | 86 | def test_dead_timers_exclude_regexp(self) -> None: 87 | result = execute_with_opt_t( 88 | stdout_timers_suffix="2", 89 | additional_argv=["-e", "dfm-auto-jf.timer", "-e", ".*timer"], 90 | ) 91 | result.assert_ok() 92 | 93 | def test_all_n_a(self) -> None: 94 | """n/a -> not available""" 95 | result = execute_with_opt_t(stdout_timers_suffix="all-n-a") 96 | result.assert_critical() 97 | -------------------------------------------------------------------------------- /tests/test_scope_units.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from tests.helper import MockResult, execute_main 4 | 5 | 6 | def execute(argv: list[str], units_suffix: str = "ok") -> MockResult: 7 | return execute_main( 8 | argv=argv, 9 | stdout=[ 10 | "systemctl-list-units_{}.txt".format(units_suffix), 11 | "systemd-analyze_12.345.txt", 12 | ], 13 | ) 14 | 15 | 16 | class TestOk: 17 | def test_ok(self) -> None: 18 | result = execute(argv=["--no-performance-data"]) 19 | result.assert_ok() 20 | result.assert_first_line("SYSTEMD OK - all") 21 | 22 | def test_multiple_units(self) -> None: 23 | result = execute_main(argv=["--no-performance-data"]) 24 | result.assert_ok() 25 | result.assert_first_line("SYSTEMD OK - all") 26 | 27 | 28 | class TestFailure: 29 | def test_failure(self) -> None: 30 | result = execute(argv=["--no-performance-data"], units_suffix="failed") 31 | result.assert_critical() 32 | result.assert_first_line("SYSTEMD CRITICAL - smartd.service: failed") 33 | 34 | 35 | class TestMultipleFailure: 36 | def test_failure_multiple(self) -> None: 37 | result = execute( 38 | argv=["--no-performance-data"], units_suffix="multiple-failure" 39 | ) 40 | result.assert_critical() 41 | if result.first_line: 42 | assert "rtkit-daemon.service: failed" in result.first_line 43 | assert "smartd.service: failed" in result.first_line 44 | -------------------------------------------------------------------------------- /tests/test_table_parser.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Optional 4 | 5 | from check_systemd import CliSource 6 | from tests.helper import convert_to_bytes 7 | 8 | Table = CliSource.Table 9 | 10 | 11 | def read_stdout(file_name: str) -> str: 12 | return convert_to_bytes(file_name).decode("utf-8") 13 | 14 | 15 | def get_parser() -> Table: 16 | return Table(read_stdout("systemctl-list-units_v246.txt")) 17 | 18 | 19 | class TestTable: 20 | def test_initialization(self) -> None: 21 | parser = get_parser() 22 | assert "description" in parser.header_row 23 | assert "systemd-tmpfiles-clean.timer" in parser.body_rows[-1] 24 | assert [2, 111, 10, 9, 10] == parser.column_lengths 25 | 26 | assert ["", "unit", "load", "active", "sub", "description"] == parser.columns 27 | 28 | def test_detect_column_lengths(self) -> None: 29 | detect = Table._Table__detect_lengths # type: ignore 30 | assert [3, 3] == detect("1 2 3") 31 | assert [2, 3, 3] == detect(" 1 2 3 ") 32 | assert [2, 2, 3, 2, 3, 2] == detect(" 1 1 2 2 3 3 ") 33 | 34 | def test_split_line_into_columns(self) -> None: 35 | split = Table._Table__split_row # type: ignore 36 | assert ["123", "456", "789"] == split("123456789", [3, 3]) 37 | assert ["UNIT", "STATE", "LOAD"] == split("UNIT STATE LOAD ", [6, 7]) 38 | 39 | def test_get_row(self) -> None: 40 | parser = get_parser() 41 | row = parser.get_row(0) 42 | assert "" == row["column_0"] 43 | assert "dev-block-254:0.device" == row["unit"] 44 | assert "loaded" == row["load"] 45 | assert "active" == row["active"] 46 | assert "plugged" == row["sub"] 47 | assert "/dev/block/254:0" == row["description"] 48 | 49 | def test_get_row_all(self) -> None: 50 | parser = get_parser() 51 | row: Optional[dict[str, str]] = None 52 | for i in range(0, parser.row_count): 53 | row = parser.get_row(i) 54 | assert row is not None 55 | assert "systemd-tmpfiles-clean.timer" == row["unit"] 56 | 57 | def test_list_rows(self) -> None: 58 | parser = get_parser() 59 | row: Optional[dict[str, str]] = None 60 | for row in parser.list_rows(): 61 | pass 62 | 63 | assert row is not None 64 | assert "systemd-tmpfiles-clean.timer" == row["unit"] 65 | 66 | def test_narrow_column_separators(self) -> None: 67 | parser = Table(read_stdout("systemctl-list-timers_all-n-a.txt")) 68 | row = parser.get_row(1) 69 | assert "n/a" == row["next"] 70 | assert "n/a" == row["left"] 71 | assert "n/a" == row["last"] 72 | assert "n/a" == row["passed"] 73 | assert "systemd-readahead-done.timer" == row["unit"] 74 | -------------------------------------------------------------------------------- /tests/test_type_checkers.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from check_systemd import Source 6 | 7 | Unit = Source.Unit 8 | 9 | check_active_state = Unit._Unit__check_active_state # type: ignore 10 | 11 | 12 | def test_check_active_state() -> None: 13 | assert check_active_state("deactivating") == "deactivating" 14 | with pytest.raises(ValueError): 15 | check_active_state("invalid") 16 | 17 | 18 | check_sub_state = Unit._Unit__check_sub_state # type: ignore 19 | 20 | 21 | def test_check_sub_state() -> None: 22 | assert check_sub_state("tentative") == "tentative" 23 | with pytest.raises(ValueError): 24 | check_sub_state("invalid") 25 | 26 | 27 | check_load_state = Unit._Unit__check_load_state # type: ignore 28 | 29 | 30 | def test_check_load_state() -> None: 31 | assert check_load_state("loaded") == "loaded" 32 | with pytest.raises(ValueError): 33 | check_load_state("invalid") 34 | -------------------------------------------------------------------------------- /tests/test_unit.py: -------------------------------------------------------------------------------- 1 | """Unit tests""" 2 | 3 | from __future__ import annotations 4 | 5 | import check_systemd 6 | from check_systemd import SystemdUnitTypesList 7 | 8 | 9 | class TestMethodConvertToSec: 10 | def convert(self, value: str) -> float: 11 | return check_systemd.CliSource._CliSource__convert_to_sec(value) # type: ignore 12 | 13 | def test_one_digits(self) -> None: 14 | assert self.convert("1s") == 1 15 | 16 | def test_one_digit_ago(self) -> None: 17 | assert self.convert("1s ago") == 1 18 | 19 | def test_two_digits(self) -> None: 20 | assert self.convert("11s") == 11 21 | 22 | def test_multiple_units(self) -> None: 23 | assert self.convert("1min 1s") == 61 24 | 25 | def test_float(self) -> None: 26 | assert self.convert("1min 1.123s") == 61.123 27 | 28 | def test_non_float_and_float(self) -> None: 29 | assert self.convert("1min 2.15s") == 62.15 30 | 31 | def test_min_sec(self) -> None: 32 | assert self.convert("34min 46.292s") == 2086.292 33 | 34 | def test_months_days(self) -> None: 35 | assert self.convert("2 months 8 days") == 5875200 36 | 37 | 38 | class TestClassSystemdUnitTypesList: 39 | def test_initialization(self) -> None: 40 | unit_types = SystemdUnitTypesList("service", "timer") 41 | assert ["service", "timer"] == list(unit_types) 42 | 43 | def test_convert_to_regexp(self) -> None: 44 | unit_types = SystemdUnitTypesList("service", "timer") 45 | assert ".*\\.(service|timer)$" == unit_types.convert_to_regexp() 46 | -------------------------------------------------------------------------------- /tests/test_version246.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from tests.helper import execute_main 4 | 5 | 6 | class TestVersion246: 7 | def test_version_246(self) -> None: 8 | result = execute_main( 9 | argv=["--no-performance-data"], 10 | stdout=[ 11 | "systemctl-list-units_v246.txt", 12 | "systemd-analyze_12.345.txt", 13 | ], 14 | ) 15 | result.assert_critical() 16 | result.assert_first_line("SYSTEMD CRITICAL - nm-wait-online.service: failed") 17 | -------------------------------------------------------------------------------- /tests/unit-files/example-service.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Example Service (to test check_systemd.py) 3 | 4 | [Service] 5 | Type=simple 6 | ExecStart=/usr/bin/tail -f /var/log/syslog 7 | -------------------------------------------------------------------------------- /tests/unit-files/example-timer.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Corresponding Service for the Example Timer (to test check_systemd.py) 3 | 4 | [Service] 5 | Type=simple 6 | ExecStart=/usr/bin/cat /etc/os-release 7 | -------------------------------------------------------------------------------- /tests/unit-files/example-timer.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Example Timer (to test check_systemd.py) 3 | 4 | [Timer] 5 | OnCalendar=*-*-* *:*:00 6 | 7 | [Install] 8 | WantedBy=timers.target 9 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py{39,310,311,312,313}, format, readme, docs, lint, typecheck 3 | isolated_build = True 4 | 5 | [testenv] 6 | # Do not use basepython here. This way the existing Python version can 7 | # be selected automatically. 8 | # basepython = python3.10 9 | deps = pytest 10 | commands = pytest 11 | 12 | [testenv:format] 13 | deps = ruff 14 | commands = 15 | ; sort imports 16 | ruff check --select I --fix . 17 | ruff format 18 | 19 | [testenv:readme] 20 | deps = 21 | readme-patcher 22 | commands = 23 | readme-patcher 24 | 25 | [testenv:docs] 26 | deps = 27 | pgi # PGI - Pure Python GObject Introspection Bindings API compatible with PyGObject. 28 | Sphinx 29 | sphinx-rtd-theme 30 | sphinx-autodoc-typehints 31 | readme-patcher 32 | commands = 33 | sphinx-build -W -q docs docs/_build 34 | 35 | [testenv:lint] 36 | deps = 37 | ruff 38 | commands = 39 | ruff check 40 | 41 | [testenv:typecheck] 42 | deps = 43 | mypy 44 | pytest 45 | nagiosplugin-stubs 46 | pygobject-stubs 47 | commands = 48 | mypy check_systemd.py tests 49 | 50 | [gh-actions] 51 | python = 52 | 3.9: py39 53 | 3.10: py310 54 | 3.11: py311 55 | 3.12: py312, format, docs, lint 56 | 3.13: py313 57 | --------------------------------------------------------------------------------