Project

General

Profile

Download (10.4 KB) Statistics
| Branch: | Tag: | Revision:
require 'ostruct'
require 'uri'

class Operatingsystem < ApplicationRecord
audited
include Authorizable
include ValidateOsFamily
include PxeLoaderSupport
extend FriendlyId
friendly_id :title

validates_lengths_from_database
before_destroy EnsureNotUsedBy.new(:hosts, :hostgroups)
has_many_hosts
has_many :hostgroups
has_many :images, :dependent => :destroy
has_and_belongs_to_many :media
has_and_belongs_to_many :ptables, :join_table => :operatingsystems_ptables, :foreign_key => :operatingsystem_id, :association_foreign_key => :ptable_id
has_and_belongs_to_many :architectures
has_and_belongs_to_many :puppetclasses
has_and_belongs_to_many :provisioning_templates, :join_table => :operatingsystems_provisioning_templates, :foreign_key => :operatingsystem_id, :association_foreign_key => :provisioning_template_id
has_many :os_default_templates, :dependent => :destroy
accepts_nested_attributes_for :os_default_templates, :allow_destroy => true,
:reject_if => :reject_empty_provisioning_template

validates :major, :numericality => {:greater_than_or_equal_to => 0}, :presence => { :message => N_("Operating System version is required") }
has_many :os_parameters, :dependent => :destroy, :foreign_key => :reference_id, :inverse_of => :operatingsystem
has_many :parameters, :dependent => :destroy, :foreign_key => :reference_id, :class_name => "OsParameter"
accepts_nested_attributes_for :os_parameters, :allow_destroy => true
include ParameterValidators
has_many :trends, :as => :trendable, :class_name => "ForemanTrend"

attr_name :to_label
validates :minor, :numericality => {:greater_than_or_equal_to => 0}, :allow_nil => true, :allow_blank => true
validates :name, :presence => true, :no_whitespace => true,
:uniqueness => { :scope => [:major, :minor], :message => N_("Operating system version already exists")}
validates :description, :uniqueness => true, :allow_blank => true
validates :password_hash, :inclusion => { :in => PasswordCrypt::ALGORITHMS }
before_validation :downcase_release_name, :set_title, :stringify_major_and_minor
validates :title, :uniqueness => true, :presence => true

before_save :set_family

default_scope -> { order(:title) }

scoped_search :on => :name, :complete_value => :true
scoped_search :on => :major, :complete_value => :true
scoped_search :on => :minor, :complete_value => :true
scoped_search :on => :description, :complete_value => :true
scoped_search :on => :type, :complete_value => :true, :rename => "family"
scoped_search :on => :title, :complete_value => :true

scoped_search :relation => :architectures, :on => :name, :complete_value => :true, :rename => "architecture", :only_explicit => true
scoped_search :relation => :media, :on => :name, :complete_value => :true, :rename => "medium", :only_explicit => true
scoped_search :relation => :provisioning_templates, :on => :name, :complete_value => :true, :rename => "template", :only_explicit => true
scoped_search :relation => :os_parameters, :on => :value, :on_key => :name, :complete_value => true, :rename => :params, :only_explicit => true

FAMILIES = { 'Debian' => %r{Debian|Ubuntu}i,
'Redhat' => %r{RedHat|Centos|Fedora|Scientific|SLC|OracleLinux}i,
'Suse' => %r{OpenSuSE|SLES|SLED}i,
'Windows' => %r{Windows}i,
'Altlinux' => %r{Altlinux}i,
'Archlinux' => %r{Archlinux}i,
'Coreos' => %r{CoreOS}i,
'Rancheros' => %r{RancherOS}i,
'Gentoo' => %r{Gentoo}i,
'Solaris' => %r{Solaris}i,
'Freebsd' => %r{FreeBSD}i,
'AIX' => %r{AIX}i,
'Junos' => %r{Junos}i,
'NXOS' => %r{NX-OS}i,
'Xenserver' => %r{XenServer}i }

class Jail < Safemode::Jail
allow :name, :media_url, :major, :minor, :family, :to_s, :repos, :==, :release, :release_name, :kernel, :initrd, :pxe_type, :medium_uri, :boot_files_uri, :password_hash
end

def self.title_name
"title".freeze
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
def family
self[:type]
end

def family=(value)
self.type = value
end

def self.families
FAMILIES.keys.sort
end
validate_inclusion_in_families :type

def self.families_as_collection
families.map do |f|
OpenStruct.new(:name => f.constantize.new.display_family, :value => f)
end
end

# Operating system family can override this method to provide an array of
# hashes, each describing a repository. For example, to describe a yum repo,
# the following structure can be returned by the method:
# [{ :baseurl => "https://dl.thesource.com/get/it/here",
# :name => "awesome",
# :description => "awesome product repo"",
# :enabled => 1,
# :gpgcheck => 1
# }]
def repos(host)
[]
end

