Project

General

Profile

« Previous | Next » 

Revision 1285cc0d

Added by Dominic Cleal over 7 years ago

fixes #17354 - test and improve idempotency of OS fact parser

View differences:

app/models/operatingsystem.rb
allow :name, :media_url, :major, :minor, :family, :to_s, :repos, :==, :release_name, :kernel, :initrd, :pxe_type, :medium_uri, :boot_files_uri, :password_hash
end
def self.inherited(child)
child.instance_eval do
# Ensure all subclasses behave in the same way as the parent, and remain
# identified as Operatingsystems instead of subclasses in UI paths etc.
#
# rubocop:disable Rails/Delegate
def model_name
superclass.model_name
end
# rubocop:enable Rails/Delegate
end
super
end
# As Rails loads an object it casts it to the class in the 'type' field. If we ensure that the type and
# family are the same thing then rails converts the record to a Debian or a solaris object as required.
# Manually managing the 'type' field allows us to control the inheritance chain and the available methods
app/models/operatingsystems/aix.rb
class AIX < Operatingsystem
PXEFILES = {:kernel => "powerpc", :initrd => "initrd"}
# Override the class representation, as this breaks many rails helpers
def class
Operatingsystem
end
def pxe_type
"nim"
end
app/models/operatingsystems/altlinux.rb
class Altlinux < Operatingsystem
PXEFILES = {:kernel => "vmlinuz", :initrd => "full.cz" }
def class
Operatingsystem
end
def boot_files_uri(medium, architecture)
raise ::Foreman::Exception.new(N_("invalid medium for %s"), to_s) unless media.include?(medium)
raise ::Foreman::Exception.new(N_("invalid architecture for %s"), to_s) unless architectures.include?(architecture)
app/models/operatingsystems/archlinux.rb
class Archlinux < Operatingsystem
PXEFILES = {:kernel => "linux", :initrd => "initrd"}
class << self
delegate :model_name, :to => :superclass
end
# Simple output of the media url
def mediumpath(host)
medium_uri(host).to_s
app/models/operatingsystems/coreos.rb
class Coreos < Operatingsystem
PXEFILES = {:kernel => 'coreos_production_pxe.vmlinuz', :initrd => 'coreos_production_pxe_image.cpio.gz'}
class << self
delegate :model_name, :to => :superclass
end
def pxe_type
'coreos'
end
app/models/operatingsystems/debian.rb
s.blank? ? description : s
end
def self.model_name
superclass.model_name
end
private
# tries to guess if this an ubuntu or a debian os
app/models/operatingsystems/freebsd.rb
# -as initrd we will use your custom FreeBSD-<arch>-<version>-mfs.img in boot
PXEFILES = {}
class << self
delegate :model_name, :to => :superclass
end
# Simple output of the media url
def mediumpath(host)
medium_uri(host).to_s.gsub("x86_64","amd64")
app/models/operatingsystems/gentoo.rb
class Gentoo < Operatingsystem
PXEFILES = {}
class << self
delegate :model_name, :to => :superclass
end
def mediumpath(host)
end
app/models/operatingsystems/junos.rb
medium_uri(host).to_s
end
def class
Operatingsystem
end
# The PXE type to use when generating actions and evaluating attributes. jumpstart, kickstart and preseed are currently supported.
def pxe_type
"ZTP"
app/models/operatingsystems/nxos.rb
medium_uri(host).to_s
end
def class
Operatingsystem
end
def template_kinds
["POAP"]
end
app/models/operatingsystems/redhat.rb
class Redhat < Operatingsystem
PXEFILES = {:kernel => "vmlinuz", :initrd => "initrd.img"}
class << self
delegate :model_name, :to => :superclass
end
# outputs kickstart installation medium based on the medium type (NFS or URL)
# it also convert the $arch string to the current host architecture
def mediumpath(host)
app/models/operatingsystems/solaris.rb
class Solaris < Operatingsystem
PXEFILES = {:initrd => "x86.miniroot", :kernel => "multiboot"}
class << self
delegate :model_name, :to => :superclass
end
def file_prefix
(self).to_s.gsub(/[\s\(\)]/,"-").gsub("--", "-").gsub(/-\Z/, "")
end
app/models/operatingsystems/suse.rb
class Suse < Operatingsystem
PXEFILES = {:kernel => "linux", :initrd => "initrd"}
class << self
delegate :model_name, :to => :superclass
end
# Simple output of the media url
def mediumpath(host)
medium_uri(host).to_s
app/models/operatingsystems/windows.rb
class Windows < Operatingsystem
PXEFILES = {:kernel => "wimboot", :initrd => "bootmgr", :bcd => "bcd", :bootsdi => "boot.sdi", :bootwim => "boot.wim"}
class << self
delegate :model_name, :to => :superclass
end
def pxe_type
"waik"
end
app/models/operatingsystems/xenserver.rb
"XenServer"
end
def self.model_name
superclass.model_name
end
def bootfile(arch, type)
pxe_prefix(arch) + "-" + eval("#{self.family}::PXEFILES[:#{type}]").split("/")[-1]
end
app/services/puppet_fact_parser.rb
attr_reader :facts
def operatingsystem
orel = os_release
orel = os_release.dup
if os_name == "Archlinux"
# Archlinux is rolling release, so it has no release. We use 1.0 always
......
end
if os.description.blank?
if os_name == 'SLES'
os.description = os_name + ' ' + orel.gsub!('.', ' SP')
os.description = os_name + ' ' + orel.gsub('.', ' SP')
elsif facts[:lsbdistdescription]
family = os.deduce_family || 'Operatingsystem'
os.description = family.constantize.shorten_description facts[:lsbdistdescription]
end
end
os.save!
os
if os.new_record?
os.save!
Operatingsystem.find_by_id(os.id) # complete reload to be an instance of the STI subclass
else
os.save!
os
end
end
def environment
test/unit/puppet_fact_parser_test.rb
assert_equal '192.168.0.1', parser.interfaces['eth0.0']['ipaddress']
end
test "should return an os" do
assert_kind_of Operatingsystem, importer.operatingsystem
end
test "should raise on an invalid os" do
@importer = PuppetFactParser.new({})
assert_raise ::Foreman::Exception do
importer.operatingsystem
end
end
test "should return an env" do
assert_kind_of Environment, importer.environment
end
......
assert_kind_of Domain, importer.domain
end
test "should make non-numeric os version strings into numeric" do
@importer = PuppetFactParser.new({'operatingsystem' => 'AnyOS', 'operatingsystemrelease' => '1&2.3y4'})
data = importer.operatingsystem
assert_equal '12', data.major
assert_equal '34', data.minor
end
describe '#operatingsystem' do
let(:os) { importer.operatingsystem }
test "should allow OS version minor component to be nil" do
@importer = PuppetFactParser.new({'operatingsystem' => 'AnyOS', 'operatingsystemrelease' => '6'})
data = importer.operatingsystem
assert_equal "AnyOS 6", data.to_s
assert_equal '6', data.major
assert_empty data.minor
assert_equal data, importer.operatingsystem
end
test "should return an os" do
assert_kind_of Operatingsystem, os
assert_os_idempotent
end
test "release_name should be nil when lsbdistcodename isn't set on Debian" do
@importer = PuppetFactParser.new(debian_facts.delete_if { |k, v| k == "lsbdistcodename" })
assert_equal nil, @importer.operatingsystem.release_name
end
test "should raise on an invalid os" do
@importer = PuppetFactParser.new({})
assert_raise ::Foreman::Exception do
importer.operatingsystem
end
end
test "should set os.release_name to the lsbdistcodename fact on Debian" do
@importer = PuppetFactParser.new(debian_facts)
assert_equal 'wheezy', @importer.operatingsystem.release_name
end
test "should make non-numeric os version strings into numeric" do
@importer = PuppetFactParser.new({'operatingsystem' => 'AnyOS', 'operatingsystemrelease' => '1&2.3y4'})
assert_equal '12', os.major
assert_equal '34', os.minor
assert_os_idempotent
end
test "should not set os.release_name to the lsbdistcodename on non-Debian OS" do
assert_not_equal 'Santiago', @importer.operatingsystem.release_name
end
test "should allow OS version minor component to be nil" do
@importer = PuppetFactParser.new({'operatingsystem' => 'AnyOS', 'operatingsystemrelease' => '6'})
assert_equal "AnyOS 6", os.to_s
assert_equal '6', os.major
assert_empty os.minor
assert_os_idempotent
end
test "should set description field from lsbdistdescription" do
assert_equal "RHEL Server 6.2", @importer.operatingsystem.description
end
test "release_name should be nil when lsbdistcodename isn't set on Debian" do
@importer = PuppetFactParser.new(debian_facts.delete_if { |k, v| k == "lsbdistcodename" })
assert_equal nil, os.release_name
assert_os_idempotent
end
test "should not alter description field if already set" do
# Need to instantiate @importer once with normal facts
assert @importer.operatingsystem.present?
# Now re-import with a different description
facts_with_desc = facts.merge({:lsbdistdescription => "A different string"})
@importer = PuppetFactParser.new facts_with_desc
assert_equal "RHEL Server 6.2", @importer.operatingsystem.description
end
test "should set os.release_name to the lsbdistcodename fact on Debian" do
@importer = PuppetFactParser.new(debian_facts)
assert_equal 'wheezy', os.release_name
assert_os_idempotent
end
test "should set description correctly for SLES" do
@importer = PuppetFactParser.new(sles_facts)
assert_equal 'SLES 11 SP3', @importer.operatingsystem.description
end
test "should not set os.release_name to the lsbdistcodename on non-Debian OS" do
assert_not_equal 'Santiago', os.release_name
end
test "should not set description if lsbdistdescription is missing" do
facts.delete('lsbdistdescription')
@importer = PuppetFactParser.new(facts)
refute @importer.operatingsystem.description
end
test "should set description field from lsbdistdescription" do
assert_equal "RHEL Server 6.2", os.description
end
test 'should accept y.z minor version' do
FactoryGirl.create(:operatingsystem, name: "CentOS",
major: "7",
minor: "2.1511",
description: "CentOS Linux 7.2.1511")
assert_valid PuppetFactParser.new({ "operatingsystem" => "CentOS",
"lsbdistdescription" => "CentOS Linux release 7.2.1511 (Core) ",
"operatingsystemrelease" => "7.2.1511"
}).operatingsystem
end
test "should not alter description field if already set" do
# Need to instantiate @importer once with normal facts
first_os = @importer.operatingsystem
assert first_os.present?
# Now re-import with a different description
facts_with_desc = facts.merge({:lsbdistdescription => "A different string"})
@importer = PuppetFactParser.new facts_with_desc
second_os = @importer.operatingsystem
assert_equal "RHEL Server 6.2", second_os.description
assert_equal first_os, second_os
end
test "should set os.major and minor correctly from AIX facts" do
@importer = PuppetFactParser.new(aix_facts)
assert_equal 'AIX', @importer.operatingsystem.family
assert_equal '6100', @importer.operatingsystem.major
assert_equal '0604', @importer.operatingsystem.minor
end
test "should set description correctly for SLES" do
@importer = PuppetFactParser.new(sles_facts)
assert_equal 'SLES 11 SP3', os.description
assert_os_idempotent
end
test 'should handle FreeBSD rolling releases correctly' do
@importer = PuppetFactParser.new(freebsd_stable_facts)
assert_equal '10', @importer.operatingsystem.major
assert_equal '1', @importer.operatingsystem.minor
end
test "should not set description if lsbdistdescription is missing" do
facts.delete('lsbdistdescription')
@importer = PuppetFactParser.new(facts)
refute os.description
assert_os_idempotent
end
test 'should handle FreeBSD patch releases correctly' do
@importer = PuppetFactParser.new(freebsd_patch_facts)
assert_equal '10', @importer.operatingsystem.major
assert_equal '1', @importer.operatingsystem.minor
end
test 'should accept y.z minor version' do
FactoryGirl.create(:operatingsystem, name: "CentOS",
major: "7",
minor: "2.1511",
description: "CentOS Linux 7.2.1511")
@importer = PuppetFactParser.new("operatingsystem" => "CentOS",
"lsbdistdescription" => "CentOS Linux release 7.2.1511 (Core) ",
"operatingsystemrelease" => "7.2.1511")
assert_valid os
assert_os_idempotent
end
test "should set os.major and minor correctly from Solaris 10 facts" do
@importer = PuppetFactParser.new(read_json_fixture('facts/solaris10.json'))
os = @importer.operatingsystem
assert_equal 'Solaris', os.family
assert_equal '10', os.major
assert_equal '9', os.minor
test "should set os.major and minor correctly from AIX facts" do
@importer = PuppetFactParser.new(aix_facts)
assert_equal 'AIX', os.family
assert_equal '6100', os.major
assert_equal '0604', os.minor
assert_os_idempotent
end
test 'should handle FreeBSD rolling releases correctly' do
@importer = PuppetFactParser.new(freebsd_stable_facts)
assert_equal '10', os.major
assert_equal '1', os.minor
assert_os_idempotent
end
test 'should handle FreeBSD patch releases correctly' do
@importer = PuppetFactParser.new(freebsd_patch_facts)
assert_equal '10', os.major
assert_equal '1', os.minor
assert_os_idempotent
end
test "should set os.major and minor correctly from Solaris 10 facts" do
@importer = PuppetFactParser.new(read_json_fixture('facts/solaris10.json'))
os = @importer.operatingsystem
assert_equal 'Solaris', os.family
assert_equal '10', os.major
assert_equal '9', os.minor
assert_os_idempotent
end
end
test "#get_interfaces" do
......
def freebsd_patch_facts
read_json_fixture('facts/facts_freebsd_patch.json')['facts']
end
def assert_os_idempotent(previous_os = self.os)
assert_equal previous_os, importer.operatingsystem, 'Different operating system returned on second call'
assert_equal previous_os.attributes, importer.operatingsystem.attributes, 'Different operating system attributes set on second call'
end
end

Also available in: Unified diff