From aed500b6b4b05a6e27c038fe092ab67680f49a4b Mon Sep 17 00:00:00 2001
From: Thomas Goirand <zigo@debian.org>
Date: Wed, 14 Feb 2024 17:20:22 +0100
Subject: [PATCH] drive-full-checker

This implements swift_drive_full_checker_config to configure the
drive-full-checker.conf from swift when this patch is merged:
https://review.opendev.org/c/openstack/swift/+/907523

This also implements a new swift::storage::drive_full_checker
class to set values in drive-full-checker.conf. This class also
setups the cron job for drive-full-checker.

Finally, a new option use_drive_full_checker is added to
swift::storage, so that it writes to /etc/swift/rsyncd.conf
instead of directly in /etc/rsyncd.conf, so that it lets
the swift-drive-full-checker do the modification before
writing to /etc/rsyncd.conf.

Note that we need a new release of the puppetlabs-rsync module
that includes the new conf_file parameter to write in
/etc/swift/rsyncd.conf instead of /etc/rsyncd.conf directly.

Depends-On: I9828f5652c65eceae13825d6c46ca4a96949c1b5
Change-Id: I1d4b6ef73f7f29ed53cda3cf89ed5b522e4652c2
---

Index: puppet-module-swift/lib/puppet/provider/swift_drive_full_checker_config/ini_setting.rb
===================================================================
--- /dev/null
+++ puppet-module-swift/lib/puppet/provider/swift_drive_full_checker_config/ini_setting.rb
@@ -0,0 +1,10 @@
+Puppet::Type.type(:swift_drive_full_checker_config).provide(
+  :ini_setting,
+  :parent => Puppet::Type.type(:openstack_config).provider(:ini_setting)
+) do
+
+  def self.file_path
+    '/etc/swift/drive-full-checker.conf'
+  end
+
+end
Index: puppet-module-swift/lib/puppet/type/swift_drive_full_checker_config.rb
===================================================================
--- /dev/null
+++ puppet-module-swift/lib/puppet/type/swift_drive_full_checker_config.rb
@@ -0,0 +1,59 @@
+Puppet::Type.newtype(:swift_drive_full_checker_config) do
+
+  ensurable
+
+  newparam(:name, :namevar => true) do
+    desc 'Section/setting name to manage from /etc/swift/drive-full-checker.conf'
+    newvalues(/\S+\/\S+/)
+  end
+
+  newproperty(:value) do
+    desc 'The value of the setting to be defined.'
+    munge do |value|
+      if value.is_a? String and !value.match("<SERVICE DEFAULT>") and value.match?(" ")
+        if !value.match?('"')
+          value = '"' + value + '"'
+        end
+      else
+        value = value.to_s.strip
+      end
+      value.capitalize! if value =~ /^(true|false)$/i
+      value
+    end
+    newvalues(/^[\S ]*$/)
+
+    def is_to_s( currentvalue )
+      if resource.secret?
+        return '[old secret redacted]'
+      else
+        return currentvalue
+      end
+    end
+
+    def should_to_s( newvalue )
+      if resource.secret?
+        return '[new secret redacted]'
+      else
+        return newvalue
+      end
+    end
+  end
+
+  newparam(:secret, :boolean => true) do
+    desc 'Whether to hide the value from Puppet logs. Defaults to `false`.'
+
+    newvalues(:true, :false)
+
+    defaultto false
+  end
+
+  newparam(:ensure_absent_val) do
+    desc 'A value that is specified as the value property will behave as if ensure => absent was specified'
+    defaultto('<SERVICE DEFAULT>')
+  end
+
+  autorequire(:anchor) do
+    ['swift::install::end']
+  end
+
+end
Index: puppet-module-swift/manifests/storage.pp
===================================================================
--- puppet-module-swift.orig/manifests/storage.pp
+++ puppet-module-swift/manifests/storage.pp
@@ -26,7 +26,8 @@
 #
 class swift::storage(
   $storage_local_net_ip,
-  $rsync_use_xinetd = $::swift::params::xinetd_available,
+  $rsync_use_xinetd               = $::swift::params::xinetd_available,
+  Boolean $use_drive_full_checker = false,
 ) inherits swift::params {
 
   include swift::deps
@@ -35,9 +36,16 @@
     fail('xinetd is not available in this distro')
   }
 
+  if $use_drive_full_checker {
+    $rsyncdconf_path = '/etc/swift/rsyncd.conf'
+  } else {
+    $rsyncdconf_path = '/etc/rsyncd.conf'
+  }
+
   ensure_resource('class', 'rsync::server', {
     'use_xinetd' => $rsync_use_xinetd,
     'address'    => $storage_local_net_ip,
     'use_chroot' => 'no',
+    'conf_file'  => $rsyncdconf_path,
   })
 }
