diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..ecf46de --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -0,0 +1,203 @@ +name: Zabbix APK Builder + +on: + # Trigger on pushes to main/test branch into the zabbix-apk-builder directory + push: + branches: [ main, test ] + paths: [ 'zabbix-apk-builder/**' ] + + # Scheduled runs at 06:00 UTC daily + schedule: + - cron: '0 6 * * *' + +jobs: + check-version: + runs-on: ubuntu-latest + # Skip the execution if the commit author is the bot itself to prevent loops + if: ${{ gitea.event.head_commit.author.name != 'Gitea Action' }} + outputs: + should_build: ${{ steps.version-check.outputs.should_build }} + latest_version: ${{ steps.version-check.outputs.latest_version }} + current_version: ${{ steps.version-check.outputs.current_version }} + is_push_trigger: ${{ steps.version-check.outputs.is_push_trigger }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Check for new Zabbix version + id: version-check + run: | + set -euo pipefail + + # Remove jq installation + # apt-get update && apt-get install -y jq + + # Detect trigger type + IS_PUSH="${{ gitea.event_name == 'push' }}" + echo "is_push_trigger=${IS_PUSH}" >> "${GITHUB_OUTPUT}" + + # Get versions + CURRENT_VERSION=$(grep '^pkgver=' zabbix-apk-builder/APKBUILD | cut -d'=' -f2) + LATEST_VERSION=$(curl -s "https://git.zabbix.com/rest/api/1.0/projects/ZBX/repos/zabbix/tags?limit=100" | \ + grep -o '"displayId":"[^"]*"' | cut -d'"' -f4 | \ + grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | grep -v 'rc\|beta\|alpha' | \ + sort -V | tail -1) + + echo "current_version=${CURRENT_VERSION}" >> "${GITHUB_OUTPUT}" + echo "latest_version=${LATEST_VERSION}" >> "${GITHUB_OUTPUT}" + + # Always build on push, build on schedule if versions differ + if [[ "${IS_PUSH}" == "true" || "${CURRENT_VERSION}" != "${LATEST_VERSION}" ]]; then + echo "should_build=true" >> "${GITHUB_OUTPUT}" + else + echo "should_build=false" >> "${GITHUB_OUTPUT}" + fi + + update-version: + needs: check-version + # Only update version during scheduled runs when new version is available + if: ${{ needs.check-version.outputs.should_build == 'true' && needs.check-version.outputs.is_push_trigger == 'false' && needs.check-version.outputs.current_version != needs.check-version.outputs.latest_version }} + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.ACCESS_TOKEN }} + + - name: Update APKBUILD version + run: | + set -euo pipefail + + LATEST_VERSION="${{ needs.check-version.outputs.latest_version }}" + CURRENT_VERSION="${{ needs.check-version.outputs.current_version }}" + + echo "Updating APKBUILD from ${CURRENT_VERSION} to ${LATEST_VERSION}" + + # Update pkgver + sed -i "s/^pkgver=.*/pkgver=${LATEST_VERSION}/" zabbix-apk-builder/APKBUILD + + # Reset pkgrel to 0 for new version + sed -i "s/^pkgrel=.*/pkgrel=0/" zabbix-apk-builder/APKBUILD + + # Clear checksums (will be regenerated during build) + sed -i 's/^sha512sums=.*/sha512sums="SKIP"/' zabbix-apk-builder/APKBUILD + + # Commit changes with [ci skip] to prevent recursive triggers + git config --local user.email "action@gitea.com" + git config --local user.name "Gitea Action" + git add zabbix-apk-builder/APKBUILD + git commit -m "AUTO: Update Zabbix to version ${LATEST_VERSION} [ci skip]" || exit 0 + git push + + build-packages: + # Build packages after version update (for scheduled runs) or immediately (for push runs) + needs: [check-version, update-version] + # Run if should_build=true AND either update-version ran successfully OR it was skipped due to push trigger + if: ${{ needs.check-version.outputs.should_build == 'true' && (success() || needs.check-version.outputs.is_push_trigger == 'true') }} + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + # Use token to ensure we get the latest version if it was updated + token: ${{ secrets.ACCESS_TOKEN }} + ref: ${{ gitea.ref }} + + - name: Verify build environment + run: | + set -euo pipefail + + echo "=== Build Environment ===" + echo "Trigger type: ${{ gitea.event_name }}" + echo "Current branch: $(git branch --show-current)" + echo "APKBUILD version: $(grep '^pkgver=' zabbix-apk-builder/APKBUILD | cut -d'=' -f2)" + echo "Target version: ${{ needs.check-version.outputs.latest_version }}" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Zabbix packages + env: + CI_RUN_ID: ${{ gitea.run_id }} + run: | + set -euo pipefail + + cd zabbix-apk-builder + chmod +x build.sh + ./build.sh + + - name: Verify and list built packages + run: | + set -euo pipefail + + cd zabbix-apk-builder + + # Verify packages exist somewhere + PACKAGE_COUNT=$(find packages -name "*.apk" | wc -l) + + if [[ $PACKAGE_COUNT -eq 0 ]]; then + echo "ERROR: No packages found" + find packages -type f 2>/dev/null || echo "packages directory is empty" + exit 1 + fi + + echo "Found $PACKAGE_COUNT packages:" + find packages -name "*.apk" -exec ls -lh {} \; + + - name: Upload packages as artifacts + uses: actions/upload-artifact@v3 + with: + name: zabbix-apk-packages-${{ gitea.run_number }} + path: zabbix-apk-builder/packages/**/*.apk + retention-days: 30 + if-no-files-found: error + + deploy-test: + needs: [check-version, build-packages] + if: ${{ needs.check-version.outputs.should_build == 'true' && contains(gitea.ref, 'test') }} + runs-on: ubuntu-latest + + steps: + - name: Download packages + uses: actions/download-artifact@v3 + with: + name: zabbix-apk-packages-${{ gitea.run_number }} + path: packages/ + + - name: Test deployment in Alpine container + run: | + set -euo pipefail + + # Find packages + AGENT_PKG=$(find packages -name "zabbix-agent-*.apk" | head -1) + PROXY_PKG=$(find packages -name "zabbix-proxy-*.apk" | head -1) + + # Test function + test_package() { + local pkg="$1" + local binary="$2" + + if [[ -f "$pkg" ]]; then + echo "Testing $(basename "$pkg")..." + CONTAINER_ID=$(docker run -d alpine:latest sleep 30) + docker cp "$pkg" "$CONTAINER_ID:/$(basename "$pkg")" + if docker exec "$CONTAINER_ID" sh -c " + apk add --allow-untrusted /$(basename "$pkg") >/dev/null 2>&1 + which $binary >/dev/null 2>&1 + $binary --version >/dev/null 2>&1 + "; then + echo "SUCCESS: $(basename "$pkg") test passed" + else + echo "FAIL: $(basename "$pkg") test failed" + fi + docker rm -f "$CONTAINER_ID" >/dev/null + else + echo "ERROR: Package not found: $pkg" + fi + } + + test_package "$AGENT_PKG" "zabbix_agentd" + test_package "$PROXY_PKG" "zabbix_proxy" \ No newline at end of file diff --git a/zabbix-apk-builder/.gitignore b/zabbix-apk-builder/.gitignore new file mode 100644 index 0000000..dd55924 --- /dev/null +++ b/zabbix-apk-builder/.gitignore @@ -0,0 +1,17 @@ +# Build artifacts +packages/ +*.apk +src/ +*.tar.gz + +# Docker cache +.docker/ + +# Backup files +*.backup +*.bak +*~ + +# OS files +.DS_Store +Thumbs.db diff --git a/zabbix-apk-builder/APKBUILD b/zabbix-apk-builder/APKBUILD new file mode 100644 index 0000000..250875f --- /dev/null +++ b/zabbix-apk-builder/APKBUILD @@ -0,0 +1,148 @@ +# Contributor: Maksym Buz +# Maintainer: Maksym Buz +pkgname=zabbix +pkgver=7.4.4 +pkgrel=0 +pkgdesc="Enterprise-class open source distributed monitoring solution" +url="https://www.zabbix.com/" +arch="all" +license="AGPL-3.0-only" +options="!check" # No test suite available +makedepends=" + autoconf + automake + curl-dev + libevent-dev + libxml2-dev + libtool + linux-headers + net-snmp-dev + openssl-dev + pcre2-dev + sqlite-dev + unixodbc-dev + libssh2-dev + " +pkgusers="zabbix" +pkggroups="zabbix" +install="$pkgname-agent.pre-install $pkgname-proxy.pre-install" +subpackages="$pkgname-agent $pkgname-proxy" +source="https://cdn.zabbix.com/zabbix/sources/stable/${pkgver%.*}/zabbix-$pkgver.tar.gz + zabbix-agent.initd + zabbix-agent.confd + zabbix-proxy.initd + zabbix-proxy.confd + " +builddir="$srcdir/$pkgname-$pkgver" + +prepare() { + default_prepare + + # Regenerate autotools files + cd "$builddir" + autoreconf -fiv + + # Create separate build directories for agent and proxy + cp -r "$builddir" "$srcdir/zabbix-agent-$pkgver" + cp -r "$builddir" "$srcdir/zabbix-proxy-$pkgver" +} + +build() { + # Build agent with minimal dependencies + cd "$srcdir/zabbix-agent-$pkgver" + ./configure \ + --build=$CBUILD \ + --host=$CHOST \ + --prefix=/usr \ + --sysconfdir=/etc/zabbix \ + --localstatedir=/var \ + --enable-agent \ + --with-libcurl \ + --with-net-snmp \ + --with-openssl \ + --with-libpcre2 + make + + # Build proxy with database support + cd "$srcdir/zabbix-proxy-$pkgver" + ./configure \ + --build=$CBUILD \ + --host=$CHOST \ + --prefix=/usr \ + --sysconfdir=/etc/zabbix \ + --localstatedir=/var \ + --enable-proxy \ + --with-libcurl \ + --with-net-snmp \ + --with-openssl \ + --with-libpcre2 \ + --with-sqlite3 \ + --with-unixodbc \ + --with-ssh2 \ + --with-libxml2 \ + --with-libevent + make +} + +package() { + # Meta-package - intentionally empty + # Users install zabbix-agent and/or zabbix-proxy directly + mkdir -p "$pkgdir" +} + +agent() { + pkgdesc="Zabbix monitoring agent" + + cd "$srcdir/zabbix-agent-$pkgver" + + # Install agent binary + install -Dm755 src/zabbix_agent/zabbix_agentd \ + "$subpkgdir"/usr/sbin/zabbix_agentd + + + # Install agent configuration + install -Dm644 conf/zabbix_agentd.conf \ + "$subpkgdir"/etc/zabbix/zabbix_agentd.conf + + # Install init script and conf + install -Dm755 "$srcdir"/zabbix-agent.initd \ + "$subpkgdir"/etc/init.d/zabbix-agent + install -Dm644 "$srcdir"/zabbix-agent.confd \ + "$subpkgdir"/etc/conf.d/zabbix-agent + + # Create directories + install -dm755 "$subpkgdir"/var/log/zabbix + install -dm755 "$subpkgdir"/var/run/zabbix +} + +proxy() { + pkgdesc="Zabbix network monitoring proxy daemon" + + cd "$srcdir/zabbix-proxy-$pkgver" + + # Install proxy binary + install -Dm755 src/zabbix_proxy/zabbix_proxy \ + "$subpkgdir"/usr/sbin/zabbix_proxy + + # Install proxy configuration + install -Dm644 conf/zabbix_proxy.conf \ + "$subpkgdir"/etc/zabbix/zabbix_proxy.conf + + # Install init script and conf + install -Dm755 "$srcdir"/zabbix-proxy.initd \ + "$subpkgdir"/etc/init.d/zabbix-proxy + install -Dm644 "$srcdir"/zabbix-proxy.confd \ + "$subpkgdir"/etc/conf.d/zabbix-proxy + + # Create directories + install -dm755 "$subpkgdir"/var/log/zabbix + install -dm755 "$subpkgdir"/var/run/zabbix +} +# --- TEST --- +sha512sums="SKIP" +SKIP +SKIP +SKIP +SKIP +SKIP +" \ No newline at end of file diff --git a/zabbix-apk-builder/Dockerfile b/zabbix-apk-builder/Dockerfile new file mode 100644 index 0000000..077010f --- /dev/null +++ b/zabbix-apk-builder/Dockerfile @@ -0,0 +1,67 @@ +FROM alpine:latest + +# Install build dependencies +RUN apk add --no-cache \ + abuild \ + alpine-sdk \ + autoconf \ + automake \ + libtool \ + linux-headers \ + pkgconfig \ + sudo \ + curl-dev \ + libevent-dev \ + libxml2-dev \ + net-snmp-dev \ + openssl-dev \ + pcre2-dev \ + sqlite-dev \ + unixodbc-dev \ + zlib-dev \ + openldap-dev \ + libssh2-dev \ + && adduser -D -G abuild builder \ + && echo "builder ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + +# Setup build environment +USER builder +WORKDIR /home/builder + +# Generate signing keys +RUN abuild-keygen -a -i -n + +# Copy package files +COPY --chown=builder:builder . /home/builder/zabbix/ + +WORKDIR /home/builder/zabbix + +# Create build script that just builds packages +USER root +RUN cat > /usr/local/bin/build-packages.sh << 'EOF' +#!/bin/sh +set -e + +echo "Building packages as builder user..." +sudo -u builder sh -c " + cd /home/builder/zabbix + echo 'Generating checksums...' + abuild checksum + echo 'Building packages...' + abuild -r +" + +echo "Build complete! Packages built in /home/builder/packages:" +find /home/builder/packages -name "*.apk" -exec ls -la {} \; + +echo "Setting proper permissions on packages..." +chmod 644 /home/builder/packages/*.apk 2>/dev/null || true + +echo "Final package list (excluding APKINDEX):" +find /home/builder/packages -name "*.apk" -exec ls -la {} \; +EOF + +RUN chmod +x /usr/local/bin/build-packages.sh + +# Set build command +CMD ["/usr/local/bin/build-packages.sh"] \ No newline at end of file diff --git a/zabbix-apk-builder/README.md b/zabbix-apk-builder/README.md new file mode 100644 index 0000000..6ff8acc --- /dev/null +++ b/zabbix-apk-builder/README.md @@ -0,0 +1,139 @@ +# Zabbix APK Builder + +Automated Alpine Linux package builder for Zabbix Agent and Proxy with CI/CD pipeline integration. + +## Features + +- ๐Ÿ”„ **Automatic Version Detection**: Monitors Zabbix releases using Bitbucket API +- ๐Ÿš€ **CI/CD Pipeline**: Full automation from version detection to package deployment +- ๐Ÿ“ฆ **Multi-package Support**: Builds agent and proxy packages +- ๐Ÿงช **Automated Testing**: Tests package installation in Alpine containers +- ๐Ÿ“Š **Gitea Integration**: Publishes packages to Gitea repository + +## Quick Start + +### Prerequisites + +- Docker installed +- Gitea repository with Actions enabled + +### Manual Build + +```bash +# Clone the repository +git clone +cd zabbix-apk-builder + +# Build packages locally +chmod +x build.sh +./build.sh + +# Check built packages +ls -la packages/builder/x86_64/ +``` + +## Package Information + +### Built Packages + +1. **zabbix-agent** - Zabbix Agent only +2. **zabbix-proxy** - Zabbix Proxy +3. **zabbix** - Meta package + +## CI/CD Pipeline + +### Automatic Triggers + +- **Daily**: Checks for new Zabbix versions at 6 AM UTC +- **Push**: Builds when code changes in main/test branches + +### Version Detection + +Uses Zabbix Bitbucket API: +```bash +https://git.zabbix.com/rest/api/1.0/projects/ZBX/repos/zabbix/tags +``` + +### Pipeline Jobs + +1. **check-version**: Detects new Zabbix releases +2. **update-version**: Updates APKBUILD automatically +3. **build-packages**: Builds APK packages +4. **publish-to-gitea**: Deploys to your repository +5. **deploy-test**: Tests installation (test branch) + +## Configuration + +### GitHub Secrets Required + +```bash +GITEA_SSH_KEY # SSH private key for Gitea access +``` + +### File Structure + +``` +zabbix-git/ +โ””โ”€โ”€ zabbix-apk-builder/ + โ”œโ”€โ”€ .gitea/ + โ”‚ โ””โ”€โ”€ workflows/ + โ”‚ โ””โ”€โ”€ build.yaml # Main CI/CD pipeline + โ”œโ”€โ”€ APKBUILD # Alpine package definition + โ”œโ”€โ”€ Dockerfile # Build environment container + โ”œโ”€โ”€ README.md # This file + โ”œโ”€โ”€ build.sh # Local build script + โ”œโ”€โ”€ packages/ # Generated packages (gitignored) + โ”œโ”€โ”€ zabbix-agent.confd # Agent configuration + โ”œโ”€โ”€ zabbix-agent.initd # Agent init script + โ”œโ”€โ”€ zabbix-agent.pre-install # Agent pre-install script + โ”œโ”€โ”€ zabbix-proxy.confd # Proxy configuration + โ”œโ”€โ”€ zabbix-proxy.initd # Proxy init script + โ””โ”€โ”€ zabbix-proxy.pre-install # Proxy pre-install script +``` + +## Development + +### Local Testing + +```bash +# Test build locally +./build.sh + +# Test in Docker +docker run --rm -it \ + -v $(pwd)/packages:/packages \ + alpine:3.18 sh -c " + apk add --allow-untrusted /packages/zabbix-agent-*.apk + zabbix_agentd --version + " +``` + +### Branch Strategy + +- **main**: Production releases, merge only +- **test**: Testing and validation + +### Making Changes + +1. Create feature branch from `main` +2. Test changes thoroughly +3. Validate CI +4. Merge to `main` + + +### Version Detection + +```bash +# Test API manually +curl -s "https://git.zabbix.com/rest/api/1.0/projects/ZBX/repos/zabbix/tags?limit=100" | \ + jq -r '.values[].displayId' | \ + grep -E '^[0-9]+\.[0-9]+\.[0-9]+ + | \ + sort -V | tail -1 +``` + +## License + +This project follows the same license as Zabbix (AGPLv3). + +--- diff --git a/zabbix-apk-builder/build.sh b/zabbix-apk-builder/build.sh new file mode 100755 index 0000000..4a425d7 --- /dev/null +++ b/zabbix-apk-builder/build.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +set -e + +# Configuration +PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +IMAGE_NAME="zabbix-apk-builder" +# Use a unique ID from the CI environment if available, otherwise fall back to PID +UNIQUE_ID="${CI_RUN_ID:-$$}" +CONTAINER_NAME="zabbix-build-${UNIQUE_ID}" +OUTPUT_DIR="$PROJECT_DIR/packages" + +echo "=== Zabbix APK Builder ===" +echo "Project directory: $PROJECT_DIR" +echo "Output directory: $OUTPUT_DIR" + +# Clean up function +cleanup() { + echo "Cleaning up container..." + docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true +} + +trap cleanup EXIT + +# Clean and create output directory +rm -rf "$OUTPUT_DIR" +mkdir -p "$OUTPUT_DIR" + +# Build Docker image +echo "Building Docker image..." +docker build -t "$IMAGE_NAME" "$PROJECT_DIR" + +# Run the build in the container +echo "Running package build in container..." +docker run --name "$CONTAINER_NAME" "$IMAGE_NAME" + +# Copy packages from container to host +echo "Copying packages from container..." +if docker cp "$CONTAINER_NAME:/home/builder/packages/." "$OUTPUT_DIR/"; then + echo "โœ… Packages copied successfully" + + # Remove APKINDEX files (we only want the .apk packages) + echo "Removing repository index files..." + find "$OUTPUT_DIR" -name "APKINDEX.tar.gz" -delete 2>/dev/null || true + + # Fix permissions on copied files + echo "Fixing file permissions..." + find "$OUTPUT_DIR" -name "*.apk" -exec chmod 644 {} \; 2>/dev/null || true + + echo "Build completed successfully!" + echo "Packages are in $OUTPUT_DIR:" + find "$OUTPUT_DIR" -name "*.apk" -exec ls -la {} \; +else + echo "โŒ Failed to copy packages" + echo "Checking what's in the container..." + docker exec "$CONTAINER_NAME" find /home/builder -name "*.apk" -exec ls -la {} \; 2>/dev/null || true + docker exec "$CONTAINER_NAME" ls -la /home/builder/packages/ 2>/dev/null || true + exit 1 +fi \ No newline at end of file diff --git a/zabbix-apk-builder/zabbix-agent.confd b/zabbix-apk-builder/zabbix-agent.confd new file mode 100644 index 0000000..171df8e --- /dev/null +++ b/zabbix-apk-builder/zabbix-agent.confd @@ -0,0 +1,11 @@ +# Configuration for zabbix-agent + +# User and group for the agent +ZABBIX_AGENT_USER="zabbix" +ZABBIX_AGENT_GROUP="zabbix" + +# Configuration file location +ZABBIX_AGENT_CONFIG="/etc/zabbix/zabbix_agentd.conf" + +# PID file location +ZABBIX_AGENT_PID="/var/run/zabbix/zabbix_agentd.pid" diff --git a/zabbix-apk-builder/zabbix-agent.initd b/zabbix-apk-builder/zabbix-agent.initd new file mode 100644 index 0000000..c350b19 --- /dev/null +++ b/zabbix-apk-builder/zabbix-agent.initd @@ -0,0 +1,42 @@ +#!/sbin/openrc-run +# Copyright 1999-2023 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +extra_commands="checkconfig" +extra_started_commands="reload" + +depend() { + need net + use logger +} + +: ${ZABBIX_AGENT_USER:=zabbix} +: ${ZABBIX_AGENT_GROUP:=zabbix} +: ${ZABBIX_AGENT_CONFIG:=/etc/zabbix/zabbix_agentd.conf} +: ${ZABBIX_AGENT_PID:=/var/run/zabbix/zabbix_agentd.pid} + +command="/usr/sbin/zabbix_agentd" +command_args="-c ${ZABBIX_AGENT_CONFIG}" +command_user="${ZABBIX_AGENT_USER}:${ZABBIX_AGENT_GROUP}" +pidfile="${ZABBIX_AGENT_PID}" +required_files="${ZABBIX_AGENT_CONFIG}" + +checkconfig() { + if [ ! -f "${ZABBIX_AGENT_CONFIG}" ] ; then + eerror "You need to create appropriate config file." + return 1 + fi +} + +start_pre() { + checkconfig || return $? + + checkpath --directory --owner ${ZABBIX_AGENT_USER}:${ZABBIX_AGENT_GROUP} --mode 0755 \ + $(dirname ${ZABBIX_AGENT_PID}) /var/log/zabbix +} + +reload() { + ebegin "Reloading ${SVCNAME}" + start-stop-daemon --signal HUP --pidfile "${pidfile}" + eend $? +} diff --git a/zabbix-apk-builder/zabbix-agent.pre-install b/zabbix-apk-builder/zabbix-agent.pre-install new file mode 100644 index 0000000..c148181 --- /dev/null +++ b/zabbix-apk-builder/zabbix-agent.pre-install @@ -0,0 +1,6 @@ +#!/bin/sh + +addgroup -S zabbix 2>/dev/null +adduser -S -D -H -s /bin/false -G zabbix -g zabbix zabbix 2>/dev/null + +exit 0 diff --git a/zabbix-apk-builder/zabbix-proxy.confd b/zabbix-apk-builder/zabbix-proxy.confd new file mode 100644 index 0000000..5cbcbf2 --- /dev/null +++ b/zabbix-apk-builder/zabbix-proxy.confd @@ -0,0 +1,11 @@ +# Configuration for zabbix-proxy + +# User and group for the proxy +ZABBIX_PROXY_USER="zabbix" +ZABBIX_PROXY_GROUP="zabbix" + +# Configuration file location +ZABBIX_PROXY_CONFIG="/etc/zabbix/zabbix_proxy.conf" + +# PID file location +ZABBIX_PROXY_PID="/var/run/zabbix/zabbix_proxy.pid" diff --git a/zabbix-apk-builder/zabbix-proxy.initd b/zabbix-apk-builder/zabbix-proxy.initd new file mode 100644 index 0000000..a137756 --- /dev/null +++ b/zabbix-apk-builder/zabbix-proxy.initd @@ -0,0 +1,43 @@ +#!/sbin/openrc-run +# Copyright 1999-2023 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +extra_commands="checkconfig" +extra_started_commands="reload" + +depend() { + need net + use logger + after postgresql mysql +} + +: ${ZABBIX_PROXY_USER:=zabbix} +: ${ZABBIX_PROXY_GROUP:=zabbix} +: ${ZABBIX_PROXY_CONFIG:=/etc/zabbix/zabbix_proxy.conf} +: ${ZABBIX_PROXY_PID:=/var/run/zabbix/zabbix_proxy.pid} + +command="/usr/sbin/zabbix_proxy" +command_args="-c ${ZABBIX_PROXY_CONFIG}" +command_user="${ZABBIX_PROXY_USER}:${ZABBIX_PROXY_GROUP}" +pidfile="${ZABBIX_PROXY_PID}" +required_files="${ZABBIX_PROXY_CONFIG}" + +checkconfig() { + if [ ! -f "${ZABBIX_PROXY_CONFIG}" ] ; then + eerror "You need to create appropriate config file." + return 1 + fi +} + +start_pre() { + checkconfig || return $? + + checkpath --directory --owner ${ZABBIX_PROXY_USER}:${ZABBIX_PROXY_GROUP} --mode 0755 \ + $(dirname ${ZABBIX_PROXY_PID}) /var/log/zabbix +} + +reload() { + ebegin "Reloading ${SVCNAME}" + start-stop-daemon --signal HUP --pidfile "${pidfile}" + eend $? +} diff --git a/zabbix-apk-builder/zabbix-proxy.pre-install b/zabbix-apk-builder/zabbix-proxy.pre-install new file mode 100644 index 0000000..c148181 --- /dev/null +++ b/zabbix-apk-builder/zabbix-proxy.pre-install @@ -0,0 +1,6 @@ +#!/bin/sh + +addgroup -S zabbix 2>/dev/null +adduser -S -D -H -s /bin/false -G zabbix -g zabbix zabbix 2>/dev/null + +exit 0 diff --git a/zabbix-tests/host-cleanup.py b/zabbix-tests/host-cleanup.py new file mode 100755 index 0000000..cc41980 --- /dev/null +++ b/zabbix-tests/host-cleanup.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +import requests +import json +import time + +# === CONFIGURATION === +ZABBIX_URL = "http://10.0.0.101:8887/api_jsonrpc.php" +ZABBIX_TOKEN = "c785634354e760a6843055ba4581bc7b6cd6eb2ec75f7c2a79f251c1719933f7" +GROUP_ID = "19" +BATCH_SIZE = 100 +HOST_PATTERN = "dummy-host-" + +HEADERS = { + "Content-Type": "application/json-rpc", + "Authorization": f"Bearer {ZABBIX_TOKEN}" +} + +def zbx_request(method, params): + payload = { + "jsonrpc": "2.0", + "method": method, + "params": params, + "id": int(time.time()) + } + r = requests.post(ZABBIX_URL, headers=HEADERS, data=json.dumps(payload)) + r.raise_for_status() + resp = r.json() + if "error" in resp: + raise Exception(f"API error: {resp['error']}") + return resp + +def cleanup_hosts(): + # Get all hosts in the group + resp = zbx_request("host.get", { + "groupids": [GROUP_ID], + "output": ["hostid", "host"] + }) + + # Filter hosts that contain the dummy pattern + hosts = [h for h in resp.get("result", []) if HOST_PATTERN in h["host"]] + + if not hosts: + print("No dummy hosts found") + return + + print(f"Deleting {len(hosts)} hosts") + + # Delete in batches + host_ids = [h["hostid"] for h in hosts] + total_deleted = 0 + + for i in range(0, len(host_ids), BATCH_SIZE): + batch = host_ids[i:i + BATCH_SIZE] + try: + resp = zbx_request("host.delete", batch) + deleted = len(resp.get("result", {}).get("hostids", [])) + total_deleted += deleted + print(f"Deleted batch {i//BATCH_SIZE + 1}: {deleted} hosts") + except Exception as e: + print(f"Error in batch {i//BATCH_SIZE + 1}: {e}") + + if i + BATCH_SIZE < len(host_ids): + time.sleep(0.5) + + print(f"Total deleted: {total_deleted}") + +if __name__ == "__main__": + cleanup_hosts() \ No newline at end of file diff --git a/zabbix-tests/host-creator.py b/zabbix-tests/host-creator.py new file mode 100755 index 0000000..cb0542e --- /dev/null +++ b/zabbix-tests/host-creator.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +import requests +import json +import time + +# === CONFIGURATION === +ZABBIX_URL = "http://10.0.0.101:8887/api_jsonrpc.php" +ZABBIX_TOKEN = "c785634354e760a6843055ba4581bc7b6cd6eb2ec75f7c2a79f251c1719933f7" +PROXY_GROUP_ID = "1" # your proxy group ID +GROUP_ID = "19" # host group for these hosts +NUM_HOSTS = 1000 # number of hosts to create +BATCH_SIZE = 100 # how many to create per call + +HEADERS = { + "Content-Type": "application/json-rpc", + "Authorization": f"Bearer {ZABBIX_TOKEN}" +} + +def zbx_request(method, params): + """Send Zabbix API request using Bearer token authentication.""" + payload = { + "jsonrpc": "2.0", + "method": method, + "params": params, + "id": int(time.time()) + } + r = requests.post(ZABBIX_URL, headers=HEADERS, data=json.dumps(payload)) + r.raise_for_status() + resp = r.json() + if "error" in resp: + raise Exception(f"Zabbix API error: {resp['error']}") + return resp + +def create_hosts(): + hosts = [] + for i in range(1, NUM_HOSTS + 1): + host_name = f"dummy-host-{i:04d}" + host = { + "host": host_name, + "groups": [{"groupid": GROUP_ID}], + "templates": [{"templateid": "10048"}], # assign Proxy Health template + "monitored_by": 2, # 2 = proxy group + "proxy_groupid": PROXY_GROUP_ID # your proxy group ID + } + hosts.append(host) + + for i in range(0, len(hosts), BATCH_SIZE): + batch = hosts[i:i + BATCH_SIZE] + print(f"Creating hosts {i+1}-{i+len(batch)}...") + try: + resp = zbx_request("host.create", batch) + created = len(resp.get("result", {}).get("hostids", [])) + print(f"Created {created} hosts.") + except Exception as e: + print(f"Error in batch {i+1}-{i+len(batch)}: {e}") + time.sleep(1) + +if __name__ == "__main__": + try: + create_hosts() + except Exception as e: + print(f"Fatal error: {e}") diff --git a/zabbix-tests/server-docker/.env b/zabbix-tests/server-docker/.env new file mode 100644 index 0000000..c6bb812 --- /dev/null +++ b/zabbix-tests/server-docker/.env @@ -0,0 +1,28 @@ +# Database Configuration +MYSQL_DATABASE=zabbix +MYSQL_USER=zabbix +MYSQL_PASSWORD=strong-password +MYSQL_ROOT_PASSWORD=very-strong-password + +# Image Versions (uncomment to override defaults) +ZABBIX_VERSION=latest +# MYSQL_VERSION=8.4.0-oraclelinux8 # Keep oraclelinux variant for architecture compatibility + +# Port Configuration +ZABBIX_SERVER_PORT=10051 +ZABBIX_WEB_PORT=8887 + +# Server Settings +PHP_TIMEZONE=Europe/Warsaw +ZBX_STARTSNMPTRAPPER=1 +ZBX_SNMPTRAPPERFILE=/tmp/traps.log +ZBX_CACHESIZE=128M +ZBX_VALUECACHESIZE=64M +ZBX_TRENDCACHESIZE=32M + +# Common Proxy Settings (applied to all proxies) +PROXY_CACHE_SIZE=128M +PROXY_HISTORY_CACHE_SIZE=32M +PROXY_HISTORY_INDEX_CACHE_SIZE=16M +PROXY_BUFFER_MODE=hybrid +PROXY_MEMORY_BUFFER_SIZE=64M \ No newline at end of file diff --git a/zabbix-tests/server-docker/docker-compose.yaml b/zabbix-tests/server-docker/docker-compose.yaml new file mode 100644 index 0000000..905d050 --- /dev/null +++ b/zabbix-tests/server-docker/docker-compose.yaml @@ -0,0 +1,224 @@ +### This will create a Zabbix server with six active proxies and a MySQL database. +### You need to configure proxies in Zabbix frontend after deployment. +### Adjust .env file for customization. + +services: + mysql-server: + image: mysql:${MYSQL_VERSION:-8.4.0-oraclelinux8} + container_name: zabbix-mysql-server + command: + - 'mysqld' + - '--character-set-server=utf8mb4' + - '--collation-server=utf8mb4_bin' + - '--log-bin-trust-function-creators=1' + environment: + - MYSQL_DATABASE=${MYSQL_DATABASE} + - MYSQL_USER=${MYSQL_USER} + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + volumes: + - ./zabbix/mysql:/var/lib/mysql + restart: unless-stopped + networks: + - zabbix-net + + zabbix-server: + image: zabbix/zabbix-server-mysql:${ZABBIX_VERSION:-latest} + container_name: zabbix-server + depends_on: + - mysql-server + ports: + - "${ZABBIX_SERVER_PORT}:10051" + environment: + - DB_SERVER_HOST=mysql-server + - MYSQL_DATABASE=${MYSQL_DATABASE} + - MYSQL_USER=${MYSQL_USER} + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + - ZBX_STARTSNMPTRAPPER=${ZBX_STARTSNMPTRAPPER} + - ZBX_SNMPTRAPPERFILE=${ZBX_SNMPTRAPPERFILE} + - ZBX_CACHESIZE=${ZBX_CACHESIZE} + - ZBX_VALUECACHESIZE=${ZBX_VALUECACHESIZE} + - ZBX_TRENDCACHESIZE=${ZBX_TRENDCACHESIZE} + volumes: + - ./zabbix/alertscripts:/usr/lib/zabbix/alertscripts + - ./zabbix/externalscripts:/usr/lib/zabbix/externalscripts + restart: unless-stopped + networks: + - zabbix-net + + zabbix-web: + image: zabbix/zabbix-web-nginx-mysql:${ZABBIX_VERSION:-latest} + container_name: zabbix-web + depends_on: + - mysql-server + - zabbix-server + ports: + - "${ZABBIX_WEB_PORT}:8080" + environment: + - DB_SERVER_HOST=mysql-server + - MYSQL_DATABASE=${MYSQL_DATABASE} + - MYSQL_USER=${MYSQL_USER} + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + - ZBX_SERVER_HOST=zabbix-server + - PHP_TZ=${PHP_TIMEZONE} + restart: unless-stopped + networks: + - zabbix-net + + zabbix-proxy-active-01: + image: zabbix/zabbix-proxy-sqlite3:${ZABBIX_VERSION:-latest} + container_name: zabbix-proxy-active-01 + depends_on: + - zabbix-server + ports: + - "10101:10051" + environment: + - ZBX_HOSTNAME=zabbix-proxy-active-01 + - ZBX_SERVER_HOST=zabbix-server + - ZBX_PROXYMODE=0 + - ZBX_CACHESIZE=${PROXY_CACHE_SIZE} + - ZBX_HISTORYCACHESIZE=${PROXY_HISTORY_CACHE_SIZE} + - ZBX_HISTORYINDEXCACHESIZE=${PROXY_HISTORY_INDEX_CACHE_SIZE} + - ZBX_PROXYBUFFERMODE=${PROXY_BUFFER_MODE} + - ZBX_PROXYMEMORYBUFFERSIZE=${PROXY_MEMORY_BUFFER_SIZE} + volumes: + - ./zabbix/proxy-01:/var/lib/zabbix/db_data:rw + restart: unless-stopped + networks: + - zabbix-net + + zabbix-proxy-active-02: + image: zabbix/zabbix-proxy-sqlite3:${ZABBIX_VERSION:-latest} + container_name: zabbix-proxy-active-02 + depends_on: + - zabbix-server + ports: + - "10102:10051" + environment: + - ZBX_HOSTNAME=zabbix-proxy-active-02 + - ZBX_SERVER_HOST=zabbix-server + - ZBX_PROXYMODE=0 + - ZBX_CACHESIZE=${PROXY_CACHE_SIZE} + - ZBX_HISTORYCACHESIZE=${PROXY_HISTORY_CACHE_SIZE} + - ZBX_HISTORYINDEXCACHESIZE=${PROXY_HISTORY_INDEX_CACHE_SIZE} + - ZBX_PROXYBUFFERMODE=${PROXY_BUFFER_MODE} + - ZBX_PROXYMEMORYBUFFERSIZE=${PROXY_MEMORY_BUFFER_SIZE} + volumes: + - ./zabbix/proxy-02:/var/lib/zabbix/db_data:rw + restart: unless-stopped + networks: + - zabbix-net + + zabbix-proxy-active-03: + image: zabbix/zabbix-proxy-sqlite3:${ZABBIX_VERSION:-latest} + container_name: zabbix-proxy-active-03 + depends_on: + - zabbix-server + ports: + - "10103:10051" + environment: + - ZBX_HOSTNAME=zabbix-proxy-active-03 + - ZBX_SERVER_HOST=zabbix-server + - ZBX_PROXYMODE=0 + - ZBX_CACHESIZE=${PROXY_CACHE_SIZE} + - ZBX_HISTORYCACHESIZE=${PROXY_HISTORY_CACHE_SIZE} + - ZBX_HISTORYINDEXCACHESIZE=${PROXY_HISTORY_INDEX_CACHE_SIZE} + - ZBX_PROXYBUFFERMODE=${PROXY_BUFFER_MODE} + - ZBX_PROXYMEMORYBUFFERSIZE=${PROXY_MEMORY_BUFFER_SIZE} + volumes: + - ./zabbix/proxy-03:/var/lib/zabbix/db_data:rw + restart: unless-stopped + networks: + - zabbix-net + + zabbix-proxy-active-04: + image: zabbix/zabbix-proxy-sqlite3:${ZABBIX_VERSION:-latest} + container_name: zabbix-proxy-active-04 + depends_on: + - zabbix-server + ports: + - "10104:10051" + environment: + - ZBX_HOSTNAME=zabbix-proxy-active-04 + - ZBX_SERVER_HOST=zabbix-server + - ZBX_PROXYMODE=0 + - ZBX_CACHESIZE=${PROXY_CACHE_SIZE} + - ZBX_HISTORYCACHESIZE=${PROXY_HISTORY_CACHE_SIZE} + - ZBX_HISTORYINDEXCACHESIZE=${PROXY_HISTORY_INDEX_CACHE_SIZE} + - ZBX_PROXYBUFFERMODE=${PROXY_BUFFER_MODE} + - ZBX_PROXYMEMORYBUFFERSIZE=${PROXY_MEMORY_BUFFER_SIZE} + volumes: + - ./zabbix/proxy-04:/var/lib/zabbix/db_data:rw + restart: unless-stopped + networks: + - zabbix-net + + zabbix-proxy-active-05: + image: zabbix/zabbix-proxy-sqlite3:${ZABBIX_VERSION:-latest} + container_name: zabbix-proxy-active-05 + depends_on: + - zabbix-server + ports: + - "10105:10051" + environment: + - ZBX_HOSTNAME=zabbix-proxy-active-05 + - ZBX_SERVER_HOST=zabbix-server + - ZBX_PROXYMODE=0 + - ZBX_CACHESIZE=${PROXY_CACHE_SIZE} + - ZBX_HISTORYCACHESIZE=${PROXY_HISTORY_CACHE_SIZE} + - ZBX_HISTORYINDEXCACHESIZE=${PROXY_HISTORY_INDEX_CACHE_SIZE} + - ZBX_PROXYBUFFERMODE=${PROXY_BUFFER_MODE} + - ZBX_PROXYMEMORYBUFFERSIZE=${PROXY_MEMORY_BUFFER_SIZE} + volumes: + - ./zabbix/proxy-05:/var/lib/zabbix/db_data:rw + restart: unless-stopped + networks: + - zabbix-net + + zabbix-proxy-active-06: + image: zabbix/zabbix-proxy-sqlite3:${ZABBIX_VERSION:-latest} + container_name: zabbix-proxy-active-06 + depends_on: + - zabbix-server + ports: + - "10106:10051" + environment: + - ZBX_HOSTNAME=zabbix-proxy-active-06 + - ZBX_SERVER_HOST=zabbix-server + - ZBX_PROXYMODE=0 + - ZBX_CACHESIZE=${PROXY_CACHE_SIZE} + - ZBX_HISTORYCACHESIZE=${PROXY_HISTORY_CACHE_SIZE} + - ZBX_HISTORYINDEXCACHESIZE=${PROXY_HISTORY_INDEX_CACHE_SIZE} + - ZBX_PROXYBUFFERMODE=${PROXY_BUFFER_MODE} + - ZBX_PROXYMEMORYBUFFERSIZE=${PROXY_MEMORY_BUFFER_SIZE} + volumes: + - ./zabbix/proxy-06:/var/lib/zabbix/db_data:rw + restart: unless-stopped + networks: + - zabbix-net + + # zabbix-proxy-passive-03: + # image: zabbix/zabbix-proxy-sqlite3:${ZABBIX_VERSION:-latest} + # container_name: zabbix-proxy-passive-03 + # depends_on: + # - zabbix-server + # ports: + # - "10103:10051" + # environment: + # - ZBX_HOSTNAME=zabbix-proxy-passive-03 + # - ZBX_SERVER_HOST=zabbix-server + # - ZBX_PROXYMODE=1 + # - ZBX_CACHESIZE=${PROXY_CACHE_SIZE} + # - ZBX_HISTORYCACHESIZE=${PROXY_HISTORY_CACHE_SIZE} + # - ZBX_HISTORYINDEXCACHESIZE=${PROXY_HISTORY_INDEX_CACHE_SIZE} + # - ZBX_PROXYBUFFERMODE=${PROXY_BUFFER_MODE} + # - ZBX_PROXYMEMORYBUFFERSIZE=${PROXY_MEMORY_BUFFER_SIZE} + # volumes: + # - ./zabbix/proxy-03:/var/lib/zabbix/db_data:rw + # restart: unless-stopped + # networks: + # - zabbix-net + +networks: + zabbix-net: + driver: bridge