diff --git a/lib/puppet/provider/service/gentoo.rb b/lib/puppet/provider/service/gentoo.rb index 4beb9ea6c..46e41e23c 100644 --- a/lib/puppet/provider/service/gentoo.rb +++ b/lib/puppet/provider/service/gentoo.rb @@ -1,56 +1,54 @@ # Manage gentoo services. Start/stop is the same as InitSvc, but enable/disable # is special. Puppet::Type.type(:service).provide :gentoo, :parent => :init do desc <<-EOT Gentoo's form of `init`-style service management. Uses `rc-update` for service enabling and disabling. EOT commands :update => "/sbin/rc-update" confine :operatingsystem => :gentoo - defaultfor :operatingsystem => :gentoo - def self.defpath superclass.defpath end def self.instances # this exclude list was found with grep -L '\/sbin\/runscript' /etc/init.d/* self.get_services(self.defpath, ['functions.sh', 'reboot.sh', 'shutdown.sh']) end def disable output = update :del, @resource[:name], :default rescue Puppet::ExecutionFailure raise Puppet::Error, "Could not disable #{self.name}: #{output}" end def enabled? begin output = update :show rescue Puppet::ExecutionFailure return :false end line = output.split(/\n/).find { |l| l.include?(@resource[:name]) } return :false unless line # If it's enabled then it will print output showing service | runlevel if output =~ /^\s*#{@resource[:name]}\s*\|\s*(boot|default)/ return :true else return :false end end def enable output = update :add, @resource[:name], :default rescue Puppet::ExecutionFailure raise Puppet::Error, "Could not enable #{self.name}: #{output}" end end diff --git a/lib/puppet/provider/service/openrc.rb b/lib/puppet/provider/service/openrc.rb new file mode 100644 index 000000000..a59f1d304 --- /dev/null +++ b/lib/puppet/provider/service/openrc.rb @@ -0,0 +1,69 @@ +# 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 to manage services. + + 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 000000000..f413a566c --- /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 000000000..1406671a4 --- /dev/null +++ b/spec/fixtures/unit/provider/service/openrc/rcstatus @@ -0,0 +1,43 @@ +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 ] + foo_with_very_very_long_servicename_no_still_not_the_end_wait_for_it_almost_there_almost_there_now_finally_the_end [ 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 000000000..30d7b8837 --- /dev/null +++ b/spec/unit/provider/service/openrc_spec.rb @@ -0,0 +1,210 @@ +#!/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 + ['/sbin/rc-service', '/bin/rc-status', '/sbin/rc-update'].each do |command| + FileTest.stubs(:file?).with(command).returns true + FileTest.stubs(:executable?).with(command).returns true + end + 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', 'foo_with_very_very_long_servicename_no_still_not_the_end_wait_for_it_almost_there_almost_there_now_finally_the_end'].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