summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2005-09-28 17:44:47 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2005-09-28 17:44:47 +0000
commita96bdac66ba703736afa7155f4d31cbe3e9fc0ef (patch)
treeb8d5099055087c5702a55bee75d5753b41258eac /lib
parent47ba186922d5034274e871a5dfa307dd33ba9ee5 (diff)
downloadpuppet-a96bdac66ba703736afa7155f4d31cbe3e9fc0ef.tar.gz
puppet-a96bdac66ba703736afa7155f4d31cbe3e9fc0ef.tar.xz
puppet-a96bdac66ba703736afa7155f4d31cbe3e9fc0ef.zip
Okay, services are now managed, um, entirely differently. I have the beginnings of a system for supporting different service management frameworks, although I currently only support <nothing> and init scripts
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@709 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib')
-rw-r--r--lib/puppet/type/service.rb286
-rwxr-xr-xlib/puppet/type/service/base.rb17
-rwxr-xr-xlib/puppet/type/service/init.rb141
3 files changed, 329 insertions, 115 deletions
diff --git a/lib/puppet/type/service.rb b/lib/puppet/type/service.rb
index ff5fd779b..3e87a136a 100644
--- a/lib/puppet/type/service.rb
+++ b/lib/puppet/type/service.rb
@@ -1,93 +1,51 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
# this is our main way of managing processes right now
#
# a service is distinct from a process in that services
# can only be managed through the interface of an init script
# which is why they have a search path for initscripts and such
+require 'puppet/type/service/init'
+
module Puppet
class State
class ServiceRunning < State
@doc = "Whether a service should be running. **true**/*false*"
@name = :running
- #@event = :file_created
# this whole thing is annoying
# i should probably just be using booleans, but for now, i'm not...
def should=(should)
case should
when false,0,"0":
- should = 0
+ should = :stopped
when true,1,"1":
- should = 1
+ should = :running
else
- warning "%s: interpreting '%s' as false" %
+ Puppet.warning "%s: interpreting '%s' as false" %
[self.class,should]
should = 0
end
+ Puppet.debug "Service should is %s" % should
@should = should
end
def retrieve
- self.is = self.running()
- debug "Running value for '%s' is '%s'" %
+ self.is = @parent.status
+ Puppet.debug "Running value for '%s' is '%s'" %
[self.parent.name,self.is]
end
- # should i cache this info?
- def running
- begin
- status = self.parent.initcmd("status")
- debug "initcmd status for '%s' is '%s'" %
- [self.parent.name,status]
-
- if status # the command succeeded
- return 1
- else
- return 0
- end
- rescue SystemCallError
- raise "Could not execute %s" % initscript
- end
-
- end
-
def sync
- if self.running > 0
- status = 1
- else
- status = 0
- end
- debug "'%s' status is '%s' and should be '%s'" %
- [self,status,should]
event = nil
- if self.should > 0
- if status < 1
- debug "Starting '%s'" % self
- if self.parent.initcmd("start")
- event = :service_started
- else
- raise "Failed to start '%s'" % self.parent.name
- end
- else
- debug "'%s' is already running, yo" % self
- #debug "Starting '%s'" % self
- #unless self.parent.initcmd("start")
- # raise "Failed to start %s" % self.name
- #end
- end
- elsif status > 0
- debug "Stopping '%s'" % self
- if self.parent.initcmd("stop")
- event = :service_stopped
- else
- raise "Failed to stop %s" % self.name
- end
+ if @should == :running
+ @parent.start
+ event = :service_started
+ elsif @should == :stopped
+ @parent.stop
+ event = :service_stopped
else
- debug "Not running '%s' and shouldn't be running" % self
+ Puppet.debug "Not running '%s' and shouldn't be running" %
+ self
end
return event
@@ -101,19 +59,41 @@ module Puppet
Puppet::State::ServiceRunning
]
@parameters = [
+ :binary,
+ :hasstatus,
:name,
- :path
+ :path,
+ :pattern,
+ :restart,
+ :start,
+ :status,
+ :stop
]
+ @paramdoc[:binary] = "The path to the daemon. This is only used for
+ systems that do not support init scripts."
+ @paramdoc[:hasstatus] = "Declare the the service's init script has a
+ functional status command. This is assumed to be default for
+ most systems, although there might be platforms on which this is
+ assumed to be true."
@paramdoc[:name] = "The name of the service to run. This name
is used to find the init script in the search path."
@paramdoc[:path] = "The search path for finding init scripts.
There is currently no default, but hopefully soon there will
be a reasonable default for all platforms."
-
- @functions = [
- :setpath
- ]
+ @paramdoc[:pattern] = "The pattern to search for in the process table.
+ This is used for stopping services on platforms that do not
+ support init scripts, and is also used for determining service
+ status on those service whose init scripts do not include a status
+ command."
+ @paramdoc[:restart] = "Specify a *restart* command manually. If left
+ unspecified, the restart method will be determined automatically."
+ @paramdoc[:start] = "Specify a *start* command manually. If left
+ unspecified, the start method will be determined automatically."
+ @paramdoc[:status] = "Specify a *status* command manually. If left
+ unspecified, the status method will be determined automatically."
+ @paramdoc[:stop] = "Specify a *stop* command manually. If left
+ unspecified, the stop method will be determined automatically."
@doc = "Manage running services. Rather than supporting managing
individual processes, puppet uses init scripts to simplify
@@ -124,76 +104,152 @@ module Puppet
@name = :service
@namevar = :name
- @searchpaths = Array.new
- @allowedmethods = [:setpath]
-
- def initialize(hash)
- @searchpaths = []
- super
-
- unless defined? @searchpaths and @searchpaths.length > 0
- raise Puppet::Error.new(
- "You must specify a valid search path for service %s" %
- self.name
- )
+ # Return the service type we're using. Default to the Service
+ # class itself, but could be set to a module.
+ def self.svctype
+ if defined? @svctype
+ return @svctype
+ else
+ return self
end
end
- def search(name)
- @searchpaths.each { |path|
- fqname = File.join(path,name)
- begin
- stat = File.stat(fqname)
- rescue
- # should probably rescue specific errors...
- debug("Could not find %s in %s" % [name,path])
- next
- end
-
- # if we've gotten this far, we found a valid script
- return fqname
- }
- raise Puppet::Error, "Could not find init script for '%s'" % name
+ # Execute a command. Basically just makes sure it exits with a 0
+ # code.
+ def execute(type, cmd)
+ output = %x(#{cmd} 2>&1)
+ unless $? == 0
+ raise Puppet::Error, "Could not %s %s: %s" %
+ [type, self.name, output.chomp]
+ end
end
- def parampath=(ary)
- @parameters[:path] = ary
- # verify each of the paths exists
- @searchpaths = ary.find_all { |dir|
- FileTest.directory?(dir)
+ # Get the process ID for a running process. Requires the 'pattern'
+ # parameter.
+ def getpid
+ unless self[:pattern]
+ raise Puppet::Error,
+ "Either a stop command or a pattern must be specified"
+ end
+ ps = Facter["ps"].value
+ regex = Regexp.new(self[:pattern])
+ IO.popen(ps) { |table|
+ table.each { |line|
+ if regex.match(line)
+ ary = line.split(/\s+/)
+ return ary[1]
+ end
+ }
}
+
+ return nil
end
- # it'd be nice if i didn't throw the output away...
- # this command returns true if the exit code is 0, and returns
- # false otherwise
- def initcmd(cmd)
- script = self.initscript
+ def initialize(hash)
+ super
- debug "Executing '%s %s' as initcmd for '%s'" %
- [script,cmd,self]
+ if self.respond_to?(:configchk)
+ self.configchk
+ end
+ end
- rvalue = Kernel.system("%s %s" %
- [script,cmd])
+ # Basically just a synonym for restarting. Used to respond
+ # to events.
+ def refresh
+ self.restart
+ end
- debug "'%s' ran with exit status '%s'" %
- [cmd,rvalue]
+ # How to restart the process.
+ def restart
+ if self[:restart] or self.respond_to?(:restartcmd)
+ cmd = self[:restart] || self.restartcmd
+ self.execute("restart", cmd)
+ else
+ self.stop
+ self.start
+ end
+ end
+ # Check if the process is running. Prefer the 'status' parameter,
+ # then 'statuscmd' method, then look in the process table. We give
+ # the object the option to not return a status command, which might
+ # happen if, for instance, it has an init script (and thus responds to
+ # 'statuscmd') but does not have 'hasstatus' enabled.
+ def status
+ if self[:status] or (self.respond_to?(:statuscmd) and self.statuscmd)
+ cmd = self[:status] || self.statuscmd
+ output = %x(#{cmd} 2>&1)
+ Puppet.debug "%s status returned %s" %
+ [self.name, output]
+ if $? == 0
+ return :running
+ else
+ return :stopped
+ end
+ elsif pid = self.getpid
+ return :running
+ else
+ return :stopped
+ end
+ end
- rvalue
+ # Run the 'start' parameter command, or the specified 'startcmd'.
+ def start
+ cmd = self[:start] || self.startcmd
+ self.execute("start", cmd)
end
- def initscript
- if defined? @initscript
- return @initscript
+ # Stop the service. If a 'stop' parameter is specified, it
+ # takes precedence; otherwise checks if the object responds to
+ # a 'stopcmd' method, and if so runs that; otherwise, looks
+ # for the process in the process table.
+ # This method will generally not be overridden by submodules.
+ def stop
+ if self[:stop]
+ return self[:stop]
+ elsif self.respond_to?(:stopcmd)
+ self.execute("stop", self.stopcmd)
else
- @initscript = self.search(self.name)
+ pid = getpid
+ unless pid
+ Puppet.info "%s is not running" % self.name
+ return false
+ end
+ output = %x("kill #{pid} 2>&1")
+ if $? != 0
+ raise Puppet::Error,
+ "Could not kill %s, PID %s: %s" %
+ [self.name, pid, output]
+ end
+ return true
end
end
- def refresh
- self.initcmd("restart")
+ # Now load any overlay modules to provide additional functionality
+ case Facter["operatingsystem"].value
+ when "Linux":
+ case Facter["distro"].value
+ when "Debian":
+ require 'puppet/type/service/init'
+ @svctype = Puppet::ServiceTypes::InitSvc
+ end
+ when "SunOS":
+ release = Integer(Facter["operatingsystemrelease"].value)
+ if release < 5.10
+ require 'puppet/type/service/init'
+ @svctype = Puppet::ServiceTypes::InitSvc
+ else
+ require 'puppet/type/service/smf'
+ @svctype = Puppet::ServiceTypes::SMFSvc
+ end
end
- end # Puppet::Type::Service
- end # Puppet::Type
+ unless defined? @svctype
+ require 'puppet/type/service/base'
+ @svctype = Puppet::ServiceTypes::BaseSvc
+ end
+ include @svctype
+ end
+ end
end
+
+# $Id$
diff --git a/lib/puppet/type/service/base.rb b/lib/puppet/type/service/base.rb
new file mode 100755
index 000000000..6c0642541
--- /dev/null
+++ b/lib/puppet/type/service/base.rb
@@ -0,0 +1,17 @@
+module Puppet
+ module ServiceTypes
+ module BaseSvc
+
+ # The command used to start. Generated if the 'binary' argument
+ # is passed.
+ def startcmd
+ if self[:binary]
+ return self[:binary]
+ else
+ raise Puppet::Error,
+ "Services must specify a start command or a binary"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/puppet/type/service/init.rb b/lib/puppet/type/service/init.rb
new file mode 100755
index 000000000..42fdfd7f7
--- /dev/null
+++ b/lib/puppet/type/service/init.rb
@@ -0,0 +1,141 @@
+module Puppet
+ module ServiceTypes
+ module InitSvc
+ # Make sure we've got a search path set up. If they don't
+ # specify one, try to determine one.
+ def configchk
+ unless defined? @searchpaths
+ @searchpaths = []
+ end
+ unless @searchpaths.length > 0
+ if init = self.defaultinit
+ @searchpaths << init
+ else
+ Puppet.notice "No default init for %s" %
+ Facter["operatingsystem"].value
+
+ raise Puppet::Error.new(
+ "You must specify a valid search path for service %s" %
+ self.name
+ )
+ end
+ end
+ end
+
+ # Get the default init path.
+ def defaultinit
+ unless defined? @defaultinit
+ case Facter["operatingsystem"].value
+ when "FreeBSD":
+ @defaultinit = "/etc/rc.d"
+ else
+ @defaultinit = "/etc/init.d"
+ @defaultrc = "/etc/rc%s.d"
+ end
+ end
+
+ return @defaultinit
+ end
+
+ # Mark that our init script supports 'status' commands.
+ def hasstatus=(value)
+ case value
+ when true, "true": @parameters[:hasstatus] = true
+ when false, "false": @parameters[:hasstatus] = false
+ else
+ raise Puppet::Error, "Invalid 'hasstatus' value %s" %
+ value.inspect
+ end
+ end
+
+ # it'd be nice if i didn't throw the output away...
+ # this command returns true if the exit code is 0, and returns
+ # false otherwise
+ def initcmd(cmd)
+ script = self.initscript
+
+ Puppet.debug "Executing '%s %s' as initcmd for '%s'" %
+ [script,cmd,self]
+
+ rvalue = Kernel.system("%s %s" %
+ [script,cmd])
+
+ Puppet.debug "'%s' ran with exit status '%s'" %
+ [cmd,rvalue]
+
+
+ rvalue
+ end
+
+ # Where is our init script?
+ def initscript
+ if defined? @initscript
+ return @initscript
+ else
+ @initscript = self.search(self.name)
+ end
+ end
+
+ # Store the search path for init scripts. This will generally not
+ # be called.
+ def parampath=(ary)
+ unless ary.is_a?(Array)
+ ary = [ary]
+ end
+ @parameters[:path] = ary
+ @searchpaths = ary.find_all { |dir|
+ File.directory?(dir)
+ }
+ end
+
+ # Enable a service, to it's started at boot time. This basically
+ # just creates links in the RC directories, which means that, well,
+ # we need to know where the rc directories are.
+ # FIXME This should probably be a state or something, and
+ # it should actually create use Symlink objects...
+ #def enable
+ #end
+
+ #def disable
+ #end
+
+ def search(name)
+ @searchpaths.each { |path|
+ fqname = File.join(path,name)
+ begin
+ stat = File.stat(fqname)
+ rescue
+ # should probably rescue specific errors...
+ Puppet.debug("Could not find %s in %s" % [name,path])
+ next
+ end
+
+ # if we've gotten this far, we found a valid script
+ return fqname
+ }
+ raise Puppet::Error, "Could not find init script for '%s'" % name
+ end
+
+ # The start command is just the init scriptwith 'start'.
+ def startcmd
+ self.initscript + " start"
+ end
+
+ # If it was specified that the init script has a 'status' command, then
+ # we just return that; otherwise, we return false, which causes it to
+ # fallback to other mechanisms.
+ def statuscmd
+ if self[:hasstatus]
+ return self.initscript + " status"
+ else
+ return false
+ end
+ end
+
+ # The stop command is just the init script with 'stop'.
+ def stopcmd
+ self.initscript + " stop"
+ end
+ end
+ end
+end