def medium_uri(host, url = nil)
url ||= host.medium.path
medium_vars_to_uri(url, host.architecture.name, host.operatingsystem)
end

def medium_vars_to_uri(url, arch, os)
URI.parse(interpolate_medium_vars(url, arch, os)).normalize
end

def interpolate_medium_vars(path, arch, os)
return "" if path.empty?

path.gsub('$arch', arch).
gsub('$major', os.major).
gsub('$minor', os.minor).
gsub('$version', os.minor.blank? ? os.major : [os.major, os.minor].compact.join('.')).
gsub('$release', os.release_name.presence || '')
end

# The OS is usually represented as the concatenation of the OS and the revision
def to_label
return description if description.present?
fullname
end

# to_label setter updates description and does not try to parse and update major, minor attributes
def to_label=(str)
self.description = str
end

def to_param
Parameterizable.parameterize("#{id}-#{title}")
end

def release
"#{major}#{('.' + minor.to_s) if minor.present?}"
end

def fullname
"#{name} #{release}"
end

def to_s
fullname
end

def self.find_by_to_label(str)
os = self.find_by_description(str.to_s)
return os if os
a = str.split(" ")
b = a[1].split('.') if a[1]
cond = {:name => a[0]}
cond[:major] = b[0] if b && b[0]
cond[:minor] = b[1] if b && b[1]
self.find_by(cond)
end

# Implemented only in the OSs subclasses where it makes sense
def available_loaders
["None", "PXELinux BIOS"]
end

# sets the prefix for the tfp files based on the os / arch combination
def pxe_prefix(arch)
"boot/#{self}-#{arch}".tr(" ", "-")
end

def pxe_files(medium, arch, host = nil)
boot_files_uri(medium, arch, host).collect do |img|
{ pxe_prefix(arch).to_sym => img.to_s}
end
end

def kernel(arch)
bootfile(arch, :kernel)
end

def initrd(arch)
bootfile(arch, :initrd)
end

def bootfile(arch, type)
pxe_prefix(arch) + "-" + self.family.constantize::PXEFILES[type.to_sym]
end

# Does this OS family support a build variant that is constructed from a prebuilt archive
def supports_image
false
end

# Compatible kinds for this OS sorted by preferrence
def template_kinds
["PXEGrub2", "PXELinux", "PXEGrub"]
end

def boot_filename(host = nil)
return default_boot_filename if host.nil? || host.pxe_loader.nil?
self.class.all_loaders_map(host.arch.nil? ? '' : host.arch.bootfilename_efi)[host.pxe_loader]
end

# Does this OS family use release_name in its naming scheme
def use_release_name?
false
end

def image_extension
raise ::Foreman::Exception.new(N_("Attempting to construct an operating system image filename but %s cannot be built from an image"), family)
end

# If this OS family requires access to its media via NFS
def self.require_nfs_access_to_medium
false
end

# Pretty method for displaying the Family name
def display_family
"Unknown"
end

def self.shorten_description(description)
# This method should be overridden in the OS subclass
# to handle shortening the specific formats of lsbdistdescription
# returned by Facter on that OS
description
end

def deduce_family
self.family || self.class.families.find do |f|
name =~ FAMILIES[f]
end
end

def boot_files_uri(medium, architecture, host = nil)
raise ::Foreman::Exception.new(N_("%{os} medium was not set for host '%{host}'"), :host => host, :os => self) if medium.nil?
raise ::Foreman::Exception.new(N_("Invalid medium '%{medium}' for '%{os}'"), :medium => medium, :os => self) unless media.include?(medium)
raise ::Foreman::Exception.new(N_("Invalid architecture '%{arch}' for '%{os}'"), :arch => architecture, :os => self) unless architectures.include?(architecture)
self.family.constantize::PXEFILES.values.map do |img|
medium_vars_to_uri("#{medium.path}/#{pxedir}/#{img}", architecture.name, self)
end
end

private

def set_family
self.family ||= self.deduce_family
end

def set_title
self.title = to_label.to_s[0..254]
end

def stringify_major_and_minor
# Cast major and minor to strings. see db/schema.rb around lines 560-562 (Or https://github.com/theforeman/foreman/blob/develop/db/migrate/20090720134126_create_operatingsystems.rb#L4).
# Need to ensure type when using major and minor as scopes for name uniqueness.
self.major = major.to_s
self.minor = minor.to_s
end

def downcase_release_name
self.release_name = release_name.downcase if release_name.present?
end

def reject_empty_provisioning_template(attributes)
template_exists = attributes[:id].present?
provisioning_template_id_empty = attributes[:provisioning_template_id].blank?
attributes[:_destroy] = 1 if template_exists && provisioning_template_id_empty
(!template_exists && provisioning_template_id_empty)
end

# overriden by operating systems
def pxe_kernel_options(params)
[]
end
end
(39-39/70)