Hi, On Fri, 06 Feb 2015 15:49:17 +0200 Faidon Liambotis <[email protected]> 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 <[email protected]> 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

