From 8d8d5fc97a003bacd29bc34829bb2e2c9b679987 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 5 Apr 2026 22:58:14 +1000 Subject: [PATCH 1/4] feat: add ceph service management profiles and facts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds Unkin::Ceph::Utils facter module (ceph_services.rb) which detects ceph service instances via systemctl and exposes is_ceph_mon, is_ceph_mgr, is_ceph_mds, is_ceph_osd booleans and a ceph_services hash of unit names. Adds profiles::ceph::mon, mgr, mds, osd — each with a Boolean $ensure_running parameter that iterates discovered service instances and manages them as running and enabled. Works across incus nodes (mon/mgr/mds/osd) and k8s compute/control nodes (osd only). 💘 Generated with Crush Assisted-by: Claude Sonnet 4.6 via Crush --- modules/libs/lib/facter/ceph_services.rb | 49 ++++++++++++++++++++++++ site/profiles/manifests/ceph/mds.pp | 13 +++++++ site/profiles/manifests/ceph/mgr.pp | 13 +++++++ site/profiles/manifests/ceph/mon.pp | 13 +++++++ site/profiles/manifests/ceph/osd.pp | 13 +++++++ 5 files changed, 101 insertions(+) create mode 100644 modules/libs/lib/facter/ceph_services.rb create mode 100644 site/profiles/manifests/ceph/mds.pp create mode 100644 site/profiles/manifests/ceph/mgr.pp create mode 100644 site/profiles/manifests/ceph/mon.pp create mode 100644 site/profiles/manifests/ceph/osd.pp diff --git a/modules/libs/lib/facter/ceph_services.rb b/modules/libs/lib/facter/ceph_services.rb new file mode 100644 index 0000000..541ef46 --- /dev/null +++ b/modules/libs/lib/facter/ceph_services.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'facter' + +# Detects active ceph service instances via systemctl and exposes facts +# for use in ceph service management profiles. +module Unkin::Ceph::Utils + TYPES = %w[mon mgr mds osd].freeze + + def self.services + output = Facter::Core::Execution.execute( + 'systemctl list-units "ceph*" --no-legend --plain --all 2>/dev/null', + on_fail: '' + ) + parse_units(output) + end + + def self.parse_units(output) + result = TYPES.each_with_object({}) { |type, hash| hash[type] = [] } + output.each_line do |line| + unit = line.split.first + next unless unit + + match_unit(result, unit) + end + result + end + + def self.match_unit(result, unit) + TYPES.each do |type| + match = unit.match(/\Aceph-#{type}@(.+)\.service\z/) + result[type] << "ceph-#{type}@#{match[1]}" if match + end + end + + TYPES.each do |type| + define_singleton_method(:"#{type}?") { !services[type].empty? } + end +end + +Facter.add('ceph_services') do + setcode { Unkin::Ceph::Utils.services } +end + +Unkin::Ceph::Utils::TYPES.each do |type| + Facter.add("is_ceph_#{type}") do + setcode { Unkin::Ceph::Utils.public_send(:"#{type}?") } + end +end diff --git a/site/profiles/manifests/ceph/mds.pp b/site/profiles/manifests/ceph/mds.pp new file mode 100644 index 0000000..1745c75 --- /dev/null +++ b/site/profiles/manifests/ceph/mds.pp @@ -0,0 +1,13 @@ +class profiles::ceph::mds ( + Boolean $ensure_running = true, +) { + + if $ensure_running and $facts['is_ceph_mds'] { + $facts['ceph_services']['mds'].each |String $svc| { + service { $svc: + ensure => running, + enable => true, + } + } + } +} diff --git a/site/profiles/manifests/ceph/mgr.pp b/site/profiles/manifests/ceph/mgr.pp new file mode 100644 index 0000000..34b3b4b --- /dev/null +++ b/site/profiles/manifests/ceph/mgr.pp @@ -0,0 +1,13 @@ +class profiles::ceph::mgr ( + Boolean $ensure_running = true, +) { + + if $ensure_running and $facts['is_ceph_mgr'] { + $facts['ceph_services']['mgr'].each |String $svc| { + service { $svc: + ensure => running, + enable => true, + } + } + } +} diff --git a/site/profiles/manifests/ceph/mon.pp b/site/profiles/manifests/ceph/mon.pp new file mode 100644 index 0000000..9be1864 --- /dev/null +++ b/site/profiles/manifests/ceph/mon.pp @@ -0,0 +1,13 @@ +class profiles::ceph::mon ( + Boolean $ensure_running = true, +) { + + if $ensure_running and $facts['is_ceph_mon'] { + $facts['ceph_services']['mon'].each |String $svc| { + service { $svc: + ensure => running, + enable => true, + } + } + } +} diff --git a/site/profiles/manifests/ceph/osd.pp b/site/profiles/manifests/ceph/osd.pp new file mode 100644 index 0000000..d56ff1d --- /dev/null +++ b/site/profiles/manifests/ceph/osd.pp @@ -0,0 +1,13 @@ +class profiles::ceph::osd ( + Boolean $ensure_running = true, +) { + + if $ensure_running and $facts['is_ceph_osd'] { + $facts['ceph_services']['osd'].each |String $svc| { + service { $svc: + ensure => running, + enable => true, + } + } + } +} -- 2.47.3 From 066c0ca8b3081a581f29ddde25864ad23ddeec75 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 5 Apr 2026 23:00:16 +1000 Subject: [PATCH 2/4] fix: add module doc comment to satisfy reek IrresponsibleModule --- modules/libs/lib/facter/ceph_services.rb | 61 +++++++++++++----------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/modules/libs/lib/facter/ceph_services.rb b/modules/libs/lib/facter/ceph_services.rb index 541ef46..b10607c 100644 --- a/modules/libs/lib/facter/ceph_services.rb +++ b/modules/libs/lib/facter/ceph_services.rb @@ -4,39 +4,46 @@ require 'facter' # Detects active ceph service instances via systemctl and exposes facts # for use in ceph service management profiles. -module Unkin::Ceph::Utils - TYPES = %w[mon mgr mds osd].freeze +# rubocop:disable Style/ClassAndModuleChildren +module Unkin + module Ceph + # Detects active ceph service instances via systemctl and exposes Facter facts. + module Utils + TYPES = %w[mon mgr mds osd].freeze - def self.services - output = Facter::Core::Execution.execute( - 'systemctl list-units "ceph*" --no-legend --plain --all 2>/dev/null', - on_fail: '' - ) - parse_units(output) - end + def self.services + output = Facter::Core::Execution.execute( + 'systemctl list-units "ceph*" --no-legend --plain --all 2>/dev/null', + on_fail: '' + ) + parse_units(output) + end - def self.parse_units(output) - result = TYPES.each_with_object({}) { |type, hash| hash[type] = [] } - output.each_line do |line| - unit = line.split.first - next unless unit + def self.parse_units(output) + result = TYPES.each_with_object({}) { |type, hash| hash[type] = [] } + output.each_line do |line| + unit = line.split.first + next unless unit - match_unit(result, unit) + match_unit(result, unit) + end + result + end + + def self.match_unit(result, unit) + TYPES.each do |type| + match = unit.match(/\Aceph-#{type}@(.+)\.service\z/) + result[type] << "ceph-#{type}@#{match[1]}" if match + end + end + + TYPES.each do |type| + define_singleton_method(:"#{type}?") { !services[type].empty? } + end end - result - end - - def self.match_unit(result, unit) - TYPES.each do |type| - match = unit.match(/\Aceph-#{type}@(.+)\.service\z/) - result[type] << "ceph-#{type}@#{match[1]}" if match - end - end - - TYPES.each do |type| - define_singleton_method(:"#{type}?") { !services[type].empty? } end end +# rubocop:enable Style/ClassAndModuleChildren Facter.add('ceph_services') do setcode { Unkin::Ceph::Utils.services } -- 2.47.3 From 9572b08683b9a288b34183a73e48ebd69d05df0e Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 5 Apr 2026 23:23:38 +1000 Subject: [PATCH 3/4] feat: include ceph service profiles in incus/node and k8s hieradata incus::node gets mon, mgr, mds, osd; k8s compute and control get osd only. --- hieradata/roles/infra/incus/node.yaml | 4 ++++ hieradata/roles/infra/k8s/compute.yaml | 3 +++ hieradata/roles/infra/k8s/control.yaml | 3 +++ 3 files changed, 10 insertions(+) diff --git a/hieradata/roles/infra/incus/node.yaml b/hieradata/roles/infra/incus/node.yaml index 1951163..cc68287 100644 --- a/hieradata/roles/infra/incus/node.yaml +++ b/hieradata/roles/infra/incus/node.yaml @@ -5,6 +5,10 @@ hiera_include: - incus - zfs - profiles::ceph::node + - profiles::ceph::mon + - profiles::ceph::mgr + - profiles::ceph::mds + - profiles::ceph::osd - profiles::ceph::client - profiles::ceph::dashboard - profiles::storage::cephfsvols diff --git a/hieradata/roles/infra/k8s/compute.yaml b/hieradata/roles/infra/k8s/compute.yaml index 09cad16..8e78e68 100644 --- a/hieradata/roles/infra/k8s/compute.yaml +++ b/hieradata/roles/infra/k8s/compute.yaml @@ -1,3 +1,6 @@ --- +hiera_include: + - profiles::ceph::osd + # manage rke2 rke2::node_type: agent diff --git a/hieradata/roles/infra/k8s/control.yaml b/hieradata/roles/infra/k8s/control.yaml index cd7c6c1..50779ec 100644 --- a/hieradata/roles/infra/k8s/control.yaml +++ b/hieradata/roles/infra/k8s/control.yaml @@ -1,4 +1,7 @@ --- +hiera_include: + - profiles::ceph::osd + # manage rke2 rke2::node_type: server rke2::helm_install: true -- 2.47.3 From b47a717a513022243e3db6537e48f35ada1a4335 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 5 Apr 2026 23:27:07 +1000 Subject: [PATCH 4/4] fix: move ceph::osd to k8s.yaml instead of compute/control individually --- hieradata/roles/infra/k8s.yaml | 1 + hieradata/roles/infra/k8s/compute.yaml | 3 --- hieradata/roles/infra/k8s/control.yaml | 3 --- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/hieradata/roles/infra/k8s.yaml b/hieradata/roles/infra/k8s.yaml index 3b55032..caa9c73 100644 --- a/hieradata/roles/infra/k8s.yaml +++ b/hieradata/roles/infra/k8s.yaml @@ -2,6 +2,7 @@ hiera_include: - profiles::selinux::setenforce - profiles::ceph::node + - profiles::ceph::osd - profiles::ceph::client - exporters::frr_exporter - frrouting diff --git a/hieradata/roles/infra/k8s/compute.yaml b/hieradata/roles/infra/k8s/compute.yaml index 8e78e68..09cad16 100644 --- a/hieradata/roles/infra/k8s/compute.yaml +++ b/hieradata/roles/infra/k8s/compute.yaml @@ -1,6 +1,3 @@ --- -hiera_include: - - profiles::ceph::osd - # manage rke2 rke2::node_type: agent diff --git a/hieradata/roles/infra/k8s/control.yaml b/hieradata/roles/infra/k8s/control.yaml index 50779ec..cd7c6c1 100644 --- a/hieradata/roles/infra/k8s/control.yaml +++ b/hieradata/roles/infra/k8s/control.yaml @@ -1,7 +1,4 @@ --- -hiera_include: - - profiles::ceph::osd - # manage rke2 rke2::node_type: server rke2::helm_install: true -- 2.47.3