-
Notifications
You must be signed in to change notification settings - Fork 80
/
Copy pathproxysql-admin-common
executable file
·958 lines (818 loc) · 30.1 KB
/
proxysql-admin-common
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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
#!/bin/bash
# This script contains functions common to proxysql-admin related scripts.
# (currently only Percona XtraDB cluster in combination with ProxySQL is supported)
# Version 2.0
###############################################################################################
# This program is copyright 2016-2022 Percona LLC and/or its affiliates.
#
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, version 2 or later
#
# You should have received a copy of the GNU General Public License version 2
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
# shellcheck disable=SC2181
#-------------------------------------------------------------------------------
#
# Step 1 : Bash internal configuration
#
#-------------------------------------------------------------------------------
#
# Step 2 : Global variables
#
#-------------------------------------------------------------------------------
#
# Step 3 : Helper functions
#
# Executes a SQL query with the (fully) specified server
#
# Globals:
# DEBUG
# TIMEOUT
#
# Arguments:
# 1: lineno
# 2: the name of the user
# 3: the user's password
# 4: the hostname of the server
# 5: the port used to connect to the server
# 6: the query to be run
# 7: (optional) arguments to the mysql client
# 8: (optional) additional options, space separated
# Available options:
# "hide_output"
# This will not show the output of the query when DEBUG is set.
# Used to stop the display of sensitve information (such as passwords)
# from being displayed when debugging.
#
function exec_sql() {
local lineno=$1
local user=$2
local password=$3
local hostname=$4
local port=$5
local query=$6
local args=""
local more_options=""
local retvalue
local retoutput
local default_auth=""
local defaults=""
if [[ $# -ge 7 ]]; then
args=$7
fi
if [[ $# -ge 8 ]]; then
more_options=$8
fi
debug "$lineno" "exec_sql : $user@$hostname:$port ==> $query"
if [[ $MYSQL_CLIENT_VERSION == "8.0" ]]; then
default_auth="default-auth=mysql_native_password"
fi
defaults=$(printf '[client]\nuser=%s\npassword="%s"\nhost=%s\nport=%s\nconnect-timeout=%s\n%s' \
"${user}" \
"${password}" \
"${hostname}" \
"${port}" \
"${TIMEOUT}" \
"${default_auth}"
)
# shellcheck disable=SC2086
if [[ $USE_STDIN_FOR_CREDENTIALS -eq 1 ]]; then
retoutput=$(printf "%s" "${defaults}" | mysql --defaults-file=/dev/stdin --protocol=tcp --unbuffered --batch --silent ${args} -e "$query")
retvalue=$?
else
retoutput=$(mysql --defaults-file=<(echo "${defaults}") --protocol=tcp --unbuffered --batch --silent ${args} -e "$query")
retvalue=$?
fi
if [[ $DEBUG -eq 1 ]]; then
local number_of_newlines=0
local dbgoutput=$retoutput
if [[ " $more_options " =~ [[:space:]]hide_output[[:space:]] ]]; then
dbgoutput="**** data hidden ****"
fi
if [[ -n $dbgoutput ]]; then
number_of_newlines=$(printf "%s" "${dbgoutput}" | wc -l)
fi
if [[ $retvalue -ne 0 ]]; then
debug "" "--> query failed $retvalue"
elif [[ -z $dbgoutput ]]; then
debug "" "--> query returned $retvalue : <query returned no data>"
elif [[ ${number_of_newlines} -eq 0 ]]; then
debug "" "--> query returned $retvalue : ${dbgoutput}"
else
debug "" "--> query returned $retvalue : <data follows>"
printf "%s\n" "${dbgoutput}" | while IFS= read -r line; do
debug "" "----> $line"
done
fi
fi
printf "%s" "${retoutput}"
return $retvalue
}
# Executes a SQL query on proxysql
#
# Globals:
# PROXYSQL_USERNAME
# PROXYSQL_PASSWORD
# PROXYSQL_HOSTNAME
# PROXYSQL_PORT
#
# Arguments:
# 1: lineno
# 2: The SQL query
# 3: (optional) Additional arguments to the mysql client for the query
# 4: (optional) more options, see exec_sql
#
function proxysql_exec() {
local lineno=$1
local query=$2
local args=""
local more_options=""
if [[ $# -ge 3 ]]; then
args=$3
fi
if [[ -z $args ]]; then
args="--skip-column_names"
fi
if [[ $# -ge 4 ]]; then
more_options=$4
fi
exec_sql "$lineno" "$PROXYSQL_USERNAME" "$PROXYSQL_PASSWORD" \
"$PROXYSQL_HOSTNAME" "$PROXYSQL_PORT" \
"$query" "$args" "$more_options"
return $?
}
# Executes a SQL query on a specific node in the cluster
#
# Globals:
# CLUSTER_USERNAME
# CLUSTER_PASSWORD
#
# Arguments:
# 1: lineno
# 2: cluster host
# 3: cluster port
# 4: The SQL query
# 5: Additional arguments to the mysql client for the query
# 6: (optional) more options, see exec_sql
#
function cluster_exec() {
local lineno=$1
local cluster_host=$2
local cluster_port=$3
local query=$4
local args=""
local more_options=""
if [[ $# -ge 5 ]]; then
args=$5
fi
if [[ $# -ge 6 ]]; then
more_options=$6
fi
exec_sql "$lineno" "$CLUSTER_USERNAME" "$CLUSTER_PASSWORD" \
"$cluster_host" "$cluster_port" \
"$query" "$args" "$more_options"
return $?
}
# Executes a SQL query on the CLUSTER_HOSTNAME/CLUSTER_PORT
# specified in the config file.
#
# Globals:
# CLUSTER_USERNAME
# CLUSTER_PASSWORD
# CLUSTER_HOSTNAME
# CLUSTER_PORT
#
# Arguments:
# 1: lineno
# 2: The SQL query
# 3: Additional arguments to the mysql client for the query
# 4: (optional) more options, see exec_sql
#
function mysql_exec() {
local lineno=$1
local query=$2
local args=""
local more_options=""
if [[ $# -ge 3 ]]; then
args=$3
fi
if [[ $# -ge 4 ]]; then
more_options=$4
fi
cluster_exec "$lineno" "$CLUSTER_HOSTNAME" "$CLUSTER_PORT" \
"$query" "$args" "$more_options"
return $?
}
# Executes a SQL query on a cluster ndde with the monitor credentials
#
# Globals:
# CLUSTER_HOSTNAME
# CLUSTER_PORT
#
# Arguments:
# 1: lineno
# 2: The monitor username
# 3: The monitor password
# 4: Additional arguments to the mysql client for the query
# 5: The SQL query
# 6: (optional) more options, see exec_sql
#
function monitor_exec() {
local lineno=$1
local user=$2
local password=$3
local args=$4
local query=$5
local more_options=""
if [[ $# -ge 6 ]]; then
more_options=$7
fi
exec_sql "$lineno" "$user" "$password" \
"$CLUSTER_HOSTNAME" "$CLUSTER_PORT" \
"$query" "$args" "$more_options"
return $?
}
# This will move the configuration from memory to the runtime (load)
# and from memory to disk (save)
#
# Globals:
# None
#
# Arguments:
# 1: the proxysql data that is being loaded and saved
# (for example "MYSQL USERS" or "MYSQL SERVERS").
# 2: the lineno where this function was invoked
# 3: (optional) call "SAVE $1 FROM RUNTIME" after
# loading data to runtime. This is done for
# MYSQL USERS to load the encrypted version of the
# password field.
#
# This function will exit the program if an error occurs while
# loaded to runtime or saving to disk.
#
function proxysql_load_to_runtime_save_to_disk() {
local data_type=$1
local lineno=$2
local reload_from_runtime=0
if [[ $# -ge 3 ]]; then
reload_from_runtime=$3
fi
proxysql_exec "$LINENO" "LOAD ${data_type} TO RUNTIME"
check_cmd $? "$lineno" "Failed to load the ${data_type} configuration to runtime."\
"\n-- Please check the ProxySQL configuration and status."
debug "$lineno" "Loaded ${data_type} to runtime"
if [[ $reload_from_runtime -eq 1 ]]; then
# This has a specific purpose for the MYSQL USERS
# This will cause the password field to be loaded with the encrypted version
# of the password field
proxysql_exec "$LINENO" "SAVE ${data_type} FROM RUNTIME"
check_cmd $? "$lineno" "Failed to load the ${data_type} configuration from the runtime."\
"\n-- Please check the ProxySQL configuration and status."
debug "$lineno" "Saved ${data_type} from runtime"
fi
proxysql_exec "$LINENO" "SAVE ${data_type} TO DISK;"
check_cmd $? "$lineno" "Failed to save the ${data_type} configuration to disk."\
"\n-- Please check the ProxySQL configuration and status."
debug "$lineno" "Saved ${data_type} to disk"
}
# Update mysql server version details in proxysql db
# Globals:
# WRITER_HOSTGROUP_ID
# CLUSTER_HOSTNAME (overwrites)
# CLUSTER_PORT (overwrites)
#
# Arguments:
# None
function update_mysql_version()
{
local cluster_node
# Find a cluster node that belongs to the cluster with $WRITER_HOSTGROUP_ID
cluster_node=$(find_cluster_node "$WRITER_HOSTGROUP_ID")
check_cmd $? "$LINENO" "Could not find a primary cluster node"
# Reset the central cluster node (so that calls to mysql_exec) will
# work with this new node, rather than the node in the config file
CLUSTER_HOSTNAME=$(echo -e "$cluster_node" | cut -f1)
CLUSTER_PORT=$(echo -e "$cluster_node" | cut -f2)
cluster_connection_check
proxysql_mysql_version_string=$(proxysql_exec "$LINENO" "select variable_value from global_variables where variable_name like 'mysql-server_version'")
check_cmd $? "$LINENO" "Failed to select the mysql-server_version variables from ProxySQL."\
"\n-- Please check the ProxySQL connection parameters and status."
mysql_version_string=$(mysql_exec "$LINENO" "SELECT VERSION();" | tail -1 | cut -d'-' -f1 )
check_cmd $? "$LINENO" "Failed to select the mysql version info from Cluster node."\
"\n-- Please check the PXC connection parameters and status."
if [[ $proxysql_mysql_version_string != "$mysql_version_string" ]]; then
proxysql_exec "$LINENO" \
"UPDATE global_variables
SET variable_value='$mysql_version_string'
WHERE variable_name='mysql-server_version';"
check_cmd $? "$LINENO" "Failed to set the mysql-server_version variables in ProxySQL."\
"\n-- Please check the ProxySQL connection parameters and status."
echo "ProxySQL MySQL version changed to $mysql_version_string"
proxysql_load_to_runtime_save_to_disk "MYSQL VARIABLES" $LINENO
fi
}
# Adds an application user to ProxySQL
#
# Globals:
# WRITER_HOSTGROUP_ID
# CLUSTER_HOSTNAME (overwrites)
# CLUSTER_PORT (overwrites)
#
# Arguments:
# None
#
function adduser(){
debug "$LINENO" "adduser ()"
local cluster_node
# Find a cluster node that belongs to the cluster with $WRITER_HOSTGROUP_ID
cluster_node=$(find_cluster_node "$WRITER_HOSTGROUP_ID")
check_cmd $? "$LINENO" "Could not find a primary cluster node"
# Reset the central cluster node (so that calls to mysql_exec) will
# work with this new node, rather than the node in the config file
CLUSTER_HOSTNAME=$(echo -e "$cluster_node" | cut -f1)
CLUSTER_PORT=$(echo -e "$cluster_node" | cut -f2)
cluster_connection_check
local cluster_app_write_username
local cluster_app_write_password
local safe_cluster_app_write_username
local safe_cluster_app_write_password
echo -e "\nAdding PXC application user to the ProxySQL database"
echo -n "Enter the PXC application user name: "
read -r cluster_app_write_username
while [[ -z "$cluster_app_write_username" ]]
do
echo -n "No input entered. Enter the PXC application user name: "
read -r cluster_app_write_username
done
safe_cluster_app_write_username=${cluster_app_write_username//\'/\'\'}
read -r -s -p "Enter the PXC application user password: " cluster_app_write_password
while [[ -z "$cluster_app_write_password" ]]
do
read -r -s -p "No input entered. Enter the PXC application user password: " cluster_app_write_password
done
safe_cluster_app_write_password=${cluster_app_write_password//\'/\'\'}
# check to see if the user already exists
local check_user
local check_cluster_user
check_user=$(proxysql_exec "$LINENO" \
"SELECT username
FROM mysql_users
WHERE username='$safe_cluster_app_write_username'")
check_cmd $? "$LINENO" "Could not retrieve the user from ProxySQL."\
"\n-- Check the ProxySQL connection parameters and status."
if [[ -n "$check_user" ]]; then
error "$LINENO" "The application user '$cluster_app_write_username' already exists in ProxySQL."
exit 1
fi
check_cluster_user=$(mysql_exec "$LINENO" "SELECT user,host FROM mysql.user where user='$safe_cluster_app_write_username'")
check_cmd $? "$LINENO" "Could not retrieve the user from PXC."\
"\n-- Check the PXC connection parameters and status."
if [[ -z "$check_cluster_user" ]]; then
local check_param
echo -e "\n\n"
read -r -p "The application user '$cluster_app_write_username' does not exist in PXC. Would you like to proceed [y/n] ? " check_param
case $check_param in
y|Y)
proxysql_exec "$LINENO" \
"INSERT INTO mysql_users
(username,password,active,default_hostgroup)
VALUES
('$safe_cluster_app_write_username','$safe_cluster_app_write_password',1,$WRITER_HOSTGROUP_ID);"
check_cmd $? "$LINENO" "Failed to add the PXC application user: '$cluster_app_write_username' to ProxySQL."\
"\n-- Please check the ProxySQL connection parameters and status."
echo -e "\nPlease create the user ${BD}$cluster_app_write_username${NBD} in PXC to access the application through ProxySQL"
;;
n|N)
exit 0
;;
*)
error "" "Please type [y/n]!"
exit 1
;;
esac
else
proxysql_exec "$LINENO" \
"INSERT INTO mysql_users
(username,password,active,default_hostgroup)
values
('$safe_cluster_app_write_username','$safe_cluster_app_write_password',1,$WRITER_HOSTGROUP_ID);"
check_cmd $? "$LINENO" "Failed to add the PXC application user: '$cluster_app_write_username' to ProxySQL."\
"\n-- Please check the ProxySQL connection parameters and status."
fi
proxysql_load_to_runtime_save_to_disk "MYSQL USERS" "$LINENO" "1"
}
# Check to see if the cluster is in mysql_galera_hostgroups
#
# Globals:
# None
#
# Arguments:
# Parameter 1 : the writer hostgroup id
#
function cluster_in_proxysql_check() {
local write_hg=$1
# As scheduler does not use the mysql_galera_hostgroups table,
# skip this check when scheduler is active
local is_scheduler_active
is_scheduler_active=$(proxysql_exec "$LINENO" \
"SELECT active
FROM runtime_scheduler
WHERE comment like '% hgW:${write_hg}%'")
if [[ $is_scheduler_active -eq 1 ]]; then
return
fi
# Check to see if mysql_galera_hostgroups already has an
# entry for the worker hostgroup
local hostgroup_in_use
hostgroup_in_use=$(proxysql_exec "$LINENO" \
"SELECT count(*)
FROM mysql_galera_hostgroups
WHERE
writer_hostgroup = $write_hg")
check_cmd $? "$LINENO" "Galera hostgroup retrieval failed."\
"\n-- Please check the ProxySQL connection parameters and status."
# If there are no hostgroups with this writer hostgroup id
# then return failure
if [[ $hostgroup_in_use -eq 0 ]]; then
error "$LINENO" "The cluster (with writer hostgroup:$WRITER_HOSTGROUP_ID) has not been configured in ProxySQL"
exit 1
fi
}
# Synchronizes the users between ProxySQL and PXC
#
# This function was created to auto sync all the existing users already
# in MySQL to proxySQL's mysql_users table. As there is not much point
# in having users in ProxySQL that don't exist in MySQL, this function
# will delete any users from ProxySQL that were not found in MySQL.
#
# Going forward you can add/remove application users in MySQL then
# rerun proxysql-admin with the --syncusers switch to replicate the changes
# to ProxySQL.
#
# LIMITATIONS: Will not work properly in cases where the same user name
# exists in MySQL with several hosts and different passwords.
# This will cause ProxySQL to throw a "UNIQUE constraint failed"
# error message.
#
# Globals:
# WRITER_HOSTGROUP_ID
# SYNCUSERS
# CLUSTER_HOSTNAME (overwrites)
# CLUSTER_PORT (overwrites)
# SINGLE_SERVER
#
# Arguments:
# None
#
function syncusers() {
debug "$LINENO" "syncusers ()"
local mysql_version
local password_field
local changes_made=0
# If a single server has been specified with --server, use that server
# and skip the cluster check (it may be a standalone node)
if [[ -n $SINGLE_SERVER ]]; then
local ws_address
ws_address=$(separate_ip_port_from_address "$SINGLE_SERVER")
CLUSTER_HOSTNAME=$(echo "$ws_address" | cut -d' ' -f1)
CLUSTER_PORT=$(echo "$ws_address" | cut -d' ' -f2)
echo -e "\nSyncing user accounts from server(${CLUSTER_HOSTNAME}:${CLUSTER_PORT}) to ProxySQL"
else
cluster_in_proxysql_check "${WRITER_HOSTGROUP_ID}"
local cluster_node
# Get current MySQL users, filter out header row and mysql.sys user
# Find a cluster node that belongs to the cluster with $WRITER_HOSTGROUP_ID
cluster_node=$(find_cluster_node "$WRITER_HOSTGROUP_ID")
check_cmd $? "$LINENO" "Could not find a primary cluster node"
# Reset the central cluster node (so that calls to mysql_exec) will
# work with this new node, rather than the node in the config file
CLUSTER_HOSTNAME=$(echo -e "$cluster_node" | cut -f1)
CLUSTER_PORT=$(echo -e "$cluster_node" | cut -f2)
echo -e "\nSyncing user accounts from PXC(${CLUSTER_HOSTNAME}:${CLUSTER_PORT}) to ProxySQL"
fi
# Check that we can connect to CLUSTER_HOSTNAME:CLUSTER_PORT
cluster_connection_check
mysql_version=$(mysql_exec "$LINENO" "SELECT VERSION();" | tail -1 | cut -d'.' -f1,2 )
check_cmd $? "$LINENO" "Could not connect to the server."\
"\n-- Please check the server connection parameters and status."
case $mysql_version in
5.5 | 5.6)
password_field="Password"
;;
5.7 | 8.0)
password_field="authentication_string"
;;
10.0 | 10.1 | 10.2 | 10.3 | 10.4 | 10.5 | 10.6 | 10.11)
password_field="Password"
;;
*)
error "$LINENO" "Unexpected database server version: ${mysql_version}"\
"\n-- This version of proxysql-admin needs to be updated."
exit 1
;;
esac
# Filter out the internal system users
mysql_users=$(mysql_exec "$LINENO" "SELECT User,HEX(${password_field}) FROM mysql.user where ${password_field}!=''" "" "hide_output" |
grep -E -v "^(mysql.sys|mysql.session|mysql.infoschema|mysql.pxc)" |
sort |
uniq )
check_cmd $? "$LINENO" "Failed to load the user list from the server."\
"\n-- Please check the server connection parameters and status."
# Checking whether user is part of proxysql admin user list
# Get current ProxySQL users and filter out header row
proxysql_users=$(get_proxysql_users)
# TEST FOR USERS THAT EXIST IN MYSQL BUT NOT IN PROXYSQL HERE AND ADD
# Escape all backslashes here, because the read will evaluate
# the escaped chars
mysql_users=${mysql_users//\\/\\\\}
# Declare associative arrays
declare -A mysql_users_full_hash # Hash of MySQL users
declare -A proxysql_users_full_hash # Hash of ProxySQL users
local username password
# Populate MySQL users hash
while read -r mysql_user; do
if [[ -z $mysql_user ]]; then
continue
fi
# Add MySQL user to hash
username=$(echo "$mysql_user" | cut -f1)
password=$(echo "$mysql_user" | cut -f2)
mysql_users_full_hash["${username}"]="$password"
done < <(printf "%s\n" "${mysql_users}")
# Populate ProxySQL users hash
while read -r proxysql_user; do
if [[ -z $proxysql_user ]]; then
continue
fi
# Extract ProxySQL username
username=$(echo "$proxysql_user" | cut -f1)
password=$(echo "$proxysql_user" | cut -f2)
# Add ProxySQL user to hash
proxysql_users_full_hash["${username}"]="$password"
done < <(printf "%s\n" "${proxysql_users}")
unset username
unset password
# Disable nounset to be able to search in the associative array
set +o nounset
# Check MySQL users against ProxySQL
for user in "${!mysql_users_full_hash[@]}"; do
username=${user}
password="${mysql_users_full_hash[${username}]}"
# Since we're using single quotes within the SQL statement, only need
# to escape the single quotes for SQL
password=${password//\'/\'\'}
debug "$LINENO" "Processing MySQL user: '${username}'"
# Check if the user exists in ProxySQL
if [[ -n ${proxysql_users_full_hash[${username}]} ]]; then
# Perform an exact match for the user and password
if [[ "${proxysql_users_full_hash[${username}]}" == "${mysql_users_full_hash[${username}]}" ]]; then
debug "$LINENO" "MySQL user: '${username}' already exists in ProxySQL, continuing"
# User already exists in ProxySQL, we may skip this user
continue
else
# If the same user exists in ProxySQL, but with a different password,
# then remove all such users from ProxySQL
echo "Removing existing user from ProxySQL: ${username}"
proxysql_exec "$LINENO" "DELETE FROM mysql_users WHERE username='${username}' and default_hostgroup=$WRITER_HOSTGROUP_ID"
check_cmd $? "$LINENO" "Failed to delete the user (${username}) from ProxySQL database."\
"\n-- Please check the ProxySQL connection parameters and status."
proxysql_exec "$LINENO" "DELETE FROM mysql_query_rules WHERE username='${username}' and destination_hostgroup in($WRITER_HOSTGROUP_ID,$READER_HOSTGROUP_ID)"
check_cmd $? "$LINENO" "Failed to delete the query rule for user (${username}) from ProxySQL database."\
"\n-- Please check the ProxySQL connection parameters and status."
changes_made=1
fi
fi
# Check for ProxySQL admin user
local is_proxysql_admin_user
is_proxysql_admin_user=$(proxysql_admin_user_check "${username}")
if [[ $is_proxysql_admin_user -eq 1 ]]; then
echo -e "\nNote : '${username}' is in proxysql admin user list, this user cannot be added to ProxySQL"\
"\n-- (For more info, see https://github.com/sysown/proxysql/issues/709)"
else
# Verify that the user does not exist in ProxySQL
check_user=$(proxysql_exec "$LINENO" "SELECT username from mysql_users where username='${username}'")
if [[ -z $check_user ]]; then
# It is confirmed that the user does not exist in ProxySQL, let's add it
echo "Adding user to ProxySQL: ${username}"
proxysql_exec "$LINENO" \
"INSERT INTO mysql_users
(username, password, active, default_hostgroup)
VALUES
('${username}', UNHEX('${password}'), 1, $WRITER_HOSTGROUP_ID)"
check_cmd $? "$LINENO" "Failed to add the user (${username}) from PXC to ProxySQL database."\
"\n-- Please check the ProxySQL connection parameters and status."
if [[ $ADD_QUERY_RULE -eq 1 ]];then
add_query_rule "${username}"
fi
changes_made=1
else
echo "Cannot add the user (${username}). The user (${username}) already exists in ProxySQL database with different hostgroup."
check_user=""
fi
fi
done # End of for loop
# Remove users from ProxySQL that do not exist in MySQL
if [[ $SYNCUSERS -eq 1 ]]; then
# Get all proxysql users
proxysql_users=$(get_proxysql_users)
# Unset the hash
declare -A proxysql_users_full_hash
# Populate ProxySQL users hash
while read -r proxysql_user; do
if [[ -z $proxysql_user ]]; then
continue
fi
# Extract ProxySQL username
username=$(echo "$proxysql_user" | cut -f1)
password=$(echo "$proxysql_user" | cut -f2)
# Add ProxySQL user to hash
proxysql_users_full_hash["${username}"]="$password"
done < <(printf "%s\n" "${proxysql_users}")
# Check ProxySQL users against MySQL
for username in "${!proxysql_users_full_hash[@]}"; do
debug "$LINENO" "Processing ProxySQL user(for removal):${username}"
if [[ -z ${mysql_users_full_hash["${username}"]} ]]; then
# Delete the ProxySQL user
echo -e "\nRemoving (non-MySQL) user from ProxySQL: ${username}"
proxysql_exec "$LINENO" "DELETE FROM mysql_users WHERE username='${username}' and default_hostgroup=$WRITER_HOSTGROUP_ID"
check_cmd $? "$LINENO" "Failed to delete the user (${username}) from ProxySQL database."\
"\n-- Please check the ProxySQL connection parameters and status."
proxysql_exec "$LINENO" "DELETE FROM mysql_query_rules WHERE username='${username}' and destination_hostgroup in($WRITER_HOSTGROUP_ID,$READER_HOSTGROUP_ID)"
check_cmd $? "$LINENO" "Failed to delete the query rule for user (${username}) from ProxySQL database."\
"\n-- Please check the ProxySQL connection parameters and status."
changes_made=1
fi
done
fi
# Save the configuration
if [[ $changes_made -eq 1 ]]; then
proxysql_load_to_runtime_save_to_disk "MYSQL USERS" $LINENO
proxysql_load_to_runtime_save_to_disk "MYSQL QUERY RULES" $LINENO
fi
# Re-enable nounset
set -o nounset
}
# Returns a list of users from the ProxySQL database
#
# Globals:
# WRITER_HOSTGROUP_ID
#
# Arguments:
# None
#
function get_proxysql_users() {
local proxysql_users
proxysql_users=$(proxysql_exec "$LINENO" \
"SELECT username,HEX(password)
FROM mysql_users
WHERE password!='' AND default_hostgroup=$WRITER_HOSTGROUP_ID" "" "hide_output")
check_cmd $? "$LINENO" "Failed to load user list from ProxySQL database."\
"\n-- Please check the ProxySQL connection parameters and status."
proxysql_users=$(echo "$proxysql_users" |
sort |
uniq )
printf "%s" "$proxysql_users"
}
# Checks if a user is a ProxySQL admin user
#
# Globals:
# None
#
# Arguments:
# 1: the name of the use to be checked
#
# Outputs (to stdout):
# 1 if the user is a proxysql admin user
# 0 if the user is not a proxysql admin user
#
function proxysql_admin_user_check(){
local userchk=$1
local proxysql_admin_users
local is_proxysql_admin_user
mapfile -t proxysql_admin_users < <(proxysql_exec "$LINENO" \
"select variable_value
from global_variables
where variable_name like 'admin-%_credentials'" |
sed 's/;/\n/g'|
cut -d':' -f1 |
grep -v variable_value)
# shellcheck disable=SC2199
if [[ " ${proxysql_admin_users[@]} " =~ [[:space:]]${userchk}[[:space:]] ]]; then
is_proxysql_admin_user=1
else
is_proxysql_admin_user=0
fi
printf "%s" "$is_proxysql_admin_user"
}
# Returns success if the native scheduler is in use.
# Returns success(0) if the native proxysql scheduler is in use.
# Else returns failure(1)
#
# In this case, the native scheduler is used if entries are
# found in mysql_galera_hostgroups, mysql_group_replication_hostgroups,
# or mysql_replication_hostgroups.
#
# Globals:
# None
#
# Arguments:
# Parameter 1: line number
#
# Returns:
# 0 (success) the native scheduler is being used
# 1 (failure) the native scheduler is not being used
# 255 (failure) error, unable to connect to proxysql
#
function is_native_scheduler_in_use()
{
local lineno=$1
local count
count=$(proxysql_exec "$LINENO" \
"SELECT COUNT(*)
FROM runtime_mysql_group_replication_hostgroups
WHERE active=1")
[[ $? -ne 0 ]] && return 255
[[ $count -gt 0 ]] && return 0
count=$(proxysql_exec "$LINENO" \
"SELECT COUNT(*)
FROM runtime_mysql_galera_hostgroups
WHERE active=1")
[[ $? -ne 0 ]] && return 255
[[ $count -gt 0 ]] && return 0
count=$(proxysql_exec "$LINENO" \
"SELECT COUNT(*)
FROM runtime_mysql_replication_hostgroups")
[[ $? -ne 0 ]] && return 255
[[ $count -gt 0 ]] && return 0
return 1
}
function is_go_scheduler_in_use()
{
local lineno=$1
local count
count=$(proxysql_exec "$LINENO" \
"SELECT COUNT(*)
FROM runtime_scheduler
WHERE active=1 AND
filename LIKE '%pxc_scheduler_handler'")
[[ $? -ne 0 ]] && return 255
[[ $count -gt 0 ]] && return 0
return 1
}
function is_other_scheduler_in_use()
{
local lineno=$1
local count
count=$(proxysql_exec "$LINENO" \
"SELECT COUNT(*)
FROM runtime_scheduler
WHERE active=1 AND
filename NOT LIKE '%pxc_scheduler_handler'")
[[ $? -ne 0 ]] && return 255
[[ $count -gt 0 ]] && return 0
return 1
}
function native_scheduler_capability_check()
{
local lineno=$1
if is_go_scheduler_in_use "${lineno}"; then
# TODO: better error messages
error "${lineno}" "Unable to setup the native ProxySQL scheduler." \
"\n---- The Percona go scheduler is currently in use" \
"\n---- and must be removed."
return 1;
fi
if is_other_scheduler_in_use "${lineno}"; then
# TODO: better error messages
error "${lineno}" "Unable to setup the native ProxySQL scheduler." \
"\n---- Another scheduler is currently in use" \
"\n---- and must be removed."
return 1;
fi
return 0;
}
function go_scheduler_capability_check()
{
local lineno=$1
if is_native_scheduler_in_use "${lineno}"; then
# TODO: better error messages
error "${lineno}" "Unable to setup the Percona go scheduler." \
"\n---- The ProxySQL native scheduler is in use" \
"\n---- and must be removed."
return 1;
fi
if is_other_scheduler_in_use "${lineno}"; then
# TODO: better error messages
error "${lineno}" "Unable to setup the Percona go scheduler." \
"\n---- Another scheduler is in use" \
"\n---- and must be removed."
return 1;
fi
return 0;
}