diff --git a/.github/workflows/plugin-ci-workflow.yml b/.github/workflows/plugin-ci-workflow.yml
index a1d1f6f..4d9e5f1 100644
--- a/.github/workflows/plugin-ci-workflow.yml
+++ b/.github/workflows/plugin-ci-workflow.yml
@@ -39,8 +39,8 @@ jobs:
- name: Lint every PHP file
run: |
- if find . -path ./vendor -prune -o -name '*.php' -print \
- | xargs -n1 php -l 2>&1 | grep -iv 'no syntax errors detected'; then
+ if find . -path ./vendor -prune -o -name '*.php' -print0 \
+ | xargs -0 -n1 php -l 2>&1 | grep -iv 'no syntax errors detected'; then
echo "Syntax errors found at the declared PHP floor"
exit 1
fi
@@ -153,19 +153,18 @@ jobs:
- name: Create MySQL Config
run: |
- echo -e "[client]\nuser = root\npassword = cactiroot\nhost = 127.0.0.1\n" > ~/.my.cnf
+ printf '[client]\nuser = root\npassword = cactiroot\nhost = 127.0.0.1\n' \
+ > "$RUNNER_TEMP/gpsmap-my.cnf"
- name: Initialize Cacti Database
- env:
- MYSQL_AUTH_USR: '--defaults-file=~/.my.cnf'
run: |
- mysql $MYSQL_AUTH_USR -e 'CREATE DATABASE IF NOT EXISTS cacti;'
- mysql $MYSQL_AUTH_USR -e "CREATE USER IF NOT EXISTS 'cactiuser'@'localhost' IDENTIFIED BY 'cactiuser';"
- mysql $MYSQL_AUTH_USR -e "GRANT ALL PRIVILEGES ON cacti.* TO 'cactiuser'@'localhost';"
- mysql $MYSQL_AUTH_USR -e "GRANT SELECT ON mysql.time_zone_name TO 'cactiuser'@'localhost';"
- mysql $MYSQL_AUTH_USR -e "FLUSH PRIVILEGES;"
- mysql $MYSQL_AUTH_USR cacti < ${{ github.workspace }}/cacti/cacti.sql
- mysql $MYSQL_AUTH_USR -e "INSERT INTO settings (name, value) VALUES ('path_php_binary', '/usr/bin/php')" cacti
+ mysql --defaults-file="$RUNNER_TEMP/gpsmap-my.cnf" -e 'CREATE DATABASE IF NOT EXISTS cacti;'
+ mysql --defaults-file="$RUNNER_TEMP/gpsmap-my.cnf" -e "CREATE USER IF NOT EXISTS 'cactiuser'@'localhost' IDENTIFIED BY 'cactiuser';"
+ mysql --defaults-file="$RUNNER_TEMP/gpsmap-my.cnf" -e "GRANT ALL PRIVILEGES ON cacti.* TO 'cactiuser'@'localhost';"
+ mysql --defaults-file="$RUNNER_TEMP/gpsmap-my.cnf" -e "GRANT SELECT ON mysql.time_zone_name TO 'cactiuser'@'localhost';"
+ mysql --defaults-file="$RUNNER_TEMP/gpsmap-my.cnf" -e "FLUSH PRIVILEGES;"
+ mysql --defaults-file="$RUNNER_TEMP/gpsmap-my.cnf" cacti < ${{ github.workspace }}/cacti/cacti.sql
+ mysql --defaults-file="$RUNNER_TEMP/gpsmap-my.cnf" -e "INSERT INTO settings (name, value) VALUES ('path_php_binary', '/usr/bin/php')" cacti
- name: Install Composer Dependencies
run: |
@@ -212,11 +211,15 @@ jobs:
run: |
cd ${{ github.workspace }}/cacti
sudo php cli/plugin_manage.php --plugin=gpsmap --install --enable
+ mysql -u cactiuser -p'cactiuser' -h 127.0.0.1 cacti -sN \
+ -e "SELECT COUNT(*) FROM information_schema.tables
+ WHERE table_schema = 'cacti' AND table_name = 'plugin_gpsmap_dns_cache';" \
+ | grep -qx '1'
- name: Check PHP Syntax for Plugin
run: |
cd ${{ github.workspace }}/cacti/plugins/gpsmap
- if find . -name '*.php' -exec php -l {} 2>&1 \; | grep -iv 'no syntax errors detected'; then
+ if find . -name '*.php' -print0 | xargs -0 -n1 php -l 2>&1 | grep -iv 'no syntax errors detected'; then
echo "Syntax errors found!"
exit 1
fi
@@ -293,12 +296,15 @@ jobs:
fi
echo "Using host_template id $TPL"
sudo php cli/add_device.php --description=mapped --ip=127.0.0.1 --template="$TPL"
+ sudo php cli/add_device.php --description=mapped-hostname --ip=localhost --template="$TPL"
mysql -u cactiuser -p'cactiuser' -h 127.0.0.1 cacti \
- -e "UPDATE host SET latitude='51.5074', longitude='-0.1278' WHERE description='mapped';"
+ -e "UPDATE host SET latitude='51.5074', longitude='-0.1278'
+ WHERE description IN ('mapped', 'mapped-hostname');"
mysql -u cactiuser -p'cactiuser' -h 127.0.0.1 cacti \
-e "INSERT INTO gpsmap_templates (templateID, templateName, upimage, recoverimage, downimage, AP)
SELECT host_template_id, 'ci', 'Green.png', 'Orange.png', 'Red.png', 0
FROM host WHERE description='mapped' LIMIT 1;"
+ sudo php plugins/gpsmap/gpsmap_dns.php
- name: Run Cacti Poller
run: |
@@ -325,6 +331,11 @@ jobs:
cat all.xml
exit 1
fi
+ if ! grep -q 'address="localhost"' all.xml; then
+ echo "all.xml contains no hostname-resolved marker"
+ cat all.xml
+ exit 1
+ fi
- name: View Cacti Logs
if: always()
diff --git a/.gitignore b/.gitignore
index 9e104b5..d3d6561 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
# review pipeline scratch
.diffcheck.php
.rv.tar
+tests/_dbg.php
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fa14bcb..de4b99d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
## Changelog
+--- 2.2 ---
+
+* issue#6, issue#104, issue#107, issue#109, issue#110: Refactor the poller for single-load dual-stack maps, asynchronous last-known-good DNS, thold-aware markers, and safe artifact pruning
+
--- 2.1 ---
* issue: Add gpsmap_templates table index to be unique by device template
diff --git a/INFO b/INFO
index 2b0f08a..229defe 100644
--- a/INFO
+++ b/INFO
@@ -1,6 +1,6 @@
[info]
name = gpsmap
-version = 2.1
+version = 2.2
longname = Maps Plugin
author = Andrew Aloia, Wixiweb, The Cacti Group
email =
diff --git a/README.md b/README.md
index 915acf5..9dfac0b 100644
--- a/README.md
+++ b/README.md
@@ -11,8 +11,9 @@ Maps tab reads a static file instead of querying the database.
## Features
-* Devices plotted by latitude and longitude, coloured by Up, Recovering, Down,
- Disabled or Undefined status
+* IPv4 and IPv6 Devices plotted by latitude and longitude, coloured by Up,
+ Recovering, Down, Disabled or Undefined status. When thold is enabled, an
+ otherwise-up Device with an active threshold uses its Down icon
* Per Device Template marker icons for the Up, Recovering and Down states
@@ -20,7 +21,7 @@ Maps tab reads a static file instead of querying the database.
furthest Device sharing the Access Point's group number, and optionally
limited to an arc
-* Drill down by subnet, from the first octet through to individual Devices
+* Drill down by subnet: /8, /16 and /24 for IPv4, and /16, /32 and /48 for IPv6
* KML export alongside the XML, so the same data opens in Google Earth
@@ -55,8 +56,8 @@ JavaScript, and there is no workaround inside Cacti.
Installing adds seven columns to Cacti's `host` table (`latitude`, `longitude`,
`GPScoverage`, `start`, `stop`, `groupnum`, `rdistance`) and creates the
-`gpsmap_templates` table. Uninstalling deliberately leaves both in place so
-coordinates survive an accidental removal.
+`gpsmap_templates` and `plugin_gpsmap_dns_cache` tables. Uninstalling
+deliberately leaves them in place so coordinates survive an accidental removal.
## Configuration
@@ -139,13 +140,21 @@ Understanding the order of events explains most of what can go wrong.
1. The poller runs and Cacti calls the plugin's `poller_bottom` hook.
2. The plugin selects every Device whose Device Template appears in
- `gpsmap_templates`, resolves each hostname to an address, and discards any
- Device without coordinates or whose name does not resolve to IPv4.
+ `gpsmap_templates`. Literal IPv4 and IPv6 addresses are used directly;
+ configured names use the last address in `plugin_gpsmap_dns_cache`.
3. It writes `plugins/gpsmap/XML/all.xml`, `all.kml` and `all-top.html`, then
repeats for each subnet prefix so the drill-down views are prebuilt.
-4. Opening the Maps tab serves a page that loads Google's map library and
+4. It starts a separate DNS refresh worker after publishing. A newly configured
+ hostname can therefore appear on the following poll, while slow DNS never
+ holds up the poller. A failed refresh retains the last-known address.
+
+5. Generated subnet files not refreshed for three poller intervals are removed.
+ Cleanup runs only after a successful Device query; unrelated files are never
+ touched.
+
+6. Opening the Maps tab serves a page that loads Google's map library and
fetches the matching `.xml` file over HTTP.
Nothing appears until a poll cycle has completed after configuration. If
@@ -165,9 +174,14 @@ cycle has completed since both were set; and `plugins/gpsmap/XML` contains
files newer than the last poll.
**Some Devices are missing.** A Device is skipped when it has no coordinates,
-when its hostname does not resolve to an IPv4 address, when its Device Template
-is not in Map Templates, or when it is disabled and Display Disabled Devices is
-off.
+when its hostname has no cached address yet, when its Device Template is not in
+Map Templates, or when it is disabled and Display Disabled Devices is off. For
+a newly added hostname, allow one additional poll cycle for the asynchronous DNS
+worker to populate the cache.
+
+**An Up Device has a red marker.** When the thold plugin is enabled, gpsmap
+uses the configured Down icon for an otherwise-up Device with any active,
+enabled threshold. Disable or clear the threshold to restore the Up icon.
**The XML directory is empty.** The Data Collector cannot write to it. Check
ownership, permissions and SELinux context. The Cacti log records a failure
diff --git a/gpsmap.php b/gpsmap.php
index d0fba7b..05e7156 100644
--- a/gpsmap.php
+++ b/gpsmap.php
@@ -21,22 +21,24 @@
chdir('../../');
include('./include/auth.php');
+require_once('./plugins/gpsmap/gpsmap_security.php');
include_once('./plugins/gpsmap/includes/setup/show.php');
include_once('./plugins/gpsmap/includes/setup/gpsmapinitial.php');
$body = '';
general_header();
-//set headers to NOT cache a page
-header("Cache-Control: no-cache, no-store, must-revalidate");
-header("Pragma: no-cache");
-header("Expires: 0");
+// set headers to NOT cache a page
+header('Cache-Control: no-cache, no-store, must-revalidate');
+header('Pragma: no-cache');
+header('Expires: 0');
-//decide what needs to be shown
+// decide what needs to be shown
switch ($show) {
- //selected nodes
+ // selected nodes
case 'setup':
$body = __('Please make sure to properly configure Maps first under Settings > Maps', 'gpsmap');
+
break;
default:
if (!$parameter) {
@@ -46,44 +48,44 @@
/* Reject any parameter that contains directory traversal sequences or
* characters outside the safe set. basename() alone does not strip
* embedded ../ so we validate the whole value first. */
- if (!preg_match('/^[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)*$/', $parameter)) {
+ if (!gpsmap_artifact_parameter_is_valid($parameter)) {
$parameter = 'all';
}
$fileLocation = './plugins/gpsmap/XML/' . $parameter . '-top.html';
if (file_exists($fileLocation)) {
- echo file_get_contents($fileLocation);
+ print file_get_contents($fileLocation);
}
break;
}
-//---------------------------------------------------------------
+// ---------------------------------------------------------------
if ($show != 'setup') { ?>