summaryrefslogtreecommitdiffstats
path: root/src/bin/admin/admin-utils.sh
blob: f0e86f26d60401dfe4e4731c3f2b38b4ff12818d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#!/bin/sh

# Copyright (C) 2014-2022 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# This is an utility script that is being included by other scripts.

# shellcheck disable=SC2086
# SC2086: Double quote to prevent globbing and word splitting.
# Reason for disable: explicitly don't quote db_port_full_parameter so it
# doesn't expand to empty string if it is not set and explicitly don't quote
# extra_arguments so it is considered multiple arguments instead of one.

# shellcheck disable=SC2154
# SC2154: ... is referenced but not assigned.
# Some variables are assigned in kea-admin.

# Exit with error if commands exit with non-zero and if undefined variables are
# used.
set -eu

# These are the default parameters. They will likely not work in any
# specific deployment. Also used in unit tests.
db_host='localhost'
db_user='keatest'
db_password='keatest'
db_name='keatest'
extra_arguments=

# Runs all the given arguments as a single command. Maintains quoting. Places
# output in ${OUTPUT} and exit code in ${EXIT_CODE}. Does not support pipes and
# redirections. Support for them could be added through eval and single
# parameter assignment, but eval is not recommended.
run_command() {
    if test -n "${DEBUG+x}"; then
        printf '%s\n' "${*}" >&2
    fi
    set +e
    OUTPUT=$("${@}")
    EXIT_CODE=${?}
    set -e
}

mysql_sanity_checks() {
    # https://bugs.mysql.com/bug.php?id=55796#c321360
    # https://dev.mysql.com/doc/refman/8.0/en/connecting.html
    # On Unix, MySQL programs treat the host name localhost specially, in a way
    # that is likely different from what you expect compared to other
    # network-based programs: the client connects using a Unix socket file.
    if test -n "${db_port+x}" && \
        { test "${db_host}" = 'localhost' || \
          test "${db_host}" = '127.0.0.1'; }; then
        printf 'Warning: the MySQL client uses the default unix socket ' >&2
        printf 'instead of TCP port %s ' "${db_port}" >&2
        printf 'when connecting to localhost. Continuing...\n' >&2
    fi
}

