Project

General

Profile

« Previous | Next » 

Revision 8c974670

Added by Ohad Levy over 12 years ago

restructure foreman module

View differences:

files/external_node.rb
#! /usr/bin/env ruby
SETTINGS = {
:url => "http://foreman",
:puppetdir => "/var/lib/puppet",
:timeout => 3,
}
### Do not edit below this line
def url
SETTINGS[:url] || raise("Must provide URL - please edit file")
end
def certname
ARGV[0] || raise("Must provide certname as an argument")
end
def puppetdir
SETTINGS[:puppetdir] || raise("Must provide puppet base directory - please edit file")
end
def stat_file
FileUtils.mkdir_p "#{puppetdir}/yaml/foreman/"
"#{puppetdir}/yaml/foreman/#{certname}.yaml"
end
def tsecs
SETTINGS[:timeout] || 3
end
require 'net/http'
require 'net/https' if url =~ /^https/
require 'fileutils'
require 'timeout'
def upload_facts
# Temp file keeping the last run time
last_run = File.exists?(stat_file) ? File.stat(stat_file).mtime.utc : Time.now - 365*24*60*60
filename = "#{puppetdir}/yaml/facts/#{certname}.yaml"
last_fact = File.stat(filename).mtime.utc
if last_fact > last_run
fact = File.read(filename)
begin
Net::HTTP.post_form(URI.parse("#{url}/fact_values/create?format=yml"), {'facts'=> fact})
rescue => e
raise "Could not send facts to Foreman: #{e}"
end
end
end
def cache result
File.open(stat_file, 'w') {|f| f.write(result) }
end
def read_cache
File.read(stat_file)
rescue => e
raise "Unable to read from Cache file: #{e}"
end
def enc
foreman_url = "#{url}/node/#{certname}?format=yml"
uri = URI.parse(foreman_url)
req = Net::HTTP::Get.new(foreman_url)
res = Net::HTTP.start(uri.host, uri.port) { |http| http.request(req) }
raise "Error retrieving node #{certname}: #{res.class}" unless res.code == "200"
res.body
end
# Actual code starts here
begin
# send facts to Foreman - optional uncomment 'upload_facts' to activate.
# if you use this option below, make sure that you don't send facts to foreman via the rake task or push facts alternatives.
#
# upload_facts
#
# query External node
begin
result = ""
timeout(tsecs) do
result = enc
cache result
end
rescue TimeoutError, SocketError, Errno::EHOSTUNREACH
# Read from cache, we got some sort of an error.
result = read_cache
ensure
puts result
end
rescue => e
warn e
exit 1
end
files/foreman-report.rb
# copy this file to your report dir - e.g. /usr/lib/ruby/1.8/puppet/reports/
# add this report in your puppetmaster reports - e.g, in your puppet.conf add:
# reports=log, foreman # (or any other reports you want)
# URL of your Foreman installation
$foreman_url="http://foreman:3000"
require 'puppet'
require 'net/http'
require 'uri'
Puppet::Reports.register_report(:foreman) do
Puppet.settings.use(:reporting)
desc "Sends reports directly to Foreman"
def process
begin
uri = URI.parse($foreman_url)
http = Net::HTTP.new(uri.host, uri.port)
if uri.scheme == 'https' then
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
req = Net::HTTP::Post.new("/reports/create?format=yml")
req.set_form_data({'report' => to_yaml})
response = http.request(req)
rescue Exception => e
raise Puppet::Error, "Could not send report to Foreman at #{$foreman_url}/reports/create?format=yml: #{e}"
end
end
end
files/push_facts.rb
#! /usr/bin/env ruby
#
# This scripts runs on remote puppetmasters that you wish to import their nodes facts into Foreman
# it uploads all of the new facts its encounter based on a control file which is stored in /tmp directory.
# This script can run in cron, e.g. once every minute
# if you run it on many puppetmasters at the same time, you might consider adding something like:
# sleep rand(10) # that not all PM hammers the DB at once.
# ohadlevy@gmail.com
# puppet config dir
puppetdir="/var/lib/puppet"
# URL where Foreman lives
url="http://foreman"
# Temp file keeping the last run time
stat_file = "/tmp/foreman_fact_importer"
require 'fileutils'
require 'net/http'
require 'net/https'
require 'uri'
last_run = File.exists?(stat_file) ? File.stat(stat_file).mtime.utc : Time.now - 365*60*60
Dir["#{puppetdir}/yaml/facts/*.yaml"].each do |filename|
last_fact = File.stat(filename).mtime.utc
if last_fact > last_run
fact = File.read(filename)
puts "Importing #{filename}"
begin
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
if uri.scheme == 'https' then
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
req = Net::HTTP::Post.new("/fact_values/create?format=yml")
req.set_form_data({'facts' => fact})
response = http.request(req)
rescue Exception => e
raise "Could not send facts to Foreman: #{e}"
end
end
end
FileUtils.touch stat_file
manifests/config.pp
class foreman::config {
Cron {
require => User["$foreman::params::user"],
user => $foreman::params::user,
environment => "RAILS_ENV=${foreman::params::environment}",
}
file {"/etc/foreman/settings.yaml":
content => template("foreman/settings.yaml.erb"),
notify => Class["foreman::service"],
owner => $foreman::params::user,
require => User["$foreman::params::user"],
}
file { $foreman::params::app_root:
ensure => directory,
}
user { $foreman::params::user:
shell => "/sbin/nologin",
comment => "Foreman",
ensure => "present",
home => $foreman::params::app_root,
require => Class["foreman::install"],
}
# cleans up the session entries in the database
# if you are using fact or report importers, this creates a session per request
# which can easily result with a lot of old and unrequired in your database
# eventually slowing it down.
cron{"clear_session_table":
command => "(cd $foreman::params::app_root && rake db:sessions:clear)",
minute => "15",
hour => "23",
}
if $foreman::params::reports { include foreman::config::reports }
if $foreman::params::enc { include foreman::config::enc }
if $foreman::params::passenger { include foreman::config::passenger }
}
manifests/config/enc.pp
class foreman::config::enc {
file{
"/etc/puppet/node.rb":
content => template("foreman/external_node.rb.erb"),
mode => 550,
owner => "puppet",
group => "puppet";
"${foreman::params::puppet_home}/yaml/foreman":
ensure => directory,
mode => 640,
owner => "puppet",
group => "puppet";
}
}
manifests/config/passenger.pp
class foreman::config::passenger {
include apache::ssl
include ::passenger
file {"foreman_vhost":
path => "${foreman::params::apache_conf_dir}/foreman.conf",
content => template("foreman/foreman-vhost.conf.erb"),
mode => 644,
notify => Exec["reload-apache"],
}
exec {"restart_foreman":
command => "/bin/touch ${foreman::params::app_root}/tmp/restart.txt",
refreshonly => true,
cwd => $foreman::params::app_root,
path => "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
require => Class["foreman::install"]
}
# passenger ~2.10 will not load the app if a config.ru doesn't exist in the app
# root. Also, passenger will run as suid to the owner of the config.ru file.
file {
"$foreman::params::app_root/config.ru":
ensure => link,
owner => $foreman::params::user,
target => "${foreman::params::app_root}/vendor/rails/railties/dispatches/config.ru";
"$foreman::params::app_root/config/environment.rb":
owner => $foreman::params::user,
require => Class["foreman::install"];
}
}
manifests/config/reports.pp
class foreman::config::reports {
# foreman reporter
file {"${foreman::params::puppet_basedir}/reports/foreman.rb":
mode => 444,
owner => puppet,
group => puppet,
content => template("foreman/foreman-report.rb.erb"),
# notify => Service["puppetmaster"],
}
cron {
"expire_old_reports":
command => "(cd $foreman::params::app_root && rake reports:expire)",
minute => "30",
hour => "7",
}
cron{"daily summary":
command => "(cd $foreman::params::app_root && rake reports:summarize)",
minute => "31",
hour => "7",
}
}
manifests/defines.pp
# common/manifests/defines/line.pp -- a trivial mechanism to ensure a line exists in a file
# Copyright (C) 2007 David Schmitt <david@schmitt.edv-bus.at>
# See LICENSE for the full license granted to you.
# Usage:
# line { description:
# file => "filename",
# line => "content",
# ensure => {absent,*present*}
# }
#
define myline($file, $line, $ensure = 'present') {
case $ensure {
default : { err ( "unknown ensure value '${ensure}'" ) }
present: {
exec { "echo '${line}' >> '${file}'":
path => ["/bin", "/sbin", "/usr/bin", "/usr/sbin"],
unless => "grep -qFx '${line}' '${file}'",
user => root,
}
}
absent: {
exec { "perl -ni -e 'print unless /^\\Q${line}\\E\$/' '${file}'":
path => ["/bin", "/sbin", "/usr/bin", "/usr/sbin"],
onlyif => "grep -qFx '${line}' '${file}'",
user => root,
}
}
}
}
define link_file($source_path, $target_path) {
file{"$target_path/$name":
ensure => link,
target => "$source_path/$name"
}
}
manifests/externalnodes.pp
class foreman::externalnodes {
file{"/etc/puppet/node.rb":
source => "puppet:///modules/foreman/external_node.rb",
mode => 555,
owner => "puppet", group => "puppet",
}
}
manifests/import_facts.pp
class foreman::import_facts {
file {"/etc/puppet/push_facts.rb":
mode => 555,
owner => puppet, group => puppet,
source => "puppet:///modules/foreman/push_facts.rb",
ensure => $using_store_configs ? {
true => "absent",
false => "present"
},
}
cron{"send_facts_to_foreman":
command => "/etc/puppet/push_facts.rb",
user => "puppet",
minute => "*/2",
ensure => $using_store_configs ? {
true => "absent",
false => "present"
},
}
}
manifests/init.pp
class foreman {
# default variables
$using_store_configs = false # true or false
$using_passenger = false # true or false
$use_development = false # used for initial download
$railspath = "/usr/share"
$foreman_dir = "${railspath}/foreman"
$foreman_user = "foreman"
import "defines.pp"
Exec {
cwd => $foreman_dir,
path => "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
require => User[$foreman_user],
user => $foreman_user,
}
Cron {
require => User["$foreman_user"],
user => $foreman_user,
environment => "RAILS_ENV=production",
}
include foreman::import_facts
include foreman::reports
include foreman::externalnodes
# Current package is available for Red Hat 5
if $lsbmajdistrelease == "5" and $lsbdistid != "Debian" {
include foreman::package
# passenger setup for Red Hat 5
include foreman::passenger
} else {
include foreman::install_from_source
}
file{$foreman_dir:
ensure => directory,
require => User[$foreman_user],
owner => $foreman_user,
}
user { $foreman_user:
shell => "/sbin/nologin",
comment => "Foreman",
ensure => "present",
home => $foreman_dir,
}
# cleans up the session entries in the database
# if you are using fact or report importers, this creates a session per request
# which can easily result with a lot of old and unrequired in your database
# eventually slowing it down.
cron{"clear_session_table":
command => "(cd $foreman_dir && rake db:sessions:clear)",
minute => "15",
hour => "23",
}
cron{"daily summary":
command => "(cd $foreman_dir && rake reports:summarize)",
minute => "30",
hour => "07",
}
include foreman::params
include foreman::install
include foreman::config
include foreman::service
}
manifests/install.pp
class foreman::install {
include foreman::install::repos
case $operatingsystem {
redhat,centos,fedora: { include foreman::install::redhat }
default: { fail("${hostname}: This module does not support operatingsystem $operatingsystem") }
}
}
manifests/install/redhat.pp
class foreman::install::redhat {
package{"foreman":
ensure => latest,
require => Class["foreman::install::repos::redhat"],
notify => Class["foreman::service"],
}
}
manifests/install/repos.pp
class foreman::install::repos {
case $operatingsystem {
redhat,centos,fedora: { include foreman::install::repos::redhat }
default: { fail("${hostname}: This module does not support operatingsystem $operatingsystem") }
}
}
manifests/install/repos/redhat.pp
class foreman::install::repos::redhat {
yumrepo {
"foreman":
descr => "Foreman stable repository",
baseurl => "http://yum.theforeman.org/stable",
gpgcheck => "0",
enabled => "1";
"foreman-testing":
descr => "Foreman testing repository",
baseurl => "http://yum.theforeman.org/test",
enabled => $foreman::params::use_testing ? {
true => "1",
default => "0",
},
gpgcheck => "0",
}
}
manifests/install_from_source.pp
class foreman::install_from_source {
$version = $use_development ? {
true => "nightly",
false => "latest"
}
Package { ensure => installed, before => Exec["db_migrate"], }
file{$railspath: ensure => directory}
package{"rake":
name => $operatingsystem ? {
default => "rake",
"CentOs" => "rubygem-rake",
"RedHat" => "rubygem-rake",
},
}
package{"sqlite3-ruby":
name => $operatingsystem ? {
default => "libsqlite3-ruby",
"CentOs" => "rubygem-sqlite3-ruby",
"RedHat" => "rubygem-sqlite3-ruby",
},
}
package{"rack": ensure => "1.0.1", provider => gem}
# Initial Foreman Install
exec{"install_foreman":
command => "wget -q http://theforeman.org/foreman-$version.tar.bz2 -O - | tar xjf -",
cwd => $railspath,
creates => "$foreman_dir/public",
notify => Exec["db_migrate"],
require => File[$foreman_dir],
}
exec{"db_migrate":
command => "rake db:migrate",
environment => "RAILS_ENV=production",
refreshonly => true
}
}
manifests/package.pp
class foreman::package {
yumrepo { "foreman":
descr => "Foreman stable repository",
baseurl => "http://yum.theforeman.org/stable",
gpgcheck => "0",
enabled => "1"
}
yumrepo { 'foreman-testing':
enabled => '0',
gpgcheck => '0',
descr => 'Foreman testing repository',
baseurl => 'http://yum.theforeman.org/test'
}
package{"foreman":
ensure => installed,
require => Yumrepo["foreman"],
notify => Service["foreman"],
}
service {"foreman":
ensure => $using_passenger ? {
true => "stopped",
false => "running"
},
enable => $using_passenger ? {
true => "false",
false => "true",
},
hasstatus => true,
}
}
manifests/params.pp
class foreman::params {
# Basic configurations
$foreman_url = "http://${fqdn}:3000"
# Should foreman act as an external node classifier (manage puppet class assignments)
$enc = true
# Should foreman receive reports from puppet
$reports = true
# Should foreman recive facts from puppet
$facts = true
# Do you use storeconfig (and run foreman on the same database) ? (note: not required)
$storeconfigs = false
# should foreman manage host provisioning as well
$unattended = true
# Enable users authentication (default user:admin pw:changeme)
$authentication = false
# configure foreman via apache and passenger
$passenger = true
# force SSL (note: requires passenger)
$ssl = true
# Advance configurations - no need to change anything here by default
# allow usage of test / RC rpms as well
$use_testing = true
$railspath = "/usr/share"
$app_root = "${railspath}/foreman"
$user = "foreman"
$environment = "production"
# OS specific paths
case $operatingsystem {
redhat,centos,fedora: {
$puppet_basedir = "/usr/lib/ruby/site_ruby/1.8/puppet"
$apache_conf_dir = "/etc/httpd/conf.d"
}
default: {
$puppet_basedir = "/usr/lib/ruby/1.8/puppet"
$apache_conf_dir = "/etc/apache2/conf.d/foreman.conf"
}
}
$puppet_home = "/var/lib/puppet"
}
manifests/passenger.pp
class foreman::passenger {
if $using_passenger {
include apache2::passenger
include apache2::ssl
}
file{"foreman_vhost":
path => $lsbdistid ? {
default => "/etc/httpd/conf.d/foreman.conf",
"Ubuntu" => "/etc/apache2/conf.d/foreman.conf",
"Debian" => "/etc/apache2/conf.d/foreman.conf"
},
content => template("foreman/foreman-vhost.conf.erb"),
mode => 644,
notify => $using_passenger ? {
true => Exec["reload-apache2"],
default => undef,
},
ensure => $using_passenger ? {
true => "present",
default => "absent",
},
}
exec{"restart_foreman":
command => "/bin/touch $foreman_dir/tmp/restart.txt",
refreshonly => true
}
#passenger ~2.10 will not load the app if a config.ru doesn't exist in the app
#root. Also, passenger will run as suid to the owner of the config.ru file.
case $lsbdistid {
'Ubuntu','Debian': {
file{"${foreman_dir}/config.ru":
ensure => file,
owner => $foreman_user,
source => "file:///${foreman_dir}/vendor/rails/railties/dispatches/config.ru",
}
}
}
}
manifests/reports.pp
# please follow the instructions at: http://theforeman.org/wiki/foreman/Puppet_Reports
class foreman::reports {
# directory where your puppet is installed
$puppet_basedir = $operatingsystem ? {
default => "/usr/lib/ruby/1.8/puppet",
"CentOs" => "/usr/lib/ruby/site_ruby/1.8/puppet",
"RedHat" => "/usr/lib/ruby/site_ruby/1.8/puppet",
}
# foreman reporter
file {"${puppet_basedir}/reports/foreman.rb":
mode => 444,
owner => puppet, group => puppet,
source => "puppet:///modules/foreman/foreman-report.rb",
}
cron{"expire_old_reports":
command => "(cd $foreman_dir && rake reports:expire)",
environment => "RAILS_ENV=production",
user => $foreman_user,
minute => "30",
hour => "7",
}
}
manifests/service.pp
class foreman::service {
service {"foreman":
ensure => $foreman::params::passenger ? {
true => "stopped",
false => "running"
},
enable => $foreman::params::passenger ? {
true => "false",
false => "true",
},
hasstatus => true,
}
}
templates/external_node.rb.erb
#! /usr/bin/env ruby
SETTINGS = {
:url => "<%= scope.lookupvar('foreman::params::foreman_url')%>",
:puppetdir => "<%= scope.lookupvar('foreman::params::puppet_home')%>",
:timeout => 3,
}
### Do not edit below this line
def url
SETTINGS[:url] || raise("Must provide URL - please edit file")
end
def certname
ARGV[0] || raise("Must provide certname as an argument")
end
def puppetdir
SETTINGS[:puppetdir] || raise("Must provide puppet base directory - please edit file")
end
def stat_file
FileUtils.mkdir_p "#{puppetdir}/yaml/foreman/"
"#{puppetdir}/yaml/foreman/#{certname}.yaml"
end
def tsecs
SETTINGS[:timeout] || 3
end
require 'net/http'
require 'net/https' if url =~ /^https/
require 'fileutils'
require 'timeout'
def upload_facts
# Temp file keeping the last run time
last_run = File.exists?(stat_file) ? File.stat(stat_file).mtime.utc : Time.now - 365*24*60*60
filename = "#{puppetdir}/yaml/facts/#{certname}.yaml"
last_fact = File.stat(filename).mtime.utc
if last_fact > last_run
fact = File.read(filename)
begin
Net::HTTP.post_form(URI.parse("#{url}/fact_values/create?format=yml"), {'facts'=> fact})
rescue => e
raise "Could not send facts to Foreman: #{e}"
end
end
end
def cache result
File.open(stat_file, 'w') {|f| f.write(result) }
end
def read_cache
File.read(stat_file)
rescue => e
raise "Unable to read from Cache file: #{e}"
end
def enc
foreman_url = "#{url}/node/#{certname}?format=yml"
uri = URI.parse(foreman_url)
req = Net::HTTP::Get.new(foreman_url)
res = Net::HTTP.start(uri.host, uri.port) { |http| http.request(req) }
raise "Error retrieving node #{certname}: #{res.class}" unless res.code == "200"
res.body
end
# Actual code starts here
begin
# send facts to Foreman - optional uncomment 'upload_facts' to activate.
# if you use this option below, make sure that you don't send facts to foreman via the rake task or push facts alternatives.
#
<% if scope.lookupvar('foreman::params::facts') and not scope.lookupvar('foreman::params::storeconfigs') -%>
upload_facts
<% end -%>
#
# query External node
begin
result = ""
timeout(tsecs) do
result = enc
cache result
end
rescue TimeoutError, SocketError, Errno::EHOSTUNREACH
# Read from cache, we got some sort of an error.
result = read_cache
ensure
puts result
end
rescue => e
warn e
exit 1
end
templates/foreman-report.rb.erb
# copy this file to your report dir - e.g. /usr/lib/ruby/1.8/puppet/reports/
# add this report in your puppetmaster reports - e.g, in your puppet.conf add:
# reports=log, foreman # (or any other reports you want)
# URL of your Foreman installation
$foreman_url=<%= scope.lookupvar("http://foreman:3000") %>
require 'puppet'
require 'net/http'
require 'uri'
Puppet::Reports.register_report(:foreman) do
Puppet.settings.use(:reporting)
desc "Sends reports directly to Foreman"
def process
begin
uri = URI.parse($foreman_url)
http = Net::HTTP.new(uri.host, uri.port)
if uri.scheme == 'https' then
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
req = Net::HTTP::Post.new("/reports/create?format=yml")
req.set_form_data({'report' => to_yaml})
response = http.request(req)
rescue Exception => e
raise Puppet::Error, "Could not send report to Foreman at #{$foreman_url}/reports/create?format=yml: #{e}"
end
end
end
templates/foreman-vhost.conf.erb
<VirtualHost <%= ipaddress %>:80>
ServerName <%= fqdn %>
ServerAlias foreman
DocumentRoot <%= scope.lookupvar 'foreman::foreman_dir' %>/public
<% if has_variable?("lsbdistid") and lsbdistid == 'Ubuntu' || lsbdistid == 'Debian' %> PassengerAppRoot <%= scope.lookupvar 'foreman::foreman_dir' %><% end %>
DocumentRoot <%= scope.lookupvar 'foreman::params::app_root' %>/public
PassengerAppRoot <%= scope.lookupvar 'foreman::params::app_root' %>
RailsAutoDetect On
AddDefaultCharset UTF-8
......
ServerAlias foreman
RailsAutoDetect On
DocumentRoot <%= scope.lookupvar 'foreman::foreman_dir' %>/public
PassengerAppRoot <%= scope.lookupvar 'foreman::foreman_dir' %>
DocumentRoot <%= scope.lookupvar 'foreman::params::app_root' %>/public
PassengerAppRoot <%= scope.lookupvar 'foreman::params::app_root' %>
# Use puppet certificates for SSL
templates/settings.yaml.erb
---
#your default puppet server - can be overridden in the host level
#if none specified, plain "puppet" will be used.
#:puppet_server: puppet
:unattended: <%= scope.lookupvar("foreman::params::unattended") %>
:puppetconfdir: /etc/puppet/puppet.conf
:login: <%= scope.lookupvar("foreman::params::authentication") %>
:require_ssl: <%= scope.lookupvar("foreman::params::ssl") %>

Also available in: Unified diff