# Description of yum repositories require 'puppet/util/inifile' module Puppet # A property for one entry in a .ini-style file class IniProperty < Puppet::Property def insync?(is) # A should property of :absent is the same as nil if is.nil? && should == :absent return true end super(is) end def sync if safe_insync?(retrieve) result = nil else result = set(self.should) if should == :absent resource.section[inikey] = nil else resource.section[inikey] = should end end result end def retrieve resource.section[inikey] end def inikey name.to_s end # Set the key associated with this property to KEY, instead # of using the property's NAME def self.inikey(key) # Override the inikey instance method # Is there a way to do this without resorting to strings ? # Using a block fails because the block can't access # the variable 'key' in the outer scope self.class_eval("def inikey ; \"#{key.to_s}\" ; end") end end # Doc string for properties that can be made 'absent' ABSENT_DOC="Set this to `absent` to remove it from the file completely." newtype(:yumrepo) do @doc = "The client-side description of a yum repository. Repository configurations are found by parsing `/etc/yum.conf` and the files indicated by the `reposdir` option in that file (see `yum.conf(5)` for details). Most parameters are identical to the ones documented in the `yum.conf(5)` man page. Continuation lines that yum supports (for the `baseurl`, for example) are not supported. This type does not attempt to read or verify the exinstence of files listed in the `include` attribute." class << self attr_accessor :filetype # The writer is only used for testing, there should be no need # to change yumconf or inifile in any other context attr_accessor :yumconf attr_writer :inifile end self.filetype = Puppet::Util::FileType.filetype(:flat) @inifile = nil @yumconf = "/etc/yum.conf" # Where to put files for brand new sections @defaultrepodir = nil def self.instances l = [] check = validproperties clear inifile.each_section do |s| next if s.name == "main" obj = new(:name => s.name, :audit => check) current_values = obj.retrieve obj.eachproperty do |property| if current_values[property].nil? obj.delete(property.name) else property.should = current_values[property] end end obj.delete(:audit) l << obj end l end # Return the Puppet::Util::IniConfig::File for the whole yum config def self.inifile if @inifile.nil? @inifile = read main = @inifile['main'] raise Puppet::Error, "File #{yumconf} does not contain a main section" if main.nil? reposdir = main['reposdir'] reposdir ||= "/etc/yum.repos.d, /etc/yum/repos.d" reposdir.gsub!(/[\n,]/, " ") reposdir.split.each do |dir| Dir::glob("#{dir}/*.repo").each do |file| @inifile.read(file) if File.file?(file) end end reposdir.split.each do |dir| if File::directory?(dir) && File::writable?(dir) @defaultrepodir = dir break end end end @inifile end # Parse the yum config files. Only exposed for the tests # Non-test code should use self.inifile to get at the # underlying file def self.read result = Puppet::Util::IniConfig::File.new result.read(yumconf) main = result['main'] raise Puppet::Error, "File #{yumconf} does not contain a main section" if main.nil? reposdir = main['reposdir'] reposdir ||= "/etc/yum.repos.d, /etc/yum/repos.d" reposdir.gsub!(/[\n,]/, " ") reposdir.split.each do |dir| Dir::glob("#{dir}/*.repo").each do |file| result.read(file) if File.file?(file) end end if @defaultrepodir.nil? reposdir.split.each do |dir| if File::directory?(dir) && File::writable?(dir) @defaultrepodir = dir break end end end result end # Return the Puppet::Util::IniConfig::Section with name NAME # from the yum config def self.section(name) result = inifile[name] if result.nil? # Brand new section path = yumconf path = File::join(@defaultrepodir, "#{name}.repo") unless @defaultrepodir.nil? Puppet::info "create new repo #{name} in file #{path}" result = inifile.add_section(name, path) end result end # Store all modifications back to disk def self.store inifile.store unless Puppet[:noop] target_mode = 0644 # FIXME: should be configurable inifile.each_file do |file| current_mode = File.stat(file).mode & 0777 unless current_mode == target_mode Puppet::info "changing mode of #{file} from %03o to %03o" % [current_mode, target_mode] File.chmod(target_mode, file) end end end end # This is only used during testing. def self.clear @inifile = nil @yumconf = "/etc/yum.conf" @defaultrepodir = nil end # Return the Puppet::Util::IniConfig::Section for this yumrepo resource def section self.class.section(self[:name]) end # Store modifications to this yumrepo resource back to disk def flush self.class.store end newparam(:name) do desc "The name of the repository. This corresponds to the `repositoryid` parameter in `yum.conf(5)`." isnamevar end newproperty(:descr, :parent => Puppet::IniProperty) do desc "A human-readable description of the repository. This corresponds to the name parameter in `yum.conf(5)`. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(/.*/) { } inikey "name" end newproperty(:mirrorlist, :parent => Puppet::IniProperty) do desc "The URL that holds the list of mirrors for this repository. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } # Should really check that it's a valid URL newvalue(/.*/) { } end newproperty(:baseurl, :parent => Puppet::IniProperty) do desc "The URL for this repository. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } # Should really check that it's a valid URL newvalue(/.*/) { } end newproperty(:enabled, :parent => Puppet::IniProperty) do desc "Whether this repository is enabled, as represented by a `0` or `1`. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(%r{(0|1)}) { } end newproperty(:gpgcheck, :parent => Puppet::IniProperty) do desc "Whether to check the GPG signature on packages installed from this repository, as represented by a `0` or `1`. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(%r{(0|1)}) { } end newproperty(:gpgkey, :parent => Puppet::IniProperty) do desc "The URL for the GPG key with which packages from this repository are signed. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } # Should really check that it's a valid URL newvalue(/.*/) { } end newproperty(:include, :parent => Puppet::IniProperty) do desc "The URL of a remote file containing additional yum configuration settings. Puppet does not check for this file's existence or validity. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } # Should really check that it's a valid URL newvalue(/.*/) { } end newproperty(:exclude, :parent => Puppet::IniProperty) do desc "List of shell globs. Matching packages will never be considered in updates or installs for this repo. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(/.*/) { } end newproperty(:includepkgs, :parent => Puppet::IniProperty) do desc "List of shell globs. If this is set, only packages matching one of the globs will be considered for update or install from this repo. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(/.*/) { } end newproperty(:enablegroups, :parent => Puppet::IniProperty) do desc "Whether yum will allow the use of package groups for this repository, as represented by a `0` or `1`. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(%r{(0|1)}) { } end newproperty(:failovermethod, :parent => Puppet::IniProperty) do desc "The failover methode for this repository; should be either `roundrobin` or `priority`. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(%r{roundrobin|priority}) { } end newproperty(:keepalive, :parent => Puppet::IniProperty) do desc "Whether HTTP/1.1 keepalive should be used with this repository, as represented by a `0` or `1`. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(%r{(0|1)}) { } end newproperty(:http_caching, :parent => Puppet::IniProperty) do desc "What to cache from this repository. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(%r(packages|all|none)) { } end newproperty(:timeout, :parent => Puppet::IniProperty) do desc "Number of seconds to wait for a connection before timing out. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(%r{[0-9]+}) { } end newproperty(:metadata_expire, :parent => Puppet::IniProperty) do desc "Number of seconds after which the metadata will expire. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(%r{[0-9]+}) { } end newproperty(:protect, :parent => Puppet::IniProperty) do desc "Enable or disable protection for this repository. Requires that the `protectbase` plugin is installed and enabled. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(%r{(0|1)}) { } end newproperty(:priority, :parent => Puppet::IniProperty) do desc "Priority of this repository from 1-99. Requires that the `priorities` plugin is installed and enabled. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(%r{[1-9][0-9]?}) { } end newproperty(:cost, :parent => Puppet::IniProperty) do desc "Cost of this repository.\n#{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(%r{\d+}) { } end newproperty(:proxy, :parent => Puppet::IniProperty) do desc "URL to the proxy server for this repository. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } # Should really check that it's a valid URL newvalue(/.*/) { } end newproperty(:proxy_username, :parent => Puppet::IniProperty) do desc "Username for this proxy. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(/.*/) { } end newproperty(:proxy_password, :parent => Puppet::IniProperty) do desc "Password for this proxy. #{ABSENT_DOC}" newvalue(:absent) { self.should = :absent } newvalue(/.*/) { } end end end