summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-01-13 16:20:53 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-01-13 16:20:53 +0000
commit5309479fd6c5356fcd32a0954addce6613de23aa (patch)
tree73c7e308d7fba2c6bd6f5a409fc5cf5bf8eb6ee6 /lib
parent3f15e38cb9e6cf69805b5608bda015a17c46db49 (diff)
downloadpuppet-5309479fd6c5356fcd32a0954addce6613de23aa.tar.gz
puppet-5309479fd6c5356fcd32a0954addce6613de23aa.tar.xz
puppet-5309479fd6c5356fcd32a0954addce6613de23aa.zip
Abstracting host support so it should easily support other types
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@818 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib')
-rw-r--r--lib/puppet/type.rb4
-rwxr-xr-xlib/puppet/type/cron.rb153
-rwxr-xr-xlib/puppet/type/host.rb234
-rwxr-xr-xlib/puppet/type/parsedtype.rb198
-rw-r--r--lib/puppet/type/service.rb4
5 files changed, 263 insertions, 330 deletions
diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb
index 441d5764c..019ce190b 100644
--- a/lib/puppet/type.rb
+++ b/lib/puppet/type.rb
@@ -1719,8 +1719,8 @@ class Type < Puppet::Element
newmetaparam(:alias) do
desc "Creates an alias for the object. This simplifies lookup of the
object so is useful in the language. It is especially useful when
- you are creating long commands using exec or when many different systems
- call a given package different names."
+ you are creating long commands using exec or when many different
+ systems call a given package different names."
munge do |aliases|
unless aliases.is_a?(Array)
diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb
index bc9fd6566..61f262015 100755
--- a/lib/puppet/type/cron.rb
+++ b/lib/puppet/type/cron.rb
@@ -1,89 +1,9 @@
require 'etc'
require 'facter'
require 'puppet/type/state'
+require 'puppet/filetype'
module Puppet
- # The Puppet::CronType modules are responsible for the actual abstraction.
- # They must implement three module functions: +read+, +write+, and +remove+,
- # analogous to the three flags accepted by most implementations of +crontab+.
- # All of these methods require the user name to be passed in.
- #
- # These modules operate on the strings that are ore become the cron tabs --
- # they do not have any semantic understanding of what they are reading or
- # writing.
- module CronType
- # Retrieve the uid of a user. This is duplication of code, but the unless
- # I start using Puppet.type(:user) objects here, it's a much better design.
- def self.uid(user)
- begin
- return Etc.getpwnam(user).uid
- rescue ArgumentError
- raise Puppet::Error, "User %s not found" % user
- end
- end
-
- # This module covers nearly everyone; SunOS is only known exception so far.
- module Default
- # Only add the -u flag when the user is different. Fedora apparently
- # does not think I should be allowed to set the user to myself.
- def self.cmdbase(user)
- uid = CronType.uid(user)
- cmd = nil
- if uid == Process.uid
- return "crontab"
- else
- return "crontab -u #{user}"
- end
- end
-
- # Read a specific user's cron tab.
- def self.read(user)
- tab = %x{#{self.cmdbase(user)} -l 2>/dev/null}
- end
-
- # Remove a specific user's cron tab.
- def self.remove(user)
- %x{#{self.cmdbase(user)} -r 2>/dev/null}
- end
-
- # Overwrite a specific user's cron tab; must be passed the user name
- # and the text with which to create the cron tab.
- def self.write(user, text)
- IO.popen("#{self.cmdbase(user)} -", "w") { |p|
- p.print text
- }
- end
- end
-
- # SunOS has completely different cron commands; this module implements
- # its versions.
- module SunOS
- # Read a specific user's cron tab.
- def self.read(user)
- Puppet::Util.asuser(user) {
- %x{crontab -l 2>/dev/null}
- }
- end
-
- # Remove a specific user's cron tab.
- def self.remove(user)
- Puppet.asuser(user) {
- %x{crontab -r 2>/dev/null}
- }
- end
-
- # Overwrite a specific user's cron tab; must be passed the user name
- # and the text with which to create the cron tab.
- def self.write(user, text)
- Puppet.asuser(user) {
- IO.popen("crontab", "w") { |p|
- p.print text
- }
- }
- end
- end
- end
-
# Model the actual cron jobs. Supports all of the normal cron job fields
# as parameters, with the 'command' as the single state. Also requires a
# completely symbolic 'name' paremeter, which gets written to the file
@@ -305,12 +225,29 @@ module Puppet
newparam(:name) do
desc "The symbolic name of the cron job. This name
is used for human reference only."
+
+ isnamevar
end
newparam(:user) do
desc "The user to run the command as. This user must
be allowed to run cron jobs, which is not currently checked by
Puppet."
+
+ # This validation isn't really a good idea, since the user might
+ # be created by Puppet, in which case the validation will fail.
+ validate do |user|
+ require 'etc'
+
+ begin
+ obj = Etc.getpwnam(user)
+ parent.uid = obj.uid
+ rescue ArgumentError
+ raise Puppet::Error, "User %s not found" % user
+ end
+
+ user
+ end
end
@doc = "Installs and manages cron jobs. All fields except the command
@@ -322,24 +259,19 @@ module Puppet
then the jobs will be considered equivalent and the new name will
be permanently associated with that job. Once this association is
made and synced to disk, you can then manage the job normally."
- @name = :cron
- @namevar = :name
-
- @loaded = {}
-
- @synced = {}
@instances = {}
+ @tabs = {}
case Facter["operatingsystem"].value
when "Solaris":
- @crontype = Puppet::CronType::SunOS
+ @filetype = Puppet::FileType.filetype(:suntab)
else
- @crontype = Puppet::CronType::Default
+ @filetype = Puppet::FileType.filetype(:crontab)
end
class << self
- attr_accessor :crontype
+ attr_accessor :filetype
end
attr_accessor :uid
@@ -355,8 +287,7 @@ module Puppet
# per-user cron tab information.
def self.clear
@instances = {}
- @loaded = {}
- @synced = {}
+ @tabs = {}
super
end
@@ -478,28 +409,33 @@ module Puppet
}
end
- # Retrieve a given user's cron job, using the @crontype's +retrieve+
+ # Retrieve a given user's cron job, using the @filetype's +retrieve+
# method. Returns nil if there was no cron job; else, returns the
# number of cron instances found.
def self.retrieve(user)
- text = @crontype.read(user)
+ @tabs[user] ||= @filetype.new(user)
+ text = @tabs[user].read
if $? != 0
# there is no cron file
return nil
else
self.parse(user, text)
end
+ end
- @loaded[user] = Time.now
+ # Remove a user's cron tab.
+ def self.remove(user)
+ @tabs[user] ||= @filetype.new(user)
+ @tabs[user].remove
end
# Store the user's cron tab. Collects the text of the new tab and
- # sends it to the +@crontype+ module's +write+ function. Also adds
+ # sends it to the +@filetype+ module's +write+ function. Also adds
# header warning users not to modify the file directly.
def self.store(user)
+ @tabs[user] ||= @filetype.new(user)
if @instances.include?(user)
- @crontype.write(user, self.header + self.tab(user))
- @synced[user] = Time.now
+ @tabs[user].write(self.tab(user))
else
Puppet.notice "No cron instances for %s" % user
end
@@ -509,13 +445,14 @@ module Puppet
# into literal text.
def self.tab(user)
if @instances.include?(user)
- return @instances[user].collect { |obj|
+ return self.header() + @instances[user].collect { |obj|
if obj.is_a? self
obj.to_cron
else
obj.to_s
end
}.join("\n") + "\n"
+
else
Puppet.notice "No cron instances for %s" % user
end
@@ -524,27 +461,15 @@ module Puppet
# Return the last time a given user's cron tab was loaded. Could
# be used for reducing writes, but currently is not.
def self.loaded?(user)
- if @loaded.include?(user)
- return @loaded[user]
+ if @tabs.include?(user)
+ return @loaded[user].loaded
else
return nil
end
end
- def paramuser=(user)
- require 'etc'
-
- begin
- obj = Etc.getpwnam(user)
- @uid = obj.uid
- rescue ArgumentError
- raise Puppet::Error, "User %s not found" % user
- end
- @parameters[:user] = user
- end
-
# Override the default Puppet::Type method because we need to call
- # the +@crontype+ retrieve method.
+ # the +@filetype+ retrieve method.
def retrieve
unless @parameters.include?(:user)
raise Puppet::Error, "You must specify the cron user"
diff --git a/lib/puppet/type/host.rb b/lib/puppet/type/host.rb
index 8918c521e..4b60e7142 100755
--- a/lib/puppet/type/host.rb
+++ b/lib/puppet/type/host.rb
@@ -1,91 +1,15 @@
require 'etc'
require 'facter'
+require 'puppet/type/parsedtype'
require 'puppet/type/state'
module Puppet
- # The Puppet::CronType modules are responsible for the actual abstraction.
- # They must implement three module functions: +read+, +write+, and +remove+,
- # analogous to the three flags accepted by most implementations of +hosttab+.
- # All of these methods require the user name to be passed in.
- #
- # These modules operate on the strings that are ore become the host tabs --
- # they do not have any semantic understanding of what they are reading or
- # writing.
- module FileType
-
- # This module covers nearly everyone; SunOS is only known exception so far.
- class Flat
- attr_accessor :loaded, :path, :synced
- def initialize(path)
- @path = path
- end
-
- # Read the file.
- def read
- if File.exists?(@path)
- @loaded = Time.now
- File.read(@path)
- else
- return nil
- end
- end
-
- # Remove the file.
- def remove
- if File.exists?(@path)
- File.unlink(@path)
- end
- end
-
- # Overwrite the file.
- def self
- @synced = Time.now
- File.open(@path, "w") { |f| f.print text; f.flush }
- end
- end
- end
-
- newtype(:host) do
- class HostParam < Puppet::State
- # Normally this would retrieve the current value, but our state is not
- # actually capable of doing so.
- def retrieve
- unless defined? @is and ! @is.nil?
- @is = :notfound
- end
- end
-
- # Determine whether the host entry should be destroyed, and figure
- # out which event to return. Finally, call @parent.sync to write the
- # host tab.
- def sync(nostore = false)
- event = nil
- if @is == :notfound
- @is = self.should
- event = :host_created
- elsif self.should == :notfound
- @parent.remove(true)
- event = :host_deleted
- elsif self.insync?
- return nil
- else
- @is = self.should
- event = :host_changed
- end
-
- unless nostore
- @parent.store
- end
-
- return event
- end
- end
-
- newstate(:ip, HostParam) do
+ newtype(:host, Puppet::Type::ParsedType) do
+ newstate(:ip) do
desc "The host's IP address."
end
- newstate(:alias, HostParam) do
+ newstate(:aliases) do
desc "Any aliases the host might have. Values can be either an array
or a comma-separated list."
@@ -107,13 +31,13 @@ module Puppet
@should
end
- munge do |value|
- unless value.is_a?(Array)
- value = [value]
+ munge do |values|
+ unless values.is_a?(Array)
+ values = [values]
end
# Split based on comma, then flatten the whole thing
- value.collect { |value|
- value.split(/,\s*/)
+ values.collect { |values|
+ values.split(/,\s*/)
}.flatten
end
end
@@ -130,62 +54,17 @@ module Puppet
@instances = []
- @hostfile = "/etc/hosts"
+ @path = "/etc/hosts"
+ @fields = [:ip, :name, :aliases]
- @hosttype = Puppet::FileType::Flat
+ @filetype = Puppet::FileType.filetype(:flat)
# case Facter["operatingsystem"].value
# when "Solaris":
-# @hosttype = Puppet::FileType::SunOS
+# @filetype = Puppet::FileType::SunOS
# else
-# @hosttype = Puppet::CronType::Default
+# @filetype = Puppet::CronType::Default
# end
- class << self
- attr_accessor :hosttype, :hostfile, :fileobj
- end
-
- # Override the Puppet::Type#[]= method so that we can store the instances
- # in per-user arrays. Then just call +super+.
- def self.[]=(name, object)
- self.instance(object)
- super
- end
-
- # In addition to removing the instances in @objects, Cron has to remove
- # per-user host tab information.
- def self.clear
- @instances = []
- @fileobj = nil
- super
- end
-
- # Override the default Puppet::Type method, because instances
- # also need to be deleted from the @instances hash
- def self.delete(child)
- if @instances.include?(child)
- @instances.delete(child)
- end
- super
- end
-
- def self.fields
- [:ip, :name, :alias]
- end
-
- # Return the header placed at the top of each generated file, warning
- # users that modifying this file manually is probably a bad idea.
- def self.header
-%{# This file was autogenerated at #{Time.now} by puppet. While it
-# can still be managed manually, it is definitely not recommended.\n\n}
- end
-
- # Store a new instance of a host. Called from Host#initialize.
- def self.instance(obj)
- unless @instances.include?(obj)
- @instances << obj
- end
- end
-
# Parse a host file
#
# This method also stores existing comments, and it stores all host
@@ -200,7 +79,7 @@ module Puppet
# add comments and blank lines to the list as they are
@instances << line
else
- if match = /^(\S+)\s+(\S+)\s*(\S+)$/.match(line)
+ if match = /^(\S+)\s+(\S+)\s*(\S*)$/.match(line)
fields().zip(match.captures).each { |param, value|
hash[param] = value
}
@@ -208,93 +87,20 @@ module Puppet
raise Puppet::Error, "Could not match '%s'" % line
end
- host = nil
- # if the host already exists with that name...
- if host = Puppet.type(:host)[hash[:name]]
- # do nothing...
- else
- # create a new host, since no existing one seems to
- # match
- host = self.create(
- :name => hash[:name]
- )
- hash.delete(:name)
+ if hash[:aliases] == ""
+ hash.delete(:aliases)
end
- hash.each { |param, value|
- host.is = [param, value]
- }
+ hash2obj(hash)
+
hash.clear
count += 1
end
}
end
- # Retrieve the text for the hosts file. Returns nil in the unlikely event
- # that it doesn't exist.
- def self.retrieve
- @fileobj ||= @hosttype.new(@hostfile)
- text = @fileobj.read
- if text.nil? or text == ""
- # there is no host file
- return nil
- else
- self.parse(text)
- end
- end
-
- # Write out the hosts file.
- def self.store
- @fileobj ||= @hosttype.new(@hostfile)
-
- if @instances.empty?
- Puppet.notice "No host instances for %s" % user
- else
- @fileobj.write(self.to_file())
- end
- end
-
- # Collect all Host instances convert them into literal text.
- def self.to_file
- str = self.header()
- unless @instances.empty?
- str += @instances.collect { |obj|
- if obj.is_a? self
- obj.to_host
- else
- obj.to_s
- end
- }.join("\n") + "\n"
-
- return str
- else
- Puppet.notice "No host instances for %s" % user
- return ""
- end
- end
-
- # Return the last time the hosts file was loaded. Could
- # be used for reducing writes, but currently is not.
- def self.loaded?(user)
- @fileobj ||= @hosttype.new(@hostfile)
- @fileobj.loaded
- end
-
- # Override the default Puppet::Type method because we need to call
- # the +@hosttype+ retrieve method.
- def retrieve
- @fileobj ||= @hosttype.new(@hostfile)
- self.class.retrieve()
- self.eachstate { |st| st.retrieve }
- end
-
- # Write the entire host file out.
- def store
- self.class.store()
- end
-
# Convert the current object into a host-style string.
- def to_host
+ def to_str
str = "%s\t%s" % [self.state(:ip).should, self[:name]]
if state = self.state(:alias)
diff --git a/lib/puppet/type/parsedtype.rb b/lib/puppet/type/parsedtype.rb
new file mode 100755
index 000000000..0add210d1
--- /dev/null
+++ b/lib/puppet/type/parsedtype.rb
@@ -0,0 +1,198 @@
+require 'etc'
+require 'facter'
+require 'puppet/type/state'
+
+module Puppet
+ class State
+ class ParsedParam < Puppet::State
+ @name = :parsedparam
+ # Normally this would retrieve the current value, but our state is not
+ # actually capable of doing so.
+ def retrieve
+ unless defined? @is and ! @is.nil?
+ @is = :notfound
+ end
+ end
+
+ # Determine whether the host entry should be destroyed, and figure
+ # out which event to return. Finally, call @parent.sync to write the
+ # host tab.
+ def sync(nostore = false)
+ ebase = @parent.class
+ if @is == :notfound
+ @is = self.should
+ tail = "created"
+ elsif self.should == :notfound
+ @parent.remove(true)
+ tail = "deleted"
+ elsif self.insync?
+ return nil
+ else
+ @is = self.should
+ tail = "changed"
+ end
+
+ unless nostore
+ @parent.store
+ end
+
+ return (ebase + "_" + tail).intern
+ end
+ end
+ end
+
+ class Type
+ # The collection of classes that are just simple records aggregated
+ # into a file. See 'host.rb' for an example.
+ class ParsedType < Puppet::Type
+ @name = :parsedtype
+ class << self
+ attr_accessor :filetype, :hostfile, :fileobj, :fields, :path
+ end
+
+ # Override 'newstate' so that all states default to having the
+ # correct parent type
+ def self.newstate(name, parent = nil, &block)
+ parent ||= Puppet::State::ParsedParam
+ super(name, parent, &block)
+ end
+
+ # Add another type var.
+ def self.initvars
+ @instances = []
+ super
+ end
+
+ # Override the Puppet::Type#[]= method so that we can store the
+ # instances in per-user arrays. Then just call +super+.
+ def self.[]=(name, object)
+ self.instance(object)
+ super
+ end
+
+ # In addition to removing the instances in @objects, Cron has to remove
+ # per-user host tab information.
+ def self.clear
+ @instances = []
+ @fileobj = nil
+ super
+ end
+
+ # Override the default Puppet::Type method, because instances
+ # also need to be deleted from the @instances hash
+ def self.delete(child)
+ if @instances.include?(child)
+ @instances.delete(child)
+ end
+ super
+ end
+
+ # Return the header placed at the top of each generated file, warning
+ # users that modifying this file manually is probably a bad idea.
+ def self.header
+%{# This file was autogenerated at #{Time.now} by puppet. While it
+# can still be managed manually, it is definitely not recommended.\n\n}
+ end
+
+ # Store a new instance of a host. Called from Host#initialize.
+ def self.instance(obj)
+ unless @instances.include?(obj)
+ @instances << obj
+ end
+ end
+
+ # Parse a file
+ #
+ # Subclasses must override this method.
+ def self.parse(text)
+ raise Puppet::DevError, "Parse was not overridden in %s" %
+ self.name
+ end
+
+ # Convert the hash to an object.
+ def self.hash2obj(hash)
+ obj = nil
+
+ unless hash.include?(:name) and hash[:name]
+ raise Puppet::DevError, "Hash was not passed with name"
+ end
+
+ # if the obj already exists with that name...
+ if obj = self[hash[:name]]
+ # do nothing...
+ else
+ # create a new obj, since no existing one seems to
+ # match
+ obj = self.create(
+ :name => hash[:name]
+ ) or return false
+ hash.delete(:name)
+ end
+
+ hash.each { |param, value|
+ obj.is = [param, value]
+ }
+ end
+
+ # Retrieve the text for the file. Returns nil in the unlikely
+ # event that it doesn't exist.
+ def self.retrieve
+ @fileobj ||= @filetype.new(@path)
+ text = @fileobj.read
+ if text.nil? or text == ""
+ # there is no host file
+ return nil
+ else
+ self.parse(text)
+ end
+ end
+
+ # Write out the file.
+ def self.store
+ @fileobj ||= @filetype.new(@path)
+
+ if @instances.empty?
+ Puppet.notice "No host instances for %s" % user
+ else
+ @fileobj.write(self.to_file())
+ end
+ end
+
+ # Collect all Host instances convert them into literal text.
+ def self.to_file
+ str = self.header()
+ unless @instances.empty?
+ str += @instances.collect { |obj|
+ obj.to_s
+ }.join("\n") + "\n"
+
+ return str
+ else
+ Puppet.notice "No host instances for %s" % user
+ return ""
+ end
+ end
+
+ # Return the last time the hosts file was loaded. Could
+ # be used for reducing writes, but currently is not.
+ def self.loaded?(user)
+ @fileobj ||= @filetype.new(@path)
+ @fileobj.loaded
+ end
+
+ # Override the default Puppet::Type method because we need to call
+ # the +@filetype+ retrieve method.
+ def retrieve
+ self.class.retrieve()
+ self.eachstate { |st| st.retrieve }
+ end
+
+ # Write the entire host file out.
+ def store
+ self.class.store()
+ end
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/type/service.rb b/lib/puppet/type/service.rb
index 21f258f0b..0e75828f0 100644
--- a/lib/puppet/type/service.rb
+++ b/lib/puppet/type/service.rb
@@ -208,6 +208,10 @@ module Puppet
"#{svcname}"
end
+ def self.inspect
+ "SvcType(#{svcname})"
+ end
+
def self.to_s
"SvcType(#{svcname})"
end