diff --git a/README.markdown b/README.markdown index 53e87e4d..c2c1ae75 100644 --- a/README.markdown +++ b/README.markdown @@ -20,8 +20,7 @@ The proxysql module installs, configures and manages the [ProxySQL](https://github.com/sysown/proxysql) service. -This module will install the ProxySQL and manage it's configuration. It also extends Puppet to be able to manage `mysql_users`, `mysql_servers`, `mysql_replication_hostgroups`, `mysql_query_rules`, `proxysql_servers`, `scheduler` and `global_variables`. - +This module will install the ProxySQL and manage it's configuration. It also extends Puppet to be able to manage `mysql_users`, `mysql_servers`, `mysql_replication_hostgroups`, `mysql_galera_hostgroups`, `mysql_query_rules`, `proxysql_servers`, `scheduler` and `global_variables`. ## Setup @@ -96,6 +95,16 @@ You can configure users\hostgroups\rules\schedulers using class parameters } }, ], + mysql_galera_hostgroups => [ + { + 'galera hostgroup 1' => { + 'writer_hostgroup' => 1, + 'backup_writer_hostgroup' => 2, + 'reader_hostgroup' => 3, + 'offline_hostgroup' => 4, + } + }, + ], mysql_rules => [ { 'mysql_query_rule-1' => { @@ -169,6 +178,13 @@ Or by using individual resources: offline_hostgroup => 11, } + proxy_mysql_galera_hostgroup { '1-2-3-4': + writer_hostgroup => 1, + backup_writer_hostgroup => 2, + reader_hostgroup => 3, + offline_hostgroup => 4, + } + proxy_mysql_user { 'tester': password => 'testerpwd', default_hostgroup => 30, @@ -231,6 +247,7 @@ You can override any configuration setting by using the `override_config_setting mysql_query_rules => { ... }, scheduler => { ... }, mysql_replication_hostgroups => { ... }, + mysql_galera_hostgroups => { ... }, } ``` @@ -485,6 +502,48 @@ Specifies how many transactions a resource can be behind the "master" until shun ##### `comment` Optional comment. +#### proxy_mysql_galera_hostgroup +`proxy_mysql_galera_hostgroup` manages an entry in the ProxySQL `mysql_galera_hostgroups` admin table. + +##### `ensure` +Whether the resource is present. Valid values are 'present', 'absent'. Defaults to 'present'. + +##### `name` +Name to describe the hostgroup config. Must be in a '`writer_hostgroup`-`backup_writer_hostgroup`-`reader_hostgroup`-`offline_hostgroup`' format. + +##### `load_to_runtime` +Specifies wheter the resource should be immediately loaded to the active runtime. Boolean, defaults to 'true'. + +##### `save_to_disk` +Specifies wheter the resource should be immediately save to disk. Boolean, defaults to 'true'. + +##### `writer_hostgroup` +Id of the writer hostgroup. Required. + +##### `backup_writer_hostgroup` +Id of the backup writer hostgroup. Required. + +##### `reader_hostgroup` +Id of the reader hostgroup. Required. + +##### `offline_hostgroup` +Id of the offline hostgroup. Required. + +##### `active` +Specifies wheter the resource is active or not. Integer, defaults to 1. + +##### `max_writers` +Specifies how many active writers the resource has. Integer, defaults to 1. + +##### `writer_is_also_reader` +Specifies if the writer is also a reader. Integer, defaults to 0. + +##### `max_transactions_behind` +Specifies how many transactions a resource can be behind the "master" until shunned. Integer, defaults to 0. + +##### `comment` +Optional comment. + #### proxy_mysql_server `proxy_mysql_server` manages an entry in the ProxySQL `mysql_servers` admin table. diff --git a/examples/init.pp b/examples/init.pp index 4edb1084..e9af5327 100644 --- a/examples/init.pp +++ b/examples/init.pp @@ -20,6 +20,11 @@ 'backup_writer_hostgroup' => 2, 'offline_hostgroup' => 11, } }, ], + mysql_galera_hostgroups => [ { 'hostgroup 2' => { 'reader_hostgroup' => 10, + 'writer_hostgroup' => 5, + 'backup_writer_hostgroup' => 2, + 'offline_hostgroup' => 11, } }, + ], mysql_rules => [ { 'testable to test DB' => { 'rule_id' => 1, 'match_pattern' => 'testtable', 'replace_pattern' => 'test.newtable', @@ -84,6 +89,21 @@ offline_hostgroup => 11, } +proxy_mysql_galera_hostgroup { '1-2-3-4': + writer_hostgroup => 1, + backup_writer_hostgroup => 2, + reader_hostgroup => 3, + offline_hostgroup => 4, + comment => 'Galera Replication Group 1', +} +proxy_mysql_galera_hostgroup { '5-6-7-8': + writer_hostgroup => 5, + backup_writer_hostgroup => 6, + reader_hostgroup => 7, + offline_hostgroup => 8, + comment => 'Galera Replication Group 2', +} + proxy_mysql_user { 'tester': password => 'testerpwd', default_hostgroup => 30, diff --git a/lib/puppet/provider/proxy_mysql_galera_hostgroup/proxysql.rb b/lib/puppet/provider/proxy_mysql_galera_hostgroup/proxysql.rb new file mode 100644 index 00000000..aea870fc --- /dev/null +++ b/lib/puppet/provider/proxy_mysql_galera_hostgroup/proxysql.rb @@ -0,0 +1,108 @@ +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'proxysql')) +Puppet::Type.type(:proxy_mysql_galera_hostgroup).provide(:proxysql, parent: Puppet::Provider::Proxysql) do + desc 'Manage galera hostgroup for a ProxySQL instance.' + commands mysql: 'mysql' + + # Build a property_hash containing all the discovered information about MySQL + # Galera servers. + def self.instances + instances = [] + hostgroups = mysql([defaults_file, '-NBe', + 'SELECT `writer_hostgroup`, `backup_writer_hostgroup`, `reader_hostgroup`, `offline_hostgroup`, `active`,`max_writers`,`writer_is_also_reader`,`max_transactions_behind`, `comment` FROM `mysql_galera_hostgroups`'].compact).split(%r{\n}) + + # To reduce the number of calls to MySQL we collect all the properties in + # one big swoop. + hostgroups.each do |line| + writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind, comment = line.split(%r{\t}) + name = "#{writer_hostgroup}-#{backup_writer_hostgroup}-#{reader_hostgroup}-#{offline_hostgroup}" + + instances << new( + name: name, + ensure: :present, + writer_hostgroup: writer_hostgroup, + backup_writer_hostgroup: backup_writer_hostgroup, + reader_hostgroup: reader_hostgroup, + offline_hostgroup: offline_hostgroup, + active: active, + max_writers: max_writers, + writer_is_also_reader: writer_is_also_reader, + max_transactions_behind: max_transactions_behind, + comment: comment + ) + end + instances + end + + # We iterate over each proxy_mysql_galera_hostgroup entry in the catalog and compare it against + # the contents of the property_hash generated by self.instances + def self.prefetch(resources) + hostgroups = instances + resources.keys.each do |name| + provider = hostgroups.find { |hostgroup| hostgroup.name == name } + resources[name].provider = provider if provider + end + end + + def create + _name = @resource[:name] + writer_hostgroup = @resource.value(:writer_hostgroup) + backup_writer_hostgroup = @resource.value(:backup_writer_hostgroup) + reader_hostgroup = @resource.value(:reader_hostgroup) + offline_hostgroup = @resource.value(:offline_hostgroup) + active = @resource.value(:active) + max_writers = @resource.value(:max_writers) + writer_is_also_reader = @resource.value(:writer_is_also_reader) + max_transactions_behind = @resource.value(:max_transactions_behind) + comment = @resource.value(:comment) || '' + + query = 'INSERT INTO `mysql_galera_hostgroups` (`writer_hostgroup`, `backup_writer_hostgroup`, `reader_hostgroup`, `offline_hostgroup`, `active`,`max_writers`,`writer_is_also_reader`,`max_transactions_behind`, `comment`)' + query << " VALUES (#{writer_hostgroup}, #{backup_writer_hostgroup}, #{reader_hostgroup}, #{offline_hostgroup}, #{active}, #{max_writers}, #{writer_is_also_reader}, #{max_transactions_behind}, '#{comment}')" + mysql([defaults_file, '-e', query].compact) + @property_hash[:ensure] = :present + + exists? ? (return true) : (return false) + end + + def destroy + writer_hostgroup = @resource.value(:writer_hostgroup) + backup_writer_hostgroup = @resource.value(:backup_writer_hostgroup) + reader_hostgroup = @resource.value(:reader_hostgroup) + offline_hostgroup = @resource.value(:offline_hostgroup) + query = 'DELETE FROM `mysql_galera_hostgroups`' + query << " WHERE `writer_hostgroup` = #{writer_hostgroup} AND `backup_writer_hostgroup` = #{backup_writer_hostgroup} `reader_hostgroup` = #{reader_hostgroup} AND `offline_hostgroup` = #{offline_hostgroup}" + mysql([defaults_file, '-e', query].compact) + + @property_hash.clear + exists? ? (return false) : (return true) + end + + def exists? + @property_hash[:ensure] == :present || false + end + + def flush + @property_hash.clear + load_to_runtime = @resource[:load_to_runtime] + mysql([defaults_file, '-NBe', 'LOAD MYSQL SERVERS TO RUNTIME'].compact) if load_to_runtime == :true + + save_to_disk = @resource[:save_to_disk] + mysql([defaults_file, '-NBe', 'SAVE MYSQL SERVERS TO DISK'].compact) if save_to_disk == :true + end + + # Generates method for all properties of the property_hash + mk_resource_methods + + def comment=(value) + writer_hostgroup = @resource.value(:writer_hostgroup) + backup_writer_hostgroup = @resource.value(:backup_writer_hostgroup) + reader_hostgroup = @resource.value(:reader_hostgroup) + offline_hostgroup = @resource.value(:offline_hostgroup) + + query = "UPDATE mysql_galera_hostgroups SET `comment` = '#{value}'" + query << " WHERE `writer_hostgroup` = #{writer_hostgroup} AND `backup_writer_hostgroup` = #{backup_writer_hostgroup}AND `reader_hostgroup` = #{reader_hostgroup} AND `offline_hostgroup` = #{offline_hostgroup}" + mysql([defaults_file, '-e', query].compact) + + @property_hash.clear + exists? ? (return false) : (return true) + end +end diff --git a/lib/puppet/type/proxy_mysql_galera_hostgroup.rb b/lib/puppet/type/proxy_mysql_galera_hostgroup.rb new file mode 100644 index 00000000..6f4fe6ba --- /dev/null +++ b/lib/puppet/type/proxy_mysql_galera_hostgroup.rb @@ -0,0 +1,79 @@ +# This has to be a separate type to enable collecting +Puppet::Type.newtype(:proxy_mysql_galera_hostgroup) do + @doc = 'Manage a ProxySQL mysql_galera_hostgroup.' + + ensurable + + autorequire(:file) { '/root/.my.cnf' } + autorequire(:class) { 'mysql::client' } + autorequire(:service) { 'proxysql' } + + validate do + raise('writer_hostgroup parameter is required.') if (self[:ensure] == :present) && self[:writer_hostgroup].nil? + raise('backup_writer_hostgroup parameter is required.') if (self[:ensure] == :present) && self[:backup_writer_hostgroup].nil? + raise('reader_hostgroup parameter is required.') if (self[:ensure] == :present) && self[:reader_hostgroup].nil? + raise('offline_hostgroup parameter is required.') if (self[:ensure] == :present) && self[:offline_hostgroup].nil? + raise('name must match writer_hostgroup-backup_writer_hostgroup-reader_hostgroup-offline_hostgroup parameters') if self[:name] != "#{self[:writer_hostgroup]}-#{self[:backup_writer_hostgroup]}-#{self[:reader_hostgroup]}-#{self[:offline_hostgroup]}" + end + + newparam(:name, namevar: true) do + desc 'name to describe the hostgroup config' + end + + newparam(:load_to_runtime) do + desc 'Load this entry to the active runtime.' + defaultto :true + newvalues(:true, :false) + end + + newparam(:save_to_disk) do + desc 'Perist this entry to the disk.' + defaultto :true + newvalues(:true, :false) + end + + newproperty(:writer_hostgroup) do + desc 'Writer hostgroup.' + newvalue(%r{\d+}) + end + + newproperty(:backup_writer_hostgroup) do + desc 'Backup Writer hostgroup.' + newvalue(%r{\d+}) + end + + newproperty(:reader_hostgroup) do + desc 'Reader hostgroup.' + newvalue(%r{\d+}) + end + + newproperty(:offline_hostgroup) do + desc 'Offline hostgroup.' + newvalue(%r{\d+}) + end + + newproperty(:active) do + desc 'active' + newvalue(%r{\d+}) + end + + newproperty(:max_writers) do + desc 'Maximum Writers' + newvalue(%r{\d+}) + end + + newproperty(:writer_is_also_reader) do + desc 'A writer is also used for reading' + newvalue(%r{\d+}) + end + + newproperty(:max_transactions_behind) do + desc 'Maximum Transaction Galera Node out-of-sync' + newvalue(%r{\d+}) + end + + newproperty(:comment) do + desc 'text field can be used to store any arbitrary data.' + newvalue(%r{[\w+]}) + end +end diff --git a/manifests/configure.pp b/manifests/configure.pp index 2f6707db..21966a9f 100644 --- a/manifests/configure.pp +++ b/manifests/configure.pp @@ -83,6 +83,34 @@ } } + if $proxysql::mysql_galera_hostgroups { + $proxysql::mysql_galera_hostgroups.each |$hostgroup| { + $hostgroup.each |$k,$v| { + $comment = $k + $reader = $hostgroup[$k][reader] + $backup = $hostgroup[$k][backup] + $writer = $hostgroup[$k][writer] + $offline = $hostgroup[$k][offline] + $active = $hostgroup[$k][active] + $writers = $hostgroup[$k][writers] + $writer_is_reader = $hostgroup[$k][writer_is_reader] + $max_transactions = $hostgroup[$k][max_transactions] + + proxy_mysql_galera_hostgroup { "${writer}-${backup}-${reader}-${offline}": + writer_hostgroup => $writer, + backup_writer_hostgroup => $backup, + reader_hostgroup => $reader, + offline_hostgroup => $offline, + active => $active, + max_writers => $writers, + writer_is_also_reader => $writer_is_reader, + max_transactions_behind => $max_transactions, + comment => $k, + } + } + } + } + if $proxysql::mysql_rules { $proxysql::mysql_rules.each |$rule| { $rule.each |$k,$v| { diff --git a/manifests/init.pp b/manifests/init.pp index 90006cc5..c727b8b4 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -211,6 +211,7 @@ Optional[Proxysql::User] $mysql_users = undef, Optional[Proxysql::Hostgroup] $mysql_hostgroups = undef, Optional[Proxysql::GroupReplicationHostgroup] $mysql_group_replication_hostgroups = undef, + Optional[Proxysql::GaleraHostgroup] $mysql_galera_hostgroups = undef, Optional[Proxysql::Rule] $mysql_rules = undef, Optional[Proxysql::Scheduler] $schedulers = undef, ) inherits ::proxysql::params { diff --git a/manifests/params.pp b/manifests/params.pp index f8c3e76d..73675c04 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -177,6 +177,7 @@ scheduler => {}, mysql_replication_hostgroups => {}, mysql_group_replication_hostgroups => {}, + mysql_galera_hostgroups => {}, } } diff --git a/spec/acceptance/class_spec.rb b/spec/acceptance/class_spec.rb index b3612727..9fb9e676 100644 --- a/spec/acceptance/class_spec.rb +++ b/spec/acceptance/class_spec.rb @@ -72,6 +72,29 @@ class { 'proxysql': comment => 'Test MySQL GR Cluster 3-20-6-30', } + proxy_mysql_galera_hostgroup { '1-2-3-4': + ensure => 'present', + writer_hostgroup => 1, + backup_writer_hostgroup => 2, + reader_hostgroup => 3, + offline_hostgroup => 4, + active => 1, + max_writers => 1, + writer_is_also_reader => 1, + max_transactions_behind => 100, + comment => 'Test MySQL Galera Cluster 1-2-3-4', + } + + proxy_mysql_galera_hostgroup { '5-6-7-8': + ensure => 'absent', + writer_hostgroup => 5, + backup_writer_hostgroup => 6, + reader_hostgroup => 7, + offline_hostgroup => 8, + max_transactions_behind => 100, + comment => 'Test MySQL Galera Cluster 5-6-7-8', + } + proxy_mysql_user { 'tester': ensure => 'absent', password => mysql_password('tester'), @@ -158,6 +181,16 @@ class { 'proxysql': its(:stdout) { is_expected.to eq('') } end + describe command("mysql -NB -e 'SELECT comment FROM mysql_galera_hostgroups WHERE writer_hostgroup = 1 AND backup_writer_hostgroup = 2 AND reader_hostgroup = 3 AND offline_hostgroup = 4;'") do + its(:exit_status) { is_expected.to eq 0 } + its(:stdout) { is_expected.to match '^Test MySQL Cluster 1-2-3-4$' } + end + + describe command("mysql -NB -e 'SELECT comment FROM mysql_galera_hostgroups WHERE writer_hostgroup = 5 AND backup_writer_hostgroup = 6 AND reader_hostgroup = 7 AND offline_hostgroup = 8;'") do + its(:exit_status) { is_expected.to eq 0 } + its(:stdout) { is_expected.to eq('') } + end + describe command("mysql -NB -e 'SELECT comment FROM mysql_group_replication_hostgroups WHERE writer_hostgroup = 5 AND backup_writer_hostgroup = 2 AND reader_hostgroup = 10 AND offline_hostgroup = 11;'") do its(:exit_status) { is_expected.to eq 0 } its(:stdout) { is_expected.to match '^Test MySQL GR Cluster 5-2-10-11$' } diff --git a/templates/proxysql_proxy.cnf.epp b/templates/proxysql_proxy.cnf.epp index 93532481..4b32088b 100644 --- a/templates/proxysql_proxy.cnf.epp +++ b/templates/proxysql_proxy.cnf.epp @@ -18,4 +18,7 @@ <% if $key == "mysql_group_replication_hostgroups" { -%> <%= $key %> = (<% if $value != {} { -%><%= $value %><% } -%>) <% } -%> +<% if $key == "mysql_galera_hostgroups" { -%> +<%= $key %> = (<% if $value != {} { -%><%= $value %><% } -%>) +<% } -%> <% } -%> diff --git a/types/galerahostgroup.pp b/types/galerahostgroup.pp new file mode 100644 index 00000000..22be3300 --- /dev/null +++ b/types/galerahostgroup.pp @@ -0,0 +1,10 @@ +# lint:ignore:2sp_soft_tabs +type Proxysql::GaleraHostgroup = Array[Hash[String, Struct[{ writer => Integer, + backup => Integer, + reader => Integer, + offline => Integer, + Optional[active] => Integer[1], + Optional[writers] => Integer, + Optional[writer_is_reader] => Integer[1], + Optional[max_transactions] => Integer, }],1,1]] +# lint:endignore