Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php: ['8.2', '8.3']
php: ['8.5']
os: ['ubuntu-24.04']

steps:
Expand All @@ -32,7 +32,7 @@ jobs:
tools: phpcs

- name: Setup dependencies
run: composer require -n --no-progress overtrue/phplint
run: composer install

- name: PHP Lint
if: success() || matrix.allow_failure
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ _icingaweb2/
_libraries/
.phpunit.cache/
.phplint.cache/
.phpbench/
reports/
/*.phar
/Icinga
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## v1.0.0

- Raise minimum requirements to PHP 8.2

**Breaking Changes**

- Add mTLS and token auth (changes configuration option names)

## v0.2.1

- Fix maxDataPoints configuration not being used
Expand Down
13 changes: 6 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
.PHONY: setup test lint phpcs
.PHONY: setup test lint phpcs coverage

# TODO: Add graphs module to modules
# ln -s path-to/icingaweb2-module-perfdatagraphs _icingaweb2/modules/perfdatagraphs
setup:
mkdir -p _libraries &&\
git clone --depth 1 -b snapshot/nightly https://github.com/Icinga/icinga-php-library.git _libraries/ipl &&\
git clone --depth 1 -b snapshot/nightly https://github.com/Icinga/icinga-php-thirdparty.git _libraries/vendor &&\
git clone --depth 1 https://github.com/Icinga/icingaweb2.git _icingaweb2 &&\
git clone --depth 1 https://github.com/Icinga/icingadb-web.git _icingaweb2/modules/icingadb
ln -s `pwd` _icingaweb2/modules/graphsgraphite
git clone --depth 1 https://github.com/NETWAYS/icingaweb2-module-perfdatagraphs.git _libraries/perfdatagraphs &&\
git clone --depth 1 https://github.com/Icinga/icingaweb2.git _icingaweb2
test:
ICINGAWEB_LIBDIR=_libraries phpunit
ICINGAWEB_LIBDIR=_libraries ./vendor/bin/phpunit
coverage:
ICINGAWEB_LIBDIR=_libraries ./vendor/bin/phpunit --coverage-html reports/
lint:
phplint application/ library/
phpcs:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This module requires the frontend module:

## Installation Requirements

* PHP version ≥ 8.0
* PHP version ≥ 8.2
* Icinga2 GraphiteWriter
* IcingaDB or IDO Database
* A Graphite compatible API to fetch the data from (Graphite, carbonapi, VictoriaMetrics, etc.)
Expand Down
106 changes: 96 additions & 10 deletions application/forms/PerfdataGraphsGraphiteConfigForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

/**
* PerfdataGraphsGraphiteConfigForm represents the configuration form for the PerfdataGraphs Graphite Module.
* TODO: Icinga Web 2.14 introduced a new Web\Form\ConfigForm, we can migrate when 2.14 is more prevalent
* Then we can also use ipl Validators.
*/
class PerfdataGraphsGraphiteConfigForm extends ConfigForm
{
Expand All @@ -29,17 +31,72 @@ public function createElements(array $formData)
'placeholder' => 'http://localhost:8081',
]);

