Hi, On Fri, 06 Feb 2015 15:49:17 +0200 Faidon Liambotis <parav...@debian.org> wrote: > This seems like a nice approach for status/start/stop/restart but > unfortunately doesn't address enabled?/enable/disable at all. For > starters, puppet seems to call update-rc.d with "defaults", not > "enable". Even "enable", though, does not seem to be sufficient for > systemd-only service files :( > > enabled? is similarly broken: it calls invoke-rc.d --query, which > returns 105 for test.service and puppet handles 105 by proceeding to > check for symlinks under /etc/rc*.d/... > > Finally, self.instances is also broken, as it just lists /etc/init.d > init files. The systemd provider calls "systemctl list-unit-files --type > service --full --all" instead. > > Honestly, I'd just switch the default provider for Debian 8+ to systemd > and let users who use a non-default init system handle it in their > manifest by supplying provider => debian or provider => upstart.
Unfortunately, switching the default provider to systemd wouldn't work either (see below). Besides that, we still have architectures where sysv-rc is the default. To put things in perspective, we currently have three types of services: A those shipping an initscript only B those shipping and initscript and a systemd service file C those shipping only a systemd service file and no initscript. This type includes all systemd-specific services, and - inevitably - many services written/managed by sysadmins. Gaudenz's patch makes sure that start/stop works for all three types of services, however it does not change the enable/disable logic nor the service listing, which are still problematic: - `update-rc.d enable|disable' does not work for type C services, as demonstrated by Faidon. - `update-rc.d -f remove && update-rc.d defaults' as currently used in the `enable' does *not* enable type C *and* type B services at all. `update-rc.d enable' would enable type B services but not type C. - querying service enable status using invoke-rc.d also doesn't work for type C services. If OTOH we were to change the default provider to systemd, we wouldn't be much better off: - `systemctl is-enabled' doesn't work for type A services, which are still the majority: $ systemctl is-enabled ferm Failed to get unit file state for ferm.service: No such file or directory - `systemctl list-unit-files' as used by systemd's self.instances, does not list type A services as well: $ systemctl list-unit-files --all --type service --no-pager | grep ferm $ So, we actually need a hybrid provider that will do the following: - Detect if we're running systemd as PID 1. This is trivial to do by checking for the existence of /run/systemd/system. - Keep compatibility with sysv-rc (and I'd suggest to drop pre-2.88 support and spare an ugly call to dpkg). - If running under systemd: • Use `systemctl enable|disable' for *all* services. This works correctly with our current systemd version for all three types of services and invokes update-rc.d if appropriate. • Use `systemctl is-enabled` to query for enabled services only for types B and C. Type A services can be detected because their (auto-generated) units have the SourcePath property set to the initscript path: $ systemctl show -pSourcePath ferm.service SourcePath=/etc/init.d/ferm For type A we need to keep the current invoke-rc.d implementation. • For self.instances, augment the type B and C services returned by `systemctl list-unit-files' with type A services from the init provider's self.instances. The attached patch on top of 3.7.2-2 (hopefully) addresses all of these issues (and drops support for pre-2.88 sysv-rc if you don't mind). I have not tested it on a sysvinit Jessie system though, so if anyone could do this it would be appreciated! Cheers, Apollon P.S.: I'm a member of the Puppet maintainers team, but I haven't touched the package (yet :). I could do an upload if anyone else can't.
From ef06e8211bb82ae4f984aaaf3a80947c633d00fd Mon Sep 17 00:00:00 2001 From: Apollon Oikonomopoulos <apoi...@debian.org> Date: Fri, 27 Feb 2015 10:55:34 +0200 Subject: [PATCH] Fix service listing and enable/disable in Debian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two support methods to detect when we're running systemd as PID 1 and if a service has only an initscript. Use these to implement the following functionality: • Under systemd, use systemctl enable/disable for all services. This works correctly for all types of services. • Under systemd, use systemctl is-enabled only for services that have a systemd unit file and fall back to invoke-rc.d for sysv services. Also, fix self.instances to augment the list of systemd-enabled services with the sysv services. Finally drop pre-2.88 sysv-rc support and use `update-rc.d enable' for all services when running under sysv-rc, preserving order changes. --- lib/puppet/provider/service/debian.rb | 94 ++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/lib/puppet/provider/service/debian.rb b/lib/puppet/provider/service/debian.rb index 9f7a2f5..681c7c8 100644 --- a/lib/puppet/provider/service/debian.rb +++ b/lib/puppet/provider/service/debian.rb @@ -15,6 +15,7 @@ Puppet::Type.type(:service).provide :debian, :parent => :init do # http://projects.reductivelabs.com/issues/2538 # is resolved. commands :invoke_rc => "/usr/sbin/invoke-rc.d" + commands :systemctl => "/bin/systemctl" # This isn't being used directly, it's just here to ensure # that the /usr/sbin/service binary is available. @@ -23,38 +24,82 @@ Puppet::Type.type(:service).provide :debian, :parent => :init do defaultfor :operatingsystem => :debian + def self.runs_on_systemd? + Dir.exists? "/run/systemd/system" + end + + def is_sysv_unit? + # The sysv generator sets the SourcePath attribute to the name of the + # initscript. Use this to detect whether a unit is backed by an initscript + # or not. + source = systemctl(:show, "-pSourcePath", @resource[:name]) + source.start_with? "SourcePath=/etc/init.d/" + end + + def self.instances + # We need to merge services with systemd unit files with those only having + # an initscript. Note that we could use `systemctl --all` to get sysv + # services as well, however it would only output *enabled* services. + i = {} + if self.runs_on_systemd? + begin + output = systemctl('list-unit-files', '--type', 'service', '--full', '--all', '--no-pager') + output.scan(/^(\S+)\.service\s+(disabled|enabled)\s*$/i).each do |m| + i[m[0]] = new(:name => m[0]) + end + rescue Puppet::ExecutionFailure + end + end + get_services(defpath).each do |sysv| + unless i.has_key?(sysv.name) + i[sysv.name] = sysv + end + end + return i.values + end + # Remove the symlinks def disable - if `dpkg --compare-versions $(dpkg-query -W --showformat '${Version}' sysv-rc) ge 2.88 ; echo $?`.to_i == 0 - update_rc @resource[:name], "disable" + if self.class.runs_on_systemd? + systemctl(:disable, @resource[:name]) else - update_rc "-f", @resource[:name], "remove" - update_rc @resource[:name], "stop", "00", "1", "2", "3", "4", "5", "6", "." + update_rc @resource[:name], "disable" end end def enabled? - # TODO: Replace system call when Puppet::Util::Execution.execute gives us a way - # to determine exit status. http://projects.reductivelabs.com/issues/2538 - system("/usr/sbin/invoke-rc.d", "--quiet", "--query", @resource[:name], "start") - - # 104 is the exit status when you query start an enabled service. - # 106 is the exit status when the policy layer supplies a fallback action - # See x-man-page://invoke-rc.d - if [104, 106].include?($CHILD_STATUS.exitstatus) - return :true - elsif [105].include?($CHILD_STATUS.exitstatus) - # 105 is unknown, which generally means the iniscript does not support query - # The debian policy states that the initscript should support methods of query - # For those that do not, peform the checks manually - # http://www.debian.org/doc/debian-policy/ch-opersys.html - if get_start_link_count >= 4 + # Initscript-backed services have no enabled status in systemd, so we + # need to query them using invoke-rc.d. + if self.class.runs_on_systemd? and not is_sysv_unit? + begin + systemctl("is-enabled", @resource[:name]) return :true - else + rescue Puppet::ExecutionFailure return :false end else - return :false + # TODO: Replace system call when Puppet::Util::Execution.execute gives us a way + # to determine exit status. http://projects.reductivelabs.com/issues/2538 + system("/usr/sbin/invoke-rc.d", "--quiet", "--query", @resource[:name], "start") + + # 104 is the exit status when you query start an enabled service. + # 106 is the exit status when the policy layer supplies a fallback action + # See x-man-page://invoke-rc.d + if [104, 106].include?($CHILD_STATUS.exitstatus) + return :true + elsif [105].include?($CHILD_STATUS.exitstatus) + # 105 is unknown, which generally means the iniscript does not support query + # The debian policy states that the initscript should support methods of query + # For those that do not, peform the checks manually + # http://www.debian.org/doc/debian-policy/ch-opersys.html + if get_start_link_count >= 4 + return :true + else + return :false + end + else + return :false + end end end @@ -63,8 +108,11 @@ Puppet::Type.type(:service).provide :debian, :parent => :init do end def enable - update_rc "-f", @resource[:name], "remove" - update_rc @resource[:name], "defaults" + if self.class.runs_on_systemd? + systemctl(:enable, @resource[:name]) + else + update_rc @resource[:name], "enable" + end end # The start, stop, restart and status command use service -- 2.1.4
signature.asc
Description: Digital signature