1#!/bin/sh 2 3# Copyright (C) 2014-2021 Internet Systems Consortium, Inc. ("ISC") 4# 5# This Source Code Form is subject to the terms of the Mozilla Public 6# License, v. 2.0. If a copy of the MPL was not distributed with this 7# file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 9# shellcheck disable=SC1091 10# SC1091: Not following: ... was not specified as input (see shellcheck -x). 11 12# shellcheck disable=SC2154 13# SC2154: ... is referenced but not assigned. 14# Reason: some variables are sourced. 15 16# Exit with error if commands exit with non-zero and if undefined variables are 17# used. 18set -eu 19 20# Include common test library. 21. "@abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh" 22 23# Include admin utilities 24. "@abs_top_srcdir@/src/bin/admin/admin-utils.sh" 25 26# Set path to the production schema scripts 27db_scripts_dir="@abs_top_srcdir@/src/share/database/scripts" 28 29# Set location of the kea-admin. 30kea_admin="@abs_top_builddir@/src/bin/admin/kea-admin" 31 32cql_wipe() { 33 # Wipe the database. 34 run_command \ 35 cql_execute_script $db_scripts_dir/cql/dhcpdb_drop.cql 36 assert_eq 0 "${EXIT_CODE}" "drop table query failed, expected %d, returned %d" 37} 38 39cql_db_init_test() { 40 test_start "cql.init" 41 42 # Wipe the database. 43 cql_wipe 44 45 # Create the database 46 run_command \ 47 "${kea_admin}" db-init cql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" 48 assert_eq 0 "${EXIT_CODE}" "kea-admin db-init cql failed, expected exit code: %d, actual: %d" 49 50 # Verify that all the expected tables exist 51 52 # Check schema_version table 53 run_command \ 54 cql_execute "SELECT version, minor FROM schema_version;" 55 assert_eq 0 "${EXIT_CODE}" "schema_version table check failed, expected exit code: %d, actual: %d" 56 57 # Check lease4 table 58 run_command \ 59 cql_execute "SELECT address, hwaddr, client_id, valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, state, user_context FROM lease4;" 60 assert_eq 0 "${EXIT_CODE}" "lease4 table check failed, expected exit code: %d, actual: %d" 61 62 # Check lease6 table 63 run_command \ 64 cql_execute "SELECT address, duid, valid_lifetime, expire, subnet_id, pref_lifetime, lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, state, user_context FROM lease6;" 65 assert_eq 0 "${EXIT_CODE}" "lease6 table check failed, expected exit code: %d, actual: %d" 66 67 # Check lease6_types table 68 run_command \ 69 cql_execute "SELECT lease_type, name FROM lease6_types;" 70 assert_eq 0 "${EXIT_CODE}" "lease6_types table check failed, expected exit code: %d, actual: %d" 71 72 # Check lease_state table 73 run_command \ 74 cql_execute "SELECT state, name FROM lease_state;" 75 assert_eq 0 "${EXIT_CODE}" "lease_state table check failed, expected exit code: %d, actual: %d" 76 77 # Trying to create it again should fail. This verifies the db present 78 # check 79 echo "Making sure keyspace creation fails the second time..." 80 run_command \ 81 "${kea_admin}" db-init cql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" 82 assert_eq 2 "${EXIT_CODE}" "kea-admin failed to deny db-init, expected exit code: %d, actual: %d" 83 84 # Wipe the database. 85 cql_wipe 86 87 test_finish 0 88} 89 90cql_db_version_test() { 91 test_start "cql.version" 92 93 # Wipe the database. 94 cql_wipe 95 96 # Create the database. 97 run_command \ 98 "${kea_admin}" db-init cql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" 99 assert_eq 0 "${EXIT_CODE}" "kea-admin db-init cql failed, expected exit code: %d, actual: %d" 100 101 # Verify that kea-admin db-version returns the correct version. 102 version=$("${kea_admin}" db-version cql -u "${db_user}" -p "${db_password}" -n "${db_name}") 103 assert_str_eq "5.0" "${version}" "Expected kea-admin to return %s, returned value was %s" 104 105 # Wipe the database. 106 cql_wipe 107 108 test_finish 0 109} 110 111cql_upgrade_test() { 112 test_start "cql.upgrade" 113 114 # Wipe the database. 115 cql_wipe 116 117 # Initialize database to schema 1.0. 118 run_command \ 119 cql_execute_script "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.cql" 120 assert_eq 0 "${EXIT_CODE}" "cannot initialize the database, expected exit code: %d, actual: %d" 121 122 run_command \ 123 "${kea_admin}" db-upgrade cql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" 124 assert_eq 0 "${EXIT_CODE}" "db-upgrade failed, expected exit code: %d, actual: %d" 125 126 # Wipe the database. 127 cql_wipe 128 129 test_finish 0 130} 131 132cql_lease4_dump_test() { 133 test_start "cql.lease4_dump_test" 134 135 test_dir="@abs_top_srcdir@/src/bin/admin/tests" 136 output_dir="@abs_top_builddir@/src/bin/admin/tests" 137 138 output_file="$output_dir/data/cql.lease4_dump_test.output.csv" 139 sorted_file="$output_dir/data/cql.lease4_dump_test.output.sorted.csv" 140 ref_file="$test_dir/data/cql.lease4_dump_test.reference.csv" 141 142 # Wipe out any residuals from prior failed runs. 143 if [ -e $output_file ] 144 then 145 rm $output_file 146 fi 147 148 if [ -e $sorted_file ] 149 then 150 rm $sorted_file 151 fi 152 153 # Wipe the database. 154 cql_wipe 155 156 # Create the database 157 run_command \ 158 "${kea_admin}" db-init cql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" 159 assert_eq 0 "${EXIT_CODE}" "kea-admin db-init cql failed, expected exit code: %d, actual: %d" 160 161 # Insert the reference record. 162 # -1073741302 corresponds to 192.0.2.10 163 # -1073741301 corresponds to 192.0.2.11 164 # -1073741300 corresponds to 192.0.2.12 165 # 1430694930 corresponds to 2015-04-04 01:15:30 166 # 1433464245 corresponds to 2015-05-05 02:30:45 167 # 1436173267 corresponds to 2015-06-06 11:01:07 168 insert_cql="\ 169 INSERT INTO lease4 (address, hwaddr, client_id, valid_lifetime, expire, subnet_id, \ 170 fqdn_fwd, fqdn_rev, hostname, state, user_context) \ 171 VALUES (-1073741302,textAsBlob('20'),textAsBlob('30'),40,1430694930,50,true,true,'one.example.com', 0, '');\ 172 INSERT INTO lease4 (address, hwaddr, client_id, valid_lifetime, expire, subnet_id, \ 173 fqdn_fwd, fqdn_rev, hostname, state, user_context) \ 174 VALUES (-1073741301,NULL,textAsBlob('123'),40,1433464245,50,true,true,'', 1, '');\ 175 INSERT INTO lease4 (address, hwaddr, client_id, valid_lifetime, expire, subnet_id, \ 176 fqdn_fwd, fqdn_rev, hostname, state, user_context) \ 177 VALUES (-1073741300,textAsBlob('22'),NULL,40,1436173267,50,true,true,'three.example.com', 2, '');" 178 179 run_command \ 180 cql_execute "$insert_cql" 181 assert_eq 0 "${EXIT_CODE}" "insert into lease4 failed, expected exit code %d, actual %d" 182 183 # Dump lease4 to output_file. 184 run_command \ 185 "${kea_admin}" lease-dump cql -4 -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" -o $output_file 186 assert_eq 0 "${EXIT_CODE}" "kea-admin lease-dump -4 failed, expected exit code %d, actual %d" 187 188 # sort data so we can compare 189 sort -g "${output_file}" > "${sorted_file}" 190 191 # Compare the dump output to reference file, they should be identical. 192 run_command \ 193 cmp -s $sorted_file $ref_file 194 assert_eq 0 "${EXIT_CODE}" "dump file does not match reference file, expected exit code %d, actual %d" 195 196 # remove the output file. 197 rm $output_file 198 199 # remove the sorted file. 200 rm $sorted_file 201 202 # Wipe the database. 203 cql_wipe 204 205 test_finish 0 206} 207 208cql_lease6_dump_test() { 209 test_start "cql.lease6_dump_test" 210 211 test_dir="@abs_top_srcdir@/src/bin/admin/tests" 212 output_dir="@abs_top_builddir@/src/bin/admin/tests" 213 214 output_file="$output_dir/data/cql.lease6_dump_test.output.csv" 215 sorted_file="$output_dir/data/cql.lease6_dump_test.output.sorted.csv" 216 ref_file="$test_dir/data/cql.lease6_dump_test.reference.csv" 217 218 # Wipe out any residuals from prior failed runs. 219 if [ -e $output_file ] 220 then 221 rm $output_file 222 fi 223 224 if [ -e $sorted_file ] 225 then 226 rm $sorted_file 227 fi 228 229 # Wipe the database. 230 cql_wipe 231 232 # Create the database. 233 run_command \ 234 "${kea_admin}" db-init cql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" 235 assert_eq 0 "${EXIT_CODE}" "could not create database, expected exit code %d, actual %d" 236 237 # Insert the reference record. 238 # 1430694930 corresponds to 2015-04-04 01:15:30 239 # 1433464245 corresponds to 2015-05-05 02:30:45 240 # 1436173267 corresponds to 2015-06-06 11:01:07 241 insert_cql="\ 242 INSERT INTO lease6 (address, duid, valid_lifetime, expire, subnet_id, \ 243 pref_lifetime, lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, \ 244 hwaddr, hwtype, hwaddr_source, state, user_context) \ 245 VALUES ('2001:db8::10',textAsBlob('20'),30,1430694930,40,50,1,60,70,true,true, \ 246 'one.example.com',textAsBlob('80'),90,16,0,'');\ 247 INSERT INTO lease6 (address, duid, valid_lifetime, expire, subnet_id, \ 248 pref_lifetime, lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, \ 249 hwaddr, hwtype, hwaddr_source, state, user_context) \ 250 VALUES ('2001:db8::11',NULL,30,1433464245,40,50,1,60,70,true,true, \ 251 '',textAsBlob('80'),90,1,1,'');\ 252 INSERT INTO lease6 (address, duid, valid_lifetime, expire, subnet_id, \ 253 pref_lifetime, lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, \ 254 hwaddr, hwtype, hwaddr_source, state, user_context) \ 255 VALUES ('2001:db8::12',textAsBlob('21'),30,1436173267,40,50,1,60,70,true,true, \ 256 'three.example.com',textAsBlob('80'),90,4,2,'');" 257 258 run_command \ 259 cql_execute "$insert_cql" 260 assert_eq 0 "${EXIT_CODE}" "insert into lease6 failed, expected exit code %d, actual %d" 261 262 # Dump lease4 to output_file. 263 run_command \ 264 "${kea_admin}" lease-dump cql -6 -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" -o $output_file 265 assert_eq 0 "${EXIT_CODE}" "kea-admin lease-dump -6 failed, status code %d" 266 267 # sort data so we can compare 268 sort -g "${output_file}" > "${sorted_file}" 269 270 # Compare the dump output to reference file, they should be identical. 271 run_command \ 272 cmp -s $sorted_file $ref_file 273 assert_eq 0 "${EXIT_CODE}" "dump file does not match reference file, expected exit code %d, actual %d" 274 275 # remove the output file. 276 rm $output_file 277 278 # remove the sorted file. 279 rm $sorted_file 280 281 # Wipe the database. 282 cql_wipe 283 284 test_finish 0 285} 286 287# Upgrades an existing schema to a target newer version 288# param target_version - desired schema version as "major.minor" 289cql_upgrade_schema_to_version() { 290 target_version=$1 291 292 # Check if the scripts directory exists at all. 293 if [ ! -d ${db_scripts_dir}/cql ]; then 294 log_error "Invalid scripts directory: ${db_scripts_dir}/cql" 295 exit 1 296 fi 297 298 # Check if there are any files in it 299 num_files=$(find ${db_scripts_dir}/cql/upgrade*.sh -type f | wc -l) 300 if [ "${num_files}" -eq 0 ]; then 301 log_error "No scripts in ${db_scripts_dir}/cql?" 302 exit 1 303 fi 304 305 version=$(cql_version) 306 for script in "${db_scripts_dir}"/cql/upgrade*.sh 307 do 308 if [ "${version}" = "${target_version}" ] 309 then 310 break 311 fi 312 313 echo "Processing $script file..." 314 "${script}" -u "${db_user}" -p "${db_password}" -k "${db_name}" 315 version=$(cql_version) 316 done 317 318 echo "Schema upgraded to $version" 319} 320 321# Verifies that you can upgrade from an earlier version and 322# that unused subnet ID values in hosts and options tables are 323# converted to -1 324cql_unused_subnet_id_test() { 325 test_start "cql.unused_subnet_id_test" 326 327 # Let's wipe the whole database 328 cql_wipe 329 330 # We need to create an older database with lease data so we can 331 # verify the upgrade mechanisms which convert subnet id values 332 # 333 # Initialize database to schema 1.0. 334 cql_execute_script "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.cql" 335 336 # Now upgrade to schema 2.0, the version just before global HRs 337 cql_upgrade_schema_to_version 2.0 338 339 # Now we need insert some hosts to "migrate" for both v4 and v6 340 qry="\ 341 INSERT INTO host_reservations (id, host_identifier_type, host_identifier, host_ipv4_subnet_id, host_ipv6_subnet_id, hostname, option_subnet_id) \ 342 VALUES (1, 0, textAsBlob('0123456'), 0, 0, 'host0', 0);\ 343 INSERT INTO host_reservations (id, host_identifier_type, host_identifier, host_ipv4_subnet_id, host_ipv6_subnet_id, hostname, option_subnet_id) \ 344 VALUES (2, 0, textAsBlob('1123456'), 4, 0, 'Host1', 4);\ 345 INSERT INTO host_reservations (id, host_identifier_type, host_identifier, host_ipv4_subnet_id, host_ipv6_subnet_id, hostname, option_subnet_id) \ 346 VALUES (3, 0, textAsBlob('2123456'), 0, 6, 'host2', 6);\ 347 INSERT INTO host_reservations (id, host_identifier_type, host_identifier, host_ipv4_subnet_id, host_ipv6_subnet_id, hostname, option_subnet_id) \ 348 VALUES (4, 0, textAsBlob('3123456'), 4, 6, 'Host3', 0);\ 349 INSERT INTO host_reservations (id, host_identifier_type, host_identifier, host_ipv4_subnet_id, host_ipv6_subnet_id, hostname, option_subnet_id) \ 350 VALUES (5, 0, textAsBlob('3123456'), -1, 6, 'host3', 6);" 351 352 run_command \ 353 cql_execute "$qry" 354 assert_eq 0 "${EXIT_CODE}" "insert hosts failed, expected exit code: %d, actual: %d" 355 356 # Ok, we have a 2.0 database with hosts and options. Let's upgrade it. 357 run_command \ 358 "${kea_admin}" db-upgrade cql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" 359 360 # Upgrade should succeed 361 assert_eq 0 "${EXIT_CODE}" "upgrade failed" 362 363 test_dir="@abs_top_srcdir@/src/bin/admin/tests" 364 ref_file="$test_dir/data/cql.subnet_id_test.reference.csv" 365 366 output_dir="@abs_top_builddir@/src/bin/admin/tests" 367 export_file="$output_dir/data/cql.subnet_id_test.csv" 368 sorted_file="$output_dir/data/cql.subnet_id_test.sorted.csv" 369 370 # Fetch host_reservation data for comparison 371 echo "Exporting host_reservation data to $export_file ..." 372 373 qry="\ 374 SELECT id, host_ipv4_subnet_id, host_ipv6_subnet_id, hostname, lower_case_hostname, option_subnet_id\ 375 FROM hosts WHERE id IN (1,2,3,4,5) ALLOW FILTERING;" 376 377 run_command \ 378 cql_execute "$qry" 379 printf '%s\n' "${OUTPUT}" > "${export_file}" 380 assert_eq 0 "${EXIT_CODE}" "insert hosts failed, expected exit code: %d, actual: %d" 381 382 # sort data so we can compare 383 grep -F '|' "${export_file}" | sort -V | tr -d " " | sed 's/|/,/g' > $sorted_file 384 385 # Compare the dump output to reference file, they should be identical. 386 run_command \ 387 cmp -s $sorted_file $ref_file 388 assert_eq 0 "${EXIT_CODE}" "export file does not match reference file, expected exit code %d, actual %d" 389 390 # remove the output file. 391 rm $export_file 392 393 # remove the sorted file. 394 rm $sorted_file 395 396 # Wipe the database. 397 cql_wipe 398 399 # Report test success. 400 test_finish 0 401} 402 403# Verifies that you can upgrade from an earlier version and 404# that all hosts and options from old host_reservation table (version 3.0) are 405# converted to new schema (version 4.0) with new key partition key and are 406# moved to new hosts table 407cql_upgrade_hosts_test() { 408 test_start "cql.update_hosts_test" 409 410 # Let's wipe the whole database 411 cql_wipe 412 413 # We need to create an older database with lease data so we can 414 # verify the upgrade mechanisms which convert subnet id values 415 # 416 # Initialize database to schema 1.0. 417 cql_execute_script "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.cql" 418 419 # Now upgrade to schema 3.0, the version just before global HRs 420 cql_upgrade_schema_to_version 3.0 421 422 # Now we need insert some hosts to "migrate" for both v4 and v6 423 test_dir="@abs_top_srcdir@/src/bin/admin/tests" 424 data_file="$test_dir/data/cql.hosts_data_test.csv" 425 ref_file="$test_dir/data/cql.hosts_data_test.reference.csv" 426 427 output_dir="@abs_top_builddir@/src/bin/admin/tests" 428 export_file="$output_dir/data/cql.hosts_test.csv" 429 sorted_file="$output_dir/data/cql.hosts_test.sorted.csv" 430 431 # Fetch host_reservation data for comparison 432 echo "Exporting host_reservation data to $export_file ..." 433 434 qry="\ 435 COPY host_reservations \ 436 (id, host_identifier, host_identifier_type, host_ipv4_subnet_id, \ 437 host_ipv6_subnet_id, host_ipv4_address, host_ipv4_next_server, \ 438 host_ipv4_server_hostname, host_ipv4_boot_file_name, hostname, \ 439 auth_key, user_context, host_ipv4_client_classes, \ 440 host_ipv6_client_classes, reserved_ipv6_prefix_address, \ 441 reserved_ipv6_prefix_length, reserved_ipv6_prefix_address_type, \ 442 iaid, option_universe, option_code, option_value, \ 443 option_formatted_value, option_space, option_is_persistent, \ 444 option_client_class, option_subnet_id, option_user_context, \ 445 option_scope_id) \ 446 FROM '$data_file'" 447 448 run_command \ 449 cql_execute "$qry" 450 assert_eq 0 "${EXIT_CODE}" "insert hosts failed, expected exit code: %d, actual: %d" 451 452 # Ok, we have a 3.0 database with hosts and options. Let's upgrade it. 453 run_command \ 454 "${kea_admin}" db-upgrade cql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" 455 456 # Upgrade should succeed 457 assert_eq 0 "${EXIT_CODE}" "upgrade failed" 458 459 qry="\ 460 COPY hosts \ 461 (id, key, host_identifier, host_identifier_type, host_ipv4_subnet_id, \ 462 host_ipv6_subnet_id, host_ipv4_address, host_ipv4_next_server, \ 463 host_ipv4_server_hostname, host_ipv4_boot_file_name, hostname, \ 464 lower_case_hostname, auth_key, user_context, \ 465 host_ipv4_client_classes, \ 466 host_ipv6_client_classes, reserved_ipv6_prefix_address, \ 467 reserved_ipv6_prefix_length, reserved_ipv6_prefix_address_type, \ 468 iaid, option_universe, option_code, option_value, \ 469 option_formatted_value, option_space, option_is_persistent, \ 470 option_client_class, option_subnet_id, option_user_context, \ 471 option_scope_id) \ 472 TO '$export_file'" 473 474 run_command \ 475 cql_execute "$qry" 476 assert_eq 0 "${EXIT_CODE}" "insert hosts failed, expected exit code: %d, actual: %d" 477 478 # sort data so we can compare 479 sort -V "${export_file}" > "${sorted_file}" 480 481 # Compare the dump output to reference file, they should be identical. 482 run_command \ 483 cmp -s $sorted_file $ref_file 484 assert_eq 0 "${EXIT_CODE}" "export file does not match reference file, expected exit code %d, actual %d" 485 486 # remove the output file. 487 rm $export_file 488 489 # remove the sorted file. 490 rm $sorted_file 491 492 # Wipe the database. 493 cql_wipe 494 495 # Report test success. 496 test_finish 0 497} 498 499 500# Run tests. 501cql_db_init_test 502cql_db_version_test 503cql_upgrade_test 504cql_lease4_dump_test 505cql_lease6_dump_test 506cql_unused_subnet_id_test 507cql_upgrade_hosts_test 508