diff --git a/modules/augeas/README b/modules/augeas/README deleted file mode 100644 index e68703a..0000000 --- a/modules/augeas/README +++ /dev/null @@ -1,17 +0,0 @@ -= Augeas Puppet module = - -== Usage == - - include augeas - -== Description == - -This module does 3 things: - - o provides a few lenses only included in recent augeas releases. - - o creates /usr/share/augeas/lenses/contrib in which other modules can put - custom lenses (see puppet-mysql/puppet-haproxy for working examples). - - o let's you force the version by defining $augeas_version, else puppet will - only ensure the packages are present. diff --git a/modules/augeas/lib/facter/augeasversion.rb b/modules/augeas/lib/facter/augeasversion.rb deleted file mode 100644 index 5ab8c8f..0000000 --- a/modules/augeas/lib/facter/augeasversion.rb +++ /dev/null @@ -1,19 +0,0 @@ -Facter.add("augeasversion") do - setcode do - begin - require 'augeas' - if Augeas.const_defined?("NO_LOAD") && Augeas.const_defined?("NO_STDINC") - aug = Augeas::open('', '', Augeas::NO_LOAD & Augeas::NO_STDINC) - elsif Augeas.const_defined?("NONE") - aug = Augeas::open('', '', Augeas::NONE) - else - nil - end - - aug.get('/augeas/version') || nil - - rescue LoadError # don't fail if augeas isn't installed - nil - end - end -end diff --git a/modules/augeas/manifests/classes/augeas.pp b/modules/augeas/manifests/classes/augeas.pp deleted file mode 100644 index 85a9046..0000000 --- a/modules/augeas/manifests/classes/augeas.pp +++ /dev/null @@ -1,48 +0,0 @@ -class augeas { - - if ( ! $augeas_version ) { - $augeas_version = "present" - } - - case $operatingsystem { - /RedHat|CentOS|Fedora/: { include augeas::redhat } - /Debian|Ubuntu|kFreeBSD/: { include augeas::debian } - default: { include augeas::base } - } -} - -class augeas::base { - - # ensure no file not managed by puppet ends up in there. - file { "/usr/share/augeas/lenses/contrib": - ensure => directory, - recurse => true, - purge => true, - force => true, - mode => 0644, - owner => "root", - group => "root", - } -} - -class augeas::redhat inherits augeas::base { - - package { - ["augeas", "augeas-libs"]: - ensure => $augeas_version, - before => File["/usr/share/augeas/lenses/contrib"], - } - package { "ruby-augeas": ensure => present } - -} - -class augeas::debian inherits augeas::base { - - package { - ["augeas-lenses", "libaugeas0", "augeas-tools"]: - ensure => $augeas_version, - before => File["/usr/share/augeas/lenses/contrib"], - } - package { "libaugeas-ruby1.8": ensure => present } - -} diff --git a/modules/augeas/manifests/init.pp b/modules/augeas/manifests/init.pp deleted file mode 100644 index b33bf58..0000000 --- a/modules/augeas/manifests/init.pp +++ /dev/null @@ -1 +0,0 @@ -import "classes/*.pp" diff --git a/modules/dbserver/manifests/init.pp b/modules/dbserver/manifests/init.pp index 9293c33..894e1d5 100644 --- a/modules/dbserver/manifests/init.pp +++ b/modules/dbserver/manifests/init.pp @@ -1,40 +1,14 @@ class dbserver { -# only required for 10.04 -- need to drop support for it. -# include mysql::server - case $db_server_type { - 'medium': { include mysql::server::medium } - 'large': { include mysql::server::large } - 'huge': { include mysql::server::huge } - default: { include mysql::server::medium } + class { 'mysql::server': + config_hash => { 'root_password' => 'foo' } } - #puppet clients 2.7.14 or later don't need custom augeas lens - #older versions do, hence this case statement - case $puppetversion { - "2.7.14":{ - mysql::config { - 'bind-address': - value => $db_server, - notify => Service["mysql"] + mysql::server::config { 'testfile': + settings => { + 'mysqld' => { + 'bind-address' => db_server, } } - default: { - file { "/usr/share/augeas/lenses/contrib/mysql.aug": - ensure => present, - source => "puppet:///dbserver/mysql.aug", - } - - augeas { "my.cnf/mysqld-spree": - context => "${mysql::params::mycnfctx}/mysqld/", - load_path => "/usr/share/augeas/lenses/contrib/", - changes => [ - "set bind-address ${db_server}", - ], - require => [ File["/etc/mysql/my.cnf"], File["${mysql::params::data_dir}"] ], - notify => Service["mysql"], - } - - } } case $db_pass { @@ -43,50 +17,15 @@ } } - # nested defines needed to get permissions - # set correctly for each app and ip pair - # $app_name is an array, so is $app_server_ips - # - define mysql-user(){ - mysql-user-rights{$app_name: - ip => $name - } - } - define mysql-user-rights($ip){ - if $deploy_demo { - mysql::rights{"demo-${name}-rights-${ip}": - ensure => present, - database => $name, - user => "spree", - host => $db_server ? { - '127.0.0.1' => 'localhost', - default => $name - }, - password => $db_pass, - notify => Exec['reset database for demo'], - require => Mysql::Database["${name}"] - } - }else{ - mysql::rights{"${name}-rights-${ip}": - ensure => present, - database => $name, - user => "spree", - host => $db_server ? { - '127.0.0.1' => 'localhost', - default => $name - }, - password => $db_pass, - require => Mysql::Database["${name}"] - } - } - } - - mysql-user{$app_server_ips:} - - mysql::database{$app_name: - ensure => present + define db-for-app(){ + mysql::db { "${name}": + user => $name, + password => $db_pass, + host => $app_server_ips, + } } + db-for-app($app_name:) # only spree app can get demo deployed, # so the name is hardcoded on purpose diff --git a/modules/mysql/.fixtures.yml b/modules/mysql/.fixtures.yml new file mode 100644 index 0000000..cecf6f5 --- /dev/null +++ b/modules/mysql/.fixtures.yml @@ -0,0 +1,5 @@ +fixtures: + repositories: + "stdlib": "git://github.com/puppetlabs/puppetlabs-stdlib" + symlinks: + "mysql": "#{source_dir}" diff --git a/modules/mysql/.gemfile b/modules/mysql/.gemfile new file mode 100644 index 0000000..9aad840 --- /dev/null +++ b/modules/mysql/.gemfile @@ -0,0 +1,5 @@ +source :rubygems + +puppetversion = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>= 2.7'] +gem 'puppet', puppetversion +gem 'puppetlabs_spec_helper', '>= 0.1.0' diff --git a/modules/mysql/.gitignore b/modules/mysql/.gitignore new file mode 100644 index 0000000..444fe1a --- /dev/null +++ b/modules/mysql/.gitignore @@ -0,0 +1,2 @@ +*.swp +pkg/ diff --git a/modules/mysql/.travis.yml b/modules/mysql/.travis.yml new file mode 100644 index 0000000..066317e --- /dev/null +++ b/modules/mysql/.travis.yml @@ -0,0 +1,17 @@ +language: ruby +rvm: + - 1.8.7 +before_script: + - "[ '2.6.9' = $PUPPET_VERSION ] && git clone git://github.com/puppetlabs/puppetlabs-create_resources.git spec/fixtures/modules/create_resources || true" +after_script: +script: "rake spec" +branches: + only: + - master +env: + - PUPPET_VERSION=2.7.13 + - PUPPET_VERSION=2.7.6 + - PUPPET_VERSION=2.6.9 +notifications: + email: false +gemfile: .gemfile diff --git a/modules/mysql/CHANGELOG b/modules/mysql/CHANGELOG new file mode 100644 index 0000000..05bca75 --- /dev/null +++ b/modules/mysql/CHANGELOG @@ -0,0 +1,128 @@ +2012-08-23 - Version 0.5.0 +* Add puppetlabs/stdlib as requirement +* Add validation for mysql privs in provider +* Add `pidfile` parameter to mysql::config +* Add `ensure` parameter to mysql::db +* Add Amazon linux support +* Change `bind_address` parameter to be optional in my.cnf template +* Fix quoting root passwords + +2012-07-24 - Version 0.4.0 +* Fix various bugs regarding database names +* FreeBSD support +* Allow specifying the storage engine +* Add a backup class +* Add a security class to purge default accounts + +2012-05-03 - Version 0.3.0 +* #14218 Query the database for available privileges +* Add mysql::java class for java connector installation +* Use correct error log location on different distros +* Fix set_mysql_rootpw to properly depend on my.cnf + +2012-04-11 - Version 0.2.0 + +2012-03-19 - William Van Hevelingen +* (#13203) Add ssl support (f7e0ea5) + +2012-03-18 - Nan Liu +* Travis ci before script needs success exit code. (0ea463b) + +2012-03-18 - Nan Liu +* Fix Puppet 2.6 compilation issues. (9ebbbc4) + +2012-03-16 - Nan Liu +* Add travis.ci for testing multiple puppet versions. (33c72ef) + +2012-03-15 - William Van Hevelingen +* (#13163) Datadir should be configurable (f353fc6) + +2012-03-16 - Nan Liu +* Document create_resources dependency. (558a59c) + +2012-03-16 - Nan Liu +* Fix spec test issues related to error message. (eff79b5) + +2012-03-16 - Nan Liu +* Fix mysql service on Ubuntu. (72da2c5) + +2012-03-16 - Dan Bode +* Add more spec test coverage (55e399d) + +2012-03-16 - Nan Liu +* (#11963) Fix spec test due to path changes. (1700349) + +2012-03-07 - François Charlier +* Add a test to check path for 'mysqld-restart' (b14c7d1) + +2012-03-07 - François Charlier +* Fix path for 'mysqld-restart' (1a9ae6b) + +2012-03-15 - Dan Bode +* Add rspec-puppet tests for mysql::config (907331a) + +2012-03-15 - Dan Bode +* Moved class dependency between sever and config to server (da62ad6) + +2012-03-14 - Dan Bode +* Notify mysql restart from set_mysql_rootpw exec (0832a2c) + +2012-03-15 - Nan Liu +* Add documentation related to osfamily fact. (8265d28) + +2012-03-14 - Dan Bode +* Mention osfamily value in failure message (e472d3b) + +2012-03-14 - Dan Bode +* Fix bug when querying for all database users (015490c) + +2012-02-09 - Nan Liu +* Major refactor of mysql module. (b1f90fd) + +2012-01-11 - Justin Ellison +* Ruby and Python's MySQL libraries are named differently on different distros. (1e926b4) + +2012-01-11 - Justin Ellison +* Per @ghoneycutt, we should fail explicitly and explain why. (09af083) + +2012-01-11 - Justin Ellison +* Removing duplicate declaration (7513d03) + +2012-01-10 - Justin Ellison +* Use socket value from params class instead of hardcoding. (663e97c) + +2012-01-10 - Justin Ellison +* Instead of hardcoding the config file target, pull it from mysql::params (031a47d) + +2012-01-10 - Justin Ellison +* Moved $socket to within the case to toggle between distros. Added a $config_file variable to allow per-distro config file destinations. (360eacd) + +2012-01-10 - Justin Ellison +* Pretty sure this is a bug, 99% of Linux distros out there won't ever hit the default. (3462e6b) + +2012-02-09 - William Van Hevelingen +* Changed the README to use markdown (3b7dfeb) + +2012-02-04 - Daniel Black +* (#12412) mysqltuner.pl update (b809e6f) + +2011-11-17 - Matthias Pigulla +* (#11363) Add two missing privileges to grant: event_priv, trigger_priv (d15c9d1) + +2011-12-20 - Jeff McCune +* (minor) Fixup typos in Modulefile metadata (a0ed6a1) + +2011-12-19 - Carl Caum +* Only notify Exec to import sql if sql is given (0783c74) + +2011-12-19 - Carl Caum +* (#11508) Only load sql_scripts on DB creation (e3b9fd9) + +2011-12-13 - Justin Ellison +* Require not needed due to implicit dependencies (3058feb) + +2011-12-13 - Justin Ellison +* Bug #11375: puppetlabs-mysql fails on CentOS/RHEL (a557b8d) + +2011-06-03 - Dan Bode - 0.0.1 +* initial commit diff --git a/modules/mysql/LICENSE b/modules/mysql/LICENSE new file mode 100644 index 0000000..8d968b6 --- /dev/null +++ b/modules/mysql/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/modules/mysql/Modulefile b/modules/mysql/Modulefile new file mode 100644 index 0000000..f807daf --- /dev/null +++ b/modules/mysql/Modulefile @@ -0,0 +1,9 @@ +name 'puppetlabs-mysql' +version '0.5.0' +source 'git://github.com/puppetlabs/puppetlabs-mysql.git' +author 'Puppet Labs' +license 'Apache 2.0' +summary 'Mysql module' +description 'Mysql module' +project_page 'http://github.com/puppetlabs/puppetlabs-mysql' +dependency 'puppetlabs/stdlib', '>= 2.2.1' diff --git a/modules/mysql/README b/modules/mysql/README deleted file mode 100644 index d2e907b..0000000 --- a/modules/mysql/README +++ /dev/null @@ -1,24 +0,0 @@ -= Mysql Puppet module = - -== Usage == - -include mysql::server - -mysql::rights{"Set rights for puppet database": - ensure => present, - database => "puppet", - user => "puppet", - password => "puppet" -} - -mysql::database{"mydb": - ensure => present -} - -== Requires == -- Depends on augeas module, available here: - http://github.com/camptocamp/puppet-augeas -- you have to define a global path for exec, like: - Exec { path => "/usr/bin:/bin/...." } - You can put it in globals.pp - diff --git a/modules/mysql/README.md b/modules/mysql/README.md new file mode 100644 index 0000000..d140c8e --- /dev/null +++ b/modules/mysql/README.md @@ -0,0 +1,129 @@ +# Mysql module for Puppet + +This module manages mysql on Linux (RedHat/Debian) distros. A native mysql provider implements database resource type to handle database, database user, and database permission. + +Pluginsync needs to be enabled for this module to function properly. +Read more about pluginsync in our [docs](http://docs.puppetlabs.com/guides/plugins_in_modules.html#enabling-pluginsync) + +## Description + +This module uses the fact osfamily which is supported by Facter 1.6.1+. If you do not have facter 1.6.1 in your environment, the following manifests will provide the same functionality in site.pp (before declaring any node): + + if ! $::osfamily { + case $::operatingsystem { + 'RedHat', 'Fedora', 'CentOS', 'Scientific', 'SLC', 'Ascendos', 'CloudLinux', 'PSBM', 'OracleLinux', 'OVS', 'OEL': { + $osfamily = 'RedHat' + } + 'ubuntu', 'debian': { + $osfamily = 'Debian' + } + 'SLES', 'SLED', 'OpenSuSE', 'SuSE': { + $osfamily = 'Suse' + } + 'Solaris', 'Nexenta': { + $osfamily = 'Solaris' + } + default: { + $osfamily = $::operatingsystem + } + } + } + +This module depends on creates_resources function which is introduced in Puppet 2.7. Users on puppet 2.6 can use the following module which provides this functionality: + +[http://github.com/puppetlabs/puppetlabs-create_resources](http://github.com/puppetlabs/puppetlabs-create_resources) + +This module is based on work by David Schmitt. The following contributor have contributed patches to this module (beyond Puppet Labs): + +* Christian G. Warden +* Daniel Black +* Justin Ellison +* Lowe Schmidt +* Matthias Pigulla +* William Van Hevelingen +* Michael Arnold + +## Usage + +### mysql +Installs the mysql-client package. + + class { 'mysql': } + +### mysql::java +Installs mysql bindings for java. + + class { 'mysql::java': } + +### mysql::python +Installs mysql bindings for python. + + class { 'mysql::python': } + +### mysql::ruby +Installs mysql bindings for ruby. + + class { 'mysql::ruby': } + +### mysql::server +Installs mysql-server packages, configures my.cnf and starts mysqld service: + + class { 'mysql::server': + config_hash => { 'root_password' => 'foo' } + } + +Database login information stored in `/root/.my.cnf`. + +### mysql::db +Creates a database with a user and assign some privileges. + + mysql::db { 'mydb': + user => 'myuser', + password => 'mypass', + host => 'localhost', + grant => ['all'], + } + +### mysql::backup +Installs a mysql backup script, cronjob, and priviledged backup user. + + class { 'mysql::backup': + backupuser => 'myuser', + backuppassword => 'mypassword', + backupdir => '/tmp/backups', + } + +### Providers for database types: +MySQL provider supports puppet resources command: + + $ puppet resource database + database { 'information_schema': + ensure => 'present', + charset => 'utf8', + } + database { 'mysql': + ensure => 'present', + charset => 'latin1', + } + +The custom resources can be used in any other manifests: + + database { 'mydb': + charset => 'latin1', + } + + database_user { 'bob@localhost': + password_hash => mysql_password('foo') + } + + database_grant { 'user@localhost/database': + privileges => ['all'] , + # Or specify individual privileges with columns from the mysql.db table: + # privileges => ['Select_priv', 'Insert_priv', 'Update_priv', 'Delete_priv'] + } + +A resource default can be specified to handle dependency: + + Database { + require => Class['mysql::server'], + } diff --git a/modules/mysql/Rakefile b/modules/mysql/Rakefile new file mode 100644 index 0000000..cd3d379 --- /dev/null +++ b/modules/mysql/Rakefile @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/rake_tasks' diff --git a/modules/mysql/TODO b/modules/mysql/TODO new file mode 100644 index 0000000..3913293 --- /dev/null +++ b/modules/mysql/TODO @@ -0,0 +1,8 @@ +The best that I can tell is that this code traces back to David Schmitt. It has been forked many times since then :) + +1. you cannot add databases to an instance that has a root password +2. you have to specify username as USER@BLAH or it cannot be found +3. mysql_grant does not complain if user does not exist +4. Needs support for pre-seeding on debian +5. the types may need to take user/password +6. rather or not to configure /etc/.my.cnf should be configurable diff --git a/modules/mysql/files/etc/mysql/my.cnf b/modules/mysql/files/etc/mysql/my.cnf deleted file mode 100644 index 4467db2..0000000 --- a/modules/mysql/files/etc/mysql/my.cnf +++ /dev/null @@ -1,141 +0,0 @@ -# file managed by puppet -# -# The MySQL database server configuration file. -# -# You can copy this to one of: -# - "/etc/mysql/my.cnf" to set global options, -# - "~/.my.cnf" to set user-specific options. -# -# One can use all long options that the program supports. -# Run program with --help to get a list of available options and with -# --print-defaults to see which it would actually understand and use. -# -# For explanations see -# http://dev.mysql.com/doc/mysql/en/server-system-variables.html - -# This will be passed to all mysql clients -# It has been reported that passwords should be enclosed with ticks/quotes -# escpecially if they contain "#" chars... -# Remember to edit /etc/mysql/debian.cnf when changing the socket location. -[client] -port = 3306 -socket = /var/run/mysqld/mysqld.sock - -# Here is entries for some specific programs -# The following values assume you have at least 32M ram - -# This was formally known as [safe_mysqld]. Both versions are currently parsed. -[mysqld_safe] -socket = /var/run/mysqld/mysqld.sock -nice = 0 - -[mysqld] -# -# * Basic Settings -# -user = mysql -pid-file = /var/run/mysqld/mysqld.pid -socket = /var/run/mysqld/mysqld.sock -port = 3306 -basedir = /usr -datadir = /var/lib/mysql -tmpdir = /tmp -language = /usr/share/mysql/english -skip-external-locking -character-set-server = utf8 -skip-character-set-client-handshake -# -# Instead of skip-networking the default is now to listen only on -# localhost which is more compatible and is not less secure. -bind-address = 127.0.0.1 -# -# * Fine Tuning -# -key_buffer = 16M -max_allowed_packet = 16M -thread_stack = 128K -thread_cache_size = 8 -#max_connections = 100 -#table_cache = 64 -#thread_concurrency = 10 -# -# * Query Cache Configuration -# -query_cache_limit = 1M -query_cache_size = 16M -# -# * Logging and Replication -# -# Both location gets rotated by the cronjob. -# Be aware that this log type is a performance killer. -#log = /var/log/mysql/mysql.log -# -# Error logging goes to syslog. This is a Debian improvement :) -# -# Here you can see queries with especially long duration -#log_slow_queries = /var/log/mysql/mysql-slow.log -#long_query_time = 2 -#log-queries-not-using-indexes -# -# The following can be used as easy to replay backup logs or for replication. -#server-id = 1 -log_bin = /var/log/mysql/mysql-bin.log -# WARNING: Using expire_logs_days without bin_log crashes the server! See README.Debian! -expire_logs_days = 10 -max_binlog_size = 100M -#binlog_do_db = include_database_name -#binlog_ignore_db = include_database_name -# -# * BerkeleyDB -# -# Using BerkeleyDB is now discouraged as its support will cease in 5.1.12. -skip-bdb -# -# * InnoDB -# -# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/. -# Read the manual for more InnoDB related options. There are many! -# You might want to disable InnoDB to shrink the mysqld process by circa 100MB. -#skip-innodb -# -# * Security Features -# -# Read the manual, too, if you want chroot! -# chroot = /var/lib/mysql/ -# -# For generating SSL certificates I recommend the OpenSSL GUI "tinyca". -# -# ssl-ca=/etc/mysql/cacert.pem -# ssl-cert=/etc/mysql/server-cert.pem -# ssl-key=/etc/mysql/server-key.pem - - - -[mysqldump] -quick -quote-names -max_allowed_packet = 16M - -[mysql] -#no-auto-rehash # faster start of mysql but no tab completition - -[isamchk] -key_buffer = 16M - -# -# * NDB Cluster -# -# See /usr/share/doc/mysql-server-*/README.Debian for more information. -# -# The following configuration is read by the NDB Data Nodes (ndbd processes) -# not from the NDB Management Nodes (ndb_mgmd processes). -# -# [MYSQL_CLUSTER] -# ndb-connectstring=127.0.0.1 - - -# -# * IMPORTANT: Additional settings that can override those from this file! -# -!includedir /etc/mysql/conf.d/ - diff --git a/modules/mysql/files/mysqltuner.pl b/modules/mysql/files/mysqltuner.pl new file mode 100644 index 0000000..f61881c --- /dev/null +++ b/modules/mysql/files/mysqltuner.pl @@ -0,0 +1,966 @@ +#!/usr/bin/perl -w +# mysqltuner.pl - Version 1.2.0 +# High Performance MySQL Tuning Script +# Copyright (C) 2006-2011 Major Hayden - major@mhtx.net +# +# For the latest updates, please visit http://mysqltuner.com/ +# Git repository available at http://github.com/rackerhacker/MySQLTuner-perl +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# This project would not be possible without help from: +# Matthew Montgomery Paul Kehrer Dave Burgess +# Jonathan Hinds Mike Jackson Nils Breunese +# Shawn Ashlee Luuk Vosslamber Ville Skytta +# Trent Hornibrook Jason Gill Mark Imbriaco +# Greg Eden Aubin Galinotti Giovanni Bechis +# Bill Bradford Ryan Novosielski Michael Scheidell +# Blair Christensen Hans du Plooy Victor Trac +# Everett Barnes Tom Krouper Gary Barrueto +# Simon Greenaway Adam Stein Isart Montane +# Baptiste M. +# +# Inspired by Matthew Montgomery's tuning-primer.sh script: +# http://forge.mysql.com/projects/view.php?id=44 +# +use strict; +use warnings; +use diagnostics; +use File::Spec; +use Getopt::Long; + +# Set up a few variables for use in the script +my $tunerversion = "1.2.0"; +my (@adjvars, @generalrec); + +# Set defaults +my %opt = ( + "nobad" => 0, + "nogood" => 0, + "noinfo" => 0, + "nocolor" => 0, + "forcemem" => 0, + "forceswap" => 0, + "host" => 0, + "socket" => 0, + "port" => 0, + "user" => 0, + "pass" => 0, + "skipsize" => 0, + "checkversion" => 0, + ); + +# Gather the options from the command line +GetOptions(\%opt, + 'nobad', + 'nogood', + 'noinfo', + 'nocolor', + 'forcemem=i', + 'forceswap=i', + 'host=s', + 'socket=s', + 'port=i', + 'user=s', + 'pass=s', + 'skipsize', + 'checkversion', + 'help', + ); + +if (defined $opt{'help'} && $opt{'help'} == 1) { usage(); } + +sub usage { + # Shown with --help option passed + print "\n". + " MySQLTuner $tunerversion - MySQL High Performance Tuning Script\n". + " Bug reports, feature requests, and downloads at http://mysqltuner.com/\n". + " Maintained by Major Hayden (major\@mhtx.net) - Licensed under GPL\n". + "\n". + " Important Usage Guidelines:\n". + " To run the script with the default options, run the script without arguments\n". + " Allow MySQL server to run for at least 24-48 hours before trusting suggestions\n". + " Some routines may require root level privileges (script will provide warnings)\n". + " You must provide the remote server's total memory when connecting to other servers\n". + "\n". + " Connection and Authentication\n". + " --host Connect to a remote host to perform tests (default: localhost)\n". + " --socket Use a different socket for a local connection\n". + " --port Port to use for connection (default: 3306)\n". + " --user Username to use for authentication\n". + " --pass Password to use for authentication\n". + "\n". + " Performance and Reporting Options\n". + " --skipsize Don't enumerate tables and their types/sizes (default: on)\n". + " (Recommended for servers with many tables)\n". + " --checkversion Check for updates to MySQLTuner (default: don't check)\n". + " --forcemem Amount of RAM installed in megabytes\n". + " --forceswap Amount of swap memory configured in megabytes\n". + "\n". + " Output Options:\n". + " --nogood Remove OK responses\n". + " --nobad Remove negative/suggestion responses\n". + " --noinfo Remove informational responses\n". + " --nocolor Don't print output in color\n". + "\n"; + exit; +} + +my $devnull = File::Spec->devnull(); + +# Setting up the colors for the print styles +my $good = ($opt{nocolor} == 0)? "[\e[0;32mOK\e[0m]" : "[OK]" ; +my $bad = ($opt{nocolor} == 0)? "[\e[0;31m!!\e[0m]" : "[!!]" ; +my $info = ($opt{nocolor} == 0)? "[\e[0;34m--\e[0m]" : "[--]" ; + +# Functions that handle the print styles +sub goodprint { print $good." ".$_[0] unless ($opt{nogood} == 1); } +sub infoprint { print $info." ".$_[0] unless ($opt{noinfo} == 1); } +sub badprint { print $bad." ".$_[0] unless ($opt{nobad} == 1); } +sub redwrap { return ($opt{nocolor} == 0)? "\e[0;31m".$_[0]."\e[0m" : $_[0] ; } +sub greenwrap { return ($opt{nocolor} == 0)? "\e[0;32m".$_[0]."\e[0m" : $_[0] ; } + +# Calculates the parameter passed in bytes, and then rounds it to one decimal place +sub hr_bytes { + my $num = shift; + if ($num >= (1024**3)) { #GB + return sprintf("%.1f",($num/(1024**3)))."G"; + } elsif ($num >= (1024**2)) { #MB + return sprintf("%.1f",($num/(1024**2)))."M"; + } elsif ($num >= 1024) { #KB + return sprintf("%.1f",($num/1024))."K"; + } else { + return $num."B"; + } +} + +# Calculates the parameter passed in bytes, and then rounds it to the nearest integer +sub hr_bytes_rnd { + my $num = shift; + if ($num >= (1024**3)) { #GB + return int(($num/(1024**3)))."G"; + } elsif ($num >= (1024**2)) { #MB + return int(($num/(1024**2)))."M"; + } elsif ($num >= 1024) { #KB + return int(($num/1024))."K"; + } else { + return $num."B"; + } +} + +# Calculates the parameter passed to the nearest power of 1000, then rounds it to the nearest integer +sub hr_num { + my $num = shift; + if ($num >= (1000**3)) { # Billions + return int(($num/(1000**3)))."B"; + } elsif ($num >= (1000**2)) { # Millions + return int(($num/(1000**2)))."M"; + } elsif ($num >= 1000) { # Thousands + return int(($num/1000))."K"; + } else { + return $num; + } +} + +# Calculates uptime to display in a more attractive form +sub pretty_uptime { + my $uptime = shift; + my $seconds = $uptime % 60; + my $minutes = int(($uptime % 3600) / 60); + my $hours = int(($uptime % 86400) / (3600)); + my $days = int($uptime / (86400)); + my $uptimestring; + if ($days > 0) { + $uptimestring = "${days}d ${hours}h ${minutes}m ${seconds}s"; + } elsif ($hours > 0) { + $uptimestring = "${hours}h ${minutes}m ${seconds}s"; + } elsif ($minutes > 0) { + $uptimestring = "${minutes}m ${seconds}s"; + } else { + $uptimestring = "${seconds}s"; + } + return $uptimestring; +} + +# Retrieves the memory installed on this machine +my ($physical_memory,$swap_memory,$duflags); +sub os_setup { + sub memerror { + badprint "Unable to determine total memory/swap; use '--forcemem' and '--forceswap'\n"; + exit; + } + my $os = `uname`; + $duflags = ($os =~ /Linux/) ? '-b' : ''; + if ($opt{'forcemem'} > 0) { + $physical_memory = $opt{'forcemem'} * 1048576; + infoprint "Assuming $opt{'forcemem'} MB of physical memory\n"; + if ($opt{'forceswap'} > 0) { + $swap_memory = $opt{'forceswap'} * 1048576; + infoprint "Assuming $opt{'forceswap'} MB of swap space\n"; + } else { + $swap_memory = 0; + badprint "Assuming 0 MB of swap space (use --forceswap to specify)\n"; + } + } else { + if ($os =~ /Linux/) { + $physical_memory = `free -b | grep Mem | awk '{print \$2}'` or memerror; + $swap_memory = `free -b | grep Swap | awk '{print \$2}'` or memerror; + } elsif ($os =~ /Darwin/) { + $physical_memory = `sysctl -n hw.memsize` or memerror; + $swap_memory = `sysctl -n vm.swapusage | awk '{print \$3}' | sed 's/\..*\$//'` or memerror; + } elsif ($os =~ /NetBSD|OpenBSD/) { + $physical_memory = `sysctl -n hw.physmem` or memerror; + if ($physical_memory < 0) { + $physical_memory = `sysctl -n hw.physmem64` or memerror; + } + $swap_memory = `swapctl -l | grep '^/' | awk '{ s+= \$2 } END { print s }'` or memerror; + } elsif ($os =~ /BSD/) { + $physical_memory = `sysctl -n hw.realmem`; + $swap_memory = `swapinfo | grep '^/' | awk '{ s+= \$2 } END { print s }'`; + } elsif ($os =~ /SunOS/) { + $physical_memory = `/usr/sbin/prtconf | grep Memory | cut -f 3 -d ' '` or memerror; + chomp($physical_memory); + $physical_memory = $physical_memory*1024*1024; + } elsif ($os =~ /AIX/) { + $physical_memory = `lsattr -El sys0 | grep realmem | awk '{print \$2}'` or memerror; + chomp($physical_memory); + $physical_memory = $physical_memory*1024; + $swap_memory = `lsps -as | awk -F"(MB| +)" '/MB /{print \$2}'` or memerror; + chomp($swap_memory); + $swap_memory = $swap_memory*1024*1024; + } + } + chomp($physical_memory); +} + +# Checks to see if a MySQL login is possible +my ($mysqllogin,$doremote,$remotestring); +sub mysql_setup { + $doremote = 0; + $remotestring = ''; + my $command = `which mysqladmin`; + chomp($command); + if (! -e $command) { + badprint "Unable to find mysqladmin in your \$PATH. Is MySQL installed?\n"; + exit; + } + # Are we being asked to connect via a socket? + if ($opt{socket} ne 0) { + $remotestring = " -S $opt{socket}"; + } + # Are we being asked to connect to a remote server? + if ($opt{host} ne 0) { + chomp($opt{host}); + $opt{port} = ($opt{port} eq 0)? 3306 : $opt{port} ; + # If we're doing a remote connection, but forcemem wasn't specified, we need to exit + if ($opt{'forcemem'} eq 0) { + badprint "The --forcemem option is required for remote connections\n"; + exit; + } + infoprint "Performing tests on $opt{host}:$opt{port}\n"; + $remotestring = " -h $opt{host} -P $opt{port}"; + $doremote = 1; + } + # Did we already get a username and password passed on the command line? + if ($opt{user} ne 0 and $opt{pass} ne 0) { + $mysqllogin = "-u $opt{user} -p'$opt{pass}'".$remotestring; + my $loginstatus = `mysqladmin ping $mysqllogin 2>&1`; + if ($loginstatus =~ /mysqld is alive/) { + goodprint "Logged in using credentials passed on the command line\n"; + return 1; + } else { + badprint "Attempted to use login credentials, but they were invalid\n"; + exit 0; + } + } + if ( -r "/etc/psa/.psa.shadow" and $doremote == 0 ) { + # It's a Plesk box, use the available credentials + $mysqllogin = "-u admin -p`cat /etc/psa/.psa.shadow`"; + my $loginstatus = `mysqladmin ping $mysqllogin 2>&1`; + unless ($loginstatus =~ /mysqld is alive/) { + badprint "Attempted to use login credentials from Plesk, but they failed.\n"; + exit 0; + } + } elsif ( -r "/etc/mysql/debian.cnf" and $doremote == 0 ){ + # We have a debian maintenance account, use it + $mysqllogin = "--defaults-file=/etc/mysql/debian.cnf"; + my $loginstatus = `mysqladmin $mysqllogin ping 2>&1`; + if ($loginstatus =~ /mysqld is alive/) { + goodprint "Logged in using credentials from debian maintenance account.\n"; + return 1; + } else { + badprint "Attempted to use login credentials from debian maintenance account, but they failed.\n"; + exit 0; + } + } else { + # It's not Plesk or debian, we should try a login + my $loginstatus = `mysqladmin $remotestring ping 2>&1`; + if ($loginstatus =~ /mysqld is alive/) { + # Login went just fine + $mysqllogin = " $remotestring "; + # Did this go well because of a .my.cnf file or is there no password set? + my $userpath = `printenv HOME`; + if (length($userpath) > 0) { + chomp($userpath); + } + unless ( -e "${userpath}/.my.cnf" ) { + badprint "Successfully authenticated with no password - SECURITY RISK!\n"; + } + return 1; + } else { + print STDERR "Please enter your MySQL administrative login: "; + my $name = <>; + print STDERR "Please enter your MySQL administrative password: "; + system("stty -echo >$devnull 2>&1"); + my $password = <>; + system("stty echo >$devnull 2>&1"); + chomp($password); + chomp($name); + $mysqllogin = "-u $name"; + if (length($password) > 0) { + $mysqllogin .= " -p'$password'"; + } + $mysqllogin .= $remotestring; + my $loginstatus = `mysqladmin ping $mysqllogin 2>&1`; + if ($loginstatus =~ /mysqld is alive/) { + print STDERR "\n"; + if (! length($password)) { + # Did this go well because of a .my.cnf file or is there no password set? + my $userpath = `ls -d ~`; + chomp($userpath); + unless ( -e "$userpath/.my.cnf" ) { + badprint "Successfully authenticated with no password - SECURITY RISK!\n"; + } + } + return 1; + } else { + print "\n".$bad." Attempted to use login credentials, but they were invalid.\n"; + exit 0; + } + exit 0; + } + } +} + +# Populates all of the variable and status hashes +my (%mystat,%myvar,$dummyselect); +sub get_all_vars { + # We need to initiate at least one query so that our data is useable + $dummyselect = `mysql $mysqllogin -Bse "SELECT VERSION();"`; + my @mysqlvarlist = `mysql $mysqllogin -Bse "SHOW /*!50000 GLOBAL */ VARIABLES;"`; + foreach my $line (@mysqlvarlist) { + $line =~ /([a-zA-Z_]*)\s*(.*)/; + $myvar{$1} = $2; + } + my @mysqlstatlist = `mysql $mysqllogin -Bse "SHOW /*!50000 GLOBAL */ STATUS;"`; + foreach my $line (@mysqlstatlist) { + $line =~ /([a-zA-Z_]*)\s*(.*)/; + $mystat{$1} = $2; + } + # Workaround for MySQL bug #59393 wrt. ignore-builtin-innodb + if (($myvar{'ignore_builtin_innodb'} || "") eq "ON") { + $myvar{'have_innodb'} = "NO"; + } + # have_* for engines is deprecated and will be removed in MySQL 5.6; + # check SHOW ENGINES and set corresponding old style variables. + # Also works around MySQL bug #59393 wrt. skip-innodb + my @mysqlenginelist = `mysql $mysqllogin -Bse "SHOW ENGINES;" 2>$devnull`; + foreach my $line (@mysqlenginelist) { + if ($line =~ /^([a-zA-Z_]+)\s+(\S+)/) { + my $engine = lc($1); + if ($engine eq "federated" || $engine eq "blackhole") { + $engine .= "_engine"; + } elsif ($engine eq "berkeleydb") { + $engine = "bdb"; + } + my $val = ($2 eq "DEFAULT") ? "YES" : $2; + $myvar{"have_$engine"} = $val; + } + } +} + +sub security_recommendations { + print "\n-------- Security Recommendations -------------------------------------------\n"; + my @mysqlstatlist = `mysql $mysqllogin -Bse "SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE password = '' OR password IS NULL;"`; + if (@mysqlstatlist) { + foreach my $line (sort @mysqlstatlist) { + chomp($line); + badprint "User '".$line."' has no password set.\n"; + } + } else { + goodprint "All database users have passwords assigned\n"; + } +} + +sub get_replication_status { + my $slave_status = `mysql $mysqllogin -Bse "show slave status\\G"`; + my ($io_running) = ($slave_status =~ /slave_io_running\S*\s+(\S+)/i); + my ($sql_running) = ($slave_status =~ /slave_sql_running\S*\s+(\S+)/i); + if ($io_running eq 'Yes' && $sql_running eq 'Yes') { + if ($myvar{'read_only'} eq 'OFF') { + badprint "This replication slave is running with the read_only option disabled."; + } else { + goodprint "This replication slave is running with the read_only option enabled."; + } + } +} + +# Checks for updates to MySQLTuner +sub validate_tuner_version { + print "\n-------- General Statistics --------------------------------------------------\n"; + if ($opt{checkversion} eq 0) { + infoprint "Skipped version check for MySQLTuner script\n"; + return; + } + my $update; + my $url = "http://mysqltuner.com/versioncheck.php?v=$tunerversion"; + if (-e "/usr/bin/curl") { + $update = `/usr/bin/curl --connect-timeout 5 '$url' 2>$devnull`; + chomp($update); + } elsif (-e "/usr/bin/wget") { + $update = `/usr/bin/wget -e timestamping=off -T 5 -O - '$url' 2>$devnull`; + chomp($update); + } + if ($update eq 1) { + badprint "There is a new version of MySQLTuner available\n"; + } elsif ($update eq 0) { + goodprint "You have the latest version of MySQLTuner\n"; + } else { + infoprint "Unable to check for the latest MySQLTuner version\n"; + } +} + +# Checks for supported or EOL'ed MySQL versions +my ($mysqlvermajor,$mysqlverminor); +sub validate_mysql_version { + ($mysqlvermajor,$mysqlverminor) = $myvar{'version'} =~ /(\d)\.(\d)/; + if (!mysql_version_ge(5)) { + badprint "Your MySQL version ".$myvar{'version'}." is EOL software! Upgrade soon!\n"; + } elsif (mysql_version_ge(6)) { + badprint "Currently running unsupported MySQL version ".$myvar{'version'}."\n"; + } else { + goodprint "Currently running supported MySQL version ".$myvar{'version'}."\n"; + } +} + +# Checks if MySQL version is greater than equal to (major, minor) +sub mysql_version_ge { + my ($maj, $min) = @_; + return $mysqlvermajor > $maj || ($mysqlvermajor == $maj && $mysqlverminor >= ($min || 0)); +} + +# Checks for 32-bit boxes with more than 2GB of RAM +my ($arch); +sub check_architecture { + if ($doremote eq 1) { return; } + if (`uname` =~ /SunOS/ && `isainfo -b` =~ /64/) { + $arch = 64; + goodprint "Operating on 64-bit architecture\n"; + } elsif (`uname` !~ /SunOS/ && `uname -m` =~ /64/) { + $arch = 64; + goodprint "Operating on 64-bit architecture\n"; + } elsif (`uname` =~ /AIX/ && `bootinfo -K` =~ /64/) { + $arch = 64; + goodprint "Operating on 64-bit architecture\n"; + } else { + $arch = 32; + if ($physical_memory > 2147483648) { + badprint "Switch to 64-bit OS - MySQL cannot currently use all of your RAM\n"; + } else { + goodprint "Operating on 32-bit architecture with less than 2GB RAM\n"; + } + } +} + +# Start up a ton of storage engine counts/statistics +my (%enginestats,%enginecount,$fragtables); +sub check_storage_engines { + if ($opt{skipsize} eq 1) { + print "\n-------- Storage Engine Statistics -------------------------------------------\n"; + infoprint "Skipped due to --skipsize option\n"; + return; + } + print "\n-------- Storage Engine Statistics -------------------------------------------\n"; + infoprint "Status: "; + my $engines; + $engines .= (defined $myvar{'have_archive'} && $myvar{'have_archive'} eq "YES")? greenwrap "+Archive " : redwrap "-Archive " ; + $engines .= (defined $myvar{'have_bdb'} && $myvar{'have_bdb'} eq "YES")? greenwrap "+BDB " : redwrap "-BDB " ; + $engines .= (defined $myvar{'have_federated_engine'} && $myvar{'have_federated_engine'} eq "YES")? greenwrap "+Federated " : redwrap "-Federated " ; + $engines .= (defined $myvar{'have_innodb'} && $myvar{'have_innodb'} eq "YES")? greenwrap "+InnoDB " : redwrap "-InnoDB " ; + $engines .= (defined $myvar{'have_isam'} && $myvar{'have_isam'} eq "YES")? greenwrap "+ISAM " : redwrap "-ISAM " ; + $engines .= (defined $myvar{'have_ndbcluster'} && $myvar{'have_ndbcluster'} eq "YES")? greenwrap "+NDBCluster " : redwrap "-NDBCluster " ; + print "$engines\n"; + if (mysql_version_ge(5)) { + # MySQL 5 servers can have table sizes calculated quickly from information schema + my @templist = `mysql $mysqllogin -Bse "SELECT ENGINE,SUM(DATA_LENGTH),COUNT(ENGINE) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema','mysql') AND ENGINE IS NOT NULL GROUP BY ENGINE ORDER BY ENGINE ASC;"`; + foreach my $line (@templist) { + my ($engine,$size,$count); + ($engine,$size,$count) = $line =~ /([a-zA-Z_]*)\s+(\d+)\s+(\d+)/; + if (!defined($size)) { next; } + $enginestats{$engine} = $size; + $enginecount{$engine} = $count; + } + $fragtables = `mysql $mysqllogin -Bse "SELECT COUNT(TABLE_NAME) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema','mysql') AND Data_free > 0 AND NOT ENGINE='MEMORY';"`; + chomp($fragtables); + } else { + # MySQL < 5 servers take a lot of work to get table sizes + my @tblist; + # Now we build a database list, and loop through it to get storage engine stats for tables + my @dblist = `mysql $mysqllogin -Bse "SHOW DATABASES"`; + foreach my $db (@dblist) { + chomp($db); + if ($db eq "information_schema") { next; } + my @ixs = (1, 6, 9); + if (!mysql_version_ge(4, 1)) { + # MySQL 3.23/4.0 keeps Data_Length in the 5th (0-based) column + @ixs = (1, 5, 8); + } + push(@tblist, map { [ (split)[@ixs] ] } `mysql $mysqllogin -Bse "SHOW TABLE STATUS FROM \\\`$db\\\`"`); + } + # Parse through the table list to generate storage engine counts/statistics + $fragtables = 0; + foreach my $tbl (@tblist) { + my ($engine, $size, $datafree) = @$tbl; + if (defined $enginestats{$engine}) { + $enginestats{$engine} += $size; + $enginecount{$engine} += 1; + } else { + $enginestats{$engine} = $size; + $enginecount{$engine} = 1; + } + if ($datafree > 0) { + $fragtables++; + } + } + } + while (my ($engine,$size) = each(%enginestats)) { + infoprint "Data in $engine tables: ".hr_bytes_rnd($size)." (Tables: ".$enginecount{$engine}.")"."\n"; + } + # If the storage engine isn't being used, recommend it to be disabled + if (!defined $enginestats{'InnoDB'} && defined $myvar{'have_innodb'} && $myvar{'have_innodb'} eq "YES") { + badprint "InnoDB is enabled but isn't being used\n"; + push(@generalrec,"Add skip-innodb to MySQL configuration to disable InnoDB"); + } + if (!defined $enginestats{'BerkeleyDB'} && defined $myvar{'have_bdb'} && $myvar{'have_bdb'} eq "YES") { + badprint "BDB is enabled but isn't being used\n"; + push(@generalrec,"Add skip-bdb to MySQL configuration to disable BDB"); + } + if (!defined $enginestats{'ISAM'} && defined $myvar{'have_isam'} && $myvar{'have_isam'} eq "YES") { + badprint "ISAM is enabled but isn't being used\n"; + push(@generalrec,"Add skip-isam to MySQL configuration to disable ISAM (MySQL > 4.1.0)"); + } + # Fragmented tables + if ($fragtables > 0) { + badprint "Total fragmented tables: $fragtables\n"; + push(@generalrec,"Run OPTIMIZE TABLE to defragment tables for better performance"); + } else { + goodprint "Total fragmented tables: $fragtables\n"; + } +} + +my %mycalc; +sub calculations { + if ($mystat{'Questions'} < 1) { + badprint "Your server has not answered any queries - cannot continue..."; + exit 0; + } + # Per-thread memory + if (mysql_version_ge(4)) { + $mycalc{'per_thread_buffers'} = $myvar{'read_buffer_size'} + $myvar{'read_rnd_buffer_size'} + $myvar{'sort_buffer_size'} + $myvar{'thread_stack'} + $myvar{'join_buffer_size'}; + } else { + $mycalc{'per_thread_buffers'} = $myvar{'record_buffer'} + $myvar{'record_rnd_buffer'} + $myvar{'sort_buffer'} + $myvar{'thread_stack'} + $myvar{'join_buffer_size'}; + } + $mycalc{'total_per_thread_buffers'} = $mycalc{'per_thread_buffers'} * $myvar{'max_connections'}; + $mycalc{'max_total_per_thread_buffers'} = $mycalc{'per_thread_buffers'} * $mystat{'Max_used_connections'}; + + # Server-wide memory + $mycalc{'max_tmp_table_size'} = ($myvar{'tmp_table_size'} > $myvar{'max_heap_table_size'}) ? $myvar{'max_heap_table_size'} : $myvar{'tmp_table_size'} ; + $mycalc{'server_buffers'} = $myvar{'key_buffer_size'} + $mycalc{'max_tmp_table_size'}; + $mycalc{'server_buffers'} += (defined $myvar{'innodb_buffer_pool_size'}) ? $myvar{'innodb_buffer_pool_size'} : 0 ; + $mycalc{'server_buffers'} += (defined $myvar{'innodb_additional_mem_pool_size'}) ? $myvar{'innodb_additional_mem_pool_size'} : 0 ; + $mycalc{'server_buffers'} += (defined $myvar{'innodb_log_buffer_size'}) ? $myvar{'innodb_log_buffer_size'} : 0 ; + $mycalc{'server_buffers'} += (defined $myvar{'query_cache_size'}) ? $myvar{'query_cache_size'} : 0 ; + + # Global memory + $mycalc{'max_used_memory'} = $mycalc{'server_buffers'} + $mycalc{"max_total_per_thread_buffers"}; + $mycalc{'total_possible_used_memory'} = $mycalc{'server_buffers'} + $mycalc{'total_per_thread_buffers'}; + $mycalc{'pct_physical_memory'} = int(($mycalc{'total_possible_used_memory'} * 100) / $physical_memory); + + # Slow queries + $mycalc{'pct_slow_queries'} = int(($mystat{'Slow_queries'}/$mystat{'Questions'}) * 100); + + # Connections + $mycalc{'pct_connections_used'} = int(($mystat{'Max_used_connections'}/$myvar{'max_connections'}) * 100); + $mycalc{'pct_connections_used'} = ($mycalc{'pct_connections_used'} > 100) ? 100 : $mycalc{'pct_connections_used'} ; + + # Key buffers + if (mysql_version_ge(4, 1)) { + $mycalc{'pct_key_buffer_used'} = sprintf("%.1f",(1 - (($mystat{'Key_blocks_unused'} * $myvar{'key_cache_block_size'}) / $myvar{'key_buffer_size'})) * 100); + } + if ($mystat{'Key_read_requests'} > 0) { + $mycalc{'pct_keys_from_mem'} = sprintf("%.1f",(100 - (($mystat{'Key_reads'} / $mystat{'Key_read_requests'}) * 100))); + } else { + $mycalc{'pct_keys_from_mem'} = 0; + } + if ($doremote eq 0 and !mysql_version_ge(5)) { + my $size = 0; + $size += (split)[0] for `find $myvar{'datadir'} -name "*.MYI" 2>&1 | xargs du -L $duflags 2>&1`; + $mycalc{'total_myisam_indexes'} = $size; + } elsif (mysql_version_ge(5)) { + $mycalc{'total_myisam_indexes'} = `mysql $mysqllogin -Bse "SELECT IFNULL(SUM(INDEX_LENGTH),0) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema') AND ENGINE = 'MyISAM';"`; + } + if (defined $mycalc{'total_myisam_indexes'} and $mycalc{'total_myisam_indexes'} == 0) { + $mycalc{'total_myisam_indexes'} = "fail"; + } elsif (defined $mycalc{'total_myisam_indexes'}) { + chomp($mycalc{'total_myisam_indexes'}); + } + + # Query cache + if (mysql_version_ge(4)) { + $mycalc{'query_cache_efficiency'} = sprintf("%.1f",($mystat{'Qcache_hits'} / ($mystat{'Com_select'} + $mystat{'Qcache_hits'})) * 100); + if ($myvar{'query_cache_size'}) { + $mycalc{'pct_query_cache_used'} = sprintf("%.1f",100 - ($mystat{'Qcache_free_memory'} / $myvar{'query_cache_size'}) * 100); + } + if ($mystat{'Qcache_lowmem_prunes'} == 0) { + $mycalc{'query_cache_prunes_per_day'} = 0; + } else { + $mycalc{'query_cache_prunes_per_day'} = int($mystat{'Qcache_lowmem_prunes'} / ($mystat{'Uptime'}/86400)); + } + } + + # Sorting + $mycalc{'total_sorts'} = $mystat{'Sort_scan'} + $mystat{'Sort_range'}; + if ($mycalc{'total_sorts'} > 0) { + $mycalc{'pct_temp_sort_table'} = int(($mystat{'Sort_merge_passes'} / $mycalc{'total_sorts'}) * 100); + } + + # Joins + $mycalc{'joins_without_indexes'} = $mystat{'Select_range_check'} + $mystat{'Select_full_join'}; + $mycalc{'joins_without_indexes_per_day'} = int($mycalc{'joins_without_indexes'} / ($mystat{'Uptime'}/86400)); + + # Temporary tables + if ($mystat{'Created_tmp_tables'} > 0) { + if ($mystat{'Created_tmp_disk_tables'} > 0) { + $mycalc{'pct_temp_disk'} = int(($mystat{'Created_tmp_disk_tables'} / ($mystat{'Created_tmp_tables'} + $mystat{'Created_tmp_disk_tables'})) * 100); + } else { + $mycalc{'pct_temp_disk'} = 0; + } + } + + # Table cache + if ($mystat{'Opened_tables'} > 0) { + $mycalc{'table_cache_hit_rate'} = int($mystat{'Open_tables'}*100/$mystat{'Opened_tables'}); + } else { + $mycalc{'table_cache_hit_rate'} = 100; + } + + # Open files + if ($myvar{'open_files_limit'} > 0) { + $mycalc{'pct_files_open'} = int($mystat{'Open_files'}*100/$myvar{'open_files_limit'}); + } + + # Table locks + if ($mystat{'Table_locks_immediate'} > 0) { + if ($mystat{'Table_locks_waited'} == 0) { + $mycalc{'pct_table_locks_immediate'} = 100; + } else { + $mycalc{'pct_table_locks_immediate'} = int($mystat{'Table_locks_immediate'}*100/($mystat{'Table_locks_waited'} + $mystat{'Table_locks_immediate'})); + } + } + + # Thread cache + $mycalc{'thread_cache_hit_rate'} = int(100 - (($mystat{'Threads_created'} / $mystat{'Connections'}) * 100)); + + # Other + if ($mystat{'Connections'} > 0) { + $mycalc{'pct_aborted_connections'} = int(($mystat{'Aborted_connects'}/$mystat{'Connections'}) * 100); + } + if ($mystat{'Questions'} > 0) { + $mycalc{'total_reads'} = $mystat{'Com_select'}; + $mycalc{'total_writes'} = $mystat{'Com_delete'} + $mystat{'Com_insert'} + $mystat{'Com_update'} + $mystat{'Com_replace'}; + if ($mycalc{'total_reads'} == 0) { + $mycalc{'pct_reads'} = 0; + $mycalc{'pct_writes'} = 100; + } else { + $mycalc{'pct_reads'} = int(($mycalc{'total_reads'}/($mycalc{'total_reads'}+$mycalc{'total_writes'})) * 100); + $mycalc{'pct_writes'} = 100-$mycalc{'pct_reads'}; + } + } + + # InnoDB + if ($myvar{'have_innodb'} eq "YES") { + $mycalc{'innodb_log_size_pct'} = ($myvar{'innodb_log_file_size'} * 100 / $myvar{'innodb_buffer_pool_size'}); + } +} + +sub mysql_stats { + print "\n-------- Performance Metrics -------------------------------------------------\n"; + # Show uptime, queries per second, connections, traffic stats + my $qps; + if ($mystat{'Uptime'} > 0) { $qps = sprintf("%.3f",$mystat{'Questions'}/$mystat{'Uptime'}); } + if ($mystat{'Uptime'} < 86400) { push(@generalrec,"MySQL started within last 24 hours - recommendations may be inaccurate"); } + infoprint "Up for: ".pretty_uptime($mystat{'Uptime'})." (".hr_num($mystat{'Questions'}). + " q [".hr_num($qps)." qps], ".hr_num($mystat{'Connections'})." conn,". + " TX: ".hr_num($mystat{'Bytes_sent'}).", RX: ".hr_num($mystat{'Bytes_received'}).")\n"; + infoprint "Reads / Writes: ".$mycalc{'pct_reads'}."% / ".$mycalc{'pct_writes'}."%\n"; + + # Memory usage + infoprint "Total buffers: ".hr_bytes($mycalc{'server_buffers'})." global + ".hr_bytes($mycalc{'per_thread_buffers'})." per thread ($myvar{'max_connections'} max threads)\n"; + if ($mycalc{'total_possible_used_memory'} > 2*1024*1024*1024 && $arch eq 32) { + badprint "Allocating > 2GB RAM on 32-bit systems can cause system instability\n"; + badprint "Maximum possible memory usage: ".hr_bytes($mycalc{'total_possible_used_memory'})." ($mycalc{'pct_physical_memory'}% of installed RAM)\n"; + } elsif ($mycalc{'pct_physical_memory'} > 85) { + badprint "Maximum possible memory usage: ".hr_bytes($mycalc{'total_possible_used_memory'})." ($mycalc{'pct_physical_memory'}% of installed RAM)\n"; + push(@generalrec,"Reduce your overall MySQL memory footprint for system stability"); + } else { + goodprint "Maximum possible memory usage: ".hr_bytes($mycalc{'total_possible_used_memory'})." ($mycalc{'pct_physical_memory'}% of installed RAM)\n"; + } + + # Slow queries + if ($mycalc{'pct_slow_queries'} > 5) { + badprint "Slow queries: $mycalc{'pct_slow_queries'}% (".hr_num($mystat{'Slow_queries'})."/".hr_num($mystat{'Questions'}).")\n"; + } else { + goodprint "Slow queries: $mycalc{'pct_slow_queries'}% (".hr_num($mystat{'Slow_queries'})."/".hr_num($mystat{'Questions'}).")\n"; + } + if ($myvar{'long_query_time'} > 10) { push(@adjvars,"long_query_time (<= 10)"); } + if (defined($myvar{'log_slow_queries'})) { + if ($myvar{'log_slow_queries'} eq "OFF") { push(@generalrec,"Enable the slow query log to troubleshoot bad queries"); } + } + + # Connections + if ($mycalc{'pct_connections_used'} > 85) { + badprint "Highest connection usage: $mycalc{'pct_connections_used'}% ($mystat{'Max_used_connections'}/$myvar{'max_connections'})\n"; + push(@adjvars,"max_connections (> ".$myvar{'max_connections'}.")"); + push(@adjvars,"wait_timeout (< ".$myvar{'wait_timeout'}.")","interactive_timeout (< ".$myvar{'interactive_timeout'}.")"); + push(@generalrec,"Reduce or eliminate persistent connections to reduce connection usage") + } else { + goodprint "Highest usage of available connections: $mycalc{'pct_connections_used'}% ($mystat{'Max_used_connections'}/$myvar{'max_connections'})\n"; + } + + # Key buffer + if (!defined($mycalc{'total_myisam_indexes'}) and $doremote == 1) { + push(@generalrec,"Unable to calculate MyISAM indexes on remote MySQL server < 5.0.0"); + } elsif ($mycalc{'total_myisam_indexes'} =~ /^fail$/) { + badprint "Cannot calculate MyISAM index size - re-run script as root user\n"; + } elsif ($mycalc{'total_myisam_indexes'} == "0") { + badprint "None of your MyISAM tables are indexed - add indexes immediately\n"; + } else { + if ($myvar{'key_buffer_size'} < $mycalc{'total_myisam_indexes'} && $mycalc{'pct_keys_from_mem'} < 95) { + badprint "Key buffer size / total MyISAM indexes: ".hr_bytes($myvar{'key_buffer_size'})."/".hr_bytes($mycalc{'total_myisam_indexes'})."\n"; + push(@adjvars,"key_buffer_size (> ".hr_bytes($mycalc{'total_myisam_indexes'}).")"); + } else { + goodprint "Key buffer size / total MyISAM indexes: ".hr_bytes($myvar{'key_buffer_size'})."/".hr_bytes($mycalc{'total_myisam_indexes'})."\n"; + } + if ($mystat{'Key_read_requests'} > 0) { + if ($mycalc{'pct_keys_from_mem'} < 95) { + badprint "Key buffer hit rate: $mycalc{'pct_keys_from_mem'}% (".hr_num($mystat{'Key_read_requests'})." cached / ".hr_num($mystat{'Key_reads'})." reads)\n"; + } else { + goodprint "Key buffer hit rate: $mycalc{'pct_keys_from_mem'}% (".hr_num($mystat{'Key_read_requests'})." cached / ".hr_num($mystat{'Key_reads'})." reads)\n"; + } + } else { + # No queries have run that would use keys + } + } + + # Query cache + if (!mysql_version_ge(4)) { + # MySQL versions < 4.01 don't support query caching + push(@generalrec,"Upgrade MySQL to version 4+ to utilize query caching"); + } elsif ($myvar{'query_cache_size'} < 1) { + badprint "Query cache is disabled\n"; + push(@adjvars,"query_cache_size (>= 8M)"); + } elsif ($mystat{'Com_select'} == 0) { + badprint "Query cache cannot be analyzed - no SELECT statements executed\n"; + } else { + if ($mycalc{'query_cache_efficiency'} < 20) { + badprint "Query cache efficiency: $mycalc{'query_cache_efficiency'}% (".hr_num($mystat{'Qcache_hits'})." cached / ".hr_num($mystat{'Qcache_hits'}+$mystat{'Com_select'})." selects)\n"; + push(@adjvars,"query_cache_limit (> ".hr_bytes_rnd($myvar{'query_cache_limit'}).", or use smaller result sets)"); + } else { + goodprint "Query cache efficiency: $mycalc{'query_cache_efficiency'}% (".hr_num($mystat{'Qcache_hits'})." cached / ".hr_num($mystat{'Qcache_hits'}+$mystat{'Com_select'})." selects)\n"; + } + if ($mycalc{'query_cache_prunes_per_day'} > 98) { + badprint "Query cache prunes per day: $mycalc{'query_cache_prunes_per_day'}\n"; + if ($myvar{'query_cache_size'} > 128*1024*1024) { + push(@generalrec,"Increasing the query_cache size over 128M may reduce performance"); + push(@adjvars,"query_cache_size (> ".hr_bytes_rnd($myvar{'query_cache_size'}).") [see warning above]"); + } else { + push(@adjvars,"query_cache_size (> ".hr_bytes_rnd($myvar{'query_cache_size'}).")"); + } + } else { + goodprint "Query cache prunes per day: $mycalc{'query_cache_prunes_per_day'}\n"; + } + } + + # Sorting + if ($mycalc{'total_sorts'} == 0) { + # For the sake of space, we will be quiet here + # No sorts have run yet + } elsif ($mycalc{'pct_temp_sort_table'} > 10) { + badprint "Sorts requiring temporary tables: $mycalc{'pct_temp_sort_table'}% (".hr_num($mystat{'Sort_merge_passes'})." temp sorts / ".hr_num($mycalc{'total_sorts'})." sorts)\n"; + push(@adjvars,"sort_buffer_size (> ".hr_bytes_rnd($myvar{'sort_buffer_size'}).")"); + push(@adjvars,"read_rnd_buffer_size (> ".hr_bytes_rnd($myvar{'read_rnd_buffer_size'}).")"); + } else { + goodprint "Sorts requiring temporary tables: $mycalc{'pct_temp_sort_table'}% (".hr_num($mystat{'Sort_merge_passes'})." temp sorts / ".hr_num($mycalc{'total_sorts'})." sorts)\n"; + } + + # Joins + if ($mycalc{'joins_without_indexes_per_day'} > 250) { + badprint "Joins performed without indexes: $mycalc{'joins_without_indexes'}\n"; + push(@adjvars,"join_buffer_size (> ".hr_bytes($myvar{'join_buffer_size'}).", or always use indexes with joins)"); + push(@generalrec,"Adjust your join queries to always utilize indexes"); + } else { + # For the sake of space, we will be quiet here + # No joins have run without indexes + } + + # Temporary tables + if ($mystat{'Created_tmp_tables'} > 0) { + if ($mycalc{'pct_temp_disk'} > 25 && $mycalc{'max_tmp_table_size'} < 256*1024*1024) { + badprint "Temporary tables created on disk: $mycalc{'pct_temp_disk'}% (".hr_num($mystat{'Created_tmp_disk_tables'})." on disk / ".hr_num($mystat{'Created_tmp_disk_tables'} + $mystat{'Created_tmp_tables'})." total)\n"; + push(@adjvars,"tmp_table_size (> ".hr_bytes_rnd($myvar{'tmp_table_size'}).")"); + push(@adjvars,"max_heap_table_size (> ".hr_bytes_rnd($myvar{'max_heap_table_size'}).")"); + push(@generalrec,"When making adjustments, make tmp_table_size/max_heap_table_size equal"); + push(@generalrec,"Reduce your SELECT DISTINCT queries without LIMIT clauses"); + } elsif ($mycalc{'pct_temp_disk'} > 25 && $mycalc{'max_tmp_table_size'} >= 256) { + badprint "Temporary tables created on disk: $mycalc{'pct_temp_disk'}% (".hr_num($mystat{'Created_tmp_disk_tables'})." on disk / ".hr_num($mystat{'Created_tmp_disk_tables'} + $mystat{'Created_tmp_tables'})." total)\n"; + push(@generalrec,"Temporary table size is already large - reduce result set size"); + push(@generalrec,"Reduce your SELECT DISTINCT queries without LIMIT clauses"); + } else { + goodprint "Temporary tables created on disk: $mycalc{'pct_temp_disk'}% (".hr_num($mystat{'Created_tmp_disk_tables'})." on disk / ".hr_num($mystat{'Created_tmp_disk_tables'} + $mystat{'Created_tmp_tables'})." total)\n"; + } + } else { + # For the sake of space, we will be quiet here + # No temporary tables have been created + } + + # Thread cache + if ($myvar{'thread_cache_size'} eq 0) { + badprint "Thread cache is disabled\n"; + push(@generalrec,"Set thread_cache_size to 4 as a starting value"); + push(@adjvars,"thread_cache_size (start at 4)"); + } else { + if ($mycalc{'thread_cache_hit_rate'} <= 50) { + badprint "Thread cache hit rate: $mycalc{'thread_cache_hit_rate'}% (".hr_num($mystat{'Threads_created'})." created / ".hr_num($mystat{'Connections'})." connections)\n"; + push(@adjvars,"thread_cache_size (> $myvar{'thread_cache_size'})"); + } else { + goodprint "Thread cache hit rate: $mycalc{'thread_cache_hit_rate'}% (".hr_num($mystat{'Threads_created'})." created / ".hr_num($mystat{'Connections'})." connections)\n"; + } + } + + # Table cache + if ($mystat{'Open_tables'} > 0) { + if ($mycalc{'table_cache_hit_rate'} < 20) { + badprint "Table cache hit rate: $mycalc{'table_cache_hit_rate'}% (".hr_num($mystat{'Open_tables'})." open / ".hr_num($mystat{'Opened_tables'})." opened)\n"; + if (mysql_version_ge(5, 1)) { + push(@adjvars,"table_cache (> ".$myvar{'table_open_cache'}.")"); + } else { + push(@adjvars,"table_cache (> ".$myvar{'table_cache'}.")"); + } + push(@generalrec,"Increase table_cache gradually to avoid file descriptor limits"); + } else { + goodprint "Table cache hit rate: $mycalc{'table_cache_hit_rate'}% (".hr_num($mystat{'Open_tables'})." open / ".hr_num($mystat{'Opened_tables'})." opened)\n"; + } + } + + # Open files + if (defined $mycalc{'pct_files_open'}) { + if ($mycalc{'pct_files_open'} > 85) { + badprint "Open file limit used: $mycalc{'pct_files_open'}% (".hr_num($mystat{'Open_files'})."/".hr_num($myvar{'open_files_limit'}).")\n"; + push(@adjvars,"open_files_limit (> ".$myvar{'open_files_limit'}.")"); + } else { + goodprint "Open file limit used: $mycalc{'pct_files_open'}% (".hr_num($mystat{'Open_files'})."/".hr_num($myvar{'open_files_limit'}).")\n"; + } + } + + # Table locks + if (defined $mycalc{'pct_table_locks_immediate'}) { + if ($mycalc{'pct_table_locks_immediate'} < 95) { + badprint "Table locks acquired immediately: $mycalc{'pct_table_locks_immediate'}%\n"; + push(@generalrec,"Optimize queries and/or use InnoDB to reduce lock wait"); + } else { + goodprint "Table locks acquired immediately: $mycalc{'pct_table_locks_immediate'}% (".hr_num($mystat{'Table_locks_immediate'})." immediate / ".hr_num($mystat{'Table_locks_waited'}+$mystat{'Table_locks_immediate'})." locks)\n"; + } + } + + # Performance options + if (!mysql_version_ge(4, 1)) { + push(@generalrec,"Upgrade to MySQL 4.1+ to use concurrent MyISAM inserts"); + } elsif ($myvar{'concurrent_insert'} eq "OFF") { + push(@generalrec,"Enable concurrent_insert by setting it to 'ON'"); + } elsif ($myvar{'concurrent_insert'} eq 0) { + push(@generalrec,"Enable concurrent_insert by setting it to 1"); + } + if ($mycalc{'pct_aborted_connections'} > 5) { + badprint "Connections aborted: ".$mycalc{'pct_aborted_connections'}."%\n"; + push(@generalrec,"Your applications are not closing MySQL connections properly"); + } + + # InnoDB + if (defined $myvar{'have_innodb'} && $myvar{'have_innodb'} eq "YES" && defined $enginestats{'InnoDB'}) { + if ($myvar{'innodb_buffer_pool_size'} > $enginestats{'InnoDB'}) { + goodprint "InnoDB data size / buffer pool: ".hr_bytes($enginestats{'InnoDB'})."/".hr_bytes($myvar{'innodb_buffer_pool_size'})."\n"; + } else { + badprint "InnoDB data size / buffer pool: ".hr_bytes($enginestats{'InnoDB'})."/".hr_bytes($myvar{'innodb_buffer_pool_size'})."\n"; + push(@adjvars,"innodb_buffer_pool_size (>= ".hr_bytes_rnd($enginestats{'InnoDB'}).")"); + } + } +} + +# Take the two recommendation arrays and display them at the end of the output +sub make_recommendations { + print "\n-------- Recommendations -----------------------------------------------------\n"; + if (@generalrec > 0) { + print "General recommendations:\n"; + foreach (@generalrec) { print " ".$_."\n"; } + } + if (@adjvars > 0) { + print "Variables to adjust:\n"; + if ($mycalc{'pct_physical_memory'} > 90) { + print " *** MySQL's maximum memory usage is dangerously high ***\n". + " *** Add RAM before increasing MySQL buffer variables ***\n"; + } + foreach (@adjvars) { print " ".$_."\n"; } + } + if (@generalrec == 0 && @adjvars ==0) { + print "No additional performance recommendations are available.\n" + } + print "\n"; +} + +# --------------------------------------------------------------------------- +# BEGIN 'MAIN' +# --------------------------------------------------------------------------- +print "\n >> MySQLTuner $tunerversion - Major Hayden \n". + " >> Bug reports, feature requests, and downloads at http://mysqltuner.com/\n". + " >> Run with '--help' for additional options and output filtering\n"; +mysql_setup; # Gotta login first +os_setup; # Set up some OS variables +get_all_vars; # Toss variables/status into hashes +validate_tuner_version; # Check current MySQLTuner version +validate_mysql_version; # Check current MySQL version +check_architecture; # Suggest 64-bit upgrade +check_storage_engines; # Show enabled storage engines +security_recommendations; # Display some security recommendations +calculations; # Calculate everything we need +mysql_stats; # Print the server stats +make_recommendations; # Make recommendations based on stats +# --------------------------------------------------------------------------- +# END 'MAIN' +# --------------------------------------------------------------------------- + +# Local variables: +# indent-tabs-mode: t +# cperl-indent-level: 8 +# perl-indent-level: 8 +# End: diff --git a/modules/mysql/lib/facter/mysql.rb b/modules/mysql/lib/facter/mysql.rb deleted file mode 100644 index ce1bedf..0000000 --- a/modules/mysql/lib/facter/mysql.rb +++ /dev/null @@ -1,8 +0,0 @@ -Facter.add("mysql_exists") do - ENV["PATH"]="/bin:/sbin:/usr/bin:/usr/sbin" - - setcode do - mysqlexists = system "which mysql >/dev/null 2>&1" - ($?.exitstatus == 0).to_s - end -end diff --git a/modules/mysql/lib/puppet/parser/functions/mysql_password.rb b/modules/mysql/lib/puppet/parser/functions/mysql_password.rb index 6443d95..74281b8 100644 --- a/modules/mysql/lib/puppet/parser/functions/mysql_password.rb +++ b/modules/mysql/lib/puppet/parser/functions/mysql_password.rb @@ -2,8 +2,14 @@ require 'digest/sha1' module Puppet::Parser::Functions - newfunction(:mysql_password, :type => :rvalue) do |args| - '*' + Digest::SHA1.hexdigest(Digest::SHA1.digest(args[0])).upcase - end -end + newfunction(:mysql_password, :type => :rvalue, :doc => <<-EOS + Returns the mysql password hash from the clear text password. + EOS + ) do |args| + + raise(Puppet::ParseError, "mysql_password(): Wrong number of arguments " + + "given (#{args.size} for 1)") if args.size != 1 + '*' + Digest::SHA1.hexdigest(Digest::SHA1.digest(args[0])).upcase + end +end diff --git a/modules/mysql/lib/puppet/provider/database/mysql.rb b/modules/mysql/lib/puppet/provider/database/mysql.rb new file mode 100644 index 0000000..30bc72e --- /dev/null +++ b/modules/mysql/lib/puppet/provider/database/mysql.rb @@ -0,0 +1,42 @@ +Puppet::Type.type(:database).provide(:mysql) do + + desc "Manages MySQL database." + + defaultfor :kernel => 'Linux' + + optional_commands :mysql => 'mysql' + optional_commands :mysqladmin => 'mysqladmin' + + def self.instances + mysql("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", '-NBe', "show databases").split("\n").collect do |name| + new(:name => name) + end + end + + def create + mysql("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", '-NBe', "create database `#{@resource[:name]}` character set #{resource[:charset]}") + end + + def destroy + mysqladmin("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", '-f', 'drop', @resource[:name]) + end + + def charset + mysql("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", '-NBe', "show create database `#{resource[:name]}`").match(/.*?(\S+)\s\*\//)[1] + end + + def charset=(value) + mysql("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", '-NBe', "alter database `#{resource[:name]}` CHARACTER SET #{value}") + end + + def exists? + begin + mysql("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", '-NBe', "show databases").match(/^#{@resource[:name]}$/) + rescue => e + debug(e.message) + return nil + end + end + +end + diff --git a/modules/mysql/lib/puppet/provider/database_grant/mysql.rb b/modules/mysql/lib/puppet/provider/database_grant/mysql.rb new file mode 100644 index 0000000..8651539 --- /dev/null +++ b/modules/mysql/lib/puppet/provider/database_grant/mysql.rb @@ -0,0 +1,198 @@ +# A grant is either global or per-db. This can be distinguished by the syntax +# of the name: +# user@host => global +# user@host/db => per-db + +Puppet::Type.type(:database_grant).provide(:mysql) do + + desc "Uses mysql as database." + + defaultfor :kernel => 'Linux' + + optional_commands :mysql => 'mysql' + optional_commands :mysqladmin => 'mysqladmin' + + def self.prefetch(resources) + @user_privs = query_user_privs + @db_privs = query_db_privs + end + + def self.user_privs + @user_privs || query_user_privs + end + + def self.db_privs + @db_privs || query_db_privs + end + + def user_privs + self.class.user_privs + end + + def db_privs + self.class.db_privs + end + + def self.query_user_privs + results = mysql("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", "-Be", "describe user") + column_names = results.split(/\n/).map { |l| l.chomp.split(/\t/)[0] } + @user_privs = column_names.delete_if { |e| !( e =~/_priv$/) } + end + + def self.query_db_privs + results = mysql("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", "-Be", "describe db") + column_names = results.split(/\n/).map { |l| l.chomp.split(/\t/)[0] } + @db_privs = column_names.delete_if { |e| !(e =~/_priv$/) } + end + + def mysql_flush + mysqladmin "--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "flush-privileges" + end + + # this parses the + def split_name(string) + matches = /^([^@]*)@([^\/]*)(\/(.*))?$/.match(string).captures.compact + case matches.length + when 2 + { + :type => :user, + :user => matches[0], + :host => matches[1] + } + when 4 + { + :type => :db, + :user => matches[0], + :host => matches[1], + :db => matches[3] + } + end + end + + def create_row + unless @resource.should(:privileges).empty? + name = split_name(@resource[:name]) + case name[:type] + when :user + mysql "--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", "-e", "INSERT INTO user (host, user) VALUES ('%s', '%s')" % [ + name[:host], name[:user], + ] + when :db + mysql "--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", "-e", "INSERT INTO db (host, user, db) VALUES ('%s', '%s', '%s')" % [ + name[:host], name[:user], name[:db], + ] + end + mysql_flush + end + end + + def destroy + mysql "--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", "-e", "REVOKE ALL ON '%s'.* FROM '%s@%s'" % [ @resource[:privileges], @resource[:database], @resource[:name], @resource[:host] ] + end + + def row_exists? + name = split_name(@resource[:name]) + fields = [:user, :host] + if name[:type] == :db + fields << :db + end + not mysql("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", '-NBe', 'SELECT "1" FROM %s WHERE %s' % [ name[:type], fields.map do |f| "%s=\"%s\"" % [f, name[f]] end.join(' AND ')]).empty? + end + + def all_privs_set? + all_privs = case split_name(@resource[:name])[:type] + when :user + user_privs + when :db + db_privs + end + all_privs = all_privs.collect do |p| p.downcase end.sort.join("|") + privs = privileges.collect do |p| p.downcase end.sort.join("|") + + all_privs == privs + end + + def privileges + name = split_name(@resource[:name]) + privs = "" + + case name[:type] + when :user + privs = mysql "--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", "-Be", 'select * from mysql.user where user="%s" and host="%s"' % [ name[:user], name[:host] ] + when :db + privs = mysql "--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", "-Be", 'select * from mysql.db where user="%s" and host="%s" and db="%s"' % [ name[:user], name[:host], name[:db] ] + end + + if privs.match(/^$/) + privs = [] # no result, no privs + else + # returns a line with field names and a line with values, each tab-separated + privs = privs.split(/\n/).map! do |l| l.chomp.split(/\t/) end + # transpose the lines, so we have key/value pairs + privs = privs[0].zip(privs[1]) + privs = privs.select do |p| p[0].match(/_priv$/) and p[1] == 'Y' end + end + + privs.collect do |p| p[0] end + end + + def privileges=(privs) + unless row_exists? + create_row + end + + # puts "Setting privs: ", privs.join(", ") + name = split_name(@resource[:name]) + stmt = '' + where = '' + all_privs = [] + case name[:type] + when :user + stmt = 'update user set ' + where = ' where user="%s" and host="%s"' % [ name[:user], name[:host] ] + all_privs = user_privs + when :db + stmt = 'update db set ' + where = ' where user="%s" and host="%s" and db="%s"' % [ name[:user], name[:host], name[:db] ] + all_privs = db_privs + end + + if privs[0].downcase == 'all' + privs = all_privs + end + + # Downcase the requested priviliges for case-insensitive selection + # we don't map! here because the all_privs object has to remain in + # the same case the DB gave it to us in + privs = privs.map { |p| p.downcase } + + # puts "stmt:", stmt + set = all_privs.collect do |p| "%s = '%s'" % [p, privs.include?(p.downcase) ? 'Y' : 'N'] end.join(', ') + # puts "set:", set + stmt = stmt << set << where + + validate_privs privs, all_privs + mysql "--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", "-Be", stmt + mysql_flush + end + + def validate_privs(set_privs, all_privs) + all_privs = all_privs.collect { |p| p.downcase } + set_privs = set_privs.collect { |p| p.downcase } + invalid_privs = Array.new + hints = Array.new + # Test each of the user provided privs to see if they exist in all_privs + set_privs.each do |priv| + invalid_privs << priv unless all_privs.include?(priv) + hints << "#{priv}_priv" if all_privs.include?("#{priv}_priv") + end + unless invalid_privs.empty? + # Print a decently helpful and gramatically correct error message + hints = "Did you mean '#{hints.join(',')}'?" unless hints.empty? + p = invalid_privs.size > 1 ? ['s', 'are not valid'] : ['', 'is not valid'] + detail = ["The privilege#{p[0]} '#{invalid_privs.join(',')}' #{p[1]}."] + fail [detail, hints].join(' ') + end + end + +end diff --git a/modules/mysql/lib/puppet/provider/database_user/mysql.rb b/modules/mysql/lib/puppet/provider/database_user/mysql.rb new file mode 100644 index 0000000..387cbba --- /dev/null +++ b/modules/mysql/lib/puppet/provider/database_user/mysql.rb @@ -0,0 +1,42 @@ +Puppet::Type.type(:database_user).provide(:mysql) do + + desc "manage users for a mysql database." + + defaultfor :kernel => 'Linux' + + optional_commands :mysql => 'mysql' + optional_commands :mysqladmin => 'mysqladmin' + + def self.instances + users = mysql("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", '-BNe' "select concat(User, '@',Host) as User from mysql.user").split("\n") + users.select{ |user| user =~ /.+@/ }.collect do |name| + new(:name => name) + end + end + + def create + mysql("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", "-e", "create user '%s' identified by PASSWORD '%s'" % [ @resource[:name].sub("@", "'@'"), @resource.value(:password_hash) ]) + end + + def destroy + mysql("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", "-e", "drop user '%s'" % @resource.value(:name).sub("@", "'@'") ) + end + + def password_hash + mysql("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", "-NBe", "select password from mysql.user where CONCAT(user, '@', host) = '%s'" % @resource.value(:name)).chomp + end + + def password_hash=(string) + mysql("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", "-e", "SET PASSWORD FOR '%s' = '%s'" % [ @resource[:name].sub("@", "'@'"), string ] ) + end + + def exists? + not mysql("--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "mysql", "-NBe", "select '1' from mysql.user where CONCAT(user, '@', host) = '%s'" % @resource.value(:name)).empty? + end + + def flush + @property_hash.clear + mysqladmin "--defaults-file=#{Facter.value(:root_home)}/.my.cnf", "flush-privileges" + end + +end diff --git a/modules/mysql/lib/puppet/provider/mysql_database/mysql.rb b/modules/mysql/lib/puppet/provider/mysql_database/mysql.rb deleted file mode 100644 index 2b70e04..0000000 --- a/modules/mysql/lib/puppet/provider/mysql_database/mysql.rb +++ /dev/null @@ -1,55 +0,0 @@ -require 'puppet/provider/package' - -Puppet::Type.type(:mysql_database).provide(:mysql, - :parent => Puppet::Provider::Package) do - - desc "Use mysql as database." - commands :mysqladmin => '/usr/bin/mysqladmin' - commands :mysql => '/usr/bin/mysql' - - # retrieve the current set of mysql users - def self.instances - dbs = [] - - cmd = "#{command(:mysql)} mysql -NBe 'show databases'" - execpipe(cmd) do |process| - process.each do |line| - dbs << new( { :ensure => :present, :name => line.chomp } ) - end - end - return dbs - end - - def query - result = { - :name => @resource[:name], - :ensure => :absent - } - - cmd = "#{command(:mysql)} mysql -NBe 'show databases'" - execpipe(cmd) do |process| - process.each do |line| - if line.chomp.eql?(@resource[:name]) - result[:ensure] = :present - end - end - end - result - end - - def create - mysqladmin "create", @resource[:name] - end - def destroy - mysqladmin "-f", "drop", @resource[:name] - end - - def exists? - if mysql("mysql", "-NBe", "show databases").match(/^#{@resource[:name]}$/) - true - else - false - end - end -end - diff --git a/modules/mysql/lib/puppet/provider/mysql_grant/mysql.rb b/modules/mysql/lib/puppet/provider/mysql_grant/mysql.rb deleted file mode 100644 index 61c32d9..0000000 --- a/modules/mysql/lib/puppet/provider/mysql_grant/mysql.rb +++ /dev/null @@ -1,155 +0,0 @@ -# A grant is either global or per-db. This can be distinguished by the syntax -# of the name: -# user@host => global -# user@host/db => per-db - -require 'puppet/provider/package' - -MYSQL_USER_PRIVS = [ :select_priv, :insert_priv, :update_priv, :delete_priv, - :create_priv, :drop_priv, :reload_priv, :shutdown_priv, :process_priv, - :file_priv, :grant_priv, :references_priv, :index_priv, :alter_priv, - :show_db_priv, :super_priv, :create_tmp_table_priv, :lock_tables_priv, - :execute_priv, :repl_slave_priv, :repl_client_priv, :create_view_priv, - :show_view_priv, :create_routine_priv, :alter_routine_priv, - :create_user_priv -] - -MYSQL_DB_PRIVS = [ :select_priv, :insert_priv, :update_priv, :delete_priv, - :create_priv, :drop_priv, :grant_priv, :references_priv, :index_priv, - :alter_priv, :create_tmp_table_priv, :lock_tables_priv, :create_view_priv, - :show_view_priv, :create_routine_priv, :alter_routine_priv, :execute_priv -] - -Puppet::Type.type(:mysql_grant).provide(:mysql) do - - desc "Uses mysql as database." - - commands :mysql => '/usr/bin/mysql' - commands :mysqladmin => '/usr/bin/mysqladmin' - - def mysql_flush - mysqladmin "flush-privileges" - end - - # this parses the - def split_name(string) - matches = /^([^@]*)@([^\/]*)(\/(.*))?$/.match(string).captures.compact - case matches.length - when 2 - { - :type => :user, - :user => matches[0], - :host => matches[1] - } - when 4 - { - :type => :db, - :user => matches[0], - :host => matches[1], - :db => matches[3] - } - end - end - - def create_row - unless @resource.should(:privileges).empty? - name = split_name(@resource[:name]) - case name[:type] - when :user - mysql "mysql", "-e", "INSERT INTO user (host, user) VALUES ('%s', '%s')" % [ - name[:host], name[:user], - ] - when :db - mysql "mysql", "-e", "INSERT INTO db (host, user, db) VALUES ('%s', '%s', '%s')" % [ - name[:host], name[:user], name[:db], - ] - end - mysql_flush - end - end - - def destroy - mysql "mysql", "-e", "REVOKE ALL ON '%s'.* FROM '%s@%s'" % [ @resource[:privileges], @resource[:database], @resource[:name], @resource[:host] ] - end - - def row_exists? - name = split_name(@resource[:name]) - fields = [:user, :host] - if name[:type] == :db - fields << :db - end - not mysql( "mysql", "-NBe", 'SELECT "1" FROM %s WHERE %s' % [ name[:type], fields.map do |f| "%s = '%s'" % [f, name[f]] end.join(' AND ')]).empty? - end - - def all_privs_set? - all_privs = case split_name(@resource[:name])[:type] - when :user - MYSQL_USER_PRIVS - when :db - MYSQL_DB_PRIVS - end - all_privs = all_privs.collect do |p| p.to_s end.sort.join("|") - privs = privileges.collect do |p| p.to_s end.sort.join("|") - - all_privs == privs - end - - def privileges - name = split_name(@resource[:name]) - privs = "" - - case name[:type] - when :user - privs = mysql "mysql", "-Be", 'select * from user where user="%s" and host="%s"' % [ name[:user], name[:host] ] - when :db - privs = mysql "mysql", "-Be", 'select * from db where user="%s" and host="%s" and db="%s"' % [ name[:user], name[:host], name[:db] ] - end - - if privs.match(/^$/) - privs = [] # no result, no privs - else - # returns a line with field names and a line with values, each tab-separated - privs = privs.split(/\n/).map! do |l| l.chomp.split(/\t/) end - # transpose the lines, so we have key/value pairs - privs = privs[0].zip(privs[1]) - privs = privs.select do |p| p[0].match(/_priv$/) and p[1] == 'Y' end - end - - privs.collect do |p| symbolize(p[0].downcase) end - end - - def privileges=(privs) - unless row_exists? - create_row - end - - # puts "Setting privs: ", privs.join(", ") - name = split_name(@resource[:name]) - stmt = '' - where = '' - all_privs = [] - case name[:type] - when :user - stmt = 'update user set ' - where = ' where user="%s" and host="%s"' % [ name[:user], name[:host] ] - all_privs = MYSQL_USER_PRIVS - when :db - stmt = 'update db set ' - where = ' where user="%s" and host="%s"' % [ name[:user], name[:host] ] - all_privs = MYSQL_DB_PRIVS - end - - if privs[0] == :all - privs = all_privs - end - - # puts "stmt:", stmt - set = all_privs.collect do |p| "%s = '%s'" % [p, privs.include?(p) ? 'Y' : 'N'] end.join(', ') - # puts "set:", set - stmt = stmt << set << where - - mysql "mysql", "-Be", stmt - mysql_flush - end -end - diff --git a/modules/mysql/lib/puppet/provider/mysql_user/mysql.rb b/modules/mysql/lib/puppet/provider/mysql_user/mysql.rb deleted file mode 100644 index adc46c3..0000000 --- a/modules/mysql/lib/puppet/provider/mysql_user/mysql.rb +++ /dev/null @@ -1,76 +0,0 @@ -require 'puppet/provider/package' - -Puppet::Type.type(:mysql_user).provide(:mysql, - # T'is funny business, this code is quite generic - :parent => Puppet::Provider::Package) do - - desc "Use mysql as database." - commands :mysql => '/usr/bin/mysql' - commands :mysqladmin => '/usr/bin/mysqladmin' - - # retrieve the current set of mysql users - def self.instances - users = [] - - cmd = "#{command(:mysql)} mysql -NBe 'select concat(user, \"@\", host), password from user'" - execpipe(cmd) do |process| - process.each do |line| - users << new( query_line_to_hash(line) ) - end - end - return users - end - - def self.query_line_to_hash(line) - fields = line.chomp.split(/\t/) - { - :name => fields[0], - :password_hash => fields[1], - :ensure => :present - } - end - - def mysql_flush - mysqladmin "flush-privileges" - end - - def query - result = {} - - cmd = "#{command(:mysql)} -NBe 'select concat(user, \"@\", host), password from user where concat(user, \"@\", host) = \"%s\"'" % @resource[:name] - execpipe(cmd) do |process| - process.each do |line| - unless result.empty? - raise Puppet::Error, - "Got multiple results for user '%s'" % @resource[:name] - end - result = query_line_to_hash(line) - end - end - result - end - - def create - mysql "mysql", "-e", "create user '%s' identified by PASSWORD '%s'" % [ @resource[:name].sub("@", "'@'"), @resource.should(:password_hash) ] - mysql_flush - end - - def destroy - mysql "mysql", "-e", "drop user '%s'" % @resource[:name].sub("@", "'@'") - mysql_flush - end - - def exists? - not mysql("mysql", "-NBe", "select '1' from user where CONCAT(user, '@', host) = '%s'" % @resource[:name]).empty? - end - - def password_hash - @property_hash[:password_hash] - end - - def password_hash=(string) - mysql "mysql", "-e", "SET PASSWORD FOR '%s' = '%s'" % [ @resource[:name].sub("@", "'@'"), string ] - mysql_flush - end -end - diff --git a/modules/mysql/lib/puppet/type/database.rb b/modules/mysql/lib/puppet/type/database.rb new file mode 100644 index 0000000..a27fc00 --- /dev/null +++ b/modules/mysql/lib/puppet/type/database.rb @@ -0,0 +1,17 @@ +# This has to be a separate type to enable collecting +Puppet::Type.newtype(:database) do + @doc = "Manage databases." + + ensurable + + newparam(:name, :namevar=>true) do + desc "The name of the database." + end + + newproperty(:charset) do + desc "The characterset to use for a database" + defaultto :utf8 + newvalue(/^\S+$/) + end + +end diff --git a/modules/mysql/lib/puppet/type/database_grant.rb b/modules/mysql/lib/puppet/type/database_grant.rb new file mode 100644 index 0000000..965695b --- /dev/null +++ b/modules/mysql/lib/puppet/type/database_grant.rb @@ -0,0 +1,75 @@ +# This has to be a separate type to enable collecting +Puppet::Type.newtype(:database_grant) do + @doc = "Manage a database user's rights." + #ensurable + + autorequire :database do + # puts "Starting db autoreq for %s" % self[:name] + reqs = [] + matches = self[:name].match(/^([^@]+)@([^\/]+)\/(.+)$/) + unless matches.nil? + reqs << matches[3] + end + # puts "Autoreq: '%s'" % reqs.join(" ") + reqs + end + + autorequire :database_user do + # puts "Starting user autoreq for %s" % self[:name] + reqs = [] + matches = self[:name].match(/^([^@]+)@([^\/]+).*$/) + unless matches.nil? + reqs << "%s@%s" % [ matches[1], matches[2] ] + end + # puts "Autoreq: '%s'" % reqs.join(" ") + reqs + end + + newparam(:name, :namevar=>true) do + desc "The primary key: either user@host for global privilges or user@host/database for database specific privileges" + end + + newproperty(:privileges, :array_matching => :all) do + desc "The privileges the user should have. The possible values are implementation dependent." + + def should_to_s(newvalue = @should) + if newvalue + unless newvalue.is_a?(Array) + newvalue = [ newvalue ] + end + newvalue.collect do |v| v.downcase end.sort.join ", " + else + nil + end + end + + def is_to_s(currentvalue = @is) + if currentvalue + unless currentvalue.is_a?(Array) + currentvalue = [ currentvalue ] + end + currentvalue.collect do |v| v.downcase end.sort.join ", " + else + nil + end + end + + # use the sorted outputs for comparison + def insync?(is) + if defined? @should and @should + case self.should_to_s + when "all" + self.provider.all_privs_set? + when self.is_to_s(is) + true + else + false + end + else + true + end + end + end + +end + diff --git a/modules/mysql/lib/puppet/type/database_user.rb b/modules/mysql/lib/puppet/type/database_user.rb new file mode 100644 index 0000000..23af104 --- /dev/null +++ b/modules/mysql/lib/puppet/type/database_user.rb @@ -0,0 +1,25 @@ +# This has to be a separate type to enable collecting +Puppet::Type.newtype(:database_user) do + @doc = "Manage a database user. This includes management of users password as well as priveleges" + + ensurable + + newparam(:name, :namevar=>true) do + desc "The name of the user. This uses the 'username@hostname' or username@hostname." + validate do |value| + # https://dev.mysql.com/doc/refman/5.1/en/account-names.html + # Regex should problably be more like this: /^[`'"]?[^`'"]*[`'"]?@[`'"]?[\w%\.]+[`'"]?$/ + raise(ArgumentError, "Invalid database user #{value}") unless value =~ /[\w-]*@[\w%\.:]+/ + username = value.split('@')[0] + if username.size > 16 + raise ArgumentError, "MySQL usernames are limited to a maximum of 16 characters" + end + end + end + + newproperty(:password_hash) do + desc "The password hash of the user. Use mysql_password() for creating such a hash." + newvalue(/\w+/) + end + +end diff --git a/modules/mysql/lib/puppet/type/mysql_database.rb b/modules/mysql/lib/puppet/type/mysql_database.rb deleted file mode 100644 index bb25ffa..0000000 --- a/modules/mysql/lib/puppet/type/mysql_database.rb +++ /dev/null @@ -1,11 +0,0 @@ -# This has to be a separate type to enable collecting -Puppet::Type.newtype(:mysql_database) do - @doc = "Manage a database." - ensurable - newparam(:name) do - desc "The name of the database." - - # TODO: only [[:alnum:]_] allowed - end -end - diff --git a/modules/mysql/lib/puppet/type/mysql_grant.rb b/modules/mysql/lib/puppet/type/mysql_grant.rb deleted file mode 100644 index 415f5aa..0000000 --- a/modules/mysql/lib/puppet/type/mysql_grant.rb +++ /dev/null @@ -1,77 +0,0 @@ -# This has to be a separate type to enable collecting -Puppet::Type.newtype(:mysql_grant) do - @doc = "Manage a database user's rights." - #ensurable - - autorequire :mysql_db do - # puts "Starting db autoreq for %s" % self[:name] - reqs = [] - matches = self[:name].match(/^([^@]+)@([^\/]+)\/(.+)$/) - unless matches.nil? - reqs << matches[3] - end - # puts "Autoreq: '%s'" % reqs.join(" ") - reqs - end - - autorequire :mysql_user do - # puts "Starting user autoreq for %s" % self[:name] - reqs = [] - matches = self[:name].match(/^([^@]+)@([^\/]+).*$/) - unless matches.nil? - reqs << "%s@%s" % [ matches[1], matches[2] ] - end - # puts "Autoreq: '%s'" % reqs.join(" ") - reqs - end - - newparam(:name) do - desc "The primary key: either user@host for global privilges or user@host/database for database specific privileges" - end - newproperty(:privileges, :array_matching => :all) do - desc "The privileges the user should have. The possible values are implementation dependent." - munge do |v| - symbolize(v) - end - - def should_to_s(newvalue = @should) - if newvalue - unless newvalue.is_a?(Array) - newvalue = [ newvalue ] - end - newvalue.collect do |v| v.to_s end.sort.join ", " - else - nil - end - end - - def is_to_s(currentvalue = @is) - if currentvalue - unless currentvalue.is_a?(Array) - currentvalue = [ currentvalue ] - end - currentvalue.collect do |v| v.to_s end.sort.join ", " - else - nil - end - end - - # use the sorted outputs for comparison - def insync?(is) - if defined? @should and @should - case self.should_to_s - when "all" - self.provider.all_privs_set? - when self.is_to_s(is) - true - else - false - end - else - true - end - end - - end -end - diff --git a/modules/mysql/lib/puppet/type/mysql_user.rb b/modules/mysql/lib/puppet/type/mysql_user.rb deleted file mode 100644 index 55d97b6..0000000 --- a/modules/mysql/lib/puppet/type/mysql_user.rb +++ /dev/null @@ -1,22 +0,0 @@ -# This has to be a separate type to enable collecting -Puppet::Type.newtype(:mysql_user) do - @doc = "Manage a database user." - ensurable - newparam(:name) do - desc "The name of the user. This uses the 'username@hostname' form." - - validate do |value| - if value.split('@').first.size > 16 - raise ArgumentError, - "MySQL usernames are limited to a maximum of 16 characters" - else - super - end - end - end - - newproperty(:password_hash) do - desc "The password hash of the user. Use mysql_password() for creating such a hash." - end -end - diff --git a/modules/mysql/manifests/administration.pp b/modules/mysql/manifests/administration.pp deleted file mode 100644 index d5d6be4..0000000 --- a/modules/mysql/manifests/administration.pp +++ /dev/null @@ -1,16 +0,0 @@ -class mysql::administration { - - include mysql::common - -# TODO: -# - permissions to edit my.cnf once augeas bug is corrected (see -# modules/cognac/manifests/classes/mysql-slave.pp) -# - .my.cnf for people in %mysql-admin - - sudo::directive { "mysql-administration": - ensure => present, - content => template("mysql/sudoers.mysql.erb"), - require => Group["mysql-admin"], - } - -} diff --git a/modules/mysql/manifests/backup.pp b/modules/mysql/manifests/backup.pp index 7b85c14..7dfffca 100644 --- a/modules/mysql/manifests/backup.pp +++ b/modules/mysql/manifests/backup.pp @@ -1,49 +1,71 @@ -/* -== Class: mysql::backup +# Class: mysql::backup +# +# This module handles ... +# +# Parameters: +# [*backupuser*] - The name of the mysql backup user. +# [*backuppassword*] - The password of the mysql backup user. +# [*backupdir*] - The target directory of the mysqldump. +# [*backupcompress*] - Boolean to compress backup with bzip2. +# +# Actions: +# GRANT SELECT, RELOAD, LOCK TABLES ON *.* TO 'user'@'localhost' +# IDENTIFIED BY 'password'; +# +# Requires: +# Class['mysql::config'] +# +# Sample Usage: +# class { 'mysql::backup': +# backupuser => 'myuser', +# backuppassword => 'mypassword', +# backupdir => '/tmp/backups', +# backupcompress => true, +# } +# +class mysql::backup ( + $backupuser, + $backuppassword, + $backupdir, + $backupcompress = true, + $ensure = 'present' +) { -Enable mysql daily backup script. - -The script /usr/local/bin/mysql-backup.sh will be run every night. It runs -mysqldump --all-databases. Backups will be stored in /var/backups/mysql. - -Attributes: -- $mysqldump_retention: defines if backup rotate on a weekly, monthly or yearly - basis. Accepted values: "week", "month", "year". Defaults to "week". -$subversion_backupdir = "/var/backups/subversion" - -*/ -class mysql::backup { - - include mysql::common - include mysql::params - - if $mysqldump_retention {} else { $mysqldump_retention = "week" } - - $data_dir = $mysql::params::data_dir - $backup_dir = $mysql::params::backup_dir + database_user { "${backupuser}@localhost": + ensure => $ensure, + password_hash => mysql_password($backuppassword), + provider => 'mysql', + require => Class['mysql::config'], + } - file { "${backup_dir}": - ensure => directory, - owner => "root", - group => "mysql-admin", - mode => 750, - require => Group["mysql-admin"] + database_grant { "${backupuser}@localhost": + privileges => [ 'Select_priv', 'Reload_priv', 'Lock_tables_priv', 'Show_view_priv' ], + require => Database_user["${backupuser}@localhost"], } - file { "/usr/local/bin/mysql-backup.sh": - ensure => present, - content => template("mysql/mysql-backup.sh.erb"), - owner => "root", - group => "root", - mode => 555, + cron { 'mysql-backup': + ensure => $ensure, + command => '/usr/local/sbin/mysqlbackup.sh', + user => 'root', + hour => 23, + minute => 5, + require => File['mysqlbackup.sh'], } - cron { "mysql-backup": - command => "/usr/local/bin/mysql-backup.sh ${mysqldump_retention}", - user => "root", - hour => 2, - minute => 0, - require => [File["${backup_dir}"], File["/usr/local/bin/mysql-backup.sh"]], + file { 'mysqlbackup.sh': + ensure => $ensure, + path => '/usr/local/sbin/mysqlbackup.sh', + mode => '0700', + owner => 'root', + group => 'root', + content => template('mysql/mysqlbackup.sh.erb'), } + file { 'mysqlbackupdir': + ensure => 'directory', + path => $backupdir, + mode => '0700', + owner => 'root', + group => 'root', + } } diff --git a/modules/mysql/manifests/client.pp b/modules/mysql/manifests/client.pp deleted file mode 100644 index 3b28ab2..0000000 --- a/modules/mysql/manifests/client.pp +++ /dev/null @@ -1,16 +0,0 @@ -# -# Class mysql::client -# -# Installs mysql client utilities such as mysqldump, mysqladmin, the "mysql" -# interactive shell, etc. -# -class mysql::client { - - package { "mysql-client": - ensure => present, - name => $operatingsystem ? { - /Debian|Ubuntu|kFreeBSD/ => "mysql-client", - /RedHat|Fedora|CentOS/ => "mysql", - }, - } -} diff --git a/modules/mysql/manifests/common.pp b/modules/mysql/manifests/common.pp deleted file mode 100644 index 185a699..0000000 --- a/modules/mysql/manifests/common.pp +++ /dev/null @@ -1,14 +0,0 @@ -/* -** class mysql::common -** -** this class should be included to use -** any mysql resource. -** -*/ -class mysql::common { - - group { 'mysql-admin': - ensure => present, - } - -} diff --git a/modules/mysql/manifests/config.pp b/modules/mysql/manifests/config.pp index e048b40..dfe7efa 100644 --- a/modules/mysql/manifests/config.pp +++ b/modules/mysql/manifests/config.pp @@ -1,64 +1,138 @@ -/* +# Class: mysql::config +# +# Parameters: +# +# [*root_password*] - root user password. +# [*old_root_password*] - previous root user password, +# [*bind_address*] - address to bind service. +# [*port*] - port to bind service. +# [*etc_root_password*] - whether to save /etc/my.cnf. +# [*service_name*] - mysql service name. +# [*config_file*] - my.cnf configuration file path. +# [*socket*] - mysql socket. +# [*datadir*] - path to datadir. +# [*ssl] - enable ssl +# [*ssl_ca] - path to ssl-ca +# [*ssl_cert] - path to ssl-cert +# [*ssl_key] - path to ssl-key +# [*log_error] - path to mysql error log +# [*default_engine] - configure a default table engine +# [*root_group] - use specified group for root-owned files +# [*restart] - whether to restart mysqld (true/false) +# +# Actions: +# +# Requires: +# +# class mysql::server +# +# Usage: +# +# class { 'mysql::config': +# root_password => 'changeme', +# bind_address => $::ipaddress, +# } +# +class mysql::config( + $root_password = 'UNSET', + $old_root_password = '', + $bind_address = $mysql::params::bind_address, + $port = $mysql::params::port, + $etc_root_password = $mysql::params::etc_root_password, + $service_name = $mysql::params::service_name, + $config_file = $mysql::params::config_file, + $socket = $mysql::params::socket, + $pidfile = $mysql::params::pidfile, + $datadir = $mysql::params::datadir, + $ssl = $mysql::params::ssl, + $ssl_ca = $mysql::params::ssl_ca, + $ssl_cert = $mysql::params::ssl_cert, + $ssl_key = $mysql::params::ssl_key, + $log_error = $mysql::params::log_error, + $default_engine = 'UNSET', + $root_group = $mysql::params::root_group, + $restart = $mysql::params::restart +) inherits mysql::params { -== Definition: mysql::config - -Set mysql configuration parameters - -Parameters: -- *value*: the value to be set, defaults to the empty string; -- *key*: optionally, set the key (defaults to $name); -- *ensure*: defaults to present. - -Example usage: - mysql::config {'mysqld/pid-file': - ensure => present, - value => '/var/run/mysqld/mysqld.pid', + File { + owner => 'root', + group => $root_group, + mode => '0400', + notify => $restart ? { + true => Exec['mysqld-restart'], + false => undef, + }, } -If the section (e.g. 'mysqld/') is ommitted in the resource name, -it defaults to 'mysqld/'. + if $ssl and $ssl_ca == undef { + fail('The ssl_ca parameter is required when ssl is true') + } -*/ -define mysql::config ( - $ensure='present', - $value='', - $key='' -) { + if $ssl and $ssl_cert == undef { + fail('The ssl_cert parameter is required when ssl is true') + } - $real_name = $key ? { - '' => $name, - default => $key, + if $ssl and $ssl_key == undef { + fail('The ssl_key parameter is required when ssl is true') } - $real_key = inline_template("<%= real_name.split('/')[-1] %>") + # This kind of sucks, that I have to specify a difference resource for + # restart. the reason is that I need the service to be started before mods + # to the config file which can cause a refresh + exec { 'mysqld-restart': + command => "service ${service_name} restart", + logoutput => on_failure, + refreshonly => true, + path => '/sbin/:/usr/sbin/:/usr/bin/:/bin/', + } - $section = inline_template("<%= if real_name.split('/')[-2] - real_name.split('/')[-2] - else - 'mysqld' - end %>") + # manage root password if it is set + if $root_password != 'UNSET' { + case $old_root_password { + '': { $old_pw='' } + default: { $old_pw="-p'${old_root_password}'" } + } - case $ensure { - present: { - $changes = "set ${real_key} ${value}" + exec { 'set_mysql_rootpw': + command => "mysqladmin -u root ${old_pw} password '${root_password}'", + logoutput => true, + unless => "mysqladmin -u root -p'${root_password}' status > /dev/null", + path => '/usr/local/sbin:/usr/bin:/usr/local/bin', + notify => $restart ? { + true => Exec['mysqld-restart'], + false => undef, + }, + require => File['/etc/mysql/conf.d'], } - absent: { - $changes = "rm ${real_key}" + file { '/root/.my.cnf': + content => template('mysql/my.cnf.pass.erb'), + require => Exec['set_mysql_rootpw'], } - default: { err ( "unknown ensure value ${ensure}" ) } + if $etc_root_password { + file{ '/etc/my.cnf': + content => template('mysql/my.cnf.pass.erb'), + require => Exec['set_mysql_rootpw'], + } + } + } else { + file { '/root/.my.cnf': + ensure => present, + } } - augeas { "my.cnf/${section}/${name}": - context => "${mysql::params::mycnfctx}/target[.='${section}']", - changes => [ - "set ${mysql::params::mycnfctx}/target[.='${section}'] ${section}", - $changes, - "rm ${mysql::params::mycnfctx}/target[count(*)=0]", - ], - require => [ File["${mysql::params::mycnf}"], - File["${mysql::params::data_dir}"] ], - notify => Service["mysql"], + file { '/etc/mysql': + ensure => directory, + mode => '0755', } + file { '/etc/mysql/conf.d': + ensure => directory, + mode => '0755', + } + file { $config_file: + content => template('mysql/my.cnf.erb'), + mode => '0644', + } + } diff --git a/modules/mysql/manifests/config/client.pp b/modules/mysql/manifests/config/client.pp deleted file mode 100644 index 73dffcd..0000000 --- a/modules/mysql/manifests/config/client.pp +++ /dev/null @@ -1,9 +0,0 @@ -class mysql::config::client { - mysql::config { - 'client/socket': - value => $::operatingsystem ? { - /RedHat|Fedora|CentOS/ => '/var/lib/mysql/mysql.sock', - default => '/var/run/mysqld/mysqld.sock', - } - } -} diff --git a/modules/mysql/manifests/config/mysqld.pp b/modules/mysql/manifests/config/mysqld.pp deleted file mode 100644 index b1ba34d..0000000 --- a/modules/mysql/manifests/config/mysqld.pp +++ /dev/null @@ -1,22 +0,0 @@ -class mysql::config::mysqld { - mysql::config { - 'pid-file': value => '/var/run/mysqld/mysqld.pid'; - 'old_passwords': value => '0'; - 'character-set-server': value => 'utf8'; - 'log-warnings': value => '1'; - 'datadir': value => "${mysql::params::data_dir}"; - 'log-error': value => $::operatingsystem ? { - /RedHat|Fedora|CentOS/ => '/var/log/mysqld.log', - default => '/var/log/mysql.err', - }; - 'log-slow-queries': value => $::operatingsystem ? { - /RedHat|Fedora|CentOS/ => '/var/log/mysql-slow-queries.log', - default => '/var/log/mysql/mysql-slow.log', - }; - # "ins log-slow-admin-statements after log-slow-queries", # BUG: not implemented in puppet yet - 'socket': value => $::operatingsystem ? { - /RedHat|Fedora|CentOS/ => '/var/lib/mysql/mysql.sock', - default => '/var/run/mysqld/mysqld.sock', - }; - } -} diff --git a/modules/mysql/manifests/config/mysqld_safe.pp b/modules/mysql/manifests/config/mysqld_safe.pp deleted file mode 100644 index 738e579..0000000 --- a/modules/mysql/manifests/config/mysqld_safe.pp +++ /dev/null @@ -1,12 +0,0 @@ -class mysql::config::mysqld_safe { - # mysqld_safe - mysql::config { - 'mysqld_safe/pid-file': - value => '/var/run/mysqld/mysqld.pid'; - 'mysqld_safe/socket': - value => $::operatingsystem ? { - /RedHat|Fedora|CentOS/ => '/var/lib/mysql/mysql.sock', - default => '/var/run/mysqld/mysqld.sock', - }; - } -} diff --git a/modules/mysql/manifests/config/performance.pp b/modules/mysql/manifests/config/performance.pp deleted file mode 100644 index d62a2cb..0000000 --- a/modules/mysql/manifests/config/performance.pp +++ /dev/null @@ -1,26 +0,0 @@ -class mysql::config::performance { - # force use of system defaults - mysql::config { - 'key_buffer': ensure => absent; - 'max_allowed_packet': ensure => absent; - 'table_cache': ensure => absent; - 'sort_buffer_size': ensure => absent; - 'read_buffer_size': ensure => absent; - 'read_rnd_buffer_size': ensure => absent; - 'net_buffer_length': ensure => absent; - 'myisam_sort_buffer_size': ensure => absent; - 'thread_cache_size': ensure => absent; - 'query_cache_size': ensure => absent; - 'thread_concurrency': ensure => absent; - 'thread_stack': ensure => absent; - 'mysqld_dump/max_allowed_packet': ensure => absent; - 'isamchk/key_buffer': ensure => absent; - 'isamchk/sort_buffer_size': ensure => absent; - 'isamchk/read_buffer': ensure => absent; - 'isamchk/write_buffer': ensure => absent; - 'myisamchk/key_buffer': ensure => absent; - 'myisamchk/sort_buffer_size': ensure => absent; - 'myisamchk/read_buffer': ensure => absent; - 'myisamchk/write_buffer': ensure => absent; - } -} diff --git a/modules/mysql/manifests/config/performance/huge.pp b/modules/mysql/manifests/config/performance/huge.pp deleted file mode 100644 index 05d25e3..0000000 --- a/modules/mysql/manifests/config/performance/huge.pp +++ /dev/null @@ -1,25 +0,0 @@ -class mysql::config::performance::huge { - mysql::config { - 'key_buffer': value => '384M'; - 'max_allowed_packet': value => '1M'; - 'table_cache': value => '512'; - 'sort_buffer_size': value => '2M'; - 'read_buffer_size': value => '2M'; - 'read_rnd_buffer_size': value => '8M'; - 'net_buffer_length': value => '8K'; - 'myisam_sort_buffer_size': value => '64M'; - 'thread_cache_size': value => '8'; - 'query_cache_size': value => '32M'; - 'thread_concurrency': value => '8'; - 'thread_stack': ensure => absent; - 'mysqld_dump/max_allowed_packet': value => '16M'; - 'isamchk/key_buffer': value => '256M'; - 'isamchk/sort_buffer_size': value => '256M'; - 'isamchk/read_buffer': value => '2M'; - 'isamchk/write_buffer': value => '2M'; - 'myisamchk/key_buffer': value => '256M'; - 'myisamchk/sort_buffer_size': value => '256M'; - 'myisamchk/read_buffer': value => '2M'; - 'myisamchk/write_buffer': value => '2M'; - } -} diff --git a/modules/mysql/manifests/config/performance/large.pp b/modules/mysql/manifests/config/performance/large.pp deleted file mode 100644 index 0d5ad11..0000000 --- a/modules/mysql/manifests/config/performance/large.pp +++ /dev/null @@ -1,25 +0,0 @@ -class mysql::config::performance::large { - mysql::config { - 'key_buffer': value => '256M'; - 'max_allowed_packet': value => '1M'; - 'table_cache': value => '256'; - 'sort_buffer_size': value => '1M'; - 'read_buffer_size': value => '1M'; - 'read_rnd_buffer_size': value => '4M'; - 'net_buffer_length': value => '8K'; - 'myisam_sort_buffer_size': value => '64M'; - 'thread_cache_size': value => '8'; - 'query_cache_size': value => '16M'; - 'thread_concurrency': value => '8'; - 'thread_stack': ensure => absent; - 'mysqld_dump/max_allowed_packet': value => '16M'; - 'isamchk/key_buffer': value => '128M'; - 'isamchk/sort_buffer_size': value => '128M'; - 'isamchk/read_buffer': value => '2M'; - 'isamchk/write_buffer': value => '2M'; - 'myisamchk/key_buffer': value => '128M'; - 'myisamchk/sort_buffer_size': value => '128M'; - 'myisamchk/read_buffer': value => '2M'; - 'myisamchk/write_buffer': value => '2M'; - } -} diff --git a/modules/mysql/manifests/config/performance/medium.pp b/modules/mysql/manifests/config/performance/medium.pp deleted file mode 100644 index b13fea2..0000000 --- a/modules/mysql/manifests/config/performance/medium.pp +++ /dev/null @@ -1,25 +0,0 @@ -class mysql::config::performance::medium { - mysql::config { - 'key_buffer': value => '16M'; - 'max_allowed_packet': value => '1M'; - 'table_cache': value => '64'; - 'sort_buffer_size': value => '512K'; - 'read_buffer_size': value => '256K'; - 'read_rnd_buffer_size': value => '512K'; - 'net_buffer_length': value => '8K'; - 'myisam_sort_buffer_size': value => '8M'; - 'thread_cache_size': ensure => absent; - 'query_cache_size': ensure => absent; - 'thread_concurrency': ensure => absent; - 'thread_stack': ensure => absent; - 'mysqld_dump/max_allowed_packet': value => '16M'; - 'isamchk/key_buffer': value => '20M'; - 'isamchk/sort_buffer_size': value => '20M'; - 'isamchk/read_buffer': value => '2M'; - 'isamchk/write_buffer': value => '2M'; - 'myisamchk/key_buffer': value => '20M'; - 'myisamchk/sort_buffer_size': value => '20M'; - 'myisamchk/read_buffer': value => '2M'; - 'myisamchk/write_buffer': value => '2M'; - } -} diff --git a/modules/mysql/manifests/config/performance/small.pp b/modules/mysql/manifests/config/performance/small.pp deleted file mode 100644 index 4603fd1..0000000 --- a/modules/mysql/manifests/config/performance/small.pp +++ /dev/null @@ -1,25 +0,0 @@ -class mysql::config::performance::small { - mysql::config { - 'key_buffer': value => '16K'; - 'max_allowed_packet': value => '1M'; - 'table_cache': value => '4'; - 'sort_buffer_size': value => '64K'; - 'read_buffer_size': value => '256K'; - 'read_rnd_buffer_size': value => '256K'; - 'net_buffer_length': value => '2K'; - 'myisam_sort_buffer_size': ensure => absent; - 'thread_cache_size': ensure => absent; - 'query_cache_size': ensure => absent; - 'thread_concurrency': ensure => absent; - 'thread_stack': value => '64K'; - 'mysqld_dump/max_allowed_packet': value => '16M'; - 'isamchk/key_buffer': value => '8M'; - 'isamchk/sort_buffer_size': value => '8M'; - 'isamchk/read_buffer': ensure => absent; - 'isamchk/write_buffer': ensure => absent; - 'myisamchk/key_buffer': value => '8M'; - 'myisamchk/sort_buffer_size': value => '8M'; - 'myisamchk/read_buffer': ensure => absent; - 'myisamchk/write_buffer': ensure => absent; - } -} diff --git a/modules/mysql/manifests/config/replication.pp b/modules/mysql/manifests/config/replication.pp deleted file mode 100644 index fab936f..0000000 --- a/modules/mysql/manifests/config/replication.pp +++ /dev/null @@ -1,13 +0,0 @@ -class mysql::config::replication { - # Replication - # by default, replication is disabled - mysql::config { - 'log-bin': ensure => absent; - 'server-id': ensure => absent; - 'master-host': ensure => absent; - 'master-user': ensure => absent; - 'master-password': ensure => absent; - 'report-host': ensure => absent; - } - -} diff --git a/modules/mysql/manifests/config/replication/master.pp b/modules/mysql/manifests/config/replication/master.pp deleted file mode 100644 index b52ad7d..0000000 --- a/modules/mysql/manifests/config/replication/master.pp +++ /dev/null @@ -1,16 +0,0 @@ -class mysql::config::replication::master inherits mysql::config::replication { - Mysql::Config['log-bin'] { - ensure => 'present', - value => 'mysql-bin', - } - - Mysql::Config['server-id'] { - ensure => 'present', - value => $mysql_serverid, - } - - mysql::config { - 'expire_logs_days': value => '7'; - 'max_binlog_size': value => '100M'; - } -} diff --git a/modules/mysql/manifests/config/replication/slave.pp b/modules/mysql/manifests/config/replication/slave.pp deleted file mode 100644 index abab678..0000000 --- a/modules/mysql/manifests/config/replication/slave.pp +++ /dev/null @@ -1,29 +0,0 @@ -class mysql::config::replication::slave inherits mysql::config::replication { - Mysql::Config['master-host'] { - ensure => present, - value => $mysql_masterhost, - } - - Mysql::Config['master-user'] { - ensure => present, - value => $mysql_masteruser, - } - - Mysql::Config['master-password'] { - ensure => present, - value => $mysql_masterpw, - } - - Mysql::Config['report-host'] { - ensure => present, - value => $hostname, - } - - mysql::config { - 'relay-log': value => '/var/lib/mysql/mysql-relay-bin'; - 'relay-log-index': value => '/var/lib/mysql/mysql-relay-bin.index'; - 'relay-log-info-file': value => '/var/lib/mysql/relay-log.info'; - 'relay_log_space_limit': value => '2048M'; - 'max_relay_log_size': value => '100M'; - } -} diff --git a/modules/mysql/manifests/database.pp b/modules/mysql/manifests/database.pp deleted file mode 100644 index 02b533e..0000000 --- a/modules/mysql/manifests/database.pp +++ /dev/null @@ -1,9 +0,0 @@ -define mysql::database($ensure) { - - if $mysql_exists == "true" { - mysql_database { $name: - ensure => $ensure, - require => File["/root/.my.cnf"], - } - } -} diff --git a/modules/mysql/manifests/db.pp b/modules/mysql/manifests/db.pp new file mode 100644 index 0000000..350252c --- /dev/null +++ b/modules/mysql/manifests/db.pp @@ -0,0 +1,83 @@ +# Define: mysql::db +# +# This module creates database instances, a user, and grants that user +# privileges to the database. It can also import SQL from a file in order to, +# for example, initialize a database schema. +# +# Since it requires class mysql::server, we assume to run all commands as the +# root mysql user against the local mysql server. +# +# Parameters: +# [*title*] - mysql database name. +# [*user*] - username to create and grant access. +# [*password*] - user's password. +# [*charset*] - database charset. +# [*host*] - host for assigning privileges to user. +# [*grant*] - array of privileges to grant user. +# [*enforce_sql*] - whether to enforce or conditionally run sql on creation. +# [*sql*] - sql statement to run. +# [*ensure*] - specifies if a database is present or absent. +# +# Actions: +# +# Requires: +# +# class mysql::server +# +# Sample Usage: +# +# mysql::db { 'mydb': +# user => 'my_user', +# password => 'password', +# host => $::hostname, +# grant => ['all'] +# } +# +define mysql::db ( + $user, + $password, + $charset = 'utf8', + $host = 'localhost', + $grant = 'all', + $sql = '', + $enforce_sql = false, + $ensure = 'present' +) { + + validate_re($ensure, '^(present|absent)$', + "${ensure} is not supported for ensure. Allowed values are 'present' and 'absent'.") + + database { $name: + ensure => $ensure, + charset => $charset, + provider => 'mysql', + require => Class['mysql::server'], + } + + database_user { "${user}@${host}": + ensure => $ensure, + password_hash => mysql_password($password), + provider => 'mysql', + require => Database[$name], + } + + if $ensure == 'present' { + database_grant { "${user}@${host}/${name}": + privileges => $grant, + provider => 'mysql', + require => Database_user["${user}@${host}"], + } + + $refresh = ! $enforce_sql + + if $sql { + exec{ "${name}-import": + command => "/usr/bin/mysql ${name} < ${sql}", + logoutput => true, + refreshonly => $refresh, + require => Database_grant["${user}@${host}/${name}"], + subscribe => Database[$name], + } + } + } +} diff --git a/modules/mysql/manifests/init.pp b/modules/mysql/manifests/init.pp new file mode 100644 index 0000000..f415436 --- /dev/null +++ b/modules/mysql/manifests/init.pp @@ -0,0 +1,24 @@ +# Class: mysql +# +# This class installs mysql client software. +# +# Parameters: +# [*client_package_name*] - The name of the mysql client package. +# +# Actions: +# +# Requires: +# +# Sample Usage: +# +class mysql ( + $package_name = $mysql::params::client_package_name, + $package_ensure = 'present' +) inherits mysql::params { + + package { 'mysql_client': + ensure => $package_ensure, + name => $package_name, + } + +} diff --git a/modules/mysql/manifests/iptables-open.pp b/modules/mysql/manifests/iptables-open.pp deleted file mode 100644 index 0d3967a..0000000 --- a/modules/mysql/manifests/iptables-open.pp +++ /dev/null @@ -1,18 +0,0 @@ -# -# == Definition: mysql::iptables-open -# -# A helper to open mysql's port, with an optional destination -# -# Example usage: -# mysql::iptables-open { "10.0.0.1": to => "192.168.1.1" } -# -define mysql::iptables-open ($to=undef) { - - iptables { "open mysql from $name": - destination => $to, - dport => "3306", - proto => "tcp", - source => $name, - jump => "ACCEPT", - } -} diff --git a/modules/mysql/manifests/java.pp b/modules/mysql/manifests/java.pp new file mode 100644 index 0000000..2713c05 --- /dev/null +++ b/modules/mysql/manifests/java.pp @@ -0,0 +1,24 @@ +# Class: mysql::java +# +# This class installs the mysql-java-connector. +# +# Parameters: +# [*java_package_name*] - The name of the mysql java package. +# +# Actions: +# +# Requires: +# +# Sample Usage: +# +class mysql::java ( + $package_name = $mysql::params::java_package_name, + $package_ensure = 'present' +) inherits mysql::params { + + package { 'mysql-connector-java': + ensure => $package_ensure, + name => $package_name, + } + +} diff --git a/modules/mysql/manifests/master.pp b/modules/mysql/manifests/master.pp deleted file mode 100644 index 6d9683b..0000000 --- a/modules/mysql/manifests/master.pp +++ /dev/null @@ -1,3 +0,0 @@ -class mysql::master inherits mysql::server { - include mysql::config::replication::master -} diff --git a/modules/mysql/manifests/master/huge.pp b/modules/mysql/manifests/master/huge.pp deleted file mode 100644 index 4a4307b..0000000 --- a/modules/mysql/manifests/master/huge.pp +++ /dev/null @@ -1,3 +0,0 @@ -class mysql::master::huge inherits mysql::server::huge { - include mysql::master -} diff --git a/modules/mysql/manifests/master/large.pp b/modules/mysql/manifests/master/large.pp deleted file mode 100644 index a063ce9..0000000 --- a/modules/mysql/manifests/master/large.pp +++ /dev/null @@ -1,3 +0,0 @@ -class mysql::master::large inherits mysql::server::large { - include mysql::master -} diff --git a/modules/mysql/manifests/master/medium.pp b/modules/mysql/manifests/master/medium.pp deleted file mode 100644 index 9802609..0000000 --- a/modules/mysql/manifests/master/medium.pp +++ /dev/null @@ -1,3 +0,0 @@ -class mysql::master::medium inherits mysql::server::medium { - include mysql::master -} diff --git a/modules/mysql/manifests/master/small.pp b/modules/mysql/manifests/master/small.pp deleted file mode 100644 index 59914fa..0000000 --- a/modules/mysql/manifests/master/small.pp +++ /dev/null @@ -1,3 +0,0 @@ -class mysql::master::small inherits mysql::server::small { - include mysql::master -} diff --git a/modules/mysql/manifests/params.pp b/modules/mysql/manifests/params.pp index dbadff7..95787db 100644 --- a/modules/mysql/manifests/params.pp +++ b/modules/mysql/manifests/params.pp @@ -1,34 +1,118 @@ +# Class: mysql::params +# +# The mysql configuration settings. +# +# Parameters: +# +# Actions: +# +# Requires: +# +# Sample Usage: +# class mysql::params { - $mycnf = $operatingsystem ? { - /RedHat|Fedora|CentOS/ => "/etc/my.cnf", - default => "/etc/mysql/my.cnf", + $bind_address = '127.0.0.1' + $port = 3306 + $etc_root_password = false + $ssl = false + $restart = true + + case $::operatingsystem { + 'Ubuntu': { + $service_provider = upstart + } + default: { + $service_provider = undef + } } - $mycnfctx = "/files${mycnf}" + case $::osfamily { + 'RedHat': { + $basedir = '/usr' + $datadir = '/var/lib/mysql' + $service_name = 'mysqld' + $client_package_name = 'mysql' + $server_package_name = 'mysql-server' + $socket = '/var/lib/mysql/mysql.sock' + $pidfile = '/var/run/mysqld/mysqld.pid' + $config_file = '/etc/my.cnf' + $log_error = '/var/log/mysqld.log' + $ruby_package_name = 'ruby-mysql' + $ruby_package_provider = 'gem' + $python_package_name = 'MySQL-python' + $java_package_name = 'mysql-connector-java' + $root_group = 'root' + $ssl_ca = '/etc/mysql/cacert.pem' + $ssl_cert = '/etc/mysql/server-cert.pem' + $ssl_key = '/etc/mysql/server-key.pem' + } - $data_dir = $mysql_data_dir ? { - "" => "/var/lib/mysql", - default => $mysql_data_dir, - } + 'Debian': { + $basedir = '/usr' + $datadir = '/var/lib/mysql' + $service_name = 'mysql' + $client_package_name = 'mysql-client' + $server_package_name = 'mysql-server' + $socket = '/var/run/mysqld/mysqld.sock' + $pidfile = '/var/run/mysqld/mysqld.pid' + $config_file = '/etc/mysql/my.cnf' + $log_error = '/var/log/mysql/error.log' + $ruby_package_name = 'libmysql-ruby' + $python_package_name = 'python-mysqldb' + $java_package_name = 'libmysql-java' + $root_group = 'root' + $ssl_ca = '/etc/mysql/cacert.pem' + $ssl_cert = '/etc/mysql/server-cert.pem' + $ssl_key = '/etc/mysql/server-key.pem' + } - $backup_dir = $mysql_backupdir ? { - "" => "/var/backups/mysql", - default => $mysql_backupdir, - } + 'FreeBSD': { + $basedir = '/usr/local' + $datadir = '/var/db/mysql' + $service_name = 'mysql-server' + $client_package_name = 'databases/mysql55-client' + $server_package_name = 'databases/mysql55-server' + $socket = '/tmp/mysql.sock' + $pidfile = '/var/db/mysql/mysql.pid' + $config_file = '/var/db/mysql/my.cnf' + $log_error = "/var/db/mysql/${::hostname}.err" + $ruby_package_name = 'ruby-mysql' + $ruby_package_provider = 'gem' + $python_package_name = 'databases/py-MySQLdb' + $java_package_name = 'databases/mysql-connector-java' + $root_group = 'wheel' + $ssl_ca = undef + $ssl_cert = undef + $ssl_key = undef + } - $replication_binlog_format = $replication_binlog_format ? { - "" => "STATEMENT", - default => $replication_binlog_format, - } + default: { + case $::operatingsystem { + 'Amazon': { + $basedir = '/usr' + $datadir = '/var/lib/mysql' + $service_name = 'mysqld' + $client_package_name = 'mysql' + $server_package_name = 'mysql-server' + $socket = '/var/lib/mysql/mysql.sock' + $config_file = '/etc/my.cnf' + $log_error = '/var/log/mysqld.log' + $ruby_package_name = 'ruby-mysql' + $ruby_package_provider = 'gem' + $python_package_name = 'MySQL-python' + $java_package_name = 'mysql-connector-java' + $root_group = 'root' + $ssl_ca = '/etc/mysql/cacert.pem' + $ssl_cert = '/etc/mysql/server-cert.pem' + $ssl_key = '/etc/mysql/server-key.pem' + } - $logfile_group = $mysql_logfile_group ? { - '' => $operatingsystem ? { - 'RedHat' => 'mysql', - 'Debian' => 'adm', - default => 'adm', - }, - default => $mysql_logfile_group, + default: { + fail("Unsupported osfamily: ${::osfamily} operatingsystem: ${::operatingsystem}, module ${module_name} only support osfamily RedHat, Debian, and FreeBSD, or operatingsystem Amazon") + } + } + } } } diff --git a/modules/mysql/manifests/python.pp b/modules/mysql/manifests/python.pp new file mode 100644 index 0000000..0a22da8 --- /dev/null +++ b/modules/mysql/manifests/python.pp @@ -0,0 +1,26 @@ +# Class: mysql::python +# +# This class installs the python libs for mysql. +# +# Parameters: +# [*ensure*] - ensure state for package. +# can be specified as version. +# [*package_name*] - name of package +# +# Actions: +# +# Requires: +# +# Sample Usage: +# +class mysql::python( + $package_name = $mysql::params::python_package_name, + $package_ensure = 'present' +) inherits mysql::params { + + package { 'python-mysqldb': + ensure => $package_ensure, + name => $package_name, + } + +} diff --git a/modules/mysql/manifests/rights.pp b/modules/mysql/manifests/rights.pp deleted file mode 100644 index dcd56c4..0000000 --- a/modules/mysql/manifests/rights.pp +++ /dev/null @@ -1,40 +0,0 @@ -/* - -== Definition: mysql::rights - -A basic helper used to create a user and grant him some privileges on a database. - -Example usage: - mysql::rights { "example case": - user => "foo", - password => "bar", - database => "mydata", - priv => ["select_priv", "update_priv"] - } - -Available parameters: -- *$ensure": defaults to present -- *$database*: the target database -- *$user*: the target user -- *$password*: user's password -- *$host*: target host, default to "localhost" -- *$priv*: target privileges, defaults to "all" (values are the fieldnames from mysql.db table). - -*/ -define mysql::rights($database, $user, $password, $host="localhost", $ensure="present", $priv="all") { - - if $mysql_exists == "true" and $ensure == "present" { - if ! defined(Mysql_user ["${user}@${host}"]) { - mysql_user { "${user}@${host}": - password_hash => mysql_password($password), - require => File["/root/.my.cnf"], - } - } - - mysql_grant { "${user}@${host}/${database}": - privileges => $priv, - require => File["/root/.my.cnf"], - } - } - -} diff --git a/modules/mysql/manifests/ruby.pp b/modules/mysql/manifests/ruby.pp new file mode 100644 index 0000000..6f8f46b --- /dev/null +++ b/modules/mysql/manifests/ruby.pp @@ -0,0 +1,28 @@ +# Class: mysql::ruby +# +# installs the ruby bindings for mysql +# +# Parameters: +# [*ensure*] - ensure state for package. +# can be specified as version. +# [*package_name*] - name of package +# +# Actions: +# +# Requires: +# +# Sample Usage: +# +class mysql::ruby ( + $package_name = $mysql::params::ruby_package_name, + $package_provider = $mysql::params::ruby_package_provider, + $package_ensure = 'present' +) inherits mysql::params { + + package{ 'ruby_mysql': + ensure => $package_ensure, + name => $package_name, + provider => $package_provider, + } + +} diff --git a/modules/mysql/manifests/server.pp b/modules/mysql/manifests/server.pp index ad8f459..b900142 100644 --- a/modules/mysql/manifests/server.pp +++ b/modules/mysql/manifests/server.pp @@ -1,15 +1,53 @@ -/* +# Class: mysql::server +# +# manages the installation of the mysql server. manages the package, service, +# my.cnf +# +# Parameters: +# [*package_name*] - name of package +# [*service_name*] - name of service +# [*config_hash*] - hash of config parameters that need to be set. +# +# Actions: +# +# Requires: +# +# Sample Usage: +# +class mysql::server ( + $package_name = $mysql::params::server_package_name, + $package_ensure = 'present', + $service_name = $mysql::params::service_name, + $service_provider = $mysql::params::service_provider, + $config_hash = {}, + $enabled = true, + $manage_service = true +) inherits mysql::params { -==Class: mysql::server + Class['mysql::server'] -> Class['mysql::config'] -*/ -class mysql::server { + $config_class = { 'mysql::config' => $config_hash } - include mysql::server::base + create_resources( 'class', $config_class ) - include mysql::config::performance - include mysql::config::mysqld - include mysql::config::replication - include mysql::config::mysqld_safe - include mysql::config::client + package { 'mysql-server': + ensure => $package_ensure, + name => $package_name, + } + + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' + } + + if $manage_service { + service { 'mysqld': + ensure => $service_ensure, + name => $service_name, + enable => $enabled, + require => Package['mysql-server'], + provider => $service_provider, + } + } } diff --git a/modules/mysql/manifests/server/account_security.pp b/modules/mysql/manifests/server/account_security.pp new file mode 100644 index 0000000..9df64c0 --- /dev/null +++ b/modules/mysql/manifests/server/account_security.pp @@ -0,0 +1,19 @@ +class mysql::server::account_security { + # Some installations have some default users which are not required. + # We remove them here. You can subclass this class to overwrite this behavior. + database_user { [ "root@${::fqdn}", 'root@127.0.0.1', 'root@::1', + "@${::fqdn}", '@localhost', '@%' ]: + ensure => 'absent', + require => Class['mysql::config'], + } + if ($::fqdn != $::hostname) { + database_user { ["root@${::hostname}", "@${::hostname}"]: + ensure => 'absent', + require => Class['mysql::config'], + } + } + database { 'test': + ensure => 'absent', + require => Class['mysql::config'], + } +} diff --git a/modules/mysql/manifests/server/base.pp b/modules/mysql/manifests/server/base.pp deleted file mode 100644 index caedab5..0000000 --- a/modules/mysql/manifests/server/base.pp +++ /dev/null @@ -1,133 +0,0 @@ -/* - -==Class: mysql::server::base - -Parameters: - $mysql_data_dir: - set the data directory path, which is used to store all the databases - - If set, copies the content of the default mysql data location. This is - necessary on Debian systems because the package installation script - creates a special user used by the init scripts. - -*/ -class mysql::server::base { - - include mysql::params - - user { "mysql": - ensure => present, - require => Package["mysql-server"], - } - - package { "mysql-server": - ensure => installed, - } - - file { "${mysql::params::data_dir}": - ensure => directory, - owner => "mysql", - group => "mysql", - seltype => "mysqld_db_t", - require => Package["mysql-server"], - } - - if( "${mysql::params::data_dir}" != "/var/lib/mysql" ) { - File["${mysql::params::data_dir}"]{ - source => "/var/lib/mysql", - recurse => true, - replace => false, - } - } - - file { "/etc/mysql/my.cnf": - ensure => present, - path => $mysql::params::mycnf, - owner => root, - group => root, - mode => 644, - seltype => "mysqld_etc_t", - require => Package["mysql-server"], - } - - service { "mysql": - ensure => running, - enable => true, - name => $operatingsystem ? { - /RedHat|Fedora|CentOS/ => "mysqld", - default => "mysql", - }, - require => Package["mysql-server"], - } - - if $mysql_user {} else { $mysql_user = "root" } - - if $mysql_password { - - if $mysql_exists == "true" { - mysql_user { "${mysql_user}@localhost": - ensure => present, - password_hash => mysql_password($mysql_password), - require => Exec["Generate my.cnf"], - } - } - - file { "/root/.my.cnf": - ensure => present, - owner => root, - group => root, - mode => 600, - content => template("mysql/my.cnf.erb"), - require => Exec["Initialize MySQL server root password"], - } - - } else { - - $mysql_password = generate("/usr/bin/pwgen", 20, 1) - - file { "/root/.my.cnf": - owner => root, - group => root, - mode => 600, - require => Exec["Initialize MySQL server root password"], - } - - } - - exec { "Initialize MySQL server root password": - unless => "test -f /root/.my.cnf", - command => "mysqladmin -u${mysql_user} password ${mysql_password}", - notify => Exec["Generate my.cnf"], - require => [Package["mysql-server"], Service["mysql"]] - } - - exec { "Generate my.cnf": - command => "/bin/echo -e \"[mysql]\nuser=${mysql_user}\npassword=${mysql_password}\n[mysqladmin]\nuser=${mysql_user}\npassword=${mysql_password}\n[mysqldump]\nuser=${mysql_user}\npassword=${mysql_password}\n[mysqlshow]\nuser=${mysql_user}\npassword=${mysql_password}\n\" > /root/.my.cnf", - refreshonly => true, - creates => "/root/.my.cnf", - } - - $logfile_group = $mysql::params::logfile_group - - file { "/etc/logrotate.d/mysql-server": - ensure => present, - content => $operatingsystem ? { - /RedHat|Fedora|CentOS/ => template('mysql/logrotate.redhat.erb'), - /Debian/ => template('mysql/logrotate.debian.erb'), - default => undef, - } - } - - file { "mysql-slow-queries.log": - ensure => present, - owner => mysql, - group => mysql, - mode => 640, - seltype => mysqld_log_t, - path => $operatingsystem ? { - /RedHat|Fedora|CentOS/ => "/var/log/mysql-slow-queries.log", - default => "/var/log/mysql/mysql-slow-queries.log", - }; - } - -} diff --git a/modules/mysql/manifests/server/config.pp b/modules/mysql/manifests/server/config.pp new file mode 100644 index 0000000..ddd034e --- /dev/null +++ b/modules/mysql/manifests/server/config.pp @@ -0,0 +1,111 @@ +# Creates a my.cnf like config file in the conf.d/ directory. +# +# IMPORTANT: this should be used AFTER the inclusion of +# mysql::server because it needs some variables +# out of the mysql::config class which will be +# included! +# +# == Parameters: +# +# - name: is the name of the file +# - notify_service: whether to notify the mysql daemon or not (default: true) +# - settings: either a string which should be the content of the file +# or a hash with the following structure +# +# section => { +# => , +# ... +# }, +# ... +# +# +section+ means all these sections you can set in +# an configuration file like +mysqld+, +client+, +# +mysqldump+ and so on +# +key+ has to be a valid property which you can set like +# +datadir+, +socket+ or even flags like +read-only+ +# +# +value+ can be +# a) a string as the value +# b) +true+ or +false+ to set a flag like 'read-only' or leave +# it out (+false+ means, nothing will be done) +# c) an array of values which can be of type a) and/or b) +# +# +# == Examples: +# +# Easy one: +# +# mysql::server::config { 'basic_config': +# settings => "[mysqld]\nskip-external-locking\n" +# } +# +# This will create the file /etc/mysql/conf.d/basic_config.cnf with +# the following content: +# +# [mysqld] +# skip-external-locking +# +# +# More complex example: +# +# mysql::server::config { 'basic_config': +# settings => { +# 'mysqld' => { +# 'query_cache_limit' => '5M', +# 'query_cache_size' => '128M', +# 'port' => 3300, +# 'skip-external-locking' => true, +# 'replicate-ignore-db' => [ +# 'tmp_table', +# 'whateveryouwant' +# ] +# }, +# +# 'client' => { +# 'port' => 3300 +# } +# } +# } +# +# This will create the file /etc/mysql/conf.d/basic_config.cnf with +# the following content: +# +# [mysqld] +# query_cache_limit = 5M +# query_cache_size = 128M +# port = 3300 +# skip-external-locking +# replicate-ignore-db = tmp_table +# replicate-ignore-db = whateveryouwant +# +# [client] +# port = 3300 +# +define mysql::server::config ( + $settings, + $notify_service = true +) { + include mysql::config + + if is_hash($settings) { + $content = template('mysql/my.conf.cnf.erb') + } else { + $content = $settings + } + + file { "/etc/mysql/conf.d/${name}.cnf": + ensure => file, + content => $content, + owner => 'root', + group => $mysql::config::root_group, + mode => '0644', + require => Package['mysql-server'], + } + + if $notify_service { + File["/etc/mysql/conf.d/${name}.cnf"] { + # XXX notifying the Service gives us a dependency circle but I don't understand why + notify => Exec['mysqld-restart'] + } + } +} diff --git a/modules/mysql/manifests/server/huge.pp b/modules/mysql/manifests/server/huge.pp deleted file mode 100644 index 8efdd0b..0000000 --- a/modules/mysql/manifests/server/huge.pp +++ /dev/null @@ -1,12 +0,0 @@ -class mysql::server::huge inherits mysql::server::base { - -# Implementation of my-huge.cnf provided as an example with mysql -# distribution. -# This is for a large system with memory of 1G-2G where the system runs mainly -# MySQL. - include mysql::config::performance::huge - include mysql::config::mysqld - include mysql::config::replication - include mysql::config::mysqld_safe - include mysql::config::client -} diff --git a/modules/mysql/manifests/server/large.pp b/modules/mysql/manifests/server/large.pp deleted file mode 100644 index 9460890..0000000 --- a/modules/mysql/manifests/server/large.pp +++ /dev/null @@ -1,13 +0,0 @@ -class mysql::server::large inherits mysql::server::base { - -# Implementation of my-large.cnf provided as an example with mysql -# distribution. -# This is for a large system with memory = 512M where the system runs mainly -# MySQL. - - include mysql::config::performance::large - include mysql::config::mysqld - include mysql::config::replication - include mysql::config::mysqld_safe - include mysql::config::client -} diff --git a/modules/mysql/manifests/server/medium.pp b/modules/mysql/manifests/server/medium.pp deleted file mode 100644 index 1d747b8..0000000 --- a/modules/mysql/manifests/server/medium.pp +++ /dev/null @@ -1,14 +0,0 @@ -class mysql::server::medium inherits mysql::server::base { - -# Implementation of my-medium.cnf provided as an example with mysql -# distribution. -# This is for a system with little memory (32M - 64M) where MySQL plays -# an important part, or systems up to 128M where MySQL is used together with -# other programs (such as a web server). - - include mysql::config::performance::medium - include mysql::config::mysqld - include mysql::config::replication - include mysql::config::mysqld_safe - include mysql::config::client -} diff --git a/modules/mysql/manifests/server/monitor.pp b/modules/mysql/manifests/server/monitor.pp new file mode 100644 index 0000000..c7cad9f --- /dev/null +++ b/modules/mysql/manifests/server/monitor.pp @@ -0,0 +1,19 @@ +class mysql::server::monitor ( + $mysql_monitor_username, + $mysql_monitor_password, + $mysql_monitor_hostname +) { + + Class['mysql::server'] -> Class['mysql::server::monitor'] + + database_user{ "${mysql_monitor_username}@${mysql_monitor_hostname}": + ensure => present, + password_hash => mysql_password($mysql_monitor_password), + } + + database_grant { "${mysql_monitor_username}@${mysql_monitor_hostname}": + privileges => [ 'process_priv', 'super_priv' ], + require => Mysql_user["${mysql_monitor_username}@${mysql_monitor_hostname}"], + } + +} diff --git a/modules/mysql/manifests/server/mysqltuner.pp b/modules/mysql/manifests/server/mysqltuner.pp new file mode 100644 index 0000000..416a3da --- /dev/null +++ b/modules/mysql/manifests/server/mysqltuner.pp @@ -0,0 +1,22 @@ +# Copyright 2009 Larry Ludwig (larrylud@gmail.com) +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +class mysql::server::mysqltuner { + # mysql performance tester + file { '/usr/bin/mysqltuner': + ensure => present, + mode => '0550', + source => 'puppet:///modules/mysql/mysqltuner.pl', + } +} diff --git a/modules/mysql/manifests/server/small.pp b/modules/mysql/manifests/server/small.pp deleted file mode 100644 index 4f34887..0000000 --- a/modules/mysql/manifests/server/small.pp +++ /dev/null @@ -1,14 +0,0 @@ -class mysql::server::small inherits mysql::server::base { - -# Implementation of my-small.cnf provided as an example with mysql -# distribution. -# This is for a system with little memory (<= 64M) where MySQL is only used -# from time to time and it's important that the mysqld daemon -# doesn't use much resources. - - include mysql::config::performance::small - include mysql::config::mysqld - include mysql::config::replication - include mysql::config::mysqld_safe - include mysql::config::client -} diff --git a/modules/mysql/manifests/server/unmanaged.pp b/modules/mysql/manifests/server/unmanaged.pp deleted file mode 100644 index 5cfc1aa..0000000 --- a/modules/mysql/manifests/server/unmanaged.pp +++ /dev/null @@ -1,2 +0,0 @@ -class mysql::server::unmanaged inherits mysql::server::base { -} diff --git a/modules/mysql/manifests/slave.pp b/modules/mysql/manifests/slave.pp deleted file mode 100644 index 6e6d30c..0000000 --- a/modules/mysql/manifests/slave.pp +++ /dev/null @@ -1,44 +0,0 @@ -/* - -== Class: mysql::slave - -Define a MySQL slave server - -*/ -class mysql::slave inherits mysql::slave::common { - - # binlog_format comes with MySQL 5.1+ - # RHEL6+, Debian6+ - case $operatingsystem { - - Debian: { - case $lsbmajdistrelease { - - "4","5": { } - - default: { - mysql::config {'binlog_format': - value => "${mysql::params::replication_binlog_format}", - } - } - } - - } # Debian - - RedHat,CentOS: { - case $lsbmajdistrelease { - - "4","5": { } - - default: { - mysql::config {'binlog_format': - value => "${mysql::params::replication_binlog_format}", - } - } - } - - } # RedHat,CentOS - - } # case $operatingsystem - -} diff --git a/modules/mysql/manifests/slave/common.pp b/modules/mysql/manifests/slave/common.pp deleted file mode 100644 index 2ff54ae..0000000 --- a/modules/mysql/manifests/slave/common.pp +++ /dev/null @@ -1,9 +0,0 @@ -/* -== Class: mysql::slave::common -** The composants available in all versions of MySQL -** This is the inital mysql::slave class. -*/ -class mysql::slave::common inherits mysql::master { - - include mysql::config::replication::slave -} diff --git a/modules/mysql/manifests/slave/huge.pp b/modules/mysql/manifests/slave/huge.pp deleted file mode 100644 index b1e047d..0000000 --- a/modules/mysql/manifests/slave/huge.pp +++ /dev/null @@ -1,3 +0,0 @@ -class mysql::slave::huge inherits mysql::server::huge { - include mysql::slave -} diff --git a/modules/mysql/manifests/slave/large.pp b/modules/mysql/manifests/slave/large.pp deleted file mode 100644 index 6b37777..0000000 --- a/modules/mysql/manifests/slave/large.pp +++ /dev/null @@ -1,3 +0,0 @@ -class mysql::slave::large inherits mysql::server::large { - include mysql::slave -} diff --git a/modules/mysql/manifests/slave/medium.pp b/modules/mysql/manifests/slave/medium.pp deleted file mode 100644 index fd39b72..0000000 --- a/modules/mysql/manifests/slave/medium.pp +++ /dev/null @@ -1,3 +0,0 @@ -class mysql::slave::medium inherits mysql::server::medium { - include mysql::slave -} diff --git a/modules/mysql/manifests/slave/small.pp b/modules/mysql/manifests/slave/small.pp deleted file mode 100644 index 9e92a21..0000000 --- a/modules/mysql/manifests/slave/small.pp +++ /dev/null @@ -1,3 +0,0 @@ -class mysql::slave::small inherits mysql::server::small { - include mysql::slave -} diff --git a/modules/mysql/spec/classes/mysql_backup_spec.rb b/modules/mysql/spec/classes/mysql_backup_spec.rb new file mode 100644 index 0000000..ff5965b --- /dev/null +++ b/modules/mysql/spec/classes/mysql_backup_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe 'mysql::backup' do + + let(:default_params) { + { 'backupuser' => 'testuser', + 'backuppassword' => 'testpass', + 'backupdir' => '/tmp', + } + } + context "standard conditions" do + let(:params) { default_params } + + it { should contain_database_user('testuser@localhost')} + + it { should contain_database_grant('testuser@localhost').with( + :privileges => [ 'Select_priv', 'Reload_priv', 'Lock_tables_priv', 'Show_view_priv' ] + )} + + it { should contain_cron('mysql-backup').with( + :command => '/usr/local/sbin/mysqlbackup.sh', + :ensure => 'present' + )} + + it { should contain_file('mysqlbackup.sh').with( + :path => '/usr/local/sbin/mysqlbackup.sh', + :ensure => 'present' + ) } + + it { should contain_file('mysqlbackupdir').with( + :path => '/tmp', + :ensure => 'directory' + )} + + it 'should have compression by default' do + verify_contents(subject, 'mysqlbackup.sh', [ + ' --all-databases | bzcat -zc > ${DIR}/mysql_backup_`date +%Y%m%d-%H%M%S`.sql.bz2', + ]) + end + end + + context "with compression disabled" do + let(:params) do + { :backupcompress => false }.merge(default_params) + end + + it { should contain_file('mysqlbackup.sh').with( + :path => '/usr/local/sbin/mysqlbackup.sh', + :ensure => 'present' + ) } + + it 'should be able to disable compression' do + verify_contents(subject, 'mysqlbackup.sh', [ + ' --all-databases > ${DIR}/mysql_backup_`date +%Y%m%d-%H%M%S`.sql', + ]) + end + end +end diff --git a/modules/mysql/spec/classes/mysql_config_spec.rb b/modules/mysql/spec/classes/mysql_config_spec.rb new file mode 100644 index 0000000..16f97c4 --- /dev/null +++ b/modules/mysql/spec/classes/mysql_config_spec.rb @@ -0,0 +1,237 @@ +require 'spec_helper' +describe 'mysql::config' do + + let :constant_parameter_defaults do + { + :root_password => 'UNSET', + :old_root_password => '', + :bind_address => '127.0.0.1', + :port => '3306', + :etc_root_password => false, + :datadir => '/var/lib/mysql', + :default_engine => 'UNSET', + :ssl => false, + } + end + + describe 'with osfamily specific defaults' do + { + 'Debian' => { + :datadir => '/var/lib/mysql', + :service_name => 'mysql', + :config_file => '/etc/mysql/my.cnf', + :socket => '/var/run/mysqld/mysqld.sock', + :pidfile => '/var/run/mysqld/mysqld.pid', + :root_group => 'root', + :ssl_ca => '/etc/mysql/cacert.pem', + :ssl_cert => '/etc/mysql/server-cert.pem', + :ssl_key => '/etc/mysql/server-key.pem' + }, + 'FreeBSD' => { + :datadir => '/var/db/mysql', + :service_name => 'mysql-server', + :config_file => '/var/db/mysql/my.cnf', + :socket => '/tmp/mysql.sock', + :pidfile => '/var/db/mysql/mysql.pid', + :root_group => 'wheel', + }, + 'Redhat' => { + :datadir => '/var/lib/mysql', + :service_name => 'mysqld', + :config_file => '/etc/my.cnf', + :socket => '/var/lib/mysql/mysql.sock', + :pidfile => '/var/run/mysqld/mysqld.pid', + :root_group => 'root', + :ssl_ca => '/etc/mysql/cacert.pem', + :ssl_cert => '/etc/mysql/server-cert.pem', + :ssl_key => '/etc/mysql/server-key.pem' + } + }.each do |osfamily, osparams| + + + describe "when osfamily is #{osfamily}" do + + let :facts do + {:osfamily => osfamily} + end + + describe 'when root password is set' do + + let :params do + {:root_password => 'foo'} + end + + it { should contain_exec('set_mysql_rootpw').with( + 'command' => 'mysqladmin -u root password \'foo\'', + 'logoutput' => true, + 'unless' => "mysqladmin -u root -p\'foo\' status > /dev/null", + 'path' => '/usr/local/sbin:/usr/bin:/usr/local/bin' + )} + + it { should contain_file('/root/.my.cnf').with( + 'content' => "[client]\nuser=root\nhost=localhost\npassword=foo\n", + 'require' => 'Exec[set_mysql_rootpw]' + )} + + end + + describe 'when root password and old password are set' do + let :params do + {:root_password => 'foo', :old_root_password => 'bar'} + end + + it { should contain_exec('set_mysql_rootpw').with( + 'command' => 'mysqladmin -u root -p\'bar\' password \'foo\'', + 'logoutput' => true, + 'unless' => "mysqladmin -u root -p\'foo\' status > /dev/null", + 'path' => '/usr/local/sbin:/usr/bin:/usr/local/bin' + )} + + end + + [ + {}, + { + :service_name => 'dans_service', + :config_file => '/home/dan/mysql.conf', + :service_name => 'dans_mysql', + :pidfile => '/home/dan/mysql.pid', + :socket => '/home/dan/mysql.sock', + :bind_address => '0.0.0.0', + :port => '3306', + :datadir => '/path/to/datadir', + :default_engine => 'InnoDB', + :ssl => true, + :ssl_ca => '/path/to/cacert.pem', + :ssl_cert => '/path/to/server-cert.pem', + :ssl_key => '/path/to/server-key.pem' + } + ].each do |passed_params| + + describe "with #{passed_params == {} ? 'default' : 'specified'} parameters" do + + let :parameter_defaults do + constant_parameter_defaults.merge(osparams) + end + + let :params do + passed_params + end + + let :param_values do + parameter_defaults.merge(params) + end + + it { should contain_exec('mysqld-restart').with( + :refreshonly => true, + :path => '/sbin/:/usr/sbin/:/usr/bin/:/bin/', + :command => "service #{param_values[:service_name]} restart" + )} + + it { should_not contain_exec('set_mysql_rootpw') } + + it { should contain_file('/root/.my.cnf')} + + it { should contain_file('/etc/mysql').with( + 'owner' => 'root', + 'group' => param_values[:root_group], + 'notify' => 'Exec[mysqld-restart]', + 'ensure' => 'directory', + 'mode' => '0755' + )} + it { should contain_file('/etc/mysql/conf.d').with( + 'owner' => 'root', + 'group' => param_values[:root_group], + 'notify' => 'Exec[mysqld-restart]', + 'ensure' => 'directory', + 'mode' => '0755' + )} + it { should contain_file(param_values[:config_file]).with( + 'owner' => 'root', + 'group' => param_values[:root_group], + 'notify' => 'Exec[mysqld-restart]', + 'mode' => '0644' + )} + it 'should have a template with the correct contents' do + content = param_value(subject, 'file', param_values[:config_file], 'content') + expected_lines = [ + "port = #{param_values[:port]}", + "socket = #{param_values[:socket]}", + "pid-file = #{param_values[:pidfile]}", + "datadir = #{param_values[:datadir]}", + "bind-address = #{param_values[:bind_address]}" + ] + if param_values[:default_engine] != 'UNSET' + expected_lines = expected_lines | [ "default-storage-engine = #{param_values[:default_engine]}" ] + end + if param_values[:ssl] + expected_lines = expected_lines | + [ + "ssl-ca = #{param_values[:ssl_ca]}", + "ssl-cert = #{param_values[:ssl_cert]}", + "ssl-key = #{param_values[:ssl_key]}" + ] + end + (content.split("\n") & expected_lines).should == expected_lines + end + end + end + end + end + end + + describe 'when etc_root_password is set with password' do + + let :facts do + {:osfamily => 'Debian'} + end + + let :params do + {:root_password => 'foo', :old_root_password => 'bar', :etc_root_password => true} + end + + it { should contain_exec('set_mysql_rootpw').with( + 'command' => 'mysqladmin -u root -p\'bar\' password \'foo\'', + 'logoutput' => true, + 'unless' => "mysqladmin -u root -p\'foo\' status > /dev/null", + 'path' => '/usr/local/sbin:/usr/bin:/usr/local/bin' + )} + + it { should contain_file('/root/.my.cnf').with( + 'content' => "[client]\nuser=root\nhost=localhost\npassword=foo\n", + 'require' => 'Exec[set_mysql_rootpw]' + )} + + end + + describe 'setting etc_root_password should fail on redhat' do + let :facts do + {:osfamily => 'Redhat'} + end + + let :params do + {:root_password => 'foo', :old_root_password => 'bar', :etc_root_password => true} + end + + it 'should fail' do + expect { subject }.to raise_error(Puppet::Error, /Duplicate (declaration|definition)/) + end + + end + + describe 'unset ssl params should fail when ssl is true on freebsd' do + let :facts do + {:osfamily => 'FreeBSD'} + end + + let :params do + { :ssl => true } + end + + it 'should fail' do + expect { subject }.to raise_error(Puppet::Error, /required when ssl is true/) + end + + end + +end diff --git a/modules/mysql/spec/classes/mysql_init_spec.rb b/modules/mysql/spec/classes/mysql_init_spec.rb new file mode 100644 index 0000000..f2b51f8 --- /dev/null +++ b/modules/mysql/spec/classes/mysql_init_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +describe 'mysql' do + + describe 'on a debian based os' do + let :facts do + { :osfamily => 'Debian'} + end + it { should contain_package('mysql_client').with( + :name => 'mysql-client', + :ensure => 'present' + )} + end + + describe 'on a freebsd based os' do + let :facts do + { :osfamily => 'FreeBSD'} + end + it { should contain_package('mysql_client').with( + :name => 'databases/mysql55-client', + :ensure => 'present' + )} + end + + describe 'on a redhat based os' do + let :facts do + {:osfamily => 'Redhat'} + end + it { should contain_package('mysql_client').with( + :name => 'mysql', + :ensure => 'present' + )} + describe 'when parameters are supplied' do + let :params do + {:package_ensure => 'latest', :package_name => 'mysql_client'} + end + it { should contain_package('mysql_client').with( + :name => 'mysql_client', + :ensure => 'latest' + )} + end + end + + describe 'on any other os' do + let :facts do + {:osfamily => 'foo'} + end + + it 'should fail' do + expect { subject }.to raise_error(/Unsupported osfamily: foo/) + end + end + +end diff --git a/modules/mysql/spec/classes/mysql_java_spec.rb b/modules/mysql/spec/classes/mysql_java_spec.rb new file mode 100644 index 0000000..bd75691 --- /dev/null +++ b/modules/mysql/spec/classes/mysql_java_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +describe 'mysql::java' do + + describe 'on a debian based os' do + let :facts do + { :osfamily => 'Debian'} + end + it { should contain_package('mysql-connector-java').with( + :name => 'libmysql-java', + :ensure => 'present' + )} + end + + describe 'on a freebsd based os' do + let :facts do + { :osfamily => 'FreeBSD'} + end + it { should contain_package('mysql-connector-java').with( + :name => 'databases/mysql-connector-java', + :ensure => 'present' + )} + end + + describe 'on a redhat based os' do + let :facts do + {:osfamily => 'Redhat'} + end + it { should contain_package('mysql-connector-java').with( + :name => 'mysql-connector-java', + :ensure => 'present' + )} + describe 'when parameters are supplied' do + let :params do + {:package_ensure => 'latest', :package_name => 'java-mysql'} + end + it { should contain_package('mysql-connector-java').with( + :name => 'java-mysql', + :ensure => 'latest' + )} + end + end + + describe 'on any other os' do + let :facts do + {:osfamily => 'foo'} + end + + it 'should fail' do + expect { subject }.to raise_error(/Unsupported osfamily: foo/) + end + end + +end diff --git a/modules/mysql/spec/classes/mysql_python_spec.rb b/modules/mysql/spec/classes/mysql_python_spec.rb new file mode 100644 index 0000000..ecf1fb3 --- /dev/null +++ b/modules/mysql/spec/classes/mysql_python_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +describe 'mysql::python' do + + describe 'on a debian based os' do + let :facts do + { :osfamily => 'Debian'} + end + it { should contain_package('python-mysqldb').with( + :name => 'python-mysqldb', + :ensure => 'present' + )} + end + + describe 'on a freebsd based os' do + let :facts do + { :osfamily => 'FreeBSD'} + end + it { should contain_package('python-mysqldb').with( + :name => 'databases/py-MySQLdb', + :ensure => 'present' + )} + end + + describe 'on a redhat based os' do + let :facts do + {:osfamily => 'Redhat'} + end + it { should contain_package('python-mysqldb').with( + :name => 'MySQL-python', + :ensure => 'present' + )} + describe 'when parameters are supplied' do + let :params do + {:package_ensure => 'latest', :package_name => 'python-mysql'} + end + it { should contain_package('python-mysqldb').with( + :name => 'python-mysql', + :ensure => 'latest' + )} + end + end + + describe 'on any other os' do + let :facts do + {:osfamily => 'foo'} + end + + it 'should fail' do + expect { subject }.to raise_error(/Unsupported osfamily: foo/) + end + end + +end diff --git a/modules/mysql/spec/classes/mysql_ruby_spec.rb b/modules/mysql/spec/classes/mysql_ruby_spec.rb new file mode 100644 index 0000000..ceb9e97 --- /dev/null +++ b/modules/mysql/spec/classes/mysql_ruby_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' + +describe 'mysql::ruby' do + + describe 'on a debian based os' do + let :facts do + { :osfamily => 'Debian'} + end + it { should contain_package('ruby_mysql').with( + :name => 'libmysql-ruby', + :ensure => 'present', + # TODO is this what we want? does this actually work + # if the provider is blank + :provider => '' + )} + end + + describe 'on a freebsd based os' do + let :facts do + { :osfamily => 'FreeBSD'} + end + it { should contain_package('ruby_mysql').with( + :name => 'ruby-mysql', + :ensure => 'present', + :provider => 'gem' + )} + end + + describe 'on a redhat based os' do + let :facts do + {:osfamily => 'Redhat'} + end + it { should contain_package('ruby_mysql').with( + :name => 'ruby-mysql', + :ensure => 'present', + :provider => 'gem' + )} + describe 'when parameters are supplied' do + let :params do + {:package_ensure => 'latest', + :package_provider => 'zypper', + :package_name => 'mysql_ruby'} + end + it { should contain_package('ruby_mysql').with( + :name => 'mysql_ruby', + :ensure => 'latest', + :provider => 'zypper' + )} + end + end + + describe 'on any other os' do + let :facts do + {:osfamily => 'foo'} + end + + it 'should fail' do + expect { subject }.to raise_error(/Unsupported osfamily: foo/) + end + end + +end diff --git a/modules/mysql/spec/classes/mysql_server_account_security_spec.rb b/modules/mysql/spec/classes/mysql_server_account_security_spec.rb new file mode 100644 index 0000000..98d4bb6 --- /dev/null +++ b/modules/mysql/spec/classes/mysql_server_account_security_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe 'mysql::server::account_security' do + + let :facts do { + :fqdn => 'myhost.mydomain', + :hostname => 'myhost' + } + end + + it 'should remove Database_User[root@myhost.mydomain]' do + should contain_database_user('root@myhost.mydomain').with_ensure('absent') + end + it 'should remove Database_User[root@myhost]' do + should contain_database_user('root@myhost').with_ensure('absent') + end + it 'should remove Database_User[root@127.0.0.1]' do + should contain_database_user('root@127.0.0.1').with_ensure('absent') + end + it 'should remove Database_User[root@::1]' do + should contain_database_user('root@::1').with_ensure('absent') + end + it 'should remove Database_User[@myhost.mydomain]' do + should contain_database_user('@myhost.mydomain').with_ensure('absent') + end + it 'should remove Database_User[@myhost]' do + should contain_database_user('@myhost').with_ensure('absent') + end + it 'should remove Database_User[@localhost]' do + should contain_database_user('@localhost').with_ensure('absent') + end + it 'should remove Database_User[@%]' do + should contain_database_user('@%').with_ensure('absent') + end + + it 'should remove Database[test]' do + should contain_database('test').with_ensure('absent') + end + +end diff --git a/modules/mysql/spec/classes/mysql_server_monitor_spec.rb b/modules/mysql/spec/classes/mysql_server_monitor_spec.rb new file mode 100644 index 0000000..b7591cc --- /dev/null +++ b/modules/mysql/spec/classes/mysql_server_monitor_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' +describe 'mysql::server::monitor' do + let :facts do + { :osfamily => 'Debian' } + end + let :pre_condition do + "include 'mysql::server'" + end + let :params do + { + :mysql_monitor_username => 'monitoruser', + :mysql_monitor_password => 'monitorpass', + :mysql_monitor_hostname => 'monitorhost' + } + end + + it { should contain_database_user("monitoruser@monitorhost")} +end diff --git a/modules/mysql/spec/classes/mysql_server_spec.rb b/modules/mysql/spec/classes/mysql_server_spec.rb new file mode 100644 index 0000000..8da66a7 --- /dev/null +++ b/modules/mysql/spec/classes/mysql_server_spec.rb @@ -0,0 +1,98 @@ +require 'spec_helper' +describe 'mysql::server' do + + let :constant_parameter_defaults do + {:config_hash => {}, + :package_ensure => 'present', + :enabled => true, + :manage_service => true + } + end + + describe 'when ubuntu use upstart' do + let :facts do + { :osfamily => 'Debian', + :operatingsystem => 'Ubuntu', + } + end + + it { should contain_service('mysqld').with( + :name => 'mysql', + :ensure => 'running', + :enable => 'true', + :provider => 'upstart', + :require => 'Package[mysql-server]' + )} + end + + describe 'with osfamily specific defaults' do + { + 'Debian' => { + :service_name => 'mysql', + :package_name => 'mysql-server' + }, + 'FreeBSD' => { + :service_name => 'mysql-server', + :package_name => 'databases/mysql55-server' + }, + 'Redhat' => { + :service_name => 'mysqld', + :package_name => 'mysql-server' + } + }.each do |osfamily, osparams| + + describe "when osfamily is #{osfamily}" do + + let :facts do + { :osfamily => osfamily } + end + + [ + {}, + { + :package_name => 'dans_package', + :package_ensure => 'latest', + :service_name => 'dans_service', + :config_hash => {'root_password' => 'foo'}, + :enabled => false, + :manage_service => false + } + ].each do |passed_params| + + describe "with #{passed_params == {} ? 'default' : 'specified'} parameters" do + + let :parameter_defaults do + constant_parameter_defaults.merge(osparams) + end + + let :params do + passed_params + end + + let :param_values do + parameter_defaults.merge(params) + end + + it { should contain_package('mysql-server').with( + :name => param_values[:package_name], + :ensure => param_values[:package_ensure] + )} + + it { + if param_values[:manage_service] + should contain_service('mysqld').with( + :name => param_values[:service_name], + :ensure => param_values[:enabled] ? 'running' : 'stopped', + :enable => param_values[:enabled], + :require => 'Package[mysql-server]' + ).without_provider + else + should_not contain_service('mysqld') + end + } + end + end + end + end + end +end diff --git a/modules/mysql/spec/defines/mysql_db_spec.rb b/modules/mysql/spec/defines/mysql_db_spec.rb new file mode 100644 index 0000000..55f5d9d --- /dev/null +++ b/modules/mysql/spec/defines/mysql_db_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe 'mysql::db', :type => :define do + let(:title) { 'test_db' } + + let(:params) { + { 'user' => 'testuser', + 'password' => 'testpass', + } + } + + it 'should report an error when ensure is not present or absent' do + params.merge!({'ensure' => 'invalid_val'}) + expect { subject }.to raise_error(Puppet::Error, + /invalid_val is not supported for ensure\. Allowed values are 'present' and 'absent'\./) + end + + it 'should not notify the import sql exec if no sql script was provided' do + should contain_database('test_db').without_notify + end + + it 'should subscribe to database if sql script is given' do + params.merge!({'sql' => 'test_sql'}) + should contain_exec('test_db-import').with_subscribe('Database[test_db]') + end + + it 'should only import sql script on creation if not enforcing' do + params.merge!({'sql' => 'test_sql', 'enforce_sql' => false}) + should contain_exec('test_db-import').with_refreshonly(true) + end + + it 'should import sql script on creation if enforcing' do + params.merge!({'sql' => 'test_sql', 'enforce_sql' => true}) + should contain_exec('test_db-import').with_refreshonly(false) + end + + it 'should not create database and database user' do + params.merge!({'ensure' => 'absent', 'host' => 'localhost'}) + should contain_database('test_db').with_ensure('absent') + should contain_database_user('testuser@localhost').with_ensure('absent') + end +end diff --git a/modules/mysql/spec/defines/mysql_server_config_spec.rb b/modules/mysql/spec/defines/mysql_server_config_spec.rb new file mode 100644 index 0000000..7d99e5b --- /dev/null +++ b/modules/mysql/spec/defines/mysql_server_config_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe 'mysql::server::config', :type => :define do + filename = '/etc/mysql/conf.d/test_config.cnf' + + let :facts do + { :osfamily => 'Debian'} + end + + let(:title) { File.basename(filename, '.cnf') } + + let(:params) { + { 'settings' => { + 'mysqld' => { + 'bind-address' => '0.0.0.0' + } + } + } + } + + it 'should notify the mysql daemon' do + should contain_file(filename).with_notify('Exec[mysqld-restart]') + end + + it 'should contain config parameter in content' do + should contain_file(filename).with_content("### MANAGED BY PUPPET ###\n[mysqld]\nbind-address = 0.0.0.0\n\n") + end + + it 'should not notify the mysql daemon' do + params.merge!({ 'notify_service' => false }) + should contain_file(filename).without_notify + end + + it 'should require on the mysql-server package' do + should contain_file(filename).with_require('Package[mysql-server]') + end +end diff --git a/modules/mysql/spec/spec.opts b/modules/mysql/spec/spec.opts new file mode 100644 index 0000000..91cd642 --- /dev/null +++ b/modules/mysql/spec/spec.opts @@ -0,0 +1,6 @@ +--format +s +--colour +--loadby +mtime +--backtrace diff --git a/modules/mysql/spec/spec_helper.rb b/modules/mysql/spec/spec_helper.rb new file mode 100644 index 0000000..2c6f566 --- /dev/null +++ b/modules/mysql/spec/spec_helper.rb @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/module_spec_helper' diff --git a/modules/mysql/spec/unit/mysql_password_spec.rb b/modules/mysql/spec/unit/mysql_password_spec.rb new file mode 100644 index 0000000..4e67ed9 --- /dev/null +++ b/modules/mysql/spec/unit/mysql_password_spec.rb @@ -0,0 +1,30 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +describe "the mysql_password function" do + before :all do + Puppet::Parser::Functions.autoloader.loadall + end + + before :each do + @scope = Puppet::Parser::Scope.new + end + + it "should exist" do + Puppet::Parser::Functions.function("mysql_password").should == "function_mysql_password" + end + + it "should raise a ParseError if there is less than 1 arguments" do + lambda { @scope.function_mysql_password([]) }.should( raise_error(Puppet::ParseError)) + end + + it "should raise a ParseError if there is more than 1 arguments" do + lambda { @scope.function_mysql_password(['foo', 'bar']) }.should( raise_error(Puppet::ParseError)) + end + + it "should convert password into a hash" do + result = @scope.function_mysql_password(["password"]) + result.should(eq('*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19')) + end + +end diff --git a/modules/mysql/spec/unit/puppet/provider/database_grant/mysql_spec.rb b/modules/mysql/spec/unit/puppet/provider/database_grant/mysql_spec.rb new file mode 100644 index 0000000..db82ded --- /dev/null +++ b/modules/mysql/spec/unit/puppet/provider/database_grant/mysql_spec.rb @@ -0,0 +1,86 @@ +require 'puppet' +require 'mocha' +require 'spec_helper' +RSpec.configure do |config| + config.mock_with :mocha +end +provider_class = Puppet::Type.type(:database_grant).provider(:mysql) +describe provider_class do + let(:root_home) { '/some/root/home' } + + before :each do + @resource = Puppet::Type::Database_grant.new( + { :privileges => 'all', :provider => 'mysql', :name => 'user@host'} + ) + @provider = provider_class.new(@resource) + Facter.stubs(:value).with(:root_home).returns(root_home) + end + + it 'should query privilegess from the database' do + provider_class.expects(:mysql) .with("--defaults-file=#{root_home}/.my.cnf", 'mysql', '-Be', 'describe user').returns <<-EOT +Field Type Null Key Default Extra +Host char(60) NO PRI +User char(16) NO PRI +Password char(41) NO +Select_priv enum('N','Y') NO N +Insert_priv enum('N','Y') NO N +Update_priv enum('N','Y') NO N +EOT + provider_class.expects(:mysql).with("--defaults-file=#{root_home}/.my.cnf", 'mysql', '-Be', 'describe db').returns <<-EOT +Field Type Null Key Default Extra +Host char(60) NO PRI +Db char(64) NO PRI +User char(16) NO PRI +Select_priv enum('N','Y') NO N +Insert_priv enum('N','Y') NO N +Update_priv enum('N','Y') NO N +EOT + provider_class.user_privs.should == [ 'Select_priv', 'Insert_priv', 'Update_priv' ] + provider_class.db_privs.should == [ 'Select_priv', 'Insert_priv', 'Update_priv' ] + end + + it 'should query set priviliges' do + provider_class.expects(:mysql).with("--defaults-file=#{root_home}/.my.cnf", 'mysql', '-Be', 'select * from mysql.user where user="user" and host="host"').returns <<-EOT +Host User Password Select_priv Insert_priv Update_priv +host user Y N Y +EOT + @provider.privileges.should == [ 'Select_priv', 'Update_priv' ] + end + + it 'should recognize when all priviliges are set' do + provider_class.expects(:mysql).with("--defaults-file=#{root_home}/.my.cnf", 'mysql', '-Be', 'select * from mysql.user where user="user" and host="host"').returns <<-EOT +Host User Password Select_priv Insert_priv Update_priv +host user Y Y Y +EOT + @provider.all_privs_set?.should == true + end + + it 'should recognize when all privileges are not set' do + provider_class.expects(:mysql).with("--defaults-file=#{root_home}/.my.cnf", 'mysql', '-Be', 'select * from mysql.user where user="user" and host="host"').returns <<-EOT +Host User Password Select_priv Insert_priv Update_priv +host user Y N Y +EOT + @provider.all_privs_set?.should == false + end + + it 'should be able to set all privileges' do + provider_class.expects(:mysql).with("--defaults-file=#{root_home}/.my.cnf", 'mysql', '-NBe', 'SELECT "1" FROM user WHERE user="user" AND host="host"').returns "1\n" + provider_class.expects(:mysql).with("--defaults-file=#{root_home}/.my.cnf", 'mysql', '-Be', "update user set Select_priv = 'Y', Insert_priv = 'Y', Update_priv = 'Y' where user=\"user\" and host=\"host\"") + provider_class.expects(:mysqladmin).with("--defaults-file=#{root_home}/.my.cnf", "flush-privileges") + @provider.privileges=(['all']) + end + + it 'should be able to set partial privileges' do + provider_class.expects(:mysql).with("--defaults-file=#{root_home}/.my.cnf", 'mysql', '-NBe', 'SELECT "1" FROM user WHERE user="user" AND host="host"').returns "1\n" + provider_class.expects(:mysql).with("--defaults-file=#{root_home}/.my.cnf", 'mysql', '-Be', "update user set Select_priv = 'Y', Insert_priv = 'N', Update_priv = 'Y' where user=\"user\" and host=\"host\"") + provider_class.expects(:mysqladmin).with("--defaults-file=#{root_home}/.my.cnf", "flush-privileges") + @provider.privileges=(['Select_priv', 'Update_priv']) + end + + it 'should be case insensitive' do + provider_class.expects(:mysql).with("--defaults-file=#{root_home}/.my.cnf", 'mysql', '-NBe', 'SELECT "1" FROM user WHERE user="user" AND host="host"').returns "1\n" + provider_class.expects(:mysql).with("--defaults-file=#{root_home}/.my.cnf", 'mysql', '-Be', "update user set Select_priv = 'Y', Insert_priv = 'Y', Update_priv = 'Y' where user=\"user\" and host=\"host\"") + provider_class.expects(:mysqladmin).with("--defaults-file=#{root_home}/.my.cnf", 'flush-privileges') + @provider.privileges=(['SELECT_PRIV', 'insert_priv', 'UpDaTe_pRiV']) + end +end diff --git a/modules/mysql/templates/logrotate.debian.erb b/modules/mysql/templates/logrotate.debian.erb deleted file mode 100644 index 246e502..0000000 --- a/modules/mysql/templates/logrotate.debian.erb +++ /dev/null @@ -1,28 +0,0 @@ -# file managed by puppet -# - I put everything in one block and added sharedscripts, so that mysql gets -# flush-logs'd only once. -# Else the binary logs would automatically increase by n times every day. -# - The error log is obsolete, messages go to syslog now. -/var/log/mysql.log /var/log/mysql/mysql.log /var/log/mysql/mysql-slow-queries.log { - daily - rotate 7 - missingok - create 640 mysql <%= logfile_group %> - compress - sharedscripts - postrotate - test -x /usr/bin/mysqladmin || exit 0 - - # If this fails, check debian.conf! - MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf" - if [ -z "`$MYADMIN ping 2>/dev/null`" ]; then - # Really no mysqld or rather a missing debian-sys-maint user? - # If this occurs and is not a error please report a bug. - if ps cax | grep -q mysqld; then - exit 1 - fi - else - $MYADMIN flush-logs - fi - endscript -} diff --git a/modules/mysql/templates/logrotate.redhat.erb b/modules/mysql/templates/logrotate.redhat.erb deleted file mode 100644 index 995827e..0000000 --- a/modules/mysql/templates/logrotate.redhat.erb +++ /dev/null @@ -1,18 +0,0 @@ -# file managed by puppet -/var/log/mysqld.log /var/log/mysql-queries.log /var/log/mysql-slow-queries.log { - daily - rotate 7 - missingok - create 640 mysql <%= logfile_group %> - compress - sharedscripts - postrotate - export MYADMIN="/usr/bin/mysqladmin --defaults-extra-file=/root/.my.cnf" - test -x /usr/bin/mysqladmin || exit 0 - if ! $MYADMIN ping 2>&1 > /dev/null; then - echo "mysql not running" && exit 1 - else - $MYADMIN flush-logs - fi - endscript -} diff --git a/modules/mysql/templates/my.cnf.erb b/modules/mysql/templates/my.cnf.erb index 7374b50..580def1 100644 --- a/modules/mysql/templates/my.cnf.erb +++ b/modules/mysql/templates/my.cnf.erb @@ -1,16 +1,47 @@ -# file managed by puppet -[mysqldump] -user = <%= mysql_user %> -password = <%= mysql_password %> +[client] +port = <%= port %> +socket = <%= socket %> +[mysqld_safe] +socket = <%= socket %> +nice = 0 +[mysqld] +user = mysql +pid-file = <%= pidfile %> +socket = <%= socket %> +port = <%= port %> +basedir = <%= basedir %> +datadir = <%= datadir %> +tmpdir = /tmp +skip-external-locking -[mysql] -user = <%= mysql_user %> -password = <%= mysql_password %> +<% if bind_address %> +bind-address = <%= bind_address %> +<% end %> -[mysqladmin] -user = <%= mysql_user %> -password = <%= mysql_password %> +key_buffer = 16M +max_allowed_packet = 16M +thread_stack = 192K +thread_cache_size = 8 +myisam-recover = BACKUP +query_cache_limit = 1M +query_cache_size = 16M +log_error = <%= log_error %> +expire_logs_days = 10 +max_binlog_size = 100M +<% if default_engine != 'UNSET' %> +default-storage-engine = <%= default_engine %> +<% end %> +<% if ssl == true %> +ssl-ca = <%= ssl_ca %> +ssl-cert = <%= ssl_cert %> +ssl-key = <%= ssl_key %> +<% end %> -[client] -user = <%= mysql_user %> -password = <%= mysql_password %> +[mysqldump] +quick +quote-names +max_allowed_packet = 16M +[mysql] +[isamchk] +key_buffer = 16M +!includedir /etc/mysql/conf.d/ diff --git a/modules/mysql/templates/my.cnf.pass.erb b/modules/mysql/templates/my.cnf.pass.erb new file mode 100644 index 0000000..38a3a4a --- /dev/null +++ b/modules/mysql/templates/my.cnf.pass.erb @@ -0,0 +1,6 @@ +[client] +user=root +host=localhost +<% unless root_password == 'UNSET' -%> +password=<%= root_password %> +<% end -%> diff --git a/modules/mysql/templates/my.conf.cnf.erb b/modules/mysql/templates/my.conf.cnf.erb new file mode 100644 index 0000000..ad58af3 --- /dev/null +++ b/modules/mysql/templates/my.conf.cnf.erb @@ -0,0 +1,17 @@ +### MANAGED BY PUPPET ### +<% settings.sort.each do |section, content| -%> +[<%= section %>] +<% content.sort.each do |key, values| -%> +<% [values].flatten.sort.each do |value| -%> +<%= value == false ? '#' : '' %><%= key -%><%= + case value + when true, false + '' + else + " = #{value}" + end +%> +<% end -%> +<% end -%> + +<% end -%> diff --git a/modules/mysql/templates/mysql-backup.sh.erb b/modules/mysql/templates/mysql-backup.sh.erb deleted file mode 100644 index 9326357..0000000 --- a/modules/mysql/templates/mysql-backup.sh.erb +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/sh -# file managed by puppet - -case "$1" in - week) - DAY=$(date +%A |tr 'A-Z' 'a-z') - ;; - month) - DAY=$(date +%d) - ;; - year) - DAY=$(date +%j) - ;; - *) - echo "Usage: $0 (week|month|year)" - exit 1 - ;; -esac - -PATH="/bin:/sbin:/usr/bin:/usr/sbin" -MYDIR="<%= data_dir %>" -BKPDIR="<%= backup_dir %>" - -# Installed ? -if [ -e /usr/bin/mysqladmin ] && [ -e /usr/bin/mysqldump ]; then - # used ? - if [ -d $MYDIR ] && [ -n "$(find $MYDIR -maxdepth 1 -type d ! -iname mysql ! -iname test )" ]; then - # Running ? - if /usr/bin/mysqladmin -s ping > /dev/null; then - /usr/bin/mysqldump --all-database --extended-insert > $BKPDIR/mysql.sql && nice -n 19 gzip -9 $BKPDIR/mysql.sql && mv -f $BKPDIR/mysql.sql.gz $BKPDIR/mysql-$DAY.sql.gz - exit $? - else - echo 'mysqld not running' - exit 1 - fi - else - # no databases to backup ? no problem - exit 0 - fi -else - echo "mysqladmin/mysqldump missing. Are you sure this cron must run ?" - exit 1 -fi diff --git a/modules/mysql/templates/mysqlbackup.sh.erb b/modules/mysql/templates/mysqlbackup.sh.erb new file mode 100644 index 0000000..b75ee4a --- /dev/null +++ b/modules/mysql/templates/mysqlbackup.sh.erb @@ -0,0 +1,23 @@ +#!/bin/sh +# +# MySQL Backup Script +# Dumps mysql databases to a file for another backup tool to pick up. +# +# MySQL code: +# GRANT SELECT, RELOAD, LOCK TABLES ON *.* TO 'user'@'localhost' +# IDENTIFIED BY 'password'; +# FLUSH PRIVILEGES; +# +##### START CONFIG ################################################### + +USER=<%= backupuser %> +PASS=<%= backuppassword %> +DIR=<%= backupdir %> + +##### STOP CONFIG #################################################### +PATH=/usr/bin:/usr/sbin:/bin:/sbin + +find $DIR -mtime +30 -exec rm -f {} \; +mysqldump -u${USER} -p${PASS} --opt --flush-logs --single-transaction \ + --all-databases <% if backupcompress %>| bzcat -zc <% end %>> ${DIR}/mysql_backup_`date +%Y%m%d-%H%M%S`.sql<% if backupcompress %>.bz2<% end %> + diff --git a/modules/mysql/templates/sudoers.mysql.erb b/modules/mysql/templates/sudoers.mysql.erb deleted file mode 100644 index 200182b..0000000 --- a/modules/mysql/templates/sudoers.mysql.erb +++ /dev/null @@ -1,16 +0,0 @@ -## This part comes from modules/mysql/manifests/classes/administration.pp -<% if has_variable?("sudo_mysql_admin_user") -%> -User_Alias MYSQL_ADMIN_USER = %mysql-admin,<%= sudo_mysql_admin_user %> -<% else -%> -User_Alias MYSQL_ADMIN_USER = %mysql-admin -<% end -%> -<% if has_variable?("sudo_mysql_admin_cmnd") %> -Cmnd_Alias MYSQL_ADMIN_CMND = <%= sudo_mysql_admin_cmnd %> -<% elsif operatingsystem == "Debian" -%> -Cmnd_Alias MYSQL_ADMIN_CMND = /etc/init.d/mysql -<% elsif operatingsystem == "RedHat" -%> -Cmnd_Alias MYSQL_ADMIN_CMND = /etc/init.d/mysqld, /sbin/service mysqld -<% end -%> -MYSQL_ADMIN_USER ALL=(root) MYSQL_ADMIN_CMND -## - diff --git a/modules/mysql/tests/backup.pp b/modules/mysql/tests/backup.pp new file mode 100644 index 0000000..cb669e6 --- /dev/null +++ b/modules/mysql/tests/backup.pp @@ -0,0 +1,8 @@ +class { 'mysql::server': + config_hash => {'root_password' => 'password'} +} +class { 'mysql::backup': + backupuser => 'myuser', + backuppassword => 'mypassword', + backupdir => '/tmp/backups', +} diff --git a/modules/mysql/tests/init.pp b/modules/mysql/tests/init.pp new file mode 100644 index 0000000..846121b --- /dev/null +++ b/modules/mysql/tests/init.pp @@ -0,0 +1 @@ +include mysql diff --git a/modules/mysql/tests/java.pp b/modules/mysql/tests/java.pp new file mode 100644 index 0000000..0fc009a --- /dev/null +++ b/modules/mysql/tests/java.pp @@ -0,0 +1 @@ +class { 'mysql::java':} diff --git a/modules/mysql/tests/mysql_database.pp b/modules/mysql/tests/mysql_database.pp new file mode 100644 index 0000000..8747f70 --- /dev/null +++ b/modules/mysql/tests/mysql_database.pp @@ -0,0 +1,12 @@ +class { 'mysql::server': + config_hash => {'root_password' => 'password'} +} +database{ ['test1', 'test2', 'test3']: + ensure => present, + charset => 'utf8', + require => Class['mysql::server'], +} +database{ 'test4': + ensure => present, + charset => 'latin1', +} diff --git a/modules/mysql/tests/mysql_grant.pp b/modules/mysql/tests/mysql_grant.pp new file mode 100644 index 0000000..8d96547 --- /dev/null +++ b/modules/mysql/tests/mysql_grant.pp @@ -0,0 +1,3 @@ +database_grant{'test1@localhost/redmine': + privileges => [update], +} diff --git a/modules/mysql/tests/mysql_user.pp b/modules/mysql/tests/mysql_user.pp new file mode 100644 index 0000000..f639084 --- /dev/null +++ b/modules/mysql/tests/mysql_user.pp @@ -0,0 +1,23 @@ +$mysql_root_pw = 'password' + +class { 'mysql::server': + config_hash => { + root_password => 'password', + } +} + +database_user{ 'redmine@localhost': + ensure => present, + password_hash => mysql_password('redmine'), + require => Class['mysql::server'], +} + +database_user{ 'dan@localhost': + ensure => present, + password_hash => mysql_password('blah') +} + +database_user{ 'dan@%': + ensure => present, + password_hash => mysql_password('blah'), +} diff --git a/modules/mysql/tests/python.pp b/modules/mysql/tests/python.pp new file mode 100644 index 0000000..04f7ffa --- /dev/null +++ b/modules/mysql/tests/python.pp @@ -0,0 +1 @@ +class { 'mysql::python':} diff --git a/modules/mysql/tests/ruby.pp b/modules/mysql/tests/ruby.pp new file mode 100644 index 0000000..e84c046 --- /dev/null +++ b/modules/mysql/tests/ruby.pp @@ -0,0 +1 @@ +include mysql::ruby diff --git a/modules/mysql/tests/server.pp b/modules/mysql/tests/server.pp new file mode 100644 index 0000000..47e2fa3 --- /dev/null +++ b/modules/mysql/tests/server.pp @@ -0,0 +1,3 @@ +class { 'mysql::server': + config_hash => { 'root_password' => 'password', }, +} diff --git a/modules/mysql/tests/server/account_security.pp b/modules/mysql/tests/server/account_security.pp new file mode 100644 index 0000000..de393cc --- /dev/null +++ b/modules/mysql/tests/server/account_security.pp @@ -0,0 +1,4 @@ +class { 'mysql::server': + config_hash => { 'root_password' => 'password', }, +} +class { 'mysql::server::account_security': } diff --git a/modules/mysql/tests/server/config.pp b/modules/mysql/tests/server/config.pp new file mode 100644 index 0000000..fe8d86e --- /dev/null +++ b/modules/mysql/tests/server/config.pp @@ -0,0 +1,11 @@ +mysql::server::config { 'testfile': + settings => { + 'mysqld' => { + 'bind-address' => '0.0.0.0', + 'read-only' => true, + }, + 'client' => { + 'port' => '3306' + } + } +}