Index: puppet-module-swift/manifests/storage/drive_full_checker.pp
===================================================================
--- /dev/null
+++ puppet-module-swift/manifests/storage/drive_full_checker.pp
@@ -0,0 +1,155 @@
+# == Class swift::storage::drive_full_checker
+#
+# Set up swift-drive-full-checker cron job
+#
+# == Parameters
+#
+# [*user*]
+#   (Optional) User with access to swift files.
+#   Defaults to $::swift::params::user.
+#
+# [*minute*]
+#   (Optional) Defaults to '*/5'.
+#
+# [*log_facility*]
+#   (Optional) Syslog log facility.
+#   Defaults to 'LOG_LOCAL2'.
+#
+# [*log_level*]
+#   (Optional) Logging level.
+#   Defaults to 'INFO'.
+#
+# [*log_address*]
+#   (Optional) Location where syslog sends the logs to.
+#   Defaults to '/dev/log'.
+#
+# [*log_name*]
+#   (Optional) Label used when logging.
+#   Defaults to 'drive-full-checker'.
+#
+# [*log_udp_host*]
+#   (Optional) If not set, the UDP receiver for syslog is disabled.
+#   Defaults to undef.
+#
+# [*log_udp_port*]
+#   (Optional) Port value for UDP receiver, if enabled.
+#   Defaults to undef.
+#
+# [*device_dir*]
+#   (Optional) Directory devices are mounted under
+#   Defaults to $facts['os_service_default'].
+#
+# [*log_to_console*]
+#   (Optional) Make drive-full-checker log to console in addition to syslog
+#   Defaults to $facts['os_service_default'].
+#
+# [*account_max_connections*]
+#   (Optional) Maximum number of rsync connections per drive for the account ring.
+#   Default to 8.
+#
+# [*account_reserved_space*]
+#   (Optional) Reserved space in GiB until when max connectiuons is set to -1
+#   in rsyncd.conf for the account rsync module.
+#   Default to 300.
+#
+# [*account_rsyncd_section_name*]
+#   (Optional) Name of the rsync module template for accounts. The '{}' string will
+#   be replaced by swift-drive-full-checker by the name of the drive.
+#   Default to ' account_{} '.
+#
+# [*container_max_connections*]
+#   (Optional) Maximum number of rsync connections per drive for the container ring.
+#   Default to 8.
+#
+# [*container_reserved_space*]
+#   (Optional) Reserved space in GiB until when max connectiuons is set to -1
+#   in rsyncd.conf for the container rsync module.
+#   Default to 300.
+#
+# [*container_rsyncd_section_name*]
+#   (Optional) Name of the rsync module template for containers. The '{}' string will
+#   be replaced by swift-drive-full-checker by the name of the drive.
+#   Default to ' container_{} '.
+#
+# [*object_max_connections*]
+#   (Optional) Maximum number of rsync connections per drive for the object ring.
+#   Default to 8.
+#
+# [*object_reserved_space*]
+#   (Optional) Reserved space in GiB until when max connectiuons is set to -1
+#   in rsyncd.conf for the object rsync module.
+#   Default to 300.
+#
+# [*object_rsyncd_section_name*]
+#   (Optional) Name of the rsync module template for objects. The '{}' string will
+#   be replaced by swift-drive-full-checker by the name of the drive.
+#   Default to ' object_{} '.
+#
+class swift::storage::drive_full_checker(
+  # cron options
+  $user                          = $::swift::params::user,
+  $minute                        = '*/5',
+  # drive-full-checker.conf options
+  $log_facility                  = 'LOG_LOCAL2',
+  $log_level                     = 'INFO',
+  $log_address                   = '/dev/log',
+  $log_name                      = 'drive-full-checker',
+  $log_udp_host                  = undef,
+  $log_udp_port                  = undef,
+  $device_dir                    = '/srv/node',
+  $log_to_console                = $facts['os_service_default'],
+  $account_max_connections       = 8,
+  $account_reserved_space        = 100,
+  $account_rsyncd_section_name   = ' account_{} ',
+  $container_max_connections     = 8,
+  $container_reserved_space      = 100,
+  $container_rsyncd_section_name = ' container_{} ',
+  $object_max_connections        = 8,
+  $object_reserved_space         = 100,
+  $object_rsyncd_section_name    = ' object_{} ',
+) inherits swift::params {
+
+  include swift::deps
+
+  swift_drive_full_checker_config {
+    'drive-full-checker/log_name'    : value => $log_name;
+    'drive-full-checker/log_facility': value => $log_facility;
+    'drive-full-checker/log_level'   : value => $log_level;
+    'drive-full-checker/log_address' : value => $log_address;
+  }
+
+  if $log_udp_host {
+    swift_drive_full_checker_config {
+      'drive-full-checker/log_udp_host': value => $log_udp_host;
+      'drive-full-checker/log_udp_port': value => pick($log_udp_port, $facts['os_service_default']);
+    }
+  } else {
+    swift_drive_full_checker_config {
+      'drive-full-checker/log_udp_host': value => $facts['os_service_default'];
+      'drive-full-checker/log_udp_port': value => $facts['os_service_default'];
+    }
+  }
+
+  swift_drive_full_checker_config {
+    'drive-full-checker/user'                          : value => $user;
+    'drive-full-checker/device_dir'                    : value => $device_dir;
+    'drive-full-checker/log_to_console'                : value => $log_to_console;
+    'drive-full-checker/account_max_connections'       : value => $account_max_connections;
+    'drive-full-checker/account_reserved_space'        : value => $account_reserved_space;
+    'drive-full-checker/account_rsyncd_section_name'   : value => $account_rsyncd_section_name;
+    'drive-full-checker/container_max_connections'     : value => $container_max_connections;
+    'drive-full-checker/container_reserved_space'      : value => $container_reserved_space;
+    'drive-full-checker/container_rsyncd_section_name' : value => $container_rsyncd_section_name;
+    'drive-full-checker/object_max_connections'        : value => $object_max_connections;
+    'drive-full-checker/object_reserved_space'         : value => $object_reserved_space;
+    'drive-full-checker/object_rsyncd_section_name'    : value => $object_rsyncd_section_name;
+  }
+
+  cron { 'swift-full-checker':
+    command     => "swift-drive-full-checker --source-file /etc/swift/rsyncd.conf",
+    environment => 'PATH=/bin:/sbin:/usr/bin:/usr/sbin SHELL=/bin/sh',
+    user        => $user,
+    minute      => $minute,
+    require     => Anchor['swift::config::end'],
+  }
+}
Index: puppet-module-swift/spec/unit/provider/swift_drive_full_checker_config/ini_setting_spec.rb
===================================================================
--- /dev/null
+++ puppet-module-swift/spec/unit/provider/swift_drive_full_checker_config/ini_setting_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+
+provider_class = Puppet::Type.type(:swift_drive_full_checker_config).provider(:ini_setting)
+
+describe provider_class do
+
+  it 'should default to the default setting when no other one is specified' do
+    resource = Puppet::Type::Swift_drive_full_checker_config.new(
+      {
+        :name => 'DEFAULT/foo',
+        :value => 'bar'
+      }
+    )
+    provider = provider_class.new(resource)
+    expect(provider.section).to eq('DEFAULT')
+    expect(provider.setting).to eq('foo')
+  end
+
+  it 'should allow setting to be set explicitly' do
+    resource = Puppet::Type::Swift_drive_full_checker_config.new(
+      {
+        :name => 'dude/foo',
+        :value => 'bar'
+      }
+    )
+    provider = provider_class.new(resource)
+    expect(provider.section).to eq('dude')
+    expect(provider.setting).to eq('foo')
+  end
+
+  it 'should ensure absent when <SERVICE DEFAULT> is specified as a value' do
+    resource = Puppet::Type::Swift_drive_full_checker_config.new(
+      {:name => 'dude/foo', :value => '<SERVICE DEFAULT>'}
+    )
+    provider = provider_class.new(resource)
+    provider.exists?
+    expect(resource[:ensure]).to eq :absent
+  end
+
+  it 'should ensure absent when value matches ensure_absent_val' do
+    resource = Puppet::Type::Swift_drive_full_checker_config.new(
+      {:name => 'dude/foo', :value => 'foo', :ensure_absent_val => 'foo' }
+    )
+    provider = provider_class.new(resource)
+    provider.exists?
+    expect(resource[:ensure]).to eq :absent
+  end
+
+end
Index: puppet-module-swift/spec/unit/type/swift_drive_full_checker_config_spec.rb
===================================================================
--- /dev/null
+++ puppet-module-swift/spec/unit/type/swift_drive_full_checker_config_spec.rb
@@ -0,0 +1,19 @@
+require 'puppet'
+require 'puppet/type/swift_drive_full_checker_config'
+
+describe 'Puppet::Type.type(:swift_drive_full_checker_config)' do
+  before :each do
+    @swift_drive_full_checker_config = Puppet::Type.type(:swift_drive_full_checker_config).new(:name => 'DEFAULT/foo', :value => 'bar')
+  end
+
+  it 'should autorequire the package that install the file' do
+    catalog = Puppet::Resource::Catalog.new
+    anchor = Puppet::Type.type(:anchor).new(:name => 'swift::install::end')
+    catalog.add_resource anchor, @swift_drive_full_checker_config
+    dependency = @swift_drive_full_checker_config.autorequire
+    expect(dependency.size).to eq(1)
+    expect(dependency[0].target).to eq(@swift_drive_full_checker_config)
+    expect(dependency[0].source).to eq(anchor)
+  end
+
+end
