init first version of pbs_special_agent
This commit is contained in:
commit
19cf015e95
15
.devcontainer/Dockerfile
Normal file
15
.devcontainer/Dockerfile
Normal file
@ -0,0 +1,15 @@
|
||||
ARG VARIANT
|
||||
FROM checkmk/check-mk-cloud:${VARIANT}
|
||||
|
||||
RUN /docker-entrypoint.sh /bin/true
|
||||
|
||||
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get -y install --no-install-recommends nodejs gcc
|
||||
|
||||
ADD requirements.txt /tmp/requirements.txt
|
||||
USER cmk
|
||||
RUN PATH="/omd/sites/cmk/bin:${PATH}" \
|
||||
OMD_ROOT="/omd/sites/cmk" \
|
||||
/omd/sites/cmk/bin/pip3 install -r /tmp/requirements.txt
|
||||
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
15
.devcontainer/build.sh
Executable file
15
.devcontainer/build.sh
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
NAME=$(python -c 'print(eval(open("package").read())["name"])')
|
||||
rm /omd/sites/cmk/var/check_mk/packages/* ||:
|
||||
ln -s $WORKSPACE/package /omd/sites/cmk/var/check_mk/packages/$NAME
|
||||
|
||||
mkp -v pack $NAME
|
||||
|
||||
# Set Outputs for GitHub Workflow steps
|
||||
if [ -n "$GITHUB_WORKSPACE" ]; then
|
||||
echo "::set-output name=pkgfile::$(ls *.mkp)"
|
||||
echo "::set-output name=pkgname::${NAME}"
|
||||
VERSION=$(python -c 'print(eval(open("package").read())["version"])')
|
||||
echo "::set-output name=pkgversion::$VERSION"
|
||||
fi
|
||||
55
.devcontainer/devcontainer.json
Normal file
55
.devcontainer/devcontainer.json
Normal file
@ -0,0 +1,55 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||
// https://github.com/microsoft/vscode-dev-containers/tree/v0.155.1/containers/ubuntu
|
||||
{
|
||||
"name": "Checkmk",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"args": { "VARIANT": "2.3.0p42" }
|
||||
},
|
||||
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"ms-python.flake8",
|
||||
"ms-python.isort",
|
||||
"ms-python.pylint",
|
||||
"ms-python.black-formatter",
|
||||
"ms-python.vscode-pylance",
|
||||
"littlefoxteam.vscode-python-test-adapter",
|
||||
"rioj7.command-variable"
|
||||
],
|
||||
// Bash as default shell.
|
||||
"settings": {"terminal.integrated.defaultProfile.linux": "bash"},
|
||||
"python.defaultInterpreterPath": "/omd/sites/cmk/bin/python3"
|
||||
}
|
||||
},
|
||||
|
||||
// Mount complete volume for site directories
|
||||
"mounts": [
|
||||
{ "source": "omd-sites", "target": "/opt/omd/sites", "type": "volume" },
|
||||
{ "source": "${localWorkspaceFolder}/lib", "target": "/opt/omd/sites/cmk/local/lib/python3/cmk", "type": "bind"},
|
||||
{ "source": "${localWorkspaceFolder}/plugins", "target": "/opt/omd/sites/cmk/local/lib/python3/cmk_addons/plugins", "type": "bind" },
|
||||
{ "source": "${localWorkspaceFolder}/plugins_legacy", "target": "/opt/omd/sites/cmk/local/share/check_mk", "type": "bind"},
|
||||
{ "source": "${localWorkspaceFolder}/packages_local", "target": "/opt/omd/sites/cmk/var/check_mk/packages_local/", "type": "bind"}
|
||||
],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": ".devcontainer/setpwd.sh",
|
||||
|
||||
// Start omd every time the container is started
|
||||
"postStartCommand": ".devcontainer/startup.sh",
|
||||
"postAttachCommand": "omd restart && cmk-update-license-usage",
|
||||
|
||||
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "cmk",
|
||||
|
||||
"remoteEnv": {
|
||||
"PATH": "/omd/sites/cmk/bin:/omd/sites/cmk/local/lib/python3/bin/:${containerEnv:PATH}",
|
||||
"OMD_ROOT": "/omd/sites/cmk",
|
||||
"OMD_SITE": "cmk",
|
||||
"CMK_SITE_ID": "cmk",
|
||||
"WORKSPACE": "${containerWorkspaceFolder}"
|
||||
}
|
||||
}
|
||||
6
.devcontainer/requirements.txt
Normal file
6
.devcontainer/requirements.txt
Normal file
@ -0,0 +1,6 @@
|
||||
urllib3<2
|
||||
flake8
|
||||
pytest
|
||||
pytest-cov
|
||||
requests-mock
|
||||
black
|
||||
9
.devcontainer/setpwd.sh
Executable file
9
.devcontainer/setpwd.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
rm -rfv $OMD_ROOT/local/lib/nagios/plugins
|
||||
ln -sv $WORKSPACE/nagios_plugins $OMD_ROOT/local/lib/nagios/plugins
|
||||
|
||||
rm -rfv $OMD_ROOT/local/tmp
|
||||
ln -sv $WORKSPACE/temp $OMD_ROOT/local/tmp
|
||||
|
||||
source /omd/sites/cmk/.profile && echo 'cmkadmin' | /omd/sites/cmk/bin/cmk-passwd -i cmkadmin
|
||||
3
.devcontainer/startup.sh
Executable file
3
.devcontainer/startup.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
find $OMD_ROOT/tmp/ -name "*.pid" -exec rm {} \;
|
||||
10
.devcontainer/template-sync-includes.conf
Normal file
10
.devcontainer/template-sync-includes.conf
Normal file
@ -0,0 +1,10 @@
|
||||
# DO NOT EDIT - Change template-sync.conf
|
||||
include .devcontainer/
|
||||
include .devcontainer/**
|
||||
exclude .github/template-sync.conf
|
||||
include .github/
|
||||
include .github/**
|
||||
include .vscode/
|
||||
include .vscode/**
|
||||
include .flake8
|
||||
include .gitignore
|
||||
4
.devcontainer/template-sync.conf
Normal file
4
.devcontainer/template-sync.conf
Normal file
@ -0,0 +1,4 @@
|
||||
# Add additional sync excludes for this repo
|
||||
#exclude .github/do-not-sync
|
||||
#exclude .flake8
|
||||
#exclude .gitignore
|
||||
23
.devcontainer/template-update.sh
Executable file
23
.devcontainer/template-update.sh
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
TEMPDIR=$(mktemp --directory)
|
||||
|
||||
cleanup() {
|
||||
echo "Removing $TEMPDIR"
|
||||
rm -rf $TEMPDIR
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
git -C $TEMPDIR clone https://github.com/Yogibaer75/checkmk_template.git
|
||||
|
||||
CMD="rsync --archive --cvs-exclude --no-owner --no-group --no-times --verbose"
|
||||
if [ -e ".devcontainer/template-sync.conf" ]; then
|
||||
CMD="${CMD} --filter='merge .devcontainer/template-sync.conf'"
|
||||
fi
|
||||
if [ -e "${TEMPDIR}/checkmk_template/.devcontainer/template-sync-includes.conf" ]; then
|
||||
CMD="${CMD} --filter='merge ${TEMPDIR}/checkmk_template/.devcontainer/template-sync-includes.conf'"
|
||||
fi
|
||||
CMD="${CMD} --filter='exclude *' ${TEMPDIR}/checkmk_template/ $(pwd)/"
|
||||
bash -c "$CMD"
|
||||
|
||||
echo $CMD
|
||||
39
.editorconfig
Normal file
39
.editorconfig
Normal file
@ -0,0 +1,39 @@
|
||||
# To see what this is about, have a look at
|
||||
# https://editorconfig.org/
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 8
|
||||
charset = utf-8
|
||||
|
||||
[*.{bat,ps1,vbs,cmd}]
|
||||
end_of_line = crlf
|
||||
|
||||
[*.{md,rst}]
|
||||
max_line_length = 80
|
||||
|
||||
[active_checks/check_*]
|
||||
max_line_length = 100
|
||||
|
||||
[checks/[!.]*]
|
||||
max_line_length = 100
|
||||
|
||||
[*.{cc,h,js,py,pl,pm,t}]
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{cc,h}]
|
||||
max_line_length = 100
|
||||
|
||||
[*.{js,py,pl,pm,t}]
|
||||
max_line_length = 100
|
||||
|
||||
[{*.scss,package.json,.envrc}]
|
||||
indent_size = 2
|
||||
|
||||
[{Makefile,*.make,*.am}]
|
||||
indent_style = tab
|
||||
37
.flake8
Normal file
37
.flake8
Normal file
@ -0,0 +1,37 @@
|
||||
[flake8]
|
||||
ignore=
|
||||
################################################################################################
|
||||
# Black is our formatting tool, so ignore any formatting-related findings.
|
||||
################################################################################################
|
||||
# whitespace before ':'
|
||||
E203,
|
||||
# line too long
|
||||
E501,
|
||||
# line break before binary operator
|
||||
W503,
|
||||
# multiple statements on one line (colon)
|
||||
E701,
|
||||
# multiple statements on one line (def)
|
||||
E704,
|
||||
################################################################################################
|
||||
# Ignore findings which are incompatible with our "import" techonology.
|
||||
################################################################################################
|
||||
# 'FOO' imported but unused
|
||||
F401,
|
||||
# module level import not at top of file
|
||||
E402,
|
||||
# 'from FOO import *' used; unable to detect undefined names
|
||||
F403,
|
||||
# 'FOO' may be undefined, or defined from star imports: BAR
|
||||
F405,
|
||||
################################################################################################
|
||||
# We should probably have a look at these findings.
|
||||
################################################################################################
|
||||
# do not assign a lambda expression, use a def
|
||||
E731,
|
||||
# ambiguous variable name 'FOO'
|
||||
E741,
|
||||
# undefined name 'FOO'
|
||||
F821,
|
||||
# local variable 'FOO' is assigned to but never used
|
||||
F841,
|
||||
53
.github/workflows/build-release.yml
vendored
Normal file
53
.github/workflows/build-release.yml
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
name: build-release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
- '!v*[a-z]'
|
||||
|
||||
jobs:
|
||||
build-release:
|
||||
name: Build Release Package
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: checkmk/check-mk-raw:2.0.0-latest
|
||||
|
||||
env:
|
||||
OMD_ROOT: /omd/sites/cmk
|
||||
OMD_SITE: cmk
|
||||
CMK_SITE_ID: cmk
|
||||
WORKSPACE: ${{ github.workspace }}
|
||||
|
||||
steps:
|
||||
- name: Initialize Checkmk Site
|
||||
run: /docker-entrypoint.sh /bin/true
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup links
|
||||
run: .devcontainer/symlink.sh
|
||||
- name: Update GITHUB_PATH
|
||||
run: echo "/omd/sites/cmk/bin" >> $GITHUB_PATH
|
||||
- name: Build Extension
|
||||
run: .devcontainer/build.sh
|
||||
id: cmkpkg
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1.0.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Upload Release Asset
|
||||
uses: actions/upload-release-asset@v1.0.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./${{ steps.cmkpkg.outputs.pkgfile }}
|
||||
asset_name: ${{ steps.cmkpkg.outputs.pkgfile }}
|
||||
asset_content_type: application/octet-stream
|
||||
39
.github/workflows/build.yml
vendored
Normal file
39
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
tags-ignore:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Checkmk package
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: checkmk/check-mk-raw:2.0.0-latest
|
||||
|
||||
env:
|
||||
OMD_ROOT: /omd/sites/cmk
|
||||
OMD_SITE: cmk
|
||||
CMK_SITE_ID: cmk
|
||||
WORKSPACE: ${{ github.workspace }}
|
||||
|
||||
steps:
|
||||
- name: Initialize Checkmk Site
|
||||
run: /docker-entrypoint.sh /bin/true
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup links
|
||||
run: .devcontainer/symlink.sh
|
||||
- name: Update GITHUB_PATH
|
||||
run: echo "/omd/sites/cmk/bin" >> $GITHUB_PATH
|
||||
- name: Build Extension
|
||||
run: .devcontainer/build.sh
|
||||
id: cmkpkg
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ steps.cmkpkg.outputs.pkgfile }}
|
||||
path: ${{ steps.cmkpkg.outputs.pkgfile }}
|
||||
26
.github/workflows/lint.yml
vendored
Normal file
26
.github/workflows/lint.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '**.py'
|
||||
|
||||
jobs:
|
||||
flake8_py3:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Install flake8
|
||||
run: pip install flake8
|
||||
- name: Run flake8
|
||||
uses: suo/flake8-github-action@releases/v1
|
||||
with:
|
||||
checkName: 'flake8_py3' # NOTE: this needs to be the same as the job name
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
30
.github/workflows/pytest.yml
vendored
Normal file
30
.github/workflows/pytest.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
name: pytest
|
||||
|
||||
on:
|
||||
push: []
|
||||
|
||||
jobs:
|
||||
pytest:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: checkmk/check-mk-raw:2.0.0-latest
|
||||
|
||||
env:
|
||||
OMD_ROOT: /omd/sites/cmk
|
||||
OMD_SITE: cmk
|
||||
CMK_SITE_ID: cmk
|
||||
WORKSPACE: ${{ github.workspace }}
|
||||
|
||||
steps:
|
||||
- name: Initialize Checkmk Site
|
||||
run: /docker-entrypoint.sh /bin/true
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup links
|
||||
run: ./.devcontainer/symlink.sh
|
||||
- name: Install pytest
|
||||
run: su -l -c "REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt pip3 install -r $GITHUB_WORKSPACE/.devcontainer/requirements.txt" cmk
|
||||
- name: Update GITHUB_PATH
|
||||
run: echo "/omd/sites/cmk/bin" >> $GITHUB_PATH
|
||||
- name: Run pytest
|
||||
run: python3 -m pytest
|
||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
*.mkp
|
||||
.coverage
|
||||
__pycache__
|
||||
debug.log
|
||||
/temp/*
|
||||
172
.pylintrc
Normal file
172
.pylintrc
Normal file
@ -0,0 +1,172 @@
|
||||
[MASTER]
|
||||
# Setup the Python paths needed for our tests.
|
||||
init-hook=
|
||||
import sys;
|
||||
sys.path.insert(0, __file__[:__file__.rfind("/.venv")]); # __file__ is somewhere deep inside the .venv
|
||||
from tests.testlib.common.repo import add_protocols_path, add_python_paths, add_otel_collector_path;
|
||||
add_protocols_path();
|
||||
add_python_paths();
|
||||
add_otel_collector_path();
|
||||
load-plugins=
|
||||
tests.pylint.cmk_edition_ignores,
|
||||
tests.pylint.checker_localization,
|
||||
tests.pylint.checker_cmk_module_layers,
|
||||
tests.pylint.checker_layering_violation,
|
||||
pylint_pydantic
|
||||
jobs=0
|
||||
# pickle collected data for later comparisons. Not used in our CI and makes runs faster
|
||||
persistent=no
|
||||
extension-pkg-whitelist=rrdtool,_ldap,netifaces,pymssql,pydantic,lxml
|
||||
|
||||
signature-mutators=cmk.utils.store.with_lock_dict
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
disable=
|
||||
#---------------------------------------------------------------------------
|
||||
# This checker is flaky. Recheck later.
|
||||
# Might be related to https://github.com/pylint-dev/pylint/issues/9101
|
||||
too-many-ancestors,
|
||||
#---------------------------------------------------------------------------
|
||||
# Not useless if that's exporting a type as done often enough in the
|
||||
# standard library.
|
||||
useless-import-alias,
|
||||
#---------------------------------------------------------------------------
|
||||
# Use local suppressions or (even better) refactor the code.
|
||||
import-outside-toplevel,
|
||||
#---------------------------------------------------------------------------
|
||||
# Enabling this would be very desirable, it vastly improves readability and
|
||||
# it might even be necessary for tools like mypy. Fixing this involves some
|
||||
# amount of relatively easy work, especially if we want to avoid code
|
||||
# duplication (introduce new classes, combine methods, etc.)
|
||||
attribute-defined-outside-init,
|
||||
#---------------------------------------------------------------------------
|
||||
# Enabling these warnings would be nice, they are mostly a sign of sloppy
|
||||
# programming practice. In some cases, they can even hide bugs.
|
||||
broad-except,
|
||||
#---------------------------------------------------------------------------
|
||||
# Enabling this would be nice, but not crucial. At the moment, we have quite
|
||||
# a few violations, so we postpone fixing this. When we do it eventually, we
|
||||
# probably want to use "include-naming-hint=yes" in the BASIC section.
|
||||
invalid-name,
|
||||
#---------------------------------------------------------------------------
|
||||
# We can probably re-enable this if we move __version__ definitions and
|
||||
# conditional imports after the normal imports.
|
||||
wrong-import-position,
|
||||
#---------------------------------------------------------------------------
|
||||
# Enabling this would be nice, but not crucial. At the moment, we have quite
|
||||
# a few violations, so we postpone fixing this.
|
||||
unused-argument,
|
||||
#---------------------------------------------------------------------------
|
||||
# Alas, these maintenance/security nightmares are still part of our base
|
||||
# "technology"... :-/ Nevertheless, reducing their usage is a very worthy
|
||||
# goal.
|
||||
exec-used,
|
||||
global-statement,
|
||||
#---------------------------------------------------------------------------
|
||||
# Enabling these would be nice, but given the current state of affairs
|
||||
# (gigantic modules with deeply nested humungous functions/methods), this
|
||||
# will be a non-trivial amount of work.
|
||||
too-few-public-methods,
|
||||
too-many-arguments,
|
||||
too-many-positional-arguments,
|
||||
too-many-boolean-expressions,
|
||||
too-many-instance-attributes,
|
||||
too-many-lines,
|
||||
too-many-locals,
|
||||
too-many-nested-blocks,
|
||||
too-many-public-methods,
|
||||
too-many-return-statements,
|
||||
too-many-statements,
|
||||
#---------------------------------------------------------------------------
|
||||
# Enabling these would be nice, but at the moment pylint is a bit too dumb,
|
||||
# so it stumbles over e.g. initialization with None. It ignores control
|
||||
# flow, so even adding e.g. isinstance() guards wouldn't help, see:
|
||||
# https://github.com/PyCQA/pylint/issues/1498.
|
||||
unsubscriptable-object,
|
||||
#---------------------------------------------------------------------------
|
||||
# Our code is still full of FIXMEs/XXXs/TODOs, perhaps fixing or removing
|
||||
# them might be a good idea some day...
|
||||
fixme,
|
||||
#---------------------------------------------------------------------------
|
||||
# Black doesn't split long strings, we'll have to this by ourselves, see
|
||||
# https://github.com/psf/black/issues/1331
|
||||
line-too-long,
|
||||
# Since Black 24 black and pylint started to disagree on `def foo(): ...`
|
||||
# https://github.com/psf/black/issues/4173
|
||||
# black is our formatter so the black way is the proper way...
|
||||
multiple-statements,
|
||||
#---------------------------------------------------------------------------
|
||||
# We are light years away from enabling these...
|
||||
missing-module-docstring,
|
||||
missing-class-docstring,
|
||||
missing-function-docstring,
|
||||
#---------------------------------------------------------------------------
|
||||
# Enabling the two spelling-related checks increases pylints runtime from
|
||||
# 11 min to 40 min, so we better keep those disabled for normal runs.
|
||||
# NOTE: If we want to enable one of these checks, we need to add pyenchant
|
||||
# to our dev dependencies.
|
||||
wrong-spelling-in-comment,
|
||||
wrong-spelling-in-docstring,
|
||||
#---------------------------------------------------------------------------
|
||||
# Pylint is full of bugs regarding this, leading to tons of false positives
|
||||
# when pathlib.path is used. Furthermore, the handling of NewTypes is totally
|
||||
# broken, see e.g. https://github.com/PyCQA/pylint/issues/2296 and
|
||||
# https://github.com/PyCQA/pylint/issues/3162.
|
||||
no-member,
|
||||
#---------------------------------------------------------------------------
|
||||
# Lots of warning due to this, but we should really go through them one by
|
||||
# one, this might drastically improve the usefulness of our backtraces.
|
||||
raise-missing-from,
|
||||
#---------------------------------------------------------------------------
|
||||
# pylint 2.7.[012] has totally screwed this up... :-/ Takes ages, too.
|
||||
duplicate-code,
|
||||
R0801,
|
||||
#---------------------------------------------------------------------------
|
||||
# A stylistic thing only, many findings, but all fixes are mechanical.
|
||||
consider-using-from-import,
|
||||
#---------------------------------------------------------------------------
|
||||
# A good idea in general, but each of the many findings has to be looked at:
|
||||
# We often mutate a dictionary while iterating over it, which is :-P
|
||||
consider-using-dict-items,
|
||||
#---------------------------------------------------------------------------
|
||||
# Purely mechanical & aesthetical, lots of findings.
|
||||
redundant-u-string-prefix,
|
||||
#---------------------------------------------------------------------------
|
||||
# Fixing this is URGENT: There are potentially lots of encoding problems
|
||||
# sleeping in our code when we are not explicit in open().
|
||||
unspecified-encoding,
|
||||
#---------------------------------------------------------------------------
|
||||
# Tons of findings, we fix this incrementally, f-strings are much more
|
||||
# performant than old-skool string splicing/formatting.
|
||||
consider-using-f-string,
|
||||
#---------------------------------------------------------------------------
|
||||
# New in version 2.15.0 which causes a few findings
|
||||
missing-timeout,
|
||||
#---------------------------------------------------------------------------
|
||||
# New in version 2.16.0 which causes a few findings
|
||||
broad-exception-raised,
|
||||
#---------------------------------------------------------------------------
|
||||
# Import order is checked by isort
|
||||
wrong-import-order,
|
||||
#---------------------------------------------------------------------------
|
||||
# New in version 3.2.0 which causes new findings
|
||||
# TODO: fix these new findings - https://jira.lan.tribe29.com/browse/CMK-17473
|
||||
possibly-used-before-assignment,
|
||||
contextmanager-generator-missing-cleanup
|
||||
|
||||
[IMPORTS]
|
||||
# This complies with PEP 8 and avoids code duplication in some cases.
|
||||
allow-wildcard-with-all=yes
|
||||
|
||||
[REPORTS]
|
||||
output-format=colorized
|
||||
|
||||
[FORMAT]
|
||||
max-line-length=100
|
||||
|
||||
[VARIABLES]
|
||||
# Be a little bit more mypy-friendly.
|
||||
additional-builtins=reveal_type
|
||||
|
||||
[LAYERING_VIOLATION]
|
||||
# layering-definition=.layering.yaml
|
||||
110
.vscode/launch.json
vendored
Normal file
110
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
{
|
||||
// Verwendet IntelliSense zum Ermitteln möglicher Attribute.
|
||||
// Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen.
|
||||
// Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python: Aktuelle Datei",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"name": "cmk - check",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "~/bin/cmk",
|
||||
"args": [
|
||||
"-vv",
|
||||
"--debug",
|
||||
"-n",
|
||||
"${input:envHOST}"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
},
|
||||
{
|
||||
"name": "cmk - discover",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "~/bin/cmk",
|
||||
"args": [
|
||||
"-vv",
|
||||
"--debug",
|
||||
"-I",
|
||||
"${input:envHOST}"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
},
|
||||
{
|
||||
"name": "cmk - rediscover",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "~/bin/cmk",
|
||||
"args": [
|
||||
"-vv",
|
||||
"--debug",
|
||||
"-II",
|
||||
"${input:envHOST}"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
},
|
||||
{
|
||||
"name": "cmk - agent build",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "~/bin/cmk",
|
||||
"args": [
|
||||
"-vv",
|
||||
"--debug",
|
||||
"-A",
|
||||
"-f",
|
||||
"${input:envHOST}"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
},
|
||||
{
|
||||
"name": "agent_redfish",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "/workspaces/checkmk_template_23/plugins/redfish/special_agents/agent_redfish.py",
|
||||
"args": [
|
||||
"-vvv",
|
||||
"--debug",
|
||||
"-P",
|
||||
"http",
|
||||
"-u",
|
||||
"admin",
|
||||
"--password-id",
|
||||
"rfpass:/omd/sites/cmk/var/check_mk/passwords_merged",
|
||||
"-p",
|
||||
"8000",
|
||||
"192.168.188.223",
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "cmk - show host config",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "~/bin/cmk",
|
||||
"args": [
|
||||
"--debug",
|
||||
"-vv",
|
||||
"-D",
|
||||
"${input:envHOST}"
|
||||
]
|
||||
},
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"id": "envHOST",
|
||||
"type": "command",
|
||||
"command": "extension.commandvariable.file.content",
|
||||
"args": {
|
||||
"fileName": "${workspaceFolder}/.env",
|
||||
"key": "CHECKHOST"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
12
.vscode/settings.json
vendored
Normal file
12
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"python.testing.pytestArgs": [
|
||||
"."
|
||||
],
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true,
|
||||
"python.defaultInterpreterPath": "/omd/sites/cmk/bin/python3",
|
||||
"python.analysis.typeCheckingMode": "off",
|
||||
"python.analysis.autoImportCompletions": true,
|
||||
"flake8.cwd": "/omd/sites/cmk/",
|
||||
"flake8.enabled": false
|
||||
}
|
||||
35
.vscode/tasks.json
vendored
Normal file
35
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Install",
|
||||
"type": "shell",
|
||||
"command": ".devcontainer/build.sh",
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Start Site",
|
||||
"type": "shell",
|
||||
"command": "omd start",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Re-Start Site",
|
||||
"type": "shell",
|
||||
"command": "omd restart",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Stop Site",
|
||||
"type": "shell",
|
||||
"command": "omd stop",
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
6
.yamllint.yml
Normal file
6
.yamllint.yml
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
extends: default
|
||||
|
||||
rules:
|
||||
line-length:
|
||||
max: 100
|
||||
19
README.md
Normal file
19
README.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Checkmk extension devcontainer template
|
||||
|
||||
## Description
|
||||
|
||||
This is a template to develop Checkmk Extensions derived from the original made by [Marius Rieder](https://github.com/jiuka/)
|
||||
|
||||
## Development
|
||||
|
||||
For the best development experience use [VSCode](https://code.visualstudio.com/) with the [Remote Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension. This maps your workspace into a checkmk docker container giving you access to the python environment and libraries the installed extension has.
|
||||
|
||||
## Directories
|
||||
|
||||
The following directories in this repo are getting mapped into the Checkmk site.
|
||||
|
||||
* `agents`, `checkman`, `checks`, `doc`, `inventory`, `notifications`, `web` are mapped into `local/share/check_mk/`
|
||||
* `agent_based` is mapped to `local/lib/check_mk/base/plugins/agent_based`
|
||||
* `nagios_plugins` is mapped to `local/lib/nagios/plugins`
|
||||
* `bakery` is mapped to `local/lib/check_mk/base/cee/plugins/bakery`
|
||||
* `temp` is mapped to `local/tmp` for storing precreated agent output
|
||||
19
package
Normal file
19
package
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
'author': u'Simon Zeyer',
|
||||
'description': u'Proxmox Backup Server Checks via REST API',
|
||||
'download_url': 'https://gitea.simonzeyer.de/simon/cmk_pbs_special_agent',
|
||||
'files': {
|
||||
'cmk_addons_plugins': [
|
||||
'agent_based/plugin_name.py',
|
||||
'checkman/plugin_name',
|
||||
'rulesets/pbs_special_agent.py',
|
||||
'server_side_calls/pbs_special_agent.py'
|
||||
'libexec/agent_pbs_special_agent'
|
||||
],
|
||||
},
|
||||
'name': 'pbs_special_agent',
|
||||
'title': u'Proxmox PBS',
|
||||
'version': '0.1',
|
||||
'version.min_required': '2.1.0',
|
||||
'version.packaged': '2.3.0p42.cce'
|
||||
}
|
||||
243
plugins/pbs_special_agent/agent_based/pbs_special_agent.py
Normal file
243
plugins/pbs_special_agent/agent_based/pbs_special_agent.py
Normal file
@ -0,0 +1,243 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import itertools
|
||||
import json
|
||||
from datetime import datetime
|
||||
from cmk.agent_based.v2 import (
|
||||
AgentSection,
|
||||
CheckPlugin,
|
||||
Service,
|
||||
Result,
|
||||
State,
|
||||
Metric,
|
||||
get_value_store,
|
||||
)
|
||||
from cmk.plugins.lib.df import (
|
||||
df_check_filesystem_single,
|
||||
FILESYSTEM_DEFAULT_LEVELS,
|
||||
)
|
||||
|
||||
ITEM_STATUS = "API Status"
|
||||
ITEM_DS_USAGE = "Datastore Usage "
|
||||
ITEM_SYNC_JOBS = "Sync Job "
|
||||
ITEM_GC = "Garbage Collection "
|
||||
ITEM_PRUNE = "Prune Job "
|
||||
ITEM_VERIFY = "Verify Job "
|
||||
ITEM_FS_ROOT = "HD Space (root)"
|
||||
ITEM_FS_SWAP = "Swap Usage"
|
||||
ITEM_CPU = "CPU Usage"
|
||||
ITEM_MEM = "Memory Usage"
|
||||
ITEM_LOAD = "Load Average"
|
||||
|
||||
def parse_jobs(item: str, item_key: str, section: dict, section_key: str, section_item_key: str):
|
||||
if item.startswith(item_key):
|
||||
for s in section[section_key]:
|
||||
if item.replace(item_key, '') == s[section_item_key]:
|
||||
next_run = ""
|
||||
if "last-run-state" in s:
|
||||
if 'next-run' in s:
|
||||
next_run = (
|
||||
f", next run: "
|
||||
f"{datetime.fromtimestamp(s['next-run'])}"
|
||||
)
|
||||
if s["last-run-state"] == "OK":
|
||||
yield Result(
|
||||
state=State.OK,
|
||||
summary=(
|
||||
f"last run state: OK"
|
||||
f"{next_run}"
|
||||
),
|
||||
)
|
||||
else:
|
||||
yield Result(
|
||||
state=State.CRIT,
|
||||
summary=(
|
||||
f"last run state: {s["last-run-state"]}"
|
||||
f"{next_run}"
|
||||
),
|
||||
)
|
||||
else:
|
||||
yield Result(
|
||||
state=State.OK,
|
||||
summary=(
|
||||
"Job running"
|
||||
),
|
||||
)
|
||||
|
||||
def parse_pbs_special_agent(string_table):
|
||||
flatlist = list(itertools.chain.from_iterable(string_table))
|
||||
# parsed = json.loads(" ".join(flatlist).replace("'", "\""))
|
||||
parsed = json.loads(" ".join(flatlist))
|
||||
return parsed
|
||||
|
||||
|
||||
def discover_pbs_special_agent(section):
|
||||
yield Service(item=ITEM_STATUS)
|
||||
if "status" in section:
|
||||
yield Service(item=ITEM_CPU)
|
||||
yield Service(item=ITEM_MEM)
|
||||
yield Service(item=ITEM_LOAD)
|
||||
if "sync" in section:
|
||||
for s in section["sync"]:
|
||||
yield Service(item=ITEM_SYNC_JOBS + s["id"])
|
||||
if "gc" in section:
|
||||
for gc in section["gc"]:
|
||||
yield Service(item=ITEM_GC + gc["store"])
|
||||
if "prune" in section:
|
||||
for prune in section["prune"]:
|
||||
yield Service(item=ITEM_PRUNE + prune["id"])
|
||||
if "verify" in section:
|
||||
for verify in section["verify"]:
|
||||
yield Service(item=ITEM_VERIFY + verify["id"])
|
||||
|
||||
|
||||
def discover_pbs_special_agent_storage(section):
|
||||
if "ds_usage" in section:
|
||||
for ds in section["ds_usage"]:
|
||||
yield Service(item=ITEM_DS_USAGE + ds["store"])
|
||||
if "status" in section:
|
||||
yield Service(item=ITEM_FS_ROOT)
|
||||
# yield Service(item=ITEM_FS_SWAP)
|
||||
|
||||
|
||||
def check_pbs_special_agent(item, section):
|
||||
# pprint(section)
|
||||
if "error" in section:
|
||||
if item == ITEM_STATUS:
|
||||
yield Result(state=State.CRIT, summary=section["error"])
|
||||
return
|
||||
if item == ITEM_STATUS:
|
||||
status = section["status"]
|
||||
version = section["version"]
|
||||
yield Result(
|
||||
state=State.OK,
|
||||
summary=(
|
||||
f"Version {version['version']}."
|
||||
f"{version['release']}"
|
||||
),
|
||||
details=(
|
||||
f"CPU: {status['cpuinfo']["model"]}, "
|
||||
f"{status['cpuinfo']['sockets']} sockets, "
|
||||
f"{status['cpuinfo']['cpus']} cores\n"
|
||||
f"Kernel: {status['current-kernel']["sysname"]} "
|
||||
f"{status['current-kernel']["release"]} "
|
||||
f"{status['current-kernel']["machine"]} "
|
||||
f"{status['current-kernel']["version"]}\n"
|
||||
f"Bios: {status['boot-info']['mode']}, "
|
||||
f"secureboot: {'on' if status['boot-info']['secureboot'] else 'off'}\n"
|
||||
)
|
||||
)
|
||||
yield from parse_jobs(item, ITEM_SYNC_JOBS, section, "sync", "id")
|
||||
yield from parse_jobs(item, ITEM_GC, section, "gc", "store")
|
||||
yield from parse_jobs(item, ITEM_PRUNE, section, "prune", "id")
|
||||
yield from parse_jobs(item, ITEM_VERIFY, section, "verify", "id")
|
||||
if item == ITEM_CPU:
|
||||
status = section["status"]
|
||||
cpu_busy = float(status.get("cpu", 0.0)) * 100 # Prozent
|
||||
cpu_wait = float(status.get("wait", 0.0)) * 100
|
||||
yield Result(
|
||||
state=State.OK,
|
||||
summary=f"CPU Busy: {cpu_busy:.1f}%, IOWait: {cpu_wait:.1f}%"
|
||||
)
|
||||
yield Metric("cpu_busy", cpu_busy)
|
||||
yield Metric("cpu_wait", cpu_wait)
|
||||
if item == ITEM_MEM:
|
||||
status = section["status"]
|
||||
mem = status.get("memory", {})
|
||||
total = mem.get("total", 0)
|
||||
used = mem.get("used", 0)
|
||||
free = mem.get("free", 0)
|
||||
yield Result(
|
||||
state=State.OK,
|
||||
summary=f"Memory used: {used}, free: {free}, total: {total}",
|
||||
)
|
||||
yield Metric("mem_total", total)
|
||||
yield Metric("mem_used", used)
|
||||
yield Metric("mem_free", free)
|
||||
if item == ITEM_LOAD:
|
||||
status = section["status"]
|
||||
loadavg = status.get("loadavg", [0.0, 0.0, 0.0])
|
||||
load1, load5, load15 = [float(x) for x in loadavg]
|
||||
yield Result(
|
||||
state=State.OK,
|
||||
summary=f"Load avg: {load1:.2f}, {load5:.2f}, {load15:.2f}",
|
||||
)
|
||||
yield Metric("load1", load1)
|
||||
yield Metric("load5", load5)
|
||||
yield Metric("load15", load15)
|
||||
|
||||
|
||||
def check_pbs_special_agent_storage(item: str, params: list, section: dict):
|
||||
if "error" in section:
|
||||
return
|
||||
if item.startswith(ITEM_DS_USAGE):
|
||||
for ds in section["ds_usage"]:
|
||||
if item.replace(ITEM_DS_USAGE, '') == ds["store"]:
|
||||
try:
|
||||
size_mb = float(ds['total']) / (1024 * 1024)
|
||||
avail_mb = float(ds['avail']) / (1024 * 1024)
|
||||
value_store = get_value_store()
|
||||
|
||||
yield from df_check_filesystem_single(
|
||||
value_store=value_store,
|
||||
mountpoint=ds["store"],
|
||||
filesystem_size=size_mb,
|
||||
free_space=avail_mb,
|
||||
reserved_space=0,
|
||||
inodes_total=None,
|
||||
inodes_avail=None,
|
||||
params=params,
|
||||
this_time=None,
|
||||
)
|
||||
except Exception:
|
||||
yield Result(
|
||||
state=State.UNKNOWN,
|
||||
summary="error checking datastore status"
|
||||
)
|
||||
if item == ITEM_FS_ROOT:
|
||||
try:
|
||||
fs_root = section["status"]["root"]
|
||||
size_mb = float(fs_root['total'])/1024/1024 #ds['total'] returning bytes instead of mb
|
||||
avail_mb = float(fs_root['avail'])/1024/1024 #ds['avail'] returning bytes instead of mb
|
||||
print(size_mb)
|
||||
value_store = get_value_store()
|
||||
|
||||
yield from df_check_filesystem_single(
|
||||
value_store=value_store,
|
||||
mountpoint="/root",
|
||||
filesystem_size=size_mb,
|
||||
free_space=avail_mb,
|
||||
reserved_space=0, # See df.py: ... if (filesystem_size is None) or (free_space is None) or (reserved_space is None): yield Result(state=State.OK, summary="no filesystem size information")
|
||||
inodes_total=None,
|
||||
inodes_avail=None,
|
||||
params=params,
|
||||
this_time=None,
|
||||
)
|
||||
except Exception as e:
|
||||
yield Result(
|
||||
state=State.UNKNOWN,
|
||||
summary=f"error checking root fs status"
|
||||
)
|
||||
|
||||
|
||||
agent_section_pbs_special_agent = AgentSection(
|
||||
name = "pbs_special_agent",
|
||||
parse_function = parse_pbs_special_agent,
|
||||
)
|
||||
|
||||
check_plugin_pbs_special_agent_status = CheckPlugin(
|
||||
name = "pbs_special_agent_status",
|
||||
sections = [ "pbs_special_agent" ],
|
||||
service_name = "PBS %s",
|
||||
discovery_function = discover_pbs_special_agent,
|
||||
check_function = check_pbs_special_agent,
|
||||
)
|
||||
check_plugin_pbs_special_agent_datastore = CheckPlugin(
|
||||
name = "pbs_special_agent_ds",
|
||||
sections = [ "pbs_special_agent" ],
|
||||
service_name = "PBS %s",
|
||||
discovery_function = discover_pbs_special_agent_storage,
|
||||
check_function = check_pbs_special_agent_storage,
|
||||
check_default_parameters=FILESYSTEM_DEFAULT_LEVELS,
|
||||
check_ruleset_name="filesystem",
|
||||
)
|
||||
174
plugins/pbs_special_agent/libexec/agent_pbs_special_agent
Executable file
174
plugins/pbs_special_agent/libexec/agent_pbs_special_agent
Executable file
@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import requests
|
||||
import argparse
|
||||
import json
|
||||
import ssl
|
||||
import hashlib
|
||||
from requests.adapters import HTTPAdapter
|
||||
from urllib3.util.retry import Retry
|
||||
from urllib3.poolmanager import PoolManager
|
||||
|
||||
def fingerprint_checking_SSLSocket(_fingerprint:str):
|
||||
class SSLSocket(ssl.SSLSocket):
|
||||
fingerprint = _fingerprint.replace(":", "").lower()
|
||||
|
||||
def do_handshake(self, *args, **kw):
|
||||
res = super().do_handshake(*args, **kw)
|
||||
|
||||
# Get full certificate in DER format
|
||||
der_bytes = self.getpeercert(binary_form=True)
|
||||
|
||||
crt_sha256 = hashlib.sha256(der_bytes).hexdigest()
|
||||
|
||||
if crt_sha256.lower() != self.fingerprint.lower():
|
||||
raise ssl.SSLError(
|
||||
"Server %r certificate fingerprint (sha1) %s does not match %r"
|
||||
% (
|
||||
self.server_hostname,
|
||||
crt_sha256,
|
||||
self.fingerprint,
|
||||
)
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
return SSLSocket
|
||||
|
||||
def api_get(session, url):
|
||||
try:
|
||||
r = session.get(url, timeout=50)
|
||||
|
||||
if r.status_code == 404:
|
||||
raise RuntimeError(f"API endpoint not found (404): {url}")
|
||||
|
||||
if not r.ok:
|
||||
raise RuntimeError(f"API error {r.status_code}: {url} - {r.text}")
|
||||
|
||||
return r.json().get("data", {})
|
||||
|
||||
except ConnectionError as e:
|
||||
raise RuntimeError(
|
||||
f"Connection refused to {url} (host or port unreachable)"
|
||||
) from e
|
||||
|
||||
except requests.exceptions.Timeout as e:
|
||||
raise RuntimeError(
|
||||
f"Timeout while connecting to {url}"
|
||||
) from e
|
||||
|
||||
except requests.exceptions.HTTPError as e:
|
||||
raise RuntimeError(
|
||||
f"HTTP error from {url}: {e}"
|
||||
) from e
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
raise RuntimeError(
|
||||
f"Request failed for {url}: {e}"
|
||||
) from e
|
||||
|
||||
def create_session():
|
||||
session = requests.Session()
|
||||
|
||||
retry = Retry(
|
||||
total=3, # total retries
|
||||
connect=3, # connection retries
|
||||
read=3, # read retries
|
||||
backoff_factor=0.5, # 0.5s, 1s, 2s
|
||||
status_forcelist=[500, 502, 503, 504],
|
||||
allowed_methods=["GET"],
|
||||
raise_on_status=False,
|
||||
)
|
||||
|
||||
adapter = HTTPAdapter(
|
||||
max_retries=retry,
|
||||
pool_connections=5,
|
||||
pool_maxsize=5,
|
||||
)
|
||||
|
||||
session.mount("https://", adapter)
|
||||
session.mount("http://", adapter)
|
||||
|
||||
return session
|
||||
|
||||
class FingerprintAdapter(HTTPAdapter):
|
||||
def init_poolmanager(self, connections, maxsize, block=False, **kwargs):
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
ctx.sslsocket_class = fingerprint_checking_SSLSocket(PBS_FINGERPRINT)
|
||||
|
||||
self.poolmanager = PoolManager(
|
||||
num_pools=connections,
|
||||
maxsize=maxsize,
|
||||
block=block,
|
||||
ssl_context=ctx,
|
||||
assert_hostname=False,
|
||||
)
|
||||
|
||||
parser = argparse.ArgumentParser("agent_pbs_special_agent")
|
||||
parser.add_argument(
|
||||
"--host",
|
||||
help="PBS Host address or fqdn without https:// and port.",
|
||||
type=str,
|
||||
default="pbs")
|
||||
parser.add_argument(
|
||||
"--port",
|
||||
help="PBS https port.",
|
||||
type=int,
|
||||
default="8007")
|
||||
parser.add_argument(
|
||||
"--fingerprint",
|
||||
help="Fingerprint of the PBS if not using valid cert.",
|
||||
type=str,
|
||||
default="")
|
||||
parser.add_argument(
|
||||
"--tokenid",
|
||||
help="API Token with audit permissions.",
|
||||
type=str,
|
||||
default="admin@pbs!checkmk")
|
||||
parser.add_argument(
|
||||
"--secret",
|
||||
help="Secret for API Token.",
|
||||
type=str)
|
||||
args = parser.parse_args()
|
||||
|
||||
PBS_HOST = args.host
|
||||
API_TOKEN_ID = args.tokenid
|
||||
PBS_PORT = args.port
|
||||
PBS_FINGERPRINT = args.fingerprint
|
||||
API_TOKEN_SECRET = args.secret
|
||||
|
||||
headers = {
|
||||
"Authorization": f"PBSAPIToken={API_TOKEN_ID}:{API_TOKEN_SECRET}"
|
||||
}
|
||||
# Disable SSL verification only if you use self-signed certs
|
||||
session = create_session()
|
||||
if PBS_FINGERPRINT != "":
|
||||
session.mount("https://", FingerprintAdapter())
|
||||
# session.verify = False
|
||||
session.headers.update(headers)
|
||||
|
||||
API_URL = f"https://{PBS_HOST}:{PBS_PORT}"
|
||||
try:
|
||||
return_json = {}
|
||||
return_json["version"] = api_get(session, f"{API_URL}/api2/json/version")
|
||||
return_json["status"] = api_get(session, f"{API_URL}/api2/json/nodes/localhost/status")
|
||||
return_json["tasks"] = api_get(session, f"{API_URL}/api2/json/nodes/localhost/tasks")
|
||||
return_json["ds_usage"] = api_get(session, f"{API_URL}/api2/json/status/datastore-usage")
|
||||
return_json["sync"] = api_get(session, f"{API_URL}/api2/json/admin/sync")
|
||||
return_json["gc"] = api_get(session, f"{API_URL}/api2/json/admin/gc")
|
||||
return_json["prune"] = api_get(session, f"{API_URL}/api2/json/admin/prune")
|
||||
return_json["verify"] = api_get(session, f"{API_URL}/api2/json/admin/verify")
|
||||
for d in return_json["ds_usage"]:
|
||||
# we do not need history data
|
||||
d.pop("history", None)
|
||||
print('<<<pbs_special_agent:sep(0)>>>')
|
||||
print(json.dumps(return_json))
|
||||
exit(0)
|
||||
except Exception as e:
|
||||
print('<<<pbs_special_agent:sep(0)>>>')
|
||||
print(json.dumps({"error": str(e)}))
|
||||
exit(0)
|
||||
|
||||
|
||||
47
plugins/pbs_special_agent/rulesets/pbs_special_agent.py
Normal file
47
plugins/pbs_special_agent/rulesets/pbs_special_agent.py
Normal file
@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
# Shebang needed only for editors
|
||||
|
||||
from cmk.rulesets.v1.form_specs import Dictionary, DictElement, String, Password, migrate_to_password, DefaultValue
|
||||
from cmk.rulesets.v1.rule_specs import SpecialAgent, Topic, Help, Title
|
||||
|
||||
def _formspec():
|
||||
return Dictionary(
|
||||
title=Title("PBS API-Token Login"),
|
||||
help_text=Help("This rule is used to showcase a special agent with configuration."),
|
||||
elements={
|
||||
"port": DictElement(
|
||||
required=True,
|
||||
parameter_form=String(
|
||||
title=Title("Port of the PBS API."),
|
||||
prefill=DefaultValue("8007"),
|
||||
),
|
||||
),
|
||||
"fingerprint": DictElement(
|
||||
required=True,
|
||||
parameter_form=String(
|
||||
title=Title("Fingerprint of the PBS"),
|
||||
prefill=DefaultValue(""),
|
||||
),
|
||||
),
|
||||
"tokenid": DictElement(
|
||||
required=True,
|
||||
parameter_form=String(
|
||||
title=Title("API Token with audit permissions."),
|
||||
),
|
||||
),
|
||||
"secret": DictElement(
|
||||
required=True,
|
||||
parameter_form=Password(
|
||||
title=Title("Secret for API Token."),
|
||||
migrate=migrate_to_password,
|
||||
),
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
rule_spec_pbs_special_agent = SpecialAgent(
|
||||
topic=Topic.CLOUD,
|
||||
name="pbs_special_agent",
|
||||
title=Title("Proxmox PBS"),
|
||||
parameter_form=_formspec
|
||||
)
|
||||
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
# Shebang needed only for editors
|
||||
|
||||
from cmk.server_side_calls.v1 import noop_parser, SpecialAgentConfig, SpecialAgentCommand, HostConfig
|
||||
|
||||
def _agent_arguments(params, host_config: HostConfig):
|
||||
args = [
|
||||
"--host", host_config.name,
|
||||
"--port", params['port'],
|
||||
"--fingerprint", str(params['fingerprint']),
|
||||
"--tokenid", str(params['tokenid']),
|
||||
"--secret", params['secret'].unsafe()
|
||||
]
|
||||
yield SpecialAgentCommand(command_arguments=args)
|
||||
|
||||
special_agent_pbs_special_agent = SpecialAgentConfig(
|
||||
name="pbs_special_agent",
|
||||
parameter_parser=noop_parser,
|
||||
commands_function=_agent_arguments
|
||||
)
|
||||
Loading…
x
Reference in New Issue
Block a user