diff --git a/src/bin/admin/admin-utils.sh b/src/bin/admin/admin-utils.sh index 2a9d4dccf8..f5e5df1b03 100755 --- a/src/bin/admin/admin-utils.sh +++ b/src/bin/admin/admin-utils.sh @@ -18,12 +18,34 @@ mysql_execute() { QUERY=$1 shift - if [ $# -gt 1 ]; then - mysql -N -B "$@" -e "${QUERY}" + if [ $# -gt 0 ]; then + cmdline=$(mysql_compose_connect_cmd_line) + cmdline="$cmdline $* -e \"$QUERY\"" + eval "mysql " $cmdline retcode=$? else - mysql -N -B --database="${db_name}" --user="${db_user}" --password="${db_password}" -e "${QUERY}" - retcode=$? + cmdline=$(mysql_compose_connect_cmd_line) + cmdline="$cmdline -e \"$QUERY\" $db_name" + eval "mysql " $cmdline + retcode="$?" + fi + + return $retcode +} + +mysql_execute_no_dbname() { + QUERY=$1 + shift + + cmdline=$(mysql_compose_connect_cmd_line) + cmdline="$cmdline -e \"$QUERY\"" + + eval "mysql " $cmdline + retcode="$?" + + if [ $retcode -ne 0 ]; then + printf "mysql returned with exit status $retcode\n" + exit $retcode fi return $retcode @@ -32,19 +54,21 @@ mysql_execute() { mysql_execute_script() { file=$1 shift - if [ $# -ge 1 ]; then - mysql -N -B "$@" < "${file}" + if [ $# -gt 0 ]; then + cmdline=$(mysql_compose_connect_cmd_line) + eval "mysql " $cmdline $* < ${file} retcode=$? else - mysql -N -B --database="${db_name}" --user="${db_user}" --password="${db_password}" < "${file}" - retcode=$? + cmdline=$(mysql_compose_connect_cmd_line) + eval "mysql "$cmdline $db_name < ${file} + retcode="$?" fi return $retcode } mysql_version() { - mysql_execute "SELECT CONCAT_WS('.', version, minor) FROM schema_version" "$@" + mysql_execute "SELECT CONCAT_WS('.', version, minor) FROM schema_version;" "$@" return $? } @@ -62,16 +86,38 @@ pgsql_execute() { QUERY=$1 shift if [ $# -gt 0 ]; then - echo "${QUERY}" | psql --set ON_ERROR_STOP=1 -A -t -h localhost -q "$@" + export PGPASSWORD=$db_password + cmdline=$(pgsql_compose_connect_cmd_line) + cmdline="$cmdline $*" + echo $QUERY | psql $cmdline retcode=$? else export PGPASSWORD=$db_password - echo "${QUERY}" | psql --set ON_ERROR_STOP=1 -A -t -h localhost -q -U "${db_user}" -d "${db_name}" + cmdline=$(pgsql_compose_connect_cmd_line) + cmdline="$cmdline -d $db_name" + echo $QUERY | psql $cmdline retcode=$? fi return $retcode } +pgsql_execute_no_dbname() { + QUERY=$1 + shift + export PGPASSWORD=$db_password + cmdline=$(pgsql_compose_connect_cmd_line) +# Sometimes we don't need to connect to a specific database +# (e.g. when we want to create a new one) +# Postgresql client requires to connect all the time a database. +# The first database is always created by the initdb command when +# the data storage area is initialized is called postgres. +# So we will connect to the default database "postgres") + cmdline="$cmdline -d postgres" + echo $QUERY | psql $cmdline + retcode=$? + return $retcode +} + # Submits SQL in a given file to PostgreSQL # There are two ways of calling this method. # pgsql_execute SQL_FILE - This call is simpler, but requires db_user, @@ -86,11 +132,16 @@ pgsql_execute_script() { file=$1 shift if [ $# -gt 0 ]; then - psql --set ON_ERROR_STOP=1 -A -t -h localhost -q -f "${file}" "$@" + export PGPASSWORD=$db_password + cmdline=$(pgsql_compose_connect_cmd_line) + cmdline="$cmdline -d $db_name -f $file $*" + eval "psql "$cmdline retcode=$? else export PGPASSWORD=$db_password - psql --set ON_ERROR_STOP=1 -A -t -h localhost -q -U "${db_user}" -d "${db_name}" -f "${file}" + cmdline=$(pgsql_compose_connect_cmd_line) + cmdline="$cmdline -d $db_name -f $file" + eval "psql "$cmdline retcode=$? fi return $retcode @@ -104,11 +155,15 @@ pgsql_version() { cql_execute() { query=$1 shift - if [ $# -gt 1 ]; then - cqlsh "$@" -e "$query" + if [ $# -gt 0 ]; then + cmdline=$(cql_compose_connect_cmd_line) + cmdline="$cmdline $* -e \"$query\"" + eval "cqlsh " $cmdline retcode=$? else - cqlsh -u "${db_user}" -p "${db_password}" -k "${db_name}" -e "${query}" + cmdline=$(cql_compose_connect_cmd_line) + cmdline="$cmdline -k $db_name -e \"$query\"" + eval "cqlsh " $cmdline retcode=$? fi @@ -120,14 +175,35 @@ cql_execute() { return $retcode } +cql_execute_no_keyspace() { + query=$1 + shift + + cmdline=$(cql_compose_connect_cmd_line) + cmdline="$cmdline -e \"$query\"" + eval "cqlsh " $cmdline + retcode=$? + + if [ $retcode -ne 0 ]; then + printf "cqlsh returned with exit status $retcode\n" + exit $retcode + fi + + return $retcode +} + cql_execute_script() { file=$1 shift - if [ $# -gt 1 ]; then - cqlsh "$@" -e "$file" + if [ $# -gt 0 ]; then + cmdline=$(cql_compose_connect_cmd_line) + cmdline="$cmdline $* -f \"$file\"" + eval "cqlsh " $cmdline retcode=$? else - cqlsh -u "${db_user}" -p "${db_password}" -k "${db_name}" -f "${file}" + cmdline=$(cql_compose_connect_cmd_line) + cmdline="$cmdline -k $db_name -f \"$file\"" + eval "cqlsh " $cmdline retcode=$? fi @@ -143,6 +219,91 @@ cql_version() { version=$(cql_execute "SELECT version, minor FROM schema_version" "$@") error=$? version=$(echo "$version" | grep -A 1 "+" | grep -v "+" | tr -d ' ' | cut -d "|" -f 1-2 | tr "|" ".") - echo "$version" + echo $version return $error } + +mysql_compose_connect_cmd_line() { + local line="-N -B" + + if [ -n "$db_server_address" ]; then + line="$line --host=$db_server_address" + fi + + if [ -n "$db_server_port" ]; then + line="$line --port=$db_server_port" + fi + + if [ -n "$db_user" ]; then + line="$line --user=$db_user" + fi + + if [ -n "$db_password" ]; then + line="$line --password=$db_password" + fi + + if [ -n "$db_host" ]; then + line="$line --host=$db_host" + fi + + echo $line +} + +pgsql_compose_connect_cmd_line() { + local line="--set ON_ERROR_STOP=1 -A -t -q" + + if [ -n "$db_server_address" ] + then + line="$line -h $db_server_address" + else + if [ -n "$db_host" ]; then + line="$line -h $db_host" + else + line="$line -h localhost" + fi + fi + + if [ -n "$db_server_port" ]; then + line="$line -p $db_server_port" + fi + + if [ -n "$db_user" ]; then + line="$line -U $db_user" + fi + + if [ -n "$db_host" ]; then + line="$line -h $db_host" + fi + + echo $line +} + +cql_compose_connect_cmd_line() { + local line="" + + if [ -n "$db_server_address" ]; then + line=$line" "$db_server_address + fi + + if [ -n "$db_server_port" ]; then + line=$line" "$db_server_port + fi + + if [ -n "$db_server_version" ]; then + line=$line" --cqlversion="$db_server_version + fi + + if [ -n "$db_user" ]; then + line=$line" -u "$db_user + fi + + if [ -n "$db_password" ]; then + line=$line" -p "$db_password + fi + + if [ -n "$db_use_ssl" ]; then + line=$line" --ssl" + fi + + echo $line +} diff --git a/src/bin/admin/kea-admin.in b/src/bin/admin/kea-admin.in index 4be383ddb4..ca2c19a653 100644 --- a/src/bin/admin/kea-admin.in +++ b/src/bin/admin/kea-admin.in @@ -25,6 +25,11 @@ db_host="localhost" db_user="keatest" db_password="keatest" db_name="keatest" +db_server_address="" +db_server_port="" + +# These are CQL default parameters +db_server_version="" # lease dump parameters dump_type=0 @@ -50,7 +55,12 @@ usage() { printf "\n" printf "COMMAND: Currently supported operations are:\n" printf "\n" + printf " - -h or --help: Displays this help.\n" + printf " - db-create: Creates new empty databases. Useful for first time installation.\n" + printf " - db-remove: Removes an existing database.\n" + printf " - create-db-and-users: internal use only\n" printf " - lease-init: Initializes new lease database. Useful for first time installation.\n" + printf " - lease-drop: Drops all tables in the lease database. \n" printf " - lease-version: Checks version of the existing lease database scheme. Useful\n" printf " - for checking lease DB version when preparing for an upgrade.\n" printf " - lease-upgrade: Upgrades your lease database scheme\n" @@ -60,6 +70,9 @@ usage() { printf "\n" printf "PARAMETERS: Parameters are optional in general, but may be required\n" printf " for specific operation.\n" + printf " -s or --server - specifies remote database server address\n" + printf " -sp or --server-port - specifies remote database server port\n" + printf " --db-server-version - specifies remote database version\n" printf " -h or --host hostname - specifies a hostname of a database to connect to\n" printf " -u or --user name - specifies username when connecting to a database\n" printf " -p or --password pass - specifies a password when connecting to a database\n" @@ -130,7 +143,7 @@ memfile_init() { # some extra sanity checks. It will refuse to use it if there are any # existing tables. It's better safe than sorry. mysql_init() { - printf "Checking if there is a database initialized already. Please ignore errors.\n" + printf "Checking if there is a database initialized already.\n" # Let's try to count the number of tables. Anything above 0 means that there # is some database in place. If there is anything, we abort. Note that @@ -140,7 +153,7 @@ mysql_init() { # RESULT=`mysql_execute "SHOW TABLES;"` ERRCODE=$? - if [ $ERRCODE -ne 0 ] + if [ $ERRCODE -ne 0 ] then log_error "mysql_init table query failed, mysql status = $ERRCODE" exit 1 @@ -155,7 +168,7 @@ mysql_init() { fi printf "Initializing database using script %s\n" $scripts_dir/mysql/dhcpdb_create.mysql - mysql -B --host=$db_host --user=$db_user --password=$db_password $db_name < $scripts_dir/mysql/dhcpdb_create.mysql + mysql_execute_script $scripts_dir/mysql/dhcpdb_create.mysql ERRCODE=$? printf "mysql returned status code $ERRCODE\n" @@ -169,8 +182,88 @@ mysql_init() { exit $ERRCODE } +#Creates a new, empty MySQL database. +mysql_db_create() { + for name in $(echo $db_name | tr "," "\n") + do + printf "Creating '$name' database\n" + + command="CREATE DATABASE $name;" + RESULT=`mysql_execute_no_dbname "$command"` + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + log_error "Database creation failed, status code: $ERRCODE?" + exit 1 + fi + + printf "The database has been created.\n" + done + exit 0 +} + +#Removes an existing MySQL database. +mysql_db_remove() { + for name in $(echo $db_name | tr "," "\n") + do + printf "Removing '$name' database\n" + + command="DROP DATABASE $name;" + RESULT=`mysql_execute_no_dbname "$command"` + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + log_error "Database deletion failed, status code: $ERRCODE?" + exit 1 + fi + + printf "The database has been removed.\n" + done + exit 0 +} + +#Creates a new, empty Postgresql database. +pgsql_db_create() { + for name in $(echo $db_name | tr "," "\n") + do + printf "Creating '$name' database\n" + + command="CREATE DATABASE $name;" + RESULT=`pgsql_execute_no_dbname "$command"` + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + log_error "Database creation failed, status code: $ERRCODE?" + exit 1 + fi + + printf "The database has been created.\n" + done + exit 0 +} + +#Removes an existing MySQL database. +pgsql_db_remove() { + for name in $(echo $db_name | tr "," "\n") + do + printf "Removing '$name' database\n" + + command="DROP DATABASE $name;" + RESULT=`pgsql_execute_no_dbname "$command"` + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + log_error "Database deletion failed, status code: $ERRCODE?" + exit 1 + fi + + printf "The database has been removed.\n" + done + exit 0 +} + pgsql_init() { - printf "Checking if there is a database initialized already. Please ignore errors.\n" + printf "Checking if there is a database initialized already.\n" # Let's try to count the number of tables. Anything above 0 means that there # is some database in place. If there is anything, we abort. @@ -202,24 +295,106 @@ pgsql_init() { exit 0 } +# Creates a new, empty CQL database. +cql_db_create() { + #setting default values for the keyspace strategy and replication + strategy="SimpleStrategy" + replication="1" + + for name in $(echo $db_name | tr "," "\n") + do + printf "Creating '$name' keyspace\n" + + command="create KEYSPACE $name WITH REPLICATION = { 'class' : '$strategy', 'replication_factor' : $replication };" + RESULT=`cql_execute_no_keyspace "$command"` + ERRCODE=$? + if [ "$ERRCODE" -ne 0 ]; then + log_error "Keyspace creation failed, status code: $ERRCODE?" + exit 1 + fi + + printf "The keyspace has been created.\n" + done + + exit 0 +} + +# Removes an existing CQL database. +cql_db_remove() { + for name in $(echo $db_name | tr "," "\n") + do + printf "Removing '$name' keyspace\n" + + command="drop KEYSPACE $name" + RESULT=`cql_execute_no_keyspace "$command"` + ERRCODE=$? + if [ "$ERRCODE" -ne 0 ]; then + log_error "Keyspace deletion failed, status code: $ERRCODE?" + exit 1 + fi + + printf "The keyspace has been removed.\n" + done + + exit 0 +} + cql_init() { - printf "Checking if there is a database initialized already... Please ignore errors.\n" + printf "Checking if there is a database initialized already...\n" result=$(cql_execute "DESCRIBE tables;") + cql_exit_code=${?} if [ $(echo "$result" | grep "" | wc -w) -gt 0 ]; then printf "Creating and initializing tables using script %s...\n" $scripts_dir/cql/dhcpdb_create.cql cql_execute_script $scripts_dir/cql/dhcpdb_create.cql else - log_error "Expected empty database $db_name, but the following tables are present \n$result. Aborting." + if [ ${cql_exit_code} -eq 0 ]; then + log_error "ERROR: Expected empty database $db_name, but the following tables are present: \n$result. Aborting." + else + log_error "ERROR: Unable to execute cql_execute, reason: \n$result. Aborting." + fi exit 2 fi version=$(cql_version) - printf "Lease DB version reported after initialization: %s\n" "$version" + printf "Lease DB version reported after initialization: %s\n" "${version}" exit 0 } +# Drop functions +memfile_drop() { + # @todo Implement this as part of #3601 + log_error "NOT IMPLEMENTED" + exit 1 +} + +mysql_drop() { + script=$scripts_dir/mysql/dhcpdb_drop.mysql + printf "Droping tables in database using script $script%s\n" + mysql -B --user=$db_user --password=$db_password $db_name < $script + return_code=$? + printf "mysql_drop returned with code $return_code\n" + exit $return_code +} + +pgsql_drop() { + script="$scripts_dir/pgsql/dhcpdb_drop.pgsql" + printf "Droping tables in database using script $script%s\n" + pgsql_execute_script $script + return_code=$? + printf "pgsql_drop returned with code $return_code\n" + exit $return_code +} + +cql_drop() { + script="$scripts_dir/cql/dhcpdb_drop.cql" + printf "Droping tables in database using script $script%s\n" + cql_execute_script $script + return_code=$? + printf "cql_drop returned with code $return_code\n" + exit $return_code +} ### Functions that implement database version checking commands memfile_version() { # @todo Implement this as part of #3601 @@ -289,7 +464,7 @@ pgsql_upgrade() { # Postgres psql does not accept pw on command line, but can do it # thru an env - export PGPASSWORD=$db_password + export db_password for script in ${scripts_dir}/pgsql/upgrade*.sh do @@ -324,7 +499,11 @@ cql_upgrade() { for script in ${scripts_dir}/cql/upgrade*.sh do echo "Processing $script file..." - sh ${script} -u ${db_user} -p ${db_password} -k ${db_name} + if [ -z ${db_server_version} ]; then + sh ${script} -u ${db_user} -p ${db_password} -k ${db_name} + else + sh ${script} -u ${db_user} -p ${db_password} -k ${db_name} --cqlversion=${db_server_version} + fi done else echo "No upgrade script available." @@ -528,6 +707,39 @@ cql_dump() { exit 0 } + +memfile_create_database_and_users() { + log_error "NOT IMPLEMENTED" + exit 1 +} + +mysql_create_database_and_users() { + for query in "GRANT ALL ON *.* TO keatest IDENTIFIED BY 'keatest';" \ + "CREATE DATABASE keatest;" \ + "CREATE USER 'keatest_readonly'@'localhost' IDENTIFIED BY 'keatest';" \ + "GRANT SELECT ON keatest.* TO keatest_readonly IDENTIFIED BY 'keatest';" \ + ; do + mysql_execute "${query}" --host=${db_server_address} --port=${db_server_port} --user=root --password=${db_password} + done +} + +pgsql_create_database_and_users() { + export db_password + for query in "CREATE USER keatest WITH PASSWORD 'keatest';" \ + "CREATE DATABASE keatest;" \ + "GRANT ALL ON DATABASE keatest TO keatest;" \ + "CREATE USER keatest_readonly WITH PASSWORD 'keatest';" \ + ; do + pgsql_execute "${query}" --host=${db_server_address} --port=${db_server_port} --username=postgres + done + export db_password="keatest" + pgsql_execute "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO keatest_readonly;" --host=${db_server_address} --port=${db_server_port} --username=keatest +} + +cql_create_database_and_users() { + cql_execute_no_keyspace "CREATE KEYSPACE keatest WITH replication = {'class' : 'SimpleStrategy','replication_factor' : 1}" +} + ### Script starts here ### # First, find what the command is @@ -537,7 +749,11 @@ if [ -z ${command} ]; then usage exit 1 fi -is_in_list "${command}" "lease-init lease-version lease-upgrade lease-dump" +if [ "${command}" = "-h" -o "${command}" = "--help" ]; then + usage + exit 0 +fi +is_in_list "${command}" "db-create db-remove lease-init lease-drop lease-version lease-upgrade lease-dump create-db-and-users" if [ ${_inlist} -eq 0 ]; then log_error "invalid command: ${command}" exit 1 @@ -563,6 +779,36 @@ while [ ! -z "${1}" ] do option=${1} case ${option} in + # Specify server address + -s|--server) + shift + db_server_address=${1} + if [ -z ${db_server_address} ]; then + log_error "-s or --server requires a parameter" + usage + exit 1 + fi + ;; + # Specify server port + -sp|--server-port) + shift + db_server_port=${1} + if [ -z ${db_server_port} ]; then + log_error "-sp or --server-port requires a parameter" + usage + exit 1 + fi + ;; + # Specify CQL database version + --db-server-version) + shift + db_server_version=${1} + if [ -z ${db_server_version} ]; then + log_error "--db-server-version requires a parameter" + usage + exit 1 + fi + ;; # Specify database host -h|--host) shift @@ -649,6 +895,40 @@ do done case ${command} in + # Create the database + db-create) + case ${backend} in + memfile) + log_error "this command is not compatible with memory file backend" + ;; + mysql) + mysql_db_create + ;; + pgsql) + pgsql_db_create + ;; + cql) + cql_db_create + ;; + esac + ;; + # Removes the database + db-remove) + case ${backend} in + memfile) + log_error "this command is not compatible with memory file backend" + ;; + mysql) + mysql_db_remove + ;; + pgsql) + pgsql_db_remove + ;; + cql) + cql_db_remove + ;; + esac + ;; # Initialize the database lease-init) case ${backend} in @@ -666,6 +946,22 @@ case ${command} in ;; esac ;; + lease-drop) + case ${backend} in + memfile) + memfile_drop + ;; + mysql) + mysql_drop + ;; + pgsql) + pgsql_drop + ;; + cql) + cql_drop + ;; + esac + ;; lease-version) case ${backend} in memfile) @@ -673,7 +969,6 @@ case ${command} in ;; mysql) mysql_version - printf "\n" ;; pgsql) pgsql_version @@ -715,6 +1010,22 @@ case ${command} in ;; esac ;; + create-db-and-users) + case ${backend} in + memfile) + memfile_create_database_and_users + ;; + mysql) + mysql_create_database_and_users + ;; + pgsql) + pgsql_create_database_and_users + ;; + cql) + cql_create_database_and_users + ;; + esac + ;; esac exit 0 diff --git a/src/bin/admin/tests/pgsql_tests.sh.in b/src/bin/admin/tests/pgsql_tests.sh.in index ef0854ef25..16f8d1de33 100644 --- a/src/bin/admin/tests/pgsql_tests.sh.in +++ b/src/bin/admin/tests/pgsql_tests.sh.in @@ -488,7 +488,7 @@ pgsql_upgrade_schema_to_version() { # Postgres psql does not accept pw on command line, but can do it # thru an env - export PGPASSWORD=$db_password + export db_password for script in ${db_scripts_dir}/pgsql/upgrade*.sh do