dhcp_test_lib.sh.in 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. # Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
  2. #
  3. # Permission to use, copy, modify, and/or distribute this software for any
  4. # purpose with or without fee is hereby granted, provided that the above
  5. # copyright notice and this permission notice appear in all copies.
  6. #
  7. # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. # AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. # PERFORMANCE OF THIS SOFTWARE.
  14. # A list of Kea processes, mainly used by the cleanup functions.
  15. KEA_PROCS="b10-dhcp4 b10-dhcp6 b10-dhcp-ddns"
  16. ### Logging functions ###
  17. # Prints error message.
  18. test_lib_error() {
  19. local s=${1} # Error message.
  20. local no_new_line=${2} # If specified, the message not terminated with
  21. # new line.
  22. printf "ERROR/test_lib: %s" "${s}"
  23. if [ -z ${no_new_line} ]; then
  24. printf "%s" "\n"
  25. fi
  26. }
  27. # Prints info message.
  28. test_lib_info() {
  29. local s=${1} # Info message.
  30. local no_new_line=${2} # If specified, the message is not terminated with
  31. # new line.
  32. printf "INFO/test_lib: %s" "${s}"
  33. if [ -z ${no_new_line} ]; then
  34. printf "%s" "\n"
  35. fi
  36. }
  37. ### Assertions ###
  38. # Assertion that checks if two numbers are equal.
  39. # If numbers are not equal, the mismatched values are presented and the
  40. # detailed error is printed. The detailed error must use the printf
  41. # formatting like this:
  42. # "Expected that some value 1 %d is equal to some other value %d".
  43. assert_eq() {
  44. val1=${1} # Reference value
  45. val2=${2} # Tested value
  46. detailed_err=${3} # Detailed error format string
  47. # If nothing found, present an error an exit.
  48. if [ ${val1} -ne ${val2} ]; then
  49. printf "Assertion failure: ${val1} != ${val2}, for val1=${val1}, val2=${val2}\n"
  50. printf "${detailed_err}\n" ${val1} ${val2}
  51. clean_exit 1
  52. fi
  53. }
  54. # Assertion that checks if one string contains another string.
  55. # If assertion fails, both strings are displayed and the detailed
  56. # error is printed. The detailed error must use the printf formatting
  57. # like this:
  58. # "Expected some string to contain this string: %s".
  59. assert_string_contains() {
  60. pattern="${1}" # Substring or awk pattern
  61. text="${2}" # Text to be searched for substring
  62. detailed_err="${3}" # Detailed error format string
  63. # Search for a pattern
  64. match=$( printf "%s" "${text}" | awk /"${pattern}"/ )
  65. # If nothing found, present an error and exit.
  66. if [ -z "${match}" ]; then
  67. printf "Assertion failure: \n\"%s\"\n\ndoesn't contain pattern:\n
  68. \"%s\"\n\n" "${text}" "${pattern}"
  69. printf "${detailed_err}\n" "\"${pattern}\""
  70. clean_exit 1
  71. fi
  72. }
  73. # Begins a test by prining its name.
  74. test_start() {
  75. TEST_NAME=${1}
  76. if [ -z ${TEST_NAME} ]; then
  77. test_lib_error "test_start requires test name as an argument"
  78. clean_exit 1
  79. fi
  80. printf "\nSTART TEST ${TEST_NAME}\n"
  81. }
  82. # Prints test result an cleans up after the test.
  83. test_finish() {
  84. local exit_code=${1} # Exit code to be returned by the exit function.
  85. if [ ${exit_code} -eq 0 ]; then
  86. cleanup
  87. printf "PASSED ${TEST_NAME}\n\n"
  88. else
  89. # Dump log file if exists for debugging purposes.
  90. if [ -s ${LOG_FILE} ]; then
  91. printf "Log file dump:\n"
  92. cat ${LOG_FILE}
  93. fi
  94. cleanup
  95. printf "FAILED ${TEST_NAME}\n\n"
  96. fi
  97. }
  98. # Stores the configuration specified as a parameter in the configuration
  99. # file which name has been set in the ${CFG_FILE} variable.
  100. create_config() {
  101. local cfg="${1}" # Configuration string.
  102. if [ -z ${CFG_FILE} ]; then
  103. test_lib_error "create_config requires CFG_FILE variable be set"
  104. clean_exit 1
  105. elif [ -z "${cfg}" ]; then
  106. test_lib_error "create_config requires argument holding a configuration"
  107. clean_exit 1
  108. fi
  109. printf "Creating Kea configuration file: %s.\n" ${CFG_FILE}
  110. printf "%b" ${cfg} > ${CFG_FILE}
  111. }
  112. # Stores the keactrl configuration specified as a parameter in the
  113. # configuration file which name has been set in the ${KEACTRL_CFG_FILE}
  114. # variable.
  115. create_keactrl_config() {
  116. local cfg="${1}" # Configuration string.
  117. if [ -z ${KEACTRL_CFG_FILE} ]; then
  118. test_lib_error "create_keactrl_config requires KEACTRL_CFG_FILE \
  119. variable be set"
  120. clean_exit 1
  121. elif [ -z "${cfg}" ]; then
  122. test_lib_error "create_keactrl_config requires argument holding a \
  123. configuration"
  124. clean_exit 1
  125. fi
  126. printf "Creating keactrl configuration file: %s.\n" ${KEACTRL_CFG_FILE}
  127. printf "%b" ${cfg} > ${KEACTRL_CFG_FILE}
  128. }
  129. # Sets Kea logger to write to the file specified by the global value
  130. # ${LOG_FILE}.
  131. set_logger() {
  132. if [ -z ${LOG_FILE} ]; then
  133. test_lib_error "set_logger requies LOG_FILE variable be set"
  134. clean_exit 1
  135. fi
  136. printf "Kea log will be stored in %s.\n" ${LOG_FILE}
  137. export B10_LOGGER_DESTINATION=${LOG_FILE}
  138. }
  139. # Returns the number of running process pids and the list of pids.
  140. # Return values:
  141. # _GET_PIDS: holds space separated list of pids.
  142. # _GET_PIDS_NUM: holds the number pids.
  143. get_pids() {
  144. local proc_name=${1} # Process name
  145. if [ -z ${proc_name} ]; then
  146. test_lib_error "get_pids requires process name"
  147. clean_exit 1
  148. fi
  149. _GET_PIDS=$( ps axwwo pid,command | grep ${proc_name} \
  150. | grep -v grep | awk '{print $1}' )
  151. _GET_PIDS_NUM=$( printf "%s" "${_GET_PIDS}" | wc -w | awk '{print $1}' )
  152. }
  153. # Returns the number of occurrences of the Kea log message in the log file.
  154. # Return value:
  155. # _GET_LOG_MESSAGES: number of log message occurrences.
  156. get_log_messages() {
  157. local msg="${1}" # Message id, e.g. DHCP6_SHUTDOWN
  158. if [ -z ${msg} ]; then
  159. test_lib_error "get_log_messages require message identifier"
  160. clean_exit 1
  161. fi
  162. _GET_LOG_MESSAGES=0
  163. # If log file is not present, the number of occurrences is 0.
  164. if [ -s ${LOG_FILE} ]; then
  165. # Grep log file for the logger message occurrences.
  166. _GET_LOG_MESSAGES=$( grep -o ${msg} ${LOG_FILE} | wc -w )
  167. # Remove whitespaces.
  168. ${_GET_LOG_MESSAGES##*[! ]}
  169. fi
  170. }
  171. # Returns the number of server configurations performed so far. Also
  172. # returns the number of configuration errors.
  173. # Return values:
  174. # _GET_RECONFIGS: number of configurations so far.
  175. # _GET_RECONFIG_ERRORS: number of configuration errors.
  176. get_reconfigs() {
  177. # Grep log file for CONFIG_COMPLETE occurences. There should
  178. # be one occurence per (re)configuration.
  179. _GET_RECONFIGS=$( grep -o CONFIG_COMPLETE ${LOG_FILE} | wc -w )
  180. # Grep log file for CONFIG_LOAD_FAIL to check for configuration
  181. # failures.
  182. _GET_RECONFIG_ERRORS=$( grep -o CONFIG_LOAD_FAIL ${LOG_FILE} | wc -w )
  183. # Remove whitespaces
  184. ${_GET_RECONFIGS##*[! ]}
  185. ${_GET_RECONFIG_ERRORS##*[! ]}
  186. }
  187. # Performs cleanup after test.
  188. # It shuts down running Kea processes and removes temporary files.
  189. # The location of the log file and the configuration files should be set
  190. # in the ${LOG_FILE}, ${CFG_FILE} and ${KEACTRL_CFG_FILE} variables
  191. # recpectively, prior to calling this function.
  192. cleanup() {
  193. # KEA_PROCS holds the name of all Kea processes. Shut down each
  194. # of them if running.
  195. for proc_name in ${KEA_PROCS}
  196. do
  197. get_pids ${proc_name}
  198. # Shut down running Kea processes.
  199. for pid in ${_GET_PIDS}
  200. do
  201. printf "Shutting down Kea proccess having pid %d.\n" ${pid}
  202. kill -9 ${pid}
  203. done
  204. done
  205. # Remove temporary files.
  206. rm -rf ${LOG_FILE}
  207. rm -rf ${CFG_FILE}
  208. rm -rf ${KEACTRL_CFG_FILE}
  209. }
  210. # Exists the test in the clean way.
  211. # It peformes the cleanup and prints whether the test has passed or failed.
  212. # If a test fails, the Kea log is dumped.
  213. clean_exit() {
  214. exit_code=${1} # Exit code to be returned by the exit function.
  215. case ${exit_code} in
  216. ''|*[!0-9]*)
  217. test_lib_error "argument passed to clean_exit must be a number" ;;
  218. esac
  219. # Print test result and perform a cleanup
  220. test_finish ${exit_code}
  221. exit ${exit_code}
  222. }
  223. # Starts Kea process in background using a configuration file specified
  224. # in the global variable ${CFG_FILE}.
  225. start_kea() {
  226. local bin=${1}
  227. if [ -z ${bin} ]; then
  228. test_lib_error "binary name must be specified for start_kea"
  229. clean_exit 1
  230. fi
  231. printf "Running command %s.\n" "\"${bin} -c ${CFG_FILE}\""
  232. ${bin} -c ${CFG_FILE} &
  233. }
  234. # Waits with timeout for Kea to start.
  235. # This function repeatedly checs if the Kea log file has been created
  236. # and is non-empty. If it is, the function assumes that Kea has started.
  237. # It doesn't check the contents of the log file though.
  238. # If the log file doesn't exist the function sleeps for a second and
  239. # checks again. This is repeated until timeout is reached or non-empty
  240. # log file is found. If timeout is reached, the function reports an
  241. # error.
  242. # Return value:
  243. # _WAIT_FOR_KEA: 0 if Kea hasn't started, 1 otherwise
  244. wait_for_kea() {
  245. local timeout=${1} # Desired timeout in seconds.
  246. case ${timeout} in
  247. ''|*[!0-9]*)
  248. test_lib_error "argument passed to wait_for_kea must be a number"
  249. clean_exit 1 ;;
  250. esac
  251. local loops=0 # Loops counter
  252. _WAIT_FOR_KEA=0
  253. test_lib_info "wait_for_kea " "skip-new-line"
  254. while [ ! -s ${LOG_FILE} ] && [ ${loops} -le ${timeout} ]; do
  255. printf "."
  256. sleep 1
  257. loops=$( expr $loops + 1 )
  258. done
  259. printf "\n"
  260. if [ ${loops} -le ${timeout} ]; then
  261. _WAIT_FOR_KEA=1
  262. fi
  263. }
  264. # Waits for a specific message to occur in the Kea log file.
  265. # This function is called when the test expects specific message
  266. # to show up in the log file as a result of some action that has
  267. # been taken. Typically, the test expects that the message
  268. # is logged when the SIGHUP or SIGTERM signal has been sent to the
  269. # Kea process.
  270. # This function waits a specified number of seconds for the number
  271. # of message occurrences to show up. If the expected number of
  272. # message doesn't occur, the error status is returned.
  273. # Return value:
  274. # _WAIT_FOR_MESSAGE: 0 if the message hasn't occured, 1 otherwise.
  275. wait_for_message() {
  276. local timeout=${1} # Expected timeout value in seconds.
  277. local message="${2}" # Expected message id.
  278. local occurrences=${3} # Number of expected occurrences.
  279. # Validate timeout
  280. case ${timeout} in
  281. ''|*[!0-9]*)
  282. test_lib_error "argument timeout passed to wait_for_message must \
  283. be a number"
  284. clean_exit 1 ;;
  285. esac
  286. # Validate message
  287. if [ -z ${message} ]; then
  288. test_lib_error "message id is a required argument for wait_for_message"
  289. clean_exit 1
  290. fi
  291. # Validate occurrences
  292. case ${occurrences} in
  293. ''|*[!0-9]*)
  294. test_lib_error "argument occurrences passed to wait_for_message \
  295. must be a number"
  296. clean_exit 1 ;;
  297. esac
  298. local loops=0 # Number of loops performed so far.
  299. _WAIT_FOR_MESSAGE=0
  300. test_lib_info "wait_for_message ${message}: " "skip-new-line"
  301. # Check if log file exists and if we reached timeout.
  302. while [ ${loops} -le ${timeout} ]; do
  303. printf "."
  304. # Check if the message has been logged.
  305. get_log_messages ${message}
  306. if [ ${_GET_LOG_MESSAGES} -ge ${occurrences} ]; then
  307. printf "\n"
  308. _WAIT_FOR_MESSAGE=1
  309. return
  310. fi
  311. # Message not recorded. Keep going.
  312. sleep 1
  313. loops=$( expr ${loops} + 1 )
  314. done
  315. printf "\n"
  316. # Timeout.
  317. }
  318. # Waits for server to be down.
  319. # Return value:
  320. # _WAIT_FOR_SERVER_DOWN: 1 if server is down, 0 if timeout occured and the
  321. # server is still running.
  322. wait_for_server_down() {
  323. local timeout=${1} # Timeout specified in seconds.
  324. local proc_name=${2} # Server process name.
  325. case ${timeout} in
  326. ''|*[!0-9]*)
  327. test_lib_error "argument passed to wait_for_server_down must be a number"
  328. clean_exit 1 ;;
  329. esac
  330. local loops=0 # Loops counter
  331. _WAIT_FOR_SERVER_DOWN=0
  332. test_lib_info "wait_for_server_down ${proc_name}: " "skip-new-line"
  333. while [ ${loops} -le ${timeout} ]; do
  334. printf "."
  335. get_pids ${proc_name}
  336. if [ ${_GET_PIDS_NUM} -eq 0 ]; then
  337. printf "\n"
  338. _WAIT_FOR_SERVER_DOWN=1
  339. return
  340. fi
  341. sleep 1
  342. loops=$( expr $loops + 1 )
  343. done
  344. printf "\n"
  345. }
  346. # Sends specified signal to the Kea process.
  347. send_signal() {
  348. local sig=${1} # Signal number.
  349. local proc_name=${2} # Process name
  350. # Validate signal
  351. case ${sig} in
  352. ''|*[!0-9]*)
  353. test_lib_error "signal number passed to send_signal \
  354. must be a number"
  355. clean_exit 1 ;;
  356. esac
  357. # Validate process name
  358. if [ -z ${proc_name} ]; then
  359. test_lib_error "send_signal requires process name be passed as argument"
  360. clean_exit 1
  361. fi
  362. # Get Kea pid.
  363. get_pids ${proc_name}
  364. if [ ${_GET_PIDS_NUM} -ne 1 ]; then
  365. printf "ERROR: expected one Kea process to be started.\
  366. Found %d processes started.\n" ${_GET_PIDS_NUM}
  367. clean_exit 1
  368. fi
  369. printf "Sending signal ${sig} to Kea process (pid=%s).\n" ${_GET_PIDS}
  370. # Actually send a signal.
  371. kill -${sig} ${_GET_PIDS}
  372. }