# Executes a given MySQL statement.
# mysql_execute SQL_QUERY PARAM1 PARAM2 .. PARAMN - Additional parameters
#     may be specified. They are passed directly to mysql.
#
# It returns the mysql command exit status to the caller as $?
mysql_execute() {
    QUERY=$1
    shift

    mysql_sanity_checks

    if [ $# -gt 0 ]; then
        mysql -N -B --host="${db_host}" ${db_port_full_parameter-} \
        --database="${db_name}" --user="${db_user}" \
        --password="${db_password}" ${extra_arguments} \
        --execute "${QUERY}" "${@}"
    else
        mysql -N -B --host="${db_host}" ${db_port_full_parameter-} \
        --database="${db_name}" --user="${db_user}" \
        --password="${db_password}" ${extra_arguments} \
        --execute "${QUERY}"
    fi
}

# Submits SQL in a given file to MySQL.
# pgsql_execute SQL_FILE PARAM1 PARAM2 .. PARAMN - Additional parameters
#     may be specified. They are passed directly to pgsql.
#
# It returns the mysql command exit status to the caller as $?
mysql_execute_script() {
    file=$1
    shift

    mysql_sanity_checks

    if [ $# -gt 0 ]; then
        mysql -N -B --host="${db_host}" ${db_port_full_parameter-} \
        --database="${db_name}" --user="${db_user}" \
        --password="${db_password}" ${extra_arguments} "${@}" < "${file}"
    else
        mysql -N -B --host="${db_host}" ${db_port_full_parameter-} \
        --database="${db_name}" --user="${db_user}" \
        --password="${db_password}" ${extra_arguments} < "${file}"
    fi
}

mysql_version() {
    mysql_execute "SELECT CONCAT_WS('.', version, minor) FROM schema_version" "$@"
}

checked_mysql_version() {
    run_command \
        mysql_execute "SELECT CONCAT_WS('.', version, minor) FROM schema_version" "$@"

    if [ "${EXIT_CODE}" -ne 0 ]
    then
        printf "Failed to get schema version, mysql status  %s\n" "${EXIT_CODE}"
    fi

    printf '%s\n' "${OUTPUT}"
    return "${EXIT_CODE}"
}

# Submits given SQL text to PostgreSQL
# pgsql_execute SQL_QUERY PARAM1 PARAM2 .. PARAMN - Additional parameters
#     may be specified. They are passed directly to pgsql.
#
# It returns the pgsql command exit status to the caller as $?
pgsql_execute() {
    QUERY=$1
    shift

    # Prioritize externally set PGPASSWORD. wipe_data.sh sets it for example.
    if test -z "${PGPASSWORD-}"; then
        PGPASSWORD="${db_password}"
    fi
    export PGPASSWORD

    if [ $# -gt 0 ]; then
        printf '%s' "${QUERY}" | psql --set ON_ERROR_STOP=1 -A -t \
        -h "${db_host}" ${db_port_full_parameter-} -q -U "${db_user}" \
        -d "${db_name}" ${extra_arguments} "${@}"
    else
        printf '%s' "${QUERY}" | psql --set ON_ERROR_STOP=1 -A -t \
        -h "${db_host}" ${db_port_full_parameter-} -q -U "${db_user}" \
        -d "${db_name}" ${extra_arguments}
    fi
}

# Submits SQL in a given file to PostgreSQL
# pgsql_execute SQL_FILE PARAM1 PARAM2 .. PARAMN - Additional parameters
#     may be specified. They are passed directly to pgsql.
#
# It returns the pgsql command exit status to the caller as $?
pgsql_execute_script() {
    file=$1
    shift

    # Prioritize externally set PGPASSWORD. wipe_data.sh sets it for example.
    if test -z "${PGPASSWORD-}"; then
        PGPASSWORD="${db_password}"
    fi
    export PGPASSWORD

    if [ $# -gt 0 ]; then
        psql --set ON_ERROR_STOP=1 -A -t -h "${db_host}" \
        ${db_port_full_parameter-} -q -U "${db_user}" -d "${db_name}" \
        ${extra_arguments} -f "${file}" "${@}"
    else
        psql --set ON_ERROR_STOP=1 -A -t -h "${db_host}" \
        ${db_port_full_parameter-} -q -U "${db_user}" -d "${db_name}" \
        ${extra_arguments} -f "${file}"
    fi
}

pgsql_version() {
    pgsql_execute "SELECT version || '.' || minor FROM schema_version" "$@"
}

checked_pgsql_version() {
    run_command \
        pgsql_execute "SELECT version || '.' || minor FROM schema_version" "$@"

    if [ "${EXIT_CODE}" -ne 0 ]
    then
        printf "Failed to get schema version, pgsql status %s\n" "${EXIT_CODE}"
    fi

    printf '%s\n' "${OUTPUT}"
    return "${EXIT_CODE}"
}

# recount IPv4 leases from scratch
_RECOUNT4_QUERY=\
"
START TRANSACTION; \
DELETE FROM lease4_stat; \
INSERT INTO lease4_stat (subnet_id, state, leases) \
    SELECT subnet_id, state, COUNT(*) \
    FROM lease4 WHERE state = 0 OR state = 1 \
    GROUP BY subnet_id, state; \
COMMIT;"
export _RECOUNT4_QUERY

# recount IPv6 leases from scratch
_RECOUNT6_QUERY=\
"
START TRANSACTION; \
DELETE FROM lease6_stat; \
INSERT INTO lease6_stat (subnet_id, lease_type, state, leases) \
    SELECT subnet_id, lease_type, state, COUNT(*) \
    FROM lease6 WHERE state = 0 OR state = 1 \
    GROUP BY subnet_id, lease_type, state; \
COMMIT;"
export _RECOUNT6_QUERY