$this->addElement('text', 'graphite_api_username', [
'label' => t('API basic auth username'),
'description' => t('The user for HTTP basic auth. Not used if empty')
$this->addElement('select', 'graphite_api_auth_method', [
'label' => 'API authentication method',
'description' => 'Authentication method to use for the API',
'multiOptions' => [
'none' => t('None'),
'basic' => 'Basic Auth',
'token' => 'Token',
],
'class' => 'autosubmit',
'required' => false,
]);

$this->addElement('password', 'graphite_api_password', [
'label' => t('API HTTP basic auth password'),
'description' => t('The password for HTTP basic auth. Not used if empty'),
'renderPassword' => true
if (isset($formData['graphite_api_auth_method']) && $formData['graphite_api_auth_method'] === 'basic') {
$this->addElement('text', 'graphite_api_auth_username', [
'label' => t('HTTP basic auth username'),
'description' => t('The user for HTTP basic auth'),
'required' => true,
]);

$this->addElement('password', 'graphite_api_auth_password', [
'label' => t('HTTP basic auth password'),
'description' => t('The password for HTTP basic auth'),
'renderPassword' => true,
'required' => true,
]);
}

if (isset($formData['graphite_api_auth_method']) && $formData['graphite_api_auth_method'] === 'token') {
$this->addElement('text', 'graphite_api_auth_tokentype', [
'label' => t('Token type for the Authorization header'),
'description' => t('API Token type for the Authorization header (default: Bearer)'),
'value' => 'Bearer',
]);

$this->addElement('password', 'graphite_api_auth_tokenvalue', [
'label' => t('Token for the Authorization header'),
'description' => t('API Token for the Authorization header'),
'renderPassword' => true,
'required' => true,
]);
}

$this->addElement('checkbox', 'graphite_api_auth_mtls', [
'label' => t('Use client certificate (mTLS)'),
'description' => t('Use client certificate (mTLS) for the connection'),
'class' => 'autosubmit',
]);

if (isset($formData['graphite_api_auth_mtls']) && $formData['graphite_api_auth_mtls'] === '1') {
$this->addElement('text', 'graphite_api_auth_mtls_cert', [
'label' => t('mTLS client certificate path'),
'description' => t('Path to the client certificate'),
'required' => true,
]);
$this->addElement('text', 'graphite_api_auth_mtls_key', [
'label' => t('mTLS client key path'),
'description' => t('Path to the client key'),
'required' => true,
]);
$this->addElement('text', 'graphite_api_auth_mtls_ca', [
'label' => t('mTLS client CA path'),
'description' => t('Path to the CA. Defaults to system CA'),
'required' => false,
]);
}

$this->addElement('number', 'graphite_api_timeout', [
'label' => t('HTTP timeout in seconds'),
'description' => t('HTTP timeout for the API in seconds. Should be higher than 0'),
Expand Down Expand Up @@ -168,15 +225,44 @@ public static function validateFormData($form): array
$baseURI = $form->getValue('graphite_api_url', 'http://localhost:8081');
$timeout = (int) $form->getValue('graphite_api_timeout', 10);
$maxDataPoints = (int) $form->getValue('graphite_max_data_points', 10000);
$username = $form->getValue('graphite_api_username', '');
$password = $form->getValue('graphite_api_password', '');
// Auth values
$authMethod = $form->getValue('graphite_api_auth_method', 'none');
$authTokenType = $form->getValue('graphite_api_auth_tokentype', 'Bearer');
$authTokenValue = $form->getValue('graphite_api_auth_tokenvalue', '');
$authUsername = $form->getValue('graphite_api_auth_username', '');
$authPassword = $form->getValue('graphite_api_auth_password', '');
// mTLS values
$authMTLS = $form->getValue('graphite_api_auth_mtls', false);
$authMTLSCert = $form->getValue('graphite_api_auth_mtls_cert', '');
$authMTLSKey = $form->getValue('graphite_api_auth_mtls_key', '');
$authMTLSCA = $form->getValue('graphite_api_auth_mtls_ca', '');
// Hint: We use a "skip TLS" logic in the UI, but Guzzle uses "verify TLS"
$tlsVerify = !(bool) $form->getValue('graphite_api_tls_insecure', false);
$hostTemplate = $form->getValue('graphite_writer_host_name_template', '');
$serviceTemplate = $form->getValue('graphite_writer_service_name_template', '');

$auth = [
'method' => mb_strtolower($authMethod),
'tokentype' => $authTokenType,
'tokenvalue' => $authTokenValue,
'username' => $authUsername,
'password' => $authPassword,
'mtls' => $authMTLS,
'mtls_cert' => $authMTLSCert,
'mtls_key' => $authMTLSKey,
'mtls_ca' => $authMTLSCA,
];

try {
$c = new Graphite($baseURI, $username, $password, $timeout, $tlsVerify, $maxDataPoints, $hostTemplate, $serviceTemplate);
$c = new Graphite(
baseURI: $baseURI,
timeout: $timeout,
maxDataPoints: $maxDataPoints,
tlsVerify: $tlsVerify,
hostNameTemplate: $hostTemplate,
serviceNameTemplate: $serviceTemplate,
auth: $auth,
);
} catch (Exception $e) {
return ['output' => 'General error: ' . $e->getMessage(), 'error' => true];
}
Expand Down
19 changes: 19 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"require-dev": {
"overtrue/phplint": "^9.7",
"phpunit/phpunit": "^13.2",
"phpbench/phpbench": "^1.7"
},
"autoload": {
"psr-4": {
"Icinga\\Module\\Perfdatagraphsgraphite\\": "library/Perfdatagraphsgraphite/",
"Icinga\\Module\\Perfdatagraphs\\": "_libraries/perfdatagraphs/library/Perfdatagraphs",
"GuzzleHttp\\Psr7\\": "_libraries/vendor/vendor/guzzlehttp/psr7/src/",
"Psr\\Http\\Message\\": "_libraries/vendor/vendor/psr/http-message/src/",
"ipl\\I18n\\": "_libraries/ipl/vendor/ipl/i18n/src/",
"ipl\\Stdlib\\": "_libraries/ipl/vendor/ipl/stdlib/src/",
"ipl\\": "_libraries/ipl/vendor/ipl/",
"Icinga\\": "_icingaweb2/library/Icinga/"
}
}
}
Loading