├── .gitmodules ├── LICENSE ├── README.md ├── build_scheduler.sh ├── config.toml ├── doc ├── _images │ └── Percona_Logo_Color.png ├── proxysql_galera_checker.md └── release-notes │ ├── proxysql-admin_v1.3.0.md │ ├── proxysql-admin_v1.3.2a.md │ ├── proxysql-admin_v1.3.5.md │ └── proxysql-admin_v1.3.6.md ├── percona-scheduler-admin ├── proxysql-admin ├── proxysql-admin-common ├── proxysql-admin.cnf ├── proxysql-common ├── proxysql-login-file ├── proxysql-logrotate ├── proxysql-status ├── tests ├── bad-login-file.clear.cnf ├── bad-login-file.cnf ├── bad-login-file2.clear.cnf ├── bad-login-file2.cnf ├── disable_proxysql.bats ├── generic-test.bats ├── login-file.clear.cnf ├── login-file.cnf ├── login-file2.clear.cnf ├── login-file2.cnf ├── proxysql-admin-testsuite.bats ├── proxysql-admin-testsuite.sh ├── scheduler-admin-testsuite.bats ├── scheduler-args.bats ├── setup_workdir.sh ├── test-common.bash └── testsuite.toml └── tools ├── enable_scheduler ├── mysql_exec ├── proxysql_exec └── run_galera_checker /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "percona-scheduler"] 2 | path = percona-scheduler 3 | url = https://github.com/percona/pxc_scheduler_handler 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Percona's build for ProxySQL with Admin tools 3 | 4 | ![Percona logo](doc/_images/Percona_Logo_Color.png) 5 | 6 | ## ProxySQL information 7 | 8 | For information on ProxySQL, see [the ProxySQL Documentation](https://proxysql.com/documentation/), or review [the ProxySQL GitHub repository](https://github.com/sysown/proxysql). 9 | 10 | ## Percona's build for ProxySQL information 11 | 12 | The Percona's build for ProxySQL has the following tools: 13 | 14 | - [ProxySQL Admin](#proxysql-admin) 15 | - [ProxySQL Status](#proxysql-status) 16 | - [Percona Scheduler Admin](#percona-scheduler-admin) 17 | 18 | For more information about Percona's build of ProxySQL and the ProxySQL Admin tools, see the [Percona build of ProxySQL and ProxySQL Admin Utilities documentation](https://docs.percona.com/proxysql/index.html). 19 | 20 | The documentation source is in the [proxysql-admin-tool-doc repository](https://github.com/percona/proxysql-admin-tool-doc). 21 | 22 | ## Installation 23 | 24 | You can install Percona's build of ProxySQL and the ProxySQL Admin utilities with either of the following methods: 25 | 26 | - [Use the Percona repository](https://docs.percona.com/proxysql/where-to-download-proxysql.html) 27 | 28 | - [Build from source code](https://github.com/percona/proxysql-admin-tool) 29 | 30 | ## Submit a Bug report or feature request 31 | 32 | If you find a bug in any of the ProxySQL tools, [you can submit a report to the project's Jira tracker](https://jira.percona.com/projects/PSQLADM). 33 | 34 | As a general rule of thumb, please try to create a bug report that has the following qualities: 35 | 36 | - Reproducible - include the steps on how to reproduce the issue 37 | 38 | - Specific - include as much detail as possible, such as which version, environment, etc. 39 | 40 | - Unique - do not duplicate existing tickets 41 | 42 | - Scoped to a single issue - only one issue per report 43 | 44 | ## Contact 45 | 46 | [You can reach us by using the Percona Community Forum](https://forums.percona.com/c/polyglot-projects/proxysql) 47 | 48 | We encourage contributions and are always looking for new members that are as dedicated to serving the community as we are. 49 | -------------------------------------------------------------------------------- /build_scheduler.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script will assist with building the pxc_scheduler_handler 3 | # Version 2.0 4 | ############################################################################################### 5 | 6 | # This program is copyright 2016-2020 Percona LLC and/or its affiliates. 7 | # 8 | # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED 9 | # WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 10 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 11 | # 12 | # This program is free software; you can redistribute it and/or modify it under 13 | # the terms of the GNU General Public License as published by the Free Software 14 | # Foundation, version 2 or later 15 | # 16 | # You should have received a copy of the GNU General Public License version 2 17 | # along with this program; if not, write to the Free Software Foundation, Inc., 18 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 19 | 20 | # shellcheck disable=SC1094,SC2181,SC1091 21 | 22 | source ./proxysql-common || exit 23 | 24 | if [ ! -d percona-scheduler ]; then 25 | git submodule update --init 26 | fi 27 | 28 | pushd percona-scheduler || exit 29 | 30 | if [[ ! -e $(command -v go 2> /dev/null) ]]; then 31 | error "" "go packages not found. Please install golang package." 32 | exit 1 33 | fi 34 | 35 | go mod tidy 36 | go build -v -a -ldflags "-X main.pxcSchedulerHandlerVersion=${PROXYSQL_ADMIN_VERSION}" -o pxc_scheduler_handler 37 | 38 | if [ $? -ne 0 ]; then 39 | error "" "go build process failed with errors. Exiting.." 40 | exit 1 41 | fi 42 | 43 | popd || exit 44 | 45 | cp percona-scheduler/pxc_scheduler_handler . 46 | echo -e "Build was successful. The binary can be found in ./pxc_scheduler_handler" 47 | 48 | echo -e 49 | ./pxc_scheduler_handler --version 50 | -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | # For the detailed manual, see 2 | # https://github.com/percona/pxc_scheduler_handler#how-to-configure-pxc-scheduler-handler 3 | # 4 | 5 | [pxccluster] 6 | activeFailover = 1 7 | failBack = false 8 | checkTimeOut = 2000 9 | mainSegment = 0 10 | sslClient = "client-cert.pem" 11 | sslKey = "client-key.pem" 12 | sslCa = "ca.pem" 13 | sslCertificatePath = "/opt/cert/ssl_test" 14 | hgW = 100 15 | hgR = 101 16 | configHgRange =8000 17 | maintenanceHgRange =9000 18 | 19 | # -------------------------------- 20 | # Set to true if there is a single writer node. If this is set, 21 | # then maxNumWriters is assumed to be 1. 22 | # 23 | # Allowable values: true,false 24 | # Default: false 25 | # 26 | singlePrimary = true 27 | 28 | # -------------------------------- 29 | # Set to the number of writer nodes desired. 30 | # 31 | # The value of this is assumed to be 1 if singlePrimary is true. 32 | # 33 | # If this is set to a value from 1 to 100, then the query rules 34 | # are setup for a distinct writer hostgroup (writes are sent to the 35 | # writer hostgroup and read are sent to the reader hostgroup). 36 | # 37 | # If this is set to a value > 100, then all queries (writes and reads) 38 | # are sent to the writer hostgroup. This is assumed to be a 39 | # load-balancing scenario, where all nodes are equivalent and accept 40 | # both reads and writes. 41 | # 42 | # Default: (none) 43 | # 44 | maxNumWriters = 1 45 | writerIsAlsoReader = 1 46 | retryUp = 0 47 | retryDown = 2 48 | clusterId = 10 49 | 50 | # Controls the primary settings during failover. 51 | # More details at https://github.com/percona/pxc_scheduler_handler#persist-primary-values 52 | # 53 | # Allowed values: 54 | # 55 | # 0 Disable 56 | # 1 Persist only write settings 57 | # 2 Persist both read and write settings 58 | persistPrimarySettings=0 59 | 60 | 61 | 62 | # == proxysql =================================================== 63 | # The proxysql section is for ProxySQL-specific information. 64 | # 65 | # These settings will be read and used whenever the scheduler is run. 66 | # 67 | [proxysql] 68 | port = 6032 69 | host = "127.0.0.1" 70 | user = "" 71 | password = "" 72 | clustered = false 73 | lockfilepath ="/var/run/pxc_scheduler_handler" 74 | respectManualOfflineSoft=false 75 | 76 | 77 | 78 | #== global ====================================================== 79 | # The global section are for variables that are not ProxySQL or 80 | # cluster specific. 81 | # 82 | # These settings will be read and used whenever the scheduler is run. 83 | # 84 | [global] 85 | debug = true 86 | 87 | # stdout: output is redirected to proxysql logs 88 | # file: output is written to the file pointed by logFile. When setting to 89 | # 'file', ensure that the user proxysql:proxysql has the appropriate 90 | # permissions to write to this location. 91 | logTarget = "stdout" #stdout | file 92 | 93 | # Defines the log level to be used. 94 | # Allowed options are [error,warning,info,debug] 95 | logLevel = "info" 96 | logFile = "/var/log/pxc_scheduler_handler/pscheduler.log" 97 | 98 | # Should be set to false if we are pxc_scheduler_handler through percona-scheduler-admin. 99 | daemonize = false 100 | daemonInterval = 2000 101 | 102 | # boolean variable which enables reporting of statistics. 103 | performance = true 104 | 105 | # Not used currently 106 | OS = "na" 107 | 108 | # Time in seconds after which the file lock is considered expired [local instance lock] 109 | lockFileTimeout = 60 #seconds 110 | 111 | # Time in seconds after which the cluster lock is considered expired 112 | lockClusterTimeout = 600 #seconds 113 | 114 | 115 | 116 | #== setup ======================================================= 117 | # These variables are used only upon Setup 118 | # Changing these variables after setup will not affect operation 119 | # 120 | [setup] 121 | 122 | # -------------------------------- 123 | # The clusterAppUser is the ProxySQL user account that should be 124 | # used by clients to access the cluster. 125 | # 126 | # Uncomment the following options (clusterAppUser and clusterAppUserPassword) 127 | # to enable the setting of the clusterAppUser for this cluster. 128 | # 129 | #clusterAppUser="proxysql_user" 130 | #clusterAppUserPassword="passw0rd" 131 | 132 | # -------------------------------- 133 | # The monitorUser is used by ProxySQL to access the servers and 134 | # check the connections. 135 | # 136 | monitorUser="monitor" 137 | monitorUserPassword="monitor" 138 | 139 | # -------------------------------- 140 | # The clusterXXX information is used to setup the cluster for 141 | # use by ProxySQL. 142 | # 143 | clusterHost="" 144 | clusterPort=3306 145 | clusterUser="admin" 146 | clusterUserPassword="admin" 147 | 148 | # -------------------------------- 149 | # ProxySQL will use SSL to connect to the backend servers 150 | # 151 | useSSL=0 152 | 153 | # -------------------------------- 154 | # Max number of connections from ProxySQL to the backend servers. 155 | # 156 | maxConnections=1000 157 | 158 | # -------------------------------- 159 | # Defines how frequently (in milliseconds) the scheduler must be run 160 | # 161 | nodeCheckInterval=2000 162 | -------------------------------------------------------------------------------- /doc/_images/Percona_Logo_Color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/percona/proxysql-admin-tool/c642b6ff3f48f1625fb4b18f1dd4cfe19729a849/doc/_images/Percona_Logo_Color.png -------------------------------------------------------------------------------- /doc/proxysql_galera_checker.md: -------------------------------------------------------------------------------- 1 | # proxysql_galera_checker usage info. 2 | 3 | `proxysql_galera_checker` script will check Percona XtraDB Cluster desynced nodes, and temporarily deactivate them. Currently, this script is developed to work with proxysql-admin [script](https://github.com/percona/proxysql-admin-tool/blob/v2.0/README.md) 4 | 5 | This script will also call `proxysql_node_monitor` script. Monitor script will check cluster node membership, and re-configure ProxySQL if cluster membership changes occur. 6 | 7 | eg: If any node goes out from cluster this script will mark as `OFFLINE_HARD` in proxysql database. When it comes back it will mark the node as `ONLINE`. 8 | 9 | The galera checker script will be added in ProxySQL [scheduler](https://github.com/sysown/proxysql/blob/master/doc/scheduler.md) table if you use `proxysql-admin` script. 10 | 11 | Galera checker usage 12 | ``` 13 | Usage: proxysql_galera_checker [hostgroup_id read] [number writers] [writers are readers 0|1] [log_file] 14 | 15 | - HOSTGROUP WRITERS (required) (0..) The hostgroup_id that contains nodes that will server 'writes' 16 | - HOSTGROUP READERS (optional) (0..) The hostgroup_id that contains nodes that will server 'reads' 17 | - NUMBER WRITERS (optional) (0..) Maximum number of write hostgroup_id node that can be marked ONLINE 18 | When 0 (default), all nodes can be marked ONLINE 19 | - WRITERS ARE READERS (optional) (0|1) When 1 (default), ONLINE nodes in write hostgroup_id will prefer not 20 | to be ONLINE in read hostgroup_id 21 | - LOG_FILE (optional) file logfile where node state checks & changes are written to (verbose) 22 | 23 | - LOG_FILE (optional) file logfile where node state checks & changes are written to (verbose) 24 | 25 | 26 | Notes about the mysql_servers in ProxySQL: 27 | 28 | - WEIGHT Hosts with a higher weight will be prefered to be put ONLINE 29 | - NODE STATUS * Nodes that are in status OFFLINE_HARD will not be checked nor will their status be changed 30 | * SHUNNED nodes are not to be used with Galera based systems, they will be checked and status 31 | will be changed to either ONLINE or OFFLINE_SOFT. 32 | ``` 33 | 34 | You can configure these parameter in scheduler table with custom configuration as follows: 35 | ``` 36 | arg1: HOSTGROUP WRITERS 37 | arg2: HOSTGROUP READERS 38 | arg3: NUMBER WRITERS 39 | arg4: WRITERS ARE READERS 40 | arg5: LOG_FILE 41 | ``` 42 | scheduler table entry. 43 | ``` 44 | mysql> select * from scheduler\G 45 | *************************** 1. row *************************** 46 | id: 11 47 | active: 1 48 | interval_ms: 5000 49 | filename: /bin/proxysql_galera_checker 50 | arg1: 10 51 | arg2: 11 52 | arg3: 1 53 | arg4: 1 54 | arg5: /var/lib/proxysql/cluster_one_proxysql_galera_check.log 55 | comment: cluster_one 56 | ``` 57 | 58 | You can also use galera checker script with custom PXC proxysql configurations. But there are some limitations to this configuration. 59 | -------------------------------------------------------------------------------- /doc/release-notes/proxysql-admin_v1.3.0.md: -------------------------------------------------------------------------------- 1 | # proxysql-admin v1.3.0 , v1.3.0a, v1.3.0b 2 | 3 | ## proxysql-admin v1.3.0b 4 | 5 | Release date : Nov 17, 2016 6 | 7 | ### Usability improvement 8 | 9 | * Added document updates for single write mode 10 | * Packaging: Added version number 11 | 12 | ### New features 13 | 14 | * Added PXC Maintenance Mode support 15 | * Percona Xtradb Cluster implemented a maintenance mode to reduce the abrupt failure of workload while the node is taken down. 16 | With this new change in PXC, ProxySQL galera checker willl continue to probe the state of individual node by checking for pxc_maint_mode (in addition to existing wsrep_local_state). 17 | If ProxySQL detects pxc_maint_mode = SHUTDOWN|MAINTENANCE then it marks the node as OFFLINE_SOFT. This will avoid creation of new connections (or workload) on said node. 18 | 19 | ## proxysql-admin v1.3.0a 20 | 21 | Release date : Oct 20, 2016 22 | 23 | ### New features 24 | 25 | * Added singlewrite mode in proxysql-admin 26 | * Added proxysql-admin logrotate support 27 | 28 | ### Bug fixes 29 | 30 | * FIXED BLD-524 - proxysql galera check log file warning messages for credentials 31 | * Removed hardcoded hostgroup id from proxysql-admin script [#1](../../../../issues/1) 32 | -------------------------------------------------------------------------------- /doc/release-notes/proxysql-admin_v1.3.2a.md: -------------------------------------------------------------------------------- 1 | # proxysql-admin v1.3.2a 2 | 3 | Release date : Jan 12, 2017 4 | 5 | ### Usability improvement 6 | 7 | * Changed proxysql-admin default mode from loadbal to singlewrite 8 | * Modified proxysql-admion script to read configuration file by default. 9 | * Improved proxysql-admin dsn connection error message. 10 | * Changed PXC default port to 3306 in configuration file. 11 | * Resolved mismatch between command line options and EXPORT variables. 12 | * Added network restriction to ProxySQL users in PXC #16 13 | * Renamed following parameter options 14 | * --proxysql-user to --proxysql-username 15 | * --cluster-user to --cluster-username 16 | * --monitor-user to --monitor-username 17 | * --galera-check-interval to --node-check-interval 18 | * --proxysql-host to --proxysql-hostname 19 | * --cluster-host to --cluster-hostname 20 | * Repalaced following parameter options 21 | * --pxc-app-write-user to --cluster-app-username 22 | * --pxc-app-write-password to --cluster-app-password 23 | * Reomoved following parameter options 24 | * --pxc-app-read-user 25 | * --pxc-app-read-password 26 | 27 | 28 | 29 | ### New features 30 | 31 | * Modified proxysql-admin script to create single user for handling read write transactions. proxysql will manage traffic automatically and split read and write operations with the help of mysql query rule and hostgroups. 32 | 33 | All SELECT operations (except 'SELECT .. FOR UPDATE') will go to read nodes and all other transactions will go to writer node. 34 | * Added __--quick-demo__ to setup dummy proxysql configuration 35 | 36 | ### Bug fixes 37 | 38 | * Fixed .mylogin.cnf issue. Now proxysql-admin user configuration will not override with .mylogin.cnf variables. [#14](../../../../issues/14) 39 | * Fixed BLD-600 40 | -------------------------------------------------------------------------------- /doc/release-notes/proxysql-admin_v1.3.5.md: -------------------------------------------------------------------------------- 1 | # proxysql-admin v1.3.5 2 | 3 | Release date : Apr 14, 2017 4 | 5 | ### Usability improvement 6 | 7 | * Merged upstream proxysql_galera_checker.sh changes to proxysql_galera_checker. 8 | * Added proxysql_node_monitor inside proxysql_galera_checker script to avoid scheduler table conflict. 9 | 10 | Currently we have two different entry in scheduler table to check PXC node status (proxysql_node_monitor and proxysql_galera_checker ). 11 | These two scripts are independent but it uses same mysql_servers table to change the PXC node status. Sometime these scripts will run together and update mysql_servers table. As per proxysql, scheduler entries should not interfere with each other. 12 | To overcome this issue proxysql_galera_checker script should call proxysql_node_monitor script to check node monitoring independently. 13 | 14 | * Added hostgroup info and date in proxysql node monitoring log. 15 | -------------------------------------------------------------------------------- /doc/release-notes/proxysql-admin_v1.3.6.md: -------------------------------------------------------------------------------- 1 | # proxysql-admin v1.3.6 2 | 3 | Release date : May 12, 2017 4 | 5 | ### Usability improvement 6 | 7 | * Improved proxysql-admin mysql connection error message. 8 | * Made --write-node optional with singlewrite mode. 9 | * Added notes in /etc/proxysql-admin.cnf for each of the parameters. 10 | * Added writer node sanity check with wsrep_incoming_addresses 11 | 12 | ### Bug fixes 13 | 14 | * Fixed PQA-145 : proxysql-admin --quick-demo mode does not set auto-configure 15 | 16 | -------------------------------------------------------------------------------- /proxysql-admin-common: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script contains functions common to proxysql-admin related scripts. 3 | # (currently only Percona XtraDB cluster in combination with ProxySQL is supported) 4 | # Version 2.0 5 | ############################################################################################### 6 | 7 | # This program is copyright 2016-2022 Percona LLC and/or its affiliates. 8 | # 9 | # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED 10 | # WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 11 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 12 | # 13 | # This program is free software; you can redistribute it and/or modify it under 14 | # the terms of the GNU General Public License as published by the Free Software 15 | # Foundation, version 2 or later 16 | # 17 | # You should have received a copy of the GNU General Public License version 2 18 | # along with this program; if not, write to the Free Software Foundation, Inc., 19 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 20 | 21 | # shellcheck disable=SC2181 22 | 23 | #------------------------------------------------------------------------------- 24 | # 25 | # Step 1 : Bash internal configuration 26 | # 27 | 28 | 29 | #------------------------------------------------------------------------------- 30 | # 31 | # Step 2 : Global variables 32 | # 33 | 34 | 35 | #------------------------------------------------------------------------------- 36 | # 37 | # Step 3 : Helper functions 38 | # 39 | 40 | 41 | # Executes a SQL query with the (fully) specified server 42 | # 43 | # Globals: 44 | # DEBUG 45 | # TIMEOUT 46 | # 47 | # Arguments: 48 | # 1: lineno 49 | # 2: the name of the user 50 | # 3: the user's password 51 | # 4: the hostname of the server 52 | # 5: the port used to connect to the server 53 | # 6: the query to be run 54 | # 7: (optional) arguments to the mysql client 55 | # 8: (optional) additional options, space separated 56 | # Available options: 57 | # "hide_output" 58 | # This will not show the output of the query when DEBUG is set. 59 | # Used to stop the display of sensitve information (such as passwords) 60 | # from being displayed when debugging. 61 | # 62 | function exec_sql() { 63 | local lineno=$1 64 | local user=$2 65 | local password=$3 66 | local hostname=$4 67 | local port=$5 68 | local query=$6 69 | local args="" 70 | local more_options="" 71 | local retvalue 72 | local retoutput 73 | local default_auth="" 74 | local defaults="" 75 | 76 | if [[ $# -ge 7 ]]; then 77 | args=$7 78 | fi 79 | 80 | if [[ $# -ge 8 ]]; then 81 | more_options=$8 82 | fi 83 | 84 | debug "$lineno" "exec_sql : $user@$hostname:$port ==> $query" 85 | 86 | if [[ $MYSQL_CLIENT_VERSION == "8.0" ]]; then 87 | default_auth="default-auth=mysql_native_password" 88 | fi 89 | 90 | defaults=$(printf '[client]\nuser=%s\npassword="%s"\nhost=%s\nport=%s\nconnect-timeout=%s\n%s' \ 91 | "${user}" \ 92 | "${password}" \ 93 | "${hostname}" \ 94 | "${port}" \ 95 | "${TIMEOUT}" \ 96 | "${default_auth}" 97 | ) 98 | 99 | # shellcheck disable=SC2086 100 | if [[ $USE_STDIN_FOR_CREDENTIALS -eq 1 ]]; then 101 | retoutput=$(printf "%s" "${defaults}" | mysql --defaults-file=/dev/stdin --protocol=tcp --unbuffered --batch --silent ${args} -e "$query") 102 | retvalue=$? 103 | else 104 | retoutput=$(mysql --defaults-file=<(echo "${defaults}") --protocol=tcp --unbuffered --batch --silent ${args} -e "$query") 105 | retvalue=$? 106 | fi 107 | 108 | if [[ $DEBUG -eq 1 ]]; then 109 | local number_of_newlines=0 110 | local dbgoutput=$retoutput 111 | 112 | if [[ " $more_options " =~ [[:space:]]hide_output[[:space:]] ]]; then 113 | dbgoutput="**** data hidden ****" 114 | fi 115 | 116 | if [[ -n $dbgoutput ]]; then 117 | number_of_newlines=$(printf "%s" "${dbgoutput}" | wc -l) 118 | fi 119 | 120 | if [[ $retvalue -ne 0 ]]; then 121 | debug "" "--> query failed $retvalue" 122 | elif [[ -z $dbgoutput ]]; then 123 | debug "" "--> query returned $retvalue : " 124 | elif [[ ${number_of_newlines} -eq 0 ]]; then 125 | debug "" "--> query returned $retvalue : ${dbgoutput}" 126 | else 127 | debug "" "--> query returned $retvalue : " 128 | printf "%s\n" "${dbgoutput}" | while IFS= read -r line; do 129 | debug "" "----> $line" 130 | done 131 | fi 132 | fi 133 | 134 | printf "%s" "${retoutput}" 135 | return $retvalue 136 | } 137 | 138 | 139 | # Executes a SQL query on proxysql 140 | # 141 | # Globals: 142 | # PROXYSQL_USERNAME 143 | # PROXYSQL_PASSWORD 144 | # PROXYSQL_HOSTNAME 145 | # PROXYSQL_PORT 146 | # 147 | # Arguments: 148 | # 1: lineno 149 | # 2: The SQL query 150 | # 3: (optional) Additional arguments to the mysql client for the query 151 | # 4: (optional) more options, see exec_sql 152 | # 153 | function proxysql_exec() { 154 | local lineno=$1 155 | local query=$2 156 | local args="" 157 | local more_options="" 158 | 159 | if [[ $# -ge 3 ]]; then 160 | args=$3 161 | fi 162 | 163 | if [[ -z $args ]]; then 164 | args="--skip-column_names" 165 | fi 166 | 167 | if [[ $# -ge 4 ]]; then 168 | more_options=$4 169 | fi 170 | 171 | exec_sql "$lineno" "$PROXYSQL_USERNAME" "$PROXYSQL_PASSWORD" \ 172 | "$PROXYSQL_HOSTNAME" "$PROXYSQL_PORT" \ 173 | "$query" "$args" "$more_options" 174 | 175 | return $? 176 | } 177 | 178 | 179 | # Executes a SQL query on a specific node in the cluster 180 | # 181 | # Globals: 182 | # CLUSTER_USERNAME 183 | # CLUSTER_PASSWORD 184 | # 185 | # Arguments: 186 | # 1: lineno 187 | # 2: cluster host 188 | # 3: cluster port 189 | # 4: The SQL query 190 | # 5: Additional arguments to the mysql client for the query 191 | # 6: (optional) more options, see exec_sql 192 | # 193 | function cluster_exec() { 194 | local lineno=$1 195 | local cluster_host=$2 196 | local cluster_port=$3 197 | local query=$4 198 | local args="" 199 | local more_options="" 200 | 201 | if [[ $# -ge 5 ]]; then 202 | args=$5 203 | fi 204 | 205 | if [[ $# -ge 6 ]]; then 206 | more_options=$6 207 | fi 208 | 209 | exec_sql "$lineno" "$CLUSTER_USERNAME" "$CLUSTER_PASSWORD" \ 210 | "$cluster_host" "$cluster_port" \ 211 | "$query" "$args" "$more_options" 212 | 213 | return $? 214 | } 215 | 216 | 217 | # Executes a SQL query on the CLUSTER_HOSTNAME/CLUSTER_PORT 218 | # specified in the config file. 219 | # 220 | # Globals: 221 | # CLUSTER_USERNAME 222 | # CLUSTER_PASSWORD 223 | # CLUSTER_HOSTNAME 224 | # CLUSTER_PORT 225 | # 226 | # Arguments: 227 | # 1: lineno 228 | # 2: The SQL query 229 | # 3: Additional arguments to the mysql client for the query 230 | # 4: (optional) more options, see exec_sql 231 | # 232 | function mysql_exec() { 233 | local lineno=$1 234 | local query=$2 235 | local args="" 236 | local more_options="" 237 | 238 | if [[ $# -ge 3 ]]; then 239 | args=$3 240 | fi 241 | 242 | if [[ $# -ge 4 ]]; then 243 | more_options=$4 244 | fi 245 | 246 | cluster_exec "$lineno" "$CLUSTER_HOSTNAME" "$CLUSTER_PORT" \ 247 | "$query" "$args" "$more_options" 248 | 249 | return $? 250 | } 251 | 252 | 253 | # Executes a SQL query on a cluster ndde with the monitor credentials 254 | # 255 | # Globals: 256 | # CLUSTER_HOSTNAME 257 | # CLUSTER_PORT 258 | # 259 | # Arguments: 260 | # 1: lineno 261 | # 2: The monitor username 262 | # 3: The monitor password 263 | # 4: Additional arguments to the mysql client for the query 264 | # 5: The SQL query 265 | # 6: (optional) more options, see exec_sql 266 | # 267 | function monitor_exec() { 268 | local lineno=$1 269 | local user=$2 270 | local password=$3 271 | local args=$4 272 | local query=$5 273 | local more_options="" 274 | 275 | if [[ $# -ge 6 ]]; then 276 | more_options=$7 277 | fi 278 | 279 | exec_sql "$lineno" "$user" "$password" \ 280 | "$CLUSTER_HOSTNAME" "$CLUSTER_PORT" \ 281 | "$query" "$args" "$more_options" 282 | 283 | return $? 284 | } 285 | 286 | 287 | # This will move the configuration from memory to the runtime (load) 288 | # and from memory to disk (save) 289 | # 290 | # Globals: 291 | # None 292 | # 293 | # Arguments: 294 | # 1: the proxysql data that is being loaded and saved 295 | # (for example "MYSQL USERS" or "MYSQL SERVERS"). 296 | # 2: the lineno where this function was invoked 297 | # 3: (optional) call "SAVE $1 FROM RUNTIME" after 298 | # loading data to runtime. This is done for 299 | # MYSQL USERS to load the encrypted version of the 300 | # password field. 301 | # 302 | # This function will exit the program if an error occurs while 303 | # loaded to runtime or saving to disk. 304 | # 305 | function proxysql_load_to_runtime_save_to_disk() { 306 | local data_type=$1 307 | local lineno=$2 308 | local reload_from_runtime=0 309 | 310 | if [[ $# -ge 3 ]]; then 311 | reload_from_runtime=$3 312 | fi 313 | 314 | proxysql_exec "$LINENO" "LOAD ${data_type} TO RUNTIME" 315 | check_cmd $? "$lineno" "Failed to load the ${data_type} configuration to runtime."\ 316 | "\n-- Please check the ProxySQL configuration and status." 317 | debug "$lineno" "Loaded ${data_type} to runtime" 318 | 319 | if [[ $reload_from_runtime -eq 1 ]]; then 320 | # This has a specific purpose for the MYSQL USERS 321 | # This will cause the password field to be loaded with the encrypted version 322 | # of the password field 323 | proxysql_exec "$LINENO" "SAVE ${data_type} FROM RUNTIME" 324 | check_cmd $? "$lineno" "Failed to load the ${data_type} configuration from the runtime."\ 325 | "\n-- Please check the ProxySQL configuration and status." 326 | debug "$lineno" "Saved ${data_type} from runtime" 327 | fi 328 | 329 | proxysql_exec "$LINENO" "SAVE ${data_type} TO DISK;" 330 | check_cmd $? "$lineno" "Failed to save the ${data_type} configuration to disk."\ 331 | "\n-- Please check the ProxySQL configuration and status." 332 | debug "$lineno" "Saved ${data_type} to disk" 333 | } 334 | 335 | 336 | # Update mysql server version details in proxysql db 337 | # Globals: 338 | # WRITER_HOSTGROUP_ID 339 | # CLUSTER_HOSTNAME (overwrites) 340 | # CLUSTER_PORT (overwrites) 341 | # 342 | # Arguments: 343 | # None 344 | function update_mysql_version() 345 | { 346 | local cluster_node 347 | 348 | # Find a cluster node that belongs to the cluster with $WRITER_HOSTGROUP_ID 349 | cluster_node=$(find_cluster_node "$WRITER_HOSTGROUP_ID") 350 | check_cmd $? "$LINENO" "Could not find a primary cluster node" 351 | 352 | # Reset the central cluster node (so that calls to mysql_exec) will 353 | # work with this new node, rather than the node in the config file 354 | CLUSTER_HOSTNAME=$(echo -e "$cluster_node" | cut -f1) 355 | CLUSTER_PORT=$(echo -e "$cluster_node" | cut -f2) 356 | cluster_connection_check 357 | 358 | proxysql_mysql_version_string=$(proxysql_exec "$LINENO" "select variable_value from global_variables where variable_name like 'mysql-server_version'") 359 | check_cmd $? "$LINENO" "Failed to select the mysql-server_version variables from ProxySQL."\ 360 | "\n-- Please check the ProxySQL connection parameters and status." 361 | mysql_version_string=$(mysql_exec "$LINENO" "SELECT VERSION();" | tail -1 | cut -d'-' -f1 ) 362 | check_cmd $? "$LINENO" "Failed to select the mysql version info from Cluster node."\ 363 | "\n-- Please check the PXC connection parameters and status." 364 | if [[ $proxysql_mysql_version_string != "$mysql_version_string" ]]; then 365 | proxysql_exec "$LINENO" \ 366 | "UPDATE global_variables 367 | SET variable_value='$mysql_version_string' 368 | WHERE variable_name='mysql-server_version';" 369 | check_cmd $? "$LINENO" "Failed to set the mysql-server_version variables in ProxySQL."\ 370 | "\n-- Please check the ProxySQL connection parameters and status." 371 | echo "ProxySQL MySQL version changed to $mysql_version_string" 372 | proxysql_load_to_runtime_save_to_disk "MYSQL VARIABLES" $LINENO 373 | fi 374 | } 375 | 376 | # Adds an application user to ProxySQL 377 | # 378 | # Globals: 379 | # WRITER_HOSTGROUP_ID 380 | # CLUSTER_HOSTNAME (overwrites) 381 | # CLUSTER_PORT (overwrites) 382 | # 383 | # Arguments: 384 | # None 385 | # 386 | function adduser(){ 387 | debug "$LINENO" "adduser ()" 388 | 389 | local cluster_node 390 | 391 | # Find a cluster node that belongs to the cluster with $WRITER_HOSTGROUP_ID 392 | cluster_node=$(find_cluster_node "$WRITER_HOSTGROUP_ID") 393 | check_cmd $? "$LINENO" "Could not find a primary cluster node" 394 | 395 | # Reset the central cluster node (so that calls to mysql_exec) will 396 | # work with this new node, rather than the node in the config file 397 | CLUSTER_HOSTNAME=$(echo -e "$cluster_node" | cut -f1) 398 | CLUSTER_PORT=$(echo -e "$cluster_node" | cut -f2) 399 | cluster_connection_check 400 | 401 | local cluster_app_write_username 402 | local cluster_app_write_password 403 | local safe_cluster_app_write_username 404 | local safe_cluster_app_write_password 405 | 406 | echo -e "\nAdding PXC application user to the ProxySQL database" 407 | echo -n "Enter the PXC application user name: " 408 | read -r cluster_app_write_username 409 | while [[ -z "$cluster_app_write_username" ]] 410 | do 411 | echo -n "No input entered. Enter the PXC application user name: " 412 | read -r cluster_app_write_username 413 | done 414 | safe_cluster_app_write_username=${cluster_app_write_username//\'/\'\'} 415 | 416 | read -r -s -p "Enter the PXC application user password: " cluster_app_write_password 417 | while [[ -z "$cluster_app_write_password" ]] 418 | do 419 | read -r -s -p "No input entered. Enter the PXC application user password: " cluster_app_write_password 420 | done 421 | safe_cluster_app_write_password=${cluster_app_write_password//\'/\'\'} 422 | 423 | # check to see if the user already exists 424 | local check_user 425 | local check_cluster_user 426 | 427 | check_user=$(proxysql_exec "$LINENO" \ 428 | "SELECT username 429 | FROM mysql_users 430 | WHERE username='$safe_cluster_app_write_username'") 431 | check_cmd $? "$LINENO" "Could not retrieve the user from ProxySQL."\ 432 | "\n-- Check the ProxySQL connection parameters and status." 433 | if [[ -n "$check_user" ]]; then 434 | error "$LINENO" "The application user '$cluster_app_write_username' already exists in ProxySQL." 435 | exit 1 436 | fi 437 | 438 | check_cluster_user=$(mysql_exec "$LINENO" "SELECT user,host FROM mysql.user where user='$safe_cluster_app_write_username'") 439 | check_cmd $? "$LINENO" "Could not retrieve the user from PXC."\ 440 | "\n-- Check the PXC connection parameters and status." 441 | if [[ -z "$check_cluster_user" ]]; then 442 | local check_param 443 | echo -e "\n\n" 444 | read -r -p "The application user '$cluster_app_write_username' does not exist in PXC. Would you like to proceed [y/n] ? " check_param 445 | case $check_param in 446 | y|Y) 447 | proxysql_exec "$LINENO" \ 448 | "INSERT INTO mysql_users 449 | (username,password,active,default_hostgroup) 450 | VALUES 451 | ('$safe_cluster_app_write_username','$safe_cluster_app_write_password',1,$WRITER_HOSTGROUP_ID);" 452 | check_cmd $? "$LINENO" "Failed to add the PXC application user: '$cluster_app_write_username' to ProxySQL."\ 453 | "\n-- Please check the ProxySQL connection parameters and status." 454 | echo -e "\nPlease create the user ${BD}$cluster_app_write_username${NBD} in PXC to access the application through ProxySQL" 455 | ;; 456 | n|N) 457 | exit 0 458 | ;; 459 | *) 460 | error "" "Please type [y/n]!" 461 | exit 1 462 | ;; 463 | esac 464 | else 465 | proxysql_exec "$LINENO" \ 466 | "INSERT INTO mysql_users 467 | (username,password,active,default_hostgroup) 468 | values 469 | ('$safe_cluster_app_write_username','$safe_cluster_app_write_password',1,$WRITER_HOSTGROUP_ID);" 470 | check_cmd $? "$LINENO" "Failed to add the PXC application user: '$cluster_app_write_username' to ProxySQL."\ 471 | "\n-- Please check the ProxySQL connection parameters and status." 472 | fi 473 | 474 | proxysql_load_to_runtime_save_to_disk "MYSQL USERS" "$LINENO" "1" 475 | } 476 | 477 | # Check to see if the cluster is in mysql_galera_hostgroups 478 | # 479 | # Globals: 480 | # None 481 | # 482 | # Arguments: 483 | # Parameter 1 : the writer hostgroup id 484 | # 485 | function cluster_in_proxysql_check() { 486 | local write_hg=$1 487 | 488 | # As scheduler does not use the mysql_galera_hostgroups table, 489 | # skip this check when scheduler is active 490 | local is_scheduler_active 491 | is_scheduler_active=$(proxysql_exec "$LINENO" \ 492 | "SELECT active 493 | FROM runtime_scheduler 494 | WHERE comment like '% hgW:${write_hg}%'") 495 | 496 | 497 | if [[ $is_scheduler_active -eq 1 ]]; then 498 | return 499 | fi 500 | 501 | # Check to see if mysql_galera_hostgroups already has an 502 | # entry for the worker hostgroup 503 | local hostgroup_in_use 504 | hostgroup_in_use=$(proxysql_exec "$LINENO" \ 505 | "SELECT count(*) 506 | FROM mysql_galera_hostgroups 507 | WHERE 508 | writer_hostgroup = $write_hg") 509 | check_cmd $? "$LINENO" "Galera hostgroup retrieval failed."\ 510 | "\n-- Please check the ProxySQL connection parameters and status." 511 | 512 | # If there are no hostgroups with this writer hostgroup id 513 | # then return failure 514 | if [[ $hostgroup_in_use -eq 0 ]]; then 515 | error "$LINENO" "The cluster (with writer hostgroup:$WRITER_HOSTGROUP_ID) has not been configured in ProxySQL" 516 | exit 1 517 | fi 518 | } 519 | 520 | # Synchronizes the users between ProxySQL and PXC 521 | # 522 | # This function was created to auto sync all the existing users already 523 | # in MySQL to proxySQL's mysql_users table. As there is not much point 524 | # in having users in ProxySQL that don't exist in MySQL, this function 525 | # will delete any users from ProxySQL that were not found in MySQL. 526 | # 527 | # Going forward you can add/remove application users in MySQL then 528 | # rerun proxysql-admin with the --syncusers switch to replicate the changes 529 | # to ProxySQL. 530 | # 531 | # LIMITATIONS: Will not work properly in cases where the same user name 532 | # exists in MySQL with several hosts and different passwords. 533 | # This will cause ProxySQL to throw a "UNIQUE constraint failed" 534 | # error message. 535 | # 536 | # Globals: 537 | # WRITER_HOSTGROUP_ID 538 | # SYNCUSERS 539 | # CLUSTER_HOSTNAME (overwrites) 540 | # CLUSTER_PORT (overwrites) 541 | # SINGLE_SERVER 542 | # 543 | # Arguments: 544 | # None 545 | # 546 | function syncusers() { 547 | debug "$LINENO" "syncusers ()" 548 | 549 | local mysql_version 550 | local password_field 551 | local changes_made=0 552 | 553 | # If a single server has been specified with --server, use that server 554 | # and skip the cluster check (it may be a standalone node) 555 | if [[ -n $SINGLE_SERVER ]]; then 556 | local ws_address 557 | 558 | ws_address=$(separate_ip_port_from_address "$SINGLE_SERVER") 559 | CLUSTER_HOSTNAME=$(echo "$ws_address" | cut -d' ' -f1) 560 | CLUSTER_PORT=$(echo "$ws_address" | cut -d' ' -f2) 561 | 562 | echo -e "\nSyncing user accounts from server(${CLUSTER_HOSTNAME}:${CLUSTER_PORT}) to ProxySQL" 563 | 564 | else 565 | cluster_in_proxysql_check "${WRITER_HOSTGROUP_ID}" 566 | 567 | local cluster_node 568 | 569 | # Get current MySQL users, filter out header row and mysql.sys user 570 | # Find a cluster node that belongs to the cluster with $WRITER_HOSTGROUP_ID 571 | cluster_node=$(find_cluster_node "$WRITER_HOSTGROUP_ID") 572 | check_cmd $? "$LINENO" "Could not find a primary cluster node" 573 | 574 | # Reset the central cluster node (so that calls to mysql_exec) will 575 | # work with this new node, rather than the node in the config file 576 | CLUSTER_HOSTNAME=$(echo -e "$cluster_node" | cut -f1) 577 | CLUSTER_PORT=$(echo -e "$cluster_node" | cut -f2) 578 | 579 | echo -e "\nSyncing user accounts from PXC(${CLUSTER_HOSTNAME}:${CLUSTER_PORT}) to ProxySQL" 580 | 581 | fi 582 | 583 | # Check that we can connect to CLUSTER_HOSTNAME:CLUSTER_PORT 584 | cluster_connection_check 585 | 586 | mysql_version=$(mysql_exec "$LINENO" "SELECT VERSION();" | tail -1 | cut -d'.' -f1,2 ) 587 | check_cmd $? "$LINENO" "Could not connect to the server."\ 588 | "\n-- Please check the server connection parameters and status." 589 | 590 | case $mysql_version in 591 | 5.5 | 5.6) 592 | password_field="Password" 593 | ;; 594 | 5.7 | 8.0 | 8.4) 595 | password_field="authentication_string" 596 | ;; 597 | 10.0 | 10.1 | 10.2 | 10.3 | 10.4 | 10.5 | 10.6 | 10.11) 598 | password_field="Password" 599 | ;; 600 | *) 601 | error "$LINENO" "Unexpected database server version: ${mysql_version}"\ 602 | "\n-- This version of proxysql-admin needs to be updated." 603 | exit 1 604 | ;; 605 | esac 606 | 607 | # Filter out the internal system users 608 | mysql_users=$(mysql_exec "$LINENO" "SELECT User,HEX(${password_field}) FROM mysql.user where ${password_field}!=''" "" "hide_output" | 609 | grep -E -v "^(mysql.sys|mysql.session|mysql.infoschema|mysql.pxc)" | 610 | sort | 611 | uniq ) 612 | check_cmd $? "$LINENO" "Failed to load the user list from the server."\ 613 | "\n-- Please check the server connection parameters and status." 614 | 615 | # Checking whether user is part of proxysql admin user list 616 | # Get current ProxySQL users and filter out header row 617 | proxysql_users=$(get_proxysql_users) 618 | 619 | # TEST FOR USERS THAT EXIST IN MYSQL BUT NOT IN PROXYSQL HERE AND ADD 620 | 621 | # Escape all backslashes here, because the read will evaluate 622 | # the escaped chars 623 | mysql_users=${mysql_users//\\/\\\\} 624 | 625 | # Declare associative arrays 626 | declare -A mysql_users_full_hash # Hash of MySQL users 627 | declare -A proxysql_users_full_hash # Hash of ProxySQL users 628 | local username password 629 | 630 | # Populate MySQL users hash 631 | while read -r mysql_user; do 632 | if [[ -z $mysql_user ]]; then 633 | continue 634 | fi 635 | 636 | # Add MySQL user to hash 637 | username=$(echo "$mysql_user" | cut -f1) 638 | password=$(echo "$mysql_user" | cut -f2) 639 | mysql_users_full_hash["${username}"]="$password" 640 | 641 | done < <(printf "%s\n" "${mysql_users}") 642 | 643 | # Populate ProxySQL users hash 644 | while read -r proxysql_user; do 645 | if [[ -z $proxysql_user ]]; then 646 | continue 647 | fi 648 | 649 | # Extract ProxySQL username 650 | username=$(echo "$proxysql_user" | cut -f1) 651 | password=$(echo "$proxysql_user" | cut -f2) 652 | 653 | # Add ProxySQL user to hash 654 | proxysql_users_full_hash["${username}"]="$password" 655 | 656 | done < <(printf "%s\n" "${proxysql_users}") 657 | 658 | unset username 659 | unset password 660 | 661 | # Disable nounset to be able to search in the associative array 662 | set +o nounset 663 | 664 | # Check MySQL users against ProxySQL 665 | for user in "${!mysql_users_full_hash[@]}"; do 666 | 667 | username=${user} 668 | password="${mysql_users_full_hash[${username}]}" 669 | 670 | # Since we're using single quotes within the SQL statement, only need 671 | # to escape the single quotes for SQL 672 | password=${password//\'/\'\'} 673 | 674 | debug "$LINENO" "Processing MySQL user: '${username}'" 675 | 676 | # Check if the user exists in ProxySQL 677 | if [[ -n ${proxysql_users_full_hash[${username}]} ]]; then 678 | 679 | # Perform an exact match for the user and password 680 | if [[ "${proxysql_users_full_hash[${username}]}" == "${mysql_users_full_hash[${username}]}" ]]; then 681 | debug "$LINENO" "MySQL user: '${username}' already exists in ProxySQL, continuing" 682 | # User already exists in ProxySQL, we may skip this user 683 | continue 684 | 685 | else 686 | 687 | # If the same user exists in ProxySQL, but with a different password, 688 | # then remove all such users from ProxySQL 689 | 690 | echo "Removing existing user from ProxySQL: ${username}" 691 | proxysql_exec "$LINENO" "DELETE FROM mysql_users WHERE username='${username}' and default_hostgroup=$WRITER_HOSTGROUP_ID" 692 | check_cmd $? "$LINENO" "Failed to delete the user (${username}) from ProxySQL database."\ 693 | "\n-- Please check the ProxySQL connection parameters and status." 694 | proxysql_exec "$LINENO" "DELETE FROM mysql_query_rules WHERE username='${username}' and destination_hostgroup in($WRITER_HOSTGROUP_ID,$READER_HOSTGROUP_ID)" 695 | check_cmd $? "$LINENO" "Failed to delete the query rule for user (${username}) from ProxySQL database."\ 696 | "\n-- Please check the ProxySQL connection parameters and status." 697 | changes_made=1 698 | fi 699 | fi 700 | 701 | # Check for ProxySQL admin user 702 | local is_proxysql_admin_user 703 | is_proxysql_admin_user=$(proxysql_admin_user_check "${username}") 704 | if [[ $is_proxysql_admin_user -eq 1 ]]; then 705 | echo -e "\nNote : '${username}' is in proxysql admin user list, this user cannot be added to ProxySQL"\ 706 | "\n-- (For more info, see https://github.com/sysown/proxysql/issues/709)" 707 | else 708 | # Verify that the user does not exist in ProxySQL 709 | check_user=$(proxysql_exec "$LINENO" "SELECT username from mysql_users where username='${username}'") 710 | if [[ -z $check_user ]]; then 711 | # It is confirmed that the user does not exist in ProxySQL, let's add it 712 | echo "Adding user to ProxySQL: ${username}" 713 | proxysql_exec "$LINENO" \ 714 | "INSERT INTO mysql_users 715 | (username, password, active, default_hostgroup) 716 | VALUES 717 | ('${username}', UNHEX('${password}'), 1, $WRITER_HOSTGROUP_ID)" 718 | check_cmd $? "$LINENO" "Failed to add the user (${username}) from PXC to ProxySQL database."\ 719 | "\n-- Please check the ProxySQL connection parameters and status." 720 | if [[ $ADD_QUERY_RULE -eq 1 ]];then 721 | add_query_rule "${username}" 722 | fi 723 | changes_made=1 724 | else 725 | echo "Cannot add the user (${username}). The user (${username}) already exists in ProxySQL database with different hostgroup." 726 | check_user="" 727 | fi 728 | fi 729 | done # End of for loop 730 | 731 | # Remove users from ProxySQL that do not exist in MySQL 732 | if [[ $SYNCUSERS -eq 1 ]]; then 733 | 734 | # Get all proxysql users 735 | proxysql_users=$(get_proxysql_users) 736 | 737 | # Unset the hash 738 | declare -A proxysql_users_full_hash 739 | 740 | # Populate ProxySQL users hash 741 | while read -r proxysql_user; do 742 | if [[ -z $proxysql_user ]]; then 743 | continue 744 | fi 745 | 746 | # Extract ProxySQL username 747 | username=$(echo "$proxysql_user" | cut -f1) 748 | password=$(echo "$proxysql_user" | cut -f2) 749 | 750 | # Add ProxySQL user to hash 751 | proxysql_users_full_hash["${username}"]="$password" 752 | 753 | done < <(printf "%s\n" "${proxysql_users}") 754 | 755 | # Check ProxySQL users against MySQL 756 | for username in "${!proxysql_users_full_hash[@]}"; do 757 | debug "$LINENO" "Processing ProxySQL user(for removal):${username}" 758 | 759 | if [[ -z ${mysql_users_full_hash["${username}"]} ]]; then 760 | # Delete the ProxySQL user 761 | echo -e "\nRemoving (non-MySQL) user from ProxySQL: ${username}" 762 | proxysql_exec "$LINENO" "DELETE FROM mysql_users WHERE username='${username}' and default_hostgroup=$WRITER_HOSTGROUP_ID" 763 | check_cmd $? "$LINENO" "Failed to delete the user (${username}) from ProxySQL database."\ 764 | "\n-- Please check the ProxySQL connection parameters and status." 765 | proxysql_exec "$LINENO" "DELETE FROM mysql_query_rules WHERE username='${username}' and destination_hostgroup in($WRITER_HOSTGROUP_ID,$READER_HOSTGROUP_ID)" 766 | check_cmd $? "$LINENO" "Failed to delete the query rule for user (${username}) from ProxySQL database."\ 767 | "\n-- Please check the ProxySQL connection parameters and status." 768 | changes_made=1 769 | fi 770 | done 771 | fi 772 | 773 | # Save the configuration 774 | if [[ $changes_made -eq 1 ]]; then 775 | proxysql_load_to_runtime_save_to_disk "MYSQL USERS" $LINENO 776 | proxysql_load_to_runtime_save_to_disk "MYSQL QUERY RULES" $LINENO 777 | fi 778 | 779 | # Re-enable nounset 780 | set -o nounset 781 | } 782 | 783 | # Returns a list of users from the ProxySQL database 784 | # 785 | # Globals: 786 | # WRITER_HOSTGROUP_ID 787 | # 788 | # Arguments: 789 | # None 790 | # 791 | function get_proxysql_users() { 792 | local proxysql_users 793 | proxysql_users=$(proxysql_exec "$LINENO" \ 794 | "SELECT username,HEX(password) 795 | FROM mysql_users 796 | WHERE password!='' AND default_hostgroup=$WRITER_HOSTGROUP_ID" "" "hide_output") 797 | check_cmd $? "$LINENO" "Failed to load user list from ProxySQL database."\ 798 | "\n-- Please check the ProxySQL connection parameters and status." 799 | proxysql_users=$(echo "$proxysql_users" | 800 | sort | 801 | uniq ) 802 | printf "%s" "$proxysql_users" 803 | } 804 | 805 | # Checks if a user is a ProxySQL admin user 806 | # 807 | # Globals: 808 | # None 809 | # 810 | # Arguments: 811 | # 1: the name of the use to be checked 812 | # 813 | # Outputs (to stdout): 814 | # 1 if the user is a proxysql admin user 815 | # 0 if the user is not a proxysql admin user 816 | # 817 | function proxysql_admin_user_check(){ 818 | local userchk=$1 819 | local proxysql_admin_users 820 | local is_proxysql_admin_user 821 | 822 | mapfile -t proxysql_admin_users < <(proxysql_exec "$LINENO" \ 823 | "select variable_value 824 | from global_variables 825 | where variable_name like 'admin-%_credentials'" | 826 | sed 's/;/\n/g'| 827 | cut -d':' -f1 | 828 | grep -v variable_value) 829 | # shellcheck disable=SC2199 830 | if [[ " ${proxysql_admin_users[@]} " =~ [[:space:]]${userchk}[[:space:]] ]]; then 831 | is_proxysql_admin_user=1 832 | else 833 | is_proxysql_admin_user=0 834 | fi 835 | printf "%s" "$is_proxysql_admin_user" 836 | } 837 | 838 | 839 | # Returns success if the native scheduler is in use. 840 | # Returns success(0) if the native proxysql scheduler is in use. 841 | # Else returns failure(1) 842 | # 843 | # In this case, the native scheduler is used if entries are 844 | # found in mysql_galera_hostgroups, mysql_group_replication_hostgroups, 845 | # or mysql_replication_hostgroups. 846 | # 847 | # Globals: 848 | # None 849 | # 850 | # Arguments: 851 | # Parameter 1: line number 852 | # 853 | # Returns: 854 | # 0 (success) the native scheduler is being used 855 | # 1 (failure) the native scheduler is not being used 856 | # 255 (failure) error, unable to connect to proxysql 857 | # 858 | function is_native_scheduler_in_use() 859 | { 860 | local lineno=$1 861 | local count 862 | 863 | count=$(proxysql_exec "$LINENO" \ 864 | "SELECT COUNT(*) 865 | FROM runtime_mysql_group_replication_hostgroups 866 | WHERE active=1") 867 | [[ $? -ne 0 ]] && return 255 868 | [[ $count -gt 0 ]] && return 0 869 | 870 | count=$(proxysql_exec "$LINENO" \ 871 | "SELECT COUNT(*) 872 | FROM runtime_mysql_galera_hostgroups 873 | WHERE active=1") 874 | [[ $? -ne 0 ]] && return 255 875 | [[ $count -gt 0 ]] && return 0 876 | 877 | count=$(proxysql_exec "$LINENO" \ 878 | "SELECT COUNT(*) 879 | FROM runtime_mysql_replication_hostgroups") 880 | [[ $? -ne 0 ]] && return 255 881 | [[ $count -gt 0 ]] && return 0 882 | 883 | return 1 884 | } 885 | 886 | function is_go_scheduler_in_use() 887 | { 888 | local lineno=$1 889 | local count 890 | 891 | count=$(proxysql_exec "$LINENO" \ 892 | "SELECT COUNT(*) 893 | FROM runtime_scheduler 894 | WHERE active=1 AND 895 | filename LIKE '%pxc_scheduler_handler'") 896 | [[ $? -ne 0 ]] && return 255 897 | [[ $count -gt 0 ]] && return 0 898 | return 1 899 | } 900 | 901 | function is_other_scheduler_in_use() 902 | { 903 | local lineno=$1 904 | local count 905 | 906 | count=$(proxysql_exec "$LINENO" \ 907 | "SELECT COUNT(*) 908 | FROM runtime_scheduler 909 | WHERE active=1 AND 910 | filename NOT LIKE '%pxc_scheduler_handler'") 911 | [[ $? -ne 0 ]] && return 255 912 | [[ $count -gt 0 ]] && return 0 913 | return 1 914 | } 915 | 916 | 917 | function native_scheduler_capability_check() 918 | { 919 | local lineno=$1 920 | if is_go_scheduler_in_use "${lineno}"; then 921 | # TODO: better error messages 922 | error "${lineno}" "Unable to setup the native ProxySQL scheduler." \ 923 | "\n---- The Percona go scheduler is currently in use" \ 924 | "\n---- and must be removed." 925 | return 1; 926 | fi 927 | 928 | if is_other_scheduler_in_use "${lineno}"; then 929 | # TODO: better error messages 930 | error "${lineno}" "Unable to setup the native ProxySQL scheduler." \ 931 | "\n---- Another scheduler is currently in use" \ 932 | "\n---- and must be removed." 933 | return 1; 934 | fi 935 | return 0; 936 | } 937 | 938 | function go_scheduler_capability_check() 939 | { 940 | local lineno=$1 941 | if is_native_scheduler_in_use "${lineno}"; then 942 | # TODO: better error messages 943 | error "${lineno}" "Unable to setup the Percona go scheduler." \ 944 | "\n---- The ProxySQL native scheduler is in use" \ 945 | "\n---- and must be removed." 946 | return 1; 947 | fi 948 | 949 | if is_other_scheduler_in_use "${lineno}"; then 950 | # TODO: better error messages 951 | error "${lineno}" "Unable to setup the Percona go scheduler." \ 952 | "\n---- Another scheduler is in use" \ 953 | "\n---- and must be removed." 954 | return 1; 955 | fi 956 | 957 | return 0; 958 | } 959 | -------------------------------------------------------------------------------- /proxysql-admin.cnf: -------------------------------------------------------------------------------- 1 | 2 | export PROXYSQL_DATADIR='/var/lib/proxysql' 3 | 4 | # -------------------------------- 5 | # encrypted login credentials file options 6 | # 7 | #export LOGIN_FILE='/path/to/loginfile' 8 | #export LOGIN_PASSWORD_FILE='/path/to/loginfile/password' 9 | 10 | # -------------------------------- 11 | # proxysql admin interface credentials. 12 | # 13 | export PROXYSQL_USERNAME='admin' 14 | export PROXYSQL_PASSWORD='admin' 15 | export PROXYSQL_HOSTNAME='localhost' 16 | export PROXYSQL_PORT='6032' 17 | 18 | # -------------------------------- 19 | # PXC admin credentials for connecting to pxc-cluster-node. 20 | # 21 | export CLUSTER_USERNAME='admin' 22 | export CLUSTER_PASSWORD='admin' 23 | export CLUSTER_HOSTNAME='localhost' 24 | export CLUSTER_PORT='3306' 25 | 26 | # -------------------------------- 27 | # proxysql monitoring user. proxysql admin script will create 28 | # this user in pxc to monitor pxc-nodes. 29 | # 30 | export MONITOR_USERNAME='monitor' 31 | export MONITOR_PASSWORD='monitor' 32 | 33 | # -------------------------------- 34 | # Application user to connect to pxc-node through proxysql 35 | # 36 | export CLUSTER_APP_USERNAME='proxysql_user' 37 | export CLUSTER_APP_PASSWORD='passw0rd' 38 | 39 | # -------------------------------- 40 | # ProxySQL hostgroup IDs 41 | # 42 | export WRITER_HOSTGROUP_ID='10' 43 | export READER_HOSTGROUP_ID='11' 44 | export BACKUP_WRITER_HOSTGROUP_ID='12' 45 | export OFFLINE_HOSTGROUP_ID='13' 46 | 47 | # -------------------------------- 48 | # ProxySQL read/write configuration mode. 49 | # 50 | export MODE='singlewrite' 51 | 52 | # -------------------------------- 53 | # max_connections default (used only when INSERTing a new mysql_servers entry) 54 | # 55 | export MAX_CONNECTIONS='1000' 56 | 57 | # -------------------------------- 58 | # Determines the maximum number of writesets a node can have queued 59 | # before the node is SHUNNED to avoid stale reads. 60 | # 61 | export MAX_TRANSACTIONS_BEHIND=100 62 | 63 | # -------------------------------- 64 | # Connections to the backend servers (from ProxySQL) will use SSL 65 | # 66 | export USE_SSL='no' 67 | 68 | # -------------------------------- 69 | # Determines if a node should be added to the reader hostgroup if it has 70 | # been promoted to the writer hostgroup. 71 | # If set to 'yes', then all writers (including backup-writers) are added to 72 | # the read hostgroup. 73 | # If set to 'no', then none of the writers (including backup-writers) are added. 74 | # If set to 'backup', then only the backup-writers will be added to 75 | # the read hostgroup. 76 | # 77 | export WRITERS_ARE_READERS='backup' 78 | -------------------------------------------------------------------------------- /proxysql-common: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script contains functions common to all proxysql-related scripts. 3 | # (currently only Percona XtraDB cluster in combination with ProxySQL is supported) 4 | # Version 2.0 5 | ############################################################################################### 6 | 7 | # This program is copyright 2016-2020 Percona LLC and/or its affiliates. 8 | # 9 | # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED 10 | # WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 11 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 12 | # 13 | # This program is free software; you can redistribute it and/or modify it under 14 | # the terms of the GNU General Public License as published by the Free Software 15 | # Foundation, version 2 or later 16 | # 17 | # You should have received a copy of the GNU General Public License version 2 18 | # along with this program; if not, write to the Free Software Foundation, Inc., 19 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 20 | 21 | #------------------------------------------------------------------------------- 22 | # 23 | # Step 1 : Bash internal configuration 24 | # 25 | 26 | #bash prompt internal configuration 27 | declare BD="" 28 | declare NBD="" 29 | declare RED="" 30 | declare NRED="" 31 | 32 | # Test if stdout and stderr are open to a terminal 33 | if [[ -t 1 ]]; then 34 | BD=$(tput bold) 35 | NBD=$(tput sgr0) 36 | fi 37 | if [[ -t 2 ]]; then 38 | RED=$(tput setaf 1) 39 | NRED=$(tput sgr0) 40 | fi 41 | 42 | #------------------------------------------------------------------------------- 43 | # 44 | # Step 2 : Global variables 45 | # 46 | 47 | # 48 | # Script parameters/constants 49 | # 50 | readonly PROXYSQL_ADMIN_VERSION="2.7.3" 51 | 52 | # The minimum required openssl version 53 | readonly REQUIRED_OPENSSL_VERSION="1.1.1" 54 | 55 | # The name of the openssl binary packaged with proxysql-admin 56 | readonly PROXYSQL_ADMIN_OPENSSL_NAME="proxysql-admin-openssl" 57 | 58 | declare -i DEBUG=0 59 | 60 | declare PROXYSQL_USERNAME="" 61 | declare PROXYSQL_PASSWORD="" 62 | declare PROXYSQL_PORT="" 63 | declare PROXYSQL_HOSTNAME="" 64 | 65 | declare CLUSTER_USERNAME="" 66 | declare CLUSTER_PASSWORD="" 67 | declare CLUSTER_HOSTNAME="" 68 | declare CLUSTER_PORT="" 69 | 70 | declare CLUSTER_APP_USERNAME="" 71 | declare CLUSTER_APP_PASSWORD="" 72 | 73 | declare MONITOR_USERNAME="" 74 | declare MONITOR_PASSWORD="" 75 | 76 | #------------------------------------------------------------------------------- 77 | # 78 | # Step 3 : Helper functions 79 | # 80 | 81 | function error() { 82 | local lineno=$1 83 | shift 84 | if [[ -n "$lineno" ]]; then 85 | printf "${BD}ERROR${NBD} (line:$lineno) : ${*//%/%%}\n" 1>&2 86 | else 87 | printf "${BD}ERROR${NBD} : ${*//%/%%}\n" 1>&2 88 | fi 89 | } 90 | 91 | function warning() { 92 | local lineno=$1 93 | shift 94 | if [[ -n "$lineno" ]]; then 95 | printf "${BD}WARNING${NBD} (line:$lineno) : ${*//%/%%}\n" 1>&2 96 | else 97 | printf "${BD}WARNING${NBD}: ${*//%/%%}\n" 1>&2 98 | fi 99 | } 100 | 101 | function debug() { 102 | if [[ $DEBUG -eq 1 ]]; then 103 | local lineno=$1 104 | shift 105 | if [[ -n "$lineno" ]]; then 106 | printf "${RED}${BD}debug (line:$lineno) : ${*//%/%%}${NBD}${NRED}\n" 1>&2 107 | else 108 | printf "${RED}debug: ${*//%/%%}${NRED}\n" 1>&2 109 | fi 110 | fi 111 | } 112 | 113 | function dump_arguments() { 114 | local arg_list="" 115 | for arg do 116 | arg_list+=" '$arg'" 117 | done 118 | echo $arg_list 119 | } 120 | 121 | 122 | # Checks the return value of the most recent command 123 | # 124 | # Globals: 125 | # None 126 | # 127 | # Arguments: 128 | # 1: the error code of the most recent command 129 | # 2: the lineno where the error occurred 130 | # 3: the error message if the error code is non-zero 131 | # 132 | # Exits the script if the retcode is non-zero. 133 | # 134 | function check_cmd() { 135 | local retcode=$1 136 | local lineno=$2 137 | shift 2 138 | 139 | if [[ ${retcode} -ne 0 ]]; then 140 | error "$lineno" $* 141 | exit 1 142 | fi 143 | } 144 | 145 | 146 | # Check the permissions for a file or directory 147 | # 148 | # Globals: 149 | # None 150 | # 151 | # Arguments: 152 | # 1: the bash test to be applied to the file 153 | # 2: the lineno where this call is invoked (used for errors) 154 | # 3: the path to the file 155 | # 4: (optional) description of the path (mostly used for existence checks) 156 | # 157 | # Exits the script if the permissions test fails. 158 | # 159 | function check_permission() { 160 | local permission=$1 161 | local lineno=$2 162 | local path_to_check=$3 163 | local description="" 164 | if [[ $# -gt 3 ]]; then 165 | description="$4" 166 | fi 167 | 168 | # shellcheck disable=SC1009,SC1073,SC1072 169 | if [ ! $permission "$path_to_check" ] ; then 170 | if [[ $permission == "-r" ]]; then 171 | error "$lineno" "You do not have READ permission for: $path_to_check" 172 | elif [[ $permission == "-w" ]]; then 173 | error "$lineno" "You do not have WRITE permission for: $path_to_check" 174 | elif [[ $permission == "-x" ]]; then 175 | error "$lineno" "You do not have EXECUTE permission for: $path_to_check" 176 | elif [[ $permission == "-e" ]]; then 177 | if [[ -n $description ]]; then 178 | error "$lineno" "Could not find the $description: $path_to_check" 179 | else 180 | error "$lineno" "Could not find: $path_to_check" 181 | fi 182 | elif [[ $permission == "-d" ]]; then 183 | if [[ -n $description ]]; then 184 | error "$lineno" "Could not find the $description: $path_to_check" 185 | else 186 | error "$lineno" "Could not find the directory: $path_to_check" 187 | fi 188 | elif [[ $permission == "-f" ]]; then 189 | if [[ -n $description ]]; then 190 | error "$lineno" "Could not find the $description: $path_to_check" 191 | else 192 | error "$lineno" "Could not find the file: $path_to_check" 193 | fi 194 | else 195 | error "$lineno" "You do not have the correct permissions for: $path_to_check" 196 | fi 197 | exit 1 198 | fi 199 | } 200 | 201 | 202 | # Separates the IP address from the port in a network address 203 | # Works for IPv4 and IPv6 204 | # 205 | # Globals: 206 | # None 207 | # 208 | # Params: 209 | # 1. The network address to be parsed 210 | # 211 | # Outputs: 212 | # A string with a space separating the IP address from the port 213 | # 214 | function separate_ip_port_from_address() 215 | { 216 | # 217 | # Break address string into host:port/path parts 218 | # 219 | local address=$1 220 | 221 | # Has to have at least one ':' to separate the port from the ip address 222 | if [[ $address =~ : ]]; then 223 | ip_addr=${address%:*} 224 | port=${address##*:} 225 | else 226 | ip_addr=$address 227 | port="" 228 | fi 229 | 230 | # Remove any braces that surround the ip address portion 231 | ip_addr=${ip_addr#\[} 232 | ip_addr=${ip_addr%\]} 233 | 234 | echo "${ip_addr} ${port}" 235 | } 236 | 237 | 238 | # Combines the IP address and port into a network address 239 | # Works for IPv4 and IPv6 240 | # (If the IP address is IPv6, the IP portion will have brackets) 241 | # 242 | # Globals: 243 | # None 244 | # 245 | # Params: 246 | # 1: The IP address portion 247 | # 2: The port 248 | # 249 | # Outputs: 250 | # A string containing the full network address 251 | # 252 | function combine_ip_port_into_address() 253 | { 254 | local ip_addr=$1 255 | local port=$2 256 | local addr 257 | 258 | if [[ ! $ip_addr =~ \[.*\] && $ip_addr =~ .*:.* ]] ; then 259 | # If there are no brackets and it does have a ':', then add the brackets 260 | # because this is an unbracketed IPv6 address 261 | addr="[${ip_addr}]:${port}" 262 | else 263 | addr="${ip_addr}:${port}" 264 | fi 265 | echo "$addr" 266 | } 267 | 268 | 269 | # Tests if a string is an integer 270 | # (checks if made up of digits with an optional minus sign) 271 | # 272 | # Arguments 273 | # Parameter 1: the string to be tested 274 | # 275 | # Returns 276 | # 0 : if the string is an integer 277 | # 1 : if the string is not like an integer 278 | # 279 | function is_integer() 280 | { 281 | if [ "$1" -eq "$1" ] 2>/dev/null; then 282 | return 0 283 | else 284 | return 1 285 | fi 286 | } 287 | 288 | 289 | # Returns the version string in a standardized format 290 | # Input "1.2.3" => echoes "010203" 291 | # Wrongly formatted values => echoes "000000" 292 | # 293 | # Globals: 294 | # None 295 | # 296 | # Arguments: 297 | # Parameter 1: a version string 298 | # like "5.1.12" 299 | # anything after the major.minor.revision is ignored 300 | # Outputs: 301 | # A string that can be used directly with string comparisons. 302 | # So, the string "5.1.12" is transformed into "050112" 303 | # Note that individual version numbers can only go up to 99. 304 | # 305 | function normalize_version() 306 | { 307 | local major=0 308 | local minor=0 309 | local patch=0 310 | 311 | # Only parses purely numeric version numbers, 1.2.3 312 | # Everything after the first three values are ignored 313 | if [[ $1 =~ ^([0-9]+)\.([0-9]+)\.?([0-9]*)([^ ])* ]]; then 314 | major=${BASH_REMATCH[1]} 315 | minor=${BASH_REMATCH[2]} 316 | patch=${BASH_REMATCH[3]} 317 | fi 318 | 319 | printf %02d%02d%02d $major $minor $patch 320 | } 321 | 322 | 323 | # Compares two version strings 324 | # The version strings passed in will be normalized to a 325 | # string-comparable version. 326 | # 327 | # Globals: 328 | # None 329 | # 330 | # Arguments: 331 | # Parameter 1: The left-side of the comparison (for example: "5.7.25") 332 | # Parameter 2: the comparison operation 333 | # '>', '>=', '=', '==', '<', '<=', "!=" 334 | # Parameter 3: The right-side of the comparison (for example: "5.7.24") 335 | # 336 | # Returns: 337 | # Returns 0 (success) if param1 op param2 338 | # Returns 1 (failure) otherwise 339 | # 340 | function compare_versions() 341 | { 342 | local version_1="$1" 343 | local op=$2 344 | local version_2="$3" 345 | 346 | if [[ -z $version_1 || -z $version_2 ]]; then 347 | error "$LINENO" "Missing version string in comparison" 348 | echo -e "-- left-side:$version_1 operation:$op right-side:$version_2" 349 | return 1 350 | fi 351 | 352 | version_1="$( normalize_version "$version_1" )" 353 | version_2="$( normalize_version "$version_2" )" 354 | 355 | if [[ ! " = == > >= < <= != " =~ " $op " ]]; then 356 | error "$LINENO" "Unknown operation : $op" 357 | echo -e "-- Must be one of : = == > >= < <=" 358 | return 1 359 | fi 360 | 361 | [[ $op == "<" && $version_1 < $version_2 ]] && return 0 362 | [[ $op == "<=" && ! $version_1 > $version_2 ]] && return 0 363 | [[ $op == "=" && $version_1 == $version_2 ]] && return 0 364 | [[ $op == "==" && $version_1 == $version_2 ]] && return 0 365 | [[ $op == ">" && $version_1 > $version_2 ]] && return 0 366 | [[ $op == ">=" && ! $version_1 < $version_2 ]] && return 0 367 | [[ $op == "!=" && $version_1 != $version_2 ]] && return 0 368 | 369 | return 1 370 | } 371 | 372 | 373 | # Outputs the version string (major.minor) for the path passed in 374 | # 375 | # Globals 376 | # None 377 | # 378 | # Arguments 379 | # Parameter 1 : Path to the client 380 | # 381 | # Outputs 382 | # The version number (major.minor). The version numbers are not 383 | # normalized, so they cannot be directly compared. 384 | # 385 | function get_mysql_version() 386 | { 387 | 388 | local mysql_path=$1 389 | local version_string 390 | version_string=$(${mysql_path} --version) 391 | 392 | if echo "$version_string" | grep -qe "[[:space:]]5\.5\."; then 393 | echo "5.5" 394 | elif echo "$version_string" | grep -qe "[[:space:]]5\.6\."; then 395 | echo "5.6" 396 | elif echo "$version_string" | grep -qe "[[:space:]]5\.7\."; then 397 | echo "5.7" 398 | elif echo "$version_string" | grep -qe "[[:space:]]8\.0\."; then 399 | echo "8.0" 400 | elif echo "$version_string" | grep -qe "[[:space:]]8\.4\."; then 401 | echo "8.4" 402 | elif echo "$version_string" | grep -qe "[[:space:]]10\.0\."; then 403 | echo "10.0" 404 | elif echo "$version_string" | grep -qe "[[:space:]]10\.1\."; then 405 | echo "10.1" 406 | elif echo "$version_string" | grep -qe "[[:space:]]10\.2\."; then 407 | echo "10.2" 408 | elif echo "$version_string" | grep -qe "[[:space:]]10\.3\."; then 409 | echo "10.3" 410 | elif echo "$version_string" | grep -qe "[[:space:]]10\.4\."; then 411 | echo "10.4" 412 | elif echo "$version_string" | grep -qe "[[:space:]]10\.5\."; then 413 | echo "10.5" 414 | elif echo "$version_string" | grep -qe "[[:space:]]10\.6\."; then 415 | echo "10.6" 416 | elif echo "$version_string" | grep -qe "[[:space:]]10\.11\."; then 417 | echo "10.11" 418 | else 419 | echo "$version_string" 420 | return 1 421 | fi 422 | return 0 423 | } 424 | 425 | 426 | # Looks for a version of OpenSSL 1.1.1 427 | # This could be openssl, openssl11, or the proxysql-admin-openssl binary. 428 | # 429 | # Globals: 430 | # REQUIRED_OPENSSL_VERSION 431 | # PROXYSQL_ADMIN_OPENSSL_NAME 432 | # 433 | # Arguments: 434 | # Parameter 1: the lineno where this function was called 435 | # 436 | # Returns 0 if a binary was found (with version 1.1.1+) 437 | # and writes the path to the binary to stdout 438 | # Returns 1 otherwise (and prints out its own error message) 439 | # 440 | function find_openssl_binary() 441 | { 442 | local lineno=$1 443 | local path_to_openssl 444 | local openssl_executable="" 445 | local value 446 | local openssl_version 447 | 448 | # Check for the proper version of the executable 449 | path_to_openssl=$(which openssl 2> /dev/null) 450 | if [[ $? -eq 0 && -n ${path_to_openssl} && -e ${path_to_openssl} ]]; then 451 | 452 | # We found a possible binary, check the version 453 | value=$(${path_to_openssl} version) 454 | openssl_version=$(expr match "$value" '.*[ \t]\+\([0-9]\+\.[0-9]\+\.[0-9]\+\)[^0-9].*') 455 | 456 | # Extract the version from version string 457 | if compare_versions "${openssl_version}" ">=" "$REQUIRED_OPENSSL_VERSION"; then 458 | openssl_executable=${path_to_openssl} 459 | fi 460 | fi 461 | 462 | # If we haven't found an acceptable openssl, look for openssl11 463 | if [[ -z $openssl_executable ]]; then 464 | # Check for openssl 1.1 executable (if installed alongside 1.0) 465 | openssl_executable=$(which openssl11 2> /dev/null) 466 | fi 467 | 468 | # If we haven't found openssl/openssl11 look for our own binary 469 | if [[ -z $openssl_executable ]]; then 470 | openssl_executable=$(which "$(dirname $0)/${PROXYSQL_ADMIN_OPENSSL_NAME}" 2> /dev/null) 471 | fi 472 | 473 | if [[ -z $openssl_executable ]]; then 474 | error "$lineno" "Could not find a v${REQUIRED_OPENSSL_VERSION}+ OpenSSL executable in the path." \ 475 | "\n-- Please check that OpenSSL v${REQUIRED_OPENSSL_VERSION} or greater is installed and in the path." 476 | return 1 477 | fi 478 | 479 | # Verify the openssl versions 480 | value=$(${openssl_executable} version) 481 | 482 | # Extract the version from version string 483 | openssl_version=$(expr match "$value" '.*[ \t]\+\([0-9]\+\.[0-9]\+\.[0-9]\+\)[^0-9].*') 484 | 485 | if compare_versions "$openssl_version" "<" "$REQUIRED_OPENSSL_VERSION"; then 486 | error "$lineno" "Could not find OpenSSL with the required version. required:${REQUIRED_OPENSSL_VERSION} found:${openssl_version}" \ 487 | "\n-- Please check that OpenSSL v${REQUIRED_OPENSSL_VERSION} or greater is installed and in the path." 488 | return 1 489 | fi 490 | 491 | debug "$LINENO" "Found openssl executable:${openssl_executable} ${openssl_version}" 492 | 493 | printf "%s" "${openssl_executable}" 494 | return 0 495 | } 496 | 497 | # Retrieves/unencrypts the login information from the login-file 498 | # 499 | # Globals: 500 | # REQUIRED_OPENSSL_VERSION 501 | # PROXYSQL_ADMIN_OPENSSL_NAME 502 | # 503 | # Arguments: 504 | # Parameter 1 : the path to the login-file 505 | # Parameter 2 : the key for the login-file 506 | # 507 | # Outputs: 508 | # Echoes the data (unencrypted) from the file 509 | # 510 | # Returns: 511 | # 1 (failure) if the openssl command failed 512 | # 0 (success) the openssl command succeeded and the contents of 513 | # the file is sent to output 514 | # 515 | function get_login_file_data() 516 | { 517 | local file_path="$1" 518 | local file_key="$2" 519 | local reval="" 520 | local encrypted_data="" 521 | local file_version file_iterations file_method file_data 522 | local value 523 | local openssl_executable="" 524 | 525 | if [[ -z $file_path || -z $file_key ]]; then 526 | error "$LINENO" "Missing file location(--login-file) or file key(--login-password or --login-password-file)" 527 | return 1 528 | fi 529 | 530 | # Check for the proper version of the executable 531 | openssl_executable=$(find_openssl_binary "$LINENO") 532 | if [[ $? -ne 0 ]]; then 533 | return 1 534 | fi 535 | 536 | if [[ ! -r $file_path ]]; then 537 | error "$LINENO" "Could not read from the login-file: $file_path" 538 | return 1 539 | fi 540 | 541 | debug "$LINENO" "Found openssl executable: $openssl_executable" 542 | debug "$LINENO" "Using the encrypted login-file:$file_path for credentials" 543 | 544 | # Get the file contents 545 | file_data=$(cat "$file_path") 546 | 547 | # Extract file parameters 548 | file_version=$(extract_value "$file_data" "version") 549 | if [[ -z $file_version ]]; then 550 | error "$LINENO" "Could not find the version information in the file: $file_path" 551 | return 1 552 | fi 553 | if [[ $file_version != "1" ]]; then 554 | error "$LINENO" "Unsupported login-file version: $file_version" 555 | return 1 556 | fi 557 | 558 | file_method=$(extract_value "$file_data" "encrypt_method") 559 | if [[ -z $file_method ]]; then 560 | error "$LINENO" "Could not find the encryption method in the file: $file_path" 561 | return 1 562 | fi 563 | if [[ $file_method != "openssl-pbkdf2-aes-256-cbc" ]]; then 564 | error "$LINENO" "Unsupported encryption method: $file_method" 565 | return 1 566 | fi 567 | 568 | file_iterations=$(extract_value "$file_data" "iterations") 569 | encrypted_data=$(extract_value "$file_data" "encrypted_data") 570 | if [[ -z $file_iterations || -z $encrypted_data ]]; then 571 | error "$LINENO" "Could not find the iteration count in the file: $file_path" 572 | return 1 573 | fi 574 | 575 | # Decrypt the data 576 | reval=$(${openssl_executable} enc -d -aes-256-cbc -pbkdf2 -iter "$file_iterations" -salt \ 577 | -in <(printf "%s" "$encrypted_data") -A -base64 -pass file:<(printf "%s" "$file_key") 2>/dev/null) 578 | if [[ $? -ne 0 ]]; then 579 | error "$LINENO" "Decryption of data in login-file failed: $file_path" 580 | return 1 581 | fi 582 | printf "%s" "${reval}" 583 | } 584 | 585 | 586 | # Extracts the value from a "name=value" line 587 | # 588 | # Globals: 589 | # None 590 | # 591 | # Arguments: 592 | # Parameter 1 : the data (captured from the login file) 593 | # Parameter 2 : the name of the variable to be extracted 594 | # 595 | # Output: 596 | # Echoes the value portion 597 | # 598 | # Returns: 599 | # 1 (failure) if there is no line using the name 600 | # 0 (success) if the name exists in the file 601 | # 602 | function extract_value() 603 | { 604 | local data=$1 605 | local name=$2 606 | local reval="" 607 | local processed_data 608 | 609 | # normalize the variable name by replacing all '_' with '-' 610 | name=${name//_/-} 611 | 612 | # This will normalize the variable name side of the strings 613 | processed_data=$(printf "%s" "$data" | awk -F= '{st=index($0,"="); cur=$0; if ($1 ~ /_/) { gsub(/_/,"-",$1);} if (st != 0) { print $1"="substr(cur,st+1) } else { print cur }}') 614 | 615 | # Check to see if the variable name is used in the login-file data 616 | printf "%s" "$processed_data" | grep -q "^[ \t]*$name=" 617 | if [[ $? -ne 0 ]]; then 618 | return 1 619 | fi 620 | 621 | # Get the named variable from the login-file data 622 | reval=$(printf "%s" "$processed_data" | grep -- "^[ \t]*$name=" | cut -d= -f2- | tail -1) 623 | 624 | printf "%s" "${reval}" 625 | return 0 626 | } 627 | 628 | 629 | # Loads the credentials from the login-file 630 | # This will also verify the OpenSSL version and set the path to the executable. 631 | # 632 | # Globals: 633 | # PROXYSQL_USERNAME (sets the value) 634 | # PROXYSQL_PASSWORD (sets the value) 635 | # PROXYSQL_HOSTNAME (sets the value) 636 | # PROXYSQL_PORT (sets the value) 637 | # CLUSTER_USERNAME (sets the value) 638 | # CLUSTER_PASSWORD (sets the value) 639 | # CLUSTER_HOSTNAME (sets the value) 640 | # CLUSTER_PORT (sets the value) 641 | # MONITOR_USERNAME (sets the value) 642 | # MONITOR_PASSWORD (sets the value) 643 | # CLUSTER_APP_USERNAME (sets the value) 644 | # CLUSTER_APP_PASSWORD (sets the value) 645 | # 646 | # Parameters: 647 | # Argument 1 : lineno this function was called from 648 | # Argument 2 : the path to the login-file 649 | # Argument 3 : the key for the login file 650 | # 651 | function load_login_file() 652 | { 653 | local lineno="$1" 654 | local login_file_path="$2" 655 | local login_key="$3" 656 | local data value 657 | 658 | data=$(get_login_file_data "${login_file_path}" "${login_key}") 659 | if [[ $? -ne 0 ]]; then 660 | return 1 661 | fi 662 | 663 | value=$(extract_value "$data" "proxysql.user") 664 | [[ $? -eq 0 ]] && PROXYSQL_USERNAME="$value" && debug "$LINENO" "Using login-file:proxysql.user"; 665 | 666 | value=$(extract_value "$data" "proxysql.password") 667 | [[ $? -eq 0 ]] && PROXYSQL_PASSWORD="$value" && debug "$LINENO" "Using login-file:proxysql.password"; 668 | 669 | value=$(extract_value "$data" "proxysql.host") 670 | [[ $? -eq 0 ]] && PROXYSQL_HOSTNAME="$value" && debug "$LINENO" "Using login-file:proxysql.host"; 671 | 672 | value=$(extract_value "$data" "proxysql.port") 673 | [[ $? -eq 0 ]] && PROXYSQL_PORT="$value" && debug "$LINENO" "Using login-file:proxysql.port"; 674 | 675 | value=$(extract_value "$data" "cluster.user") 676 | [[ $? -eq 0 ]] && CLUSTER_USERNAME="$value" && debug "$LINENO" "Using login-file:cluster.user"; 677 | 678 | value=$(extract_value "$data" "cluster.password") 679 | [[ $? -eq 0 ]] && CLUSTER_PASSWORD="$value" && debug "$LINENO" "Using login-file:cluster.password"; 680 | 681 | value=$(extract_value "$data" "cluster.host") 682 | [[ $? -eq 0 ]] && CLUSTER_HOSTNAME="$value" && debug "$LINENO" "Using login-file:cluster.host"; 683 | 684 | value=$(extract_value "$data" "cluster.port") 685 | [[ $? -eq 0 ]] && CLUSTER_PORT="$value" && debug "$LINENO" "Using login-file:cluster.port"; 686 | 687 | 688 | value=$(extract_value "$data" "monitor.user") 689 | [[ $? -eq 0 ]] && MONITOR_USERNAME="$value" && debug "$LINENO" "Using login-file:monitor.user"; 690 | 691 | value=$(extract_value "$data" "monitor.password") 692 | [[ $? -eq 0 ]] && MONITOR_PASSWORD="$value" && debug "$LINENO" "Using login-file:monitor.password"; 693 | 694 | 695 | value=$(extract_value "$data" "cluster-app.user") 696 | [[ $? -eq 0 ]] && CLUSTER_APP_USERNAME="$value" && debug "$LINENO" "Using login-file:cluster-app.user"; 697 | 698 | value=$(extract_value "$data" "cluster-app.password") 699 | [[ $? -eq 0 ]] && CLUSTER_APP_PASSWORD="$value" && debug "$LINENO" "Using login-file:cluster-app.password"; 700 | 701 | return 0 702 | } 703 | 704 | 705 | # Writes/Encrypts the login information and writes it out to a file 706 | # 707 | # Globals: 708 | # REQUIRED_OPENSSL_VERSION 709 | # PROXYSQL_ADMIN_OPENSSL_NAME 710 | # 711 | # Arguments: 712 | # Parameter 1 : path to the file with the unencrypted data 713 | # Parameter 2 : the password 714 | # Parameter 3 : the destination file (will be overwritten) 715 | # 716 | # Outputs: 717 | # Echoes the data (unencrypted) from the file 718 | # 719 | # Returns: 720 | # 1 (failure) if the openssl command failed 721 | # 0 (success) the openssl command succeeded and the contents of 722 | # the file is sent to output 723 | # 724 | function write_login_file() 725 | { 726 | local input_file_path="$1" 727 | local file_key="$2" 728 | local output_file_path="$3" 729 | local value 730 | local openssl_executable="" 731 | 732 | local reval="" 733 | local encrypted_data="" 734 | local file_version file_iterations file_method 735 | 736 | # Verify the openssl versions 737 | openssl_executable=$(find_openssl_binary "$LINENO") 738 | if [[ $? -ne 0 ]]; then 739 | return 1 740 | fi 741 | 742 | debug "$LINENO" "Found openssl executable: $openssl_executable" 743 | debug "$LINENO" "Writing to output file: $OUTFILE" 744 | 745 | # Setup encryption parameters 746 | file_iterations=100000 747 | 748 | # Encrypt the data 749 | reval=$(${openssl_executable} enc -aes-256-cbc -pbkdf2 -iter "$file_iterations" -salt \ 750 | -in <(cat ${input_file_path}) -A -base64 -pass file:<(printf "%s" "$file_key") 2>/dev/null) 751 | if [[ $? -ne 0 ]]; then 752 | error "$LINENO" "Encryption of input data failed: $input_file_path" 753 | return 1 754 | fi 755 | 756 | # Write out the file 757 | printf "# Created $(date)\n" > ${OUTFILE} 758 | printf "version=%s\n" "1" >> ${OUTFILE} 759 | printf "encrypt_method=%s\n" "openssl-pbkdf2-aes-256-cbc" >> ${OUTFILE} 760 | printf "iterations=%s\n" "${file_iterations}" >> ${OUTFILE} 761 | printf "encrypted_data=%s\n" "${reval}" >> ${OUTFILE} 762 | 763 | return 0 764 | } 765 | -------------------------------------------------------------------------------- /proxysql-login-file: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | # This script will assist with creating (encrypting) the login file 3 | # Version 2.0 4 | ############################################################################################### 5 | 6 | # This program is copyright 2016-2020 Percona LLC and/or its affiliates. 7 | # 8 | # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED 9 | # WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 10 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 11 | # 12 | # This program is free software; you can redistribute it and/or modify it under 13 | # the terms of the GNU General Public License as published by the Free Software 14 | # Foundation, version 2 or later 15 | # 16 | # You should have received a copy of the GNU General Public License version 2 17 | # along with this program; if not, write to the Free Software Foundation, Inc., 18 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 19 | 20 | # shellcheck disable=SC2046,SC2181,SC1091 21 | 22 | # Include the common functions 23 | . $(dirname "${BASH_SOURCE[0]}")/proxysql-common 24 | 25 | # The file used as input (unencrypted) 26 | declare INFILE="/dev/stdin" 27 | 28 | # The destination file 29 | declare OUTFILE="/dev/stdout" 30 | 31 | # The password used to generate the key for the login-file 32 | declare LOGIN_PASSWORD="" 33 | 34 | # The file that contains the key for the login-file 35 | declare LOGIN_PASSWORD_FILE="" 36 | 37 | # What this script is trying to, either "encrypt" or "decrypt" 38 | declare OPERATION="encrypt" 39 | 40 | function usage() 41 | { 42 | local path=$0 43 | cat << EOF 44 | Usage example: 45 | $ ${path##*/} [options] 46 | 47 | This script is used to create the encrypted login-file. 48 | 49 | Options: 50 | -h,--help : Prints out this help text. 51 | -v,--version : Prints out the script name and version 52 | 53 | --in= : The source file that will be encrypted. 54 | (default: stdin) 55 | --out= : The destination file that will contain the 56 | encrypted data and metadata information. 57 | (default: stdout) 58 | 59 | --password= : The key used to decrypt the encrypted login-file. 60 | This cannot be used with --login-password-file. 61 | --password-file= : Read the key from a file using the . 62 | This cannot be used with --login-password 63 | 64 | --decrypt : Decrypts the login-file data. --in is now the 65 | path to the encrypted login-file and --out is 66 | used for the unencrypted data. 67 | EOF 68 | } 69 | 70 | 71 | # 72 | # Prints out the script version 73 | # 74 | # Globals: 75 | # PROXYSQL_ADMIN_VERSION 76 | # 77 | # Parameters: 78 | # None 79 | # 80 | function version() 81 | { 82 | local path=$0 83 | printf "%s version %s\n" "${path##*/}" "${PROXYSQL_ADMIN_VERSION}" 84 | } 85 | 86 | 87 | 88 | function parse_args() 89 | { 90 | local go_out="" 91 | 92 | # TODO: kennt, what happens if we don't have a functional getopt()? 93 | # Check if we have a functional getopt(1) 94 | if ! getopt --test; then 95 | go_out="$(getopt --options=hv --longoptions=decrypt,debug,version,in:,out:,password:,password-file:,help \ 96 | --name="$(basename "$0")" -- "$@")" 97 | if [[ $? -ne 0 ]]; then 98 | # no place to send output 99 | echo "Script error: getopt() failed" >&2 100 | exit 1 101 | fi 102 | eval set -- "$go_out" 103 | fi 104 | 105 | while [[ $# -gt 0 ]]; 106 | do 107 | arg="$1" 108 | case "$arg" in 109 | -- ) 110 | shift 111 | break;; 112 | --in ) 113 | INFILE="$2" 114 | check_permission -e "$LINENO" "$INFILE" "input login-file" 115 | check_permission -r "$LINENO" "$INFILE" "input login-file" 116 | debug "$LINENO" "--in specified, using : $INFILE" 117 | shift 2 118 | ;; 119 | --out ) 120 | OUTFILE="$2" 121 | debug "$LINENO" "--out specified, using : $OUTFILE" 122 | shift 2 123 | ;; 124 | --password) 125 | if [[ -n $LOGIN_PASSWORD_FILE ]]; then 126 | error "$LINENO" "--password cannot be used with --password-file" 127 | exit 1 128 | fi 129 | LOGIN_PASSWORD="$2" 130 | shift 2 131 | ;; 132 | --password-file) 133 | if [[ -n $LOGIN_PASSWORD ]]; then 134 | error "$LINENO" "--password-file cannot be used with --password" 135 | exit 1 136 | fi 137 | LOGIN_PASSWORD_FILE="$2" 138 | shift 2 139 | ;; 140 | --decrypt ) 141 | OPERATION="decrypt" 142 | shift 143 | ;; 144 | --debug ) 145 | # shellcheck disable=SC2034 146 | DEBUG=1 147 | shift 1 148 | ;; 149 | -v | --version) 150 | version 151 | exit 0 152 | ;; 153 | -h | --help ) 154 | usage 155 | exit 0 156 | ;; 157 | esac 158 | done 159 | 160 | # Get the password 161 | if [[ -n $LOGIN_PASSWORD_FILE ]]; then 162 | if [[ ! -r $LOGIN_PASSWORD_FILE ]]; then 163 | error "$LINENO" "Cannot read from the password file: $LOGIN_PASSWORD_FILE" 164 | exit 1 165 | fi 166 | LOGIN_PASSWORD=$(cat "$LOGIN_PASSWORD_FILE") 167 | if [[ -z $LOGIN_PASSWORD ]]; then 168 | error "$LINENO" "Did not find any data in the password file: $LOGIN_PASSWORD_FILE" 169 | exit 1 170 | fi 171 | fi 172 | if [[ -z $LOGIN_PASSWORD ]]; then 173 | read -r -s -p "Enter the password:" LOGIN_PASSWORD 174 | echo 175 | fi 176 | } 177 | 178 | 179 | function main() 180 | { 181 | local data="" 182 | if [[ ${OPERATION} == "decrypt" ]]; then 183 | data=$(get_login_file_data "${INFILE}" "${LOGIN_PASSWORD}") 184 | if [[ $? -ne 0 ]]; then 185 | exit 1 186 | fi 187 | echo -e "$data" > "${OUTFILE}" 188 | else 189 | write_login_file "${INFILE}" "${LOGIN_PASSWORD}" "$OUTFILE" 190 | if [[ $? -ne 0 ]]; then 191 | exit 1 192 | fi 193 | fi 194 | } 195 | 196 | 197 | # 198 | # Execute the script 199 | # 200 | parse_args "$@" 201 | main 202 | -------------------------------------------------------------------------------- /proxysql-logrotate: -------------------------------------------------------------------------------- 1 | 2 | /var/lib/proxysql/*.log { 3 | missingok 4 | daily 5 | notifempty 6 | compress 7 | create 0600 proxysql proxysql 8 | rotate 15 9 | postrotate 10 | . /etc/proxysql-admin.cnf 11 | /usr/bin/printf "%s\n" "[client]" "user=${PROXYSQL_USERNAME}" "password=${PROXYSQL_PASSWORD}" "host=${PROXYSQL_HOSTNAME}" "port=${PROXYSQL_PORT}" | /usr/bin/mysql --defaults-file=/dev/stdin --protocol=tcp -Nse "PROXYSQL FLUSH LOGS" 12 | endscript 13 | } 14 | -------------------------------------------------------------------------------- /proxysql-status: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | # This script will assist with configuring ProxySQL 3 | # by querying the ProxySQL tables 4 | # Version 2.0 5 | ############################################################################################### 6 | 7 | # This program is copyright 2016-2020 Percona LLC and/or its affiliates. 8 | # 9 | # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED 10 | # WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 11 | # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 12 | # 13 | # This program is free software; you can redistribute it and/or modify it under 14 | # the terms of the GNU General Public License as published by the Free Software 15 | # Foundation, version 2 or later 16 | # 17 | # You should have received a copy of the GNU General Public License version 2 18 | # along with this program; if not, write to the Free Software Foundation, Inc., 19 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 20 | 21 | # shellcheck disable=SC2046,SC2181,SC1091 22 | 23 | # Include the common functions 24 | . $(dirname "${BASH_SOURCE[0]}")/proxysql-common 25 | 26 | # Global variables 27 | declare USER="" 28 | declare PASSWORD="" 29 | declare HOST="" 30 | declare PORT="" 31 | declare RUNTIME_OPTION="" 32 | declare DUMP_ALL=1 33 | declare DUMP_MAIN=0 34 | declare DUMP_STATS=0 35 | declare DUMP_MONITOR=0 36 | declare DUMP_FILES=0 37 | declare TABLE_FILTER="" 38 | declare DUMP_STATS_RESET_TABLE=0 39 | 40 | # The login-file is the path to an encrypted file that 41 | # contains the credentials for proxysql, PXC cluster, 42 | # monitor, and cluster-app logins. 43 | declare LOGIN_FILE="" 44 | declare LOGIN_PASSWORD="" 45 | declare LOGIN_PASSWORD_FILE="" 46 | 47 | # Set this to 1 if the default user credentials from my.cnf 48 | # are being used, set to 0 if the default my.cnf user credentials 49 | # are not being used (default) 50 | declare CREDENTIALS_FROM_CLIENT_CONFIG=0 51 | 52 | # 53 | # If set to 1, then calls to MySQL/ProxySQL via the MySQL client 54 | # will have credentials passed via stdin rather than using bash 55 | # process substitution. 56 | # 57 | declare USE_STDIN_FOR_CREDENTIALS=0 58 | 59 | 60 | function usage() { 61 | local path=$0 62 | cat << EOF 63 | Usage example: 64 | $ ${path##*/} [options] [ ] 65 | 66 | Options: 67 | --files : display contents of proxysql-admin related files 68 | --main : display main tables (both on-disk and runtime) 69 | --monitor : display monitor tables 70 | --runtime : display runtime-related data 71 | (implies --main) 72 | --stats : display stats tables 73 | --table= : display only tables that contain the table name 74 | (note: this is a case-sensitive match) 75 | --with-stats-reset : display _reset tables, by default _reset tables 76 | will not be queried. 77 | 78 | --login-file= 79 | : Read login credentials from an encrypted file. 80 | If the --login-password or --login-password-file 81 | options are not specified, then the user 82 | will be prompted for the password. 83 | (command line options override any login file values) 84 | --login-password= 85 | : The key used to decrypt the encrypted login-file. 86 | This cannot be used with --login-password-file. 87 | --login-password-file= 88 | : Read the key from a file using the . 89 | This cannot be used with --login-password 90 | --use-stdin-for-credentials 91 | : If set, then the MySQL client will use stdin to send 92 | credentials to the client (instead of process 93 | substitution). 94 | (default: process substitution is used) 95 | 96 | The default is to display all tables and files. 97 | 98 | If no credentials are specified (on the command line or via a login-file) then: 99 | 1. The default MySQL client credentials are used (usually found 100 | in ~/.my.cnf), if they connect to a ProxySQL instance). 101 | 2. If the default MySQL client credentials do not exist, or do not connect 102 | to a ProxySQL instance, then the credentials in /etc/proxysql-admin.cnf 103 | are used. 104 | 105 | EOF 106 | } 107 | 108 | 109 | # 110 | # Prints out the script version 111 | # 112 | # Globals: 113 | # PROXYSQL_ADMIN_VERSION 114 | # 115 | # Parameters: 116 | # None 117 | # 118 | function version() 119 | { 120 | local path=$0 121 | printf "%s version %s\n" "${path##*/}" "${PROXYSQL_ADMIN_VERSION}" 122 | } 123 | 124 | 125 | # 126 | # Executes an SQL query 127 | # 128 | # Globals: 129 | # USER 130 | # PASSWORD 131 | # HOST 132 | # PORT 133 | # LOGIN_PATH 134 | # 135 | # Arguments: 136 | # 1: arguments to be passed to mysql 137 | # 2: the query 138 | # 139 | function mysql_exec() { 140 | local args=$1 141 | local query=$2 142 | local retvalue 143 | local retoutput 144 | local defaults="" 145 | local default_auth="" 146 | 147 | # shellcheck disable=SC2086 148 | if [[ $CREDENTIALS_FROM_CLIENT_CONFIG -eq 0 ]]; then 149 | defaults=$(printf '[client]\nuser=%s\npassword="%s"\nhost=%s\nport=%s\n%s' \ 150 | "${USER}" \ 151 | "${PASSWORD}" \ 152 | "${HOST}" \ 153 | "${PORT}" \ 154 | "${default_auth}" 155 | ) 156 | 157 | if [[ $USE_STDIN_FOR_CREDENTIALS -eq 1 ]]; then 158 | retoutput=$(printf "%s" "${defaults}" | mysql --defaults-file=/dev/stdin --protocol=tcp --unbuffered --batch --silent ${args} -e "$query") 159 | retvalue=$? 160 | else 161 | retoutput=$(mysql --defaults-file=<(echo "${defaults}") --protocol=tcp --unbuffered --batch --silent ${args} -e "$query") 162 | retvalue=$? 163 | fi 164 | else 165 | retoutput=$(mysql ${args} -e "${query}") 166 | retvalue=$? 167 | fi 168 | 169 | if [[ -n $retoutput ]]; then 170 | printf "%s\n" "${retoutput}" 171 | fi 172 | return $retvalue 173 | } 174 | 175 | 176 | function parse_args() { 177 | local go_out="" 178 | 179 | # TODO: kennt, what happens if we don't have a functional getopt()? 180 | # Check if we have a functional getopt(1) 181 | if ! getopt --test; then 182 | go_out="$(getopt --options=hv --longoptions=runtime,main,stats,monitor,files,table:,with-stats-reset,login-file:,login-password:,login-password-file:,use-stdin-for-credentials,version,help \ 183 | --name="$(basename "$0")" -- "$@")" 184 | check_cmd $? "$LINENO" "Script error: getopt() failed with arguments: $*" 185 | eval set -- "$go_out" 186 | fi 187 | 188 | while [[ $# -gt 0 ]]; 189 | do 190 | arg="$1" 191 | case "$arg" in 192 | -- ) 193 | shift 194 | break;; 195 | --runtime ) 196 | shift 197 | RUNTIME_OPTION=" LIKE 'runtime_%'" 198 | DUMP_ALL=0 199 | DUMP_MAIN=1 200 | ;; 201 | --main ) 202 | shift 203 | DUMP_ALL=0 204 | DUMP_MAIN=1 205 | ;; 206 | --stats ) 207 | shift 208 | DUMP_ALL=0 209 | DUMP_STATS=1 210 | ;; 211 | --monitor ) 212 | shift 213 | DUMP_ALL=0 214 | DUMP_MONITOR=1 215 | ;; 216 | --files ) 217 | shift 218 | DUMP_ALL=0 219 | DUMP_FILES=1 220 | ;; 221 | --table ) 222 | TABLE_FILTER=$2 223 | shift 2 224 | ;; 225 | --with-stats-reset ) 226 | shift 227 | DUMP_STATS_RESET_TABLE=1 228 | ;; 229 | --login-file) 230 | LOGIN_FILE="$2" 231 | check_permission -e "$LINENO" "$LOGIN_FILE" "login-file" 232 | check_permission -r "$LINENO" "$LOGIN_FILE" "login-file" 233 | debug "$LINENO" "--login-file specified, using : $LOGIN_FILE" 234 | shift 2 235 | ;; 236 | --login-password) 237 | if [[ -n $LOGIN_PASSWORD_FILE ]]; then 238 | error "$LINENO" "--login-password cannot be used with --login-password-file" 239 | exit 1 240 | fi 241 | LOGIN_PASSWORD="$2" 242 | shift 2 243 | ;; 244 | --login-password-file) 245 | if [[ -n $LOGIN_PASSWORD ]]; then 246 | error "$LINENO" "--login-password-file cannot be used with --login-password" 247 | exit 1 248 | fi 249 | LOGIN_PASSWORD_FILE="$2" 250 | shift 2 251 | ;; 252 | --use-stdin-for-credentials ) 253 | USE_STDIN_FOR_CREDENTIALS=1 254 | shift 255 | ;; 256 | -v | --version ) 257 | version 258 | exit 0 259 | ;; 260 | -h | --help ) 261 | usage 262 | exit 0 263 | ;; 264 | esac 265 | done 266 | 267 | if [[ $# -eq 0 && -z $LOGIN_FILE ]]; then 268 | # If no credentials have been provided, try the default 269 | # mysql client credentials 270 | 271 | mysql -e "SHOW tables" 2>/dev/null | grep -q "runtime_proxysql_servers" 272 | if [[ $? -eq 0 ]]; then 273 | echo -e "Connecting to ProxySQL with the default MySQL client credentials" 274 | echo -e "Usually found in ~/.my.cnf" 275 | CREDENTIALS_FROM_CLIENT_CONFIG=1 276 | fi 277 | fi 278 | 279 | if [[ $CREDENTIALS_FROM_CLIENT_CONFIG -eq 0 ]]; then 280 | 281 | # Load the data if the login-file has been set 282 | # Run this before the command-line parsing, so that the command-line 283 | # options can override the login path settings 284 | if [[ -n $LOGIN_FILE ]]; then 285 | 286 | # Check for key 287 | if [[ -n $LOGIN_PASSWORD_FILE ]]; then 288 | #if [[ ! -e $LOGIN_PASSWORD_FILE ]]; then 289 | # error "$LINENO" "Cannot read from the login-password file: $LOGIN_PASSWORD_FILE" 290 | # exit 1 291 | #fi 292 | LOGIN_PASSWORD=$(cat "$LOGIN_PASSWORD_FILE") 293 | if [[ -z $LOGIN_PASSWORD ]]; then 294 | error "$LINENO" "Did not find any data in the login-password file: $LOGIN_PASSWORD_FILE" 295 | exit 1 296 | fi 297 | fi 298 | if [[ -z $LOGIN_PASSWORD ]]; then 299 | read -r -s -p "Enter the login-file password:" LOGIN_PASSWORD 300 | echo 301 | fi 302 | 303 | # Extract the information 304 | load_login_file "$LINENO" "$LOGIN_FILE" "$LOGIN_PASSWORD" 305 | if [[ $? -ne 0 ]]; then 306 | error "$LINENO" "Cannot read the credentials from the login-file" 307 | exit 1 308 | fi 309 | 310 | [[ -n $PROXYSQL_USERNAME ]] && USER=$PROXYSQL_USERNAME; 311 | [[ -n $PROXYSQL_PASSWORD ]] && PASSWORD=$PROXYSQL_PASSWORD; 312 | [[ -n $PROXYSQL_HOSTNAME ]] && HOST=$PROXYSQL_HOSTNAME; 313 | [[ -n $PROXYSQL_PORT ]] && PORT=$PROXYSQL_PORT; 314 | 315 | elif [[ $# -eq 0 ]]; then 316 | # When no arguments are passed try to read from /etc/proxysql-admin.cnf 317 | if [[ ! -r /etc/proxysql-admin.cnf ]]; then 318 | error $LINENO "Cannot find /etc/proxysql-admin.cnf to read the credentials." \ 319 | "\nYou can either consider creating the cnf file or pass the credentials through command-line in the format " 320 | exit 1 321 | else 322 | source /etc/proxysql-admin.cnf 323 | USER=$PROXYSQL_USERNAME 324 | PASSWORD=$PROXYSQL_PASSWORD 325 | HOST=$PROXYSQL_HOSTNAME 326 | PORT=$PROXYSQL_PORT 327 | fi 328 | 329 | elif [[ $# -ne 4 ]]; then 330 | error "$LINENO" "Incorrect usage: Please use the format " 331 | usage 332 | exit 1 333 | else 334 | [[ -n ${1+} ]] && USER=$1 335 | [[ -n ${2+} ]] && PASSWORD=$2 336 | [[ -n ${3+} ]] && HOST=$3 337 | [[ -n ${4+} ]] && PORT=$4 338 | fi 339 | 340 | 341 | if [[ -z $USER || -z $PASSWORD || -z $HOST || -z $PORT ]]; then 342 | 343 | error "$LINENO" "One of the user, password, host, or port parameterd is missing." 344 | exit 1 345 | fi 346 | fi 347 | 348 | } 349 | 350 | 351 | parse_args "$@" 352 | 353 | # Run a test to see if we can connect 354 | TABLES=$(mysql_exec -BN "SELECT 1" >/dev/null) 355 | if [[ $? -ne 0 ]]; then 356 | error "$LINENO" "Cannot connect to the server at $HOST:$PORT" 357 | echo -e "Please check that the address is correct and the server is online" 358 | exit 1 359 | fi 360 | 361 | if [[ $DUMP_ALL -eq 1 || $DUMP_MAIN -eq 1 ]]; then 362 | echo "............ DUMPING MAIN DATABASE ............" 363 | TABLES=$(mysql_exec -BN "SHOW TABLES $RUNTIME_OPTION" 2>/dev/null) 364 | for table in $TABLES 365 | do 366 | if [[ -n $TABLE_FILTER && $table != *${TABLE_FILTER}* ]]; then 367 | continue 368 | fi 369 | echo "***** DUMPING $table *****" 370 | mysql_exec -t "SELECT * FROM $table" 371 | echo "***** END OF DUMPING $table *****" 372 | echo "" 373 | done 374 | echo "............ END OF DUMPING MAIN DATABASE ............" 375 | echo "" 376 | fi 377 | 378 | if [[ $DUMP_ALL -eq 1 || $DUMP_STATS -eq 1 ]]; then 379 | echo "............ DUMPING STATS DATABASE ............" 380 | TABLES=$(mysql_exec -BN "SHOW TABLES FROM stats" 2> /dev/null) 381 | for table in $TABLES 382 | do 383 | if [[ -n $TABLE_FILTER && $table != *${TABLE_FILTER}* ]]; then 384 | continue 385 | fi 386 | # Dump _reset tables only if we specify option --with-stats-reset 387 | if [[ $DUMP_STATS_RESET_TABLE -eq 0 ]]; then 388 | if echo "$table" | grep -q "_reset$"; then 389 | continue 390 | fi 391 | fi 392 | echo "***** DUMPING stats.$table *****" 393 | mysql_exec "-t --database=stats" "SELECT * FROM $table" 2> /dev/null 394 | echo "***** END OF DUMPING stats.$table *****" 395 | echo "" 396 | done 397 | echo "............ END OF DUMPING STATS DATABASE ............" 398 | echo "" 399 | fi 400 | 401 | if [[ $DUMP_ALL -eq 1 || $DUMP_MONITOR -eq 1 ]]; then 402 | echo "............ DUMPING MONITOR DATABASE ............" 403 | TABLES=$(mysql_exec -BN "SHOW TABLES FROM monitor" 2> /dev/null) 404 | for table in $TABLES 405 | do 406 | if [[ -n $TABLE_FILTER && $table != *${TABLE_FILTER}* ]]; then 407 | continue 408 | fi 409 | echo "***** DUMPING monitor.$table *****" 410 | mysql_exec "-t --database=monitor" "SELECT * FROM $table" 2> /dev/null 411 | echo "***** END OF DUMPING monitor.$table *****" 412 | echo "" 413 | done 414 | echo "............ END OF DUMPING MONITOR DATABASE ............" 415 | echo "" 416 | fi 417 | 418 | if [[ $DUMP_ALL -eq 1 || $DUMP_FILES -eq 1 ]]; then 419 | if [[ -z $TABLE_FILTER ]]; then 420 | if [[ -r "/var/lib/proxysql/host_priority.conf" ]]; then 421 | echo "............ DUMPING HOST PRIORITY FILE ............" 422 | cat /var/lib/proxysql/host_priority.conf 2>&1 423 | echo "............ END OF DUMPING HOST PRIORITY FILE ............" 424 | else 425 | echo "/var/lib/proxysql/host_priority.conf not found or not readble by you!" 426 | fi 427 | echo "" 428 | if [[ -r "/etc/proxysql-admin.cnf" ]]; then 429 | echo "............ DUMPING PROXYSQL ADMIN CNF FILE ............" 430 | cat /etc/proxysql-admin.cnf 2>&1 431 | echo "............ END OF DUMPING PROXYSQL ADMIN CNF FILE ............" 432 | else 433 | echo "/etc/proxysql-admin.cnf not found or not readble by you!" 434 | fi 435 | echo "" 436 | if [[ -r "/etc/config.toml" ]]; then 437 | echo "............ DUMPING PERCONA SCHEDULER ADMIN CNF FILE ............" 438 | cat /etc/config.toml 2>&1 439 | echo "............ END OF DUMPING PERCONA SCHEDULER ADMIN CNF FILE ............" 440 | else 441 | echo "/etc/config.toml not found or not readble by you!" 442 | fi 443 | echo "" 444 | fi 445 | fi 446 | -------------------------------------------------------------------------------- /tests/bad-login-file.clear.cnf: -------------------------------------------------------------------------------- 1 | 2 | # -------------------------------- 3 | # proxysql admin interface credentials. 4 | proxysql.user=admin 5 | proxysql.password=xxxxx 6 | proxysql.host=localhost 7 | proxysql.port=6032 8 | 9 | # -------------------------------- 10 | # PXC admin credentials for connecting to pxc-cluster-node. 11 | cluster.user=admin 12 | cluster.password=xxxxx 13 | cluster.host=localhost 14 | cluster.port=4110 15 | 16 | # -------------------------------- 17 | # proxysql monitoring user. proxysql admin script will create 18 | # this user in pxc to monitor pxc-nodes. 19 | monitor.user=monitor 20 | monitor.password=xxxx 21 | 22 | # -------------------------------- 23 | # Application user to connect to pxc-node through proxysql 24 | cluster-app.user=cluster_one 25 | cluster-app.password=xxxx 26 | 27 | 28 | -------------------------------------------------------------------------------- /tests/bad-login-file.cnf: -------------------------------------------------------------------------------- 1 | # Created Wed Sep 30 06:32:46 PDT 2020 2 | version=1 3 | encrypt_method=openssl-pbkdf2-aes-256-cbc 4 | iterations=100000 5 | encrypted_data=U2FsdGVkX19YHF55Dcsz4mgVmN49w073+u+sHJbO6w/5bMyZIlrhmUd3/OR51HrVueF2WTdBSR7znTiYpe4Wr9mW5FlXPZzgNk36f5ULKM/dvT9LqpHNlmiFuQqMOjRMXTSwzOxAiobXEn7WewNwXSvrir9H/qnBO5fQjCwowPnvlOYbaAlPXKQU2zsn++z6+Po820uJTqyvMXBzCeBFhcatTsfdNBEMMj4hxtzolu5/pttZz2dqC7XO93ZL0hK+ImldIMCNDkzuS8QLFhXt+Jw47G+WOUz3UFkMx3OHwAYcgFrjaB6YJhMm62skugCwVePXf22DMD9bsrChJmLwZdQsNUnLi5H0u1y4RwO4Mk8pakjwqH1lxGsWosQfLHrGOlHzi4Ta3wVQSnZP9nUgriDJrl1JsYbocBQ23FztFzH8NVux0vs+3BBYMrPQC8ebfcGFESkNUkGu+T0PG5UNhM1wRFNoM43zgCBKizkhicnTZ30LeOV2oR8jb0OmhYwiJd+CKQPPIUjqqCiXA9Pb9b980+O0TO+EkTNTb8ZAEqnH3ZAQczYUPhhfP6x+ar/1POOzda6UIf0h8q0p6dG5RpuVV+3EP+ibI3K88wnkIm8XKRnRoQetFniDNpLfHyd04fao6MIusOTNg5AEGpd26rmAwYvrHii9WZuOe71K7NK/tXq4+QrDw8TRuCrxPx3oC+1Hc5fxM+0ObcLgZ0stAFtNlsFFfOmQwmNkOdLOdlN0wnMpcuRTV5SKwaPxG99846kd97om5jJkyu8z6OfPvza/aCvqeMYyeXbTschMVLRsmggQ0xsKAo9JwNXu6aZnWvDXFn1gqPtL/Wo9Zve9g/N/t5oLQvhMgyZI3cE+vvfiqI7jto301Vf6lc05KdAGDM8Bo7+1IdE5kI8qGNnzNQrklIZoG8q8WdeUAaqMPhw= 6 | -------------------------------------------------------------------------------- /tests/bad-login-file2.clear.cnf: -------------------------------------------------------------------------------- 1 | 2 | # -------------------------------- 3 | # proxysql admin interface credentials. 4 | proxysql.user=admin 5 | proxysql.password=xxxxx 6 | proxysql.host=localhost 7 | proxysql.port=6032 8 | 9 | # -------------------------------- 10 | # PXC admin credentials for connecting to pxc-cluster-node. 11 | cluster.user=admin 12 | cluster.password=xxxxx 13 | cluster.host=localhost 14 | cluster.port=4210 15 | 16 | # -------------------------------- 17 | # proxysql monitoring user. proxysql admin script will create 18 | # this user in pxc to monitor pxc-nodes. 19 | monitor.user=monitor 20 | monitor.password=xxxx 21 | 22 | # -------------------------------- 23 | # Application user to connect to pxc-node through proxysql 24 | cluster-app.user=cluster_two 25 | cluster-app.password=xxxx 26 | 27 | 28 | -------------------------------------------------------------------------------- /tests/bad-login-file2.cnf: -------------------------------------------------------------------------------- 1 | # Created Wed Sep 30 06:32:35 PDT 2020 2 | version=1 3 | encrypt_method=openssl-pbkdf2-aes-256-cbc 4 | iterations=100000 5 | encrypted_data=U2FsdGVkX19wMUVJ9D0RrDTmUAcN2BMhRYpK18CdSFDM0vsCCE+yYzuL8FBV3mHrVEMBw8fJHDLkGKmpMvHUReSo8zdU3++7IBA3vcaVPEHUkmMQs/JUeGIhROMjgcAcO3QHpelhbSD2tVn9p+fbkQyGhSQ7gNbZwutL/TLGDKZT5f/z+Vjd0nWNx5TOIzbAjOF4OjygKKQ7Na/wxl+sb+vYIj6UypaeWtJY3k1O9ODlEolc7ZiKC4lKJbsN3OqP4Ik0jjQmjkuhnwPj5jQZ3M30/WzxZMDQTjVfMsSM9IgEeyEOBFATIETbvrGKu712PGRFMRCwnRzbbhZsfQAcXm2VT6lRIUFvtn+VDi1umtKU4BVjJowB0rM4i1Y00JVLsaLAb+IfA6PMwSAMmcxYtaJch+9Kfw74hDx5aCefccZZg0kCRg+XAbRWB7OSPGvYvz/nOp83ti7ZDzpGvM1SSXyrEquTMD0FOw7p3BvByR7I6Qf4bVSHZuhTz931OSi6Hy16jxR81B0+nng4vb7orUWS128GNsxSugO16r//4zR7L3MCfNxpUAAP9hNK6KNh9iQNwqjWF40xblmL4DjhzQrN+Y1e4u1QMCnpdh0lmcaPU9H/D+ag8ntWDNj5oiyE0+M6zjy0X8FCnqJi/WEn3P0MBH3+2n1d5F1Ka1qB2u5YUL7Qg7ljv9J0eXY9Gmq2yhOrCBGAlcjLRduK+RmgoZf+gyIZxR1opJ4onshZ+99b5QVywYIeddiSMvknFe4CTuXEYIIVWOmet7Bhhurb2NfLUsYG105p4Fp8vqRHzZNFaveJoduF1+BL33v56hzuoZDJNJWEnfyBrZY2TzNAKXWX2jpuDq796EOFWZh57XhXT1vhlQc1QyxabQGSEDYKLP1fTX5cIT6RwmsTv/HOeo6kuEwf4ZEIvnqIVVIpv+U= 6 | -------------------------------------------------------------------------------- /tests/disable_proxysql.bats: -------------------------------------------------------------------------------- 1 | ## proxysql-admin setup tests 2 | # 3 | 4 | # 5 | # Variable initialization 6 | # 7 | source /etc/proxysql-admin.cnf 8 | PXC_BASEDIR=$WORKDIR/pxc-bin 9 | PROXYSQL_BASEDIR=$WORKDIR/proxysql-bin 10 | 11 | # Declare some GLOBALS 12 | # These are used to return data from get_node_data() 13 | load test-common 14 | 15 | WSREP_CLUSTER_NAME=$(cluster_exec "select @@wsrep_cluster_name" 2> /dev/null) 16 | 17 | @test "run proxysql-admin -d ($WSREP_CLUSTER_NAME)" { 18 | run sudo PATH=$WORKDIR:$PATH $WORKDIR/proxysql-admin -d 19 | echo "$output" >&2 20 | [ "$status" -eq 0 ] 21 | } 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/generic-test.bats: -------------------------------------------------------------------------------- 1 | ## Generic bats tests 2 | 3 | # 4 | # Variable initialization 5 | # 6 | source /etc/proxysql-admin.cnf 7 | PXC_BASEDIR=$WORKDIR/pxc-bin 8 | PROXYSQL_BASEDIR=$WORKDIR/proxysql-bin 9 | 10 | @test "run proxysql-admin under root privileges" { 11 | if [[ $(id -u) -ne 0 ]] ; then 12 | skip "Skipping this test, because you are NOT running under root" 13 | fi 14 | run $WORKDIR/proxysql-admin 15 | echo "$output" >&2 16 | [ "$status" -eq 1 ] 17 | [ "${lines[0]}" = "Usage: proxysql-admin [ options ]" ] 18 | } 19 | 20 | @test "run proxysql-admin without any arguments" { 21 | run sudo $WORKDIR/proxysql-admin 22 | echo "$output" >&2 23 | [ "$status" -eq 1 ] 24 | [ "${lines[0]}" = "Usage: proxysql-admin [ options ]" ] 25 | } 26 | 27 | @test "run proxysql-admin --help" { 28 | run sudo $WORKDIR/proxysql-admin --help 29 | echo "$output" >&2 30 | [ "$status" -eq 0 ] 31 | [ "${lines[0]}" = "Usage: proxysql-admin [ options ]" ] 32 | } 33 | 34 | @test "run proxysql-admin with wrong option" { 35 | run sudo $WORKDIR/proxysql-admin test 36 | echo "$output" >&2 37 | [ "$status" -eq 1 ] 38 | } 39 | 40 | @test "run proxysql-admin --config-file without parameters" { 41 | run sudo $WORKDIR/proxysql-admin --config-file 42 | echo "$output" >&2 43 | [ "$status" -eq 1 ] 44 | } 45 | 46 | @test "run proxysql-admin check default configuration file" { 47 | run ls /etc/proxysql-admin.cnf 48 | echo "$output" >&2 49 | [ "$status" -eq 0 ] 50 | [ "${lines[0]}" == "/etc/proxysql-admin.cnf" ] 51 | } 52 | 53 | @test "run proxysql-admin --proxysql-username without parameters" { 54 | run sudo $WORKDIR/proxysql-admin --proxysql-username 55 | echo "$output" >&2 56 | [ "$status" -eq 1 ] 57 | } 58 | 59 | @test "run proxysql-admin --proxysql-port without parameters" { 60 | run sudo $WORKDIR/proxysql-admin --proxysql-port 61 | echo "$output" >&2 62 | [ "$status" -eq 1 ] 63 | } 64 | 65 | @test "run proxysql-admin --proxysql-hostname without parameters" { 66 | run sudo $WORKDIR/proxysql-admin --proxysql-hostname 67 | echo "$output" >&2 68 | [ "$status" -eq 1 ] 69 | } 70 | 71 | @test "run proxysql-admin --cluster-username without parameters" { 72 | run sudo $WORKDIR/proxysql-admin --cluster-username 73 | echo "$output" >&2 74 | [ "$status" -eq 1 ] 75 | } 76 | 77 | @test "run proxysql-admin --cluster-port without parameters" { 78 | run sudo $WORKDIR/proxysql-admin --cluster-port 79 | echo "$output" >&2 80 | [ "$status" -eq 1 ] 81 | } 82 | 83 | @test "run proxysql-admin --cluster-hostname without parameters" { 84 | run sudo $WORKDIR/proxysql-admin --cluster-hostname 85 | echo "$output" >&2 86 | [ "$status" -eq 1 ] 87 | } 88 | 89 | @test "run proxysql-admin --cluster-app-username without parameters" { 90 | run sudo $WORKDIR/proxysql-admin --cluster-app-username 91 | echo "$output" >&2 92 | [ "$status" -eq 1 ] 93 | } 94 | 95 | @test "run proxysql-admin --monitor-username without parameters" { 96 | run sudo $WORKDIR/proxysql-admin --monitor-username 97 | echo "$output" >&2 98 | [ "$status" -eq 1 ] 99 | } 100 | 101 | @test "run proxysql-admin --mode without parameters" { 102 | run sudo $WORKDIR/proxysql-admin --mode 103 | echo "$output" >&2 104 | [ "$status" -eq 1 ] 105 | } 106 | 107 | @test "run proxysql-admin --mode check wrong option" { 108 | run sudo $WORKDIR/proxysql-admin --mode=foo 109 | echo "$output" >&2 110 | [ "$status" -eq 1 ] 111 | [ "${lines[0]}" == "ERROR : Invalid --mode passed: 'foo'" ] 112 | } 113 | 114 | @test "run proxysql-admin --write-node without parameters" { 115 | run sudo $WORKDIR/proxysql-admin --write-node 116 | echo "$output" >&2 117 | [ "$status" -eq 1 ] 118 | [[ "${lines[0]}" =~ .*--write-node.* ]] 119 | } 120 | 121 | @test "run proxysql-admin --write-node with missing port" { 122 | run sudo $WORKDIR/proxysql-admin --write-node=1.1.1.1,2.2.2.2:44 --disable 123 | echo "$output" >&2 124 | [ "$status" -eq 1 ] 125 | [[ "${lines[0]}" =~ ERROR.*--write-node.*expects.* ]] 126 | 127 | run sudo $WORKDIR/proxysql-admin --write-node=[1:1:1:1],[2:2:2:2]:44 --disable 128 | echo "$output" >&2 129 | [ "$status" -eq 1 ] 130 | [[ "${lines[0]}" =~ ERROR.*--write-node.*expects.* ]] 131 | } 132 | 133 | @test "run proxysql-admin --server without parameters" { 134 | run sudo $WORKDIR/proxysql-admin --server 135 | echo "$output" >&2 136 | [ "$status" -eq 1 ] 137 | [[ "${lines[0]}" =~ .*--server.* ]] 138 | } 139 | 140 | @test "run proxysql-admin --server with missing port" { 141 | run sudo $WORKDIR/proxysql-admin --server=1.1.1.1 --syncusers 142 | echo "$output" >&2 143 | [ "$status" -eq 1 ] 144 | [[ "${lines[0]}" =~ ERROR.*--server.*expected.* ]] 145 | 146 | run sudo $WORKDIR/proxysql-admin --server=[1:1:1:1] --syncusers 147 | echo "$output" >&2 148 | [ "$status" -eq 1 ] 149 | [[ "${lines[0]}" =~ ERROR.*--server.*expected.* ]] 150 | } 151 | 152 | @test "run proxysql-admin --server with unsupported commands" { 153 | run sudo $WORKDIR/proxysql-admin --server=1.1.1.1 --disable 154 | echo "$output" >&2 155 | [ "$status" -eq 1 ] 156 | [[ "${lines[0]}" =~ ERROR.*--server.*can.only.be.used.* ]] 157 | 158 | run sudo $WORKDIR/proxysql-admin --server=[1:1:1:1] --update-cluster 159 | echo "$output" >&2 160 | [ "$status" -eq 1 ] 161 | [[ "${lines[0]}" =~ ERROR.*--server.*can.only.be.used.* ]] 162 | } 163 | 164 | @test 'run proxysql-admin --max-connections with empty parameters' { 165 | run sudo $WORKDIR/proxysql-admin --max-connections 166 | echo "$output" >&2 167 | [ "$status" -eq 1 ] 168 | } 169 | 170 | @test 'run proxysql-admin --max-connections without parameters' { 171 | run sudo $WORKDIR/proxysql-admin --max-connections= 172 | echo "$output" >&2 173 | [ "$status" -eq 1 ] 174 | } 175 | 176 | @test 'run proxysql-admin --max-connections with non-number parameter' { 177 | run sudo $WORKDIR/proxysql-admin --max-connections=abc 178 | echo "$output" >&2 179 | [ "$status" -eq 1 ] 180 | } 181 | 182 | @test 'run proxysql-admin --max-connections with negative values' { 183 | run sudo $WORKDIR/proxysql-admin --max-connections=-1 184 | echo "$output" >&2 185 | [ "$status" -eq 1 ] 186 | } 187 | 188 | @test 'run proxysql-admin --login-file with nonexistent file' { 189 | run sudo $WORKDIR/proxysql-admin --login-file=dummy.file.should.not.exist.cnf 190 | echo "$output" >&2 191 | [ "$status" -eq 1 ] 192 | } 193 | 194 | @test 'run proxysql-admin --login-file with nonexistent --login-password-file' { 195 | run sudo $WORKDIR/proxysql-admin --login-file=login-file.cnf --login-password-file=dummy.file.should.not.exist.cnf 196 | echo "$output" >&2 197 | [ "$status" -eq 1 ] 198 | } 199 | 200 | @test "run proxysql-admin --version check" { 201 | admin_version=$(sudo $WORKDIR/proxysql-admin -v | grep --extended-regexp -oe '[1-9]\.[0-9]\.[0-9]+') 202 | proxysql_version=$(sudo $PROXYSQL_BASEDIR/usr/bin/proxysql --help | grep --extended-regexp -oe '[1-9]\.[0-9]\.[0-9]+') 203 | echo "proxysql_version:$proxysql_version admin_version:$admin_version" >&2 204 | [ "${proxysql_version}" = "${admin_version}" ] 205 | } 206 | -------------------------------------------------------------------------------- /tests/login-file.clear.cnf: -------------------------------------------------------------------------------- 1 | 2 | # -------------------------------- 3 | # proxysql admin interface credentials. 4 | proxysql.user=admin 5 | proxysql.password=admin 6 | proxysql.host=localhost 7 | proxysql.port=6032 8 | 9 | # -------------------------------- 10 | # PXC admin credentials for connecting to pxc-cluster-node. 11 | cluster.user=admin 12 | cluster.password=admin 13 | cluster.host=localhost 14 | cluster.port=4110 15 | 16 | # -------------------------------- 17 | # proxysql monitoring user. proxysql admin script will create 18 | # this user in pxc to monitor pxc-nodes. 19 | monitor.user=monitor 20 | monitor.password=monitor 21 | 22 | # -------------------------------- 23 | # Application user to connect to pxc-node through proxysql 24 | cluster-app.user=cluster_one 25 | cluster-app.password=passw0rd 26 | 27 | 28 | -------------------------------------------------------------------------------- /tests/login-file.cnf: -------------------------------------------------------------------------------- 1 | # Created Wed Sep 30 06:32:57 PDT 2020 2 | version=1 3 | encrypt_method=openssl-pbkdf2-aes-256-cbc 4 | iterations=100000 5 | encrypted_data=U2FsdGVkX19fwq5TWk7CWKQLG3I0gvlaiNhC+XvT1T/SacrRr2oeiheIjm4IkRSDPMjv4CvRz8xDa+JR1aIogH8MzbBrgIrvP5rOO3XB1sPUWeeJzIopHKhdm4ISlxwlLjyXq2V9rM7x3AryvL2sAq9bBHWhYio39/emdANiDXXOUGMWeNsNIk4yc6TRFwJEZ9IZ/vbvvK5ZhuS78f0xlz80Yg8ieegkqxWk09s8zieg/kJ14KW6Goyk9L4Fncn50lsB1Juph3DXbHFP+7N4Wn2m94NYdkRn69/r1MFMEfl1BYNCfCQe2zdUjqyY5CmSatrjRFi+uRdqY/tie3pK4yebX8yVu1gxDd4/qeik0/QL3rvGyrgT6HCpL6/s3Bc93asnaRx/dxAmMEGegm8iKWfvLEuGAn1lUZJg0Pu60QZdQBXx+EQq42qCaivQU2f0G50d4S3LjN0fYScxY8Z6PfumXV5O1xHUQIyg0uqDcnI9jh7JHVXeDE0vAAbEm2QemvTefls4bvb42VxVC4MX1FRwkm0asUEBK4KbZ2c7XcAgIszqX83XzWC+G1ezFAVyO6eDIVJLAHG3IiwzxhHwji4naAOkGvQbzSZi3VeoaLuJ8wae0RgmRKd1Xai/0pC9L34LsEJxOw3n6Y9whLjWET18wedJYee28ss/tPRqCka/KTDl43toOKLpHLGsqaapfBrN0JItXYohghDIGV28Nex6nyOR+o7XXWvH1Ex0EGjWTLJHwBu8DL4S7VI+11YzAvWyxl/cASedSepYvqQ6+x1ZcIIsfY3WAaJ6rsSRM9tSVvw8925EdE/xTAVgrjsg7DsTOmOxtSMOfUgDwKsMfQiWy/mt88aSwdSKJYiGOIAFoSmlXCvgV021T3gI+B/0TFi40M6eFPnTSnjNla0xMSrrzAplOvGp4Kap7L3LjH8= 6 | -------------------------------------------------------------------------------- /tests/login-file2.clear.cnf: -------------------------------------------------------------------------------- 1 | 2 | # -------------------------------- 3 | # proxysql admin interface credentials. 4 | proxysql.user=admin 5 | proxysql.password=admin 6 | proxysql.host=localhost 7 | proxysql.port=6032 8 | 9 | # -------------------------------- 10 | # PXC admin credentials for connecting to pxc-cluster-node. 11 | cluster.user=admin 12 | cluster.password=admin 13 | cluster.host=localhost 14 | cluster.port=4210 15 | 16 | # -------------------------------- 17 | # proxysql monitoring user. proxysql admin script will create 18 | # this user in pxc to monitor pxc-nodes. 19 | monitor.user=monitor 20 | monitor.password=monitor 21 | 22 | # -------------------------------- 23 | # Application user to connect to pxc-node through proxysql 24 | cluster-app.user=cluster_two 25 | cluster-app.password=passw0rd 26 | 27 | 28 | -------------------------------------------------------------------------------- /tests/login-file2.cnf: -------------------------------------------------------------------------------- 1 | # Created Wed Sep 30 06:33:08 PDT 2020 2 | version=1 3 | encrypt_method=openssl-pbkdf2-aes-256-cbc 4 | iterations=100000 5 | encrypted_data=U2FsdGVkX19EwylQLNtVvXMggcujuiLnETNscBx7OB9k0l/YUQSOIWoAaP2Bg29E6abK6FB0fb2jYD8RLrxkGriqEHtcoA+sfaPPpVbuiJ8m+m7S9L0on/UDBLbJVxhkg7AmMQ66h+Cg4oIhSyUD7k076T2/HHMRDHvjOKvwuNmgQpukIYhYlpzhNVZ1woTmdPzfrwB/SYu5bLKEMctafG4N96jmY34WFyVU0t/ZINgbxsaRFDZdz8aqfShSE/CHkV/+2biDm1GTikaC+s/FQmu4uW/ERRuXTcPApNAGsmlOWnkXJUkhAaBrU0pS8G13HIVXOJCkAsD58O5Z8R8A3K9wBwGzNZPq7BoVlF22ehLJgkHUmAwr1Oj1k2HHfFvc1wfJvzwXBPmaBMy2IIOU/nklgRUKwNt/b0UZPAKzEDJbOxa0rxbqgoRPiH42F+mv9sBGGbxHpM6GOckz/TUc4i1sY2LR+R7IHHMCoSMlJMMmVIHsh+Ke4zJWMugkfoQG1odZ/AwJISZt+uLQUGt83byNpMQGFVgSG3L9f1v53OH5Q2O+U74bJg/aco55+eyyR57udDD5kI8ozLBH9XSJISLSYJ10NwLRlqZG8ooHYys53B5Ld1rjKMbacairCDrD2P0DKXHHXV9St/8QnukV9uRKc3/1Bm1BMRcUzG51tZie9a5yvEjgD59LDr7yYnuaC070k6AtWt2rCXxRjpCl5YifWb6Tfm5MJ/myiLttodLMFJiYofMthd51FfR0dTEs+bOS2Ya6B3cInp8/tbzWB3ZfUHS8HC67rtnWwwIBWIQDfPGez9qBgSokdgXOx+rD98VfctL97qfYkGBNA/OGct1sJ44BH/W46SYC+bXNHtVeF0gbA4CktQS8U3uRtp80Zft5LSUTCp7Oc6D5I/9toeSm6gSNqr9zRqHkeaEB5oA= 6 | -------------------------------------------------------------------------------- /tests/scheduler-args.bats: -------------------------------------------------------------------------------- 1 | ## Test of go-scheduler argument handling 2 | 3 | # 4 | # Variable initialization 5 | # 6 | source /etc/proxysql-admin.cnf 7 | PXC_BASEDIR=$WORKDIR/pxc-bin 8 | PROXYSQL_BASEDIR=$WORKDIR/proxysql-bin 9 | 10 | @test "run percona-scheduler-admin under root privileges" { 11 | if [[ $(id -u) -ne 0 ]] ; then 12 | skip "Skipping this test, because you are NOT running under root" 13 | fi 14 | run $WORKDIR/percona-scheduler-admin 15 | echo "$output" >&2 16 | [ "$status" -eq 1 ] 17 | [ "${lines[0]}" = "Usage: percona-scheduler-admin [ options ]" ] 18 | } 19 | 20 | @test "run percona-scheduler-admin without any arguments" { 21 | run sudo $WORKDIR/percona-scheduler-admin 22 | echo "$output" >&2 23 | [ "$status" -eq 1 ] 24 | [ "${lines[0]}" = "Usage: percona-scheduler-admin [ options ]" ] 25 | } 26 | 27 | @test "run percona-scheduler-admin --help" { 28 | run sudo $WORKDIR/percona-scheduler-admin --help 29 | echo "$output" >&2 30 | [ "$status" -eq 0 ] 31 | [ "${lines[0]}" = "Usage: percona-scheduler-admin [ options ]" ] 32 | } 33 | 34 | @test "run percona-scheduler-admin with wrong option" { 35 | run sudo $WORKDIR/percona-scheduler-admin test 36 | echo "$output" >&2 37 | [ "$status" -eq 2 ] 38 | } 39 | 40 | @test "run percona-scheduler-admin --config-file without parameters" { 41 | run sudo $WORKDIR/percona-scheduler-admin --config-file 42 | echo "$output" >&2 43 | [ "$status" -eq 1 ] 44 | } 45 | 46 | @test "run percona-scheduler-admin --config-file with incorrect filename" { 47 | run sudo $WORKDIR/percona-scheduler-admin --config-file=$SCRIPTDIR/missing_file.txt 48 | echo "$output" >&2 49 | [ "$status" -eq 2 ] 50 | } 51 | 52 | @test "run percona-scheduler-admin --proxysql-username without parameters" { 53 | run sudo $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --proxysql-username 54 | echo "$output" >&2 55 | [ "$status" -eq 1 ] 56 | } 57 | 58 | @test "run percona-scheduler-admin --proxysql-port without parameters" { 59 | run sudo $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --proxysql-port 60 | echo "$output" >&2 61 | [ "$status" -eq 1 ] 62 | } 63 | 64 | @test "run percona-scheduler-admin --proxysql-hostname without parameters" { 65 | run sudo $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --proxysql-hostname 66 | echo "$output" >&2 67 | [ "$status" -eq 1 ] 68 | } 69 | 70 | @test "run percona-scheduler-admin --cluster-username without parameters" { 71 | run sudo $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --cluster-username 72 | echo "$output" >&2 73 | [ "$status" -eq 1 ] 74 | } 75 | 76 | @test "run percona-scheduler-admin --cluster-port without parameters" { 77 | run sudo $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --cluster-port 78 | echo "$output" >&2 79 | [ "$status" -eq 1 ] 80 | } 81 | 82 | @test "run percona-scheduler-admin --cluster-hostname without parameters" { 83 | run sudo $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --cluster-hostname 84 | echo "$output" >&2 85 | [ "$status" -eq 1 ] 86 | } 87 | 88 | @test "run percona-scheduler-admin --cluster-app-username without parameters" { 89 | run sudo $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --cluster-app-username 90 | echo "$output" >&2 91 | [ "$status" -eq 1 ] 92 | } 93 | 94 | @test "run percona-scheduler-admin --monitor-username without parameters" { 95 | run sudo $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --monitor-username 96 | echo "$output" >&2 97 | [ "$status" -eq 1 ] 98 | } 99 | 100 | @test "run percona-scheduler-admin --write-node without parameters" { 101 | run sudo $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --write-node 102 | echo "$output" >&2 103 | [ "$status" -eq 1 ] 104 | [[ "${lines[0]}" =~ .*--write-node.* ]] 105 | } 106 | 107 | @test "run percona-scheduler-admin --write-node with missing port" { 108 | run sudo PATH=$PATH $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --write-node=1.1.1.1,2.2.2.2:44 --disable 109 | echo "$output" >&2 110 | [ "$status" -eq 1 ] 111 | [[ "${lines[0]}" =~ ERROR.*--write-node.*expects.* ]] 112 | 113 | run sudo PATH=$PATH $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --write-node=[1:1:1:1],[2:2:2:2]:44 --disable 114 | echo "$output" >&2 115 | [ "$status" -eq 1 ] 116 | [[ "${lines[0]}" =~ ERROR.*--write-node.*expects.* ]] 117 | } 118 | 119 | @test "run percona-scheduler-admin --version check" { 120 | admin_version=$(sudo $WORKDIR/percona-scheduler-admin -v | head -1 | grep --extended-regexp -oe '[1-9]\.[0-9]\.[0-9]+') 121 | scheduler_version=$(sudo $WORKDIR/percona-scheduler-admin -v | head -1 | grep --extended-regexp -oe '[1-9]\.[0-9]\.[0-9]+') 122 | proxysql_version=$(sudo $PROXYSQL_BASEDIR/usr/bin/proxysql --help | grep --extended-regexp -oe '[1-9]\.[0-9]\.[0-9]+') 123 | echo "proxysql_version:$proxysql_version admin_version:$admin_version scheduler_version:$scheduler_version" >&2 124 | 125 | # All the versions should be the same 126 | [ "${proxysql_version}" = "${admin_version}" ] 127 | [ "${admin_version}" = "${scheduler_version}" ] 128 | } 129 | 130 | # Mutually exclusive options 131 | @test "run percona-scheduler-admin --auto-assign-weights, write-node and --update-write-weight options" { 132 | run sudo PATH=$PATH $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --auto-assign-weights --write-node=1.1.1.1,2.2.2.2:44 133 | echo "$output" >&2 134 | [ "$status" -eq 1 ] 135 | [[ "${lines[0]}" =~ ERROR.*options.are.mutually.exclusive.* ]] 136 | 137 | run sudo PATH=$PATH $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --write-node=2.2.2.2:44 --update-write-weight="[::1]:4130,2000" 138 | echo "$output" >&2 139 | [ "$status" -eq 1 ] 140 | [[ "${lines[0]}" =~ ERROR.*options.are.mutually.exclusive.* ]] 141 | 142 | run sudo PATH=$PATH $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --update-write-weight="[::1]:4130,2000" --auto-assign-weights 143 | echo "$output" >&2 144 | [ "$status" -eq 1 ] 145 | [[ "${lines[0]}" =~ ERROR.*options.are.mutually.exclusive.* ]] 146 | } 147 | 148 | # Malformed address in --update-write-weight 149 | @test "run percona-scheduler-admin --update-write-weight" { 150 | run sudo PATH=$PATH $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --update-write-weight="[::1]:4130av,20s00" 151 | echo "$output" >&2 152 | [ "$status" -eq 1 ] 153 | [[ "${lines[0]}" =~ ERROR.*expected.address.in.format.* ]] 154 | 155 | run sudo PATH=$PATH $WORKDIR/percona-scheduler-admin --config-file=testsuite.toml --update-write-weight="[::1]:4130,20s00" 156 | echo "$output" >&2 157 | [ "$status" -eq 1 ] 158 | [[ "${lines[0]}" =~ ERROR.*Weight.in.--update-write-weight.requires.a.number.* ]] 159 | } 160 | -------------------------------------------------------------------------------- /tests/setup_workdir.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Created by Mohit Joshi, Percona LLC 3 | # Creation date: 15-April-2021 4 | # 5 | # The script is used for creating the workdir which must be passed to proxysql-admin-testsuite.sh 6 | # for testing the proxysql-admin functionality 7 | 8 | # Create Helper Functions 9 | 10 | help() { 11 | cat << EOF 12 | Usage: 13 | ./setup_workdir.sh /path/where/the/workdir/should/be/created skip_download 14 | eg. 15 | 1) ./setup_workdir.sh ~/workdir1 1 => skips the download of PXC 16 | 2) ./setup_workdir.sh /tmp/workdir2 0 17 | 3) ./setup_workdir.sh workdir3 18 | 19 | 20 | Note: The script will exit if there exists a directory by the same name 21 | EOF 22 | } 23 | 24 | enable_repo() { 25 | # parameters are: 26 | local repo_name=$1 27 | local repo_type=$2 28 | 29 | # Assuming percona-release utility is installed on the machine 30 | sudo percona-release enable "$repo_name" "$repo_type" 31 | } 32 | 33 | install_package() { 34 | # parameters are stored in array varible: 35 | local -a pkg_name_arr=("$@") 36 | 37 | if [ -f /usr/bin/yum ]; then 38 | for file in "${pkg_name_arr[@]}"; do 39 | sudo yum install -y "$file" 40 | done 41 | elif [ -f /usr/bin/apt ]; then 42 | for file in "${pkg_name_arr[@]}"; do 43 | sudo apt-get update -y 44 | sudo apt-get install -y "$file" 45 | done 46 | fi 47 | } 48 | 49 | # Call the helper function if no argument is passed 50 | if [[ $# -eq 0 ]]; then 51 | help 52 | exit 1 53 | fi 54 | 55 | # Check if Proxysql is installed 56 | echo "Looking for proxysql package installed on the machine" 57 | if [[ ! -e $(which proxysql) ]];then 58 | echo "...ProxySQL not found" 59 | echo "Installing proxysql2 package" 60 | enable_repo proxysql testing 61 | install_package proxysql2 62 | echo "...ProxySQL installed successfully" 63 | PROXYSQL=$(which proxysql) 64 | else 65 | PROXYSQL=$(which proxysql) 66 | echo "...ProxySQL found at $PROXYSQL" 67 | fi 68 | 69 | # Ensure we have read permission on ProxySQL configuration file 70 | sudo chmod 644 /etc/proxysql*.cnf 71 | 72 | # Check if mysql client is installed 73 | echo "Looking for mysql client installed on the machine" 74 | if [[ ! -e $(which mysql) ]]; then 75 | echo "...mysql client not found" 76 | echo "Installing latest mysql client" 77 | enable_repo pxc-80 release 78 | install_package percona-xtradb-cluster-client 79 | echo "...mysql client install successfully" 80 | else 81 | echo "...mysql client found at $(which mysql)" 82 | fi 83 | 84 | WORKDIR=$1 85 | SKIP_DOWNLOAD=$2 86 | SKIP_DOWNLOAD=${SKIP_DOWNLOAD:=0} 87 | VERSION="8.4" 88 | 89 | 90 | # Fetch PXC versions 91 | if [[ $VERSION == "8.0" ]]; then 92 | 93 | LATEST_VERSION=$(git ls-remote --refs --sort='version:refname' --tags https://github.com/percona/percona-xtradb-cluster | \ 94 | grep 'Percona-XtraDB-Cluster-8.0' | tail -n1 | cut -d '/' -f3 | cut -d '-' -f4) 95 | VERSION_SUFFIX=$(git ls-remote --refs --sort='version:refname' --tags https://github.com/percona/percona-xtradb-cluster | \ 96 | grep 'Percona-XtraDB-Cluster-8.0' | tail -n1 | cut -d '/' -f3 | cut -d '-' -f5) 97 | elif [[ $VERSION == "8.4" ]]; then 98 | LATEST_VERSION=$(git ls-remote --refs --sort='version:refname' --tags https://github.com/percona/percona-xtradb-cluster | \ 99 | grep 'Percona-XtraDB-Cluster-8.4' | tail -n1 | cut -d '/' -f3 | cut -d '-' -f4) 100 | VERSION_SUFFIX=$(git ls-remote --refs --sort='version:refname' --tags https://github.com/percona/percona-xtradb-cluster | \ 101 | grep 'Percona-XtraDB-Cluster-8.4' | tail -n1 | cut -d '/' -f3 | cut -d '-' -f5) 102 | fi 103 | 104 | if [ -d "$WORKDIR" ]; then 105 | echo "Directory with the provided name already exist." 106 | echo "Exiting..." 107 | exit 1 108 | else 109 | mkdir -p "$WORKDIR" "$WORKDIR"/proxysql-2.0/usr/bin "$WORKDIR"/proxysql-2.0/etc 110 | if [ -d "$WORKDIR" ]; then 111 | echo "...Work Directory created successfully"; 112 | fi 113 | fi 114 | 115 | echo "Looking for ProxySQL Admin Base directory"; 116 | SCRIPT=$(readlink -f "$0") 117 | SCRIPTPATH=$(dirname "$SCRIPT") 118 | PROXYSQL_ADMIN_BASEDIR=$(realpath "$SCRIPTPATH"/../) 119 | 120 | if [ -f "$PROXYSQL_ADMIN_BASEDIR"/proxysql-admin ]; then 121 | echo "...ProxySQL Base Directory found at $PROXYSQL_ADMIN_BASEDIR" 122 | else 123 | echo "...ProxySQL Base Directory not found. Exiting!" 124 | exit 1 125 | fi 126 | 127 | echo "Looking for Percona Scheduler Handler"; 128 | if [ -f "$PROXYSQL_ADMIN_BASEDIR"/pxc_scheduler_handler ]; then 129 | echo "...Percona Scheduler binary found at $PROXYSQL_ADMIN_BASEDIR/pxc_scheduler_handler" 130 | else 131 | echo "...Not found. Building percona scheduler" 132 | pushd "$PROXYSQL_ADMIN_BASEDIR" > /dev/null || exit 133 | bash build_scheduler.sh > /dev/null 2>&1 134 | popd > /dev/null || exit 135 | fi 136 | 137 | echo "Creating Symbolic links" 138 | ln -s "$PROXYSQL" "$WORKDIR"/proxysql-2.0/usr/bin 139 | ln -s "$PROXYSQL_ADMIN_BASEDIR"/proxysql-admin.cnf "$WORKDIR"/proxysql-2.0/etc 140 | for file in proxysql-admin proxysql-common proxysql-admin-common percona-scheduler-admin proxysql-login-file pxc_scheduler_handler 141 | do 142 | ln -s "$PROXYSQL_ADMIN_BASEDIR"/$file "$WORKDIR" 143 | done; 144 | 145 | 146 | echo "...Symbolic links created successfully" 147 | 148 | 149 | echo "Copying config files required for the test" 150 | cp $PROXYSQL_ADMIN_BASEDIR/tests/testsuite.toml $WORKDIR 151 | echo "...Copying done" 152 | 153 | if [[ ! $SKIP_DOWNLOAD -eq 1 ]];then 154 | echo "Fetching the PXC tarball packages" 155 | if [[ $VERSION == "8.0" ]]; then 156 | wget -O $WORKDIR/Percona-XtraDB-Cluster_${LATEST_VERSION}_Linux.x86_64.glibc2.35-minimal.tar.gz http://downloads.percona.com/downloads/Percona-XtraDB-Cluster-80/Percona-XtraDB-Cluster-${LATEST_VERSION}/binary/tarball/Percona-XtraDB-Cluster_${LATEST_VERSION}-${VERSION_SUFFIX}_Linux.x86_64.glibc2.35-minimal.tar.gz 157 | elif [[ $VERSION == "8.4" ]]; then 158 | wget -O $WORKDIR/Percona-XtraDB-Cluster_${LATEST_VERSION}_Linux.x86_64.glibc2.35-minimal.tar.gz http://downloads.percona.com/downloads/Percona-XtraDB-Cluster-84/Percona-XtraDB-Cluster-${LATEST_VERSION}/binary/tarball/Percona-XtraDB-Cluster_${LATEST_VERSION}-${VERSION_SUFFIX}_Linux.x86_64.glibc2.35-minimal.tar.gz 159 | fi 160 | echo "...Successful" 161 | fi 162 | 163 | echo "The workdir is ready for use located at: $WORKDIR" 164 | echo "Run: $PROXYSQL_ADMIN_BASEDIR/tests/proxysql-admin-testsuite.sh $WORKDIR" 165 | 166 | -------------------------------------------------------------------------------- /tests/test-common.bash: -------------------------------------------------------------------------------- 1 | # Common helper file for bats test script 2 | 3 | # The minimum required openssl version 4 | readonly REQUIRED_OPENSSL_VERSION="1.1.1" 5 | 6 | # The name of the openssl binary packaged with proxysql-admin 7 | readonly PROXYSQL_ADMIN_OPENSSL_NAME="proxysql-admin-openssl" 8 | 9 | declare DEBUG_SQL_QUERY=0 10 | 11 | 12 | function exec_sql() { 13 | local user=$1 14 | local password=$2 15 | local hostname=$3 16 | local port=$4 17 | local query=$5 18 | local args="--skip-column_names" 19 | local retvalue 20 | local retoutput 21 | 22 | if [[ $# -ge 6 ]]; then 23 | args=$6 24 | fi 25 | 26 | if [[ $DEBUG_SQL_QUERY -eq 1 ]]; then 27 | echo "exec_sql : $user@$hostname:$port $password ==> $query" >&2 28 | fi 29 | 30 | retoutput=$(printf "[client]\nuser=${user}\npassword=\"${password}\"\nhost=${hostname}\nport=${port}" \ 31 | | $PXC_BASEDIR/bin/mysql --defaults-file=/dev/stdin --protocol=tcp \ 32 | --unbuffered --batch --silent ${args} -e "${query}") 33 | retvalue=$? 34 | 35 | if [[ $DEBUG_SQL_QUERY -eq 1 ]]; then 36 | local number_of_newlines=0 37 | local dbgoutput=$retoutput 38 | 39 | if [[ -n $retoutput ]]; then 40 | number_of_newlines=$(printf "%s" "${dbgoutput}" | wc -l) 41 | fi 42 | 43 | if [[ $retvalue -ne 0 ]]; then 44 | echo "--> query failed $retvalue" >&2 45 | elif [[ -z $dbgoutput ]]; then 46 | echo "--> query returned $retvalue : " >&2 47 | elif [[ ${number_of_newlines} -eq 0 ]]; then 48 | echo "--> query returned $retvalue : ${dbgoutput}" >&2 49 | else 50 | echo "--> query returned $retvalue : " >&2 51 | printf "%s\n" "${dbgoutput}" | while IFS= read -r line; do 52 | echo "----> $line" >&2 53 | done 54 | fi 55 | fi 56 | 57 | 58 | printf "%s" "${retoutput}" 59 | return $retvalue 60 | } 61 | 62 | function proxysql_exec() { 63 | local query=$1 64 | local args="" 65 | 66 | if [[ $# -ge 2 ]]; then 67 | args=$2 68 | fi 69 | 70 | exec_sql "$PROXYSQL_USERNAME" "$PROXYSQL_PASSWORD" \ 71 | "$PROXYSQL_HOSTNAME" "$PROXYSQL_PORT" \ 72 | "$query" "$args" 73 | 74 | return $? 75 | } 76 | 77 | function cluster_exec() { 78 | local query=$1 79 | local args="" 80 | 81 | if [[ $# -ge 2 ]]; then 82 | args=$2 83 | fi 84 | 85 | exec_sql "$CLUSTER_USERNAME" "$CLUSTER_PASSWORD" \ 86 | "$CLUSTER_HOSTNAME" "$CLUSTER_PORT" \ 87 | "$query" "$args" 88 | 89 | return $? 90 | } 91 | 92 | function mysql_exec() { 93 | local hostname=$1 94 | local port=$2 95 | local query=$3 96 | local args="" 97 | 98 | if [[ $# -ge 4 ]]; then 99 | args=$4 100 | fi 101 | 102 | exec_sql "$CLUSTER_USERNAME" "$CLUSTER_PASSWORD" \ 103 | "$hostname" "$port" \ 104 | "$query" "$args" 105 | 106 | return $? 107 | } 108 | 109 | 110 | # Returns information about the nodes in the hostgroup 111 | # 112 | # Globals: 113 | # HOSTS 114 | # PORTS 115 | # STATUS 116 | # COMMENTS 117 | # HOSTGROUPS 118 | # WEIGHTS 119 | # MAX_CONNECTIONS 120 | # 121 | # Arguments: 122 | # 1: hostgroup 123 | # 124 | function get_node_data() { 125 | local hostgroup=$1 126 | local query="" 127 | local data 128 | 129 | HOSTS=() 130 | PORTS=() 131 | STATUS=() 132 | COMMENTS=() 133 | HOSTGROUPS=() 134 | WEIGHTS=() 135 | MAX_CONNECTIONS=() 136 | 137 | if [[ -n $hostgroup ]]; then 138 | if [[ $hostgroup =~ ',' ]]; then 139 | query+="hostgroup_id IN ($hostgroup)" 140 | else 141 | query+="hostgroup_id=$hostgroup" 142 | fi 143 | fi 144 | 145 | data=$(proxysql_exec "SELECT hostname,port,status,comment,hostgroup_id,weight,max_connections 146 | FROM runtime_mysql_servers 147 | WHERE $query and STATUS != 'SHUNNED' 148 | ORDER BY status,hostname,port,hostgroup_id") 149 | local rc=$? 150 | 151 | if [[ $rc -ne 0 ]]; then 152 | return $rc 153 | fi 154 | 155 | while read line; do 156 | HOSTS+=($(echo -e "$line" | cut -f1)) 157 | PORTS+=($(echo -e "$line" | cut -f2)) 158 | STATUS+=($(echo -e "$line" | cut -f3)) 159 | COMMENTS+=($(echo -e "$line" | cut -f4)) 160 | HOSTGROUPS+=($(echo -e "$line" | cut -f5)) 161 | WEIGHTS+=($(echo -e "$line" | cut -f6)) 162 | MAX_CONNECTIONS+=($(echo -e "$line" | cut -f7)) 163 | done< <(printf "%s\n" "$data") 164 | } 165 | 166 | function retrieve_reader_info() { 167 | get_node_data $READER_HOSTGROUP_ID 168 | read_host=("${HOSTS[@]}") 169 | read_port=("${PORTS[@]}") 170 | read_status=("${STATUS[@]}") 171 | read_comment=("${COMMENTS[@]}") 172 | read_hostgroup=("${HOSTGROUPS[@]}") 173 | read_weight=("${WEIGHTS[@]}") 174 | read_max_connections=("${MAX_CONNECTIONS[@]}") 175 | } 176 | 177 | function retrieve_writer_info() { 178 | local hg_id=$1 179 | get_node_data $hg_id 180 | write_host=("${HOSTS[@]}") 181 | write_port=("${PORTS[@]}") 182 | write_status=("${STATUS[@]}") 183 | write_comment=("${COMMENTS[@]}") 184 | write_hostgroup=("${HOSTGROUPS[@]}") 185 | write_weight=("${WEIGHTS[@]}") 186 | write_max_connections=("${MAX_CONNECTIONS[@]}") 187 | } 188 | 189 | 190 | function retrieve_slave_info() { 191 | local data="" 192 | 193 | local HOSTGROUPS=() 194 | local HOSTS=() 195 | local PORTS=() 196 | local STATUS=() 197 | local COMMENTS=() 198 | 199 | data=$(proxysql_exec "SELECT 200 | hostgroup_id,hostname,port,status,comment 201 | FROM runtime_mysql_servers 202 | WHERE hostgroup_id in ($ALL_HOSTGROUPS) 203 | AND comment = 'SLAVEREAD' 204 | ORDER BY hostgroup_id,hostname,port,status") 205 | local rc=$? 206 | if [[ $rc -ne 0 ]]; then 207 | return $rc 208 | fi 209 | 210 | while read line; do 211 | HOSTGROUPS+=($(echo -e "$line" | cut -f1)) 212 | HOSTS+=($(echo -e "$line" | cut -f2)) 213 | PORTS+=($(echo -e "$line" | cut -f3)) 214 | STATUS+=($(echo -e "$line" | cut -f4)) 215 | COMMENTS+=($(echo -e "$line" | cut -f5)) 216 | done< <(printf "$data\n") 217 | 218 | slave_hostgroup=("${HOSTGROUPS[@]}") 219 | slave_host=("${HOSTS[@]}") 220 | slave_port=("${PORTS[@]}") 221 | slave_status=("${STATUS[@]}") 222 | slave_comment=("${COMMENTS[@]}") 223 | } 224 | 225 | # Queries the node for it's slave status and returns the 226 | # data in a a string with four fields: 227 | # master_host:slave_io_running:slave_sql_running:seconds_behind_master 228 | # This is to get around the lack of associative array support in 229 | # the distros. 230 | # 231 | # Globals: 232 | # slave_status 233 | # 234 | # Arguments: 235 | # 1: host address 236 | # 2: port 237 | # 238 | function retrieve_slavenode_status() { 239 | local host=$1 240 | local port=$2 241 | local status 242 | 243 | status=$(mysql_exec "${host}" "${port}" 'SHOW SLAVE STATUS\G' "--silent" | sed 's/ //g') 244 | 245 | result="" 246 | result+=$(echo "$status" | grep "^Master_Host:" | cut -d: -f2-) 247 | result+="\t" 248 | result+=$(echo "$status" | grep "^Slave_IO_Running:" | cut -d: -f2) 249 | result+="\t" 250 | result+=$(echo "$status" | grep "^Slave_SQL_Running:" | cut -d: -f2) 251 | result+="\t" 252 | result+=$(echo "$status" | grep "^Seconds_Behind_Master:" | cut -d: -f2) 253 | 254 | echo "$result" 255 | } 256 | 257 | 258 | function wait_for_server_start() { 259 | local socket=$1 260 | local cluster_size=$2 261 | 262 | for X in $( seq 0 30 ); do 263 | sleep 1 264 | if ${PXC_BASEDIR}/bin/mysqladmin -uroot -S${socket} ping > /dev/null 2>&1; then 265 | # Check the WSREP_READY status 266 | ready_status=$(${PXC_BASEDIR}/bin/mysql -uroot -$${socket} -Ns -e "show status like 'wsrep_ready'") 267 | if [[ -n $ready_status ]]; then 268 | ready_status=$(echo "$ready_status" | awk '{ print $2 }') 269 | if [[ $ready_status == "ON" ]]; then 270 | size=$(${PXC_BASEDIR}/bin/mysql -uroot -$${socket} -Ns -e "show status like 'wsrep_cluster_size'") 271 | if [[ -n $size ]]; then 272 | size=$(echo "$size" | awk '{ print $2 }') 273 | if [[ $size -eq $cluster_size ]]; then 274 | break 275 | fi 276 | fi 277 | fi 278 | fi 279 | 280 | fi 281 | done 282 | } 283 | 284 | function wait_for_server_shutdown() { 285 | local socket=$1 286 | local cluster_size=$2 287 | 288 | for X in $( seq 0 30 ); do 289 | sleep 1 290 | if ! ${PXC_BASEDIR}/bin/mysqladmin -uroot -S${socket} ping > /dev/null 2>&1; then 291 | return 0 292 | fi 293 | done 294 | return 1 295 | } 296 | 297 | 298 | 299 | function restart_server() { 300 | local restart_cmd=$1 301 | local restart_user=$2 302 | local bootstrap="" 303 | local pxc_datadir 304 | local options="" 305 | 306 | if [[ $# -ge 3 ]]; then 307 | bootstrap=$3 308 | fi 309 | 310 | local new_start_cmd=$restart_cmd 311 | 312 | if [[ $bootstrap == "bootstrap" ]]; then 313 | if echo "$restart_cmd" | grep -q -v "\-\-wsrep-new-cluster"; then 314 | # There is no --wsrep-new-cluster, so add it to the command line 315 | options+=" --wsrep-new-cluster " 316 | fi 317 | 318 | # Extract the datadir 319 | pxc_datadir=$(echo $restart_cmd | grep -o "\-\-datadir=[^ ]*") 320 | pxc_datadir=$(echo "$pxc_datadir" | cut -d'=' -f2) 321 | 322 | # Update the grastate.dat file to ensure that the node can bootstrap 323 | # Ensure that safe_to_bootstrap is set to 1 324 | sed -i "s/^safe_to_bootstrap:[ \t]0/safe_to_bootstrap: 1/" "${pxc_datadir}/grastate.dat" 325 | else 326 | new_start_cmd=$(echo "$new_start_cmd" | sed "s/\-\-wsrep-new-cluster//g") 327 | fi 328 | 329 | if [[ ! $new_start_cmd =~ --user=$restart_user ]]; then 330 | options+="--user=$restart_user" 331 | fi 332 | nohup $new_start_cmd $options 3>&- &>/dev/null & 333 | } 334 | 335 | function dump_nodes() { 336 | local lineno=$1 337 | local msg=$2 338 | echo "$lineno Dumping server info : $msg" >&2 339 | proxysql_exec "SELECT 340 | hostgroup_id,hostname,port,status,comment,weight 341 | FROM mysql_servers 342 | WHERE hostgroup_id IN ($ALL_HOSTGROUPS) 343 | ORDER BY hostgroup_id,status,hostname,port" >&2 344 | echo "" >&2 345 | } 346 | 347 | function dump_runtime_nodes() { 348 | local lineno=$1 349 | local msg=$2 350 | echo "$lineno Dumping runtime server info : $msg" >&2 351 | proxysql_exec "SELECT hostgroup_id,hostname,port,status,comment,weight 352 | FROM runtime_mysql_servers 353 | WHERE hostgroup_id IN ($ALL_HOSTGROUPS) 354 | ORDER BY hostgroup_id,status,hostname,port" >&2 355 | echo "" >&2 356 | } 357 | 358 | function require_pxc_maint_mode() { 359 | if [[ $MYSQL_VERSION =~ ^5.5 || $MYSQL_VERSION =~ ^5.6 ]]; then 360 | skip "requires pxc_maint_mode" 361 | fi 362 | } 363 | 364 | 365 | 366 | # Returns the version string in a standardized format 367 | # Input "1.2.3" => echoes "010203" 368 | # Wrongly formatted values => echoes "000000" 369 | # 370 | # Globals: 371 | # None 372 | # 373 | # Arguments: 374 | # Parameter 1: a version string 375 | # like "5.1.12" 376 | # anything after the major.minor.revision is ignored 377 | # Outputs: 378 | # A string that can be used directly with string comparisons. 379 | # So, the string "5.1.12" is transformed into "050112" 380 | # Note that individual version numbers can only go up to 99. 381 | # 382 | function normalize_version() 383 | { 384 | local major=0 385 | local minor=0 386 | local patch=0 387 | 388 | # Only parses purely numeric version numbers, 1.2.3 389 | # Everything after the first three values are ignored 390 | if [[ $1 =~ ^([0-9]+)\.([0-9]+)\.?([0-9]*)([^ ])* ]]; then 391 | major=${BASH_REMATCH[1]} 392 | minor=${BASH_REMATCH[2]} 393 | patch=${BASH_REMATCH[3]} 394 | fi 395 | 396 | printf %02d%02d%02d $major $minor $patch 397 | } 398 | 399 | 400 | # Compares two version strings 401 | # The version strings passed in will be normalized to a 402 | # string-comparable version. 403 | # 404 | # Globals: 405 | # None 406 | # 407 | # Arguments: 408 | # Parameter 1: The left-side of the comparison (for example: "5.7.25") 409 | # Parameter 2: the comparison operation 410 | # '>', '>=', '=', '==', '<', '<=', "!=" 411 | # Parameter 3: The right-side of the comparison (for example: "5.7.24") 412 | # 413 | # Returns: 414 | # Returns 0 (success) if param1 op param2 415 | # Returns 1 (failure) otherwise 416 | # 417 | function compare_versions() 418 | { 419 | local version_1="$1" 420 | local op=$2 421 | local version_2="$3" 422 | 423 | if [[ -z $version_1 || -z $version_2 ]]; then 424 | echo "$LINENO : Missing version string in comparison" >&2 425 | echo -e "-- left-side:$version_1 operation:$op right-side:$version_2" >&2 426 | return 1 427 | fi 428 | 429 | version_1="$( normalize_version "$version_1" )" 430 | version_2="$( normalize_version "$version_2" )" 431 | 432 | if [[ ! " = == > >= < <= != " =~ " $op " ]]; then 433 | echo "$LINENO : Unknown operation : $op" >&2 434 | echo -e "-- Must be one of : = == > >= < <=" >&2 435 | return 1 436 | fi 437 | 438 | [[ $op == "<" && $version_1 < $version_2 ]] && return 0 439 | [[ $op == "<=" && ! $version_1 > $version_2 ]] && return 0 440 | [[ $op == "=" && $version_1 == $version_2 ]] && return 0 441 | [[ $op == "==" && $version_1 == $version_2 ]] && return 0 442 | [[ $op == ">" && $version_1 > $version_2 ]] && return 0 443 | [[ $op == ">=" && ! $version_1 < $version_2 ]] && return 0 444 | [[ $op == "!=" && $version_1 != $version_2 ]] && return 0 445 | 446 | return 1 447 | } 448 | 449 | 450 | 451 | # Looks for a version of OpenSSL 1.1.1 452 | # This could be openssl, openssl11, or the proxysql-admin-openssl binary. 453 | # 454 | # Globals: 455 | # REQUIRED_OPENSSL_VERSION 456 | # PROXYSQL_ADMIN_OPENSSL_NAME 457 | # 458 | # Arguments: 459 | # Parameter 1: the lineno where this function was called 460 | # 461 | # Returns 0 if a binary was found (with version 1.1.1+) 462 | # and writes the path to the binary to stdout 463 | # Returns 1 otherwise (and prints out its own error message) 464 | # 465 | function find_openssl_binary() 466 | { 467 | local lineno=$1 468 | local path_to_openssl 469 | local openssl_executable="" 470 | local value 471 | local openssl_version 472 | 473 | # Check for the proper version of the executable 474 | path_to_openssl=$(which openssl 2> /dev/null) 475 | if [[ $? -eq 0 && -n ${path_to_openssl} && -e ${path_to_openssl} ]]; then 476 | 477 | # We found a possible binary, check the version 478 | value=$(${path_to_openssl} version) 479 | openssl_version=$(expr match "$value" '.*[ \t]\+\([0-9]\+\.[0-9]\+\.[0-9]\+\)[^0-9].*') 480 | 481 | # Extract the version from version string 482 | if compare_versions "${openssl_version}" ">=" "$REQUIRED_OPENSSL_VERSION"; then 483 | openssl_executable=${path_to_openssl} 484 | fi 485 | fi 486 | 487 | # If we haven't found an acceptable openssl, look for openssl11 488 | if [[ -z $openssl_executable ]]; then 489 | # Check for openssl 1.1 executable (if installed alongside 1.0) 490 | openssl_executable=$(which openssl11 2> /dev/null) 491 | fi 492 | 493 | # If we haven't found openssl/openssl11 look for our own binary 494 | if [[ -z $openssl_executable ]]; then 495 | openssl_executable=$(which "${WORKDIR}/${PROXYSQL_ADMIN_OPENSSL_NAME}" 2> /dev/null) 496 | fi 497 | 498 | if [[ -z $openssl_executable ]]; then 499 | echo -e "$LINENO : Could not find a v${REQUIRED_OPENSSL_VERSION}+ OpenSSL executable in the path." \ 500 | "\n-- Please check that OpenSSL v${REQUIRED_OPENSSL_VERSION} or greater is installed and in the path." >&2 501 | return 1 502 | fi 503 | 504 | # Verify the openssl versions 505 | value=$(${openssl_executable} version) 506 | 507 | # Extract the version from version string 508 | openssl_version=$(expr match "$value" '.*[ \t]\+\([0-9]\+\.[0-9]\+\.[0-9]\+\)[^0-9].*') 509 | 510 | if compare_versions "$openssl_version" "<" "$REQUIRED_OPENSSL_VERSION"; then 511 | echo -e "$LINENO : Could not find OpenSSL with the required version. required:${REQUIRED_OPENSSL_VERSION} found:${openssl_version}" \ 512 | "\n-- Please check that OpenSSL v${REQUIRED_OPENSSL_VERSION} or greater is installed and in the path." >& 2 513 | return 1 514 | fi 515 | 516 | echo "$LINENO : Found openssl executable:${openssl_executable} ${openssl_version}" >&2 517 | 518 | printf "%s" "${openssl_executable}" 519 | return 0 520 | } 521 | -------------------------------------------------------------------------------- /tests/testsuite.toml: -------------------------------------------------------------------------------- 1 | 2 | [pxccluster] 3 | activeFailover = 1 4 | failBack = false 5 | checkTimeOut = 2000 6 | # debug = 1 //Deprecated: this is redundant and not in use 7 | mainSegment = 0 8 | sslClient = "client-cert.pem" 9 | sslKey = "client-key.pem" 10 | sslCa = "ca.pem" 11 | sslCertificatePath = "/opt/cert/ssl_test" 12 | hgW = 100 13 | hgR = 101 14 | configHgRange =8000 15 | maintenanceHgRange =9000 16 | #bckHgW = 8100 #deprecated 17 | #bckHgR = 8101 #deprecated 18 | 19 | # -------------------------------- 20 | # Set to true if there is a single writer node. If this is set, 21 | # then maxNumWriters is assumed to be 1. 22 | # 23 | # Allowable values: true,false 24 | # Default: true 25 | # 26 | singlePrimary = true 27 | 28 | # -------------------------------- 29 | # Set to the number of writer nodes desired. 30 | # 31 | # The value of this is assumed to be 1 if singlePrimary is true. 32 | # 33 | # If this is set to a value from 1 to 100, then the query rules 34 | # are setup for a distinct writer hostgroup (writes are sent to the 35 | # writer hostgroup and read are sent to the reader hostgroup). 36 | # 37 | # If this is set to a value > 100, then all queries (writes and reads) 38 | # are sent to the writer hostgroup. This is assumed to be a 39 | # load-balancing scenario, where all nodes are equivalent and accept 40 | # both reads and writes. 41 | # 42 | # Default: (none) 43 | # 44 | 45 | maxNumWriters = 1 46 | 47 | writerIsAlsoReader = 1 48 | 49 | retryUp = 0 50 | retryDown = 2 51 | clusterId = 10 52 | persistPrimarySettings=0 #0 disable| 1 only persist Write settings | 2 persist Read and Write settings 53 | 54 | # == proxysql =================================================== 55 | # The proxysql section is for ProxySQL-specific information. 56 | # 57 | # These settings will be read and used whenever the scheduler is run. 58 | # 59 | [proxysql] 60 | port = 6032 61 | host = "127.0.0.1" 62 | user = "admin" 63 | password = "admin" 64 | clustered = false 65 | respectManualOfflineSoft=false 66 | lockfilepath = "/tmp/" 67 | 68 | #== global ====================================================== 69 | # The global section are for variables that are not ProxySQL or 70 | # cluster specific. 71 | # 72 | # These settings will be read and used whenever the scheduler is run. 73 | # 74 | [global] 75 | debug = true 76 | 77 | #?? Should we just have logFile, what advantage does logTarget have? 78 | logLevel = "debug" 79 | logTarget = "file" #stdout | file 80 | logFile = "/tmp/pscheduler.log" 81 | 82 | #?? Should we use the development for these two 83 | daemonize = false 84 | daemonInterval = 2000 85 | performance = true 86 | 87 | # Not used currently 88 | OS = "na" 89 | 90 | #?? Make common lockfileTimeout -> lockFileTimeout 91 | lockfiletimeout = 60 #seconds 92 | lockclustertimeout = 600 #120 # seconds 93 | 94 | 95 | #== setup ======================================================= 96 | # These variables are used only upon Setup 97 | # Changing these variables after setup will not affect operation 98 | # 99 | [setup] 100 | 101 | # -------------------------------- 102 | # The clusterAppUser is the ProxySQL user account that should be 103 | # used by clients to access the cluster. 104 | # 105 | # Uncomment the following options (clusterAppUser and clusterAppUserPassword) 106 | # to enable the setting of the clusterAppUser for this cluster. 107 | # 108 | #clusterAppUser="proxysql_user" 109 | #clusterAppUserPassword="passw0rd" 110 | 111 | # -------------------------------- 112 | # The monitorUser is used by ProxySQL to access the servers and 113 | # check the connections. 114 | # 115 | monitorUser="monitor" 116 | monitorUserPassword="monitor" 117 | 118 | # -------------------------------- 119 | # The clusterXXX information is used to setup the cluster for 120 | # use by ProxySQL. 121 | # 122 | clusterHost="localhost" 123 | clusterPort=4110 124 | clusterUser="admin" 125 | clusterUserPassword="admin" 126 | 127 | # -------------------------------- 128 | # ProxySQL will use SSL to connect to the backend servers 129 | # 130 | useSSL=0 131 | 132 | # -------------------------------- 133 | # Max number of connections from ProxySQL to the backend servers. 134 | # 135 | maxConnections=1000 136 | 137 | nodeCheckInterval=2000 138 | -------------------------------------------------------------------------------- /tools/enable_scheduler: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | # Turn off shell globbing (because we do SQL querying below) 4 | set -o noglob 5 | 6 | function usage() { 7 | local path=$0 8 | cat << EOF 9 | Usage example: 10 | $ ${path##*/} [options] 11 | 12 | This tool is used to enable/disable the specified scheduler that matches the write-hg. 13 | 14 | Options: 15 | --write-hg= : the write hostgroup (specifies the scheduler to be enabled/disabled) 16 | --enable : activate the scheduler (default) 17 | --disable : deactivate the scheduler 18 | --user= : specify the user name 19 | --password= : specify the user password 20 | --host= : specify host address 21 | --port= : specify host port 22 | --query-options= : mysql client command line options, (example: "--silent -N") 23 | 24 | Examples: 25 | ${path##*/} --enable --write-hg=10 26 | 27 | The default is to use the ProxySQL credentials in /etc/proxysql-admin.cnf. 28 | 29 | EOF 30 | } 31 | 32 | # 33 | # Executes an SQL query 34 | # 35 | # Globals: 36 | # USER 37 | # PASSWORD 38 | # HOST 39 | # PORT 40 | # 41 | # Arguments: 42 | # 1: arguments to be passed to mysql 43 | # 2: the query 44 | # 45 | function exec_sql() { 46 | local args=$1 47 | local query=$2 48 | local retvalue 49 | local retoutput 50 | 51 | retoutput=$(printf "[client]\nuser=${USER}\npassword=\"${PASSWORD}\"\nhost=${HOST}\nport=${PORT}" \ 52 | | mysql --defaults-file=/dev/stdin --protocol=tcp \ 53 | ${args} -e "${query}") 54 | retvalue=$? 55 | 56 | printf "${retoutput//%/%%}" 57 | return $retvalue 58 | } 59 | 60 | 61 | declare USER="" 62 | declare PASSWORD="" 63 | declare HOST="" 64 | declare PORT="" 65 | declare WRITE_HG="" 66 | declare ENABLE=1 67 | 68 | function parse_args() { 69 | local param value 70 | local positional_params="" 71 | 72 | while [[ $# -gt 0 && "$1" != "" ]]; do 73 | param=`echo $1 | awk -F= '{print $1}'` 74 | value=`echo $1 | awk -F= '{print $2}'` 75 | 76 | # Assume that all options start with a '-' 77 | # otherwise treat as a positional parameter 78 | if [[ ! $param =~ ^- ]]; then 79 | positional_params+="$1 " 80 | shift 81 | continue 82 | fi 83 | case $param in 84 | -h | --help) 85 | usage 86 | exit 87 | ;; 88 | --user) 89 | USER=$value 90 | ;; 91 | --password) 92 | PASSWORD=$value 93 | ;; 94 | --host) 95 | HOST=$value 96 | ;; 97 | --port) 98 | PORT=$value 99 | ;; 100 | --write-hg) 101 | WRITE_HG=$value 102 | ;; 103 | --enable) 104 | ENABLE=1 105 | ;; 106 | --disable) 107 | ENABLE=0 108 | ;; 109 | *) 110 | echo "ERROR: unknown parameter \"$param\"" 111 | usage 112 | exit 1 113 | ;; 114 | esac 115 | shift 116 | done 117 | } 118 | 119 | 120 | if [[ -r /etc/proxysql-admin.cnf ]]; then 121 | source /etc/proxysql-admin.cnf 122 | USER=$PROXYSQL_USERNAME 123 | PASSWORD=$PROXYSQL_PASSWORD 124 | HOST=$PROXYSQL_HOSTNAME 125 | PORT=$PROXYSQL_PORT 126 | fi 127 | 128 | parse_args "$@" 129 | if [[ -z $WRITE_HG ]]; then 130 | echo "Error: Must specify a write hostgroup" 131 | exit 1 132 | fi 133 | 134 | exec_sql "" "UPDATE scheduler SET active=$ENABLE WHERE ARG1 LIKE '% --write-hg=$WRITE_HG %'; LOAD scheduler TO RUNTIME" 135 | if [[ $? -ne 0 ]]; then 136 | echo "scheduler update failed" 137 | exit 1 138 | fi 139 | 140 | value=$(exec_sql "-Ns" "SELECT active FROM scheduler WHERE ARG1 LIKE '% --write-hg=$WRITE_HG %'") 141 | echo " scheduler active value for write hostgroup $WRITE_HG : $value" 142 | value=$(exec_sql "-Ns" "SELECT active FROM runtime_scheduler WHERE ARG1 LIKE '% --write-hg=$WRITE_HG %'") 143 | echo "runtime scheduler active value for write hostgroup $WRITE_HG : $value" 144 | -------------------------------------------------------------------------------- /tools/mysql_exec: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | # Turn off shell globbing (because we do SQL querying below) 4 | set -o noglob 5 | 6 | function usage() { 7 | local path=$0 8 | cat << EOF 9 | Usage example: 10 | $ ${path##*/} [options] 11 | 12 | This tool executes a query on the PXC cluster. 13 | 14 | Options: 15 | --user= : specify the user name (default:root) 16 | --password= : specify the user password 17 | --host= : specify host address 18 | --port= : specify host port 19 | --socket= : path to the socket file (default:/tmp/cluster_one1.sock) 20 | --query-options= : mysql client command line options, (example: "--silent -N") 21 | 22 | If the host and port are specified, the socket file is ignored. 23 | 24 | Examples: 25 | ${path##*/} --query-options="--table" "select * from table_name" 26 | 27 | The default is to connect using the PXC cluster sockets utilizing the default 28 | PXC root user credentials. 29 | 30 | EOF 31 | } 32 | 33 | # 34 | # Executes an SQL query 35 | # 36 | # Globals: 37 | # USER 38 | # PASSWORD 39 | # HOST 40 | # PORT 41 | # 42 | # Arguments: 43 | # 1: arguments to be passed to mysql 44 | # 2: the query 45 | # 46 | function exec_sql() { 47 | local args=$1 48 | local query=$2 49 | local retvalue 50 | local retoutput 51 | 52 | if [[ -n $HOST && -n $PORT ]]; then 53 | retoutput=$(printf "[client]\nuser=${USER}\npassword=\"${PASSWORD}\"\nhost=${HOST}\nport=${PORT}" \ 54 | | mysql --defaults-file=/dev/stdin --protocol=tcp \ 55 | ${args} -e "${query}") 56 | elif [[ -n $SOCKET ]]; then 57 | if [[ ! -e $SOCKET ]]; then 58 | echo "Error: Could not find socket file: $SOCKET" 59 | exit 1 60 | fi 61 | retoutput=$(printf "[client]\nuser=${USER}\npassword=\"${PASSWORD}\"\n" \ 62 | | mysql --defaults-file=/dev/stdin --socket=$SOCKET --user=$USER \ 63 | ${args} -e "${query}") 64 | else 65 | echo "Error: requires a socket or host/port to be specified" 66 | exit 1 67 | fi 68 | retvalue=$? 69 | 70 | printf "${retoutput//%/%%}" 71 | return $retvalue 72 | } 73 | 74 | 75 | declare USER="root" 76 | declare PASSWORD="" 77 | declare HOST="" 78 | declare PORT="" 79 | declare QUERY_OPTIONS="" 80 | declare QUERY="" 81 | declare SOCKET="/tmp/cluster_one1.sock" 82 | 83 | function parse_args() { 84 | local param value 85 | local positional_params="" 86 | 87 | while [[ $# -gt 0 && "$1" != "" ]]; do 88 | param=`echo $1 | awk -F= '{print $1}'` 89 | value=`echo $1 | awk -F= '{print $2}'` 90 | 91 | # Assume that all options start with a '-' 92 | # otherwise treat as a positional parameter 93 | if [[ ! $param =~ ^- ]]; then 94 | positional_params+="$1 " 95 | shift 96 | continue 97 | fi 98 | case $param in 99 | -h | --help) 100 | usage 101 | exit 102 | ;; 103 | --user) 104 | USER=$value 105 | ;; 106 | --password) 107 | PASSWORD=$value 108 | ;; 109 | --host) 110 | HOST=$value 111 | ;; 112 | --port) 113 | PORT=$value 114 | ;; 115 | --socket) 116 | SOCKET=$value 117 | ;; 118 | --query-options) 119 | QUERY_OPTIONS=$value 120 | ;; 121 | *) 122 | echo "ERROR: unknown parameter \"$param\"" 123 | usage 124 | exit 1 125 | ;; 126 | esac 127 | shift 128 | done 129 | 130 | # handle positional parameters (we only expect one) 131 | QUERY=$positional_params 132 | } 133 | 134 | parse_args "$@" 135 | 136 | if [[ -z $QUERY ]]; then 137 | echo "Error, no query specified (nothing to run)" 138 | exit 1 139 | fi 140 | 141 | exec_sql "$QUERY_OPTIONS" "$QUERY" 142 | -------------------------------------------------------------------------------- /tools/proxysql_exec: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | # Turn off shell globbing (because we do SQL querying below) 4 | set -o noglob 5 | 6 | function usage() { 7 | local path=$0 8 | cat << EOF 9 | Usage example: 10 | $ ${path##*/} [options] 11 | 12 | This tool executes a query on ProxySQL. 13 | 14 | Options: 15 | --user= : specify the user name 16 | --password= : specify the user password 17 | --host= : specify host address 18 | --port= : specify host port 19 | --query-options= : mysql client command line options, (example: "--silent -N") 20 | 21 | Examples: 22 | ${path##*/} --query-options="--table" "select * from mysql_servers" 23 | 24 | The default is to use the ProxySQL credentials in /etc/proxysql-admin.cnf. 25 | 26 | EOF 27 | } 28 | 29 | # 30 | # Executes an SQL query 31 | # 32 | # Globals: 33 | # USER 34 | # PASSWORD 35 | # HOST 36 | # PORT 37 | # 38 | # Arguments: 39 | # 1: arguments to be passed to mysql 40 | # 2: the query 41 | # 42 | function exec_sql() { 43 | local args=$1 44 | local query=$2 45 | local retvalue 46 | local retoutput 47 | 48 | retoutput=$(printf "[client]\nuser=${USER}\npassword=\"${PASSWORD}\"\nhost=${HOST}\nport=${PORT}" \ 49 | | mysql --defaults-file=/dev/stdin --protocol=tcp \ 50 | ${args} -e "${query}") 51 | retvalue=$? 52 | 53 | printf "${retoutput//%/%%}" 54 | return $retvalue 55 | } 56 | 57 | 58 | declare USER="" 59 | declare PASSWORD="" 60 | declare HOST="" 61 | declare PORT="" 62 | declare QUERY_OPTIONS="" 63 | declare QUERY="" 64 | 65 | function parse_args() { 66 | local param value 67 | local positional_params="" 68 | 69 | while [[ $# -gt 0 && "$1" != "" ]]; do 70 | param=`echo $1 | awk -F= '{print $1}'` 71 | value=`echo $1 | awk -F= '{print $2}'` 72 | 73 | # Assume that all options start with a '-' 74 | # otherwise treat as a positional parameter 75 | if [[ ! $param =~ ^- ]]; then 76 | positional_params+="$1 " 77 | shift 78 | continue 79 | fi 80 | case $param in 81 | -h | --help) 82 | usage 83 | exit 84 | ;; 85 | --user) 86 | USER=$value 87 | ;; 88 | --password) 89 | PASSWORD=$value 90 | ;; 91 | --host) 92 | HOST=$value 93 | ;; 94 | --port) 95 | PORT=$value 96 | ;; 97 | --query-options) 98 | QUERY_OPTIONS=$value 99 | ;; 100 | *) 101 | echo "ERROR: unknown parameter \"$param\"" 102 | usage 103 | exit 1 104 | ;; 105 | esac 106 | shift 107 | done 108 | 109 | # handle positional parameters (we only expect one) 110 | QUERY=$positional_params 111 | } 112 | 113 | 114 | if [[ -r /etc/proxysql-admin.cnf ]]; then 115 | source /etc/proxysql-admin.cnf 116 | USER=$PROXYSQL_USERNAME 117 | PASSWORD=$PROXYSQL_PASSWORD 118 | HOST=$PROXYSQL_HOSTNAME 119 | PORT=$PROXYSQL_PORT 120 | fi 121 | 122 | parse_args "$@" 123 | 124 | if [[ -z $QUERY ]]; then 125 | echo "Error, no query specified (nothing to run)" 126 | exit 1 127 | fi 128 | 129 | echo "Query : $QUERY" 130 | exec_sql "$QUERY_OPTIONS" "$QUERY" 131 | echo 132 | -------------------------------------------------------------------------------- /tools/run_galera_checker: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | # Turn off shell globbing (because we do SQL querying below) 4 | set -o noglob 5 | 6 | function usage() { 7 | local path=$0 8 | cat << EOF 9 | Usage example: 10 | $ ${path##*/} [options] 11 | 12 | Options: 13 | --write-hg= : The scheduler for this WRITE hostgroup 14 | will be invoked. 15 | --debug : Enable DEBUG 16 | --log= : Use this path for the log file instead 17 | of the one used by the scheduler. 18 | --node-monitor-log= : Sets the output of the node monitor 19 | to this path. 20 | 21 | The options below are used when querying ProxySQL. If unspecified, the 22 | values in /etc/proxysql-admin.cnf are used. 23 | --user= : specify the user name 24 | --password= : specify the user password 25 | --host= : specify host address 26 | --port= : specify host port 27 | 28 | EOF 29 | } 30 | 31 | # 32 | # Executes an SQL query 33 | # 34 | # Globals: 35 | # USER 36 | # PASSWORD 37 | # HOST 38 | # PORT 39 | # 40 | # Arguments: 41 | # 1: arguments to be passed to mysql 42 | # 2: the query 43 | # 44 | function exec_sql() { 45 | local args=$1 46 | local query=$2 47 | local retvalue 48 | local retoutput 49 | 50 | retoutput=$(printf "[client]\nuser=${USER}\npassword=\"${PASSWORD}\"\nhost=${HOST}\nport=${PORT}" \ 51 | | mysql --defaults-file=/dev/stdin --protocol=tcp \ 52 | ${args} -e "${query}") 53 | retvalue=$? 54 | 55 | printf "%s" "${retoutput//%/%%}" 56 | return $retvalue 57 | } 58 | 59 | 60 | declare DEBUG=0 61 | declare LOG_FILE="" 62 | declare NODE_MONITOR_LOG="" 63 | declare WRITE_HG="" 64 | 65 | declare USER="" 66 | declare PASSWORD="" 67 | declare HOST="" 68 | declare PORT="" 69 | 70 | function parse_args() { 71 | local param value 72 | local positional_params="" 73 | 74 | while [[ $# -gt 0 && "$1" != "" ]]; do 75 | param=`echo $1 | awk -F= '{print $1}'` 76 | value=`echo $1 | awk -F= '{print $2}'` 77 | 78 | # Assume that all options start with a '-' 79 | # otherwise treat as a positional parameter 80 | if [[ ! $param =~ ^- ]]; then 81 | positional_params+="$1 " 82 | shift 83 | continue 84 | fi 85 | case $param in 86 | -h | --help) 87 | usage 88 | exit 89 | ;; 90 | --user) 91 | USER=$value 92 | ;; 93 | --password) 94 | PASSWORD=$value 95 | ;; 96 | --host) 97 | HOST=$value 98 | ;; 99 | --port) 100 | PORT=$value 101 | ;; 102 | --write-hg) 103 | if [[ -z $value ]]; then 104 | echo "Error: no value provided with --write-hg" 105 | exit 1 106 | fi 107 | WRITE_HG=$value 108 | ;; 109 | --debug) 110 | DEBUG=1 111 | ;; 112 | --log) 113 | if [[ -z $value ]]; then 114 | echo "Error: no value provided with --log" 115 | exit 1 116 | fi 117 | LOG_FILE=$value 118 | ;; 119 | --node-monitor-log ) 120 | NODE_MONITOR_LOG=$value 121 | if [[ -z $value ]]; then 122 | echo "Error: no value provided with --node-monitor-log" 123 | exit 1 124 | fi 125 | ;; 126 | *) 127 | echo "ERROR: unknown parameter \"$param\"" 128 | usage 129 | exit 1 130 | ;; 131 | esac 132 | shift 133 | done 134 | 135 | # handle positional parameters (we only expect one) 136 | QUERY=$positional_params 137 | } 138 | 139 | 140 | if [[ -r /etc/proxysql-admin.cnf ]]; then 141 | source /etc/proxysql-admin.cnf 142 | USER=$PROXYSQL_USERNAME 143 | PASSWORD=$PROXYSQL_PASSWORD 144 | HOST=$PROXYSQL_HOSTNAME 145 | PORT=$PROXYSQL_PORT 146 | fi 147 | 148 | parse_args "$@" 149 | 150 | if [[ -z $WRITE_HG ]]; then 151 | echo "Error: A WRITE hostgroup is required (--write-hg=)" 152 | exit 1 153 | fi 154 | 155 | # Retrieve the scheduler information for this write hostgroup 156 | scheduler_id=$(exec_sql "-Ns" "SELECT id FROM scheduler WHERE arg1 like '% --write-hg=$WRITE_HOSTGROUP_ID %' OR arg1 like '% -w $WRITE_HOSTGROUP_ID %'") 157 | if [[ -z $scheduler_id ]]; then 158 | echo "Error: Could not retrieve the scheduler for write hostgroup: $WRITE_HG" 159 | exit 1 160 | fi 161 | 162 | galera_checker=$(exec_sql "-Ns" "SELECT filename FROM scheduler WHERE id=$scheduler_id") 163 | galera_checker_args=$(exec_sql "-Ns" "SELECT arg1 FROM scheduler WHERE id=$scheduler_id") 164 | if [[ -z $galera_checker || -z $galera_checker_args ]]; then 165 | echo "Could not retrieve the galera checker information" 166 | exit 1 167 | fi 168 | 169 | if [[ $DEBUG -ne 0 ]]; then 170 | if [[ ! " $galera_checker_args " =~ [[:space:]]--debug[[:space:]] ]]; then 171 | galera_checker_args+=" --debug" 172 | fi 173 | fi 174 | 175 | if [[ -n $LOG_FILE ]]; then 176 | # Replace the existing log file option 177 | # Remove any existing --log=XXX option 178 | galera_checker_args=$(echo "$galera_checker_args" | sed 's/--log=[^ ]*//g') 179 | 180 | # Append on the --log option 181 | galera_checker_args+=" --log=$LOG_FILE" 182 | fi 183 | 184 | if [[ -n $NODE_MONITOR_LOG ]]; then 185 | galera_checker_args=$(echo "$galera_checker_args" | sed 's/--node-monitor-log=[^ ]*//g') 186 | 187 | # Append on the --node-monitor-log option 188 | galera_checker_args+=" --node-monitor-log=$NODE_MONITOR_LOG" 189 | fi 190 | 191 | # TODO: kennt, should support --log-text also 192 | 193 | # Execute the galera checker statement 194 | ${galera_checker} "${galera_checker_args}" 195 | --------------------------------------------------------------------------------