test: benchmark of server, BIND and Kea with generated data
bench / Benchmark (server, BIND, Kea) (push) Canceled after 1m30s
bench / Benchmark (server, BIND, Kea) (push) Canceled after 1m30s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7c223161a1
commit
43bf44a8ca
@@ -0,0 +1,36 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Benchmarks of server, BIND and Kea (test/bench, docs/SYSTEM-REQUIREMENTS.md).
|
||||
# Started by hand; the results are printed as a Markdown table in the log
|
||||
# (and in the job summary where the runner supports it).
|
||||
name: bench
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [m6-bench]
|
||||
|
||||
concurrency:
|
||||
group: bench-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
bench:
|
||||
name: Benchmark (server, BIND, Kea)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
# The Docker daemon may be shared: unique compose project.
|
||||
COMPOSE_PROJECT_NAME: ddi-bench-${{ github.run_id }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: false
|
||||
- name: Tools
|
||||
run: command -v jq >/dev/null || { apt-get update -qq && apt-get install -y -qq --no-install-recommends jq; }
|
||||
- name: Build
|
||||
run: make build-go
|
||||
- name: Benchmark
|
||||
run: make bench
|
||||
@@ -150,6 +150,11 @@ test-integration:
|
||||
go test -tags integration -count=1 -v -timeout 20m \
|
||||
./server/internal/configgen/integration/ ./agent/role/integration/
|
||||
|
||||
## bench: measure server, BIND and Kea with generated data (needs docker and bin/ from build-go)
|
||||
.PHONY: bench
|
||||
bench:
|
||||
test/bench/run.sh
|
||||
|
||||
## vuln: scan dependencies for known vulnerabilities
|
||||
.PHONY: vuln
|
||||
vuln:
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# BIND 9.20 for the benchmark. named is started by run.sh once the generated
|
||||
# files are in place.
|
||||
FROM internetsystemsconsortium/bind9:9.20
|
||||
COPY --chmod=0644 bind/named.conf /etc/bind/named.conf
|
||||
ENTRYPOINT ["tail", "-f", "/dev/null"]
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Distribution-like named.conf for the benchmark: the three include files
|
||||
// written by the DNS agent (keys before zones), as set up by the installer.
|
||||
|
||||
options {
|
||||
directory "/var/cache/bind";
|
||||
pid-file "/run/named/named.pid";
|
||||
listen-on { any; };
|
||||
listen-on-v6 { none; };
|
||||
dnssec-validation no;
|
||||
include "/etc/bind/ddi-suite/ddi-suite-options.conf";
|
||||
};
|
||||
|
||||
include "/etc/bind/ddi-suite/ddi-suite-keys.conf";
|
||||
include "/etc/bind/ddi-suite/ddi-suite.conf";
|
||||
@@ -0,0 +1,76 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Benchmark environment: management server with PostgreSQL 17, BIND 9.20
|
||||
# with dnsperf, Kea 3.2.1 with perfdhcp. Driven by test/bench/run.sh through
|
||||
# `docker compose exec` and `docker cp`, so it works with a remote or shared
|
||||
# Docker daemon: no bind mounts, no published ports, no fixed subnet.
|
||||
#
|
||||
# TEST ONLY. Passwords and keys are throwaway values.
|
||||
|
||||
name: ddi-bench
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:17.11-alpine
|
||||
environment:
|
||||
POSTGRES_USER: ddi
|
||||
POSTGRES_DB: ddi
|
||||
POSTGRES_PASSWORD: bench-only
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ddi -d ddi"]
|
||||
interval: 2s
|
||||
timeout: 3s
|
||||
retries: 30
|
||||
|
||||
server:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: server.Dockerfile
|
||||
environment:
|
||||
DDI_DATABASE_URL: postgres://ddi:bench-only@postgres:5432/ddi?sslmode=disable
|
||||
DDI_HTTP_LISTEN: 127.0.0.1:8080
|
||||
DDI_MASTER_KEY_FILE: /var/lib/ddi/master.key
|
||||
DDI_STATE_DIR: /var/lib/ddi/state
|
||||
DDI_AGENT_API_LISTEN: 0.0.0.0:8444
|
||||
DDI_AGENT_API_PUBLIC_ADDRESS: server:8444
|
||||
DDI_AGENT_API_SERVER_NAMES: server
|
||||
DDI_LOG_LEVEL: warn
|
||||
# Unreachable license server: the server runs in trial mode.
|
||||
KEYGEN_URL: http://127.0.0.1:1
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:8080/readyz | grep -q '\"database\":\"ok\"'"]
|
||||
interval: 2s
|
||||
timeout: 3s
|
||||
retries: 60
|
||||
|
||||
bind:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: bind.Dockerfile
|
||||
user: "0:0"
|
||||
init: true
|
||||
|
||||
dnsperf:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: dnsperf.Dockerfile
|
||||
init: true
|
||||
|
||||
kea:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: kea.Dockerfile
|
||||
user: "0:0"
|
||||
init: true
|
||||
environment:
|
||||
KEA_CONTROL_SOCKET_DIR: /run/kea
|
||||
|
||||
perfdhcp:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: perfdhcp.Dockerfile
|
||||
user: "0:0"
|
||||
init: true
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Generates the synthetic data set of the benchmark (test/bench/run.sh).
|
||||
#
|
||||
# Network k (0 <= k < 64) is 10.a.b.0/21 with a = k / 32, b = (k % 32) * 8,
|
||||
# gateway 10.a.b.1 and a DHCP pool over the upper half (10.a.(b+4).1 to
|
||||
# 10.a.(b+7).254). Each network holds 1,000 address entries in its lower
|
||||
# half: 70 % static, 20 % manual_dhcp (with MAC), 10 % reserved. All host
|
||||
# names live in the zone bench.test, so 1,000 entries yield 900 A and 900
|
||||
# PTR records.
|
||||
#
|
||||
# Usage: datagen.sh <from-k> <to-k> <out-dir>
|
||||
# writes <out-dir>/networks.txt "cidr gateway pool-start pool-end name"
|
||||
# <out-dir>/addresses-NN.csv import files of at most 5,000 rows
|
||||
# <out-dir>/queries.txt dnsperf input for networks 0 .. to-k-1
|
||||
# (A and PTR of existing names, 10 % NXDOMAIN)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
from="$1" to="$2" out="$3"
|
||||
mkdir -p "$out"
|
||||
rm -f "$out"/addresses-*.csv
|
||||
|
||||
awk -v from="$from" -v to="$to" -v out="$out" '
|
||||
function ip(k, o, a, b) {
|
||||
a = int(k / 32); b = (k % 32) * 8
|
||||
return sprintf("10.%d.%d.%d", a, b + int(o / 256), o % 256)
|
||||
}
|
||||
BEGIN {
|
||||
nets = out "/networks.txt"
|
||||
printf "" > nets
|
||||
rows = 0; chunk = -1
|
||||
for (k = from; k < to; k++) {
|
||||
a = int(k / 32); b = (k % 32) * 8
|
||||
printf "10.%d.%d.0/21 10.%d.%d.1 10.%d.%d.1 10.%d.%d.254 bench-%02d\n", \
|
||||
a, b, a, b, a, b + 4, a, b + 7, k >> nets
|
||||
for (i = 0; i < 1000; i++) {
|
||||
if (rows % 5000 == 0) {
|
||||
if (chunk >= 0) close(file)
|
||||
chunk++
|
||||
file = sprintf("%s/addresses-%02d.csv", out, chunk)
|
||||
print "ip,type,mac,hostname,description,owner,tags" > file
|
||||
}
|
||||
rows++
|
||||
m = i % 10
|
||||
addr = ip(k, i + 2)
|
||||
if (m <= 6) {
|
||||
printf "%s,static,,h%d-%d,bench host,bench,bench\n", addr, k, i > file
|
||||
} else if (m <= 8) {
|
||||
printf "%s,manual_dhcp,02:bb:00:%02x:%02x:%02x,h%d-%d,bench reservation,bench,bench;dhcp\n", \
|
||||
addr, k, int(i / 256), i % 256, k, i > file
|
||||
} else {
|
||||
printf "%s,reserved,,,bench reserved,bench,bench\n", addr > file
|
||||
}
|
||||
}
|
||||
}
|
||||
close(file)
|
||||
}'
|
||||
|
||||
# Queries in pseudo-random order (fixed seed), over all networks so far.
|
||||
awk -v to="$to" '
|
||||
BEGIN {
|
||||
srand(42)
|
||||
for (k = 0; k < to; k++) {
|
||||
a = int(k / 32); b = (k % 32) * 8
|
||||
for (i = 0; i < 1000; i++) {
|
||||
if (i % 10 == 9) continue
|
||||
o = i + 2
|
||||
printf "%.8f h%d-%d.bench.test A\n", rand(), k, i
|
||||
printf "%.8f %d.%d.%d.10.in-addr.arpa PTR\n", rand(), o % 256, b + int(o / 256), a
|
||||
}
|
||||
}
|
||||
n = to * 1800 / 9
|
||||
for (j = 0; j < n; j++) printf "%.8f nx%d.bench.test A\n", rand(), j
|
||||
}' | sort -k1,1 | cut -d' ' -f2- >"$out/queries.txt"
|
||||
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Load generator for BIND (dnsperf from Debian).
|
||||
FROM debian:trixie-slim
|
||||
RUN apt-get update -qq \
|
||||
&& apt-get install -y -qq --no-install-recommends dnsperf \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
ENTRYPOINT ["tail", "-f", "/dev/null"]
|
||||
@@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Kea DHCPv4 3.2.1 for the benchmark (same image as test/e2e). kea-dhcp4 is
|
||||
# started by run.sh with the generated configuration.
|
||||
FROM docker.cloudsmith.io/isc/docker/kea-dhcp4:3.2.1
|
||||
ENTRYPOINT ["tail", "-f", "/dev/null"]
|
||||
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Load generator for Kea: perfdhcp from the ISC package repository that the
|
||||
# Kea image is built from.
|
||||
FROM docker.cloudsmith.io/isc/docker/kea-dhcp4:3.2.1
|
||||
USER root
|
||||
RUN apk add --no-cache isc-kea-perfdhcp || { cat /etc/apk/repositories; apk search isc-kea; exit 1; }
|
||||
ENTRYPOINT ["tail", "-f", "/dev/null"]
|
||||
Executable
+421
@@ -0,0 +1,421 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Benchmark of the management server, BIND and Kea with the configuration
|
||||
# our generator produces (docs/SYSTEM-REQUIREMENTS.md). Two data sets: about
|
||||
# 5,000 and about 50,000 address entries (datagen.sh). For each:
|
||||
#
|
||||
# server CSV import through the API, server and PostgreSQL memory, database
|
||||
# size, revision creation, config preview, GET /addresses latency
|
||||
# with filters, latency of single changes, audit and revision growth
|
||||
# BIND the generated zones in BIND 9.20: load time, memory, dnsperf
|
||||
# throughput and latency
|
||||
# Kea the generated kea-dhcp4.conf in Kea 3.2.1: start time, memory,
|
||||
# perfdhcp DORA rate against a /16 pool at increasing rates, CPU,
|
||||
# lease file growth
|
||||
#
|
||||
# Prints a Markdown report (also appended to $GITHUB_STEP_SUMMARY if set).
|
||||
# Needs Docker with compose and bin/ddi-server from `make build-go`.
|
||||
#
|
||||
# Usage: test/bench/run.sh (make bench)
|
||||
# Environment: BIN_DIR, KEEP=1 keeps the containers, BENCH_OUT (report dir,
|
||||
# default a temporary directory), BENCH_SCALES (default "5000 50000").
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
here="$(cd "$(dirname "$0")" && pwd)"
|
||||
root="$(cd "$here/../.." && pwd)"
|
||||
bin_dir="${BIN_DIR:-$root/bin}"
|
||||
case "$bin_dir" in /*) ;; *) bin_dir="$root/$bin_dir" ;; esac
|
||||
work="$(mktemp -d)"
|
||||
out="${BENCH_OUT:-$work/out}"
|
||||
mkdir -p "$out"
|
||||
res="$out/results.txt"
|
||||
: >"$res"
|
||||
read -r -a scales <<<"${BENCH_SCALES:-5000 50000}"
|
||||
rates=(500 1000 2000 4000 8000)
|
||||
|
||||
dc() { docker compose -f "$here/compose.yaml" "$@"; }
|
||||
bench() { dc exec -T server /bench/bench.sh "$@"; }
|
||||
cid() { dc ps -q "$1"; }
|
||||
pg() { dc exec -T postgres psql -U ddi -d ddi -Atc "$1"; }
|
||||
step() { printf '\n=== %s\n' "$*" >&2; }
|
||||
fail() {
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
now_ms() { echo $(($(date +%s%N) / 1000000)); }
|
||||
|
||||
# put KEY VALUE records a result; get KEY prints it ("–" if missing).
|
||||
put() {
|
||||
printf '%s %s\n' "$1" "$2" >>"$res"
|
||||
printf ' %-40s %s\n' "$1" "$2" >&2
|
||||
}
|
||||
get() {
|
||||
local v
|
||||
v="$(awk -v k="$1" '$1 == k { $1 = ""; sub(/^ /, ""); v = $0 } END { print v }' "$res")"
|
||||
printf '%s' "${v:-–}"
|
||||
}
|
||||
mib() { awk -v b="$1" 'BEGIN { printf "%.1f", b / 1048576 }'; }
|
||||
kib_to_mib() { awk -v k="$1" 'BEGIN { printf "%.1f", k / 1024 }'; }
|
||||
|
||||
# rss CONTAINER PROCESS prints "VmRSS VmHWM" of the process in MiB.
|
||||
rss() {
|
||||
# shellcheck disable=SC2016 # expanded in the container
|
||||
dc exec -T "$1" sh -c 'p=$(pidof '"$2"' | cut -d" " -f1); grep -E "^Vm(RSS|HWM):" /proc/$p/status' |
|
||||
awk '/VmRSS/ { r = $2 } /VmHWM/ { h = $2 } END { printf "%.1f %.1f\n", r / 1024, h / 1024 }'
|
||||
}
|
||||
# ticks CONTAINER PROCESS prints the CPU time (user + system) in clock ticks.
|
||||
ticks() {
|
||||
# shellcheck disable=SC2016 # expanded in the container
|
||||
dc exec -T "$1" sh -c 'p=$(pidof '"$2"' | cut -d" " -f1); cat /proc/$p/stat' |
|
||||
awk '{ sub(/^.*\) /, ""); print $12 + $13 }'
|
||||
}
|
||||
# cmem CONTAINER prints the memory use of a container (docker stats).
|
||||
cmem() { docker stats --no-stream --format '{{.MemUsage}}' "$(cid "$1")" | awk '{ print $1 }'; }
|
||||
|
||||
report() {
|
||||
local s
|
||||
{
|
||||
echo "## Benchmark DDI-Suite"
|
||||
echo
|
||||
echo "Commit \`${GITHUB_SHA:-$(git -C "$root" rev-parse --short HEAD 2>/dev/null || echo unknown)}\`, $(date -u +%Y-%m-%dT%H:%MZ)."
|
||||
echo
|
||||
echo "### Hardware (Docker host of the runner)"
|
||||
echo
|
||||
echo "| | |"
|
||||
echo "| --- | --- |"
|
||||
echo "| CPU | $(get hw.cpu) |"
|
||||
echo "| Cores (nproc) | $(get hw.ncpu) |"
|
||||
echo "| RAM | $(get hw.mem) |"
|
||||
echo "| Kernel / Docker | $(get hw.kernel) / $(get hw.docker) |"
|
||||
echo
|
||||
echo "### Management server (PostgreSQL 17.11, defaults of the official image)"
|
||||
echo
|
||||
printf '| Measurement |'
|
||||
for s in "${scales[@]}"; do printf ' %s entries |' "$s"; done
|
||||
printf '\n| --- |'
|
||||
for _ in "${scales[@]}"; do printf ' ---: |'; done
|
||||
printf '\n'
|
||||
row() {
|
||||
local label="$1" key="$2"
|
||||
printf '| %s |' "$label"
|
||||
for s in "${scales[@]}"; do printf ' %s |' "$(get "s$s.$key")"; done
|
||||
printf '\n'
|
||||
}
|
||||
row "Networks / address entries" nets_entries
|
||||
row "CSV import through the API (s, total)" import_s
|
||||
row "CSV import rate (entries/s)" import_rate
|
||||
row "Server RSS after import / peak (MiB)" server_rss
|
||||
row "PostgreSQL container memory" pg_mem
|
||||
row "Database size after import (MiB)" db_mib
|
||||
row "Table addresses incl. indexes (MiB)" addresses_mib
|
||||
row "Audit log: entries / size (MiB)" audit
|
||||
row "POST /revisions, first revision (s)" rev1_s
|
||||
row "Storage per revision (MiB)" rev1_mib
|
||||
row "Generated files: DNS files / total size (MiB)" files
|
||||
row "GET /config/preview (s)" preview_s
|
||||
row "GET /config/pending (s)" pending_s
|
||||
row "GET /addresses?limit=100 (median / p95 ms)" lat_all
|
||||
row "… &network_id=<one network> (ms)" lat_net
|
||||
row "… &type=manual_dhcp (ms)" lat_type
|
||||
row "… &q=<host name prefix> (ms)" lat_host
|
||||
row "… &q=<MAC prefix> (ms)" lat_mac
|
||||
row "… sort=hostname desc, last page (ms)" lat_deep
|
||||
row "PUT /addresses/{id}, mean of 50 (ms)" upd_ms
|
||||
row "Audit growth per change (bytes)" audit_per_change
|
||||
row "POST /revisions after 50 changes (s)" rev2_s
|
||||
row "Storage of that revision (MiB)" rev2_mib
|
||||
row "Server RSS at the end / peak (MiB)" server_rss_end
|
||||
echo
|
||||
echo "### BIND 9.20 (files from GET /config/preview)"
|
||||
echo
|
||||
printf '| Measurement |'
|
||||
for s in "${scales[@]}"; do printf ' %s entries |' "$s"; done
|
||||
printf '\n| --- |'
|
||||
for _ in "${scales[@]}"; do printf ' ---: |'; done
|
||||
printf '\n'
|
||||
row "Zones / A+PTR records" bind_zones
|
||||
row "Zone files (MiB)" bind_files_mib
|
||||
row "Start until all zones loaded (s)" bind_load_s
|
||||
row "named RSS after loading (MiB)" bind_rss
|
||||
row "dnsperf max: queries/s" bind_qps
|
||||
row "dnsperf max: mean latency (ms) / lost" bind_lat
|
||||
row "named CPU during max run (% of one core)" bind_cpu
|
||||
row "dnsperf at 5,000 queries/s: mean latency (ms)" bind_lat5k
|
||||
row "named RSS after the runs (MiB)" bind_rss_end
|
||||
echo
|
||||
echo "### Kea 3.2.1 (kea-dhcp4.conf from GET /config/preview, /16 pool 10.200.0.0/16)"
|
||||
echo
|
||||
printf '| Measurement |'
|
||||
for s in "${scales[@]}"; do printf ' %s entries |' "$s"; done
|
||||
printf '\n| --- |'
|
||||
for _ in "${scales[@]}"; do printf ' ---: |'; done
|
||||
printf '\n'
|
||||
row "Subnets / reservations" kea_size
|
||||
row "kea-dhcp4 -t (s)" kea_test_s
|
||||
row "Start until DHCP4_STARTED (s)" kea_start_s
|
||||
row "kea-dhcp4 RSS after start (MiB)" kea_rss
|
||||
for r in "${rates[@]}"; do
|
||||
row "perfdhcp $r/s: DORA/s, drops D-O / R-A" "kea_$r"
|
||||
row "perfdhcp $r/s: mean delay R-A (ms), CPU %, RSS (MiB), lease file (MiB)" "kea_${r}_res"
|
||||
done
|
||||
echo
|
||||
echo "Method: test/bench/run.sh. Each perfdhcp run lasts 10 s with 50,000 simulated clients (later runs renew"
|
||||
echo "existing leases); dnsperf runs 15 s with 16 clients and up to 500 outstanding queries (A and PTR of existing"
|
||||
echo "names, 10 % NXDOMAIN). Client and server share the Docker host; the daemon may be shared with other jobs."
|
||||
} >"$out/report.md"
|
||||
cat "$out/report.md"
|
||||
if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then
|
||||
cat "$out/report.md" >>"$GITHUB_STEP_SUMMARY" || true
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
local rc=$?
|
||||
if [[ $rc -ne 0 ]]; then
|
||||
echo "FAILED (exit $rc); logs follow" >&2
|
||||
dc logs --no-color --tail 100 >&2 || true
|
||||
dc exec -T bind sh -c 'tail -n 50 /tmp/named.log 2>/dev/null' >&2 || true
|
||||
dc exec -T kea sh -c 'tail -n 50 /tmp/kea.log 2>/dev/null' >&2 || true
|
||||
fi
|
||||
report || true
|
||||
if [[ "${KEEP:-}" != 1 ]]; then
|
||||
dc down -v --remove-orphans >/dev/null 2>&1 || true
|
||||
fi
|
||||
rm -rf "${here:?}/bin" "$work/s"* "$work/cfg"*
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
step "Build images"
|
||||
mkdir -p "$here/bin"
|
||||
cp "$bin_dir/ddi-server" "$here/bin/ddi-server"
|
||||
for i in 1 2 3; do
|
||||
dc build --pull && break
|
||||
[[ $i == 3 ]] && fail "image build failed"
|
||||
sleep 15
|
||||
done
|
||||
dc up -d --wait
|
||||
|
||||
step "Hardware"
|
||||
put hw.ncpu "$(docker info --format '{{.NCPU}}')"
|
||||
put hw.mem "$(docker info --format '{{.MemTotal}}' | awk '{ printf "%.1f GiB", $1 / 1073741824 }')"
|
||||
put hw.kernel "$(docker info --format '{{.KernelVersion}}')"
|
||||
put hw.docker "$(docker info --format '{{.ServerVersion}}')"
|
||||
put hw.cpu "$(dc exec -T server sh -c "grep -m1 'model name' /proc/cpuinfo | cut -d: -f2" | sed 's/^ *//')"
|
||||
|
||||
step "Set up the server"
|
||||
password="$(dc exec -T server ddi-server bootstrap-admin | sed -n 's/^password: //p')"
|
||||
[[ -n "$password" ]] || fail "bootstrap-admin printed no password"
|
||||
bench setup "$password"
|
||||
bind_ip="$(dc exec -T bind hostname -i | awk '{ print $1 }')"
|
||||
kea_ip="$(dc exec -T kea hostname -i | awk '{ print $1 }')"
|
||||
relay_ip="$(dc exec -T perfdhcp hostname -i | awk '{ print $1 }')"
|
||||
echo "bind $bind_ip, kea $kea_ip, perfdhcp (relay) $relay_ip" >&2
|
||||
|
||||
# ---------------------------------------------------------------- server
|
||||
|
||||
server_bench() {
|
||||
local s="$1" from="$2" to="$3" data="$work/s$1" t=0 n=0 f r sec created
|
||||
step "Server: data set $s"
|
||||
"$here/datagen.sh" "$from" "$to" "$data"
|
||||
dc exec -T server mkdir -p "/bench/data-$s"
|
||||
docker cp "$data/." "$(cid server):/bench/data-$s/"
|
||||
bench networks "/bench/data-$s/networks.txt" >/dev/null
|
||||
for f in "$data"/addresses-*.csv; do
|
||||
_o="$(bench import "/bench/data-$s/$(basename "$f")")"
|
||||
read -r sec created <<<"$_o"
|
||||
echo " import $(basename "$f"): $created in $sec s" >&2
|
||||
t="$(awk -v a="$t" -v b="$sec" 'BEGIN { print a + b }')"
|
||||
n=$((n + created))
|
||||
done
|
||||
local entries
|
||||
entries="$(pg "select count(*) from addresses")"
|
||||
put "s$s.nets_entries" "$(pg "select count(*) from networks") / $entries"
|
||||
put "s$s.import_s" "$(awk -v t="$t" 'BEGIN { printf "%.1f", t }') ($n new)"
|
||||
put "s$s.import_rate" "$(awk -v t="$t" -v n="$n" 'BEGIN { printf "%.0f", n / t }')"
|
||||
_o="$(rss server ddi-server)"
|
||||
read -r r h <<<"$_o"
|
||||
put "s$s.server_rss" "$r / $h"
|
||||
put "s$s.pg_mem" "$(cmem postgres)"
|
||||
put "s$s.db_mib" "$(mib "$(pg "select pg_database_size('ddi')")")"
|
||||
put "s$s.addresses_mib" "$(mib "$(pg "select pg_total_relation_size('addresses')")")"
|
||||
put "s$s.audit" "$(pg "select count(*) from audit_log") / $(mib "$(pg "select pg_total_relation_size('audit_log')")")"
|
||||
|
||||
local revsize="select pg_total_relation_size('config_revisions') + pg_total_relation_size('config_revision_files')"
|
||||
local before after num bytes
|
||||
before="$(pg "$revsize")"
|
||||
_o="$(bench revision "bench $s")"
|
||||
read -r num sec bytes <<<"$_o"
|
||||
after="$(pg "$revsize")"
|
||||
put "s$s.rev1_s" "$sec"
|
||||
put "s$s.rev1_mib" "$(mib $((after - before)))"
|
||||
echo " revision $num, response $bytes bytes" >&2
|
||||
|
||||
local files
|
||||
_o="$(bench preview "/tmp/cfg-$s" "$relay_ip")"
|
||||
read -r sec bytes files <<<"$_o"
|
||||
put "s$s.preview_s" "$sec"
|
||||
docker cp "$(cid server):/tmp/cfg-$s" "$work/cfg-$s"
|
||||
put "s$s.files" "$files / $(mib "$(du -sb "$work/cfg-$s" | awk '{ print $1 }')")"
|
||||
_o="$(bench timed /config/pending)"
|
||||
read -r sec bytes <<<"$_o"
|
||||
put "s$s.pending_s" "$sec"
|
||||
|
||||
local net med p95 total
|
||||
net="$(bench network-id bench-03)"
|
||||
lat() {
|
||||
_o="$(bench latency 20 "$2")"
|
||||
read -r med p95 total <<<"$_o"
|
||||
put "s$s.$1" "$med / $p95"
|
||||
echo " ($total matches)" >&2
|
||||
}
|
||||
lat lat_all "limit=100"
|
||||
lat lat_net "limit=100&network_id=$net"
|
||||
lat lat_type "limit=100&type=manual_dhcp"
|
||||
lat lat_host "limit=100&q=h3-12"
|
||||
lat lat_mac "limit=100&q=02:bb:00:03:00"
|
||||
lat lat_deep "limit=100&sort=hostname&order=desc&offset=$((entries - 100))"
|
||||
|
||||
local abefore aafter ms cnt
|
||||
abefore="$(pg "select pg_total_relation_size('audit_log')")"
|
||||
_o="$(bench updates 50)"
|
||||
read -r ms cnt <<<"$_o"
|
||||
aafter="$(pg "select pg_total_relation_size('audit_log')")"
|
||||
put "s$s.upd_ms" "$ms"
|
||||
put "s$s.audit_per_change" "$(((aafter - abefore) / cnt)) (row $(pg "select avg(pg_column_size(a.*))::int from audit_log a where action = 'address.update'"))"
|
||||
before="$(pg "$revsize")"
|
||||
_o="$(bench revision "bench $s, 50 changes")"
|
||||
read -r num sec bytes <<<"$_o"
|
||||
after="$(pg "$revsize")"
|
||||
put "s$s.rev2_s" "$sec"
|
||||
put "s$s.rev2_mib" "$(mib $((after - before)))"
|
||||
_o="$(rss server ddi-server)"
|
||||
read -r r h <<<"$_o"
|
||||
put "s$s.server_rss_end" "$r / $h"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- BIND
|
||||
|
||||
bind_bench() {
|
||||
local s="$1" cfg="$work/cfg-$1"
|
||||
step "BIND: data set $s"
|
||||
dc exec -T bind sh -c 'pkill -x named 2>/dev/null; sleep 1
|
||||
rm -rf /etc/bind/ddi-suite /var/lib/bind/ddi-suite /tmp/named.log
|
||||
mkdir -p /etc/bind/ddi-suite /var/lib/bind/ddi-suite /run/named /var/cache/bind'
|
||||
docker cp "$cfg/dns/." "$(cid bind):/etc/bind/ddi-suite/"
|
||||
dc exec -T bind sh -c 'cd /etc/bind/ddi-suite && mv zones/* /var/lib/bind/ddi-suite/ && rmdir zones
|
||||
chown -R bind:bind /var/lib/bind/ddi-suite /run/named /var/cache/bind'
|
||||
local zones records
|
||||
zones="$(dc exec -T bind sh -c 'ls /var/lib/bind/ddi-suite | wc -l')"
|
||||
records="$(dc exec -T bind sh -c 'cat /var/lib/bind/ddi-suite/* | grep -cE "[[:space:]](A|PTR)[[:space:]]"')"
|
||||
put "s$s.bind_zones" "$zones / $records"
|
||||
put "s$s.bind_files_mib" "$(mib "$(du -sb "$cfg/dns/zones" | awk '{ print $1 }')")"
|
||||
dc exec -T -d bind sh -c 'exec named -u bind -g -c /etc/bind/named.conf >/tmp/named.log 2>&1'
|
||||
local i
|
||||
for i in $(seq 600); do
|
||||
dc exec -T bind grep -q 'running$' /tmp/named.log 2>/dev/null && break
|
||||
if dc exec -T bind grep -qE 'exiting \(due to fatal error\)|loading configuration: failure' /tmp/named.log 2>/dev/null; then
|
||||
fail "named failed to start"
|
||||
fi
|
||||
[[ $i == 600 ]] && fail "named did not start within 5 minutes"
|
||||
sleep 0.5
|
||||
done
|
||||
# shellcheck disable=SC2016 # awk program
|
||||
put "s$s.bind_load_s" "$(dc exec -T bind awk '
|
||||
function t(x, a) { split(x, a, ":"); return a[1] * 3600 + a[2] * 60 + a[3] }
|
||||
/starting BIND/ && !s { s = t($2) }
|
||||
/all zones loaded/ { e = t($2) }
|
||||
END { printf "%.2f", e - s }' /tmp/named.log)"
|
||||
_o="$(rss bind named)"
|
||||
read -r r _ <<<"$_o"
|
||||
put "s$s.bind_rss" "$r"
|
||||
|
||||
docker cp "$work/s$s/queries.txt" "$(cid dnsperf):/queries.txt"
|
||||
local t0 t1 c0 c1 o
|
||||
c0="$(ticks bind named)"
|
||||
t0="$(now_ms)"
|
||||
o="$(dc exec -T dnsperf dnsperf -s "$bind_ip" -d /queries.txt -l 15 -c 16 -q 500 2>&1)"
|
||||
t1="$(now_ms)"
|
||||
c1="$(ticks bind named)"
|
||||
echo "$o" >&2
|
||||
put "s$s.bind_qps" "$(awk '/Queries per second:/ { printf "%.0f", $4 }' <<<"$o")"
|
||||
put "s$s.bind_lat" "$(awk '/Average Latency/ { a = $4 * 1000 } /Queries lost:/ { l = $4 } END { printf "%.2f / %s", a, l }' <<<"$o")"
|
||||
put "s$s.bind_cpu" "$(awk -v c=$((c1 - c0)) -v ms=$((t1 - t0)) 'BEGIN { printf "%.0f", c / (ms / 1000) }')"
|
||||
o="$(dc exec -T dnsperf dnsperf -s "$bind_ip" -d /queries.txt -l 15 -c 16 -Q 5000 2>&1)"
|
||||
echo "$o" >&2
|
||||
put "s$s.bind_lat5k" "$(awk '/Average Latency/ { printf "%.2f", $4 * 1000 }' <<<"$o")"
|
||||
_o="$(rss bind named)"
|
||||
read -r r _ <<<"$_o"
|
||||
put "s$s.bind_rss_end" "$r"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- Kea
|
||||
|
||||
kea_bench() {
|
||||
local s="$1" cfg="$work/cfg-$1"
|
||||
step "Kea: data set $s"
|
||||
dc exec -T kea sh -c 'pkill -x kea-dhcp4 2>/dev/null; sleep 1
|
||||
rm -f /var/lib/kea/kea-leases4.csv* /tmp/kea.log
|
||||
mkdir -p /run/kea /var/lib/kea && chmod 750 /run/kea'
|
||||
docker cp "$cfg/dhcp/kea-dhcp4.conf" "$(cid kea):/etc/kea/kea-dhcp4.conf"
|
||||
put "s$s.kea_size" "$(jq '.Dhcp4.subnet4 | length' "$cfg/dhcp/kea-dhcp4.conf") / $(jq '[.Dhcp4.subnet4[].reservations // [] | length] | add' "$cfg/dhcp/kea-dhcp4.conf")"
|
||||
local t0 t1 base
|
||||
t0="$(now_ms)"
|
||||
dc exec -T kea true
|
||||
t1="$(now_ms)"
|
||||
base=$((t1 - t0))
|
||||
t0="$(now_ms)"
|
||||
dc exec -T kea kea-dhcp4 -t /etc/kea/kea-dhcp4.conf >/dev/null
|
||||
t1="$(now_ms)"
|
||||
put "s$s.kea_test_s" "$(awk -v ms=$((t1 - t0 - base)) 'BEGIN { printf "%.2f", ms / 1000 }')"
|
||||
dc exec -T -d kea sh -c 'exec kea-dhcp4 -c /etc/kea/kea-dhcp4.conf >/tmp/kea.log 2>&1'
|
||||
local i
|
||||
for i in $(seq 600); do
|
||||
dc exec -T kea grep -q DHCP4_STARTED /tmp/kea.log 2>/dev/null && break
|
||||
dc exec -T kea pidof kea-dhcp4 >/dev/null || fail "kea-dhcp4 exited"
|
||||
[[ $i == 600 ]] && fail "Kea did not start within 5 minutes"
|
||||
sleep 0.5
|
||||
done
|
||||
# shellcheck disable=SC2016 # awk program
|
||||
put "s$s.kea_start_s" "$(dc exec -T kea awk '
|
||||
function t(x, a) { split(x, a, ":"); return a[1] * 3600 + a[2] * 60 + a[3] }
|
||||
!s && $2 ~ /^[0-9][0-9]:/ { s = t($2) }
|
||||
/DHCP4_STARTED/ { e = t($2) }
|
||||
END { printf "%.2f", e - s }' /tmp/kea.log)"
|
||||
_o="$(rss kea kea-dhcp4)"
|
||||
read -r r _ <<<"$_o"
|
||||
put "s$s.kea_rss" "$r"
|
||||
|
||||
local rate o c0 c1 lf
|
||||
for rate in "${rates[@]}"; do
|
||||
c0="$(ticks kea kea-dhcp4)"
|
||||
t0="$(now_ms)"
|
||||
o="$(dc exec -T perfdhcp timeout 60 perfdhcp -4 -r "$rate" -p 10 -R 50000 "$kea_ip" 2>&1 || true)"
|
||||
t1="$(now_ms)"
|
||||
c1="$(ticks kea kea-dhcp4)"
|
||||
echo "$o" | tail -n 45 >&2
|
||||
lf="$(dc exec -T kea stat -c %s /var/lib/kea/kea-leases4.csv 2>/dev/null || echo 0)"
|
||||
_o="$(rss kea kea-dhcp4)"
|
||||
read -r r _ <<<"$_o"
|
||||
put "s$s.kea_$rate" "$(awk '
|
||||
/^Rate:/ { rate = $2 }
|
||||
/Statistics for: DISCOVER-OFFER/ { sec = "do" }
|
||||
/Statistics for: REQUEST-ACK/ { sec = "ra" }
|
||||
/^drops ratio:/ { d[sec] = $3 }
|
||||
END { printf "%.0f / %s %% / %s %%", rate, d["do"], d["ra"] }' <<<"$o")"
|
||||
put "s$s.kea_${rate}_res" "$(awk '
|
||||
/Statistics for: REQUEST-ACK/ { sec = "ra" }
|
||||
/^avg delay:/ && sec == "ra" { printf "%s", $3; exit }' <<<"$o") / $(awk -v c=$((c1 - c0)) -v ms=$((t1 - t0)) 'BEGIN { printf "%.0f", c / (ms / 1000) }') / $r / $(mib "$lf")"
|
||||
done
|
||||
}
|
||||
|
||||
prev=0
|
||||
for s in "${scales[@]}"; do
|
||||
server_bench "$s" "$prev" $((s / 1000))
|
||||
prev=$((s / 1000))
|
||||
bind_bench "$s"
|
||||
kea_bench "$s"
|
||||
done
|
||||
|
||||
step "Done"
|
||||
@@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Management server for the benchmark. bin/ is filled by run.sh with the
|
||||
# binaries built by `make build-go` (static, CGO disabled).
|
||||
FROM alpine:3.22
|
||||
RUN apk add --no-cache curl jq
|
||||
COPY --chmod=0755 bin/ddi-server /usr/local/bin/ddi-server
|
||||
COPY --chmod=0755 server/entrypoint.sh /bench/entrypoint.sh
|
||||
COPY --chmod=0755 server/bench.sh /bench/bench.sh
|
||||
ENTRYPOINT ["/bench/entrypoint.sh"]
|
||||
Executable
+178
@@ -0,0 +1,178 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# REST API steps of the benchmark, run inside the server container (curl and
|
||||
# jq are available there; the API listens on 127.0.0.1 only). Results are
|
||||
# printed as "key value" lines on stdout; diagnostics go to stderr.
|
||||
#
|
||||
# Usage: bench.sh <step> [args]
|
||||
# setup <bootstrap-password> change the password, generator settings,
|
||||
# the /16 DHCP test network
|
||||
# networks <file> create the networks of datagen.sh
|
||||
# import <csv> import one CSV file: "<seconds> <created>"
|
||||
# latency <reps> <query> GET /addresses?<query> <reps> times:
|
||||
# "<median-ms> <p95-ms> <total>"
|
||||
# network-id <name> print the ID of a network
|
||||
# updates <n> change the description of n addresses one by
|
||||
# one: "<avg-ms>"
|
||||
# revision <comment> POST /revisions: "<number> <seconds> <bytes>"
|
||||
# timed <path> GET <path>: "<seconds> <bytes>"
|
||||
# preview <out-dir> <relay-ip> GET /config/preview, write the files to
|
||||
# <out-dir>/dns and <out-dir>/dhcp (the Kea
|
||||
# test subnet gets <relay-ip> as relay):
|
||||
# "<seconds> <bytes> <dns-files>"
|
||||
# Results of timed_req are split into positional parameters on purpose.
|
||||
# shellcheck disable=SC2086
|
||||
set -eu
|
||||
|
||||
base=http://127.0.0.1:8080/api/v1
|
||||
jar=/tmp/bench-cookies
|
||||
csrf_file=/tmp/bench-csrf
|
||||
password=bench-admin-password-1
|
||||
|
||||
# req METHOD PATH [JSON] prints the response body; fails on HTTP >= 400.
|
||||
req() {
|
||||
csrf=$(cat "$csrf_file" 2>/dev/null || true)
|
||||
out=$(curl -sS -b "$jar" -c "$jar" -X "$1" -H 'Content-Type: application/json' \
|
||||
-H "X-CSRF-Token: $csrf" ${3:+--data "$3"} -w '\n%{http_code}' "$base$2")
|
||||
code=$(printf '%s\n' "$out" | tail -n1)
|
||||
body=$(printf '%s\n' "$out" | sed '$d')
|
||||
case "$code" in
|
||||
2*) printf '%s\n' "$body" ;;
|
||||
*)
|
||||
echo "$1 $2 -> HTTP $code: $body" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# timed_req METHOD PATH OUTFILE [curl options] writes the body to OUTFILE and
|
||||
# prints "<seconds> <bytes>"; fails on HTTP >= 400.
|
||||
timed_req() {
|
||||
m=$1 p=$2 f=$3
|
||||
shift 3
|
||||
csrf=$(cat "$csrf_file" 2>/dev/null || true)
|
||||
res=$(curl -sS -b "$jar" -c "$jar" -X "$m" -H "X-CSRF-Token: $csrf" -o "$f" \
|
||||
-w '%{http_code} %{time_total} %{size_download}' "$@" "$base$p")
|
||||
set -- $res
|
||||
case "$1" in
|
||||
2*) echo "$2 $3" ;;
|
||||
*)
|
||||
echo "$m $p -> HTTP $1: $(head -c 2000 "$f")" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
login() {
|
||||
req POST /auth/login "{\"username\":\"admin\",\"password\":\"$1\"}" | jq -r .csrf_token >"$csrf_file"
|
||||
}
|
||||
|
||||
session() {
|
||||
if [ -s "$csrf_file" ] && req GET /auth/session >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
login "$password"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
setup)
|
||||
login "$2"
|
||||
req POST /auth/password "{\"current_password\":\"$2\",\"new_password\":\"$password\"}" >/dev/null
|
||||
session
|
||||
req GET /settings/generator | jq '
|
||||
.dns.primary_ns = "ns1.bench.test"
|
||||
| .dns.hostmaster = "hostmaster@bench.test"
|
||||
| .dns.name_servers = [{"name": "ns1.bench.test", "addresses": ["10.0.0.53"]}]' >/tmp/settings.json
|
||||
req PUT /settings/generator "$(cat /tmp/settings.json)" >/dev/null
|
||||
req POST /networks '{"cidr":"10.200.0.0/16","name":"bench-dhcp","gateway":"10.200.0.1","domain":"bench.test",
|
||||
"dhcp_enabled":true,"pools":[{"start":"10.200.0.10","end":"10.200.255.250"}],"dns_servers":["10.0.0.53"]}' >/dev/null
|
||||
;;
|
||||
networks)
|
||||
session
|
||||
n=0
|
||||
while read -r cidr gw start end name; do
|
||||
req POST /networks "{\"cidr\":\"$cidr\",\"name\":\"$name\",\"gateway\":\"$gw\",\"domain\":\"bench.test\",
|
||||
\"dhcp_enabled\":true,\"pools\":[{\"start\":\"$start\",\"end\":\"$end\"}],\"dns_servers\":[\"10.0.0.53\"]}" >/dev/null
|
||||
n=$((n + 1))
|
||||
done <"$2"
|
||||
echo "$n"
|
||||
;;
|
||||
import)
|
||||
session
|
||||
r=$(timed_req POST /addresses/import /tmp/import.json -H 'Content-Type: text/csv' --data-binary "@$2")
|
||||
set -- $r
|
||||
created=$(jq -r '.created' /tmp/import.json)
|
||||
problems=$(jq -r '.problems | length' /tmp/import.json)
|
||||
if [ "$problems" != 0 ]; then
|
||||
jq -c '.problems[:5]' /tmp/import.json >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$1 $created"
|
||||
;;
|
||||
latency)
|
||||
session
|
||||
reps=$2 query=$3
|
||||
: >/tmp/lat.txt
|
||||
i=0
|
||||
while [ "$i" -lt "$reps" ]; do
|
||||
r=$(timed_req GET "/addresses?$query" /tmp/lat.json)
|
||||
echo "${r%% *}" >>/tmp/lat.txt
|
||||
i=$((i + 1))
|
||||
done
|
||||
total=$(jq -r .total /tmp/lat.json)
|
||||
sort -n /tmp/lat.txt | awk -v total="$total" '{ v[NR] = $1 } END {
|
||||
p95 = int(NR * 0.95 + 0.999); if (p95 > NR) p95 = NR
|
||||
med = (NR % 2) ? v[(NR + 1) / 2] : (v[NR / 2] + v[NR / 2 + 1]) / 2
|
||||
printf "%.1f %.1f %d\n", med * 1000, v[p95] * 1000, total }'
|
||||
;;
|
||||
network-id)
|
||||
session
|
||||
req GET /networks | jq -r --arg n "$2" '.items[] | select(.name == $n) | .id'
|
||||
;;
|
||||
updates)
|
||||
session
|
||||
req GET "/addresses?type=static&limit=$2&sort=ip" >/tmp/upd.json
|
||||
: >/tmp/upd.txt
|
||||
jq -c '.items[] | {id, ip, type, hostname, description, owner, tags, version}' /tmp/upd.json |
|
||||
while read -r a; do
|
||||
id=$(printf '%s' "$a" | jq -r .id)
|
||||
body=$(printf '%s' "$a" | jq -c '{ip, type, hostname, owner, tags, version, description: (.description + " (changed)")}')
|
||||
r=$(timed_req PUT "/addresses/$id" /tmp/upd-one.json -H 'Content-Type: application/json' --data "$body")
|
||||
echo "${r%% *}" >>/tmp/upd.txt
|
||||
done
|
||||
awk '{ s += $1 } END { printf "%.1f %d\n", s / NR * 1000, NR }' /tmp/upd.txt
|
||||
;;
|
||||
revision)
|
||||
session
|
||||
r=$(timed_req POST /revisions /tmp/rev.json -H 'Content-Type: application/json' --data "{\"comment\":\"$2\"}")
|
||||
set -- $r
|
||||
echo "$(jq -r .number /tmp/rev.json) $1 $2"
|
||||
;;
|
||||
timed)
|
||||
session
|
||||
timed_req GET "$2" /tmp/timed.out
|
||||
;;
|
||||
preview)
|
||||
session
|
||||
out=$2
|
||||
r=$(timed_req GET /config/preview /tmp/preview.json)
|
||||
set -- "$out" "$3" $r
|
||||
rm -rf "$out"
|
||||
mkdir -p "$out/dns" "$out/dhcp"
|
||||
jq -r '.dns[] | "\(.path)\t\(.content | @base64)"' /tmp/preview.json |
|
||||
while IFS="$(printf '\t')" read -r path b64; do
|
||||
mkdir -p "$out/dns/$(dirname "$path")"
|
||||
printf '%s' "$b64" | base64 -d >"$out/dns/$path"
|
||||
done
|
||||
jq -r '.dhcp[] | select(.path == "kea-dhcp4.conf") | .content' /tmp/preview.json |
|
||||
jq --arg ip "$2" '(.Dhcp4.subnet4[] | select(.subnet == "10.200.0.0/16")) |= . + {relay: {"ip-addresses": [$ip]}}' \
|
||||
>"$out/dhcp/kea-dhcp4.conf"
|
||||
jq -c '{warnings: (.warnings | length), dns_error, dhcp_error}' /tmp/preview.json >&2
|
||||
echo "$3 $4 $(jq '.dns | length' /tmp/preview.json)"
|
||||
;;
|
||||
*)
|
||||
echo "unknown step $1" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Creates the master key on first start and runs the server.
|
||||
set -eu
|
||||
mkdir -p "$(dirname "$DDI_MASTER_KEY_FILE")" "$DDI_STATE_DIR"
|
||||
if [ ! -f "$DDI_MASTER_KEY_FILE" ]; then
|
||||
ddi-server gen-master-key >/dev/null
|
||||
fi
|
||||
exec ddi-server serve
|
||||
Reference in New Issue
Block a user