Please review pull request #654: Add openrc service provider for Gentoo and Funtoo opened by (stschulte)
Description:
Gentoo uses openrc (at least since baselayout-2) to
start/stop/enable/disable services. This can be done with the following
commands
/bin/rc-status - show runlevels
/sbin/rc-service - start/stop a service
/sbin/rc-update - enable/disable a service
The openrc provider was written as a replacement for the current gentoo
provider. The gentoo provider does just use the openrc commands to
enable/disable a service but it still calls the initscripts directly to
start a stop service. The gentoo provider does also have problems with
long service names (#5197).The reason why this is a seperate provider: #5197 states that the
rc-service command was probably introduced when gentoo upgraded from
baselayout-1 to baselayout-2 (I wasn't really able to verify this)
so the openrc provider will not work on all gentoo systems.It is also notable that the new openrc provider does not put any confines
on operatingsystem so the service provider may also be used on other
systems that use openrc (like the Gentoo fork "Funtoo")
- Opened: Wed Apr 11 19:43:45 UTC 2012
- Based on: puppetlabs:2.7.x (a6087a46a67b14277b5ce257da2df015ddf136da)
- Requested merge: stschulte:feature/2.7.x/13880 (fdb5b8afbc8947ed8b3c7a8c4546fa1e9df02d43)
Diff follows:
diff --git a/lib/puppet/provider/service/gentoo.rb b/lib/puppet/provider/service/gentoo.rb
index 8928d44..13a08f7 100644
--- a/lib/puppet/provider/service/gentoo.rb
+++ b/lib/puppet/provider/service/gentoo.rb
@@ -12,8 +12,6 @@
confine :operatingsystem => :gentoo
- defaultfor :operatingsystem => :gentoo
-
def self.defpath
superclass.defpath
end
diff --git a/lib/puppet/provider/service/openrc.rb b/lib/puppet/provider/service/openrc.rb
new file mode 100644
index 0000000..e04f61a
--- /dev/null
+++ b/lib/puppet/provider/service/openrc.rb
@@ -0,0 +1,70 @@
+# Gentoo OpenRC
+Puppet::Type.type(:service).provide :openrc, :parent => :base do
+ desc <<-EOT
+ Support for Gentoo's OpenRC Initskripts
+
+ Uses rc-update, rc-status and rc-service
+
+
+ EOT
+
+ defaultfor :operatingsystem => :gentoo
+ defaultfor :operatingsystem => :funtoo
+
+ commands :rcservice => '/sbin/rc-service'
+ commands :rcstatus => '/bin/rc-status'
+ commands :rcupdate => '/sbin/rc-update'
+
+ self::STATUSLINE = /^\s+(.*?)\s*\[\s*(.*)\s*\]$/
+
+ def enable
+ rcupdate('-C', :add, @resource[:name])
+ end
+
+ def disable
+ rcupdate('-C', :del, @resource[:name])
+ end
+
+ # rc-status -a shows all runlevels and dynamic runlevels which
+ # are not considered as enabled. We have to find out under which
+ # runlevel our service is listed
+ def enabled?
+ enabled = :false
+ rcstatus('-C', '-a').each_line do |line|
+ case line.chomp
+ when /^Runlevel: /
+ enabled = :true
+ when /^\S+/ # caption of a dynamic runlevel
+ enabled = :false
+ when self.class::STATUSLINE
+ return enabled if @resource[:name] == $1
+ end
+ end
+ :false
+ end
+
+ def self.instances
+ instances = []
+ rcservice('-C', '--list').each_line do |line|
+ instances << new(:name => line.chomp)
+ end
+ instances
+ end
+
+ def restartcmd
+ (@resource[:hasrestart] == :true) && [command(:rcservice), @resource[:name], :restart]
+ end
+
+ def startcmd
+ [command(:rcservice), @resource[:name], :start ]
+ end
+
+ def stopcmd
+ [command(:rcservice), @resource[:name], :stop]
+ end
+
+ def statuscmd
+ ((@resource.provider.get(:hasstatus) == true) || (@resource[:hasstatus] == :true)) && [command(:rcservice), @resource[:name], :status]
+ end
+
+end
diff --git a/spec/fixtures/unit/provider/service/openrc/rcservice_list b/spec/fixtures/unit/provider/service/openrc/rcservice_list
new file mode 100644
index 0000000..f413a56
--- /dev/null
+++ b/spec/fixtures/unit/provider/service/openrc/rcservice_list
@@ -0,0 +1,8 @@
+alsasound
+consolefont
+lvm-monitoring
+pydoc-2.7
+pydoc-3.2
+wpa_supplicant
+xdm
+xdm-setup
diff --git a/spec/fixtures/unit/provider/service/openrc/rcstatus b/spec/fixtures/unit/provider/service/openrc/rcstatus
new file mode 100644
index 0000000..3464951
--- /dev/null
+++ b/spec/fixtures/unit/provider/service/openrc/rcstatus
@@ -0,0 +1,42 @@
+Runlevel: boot
+ hwclock [ started ]
+ modules [ started ]
+ dmcrypt [ started ]
+ lvm [ started ]
+ fsck [ started ]
+ root [ started ]
+ mtab [ started ]
+ swap [ started ]
+ localmount [ started ]
+ sysctl [ started ]
+ bootmisc [ started ]
+ hostname [ started ]
+ termencoding [ started ]
+ keymaps [ started ]
+ net.lo [ started ]
+ procfs [ started ]
+ rsyslog [ started ]
+ swapfiles [ started ]
+ urandom [ started ]
+Runlevel: default
+ netmount [ started ]
+ xdm [ started ]
+ alsasound [ started ]
+ udev-postmount [ started ]
+ local [ started ]
+Runlevel: shutdown
+ killprocs [ stopped ]
+ savecache [ stopped ]
+ mount-ro [ stopped ]
+Runlevel: sysinit
+ dmesg [ started ]
+ udev [ started ]
+ devfs [ started ]
+Dynamic Runlevel: hotplugged
+ net.eth0 [ started ]
+ pcscd [ started ]
+Dynamic Runlevel: needed
+ sysfs [ started ]
+ udev-mount [ started ]
+Dynamic Runlevel: manual
+ sshd [ started ]
diff --git a/spec/unit/provider/service/openrc_spec.rb b/spec/unit/provider/service/openrc_spec.rb
new file mode 100755
index 0000000..8abaa64
--- /dev/null
+++ b/spec/unit/provider/service/openrc_spec.rb
@@ -0,0 +1,206 @@
+#!/usr/bin/env rspec
+
+require 'spec_helper'
+
+describe Puppet::Type.type(:service).provider(:openrc) do
+
+ before :each do
+ Puppet::Type.type(:service).stubs(:defaultprovider).returns described_class
+ end
+
+ describe ".instances" do
+
+ it "should have an instances method" do
+ described_class.should respond_to :instances
+ end
+
+ it "should get a list of services from rc-service --list" do
+ described_class.expects(:rcservice).with('-C','--list').returns File.read(my_fixture('rcservice_list'))
+ described_class.instances.map(&:name).should == [
+ 'alsasound',
+ 'consolefont',
+ 'lvm-monitoring',
+ 'pydoc-2.7',
+ 'pydoc-3.2',
+ 'wpa_supplicant',
+ 'xdm',
+ 'xdm-setup'
+ ]
+ end
+ end
+
+ describe "#start" do
+ it "should use the supplied start command if specified" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :start => '/bin/foo'))
+ provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :squelch => true)
+ provider.start
+ end
+ it "should start the service with rc-service start otherwise" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))
+ provider.expects(:execute).with(['/sbin/rc-service','sshd',:start], :failonfail => true, :squelch => true)
+ provider.start
+ end
+ end
+
+ describe "#stop" do
+ it "should use the supplied stop command if specified" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :stop => '/bin/foo'))
+ provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :squelch => true)
+ provider.stop
+ end
+ it "should stop the service with rc-service stop otherwise" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))
+ provider.expects(:execute).with(['/sbin/rc-service','sshd',:stop], :failonfail => true, :squelch => true)
+ provider.stop
+ end
+ end
+
+ describe "#enabled?" do
+
+ before :each do
+ described_class.any_instance.stubs(:rcstatus).with('-C','-a').returns File.read(my_fixture('rcstatus'))
+ end
+
+ it "should run rc-status to get a list of enabled services" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))
+ provider.expects(:rcstatus).with('-C','-a').returns "\n"
+ provider.enabled?
+ end
+
+ ['hwclock', 'modules', 'urandom'].each do |service|
+ it "should consider service #{service} in runlevel boot as enabled" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => service))
+ provider.enabled?.should == :true
+ end
+ end
+
+ ['netmount', 'xdm', 'local'].each do |service|
+ it "should consider service #{service} in runlevel default as enabled" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => service))
+ provider.enabled?.should == :true
+ end
+ end
+
+ ['net.eth0', 'pcscd'].each do |service|
+ it "should consider service #{service} in dynamic runlevel: hotplugged as disabled" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => service))
+ provider.enabled?.should == :false
+ end
+ end
+
+ ['sysfs', 'udev-mount'].each do |service|
+ it "should consider service #{service} in dynamic runlevel: needed as disabled" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => service))
+ provider.enabled?.should == :false
+ end
+ end
+
+ ['sshd'].each do |service|
+ it "should consider service #{service} in dynamic runlevel: manual as disabled" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => service))
+ provider.enabled?.should == :false
+ end
+ end
+
+ end
+
+ describe "#enable" do
+ it "should run rc-update add to enable a service" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))
+ provider.expects(:rcupdate).with('-C', :add, 'sshd')
+ provider.enable
+ end
+ end
+
+ describe "#disable" do
+ it "should run rc-update del to disable a service" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))
+ provider.expects(:rcupdate).with('-C', :del, 'sshd')
+ provider.disable
+ end
+ end
+
+ describe "#status" do
+
+ describe "when a special status command if specified" do
+ it "should use the status command from the resource" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))
+ provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :squelch => true).never
+ provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :squelch => true)
+ provider.status
+ end
+
+ it "should return :stopped when status command returns with a non-zero exitcode" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))
+ provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :squelch => true).never
+ provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :squelch => true)
+ $CHILD_STATUS.stubs(:exitstatus).returns 3
+ provider.status.should == :stopped
+ end
+
+ it "should return :running when status command returns with a zero exitcode" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))
+ provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :squelch => true).never
+ provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :squelch => true)
+ $CHILD_STATUS.stubs(:exitstatus).returns 0
+ provider.status.should == :running
+ end
+ end
+
+ describe "when hasstatus is false" do
+ it "should return running if a pid can be found" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => false))
+ provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :squelch => true).never
+ provider.expects(:getpid).returns 1000
+ provider.status.should == :running
+ end
+
+ it "should return stopped if no pid can be found" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => false))
+ provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :squelch => true).never
+ provider.expects(:getpid).returns nil
+ provider.status.should == :stopped
+ end
+ end
+
+ describe "when hasstatus is true" do
+ it "should return running if rc-service status exits with a zero exitcode" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => true))
+ provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :squelch => true)
+ $CHILD_STATUS.stubs(:exitstatus).returns 0
+ provider.status.should == :running
+ end
+
+ it "should return stopped if rc-service status exits with a non-zero exitcode" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => true))
+ provider.expects(:execute).with(['/sbin/rc-service','sshd',:status], :failonfail => false, :squelch => true)
+ $CHILD_STATUS.stubs(:exitstatus).returns 3
+ provider.status.should == :stopped
+ end
+ end
+ end
+
+ describe "#restart" do
+ it "should use the supplied restart command if specified" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :restart => '/bin/foo'))
+ provider.expects(:execute).with(['/sbin/rc-service','sshd',:restart], :failonfail => true, :squelch => true).never
+ provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :squelch => true)
+ provider.restart
+ end
+
+ it "should restart the service with rc-service restart if hasrestart is true" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => true))
+ provider.expects(:execute).with(['/sbin/rc-service','sshd',:restart], :failonfail => true, :squelch => true)
+ provider.restart
+ end
+
+ it "should restart the service with rc-service stop/start if hasrestart is false" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => false))
+ provider.expects(:execute).with(['/sbin/rc-service','sshd',:restart], :failonfail => true, :squelch => true).never
+ provider.expects(:execute).with(['/sbin/rc-service','sshd',:stop], :failonfail => true, :squelch => true)
+ provider.expects(:execute).with(['/sbin/rc-service','sshd',:start], :failonfail => true, :squelch => true)
+ provider.restart
+ end
+ end
+
+end
-- You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to [email protected].
For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.
