Fixing #751 -- the interface providers now have basic tests, at least to verify that prefetching and listing works. I think these resource types need to be largely rewritten, though, and they currently have no relationship to ifconfig, which seems strange.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@2747 980ebf18-57e1-0310-9a29-db15c13687c0
This commit is contained in:
Родитель
7bda32e9fb
Коммит
22e7b39087
|
@ -2,6 +2,8 @@ require 'puppet/provider/parsedfile'
|
|||
require 'erb'
|
||||
|
||||
Puppet::Type.type(:interface).provide(:redhat) do
|
||||
INTERFACE_DIR = "/etc/sysconfig/network-scripts"
|
||||
confine :exists => INTERFACE_DIR
|
||||
defaultfor :operatingsystem => [:fedora, :centos, :redhat]
|
||||
|
||||
# Create the setter/gettor methods to match the model.
|
||||
|
@ -32,14 +34,32 @@ LOOPBACKDUMMY
|
|||
# maximum number of aliases per interface
|
||||
MAX_ALIASES_PER_IFACE = 10
|
||||
|
||||
INTERFACE_DIR = "/etc/sysconfig/network-scripts"
|
||||
|
||||
@@dummies = []
|
||||
@@aliases = Hash.new { |hash, key| hash[key] = [] }
|
||||
|
||||
# calculate which dummy interfaces are currently already in
|
||||
# use prior to needing to call self.next_dummy later on.
|
||||
def self.instances
|
||||
# parse all of the config files at once
|
||||
Dir.glob("%s/ifcfg-*" % INTERFACE_DIR).collect do |file|
|
||||
|
||||
record = parse(file)
|
||||
|
||||
# store the existing dummy interfaces
|
||||
if record[:interface_type] == :dummy
|
||||
@@dummies << record[:ifnum] unless @@dummies.include?(record[:ifnum])
|
||||
end
|
||||
|
||||
if record[:interface_type] == :alias
|
||||
@@aliases[record[:interface]] << record[:ifnum]
|
||||
end
|
||||
new(record)
|
||||
end
|
||||
end
|
||||
|
||||
# return the next avaliable dummy interface number, in the case where
|
||||
# ifnum is not manually specified
|
||||
|
||||
def self.next_dummy
|
||||
MAX_DUMMIES.times do |i|
|
||||
unless @@dummies.include?(i.to_s)
|
||||
|
@ -60,110 +80,6 @@ LOOPBACKDUMMY
|
|||
end
|
||||
end
|
||||
|
||||
# calculate which dummy interfaces are currently already in
|
||||
# use prior to needing to call self.next_dummy later on.
|
||||
def self.prefetch
|
||||
# parse all of the config files at once
|
||||
Dir.glob("%s/ifcfg-*" % INTERFACE_DIR).each do |file|
|
||||
|
||||
record = parse(file)
|
||||
|
||||
# store the existing dummy interfaces
|
||||
if record[:interface_type] == :dummy
|
||||
@@dummies << record[:ifnum]
|
||||
end
|
||||
|
||||
if record[:interface_type] == :alias
|
||||
@@aliases[record[:interface]] << record[:ifnum]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
@model.class.validproperties.each do |property|
|
||||
if value = @model.should(property)
|
||||
@property_hash[property] = value
|
||||
end
|
||||
end
|
||||
@property_hash[:name] = @model.name
|
||||
|
||||
return (@model.class.name.to_s + "_created").intern
|
||||
end
|
||||
|
||||
def destroy
|
||||
File.unlink(@model[:target])
|
||||
end
|
||||
|
||||
def exists?
|
||||
FileTest.exists?(@model[:target])
|
||||
end
|
||||
|
||||
# generate the content for the interface file, so this is dependent
|
||||
# on whether we are adding an alias to a real interface, or a loopback
|
||||
# address (also dummy) on linux. For linux it's quite involved, and we
|
||||
# will use an ERB template
|
||||
def generate
|
||||
# choose which template to use for the interface file, based on
|
||||
# the interface type
|
||||
case @model.should(:interface_type)
|
||||
when :loopback
|
||||
return LOOPBACK_TEMPLATE.result(binding)
|
||||
when :alias
|
||||
return ALIAS_TEMPLATE.result(binding)
|
||||
end
|
||||
end
|
||||
|
||||
# Where should the file be written out?
|
||||
# This defaults to INTERFACE_DIR/ifcfg-<namevar>, but can have a
|
||||
# more symbolic name by setting interface_desc in the type.
|
||||
def file_path
|
||||
@model[:interface_desc] ||= @model[:name]
|
||||
return File.join(INTERFACE_DIR, "ifcfg-" + @model[:interface_desc])
|
||||
|
||||
end
|
||||
|
||||
# create the device name, so this based on the IP, and interface + type
|
||||
def device
|
||||
case @model.should(:interface_type)
|
||||
when :loopback
|
||||
@property_hash[:ifnum] ||= self.class.next_dummy
|
||||
return "dummy" + @property_hash[:ifnum]
|
||||
when :alias
|
||||
@property_hash[:ifnum] ||= self.class.next_alias(@model[:interface])
|
||||
return @model[:interface] + ":" + @property_hash[:ifnum]
|
||||
end
|
||||
end
|
||||
|
||||
# whether the device is to be brought up on boot or not. converts
|
||||
# the true / false of the type, into yes / no values respectively
|
||||
# writing out the ifcfg-* files
|
||||
def on_boot
|
||||
case @property_hash[:onboot].to_s
|
||||
when "true"
|
||||
return "yes"
|
||||
when "false"
|
||||
return "no"
|
||||
else
|
||||
return "neither"
|
||||
end
|
||||
end
|
||||
|
||||
# Write the new file out.
|
||||
def flush
|
||||
# Don't flush to disk if we're removing the config.
|
||||
return if @model.should(:ensure) == :absent
|
||||
|
||||
@property_hash.each do |name, val|
|
||||
if val == :absent
|
||||
raise ArgumentError, "Propety %s must be provided" % val
|
||||
end
|
||||
end
|
||||
|
||||
File.open(@model[:target], "w") do |f|
|
||||
f.puts generate()
|
||||
end
|
||||
end
|
||||
|
||||
# base the ifnum, for dummy / loopback interface in linux
|
||||
# on the last octect of the IP address
|
||||
|
||||
|
@ -198,7 +114,7 @@ LOOPBACKDUMMY
|
|||
# creating these
|
||||
opts[:ifnum] = opts[:device].sub("dummy",'')
|
||||
|
||||
@@dummies << opts[:ifnum].to_s
|
||||
@@dummies << opts[:ifnum].to_s unless @@dummies.include?(opts[:ifnum].to_s)
|
||||
else
|
||||
opts[:interface_type] = :normal
|
||||
opts[:interface] = opts[:device]
|
||||
|
@ -233,8 +149,102 @@ LOOPBACKDUMMY
|
|||
|
||||
end
|
||||
|
||||
# Prefetch our interface list, yo.
|
||||
def self.prefetch(resources)
|
||||
instances.each do |prov|
|
||||
if resource = resources[prov.name]
|
||||
resource.provider = prov
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
@resource.class.validproperties.each do |property|
|
||||
if value = @resource.should(property)
|
||||
@property_hash[property] = value
|
||||
end
|
||||
end
|
||||
@property_hash[:name] = @resource.name
|
||||
|
||||
return (@resource.class.name.to_s + "_created").intern
|
||||
end
|
||||
|
||||
def destroy
|
||||
File.unlink(@resource[:target])
|
||||
end
|
||||
|
||||
def exists?
|
||||
FileTest.exists?(@resource[:target])
|
||||
end
|
||||
|
||||
# generate the content for the interface file, so this is dependent
|
||||
# on whether we are adding an alias to a real interface, or a loopback
|
||||
# address (also dummy) on linux. For linux it's quite involved, and we
|
||||
# will use an ERB template
|
||||
def generate
|
||||
# choose which template to use for the interface file, based on
|
||||
# the interface type
|
||||
case @resource.should(:interface_type)
|
||||
when :loopback
|
||||
return LOOPBACK_TEMPLATE.result(binding)
|
||||
when :alias
|
||||
return ALIAS_TEMPLATE.result(binding)
|
||||
end
|
||||
end
|
||||
|
||||
# Where should the file be written out?
|
||||
# This defaults to INTERFACE_DIR/ifcfg-<namevar>, but can have a
|
||||
# more symbolic name by setting interface_desc in the type.
|
||||
def file_path
|
||||
@resource[:interface_desc] ||= @resource[:name]
|
||||
return File.join(INTERFACE_DIR, "ifcfg-" + @resource[:interface_desc])
|
||||
|
||||
end
|
||||
|
||||
# create the device name, so this based on the IP, and interface + type
|
||||
def device
|
||||
case @resource.should(:interface_type)
|
||||
when :loopback
|
||||
@property_hash[:ifnum] ||= self.class.next_dummy
|
||||
return "dummy" + @property_hash[:ifnum]
|
||||
when :alias
|
||||
@property_hash[:ifnum] ||= self.class.next_alias(@resource[:interface])
|
||||
return @resource[:interface] + ":" + @property_hash[:ifnum]
|
||||
end
|
||||
end
|
||||
|
||||
# whether the device is to be brought up on boot or not. converts
|
||||
# the true / false of the type, into yes / no values respectively
|
||||
# writing out the ifcfg-* files
|
||||
def on_boot
|
||||
case @property_hash[:onboot].to_s
|
||||
when "true"
|
||||
return "yes"
|
||||
when "false"
|
||||
return "no"
|
||||
else
|
||||
return "neither"
|
||||
end
|
||||
end
|
||||
|
||||
# Write the new file out.
|
||||
def flush
|
||||
# Don't flush to disk if we're removing the config.
|
||||
return if @resource.should(:ensure) == :absent
|
||||
|
||||
@property_hash.each do |name, val|
|
||||
if val == :absent
|
||||
raise ArgumentError, "Propety %s must be provided" % val
|
||||
end
|
||||
end
|
||||
|
||||
File.open(@resource[:target], "w") do |f|
|
||||
f.puts generate()
|
||||
end
|
||||
end
|
||||
|
||||
def prefetch
|
||||
@property_hash = self.class.parse(@model[:target])
|
||||
@property_hash = self.class.parse(@resource[:target])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -12,10 +12,7 @@ Puppet::Type.type(:interface).provide(:sunos,
|
|||
# Two types of lines:
|
||||
# the first line does not start with 'addif'
|
||||
# the rest do
|
||||
|
||||
|
||||
record_line :sunos, :fields => %w{interface_type name ifopts onboot}, :rts => true, :absent => "", :block_eval => :instance do
|
||||
|
||||
# Parse our interface line
|
||||
def process(line)
|
||||
details = {:ensure => :present}
|
||||
|
@ -80,6 +77,14 @@ Puppet::Type.type(:interface).provide(:sunos,
|
|||
%{}
|
||||
end
|
||||
|
||||
# Get a list of interface instances.
|
||||
def self.instances
|
||||
Dir.glob("/etc/hostname.*").collect do |file|
|
||||
# We really only expect one record from each file
|
||||
parse(file).shift
|
||||
end.collect { |record| new(record) }
|
||||
end
|
||||
|
||||
def self.match(hash)
|
||||
# see if we can match the has against an existing object
|
||||
if model.find { |obj| obj.value(:name) == hash[:name] }
|
||||
|
@ -89,10 +94,22 @@ Puppet::Type.type(:interface).provide(:sunos,
|
|||
end
|
||||
end
|
||||
|
||||
# Prefetch our interface list, yo.
|
||||
def self.prefetch(resources)
|
||||
instances.each do |prov|
|
||||
if resource = resources[prov.name]
|
||||
resource.provider = prov
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Where should the file be written out? Can be overridden by setting
|
||||
# :target in the model.
|
||||
def file_path
|
||||
return File.join("/etc", "hostname." + @model[:interface])
|
||||
unless @resource[:interface]
|
||||
raise ArgumentError, "You must provide the interface name on Solaris"
|
||||
end
|
||||
return File.join("/etc", "hostname." + @resource[:interface])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ Puppet::Type.newtype(:interface) do
|
|||
newparam(:target) do
|
||||
desc "The path to the file this resource creates."
|
||||
|
||||
defaultto { @parent.provider.file_path }
|
||||
defaultto { @resource.provider.file_path }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
$:.unshift("../../lib") if __FILE__ =~ /\.rb$/
|
||||
|
||||
require 'puppettest'
|
||||
require 'mocha'
|
||||
|
||||
class TestInterfaceType < PuppetTest::TestCase
|
||||
include PuppetTest
|
||||
|
||||
def setup
|
||||
super
|
||||
@type = Puppet::Type.type(:interface)
|
||||
end
|
||||
|
||||
def test_prefetch
|
||||
interface = @type.create(:name => "127.0.0.1", :interface => "lo0", :check => :all)
|
||||
|
||||
@type.suitableprovider.each do |provider|
|
||||
assert_nothing_raised("Could not prefetch interfaces from %s provider" % provider.name) do
|
||||
provider.prefetch("eth0" => interface)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_instances
|
||||
@type.suitableprovider.each do |provider|
|
||||
list = nil
|
||||
assert_nothing_raised("Could not get instance list from %s" % provider.name) do
|
||||
list = provider.instances
|
||||
end
|
||||
assert(list.length > 0, "Did not get any instances from %s" % provider.name)
|
||||
list.each do |interface|
|
||||
assert_instance_of(provider, interface, "%s provider returned something other than a provider instance" % provider.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# $Id$
|
Загрузка…
Ссылка в новой задаче