kea-admin.in 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. #!/bin/sh
  2. # Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
  3. #
  4. # This Source Code Form is subject to the terms of the Mozilla Public
  5. # License, v. 2.0. If a copy of the MPL was not distributed with this
  6. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  7. # This is kea-admin script that conducts administrative tasks on the Kea
  8. # installation. Currently supported operations are:
  9. #
  10. # - lease database init
  11. # - lease database version check
  12. # - lease database version upgrade
  13. # Get the location of the kea-admin scripts
  14. prefix=@prefix@
  15. SCRIPTS_DIR_DEFAULT=@datarootdir@/@PACKAGE@/scripts
  16. scripts_dir=${SCRIPTS_DIR_DEFAULT}
  17. # These are the default parameters. They will likely not work in any
  18. # specific deployment.
  19. db_user="keatest"
  20. db_password="keatest"
  21. db_name="keatest"
  22. # lease dump parameters
  23. dump_type=0
  24. dump_file=""
  25. dump_qry=""
  26. # Include utilities. Use installed version if available and
  27. # use build version if it isn't.
  28. if [ -e @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh ]; then
  29. . @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh
  30. else
  31. . @abs_top_srcdir@/src/bin/admin/admin-utils.sh
  32. fi
  33. # Prints out usage version.
  34. usage() {
  35. printf "kea-admin @PACKAGE_VERSION@\n"
  36. printf "\n"
  37. printf "This is a kea-admin script that conducts administrative tasks on\n"
  38. printf "the Kea installation.\n"
  39. printf "\n"
  40. printf "Usage: $0 COMMAND BACKEND [parameters]\n"
  41. printf "\n"
  42. printf "COMMAND: Currently supported operations are:\n"
  43. printf "\n"
  44. printf " - lease-init: Initalizes new lease database. Useful for first time installation.\n"
  45. printf " - lease-version: Checks version of the existing lease database scheme. Useful\n"
  46. printf " - for checking lease DB version when preparing for an upgrade.\n"
  47. printf " - lease-upgrade: Upgrades your lease database scheme\n"
  48. printf " - lease-dump: Dump current leases to a CSV file\n"
  49. printf "\n"
  50. printf "BACKEND - one of the supported backends: memfile|mysql|pgsql|cql\n"
  51. printf "\n"
  52. printf "PARAMETERS: Parameters are optional in general, but may be required\n"
  53. printf " for specific operation.\n"
  54. printf " -u or --user name - specifies username when connecting to a database\n"
  55. printf " -p or --password pass - specifies a password when connecting to a database\n"
  56. printf " -n or --name database - specifies a database name to connect to\n"
  57. printf " -d or --directory - path to upgrade scripts (default: ${SCRIPTS_DIR_DEFAULT})\n"
  58. printf "\n"
  59. printf " Parameters specific to lease-dump:\n"
  60. printf " -4 to dump IPv4 leases to file\n"
  61. printf " -6 to dump IPv6 leases to file\n"
  62. printf " -o or --output - name of file to which leases will be dumped\n"
  63. }
  64. ### Logging functions ###
  65. # Logs message at the error level.
  66. # Takes one parameter that is printed as is.
  67. log_error() {
  68. printf "ERROR/kea-admin: ${1}\n"
  69. }
  70. # Logs message at the warning level.
  71. # Takes one parameter that is printed as is.
  72. log_warning() {
  73. printf "WARNING/kea-admin: ${1}\n"
  74. }
  75. # Logs message at the info level.
  76. # Takes one parameter that is printed as is.
  77. log_info() {
  78. printf "INFO/kea-admin: ${1}\n"
  79. }
  80. ### Convenience functions ###
  81. # Checks if the value is in the list. An example usage of this function
  82. # is to determine whether the kea-admin command belongs to the list of
  83. # supported commands.
  84. is_in_list() {
  85. local member=${1} # Value to be checked
  86. local list="${2}" # Comma separated list of items
  87. _inlist=0 # Return value: 0 if not in list, 1 otherwise.
  88. if [ -z ${member} ]; then
  89. log_error "missing ${class}"
  90. fi
  91. # Iterate over all items on the list and compare with the member.
  92. # If they match, return, otherwise log error and exit.
  93. for item in ${list}
  94. do
  95. if [ ${item} = ${member} ]; then
  96. _inlist=1
  97. return
  98. fi
  99. done
  100. }
  101. ### Functions that implement database initialization commands
  102. memfile_init() {
  103. # @todo Implement this as part of #3601
  104. log_error "NOT IMPLEMENTED"
  105. exit 1
  106. }
  107. # Initializes a new, empty MySQL database.
  108. # It essentially calls scripts/mysql/dhcpdb_create.mysql script, with
  109. # some extra sanity checks. It will refuse to use it if there are any
  110. # existing tables. It's better safe than sorry.
  111. mysql_init() {
  112. printf "Checking if there is a database initialized already. Please ignore errors.\n"
  113. # Let's try to count the number of tables. Anything above 0 means that there
  114. # is some database in place. If there is anything, we abort. Note that
  115. # mysql may spit out connection or access errors to stderr, we ignore those.
  116. # We should not hide them as they may give hints to user what is wrong with
  117. # his setup.
  118. #
  119. RESULT=`mysql_execute "SHOW TABLES;"`
  120. ERRCODE=$?
  121. if [ $ERRCODE -ne 0 ]
  122. then
  123. log_error "mysql_init table query failed, mysql status = $ERRCODE"
  124. exit 1
  125. fi
  126. COUNT=`echo $RESULT | wc -w`
  127. if [ $COUNT -gt 0 ]; then
  128. # Let't start with a new line. mysql could have printed something out.
  129. printf "\n"
  130. log_error "Expected empty database $db_name, but there are $COUNT tables: \n$RESULT. Aborting."
  131. exit 1
  132. fi
  133. printf "Initializing database using script %s\n" $scripts_dir/mysql/dhcpdb_create.mysql
  134. mysql -B --user=$db_user --password=$db_password $db_name < $scripts_dir/mysql/dhcpdb_create.mysql
  135. ERRCODE=$?
  136. printf "mysql returned status code $ERRCODE\n"
  137. if [ "$ERRCODE" -eq 0 ]; then
  138. printf "Lease DB version reported after initialization: "
  139. mysql_version
  140. printf "\n"
  141. fi
  142. exit $ERRCODE
  143. }
  144. pgsql_init() {
  145. printf "Checking if there is a database initialized already. Please ignore errors.\n"
  146. # Let's try to count the number of tables. Anything above 0 means that there
  147. # is some database in place. If there is anything, we abort.
  148. RESULT=`pgsql_execute "\d"`
  149. ERRCODE=$?
  150. if [ "$ERRCODE" -ne 0 ]; then
  151. log_error "pgsql_init: table query failed, status code: $ERRCODE?"
  152. exit 1
  153. fi
  154. COUNT=`echo "$RESULT" | wc -w`
  155. if [ $COUNT -gt 0 ]; then
  156. printf "\n"
  157. log_error "Expected empty database $db_name, but the following tables are present \n$RESULT. Aborting."
  158. exit 2
  159. fi
  160. init_script="$scripts_dir/pgsql/dhcpdb_create.pgsql"
  161. printf "Initializing database using script %s\n" $init_script
  162. RESULT=`pgsql_execute_script $init_script`
  163. ERRCODE=$?
  164. if [ "$ERRCODE" -ne 0 ]; then
  165. log_error "Database initialization failed, status code: $ERRCODE?"
  166. exit 1
  167. fi
  168. version=`pgsql_version`
  169. printf "Lease DB version reported after initialization: $version\n"
  170. exit 0
  171. }
  172. cql_init() {
  173. printf "Checking if there is a database initialized already... Please ignore errors.\n"
  174. result=`cql_execute "USE $db_name; DESCRIBE tables;"`
  175. if [ "$result"="<empty>" ]; then
  176. printf "Creating and initializing tables using script %s...\n" $scripts_dir/cql/dhcpdb_create.cql
  177. cql_execute_script $scripts_dir/cql/dhcpdb_create.cql
  178. else
  179. log_error "Expected empty database $db_name, but the following tables are present \n$result. Aborting."
  180. exit 2
  181. fi
  182. version=`cql_version`
  183. printf "Lease DB version reported after initialization: $version\n"
  184. exit 0
  185. }
  186. ### Functions that implement database version checking commands
  187. memfile_version() {
  188. # @todo Implement this as part of #3601
  189. log_error "NOT IMPLEMENTED"
  190. exit 1
  191. }
  192. ### Functions used for upgrade
  193. memfile_upgrade() {
  194. # @todo Implement this as part of #3601
  195. log_error "NOT IMPLEMENTED"
  196. exit 1
  197. }
  198. # Upgrades existing MySQL database installation. The idea is that
  199. # it will go over all upgrade scripts from (prefix)/share/kea/scripts/mysql
  200. # and run them one by one. They will be named properly, so they will
  201. # be run in order.
  202. #
  203. # This function prints version before and after upgrade.
  204. mysql_upgrade() {
  205. printf "Lease DB version reported before upgrade: "
  206. mysql_version
  207. printf "\n"
  208. # Check if the scripts directory exists at all.
  209. if [ ! -d ${scripts_dir}/mysql ]; then
  210. log_error "Invalid scripts directory: ${scripts_dir}/mysql"
  211. exit 1
  212. fi
  213. # Check if there are any files in it
  214. num_files=$(find ${scripts_dir}/mysql/upgrade*.sh -type f | wc -l)
  215. if [ $num_files -eq 0 ]; then
  216. log_error "No scripts in ${scripts_dir}/mysql or the directory is not readable or does not have any upgrade* scripts."
  217. exit 1
  218. fi
  219. for script in ${scripts_dir}/mysql/upgrade*.sh
  220. do
  221. echo "Processing $script file..."
  222. sh ${script} --user=${db_user} --password=${db_password} ${db_name}
  223. done
  224. printf "Lease DB version reported after upgrade: "
  225. mysql_version
  226. printf "\n"
  227. }
  228. pgsql_upgrade() {
  229. version=`pgsql_version`
  230. printf "Lease DB version reported before upgrade: $version\n"
  231. # Check if the scripts directory exists at all.
  232. if [ ! -d ${scripts_dir}/pgsql ]; then
  233. log_error "Invalid scripts directory: ${scripts_dir}/pgsql"
  234. exit 1
  235. fi
  236. # Check if there are any files in it
  237. num_files=$(find ${scripts_dir}/pgsql/upgrade*.sh -type f | wc -l)
  238. if [ $num_files -eq 0 ]; then
  239. log_error "No scripts in ${scripts_dir}/pgsql or the directory is not readable or does not have any upgrade* scripts."
  240. exit 1
  241. fi
  242. # Postgres psql does not accept pw on command line, but can do it
  243. # thru an env
  244. export PGPASSWORD=$db_password
  245. for script in ${scripts_dir}/pgsql/upgrade*.sh
  246. do
  247. echo "Processing $script file..."
  248. sh ${script} -U ${db_user} -d ${db_name}
  249. done
  250. version=`pgsql_version`
  251. printf "Lease DB version reported after upgrade: $version\n"
  252. exit 0
  253. }
  254. cql_upgrade() {
  255. version=`cql_version`
  256. printf "Lease DB version reported before upgrade: $version\n"
  257. # Check if the scripts directory exists at all.
  258. if [ ! -d ${scripts_dir}/cql ]; then
  259. log_error "Invalid scripts directory: ${scripts_dir}/cql"
  260. exit 1
  261. fi
  262. # Check if directory is readable.
  263. if [ ! -r ${scripts_dir}/cql ]; then
  264. log_error "Directory is not readable: ${scripts_dir}/cql"
  265. exit 1
  266. fi
  267. # Check if there are upgrade scripts.
  268. files=$(find ${scripts_dir}/cql/upgrade*.sh -type f)
  269. if [ $? -eq 0 ]; then # Upgrade scripts are present.
  270. for script in ${scripts_dir}/cql/upgrade*.sh
  271. do
  272. echo "Processing $script file..."
  273. sh ${script} -u ${db_user} -p ${db_password} -k ${db_name}
  274. done
  275. else
  276. echo "No upgrade script available."
  277. fi
  278. version=`cql_version`
  279. printf "Lease DB version reported after upgrade: $version\n"
  280. exit 0
  281. }
  282. # Utility function which tests if the given file exists and
  283. # if so notifies the user and provides them the opportunity
  284. # to abort the current command.
  285. check_file_overwrite () {
  286. local file=$1
  287. if [ -e ${file} ]
  288. then
  289. echo "Output file, $file, exists and will be overwritten."
  290. echo "Do you wish to continue? (y/n)"
  291. read ans
  292. if [ ${ans} != "y" ]
  293. then
  294. echo "$command aborted by user."
  295. exit 1
  296. fi
  297. fi
  298. }
  299. ### Functions used for dump
  300. # Sets the global variable, dump_qry, to the schema-version specific
  301. # SQL text needed to dump the lease data for the current backend
  302. # and protocol
  303. get_dump_query() {
  304. local version=$1
  305. case ${backend} in
  306. mysql)
  307. invoke="call"
  308. ;;
  309. pgsql)
  310. invoke="select * from"
  311. ;;
  312. cql)
  313. invoke="select * from"
  314. ;;
  315. *)
  316. log_error "unsupported backend ${backend}"
  317. usage
  318. exit 1
  319. ;;
  320. esac
  321. dump_qry="${invoke} lease${dump_type}DumpHeader();${invoke} lease${dump_type}DumpData();";
  322. }
  323. memfile_dump() {
  324. log_error "lease-dump is not supported for memfile"
  325. exit 1
  326. }
  327. mysql_dump() {
  328. # get the correct dump query
  329. version=`mysql_version`
  330. retcode=$?
  331. if [ $retcode -ne 0 ]
  332. then
  333. log_error "lease-dump: mysql_version failed, exit code $retcode"
  334. exit 1;
  335. fi
  336. # Fetch the correct SQL text. Note this function will exit
  337. # if it fails.
  338. get_dump_query $version
  339. # Make sure they specified a file
  340. if [ "$dump_file" = "" ]; then
  341. log_error "you must specify an output file for lease-dump"
  342. usage
  343. exit 1
  344. fi
  345. # If output file exists, notify user, allow them a chance to bail
  346. check_file_overwrite $dump_file
  347. # Check the temp file too
  348. tmp_file="$dump_file.tmp"
  349. check_file_overwrite $tmp_file
  350. # Run the sql to output tab-delimited lease data to a temp file.
  351. # By using a temp file we can check for MySQL errors before using
  352. # 'tr' to translate tabs to commas. We do not use MySQL's output
  353. # to file as that requires linux superuser privileges to execute
  354. # the select.
  355. mysql_execute "${dump_qry}" > $tmp_file
  356. retcode=$?
  357. if [ $retcode -ne 0 ]; then
  358. log_error "lease-dump: mysql_execute failed, exit code $retcode";
  359. exit 1
  360. fi
  361. # Now translate tabs to commas.
  362. cat $tmp_file | tr '\t' ',' >$dump_file
  363. if [ $? -ne 0 ]; then
  364. log_error "lease-dump: reformatting failed";
  365. exit 1
  366. fi
  367. # delete the tmp file on success
  368. rm $tmp_file
  369. echo lease$dump_type successfully dumped to $dump_file
  370. exit 0
  371. }
  372. ### Functions used for dump
  373. pgsql_dump() {
  374. version=`pgsql_version`
  375. get_dump_query $version
  376. # Make sure they specified a file
  377. if [ "$dump_file" = "" ]; then
  378. log_error "you must specify an output file for lease-dump"
  379. usage
  380. exit 1
  381. fi
  382. # If output file exists, notify user, allow them a chance to bail
  383. check_file_overwrite $dump_file
  384. # psql does not accept password as a parameter but will look in the environment
  385. export PGPASSWORD=$db_password
  386. # Call psql and redirect output to the dump file. We don't use psql "to csv"
  387. # as it can only be run as db superuser.
  388. echo "$dump_qry" | psql --set ON_ERROR_STOP=1 -t -q --user=$db_user --dbname=$db_name -w --no-align --field-separator=',' >$dump_file
  389. retcode=$?
  390. # Check for errors.
  391. if [ $retcode -ne 0 ]; then
  392. log_error "lease-dump: psql call failed, exit code: $retcode";
  393. exit 1
  394. fi
  395. echo lease$dump_type successfully dumped to $dump_file
  396. exit 0
  397. }
  398. cql_dump() {
  399. # get the correct dump query
  400. version=`cql_version`
  401. retcode=$?
  402. if [ $retcode -ne 0 ]
  403. then
  404. log_error "lease-dump: cql_version failed, exit code $retcode"
  405. exit 1;
  406. fi
  407. # Fetch the correct SQL text. Note this function will exit
  408. # if it fails.
  409. select_where_clause=""
  410. if [ $dump_type -eq 4 ]; then
  411. dump_qry="SELECT address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state FROM keatest.lease4"
  412. select_where_clause=" WHERE address = 0" # invalid address
  413. elif [ $dump_type -eq 6 ]; then
  414. dump_qry="SELECT address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,hwtype,hwaddr_source,state FROM keatest.lease6"
  415. select_where_clause=" WHERE address = ::" # invalid address
  416. fi
  417. # Make sure they specified a file
  418. if [ "$dump_file" = "" ]; then
  419. log_error "you must specify an output file for lease-dump"
  420. usage
  421. exit 1
  422. fi
  423. # If output file exists, notify user, allow them a chance to bail
  424. check_file_overwrite $dump_file
  425. cql_execute "${dump_qry}${select_where_clause}" | head -n 2 | tail -n 1 | sed -e 's/\s*//g' | sed -e 's/|/,/g' > $dump_file
  426. if [ $? -ne 0 ]; then
  427. log_error "lease-dump: cql_execute failed, exit code $retcode";
  428. exit 1
  429. fi
  430. cql_execute "${dump_qry}" | tail -n +4 | head -n -2 | sed -e 's/\s*//g' | sed -e 's/|/,/g' | sort -r >> $dump_file
  431. if [ $? -ne 0 ]; then
  432. log_error "lease-dump: cql_execute failed, exit code $retcode";
  433. exit 1
  434. fi
  435. echo lease$dump_type successfully dumped to $dump_file
  436. exit 0
  437. }
  438. ### Script starts here ###
  439. # First, find what the command is
  440. command=${1}
  441. if [ -z ${command} ]; then
  442. log_error "missing command"
  443. usage
  444. exit 1
  445. fi
  446. is_in_list "${command}" "lease-init lease-version lease-upgrade lease-dump"
  447. if [ ${_inlist} -eq 0 ]; then
  448. log_error "invalid command: ${command}"
  449. exit 1
  450. fi
  451. shift
  452. # Second, check what's the backend
  453. backend=${1}
  454. if [ -z ${backend} ]; then
  455. log_error "missing backend"
  456. usage
  457. exit 1
  458. fi
  459. is_in_list "${backend}" "memfile mysql pgsql cql"
  460. if [ ${_inlist} -eq 0 ]; then
  461. log_error "invalid backend: ${backend}"
  462. exit 1
  463. fi
  464. shift
  465. # Ok, let's process parameters (if there are any)
  466. while [ ! -z "${1}" ]
  467. do
  468. option=${1}
  469. case ${option} in
  470. # Specify database user
  471. -u|--user)
  472. shift
  473. db_user=${1}
  474. if [ -z ${db_user} ]; then
  475. log_error "-u or --user requires a parameter"
  476. usage
  477. exit 1
  478. fi
  479. ;;
  480. # Specify database password
  481. -p|--password)
  482. shift
  483. db_password=${1}
  484. if [ -z ${db_password} ]; then
  485. log_error "-p or --password requires a parameter"
  486. usage
  487. exit 1
  488. fi
  489. ;;
  490. # Specify database name
  491. -n|--name)
  492. shift
  493. db_name=${1}
  494. if [ -z ${db_name} ]; then
  495. log_error "-n or --name requires a parameter"
  496. usage
  497. exit 1
  498. fi
  499. ;;
  500. -d|--directory)
  501. shift
  502. scripts_dir=${1}
  503. if [ -z ${scripts_dir} ]; then
  504. log_error "-d or --directory requires a parameter"
  505. usage
  506. exit 1
  507. fi
  508. ;;
  509. # specify DHCPv4 lease type
  510. -4)
  511. if [ $dump_type -eq 6 ]; then
  512. log_error "you may not specify both -4 and -6"
  513. usage
  514. exit 1
  515. fi
  516. dump_type=4
  517. ;;
  518. # specify DHCPv6 lease type
  519. -6)
  520. if [ $dump_type -eq 4 ]; then
  521. log_error "you may not specify both -4 and -6"
  522. usage
  523. exit 1
  524. fi
  525. dump_type=6
  526. ;;
  527. # specify output file, currently only used by lease dump
  528. -o|--output)
  529. shift
  530. dump_file=${1}
  531. if [ -z ${dump_file} ]; then
  532. log_error "-o or --output requires a parameter"
  533. usage
  534. exit 1
  535. fi
  536. ;;
  537. *)
  538. log_error "invalid option: ${option}"
  539. usage
  540. exit 1
  541. esac
  542. shift
  543. done
  544. case ${command} in
  545. # Initialize the database
  546. lease-init)
  547. case ${backend} in
  548. memfile)
  549. memfile_init
  550. ;;
  551. mysql)
  552. mysql_init
  553. ;;
  554. pgsql)
  555. pgsql_init
  556. ;;
  557. cql)
  558. cql_init
  559. ;;
  560. esac
  561. ;;
  562. lease-version)
  563. case ${backend} in
  564. memfile)
  565. memfile_version
  566. ;;
  567. mysql)
  568. mysql_version
  569. printf "\n"
  570. ;;
  571. pgsql)
  572. pgsql_version
  573. ;;
  574. cql)
  575. cql_version
  576. ;;
  577. esac
  578. ;;
  579. lease-upgrade)
  580. case ${backend} in
  581. memfile)
  582. memfile_upgrade
  583. ;;
  584. mysql)
  585. mysql_upgrade
  586. ;;
  587. pgsql)
  588. pgsql_upgrade
  589. ;;
  590. cql)
  591. cql_upgrade
  592. ;;
  593. esac
  594. ;;
  595. lease-dump)
  596. case ${backend} in
  597. memfile)
  598. memfile_dump
  599. ;;
  600. mysql)
  601. mysql_dump
  602. ;;
  603. pgsql)
  604. pgsql_dump
  605. ;;
  606. cql)
  607. cql_dump
  608. ;;
  609. esac
  610. ;;
  611. esac
  612. exit 0