summaryrefslogtreecommitdiffstats
path: root/debian/tests
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--debian/tests/control13
-rw-r--r--debian/tests/kea-ctrl-agent-debconf266
-rw-r--r--debian/tests/kea-dhcp4277
-rw-r--r--debian/tests/kea-dhcp4.conf.template71
-rw-r--r--debian/tests/smoke-tests63
5 files changed, 690 insertions, 0 deletions
diff --git a/debian/tests/control b/debian/tests/control
new file mode 100644
index 0000000..bbed706
--- /dev/null
+++ b/debian/tests/control
@@ -0,0 +1,13 @@
+# Keep this test as the first, as it will verify the default installation
+# behavior wrt kea-ctrl-agent password configuration
+Tests: kea-ctrl-agent-debconf
+Restrictions: needs-root, allow-stderr
+Depends: kea-ctrl-agent
+
+Tests: smoke-tests
+Restrictions: needs-root, allow-stderr
+Depends: kea, curl, jq
+
+Tests: kea-dhcp4
+Restrictions: needs-root, allow-stderr, breaks-testbed
+Depends: kea-dhcp4-server, kea-ctrl-agent, isc-dhcp-client, bridge-utils, iproute2, jq
diff --git a/debian/tests/kea-ctrl-agent-debconf b/debian/tests/kea-ctrl-agent-debconf
new file mode 100644
index 0000000..5790977
--- /dev/null
+++ b/debian/tests/kea-ctrl-agent-debconf
@@ -0,0 +1,266 @@
+#!/bin/bash
+
+set -e
+
+pw_file="/etc/kea/kea-api-password"
+pw_secret="secret_password_${RANDOM}"
+service="kea-ctrl-agent.service"
+
+cleanup() {
+ /bin/true
+}
+
+trap cleanup EXIT
+
+override_systemd_throttling() {
+ mkdir -p /run/systemd/system/kea-ctrl-agent.service.d
+ cat > /run/systemd/system/kea-ctrl-agent.service.d/override.conf <<EOF
+[Unit]
+StartLimitIntervalSec=0
+EOF
+ systemctl daemon-reload
+}
+
+check_perms() {
+ local file="${1}"
+ local wanted_perms="${2}"
+ local perms
+
+ perms=$(stat -c %U:%G:%a "${file}")
+ if [ "${perms}" != "${wanted_perms}" ]; then
+ echo "## ERROR: permissions are ${perms} and should be ${wanted_perms}"
+ return 1
+ else
+ echo "## OK, permissions are ${perms}"
+ fi
+}
+
+service_status_must_be() {
+ local service_status
+ local wanted_status="${1}"
+ service_status=$(systemctl is-active "${service}" || /bin/true)
+ systemctl status "${service}" || /bin/true
+ if [ "${service_status}" != "${wanted_status}" ]; then
+ echo "## ERROR, service is ${service_status}"
+ return 1
+ else
+ echo "## OK, service is ${service_status}"
+ fi
+}
+
+reconfigure_unconfigured() {
+ debconf-set-selections << EOF
+kea-ctrl-agent kea-ctrl-agent/make_a_choice select unconfigured
+EOF
+ dpkg-reconfigure kea-ctrl-agent
+}
+
+reconfigure_password() {
+ local password="${1}"
+ debconf-set-selections << EOF
+kea-ctrl-agent kea-ctrl-agent/make_a_choice select configured_password
+kea-ctrl-agent kea-ctrl-agent/kea_api_password password ${password}
+kea-ctrl-agent kea-ctrl-agent/kea_api_password_again password ${password}
+EOF
+ dpkg-reconfigure kea-ctrl-agent
+}
+
+reconfigure_random() {
+ debconf-set-selections << EOF
+kea-ctrl-agent kea-ctrl-agent/make_a_choice select configured_random_password
+EOF
+ dpkg-reconfigure kea-ctrl-agent
+}
+
+test_fresh_install() {
+ echo
+ echo "## Running ${FUNCNAME[0]}"
+ # On a fresh install, which is the situation we are in as this is the first
+ # test being run, there is no kea-api-password file, and the service isn't
+ # running
+ echo "## Fresh install, default options, there must be no ${pw_file} file"
+ ls -la "$(dirname ${pw_file})"
+ test ! -f "${pw_file}"
+
+ echo
+ echo "## With no ${pw_file}, the service must not be running"
+ service_status_must_be inactive
+ echo
+}
+
+test_service_wont_start_without_pwfile() {
+ echo
+ echo "## Running ${FUNCNAME[0]}"
+ echo "## With no ${pw_file}, service must not start"
+ ls -la "$(dirname ${pw_file})"
+ test ! -f "${pw_file}"
+ echo "## Current status:"
+ systemctl status "${service}" || /bin/true
+ echo
+ echo "## Attempting to start ${service}"
+ systemctl start "${service}"
+ service_status_must_be inactive
+ echo
+}
+
+test_configured_password() {
+ echo
+ echo "## Running ${FUNCNAME[0]}"
+ echo "## Reconfiguring kea-ctrl-agent with password ${pw_secret}"
+ reconfigure_password "${pw_secret}"
+
+ echo "## Checking that ${pw_file} exists and has ${pw_secret}"
+ ls -la "$(dirname ${pw_file})"
+ test -f "${pw_file}"
+ generated_pw=$(cat "${pw_file}")
+ if [ "${generated_pw}" != "${pw_secret}" ]; then
+ echo "## ERROR, password from ${pw_file} is not equal to ${pw_secret}: ${generated_pw}"
+ return 1
+ else
+ echo "## OK, password from ${pw_file} is ${generated_pw}"
+ fi
+
+ echo "## Checking that ${pw_file} has expected permissions and ownership"
+ check_perms "${pw_file}" "root:_kea:640"
+ echo
+
+ echo
+ echo "## Checking that the service is running"
+ service_status_must_be active
+}
+
+test_configured_random_password() {
+ local generated_pw
+
+ echo
+ echo "## Running ${FUNCNAME[0]}"
+ echo "## Reconfiguring kea-ctrl-agent with random password option"
+ reconfigure_random
+
+ echo "## Checking that ${pw_file} exists and has a password different from ${pw_secret}"
+ ls -la "$(dirname ${pw_file})"
+ test -f "${pw_file}"
+
+ generated_pw=$(cat "${pw_file}")
+ if [ "${generated_pw}" = "${pw_secret}" ]; then
+ echo "## ERROR, generated random password \"${generated_pw}\" is equal to \"${pw_secret}\""
+ return 1
+ else
+ echo "## OK, generated random password is \"${generated_pw}\""
+ fi
+ echo
+ echo "## Checking that ${pw_file} has expected permissions and ownership"
+ check_perms "${pw_file}" "root:_kea:640"
+ echo
+
+ echo
+ echo "## Checking that the service is running"
+ service_status_must_be active
+}
+
+test_unconfigured() {
+ local -r new_secret="${pw_secret}${pw_secret}"
+ local contents
+
+ echo
+ echo "## Running ${FUNCNAME[0]}"
+ echo "## Reconfiguring kea-ctrl-agent with option \"unconfigured\" should leave things as they were"
+ echo
+ echo "## Overwriting ${pw_file} with ${new_secret}"
+ printf "%s" "${new_secret}" > "${pw_file}"
+
+ echo "## Reconfiguring"
+ reconfigure_unconfigured
+
+ echo
+ echo "## ${pw_file} should still contain ${new_secret}"
+ contents=$(cat "${pw_file}")
+ if [ "${contents}" != "${new_secret}" ]; then
+ echo "## ERROR, ${pw_file} now contains \"${contents}\""
+ return 1
+ else
+ echo "## OK, same content"
+ fi
+
+ echo "## Removing ${pw_file} and reconfiguring, a new one should not be created, and the service must be stopped"
+ rm -f "${pw_file}"
+ ls -la $(dirname "${pw_file}")
+ echo "## Reconfiguring"
+ reconfigure_unconfigured
+
+ echo "## ${pw_file} was not recreated"
+ ls -la $(dirname "${pw_file}")
+ test ! -f "${pw_file}"
+ echo "## With no ${pw_file}, the service must not be running"
+ service_status_must_be inactive
+}
+
+test_no_start_with_empty_password() {
+ echo
+ echo "## Running ${FUNCNAME[0]}"
+ echo "## kea-ctrl-agent must not start with an empty password file"
+ echo
+ echo "## Truncating ${pw_file}"
+ truncate -s 0 "${pw_file}"
+ ls -la $(dirname "${pw_file}")
+ test ! -s "${pw_file}"
+ echo
+ echo "## Restarting kea-ctrl-agent"
+ systemctl restart "${service}"
+ echo
+ echo "## Service must not be started"
+ service_status_must_be inactive
+}
+
+test_empty_password_via_debconf() {
+ local service_status
+ local contents
+
+ echo
+ echo "## Running ${FUNCNAME[0]}"
+ echo "## Reconfiguring with password set to ${pw_secret}"
+ reconfigure_password "${pw_secret}"
+
+ echo
+ echo "## ${pw_file} must now contain ${pw_secret}"
+ contents=$(cat "${pw_file}")
+ if [ "${contents}" != "${pw_secret}" ]; then
+ echo "## ERROR, ${pw_file} now contains \"${contents}\""
+ return 1
+ else
+ echo "## OK, same content"
+ fi
+
+ echo
+ echo "## Service must be running"
+ service_status_must_be active
+
+ echo
+ echo "## Reconfiguring with an empty password should not change the existing password"
+ # set an empty password (no args)
+ reconfigure_password
+ ls -la $(dirname "${pw_file}")
+ contents=$(cat "${pw_file}")
+ if [ "${contents}" != "${pw_secret}" ]; then
+ echo "## ERROR, ${pw_file} now contains \"${contents}\""
+ return 1
+ else
+ echo "## OK, same content"
+ fi
+
+ echo
+ echo "## Service must be running"
+ service_status_must_be active
+}
+
+
+# we restart kea-ctrl-agent a lot during this test
+override_systemd_throttling
+
+test_fresh_install
+test_service_wont_start_without_pwfile
+test_configured_password
+test_configured_random_password
+test_unconfigured
+test_no_start_with_empty_password
+test_empty_password_via_debconf
diff --git a/debian/tests/kea-dhcp4 b/debian/tests/kea-dhcp4
new file mode 100644
index 0000000..66ce927
--- /dev/null
+++ b/debian/tests/kea-dhcp4
@@ -0,0 +1,277 @@
+#!/bin/bash
+
+set -e
+set -o pipefail
+
+bridge="keabr0"
+bridge_ip="192.168.127.1/24"
+subnetcidr="192.168.127.0/24"
+pool_range="192.168.127.10 - 192.168.127.250"
+test_domain="example.autopkgtest"
+server_iface="p1"
+client_iface="client0"
+client_ns="clientns"
+declare -A dhcp4_config
+resolv_conf_bkp=$(mktemp)
+kea_password_file="/etc/kea/kea-api-password"
+
+# kea-ctrl-agent needs a password file, or else it won't start
+# this also tests the debconf mechanism
+debconf-set-selections << eof
+kea-ctrl-agent kea-ctrl-agent/make_a_choice select configured_random_password
+eof
+dpkg-reconfigure kea-ctrl-agent
+[ -s "${kea_password_file}" ] || {
+ echo "ERROR, debconf-set-selections failed to set a password for kea-ctrl-agent"
+ exit 1
+}
+
+auth_params="--auth-user kea-api --auth-password $(cat ${kea_password_file})"
+
+cleanup() {
+ rc=$?
+ set +e # so we don't exit midcleanup
+ if [ ${rc} -ne 0 ]; then
+ echo "## FAIL"
+ echo
+ echo "## dmesg"
+ dmesg -T | tail -n 500
+ echo
+ echo "## kea logs"
+ journalctl -u kea-dhcp4-server.service
+ fi
+ echo
+ echo "## Cleaning up"
+ ip link set "${server_iface}" down
+ ip link del "${server_iface}"
+ ip link set "${bridge}" down
+ brctl delbr "${bridge}"
+ ip netns delete "${client_ns}"
+ sed -r -i "/example.autopkgtest/d" /etc/hosts
+ if [ -s "${resolv_conf_bkp}" ]; then
+ cat "${resolv_conf_bkp}" > /etc/resolv.conf
+ fi
+ rm -f "${resolv_conf_bkp}"
+ # restore it for when we are called from the main script, and not the trap
+ set -e
+}
+
+trap cleanup EXIT
+
+run_on_client() {
+ ip netns exec "${client_ns}" "$@"
+}
+
+setup() {
+ cleanup 2>/dev/null
+ # so we don't have to worry about it being a symlink
+ cat /etc/resolv.conf > "${resolv_conf_bkp}"
+ echo "127.0.1.1 $(hostname).${test_domain} $(hostname)" >> /etc/hosts
+ ip netns add "${client_ns}"
+ ip link add "${server_iface}" type veth peer "${client_iface}" netns "${client_ns}"
+ brctl addbr "${bridge}"
+ brctl addif "${bridge}" "${server_iface}"
+ ip link set "${server_iface}" up
+ ip link set "${bridge}" up
+ ip addr add "${bridge_ip}" dev "${bridge}"
+}
+
+render_dhcp4_conf() {
+ local -n config="${1}"
+ local -r service="dhcp4"
+
+ template="debian/tests/kea-${service}.conf.template"
+ [ -f "${template}" ] || return 1
+ output="/etc/kea/kea-${service}.conf"
+
+ cat "${template}" | sed -r \
+ -e "s,@interface@,${config[interface]}," \
+ -e "s,@dnsip@,${config[dnsip]}," \
+ -e "s,@domain@,${config[domain]}," \
+ -e "s/@domainsearch@/${config[domainsearch]}/" \
+ -e "s,@router@,${config[router]}," \
+ -e "s,@subnetcidr@,${config[subnetcidr]}," \
+ -e "s,@poolrange@,${config[poolrange]}," \
+ -e "s,@multiarch@,$(dpkg-architecture -qDEB_HOST_MULTIARCH)," \
+ > "${output}"
+}
+
+json_get_length() {
+ echo "${1}" | jq '. | length'
+}
+
+kea_get_leases_by_mac() {
+ local mac="${1}"
+ echo "\"hw-address\": \"${mac}\"" | kea-shell ${auth_params} --service dhcp4 lease4-get-by-hw-address
+}
+
+get_result_from_lease() {
+ echo "${1}" | jq -r '.[0].result'
+}
+
+get_number_of_leases() {
+ echo "${1}" | jq '.[0].arguments.leases | length'
+}
+
+get_ip_from_lease() {
+ echo "${1}" | jq -r '.[0]["arguments"]["leases"][0]["ip-address"]'
+}
+
+get_mac_from_lease() {
+ echo "${1}" | jq -r '.[0]["arguments"]["leases"][0]["hw-address"]'
+}
+
+get_valid_lifetime_from_lease() {
+ echo "${1}" | jq -r '.[0]["arguments"]["leases"][0]["valid-lft"]'
+}
+
+check_leases() {
+ local data="${1}"
+ local if_mac="${2}"
+ local if_ip="${3}"
+ local res
+
+ res=$(json_get_length "${data}")
+ if [ ${res} != 1 ]; then
+ echo "## ERROR"
+ echo "## Expected 1 result, got ${res}:"
+ return 1
+ fi
+
+ res=$(get_result_from_lease "${data}")
+ if [ ${res} != 0 ]; then
+ echo "## ERROR"
+ echo "## Failed to obtain leases from server, code ${res}"
+ return 1
+ fi
+
+ res=$(get_number_of_leases "${data}")
+ if [ ${res} -ne 1 ]; then
+ echo "## ERROR"
+ echo "## Expected 1 lease, got ${res}:"
+ return 1
+ fi
+
+ res=$(get_ip_from_lease "${data}")
+ if [ "${if_ip}" != "${res}" ]; then
+ echo "## ERROR"
+ echo "## IP from lease (${res}) does not match IP from interface: ${if_ip}"
+ run_on_client ip a show
+ return 1
+ fi
+
+ res=$(get_mac_from_lease "${data}")
+ if [ "${if_mac}" != "${res}" ]; then
+ echo "## ERROR"
+ echo "## MAC from lease (${res}) does not match MAC from client interface: ${if_mac}"
+ run_on_client ip l show
+ return 1
+ fi
+}
+
+
+setup
+
+dhcp4_config["interface"]="${bridge}"
+# get rid of the CIDR part at the end
+dhcp4_config["dnsip"]="${bridge_ip%%/*}"
+dhcp4_config["domain"]="${test_domain}"
+dhcp4_config["domainsearch"]="${test_domain}"
+# get rid of the CIDR part at the end
+dhcp4_config["router"]="${bridge_ip%%/*}"
+dhcp4_config["subnetcidr"]="${subnetcidr}"
+dhcp4_config["poolrange"]="${pool_range}"
+
+echo
+echo "## Configuring kea-dhcp4 and restarting the service"
+render_dhcp4_conf dhcp4_config
+systemctl restart kea-dhcp4-server.service
+sleep 2s
+
+echo
+echo "## Obtaining IP via dhclient"
+run_on_client timeout -v 60s dhclient -v "${client_iface}"
+echo "## OK"
+
+ip=$(run_on_client ip -4 -o addr show dev "${client_iface}" | awk '{print $4}')
+ip=${ip%%/*} # remove the CIDR part
+mac=$(run_on_client ip -4 link show dev "${client_iface}" | grep "link/ether" | awk '{print $2}')
+
+echo
+echo "## Got ip=${ip}"
+
+echo
+echo "## Checking leases that match client's ethernet address ${mac}"
+# this will break if/when we close LP: #2007312
+leases=$(kea_get_leases_by_mac "${mac}")
+echo "## Leases:"
+echo "${leases}" | jq .
+
+check_leases "${leases}" "${mac}" "${ip}"
+echo "## OK"
+
+echo
+echo "## INFO: Networking in the ${client_ns} namespace:"
+echo
+echo "## Interfaces"
+run_on_client ip a
+echo
+echo "## Routes"
+run_on_client ip route
+echo
+echo "## DNS"
+if command -v resolvectl > /dev/null 2>&1; then
+ run_on_client resolvectl status
+else
+ echo "## Skipping DNS info (no resolvectl installed)"
+fi
+
+echo
+echo "## Checking that the DNS domain \"${test_domain}\" was added to resolv.conf"
+if grep -E "^search[[:blank:]]" /etc/resolv.conf | grep -q -w -F "${test_domain}"; then
+ echo "## OK"
+else
+ echo "## ERROR"
+ echo "## /etc/resolv.conf does not contain ${test_domain}"
+ cat /etc/resolv.conf
+ exit 1
+fi
+
+echo
+echo "## Releasing IP via dhclient -r"
+run_on_client timeout -v 60s dhclient -v -r
+echo "## OK"
+
+echo
+# As per entry 2072 in
+# https://downloads.isc.org/isc/kea/2.4.0/Kea-2.4.0-ReleaseNotes.txt, starting
+# from kea 2.3.2, a lease is no longer deleted from the lease database after a
+# release request. Instead, it is expired to enable lease affinity. It is kept
+# for `hold-reclaimed-time` seconds. Its default value is 3600 seconds.
+# https://kea.readthedocs.io/en/kea-2.4.0/arm/lease-expiration.html
+echo "## Checking that the lease was expired"
+leases=$(kea_get_leases_by_mac "${mac}")
+echo "${leases}" | jq .
+n_results=$(json_get_length "${leases}")
+if [ ${n_results} -ne 1 ]; then
+ echo "## ERROR, expected 1 result, got ${n_results}"
+ echo "${leases}" | jq .
+ exit 1
+fi
+
+n_leases=$(get_number_of_leases "${leases}")
+if [ ${n_leases} -ne 1 ]; then
+ echo "## ERROR"
+ echo "## Expected 1 lease, got ${n_leases}:"
+ echo "${leases}" | jq .
+ exit 1
+fi
+lft=$(get_valid_lifetime_from_lease "${leases}")
+if [ ${lft} -gt 0 ]; then
+ echo "## ERROR"
+ echo "## Expected expired lease lifetime (0), got ${lft}"
+ echo "${leases}" | jq .
+ exit 1
+fi
+
+echo "## OK"
diff --git a/debian/tests/kea-dhcp4.conf.template b/debian/tests/kea-dhcp4.conf.template
new file mode 100644
index 0000000..2addefd
--- /dev/null
+++ b/debian/tests/kea-dhcp4.conf.template
@@ -0,0 +1,71 @@
+{
+"Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [ "@interface@" ],
+ "service-sockets-max-retries": 10,
+ "service-sockets-retry-wait-time": 1000
+ },
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/run/kea/kea4-ctrl-socket"
+ },
+ "hooks-libraries": [
+ {
+ "library": "/usr/lib/@multiarch@/kea/hooks/libdhcp_lease_cmds.so"
+ }
+ ],
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+ "expired-leases-processing": {
+ "reclaim-timer-wait-time": 10,
+ "flush-reclaimed-timer-wait-time": 25,
+ "hold-reclaimed-time": 3600,
+ "max-reclaim-leases": 100,
+ "max-reclaim-time": 250,
+ "unwarned-reclaim-cycles": 5
+ },
+ "renew-timer": 900,
+ "rebind-timer": 1800,
+ "valid-lifetime": 3600,
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "data": "@dnsip@"
+ },
+ {
+ "code": 15,
+ "data": "@domain@"
+ },
+ {
+ "name": "domain-search",
+ "data": "@domainsearch@"
+ }
+ ],
+ "subnet4": [
+ {
+ "subnet": "@subnetcidr@",
+ "pools": [ { "pool": "@poolrange@" } ],
+ "option-data": [
+ {
+ "name": "routers",
+ "data": "@router@"
+ }
+ ]
+ }
+ ],
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output_options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0
+ }
+ ]
+}
+}
diff --git a/debian/tests/smoke-tests b/debian/tests/smoke-tests
new file mode 100644
index 0000000..2de85c4
--- /dev/null
+++ b/debian/tests/smoke-tests
@@ -0,0 +1,63 @@
+#!/bin/bash
+
+set -exo pipefail
+
+# kea-ctrl-agent needs a password file, or else it won't start
+# this also tests the debconf mechanism
+debconf-set-selections << eof
+kea-ctrl-agent kea-ctrl-agent/make_a_choice select configured_random_password
+eof
+
+dpkg-reconfigure kea-ctrl-agent
+kea_password_file="/etc/kea/kea-api-password"
+[ -s "${kea_password_file}" ] || {
+ echo "ERROR, debconf-set-selections failed to set a password for kea-ctrl-agent"
+ exit 1
+}
+
+# Arbitrary wait to allow for the services to start.
+# This is needed to avoid having racy/flaky tests.
+sleep 5
+
+# Check that the PID files are in the right location
+for f in kea-dhcp4.kea-dhcp4.pid kea-dhcp6.kea-dhcp6.pid kea-ctrl-agent.kea-ctrl-agent.pid kea-dhcp-ddns.kea-dhcp-ddns.pid; do
+ test -f "/run/kea/$f"
+done
+
+# Check that the sockets are in the right location
+for socket in kea-ddns-ctrl-socket kea4-ctrl-socket kea6-ctrl-socket; do
+ test -S "/run/kea/$socket"
+done
+
+# Check that lock files are in the right location
+test -f /run/lock/kea/logger_lockfile
+
+check_kea_version() {
+ CHECKED_VERSION=$1
+ if [[ ! ${CHECKED_VERSION} =~ [0-9]+(\.[0-9]+){2} ]]; then
+ echo "Version [ ${CHECKED_VERSION} ] does not match X.Y.Z format"
+ exit 1
+ fi
+}
+
+# Check dhcp4 server configuration file
+kea-dhcp4 -t /etc/kea/kea-dhcp4.conf > /dev/null
+
+# Check dhcp6 server configuration file
+kea-dhcp6 -t /etc/kea/kea-dhcp6.conf > /dev/null
+
+# Check if we need to provide authentication
+auth_params=""
+basic_auth_params=""
+if [ -s /etc/kea/kea-api-password ]; then
+ auth_params="--auth-user kea-api --auth-password $(cat /etc/kea/kea-api-password)"
+ basic_auth_params="-u kea-api:$(cat /etc/kea/kea-api-password)"
+fi
+
+# Check control agent API
+TEST_KEA_VERSION=$(curl ${basic_auth_params} -s -X POST -H "Content-Type: application/json" -d '{ "command": "version-get", "service": [ "dhcp4" ] }' 127.0.0.1:8000 | jq -r '.[0].text')
+check_kea_version "${TEST_KEA_VERSION}"
+
+# Check control agent API through kea-shell
+TEST_KEA_VERSION=$(echo | kea-shell --service dhcp4 --host 127.0.0.1 --port 8000 ${auth_params} version-get | jq -r '.[0].text')
+check_kea_version "${TEST_KEA_VERSION}"