Added keepalived and common

This commit is contained in:
Eugene Kirpichov 2012-08-14 15:45:17 -07:00
commit 11e1b4f2db
20 changed files with 771 additions and 0 deletions

View File

@ -0,0 +1,22 @@
The common module installs various functions that are required by other
modules. This module should be installed before any of the other module.
To use this module, follow these directions:
1. Your modules directory will need all the files included in this
repository placed under a directory called "common"
2. Add the following line to manifests/site.pp:
import "modules.pp"
3. Add the following line to manifests/modules.pp:
import "common"
Author:: David Schmitt (mailto:david@dasz.at)
Copyright:: Copyright (c) 2007-2009 dasz.at OG
License:: 3-clause BSD

View File

@ -0,0 +1 @@
this directory contains various data collected for system wide configurations

View File

@ -0,0 +1,22 @@
# This function has two modes of operation:
#
# basename(string) : string
#
# Returns the last component of the filename given as argument, which must be
# formed using forward slashes ("/") regardless of the separator used on the
# local file system.
#
# basename(string[]) : string[]
#
# Returns an array of strings with the basename of each item from the argument.
#
module Puppet::Parser::Functions
newfunction(:basename, :type => :rvalue) do |args|
if args[0].is_a?(Array)
args.collect do |a| File.basename(a) end
else
File.basename(args[0])
end
end
end

View File

@ -0,0 +1,17 @@
# concat.rb
# concatenate the contents of arrays
# Use it like this:
# $base_metrics = ['a', 'b']
# $extended_metrics = ['c']
# $metrics = concat($base_metrics, $extended_metrics)
module Puppet::Parser::Functions
newfunction(:concat, :type => :rvalue) do |args|
result = []
args.each do |arg|
result = result.concat(arg)
end
result
end
end

View File

@ -0,0 +1,22 @@
# This function has two modes of operation:
#
# dirname(string) : string
#
# Returns all components of the filename given as argument except the last
# one. The filename must be formed using forward slashes (``/..) regardless of
# the separator used on the local file system.
#
# dirname(string[]) : string[]
#
# Returns an array of strings with the basename of each item from the argument.
#
module Puppet::Parser::Functions
newfunction(:dirname, :type => :rvalue) do |args|
if args[0].is_a?(Array)
args.collect do |a| File.dirname(a) end
else
File.dirname(args[0])
end
end
end

View File

@ -0,0 +1,183 @@
# Puppet External Data Sources
#
# This is a parser function to read data from external files, this version
# uses CSV files but the concept can easily be adjust for databases, yaml
# or any other queryable data source.
#
# The object of this is to make it obvious when it's being used, rather than
# magically loading data in when an module is loaded I prefer to look at the code
# and see statements like:
#
# $snmp_contact = extlookup("snmp_contact")
#
# The above snippet will load the snmp_contact value from CSV files, this in its
# own is useful but a common construct in puppet manifests is something like this:
#
# case $domain {
# "myclient.com": { $snmp_contact = "John Doe <john@myclient.com>" }
# default: { $snmp_contact = "My Support <support@my.com>" }
# }
#
# Over time there will be a lot of this kind of thing spread all over your manifests
# and adding an additional client involves grepping through manifests to find all the
# places where you have constructs like this.
#
# This is a data problem and shouldn't be handled in code, a using this function you
# can do just that.
#
# First you configure it in site.pp:
# $extlookup_datadir = "/etc/puppet/manifests/extdata"
# $extlookup_precedence = ["%{fqdn}", "domain_%{domain}", "common"]
#
# The array tells the code how to resolve values, first it will try to find it in
# web1.myclient.com.csv then in domain_myclient.com.csv and finally in common.csv
#
# Now create the following data files in /etc/puppet/manifests/extdata
#
# domain_myclient.com.csv:
# snmp_contact,John Doe <john@myclient.com>
# root_contact,support@%{domain}
# client_trusted_ips,192.168.1.130,192.168.10.0/24
#
# common.csv:
# snmp_contact,My Support <support@my.com>
# root_contact,support@my.com
#
# Now you can replace the case statement with the simple single line to achieve
# the exact same outcome:
#
# $snmp_contact = extlookup("snmp_contact")
#
# The obove code shows some other features, you can use any fact or variable that
# is in scope by simply using %{varname} in your data files, you can return arrays
# by just having multiple values in the csv after the initial variable name.
#
# In the event that a variable is nowhere to be found a critical error will be raised
# that will prevent your manifest from compiling, this is to avoid accidentally putting
# in empty values etc. You can however specify a default value:
#
# $ntp_servers = extlookup("ntp_servers", "1.${country}.pool.ntp.org")
#
# In this case it will default to "1.${country}.pool.ntp.org" if nothing is defined in
# any data file.
#
# You can also specify an additional data file to search first before any others at use
# time, for example:
#
# $version = extlookup("rsyslog_version", "present", "packages")
#
# package{"rsyslog": ensure => $version }
#
# This will look for a version configured in packages.csv and then in the rest as configured
# by $extlookup_precedence if it's not found anywhere it will default to "present", this kind
# of use case makes puppet a lot nicer for managing large amounts of packages since you do not
# need to edit a load of manifests to do simple things like adjust a desired version number.
#
# For more information on installing and writing your own custom functions see:
# http://reductivelabs.com/trac/puppet/wiki/WritingYourOwnFunctions
#
# For further help contact Volcane on #puppet
require 'csv'
module Puppet::Parser::Functions
newfunction(:extlookup, :type => :rvalue) do |args|
key = args[0]
default = "_ExtUNSET_"
datafile = "_ExtUNSET_"
default = args[1] if args[1]
datafile = args[2] if args[2]
extlookup_datadir = lookupvar('extlookup_datadir')
extlookup_precedence = Array.new
# precedence values can have variables embedded in them
# in the form %{fqdn}, you could for example do
#
# $extlookup_precedence = ["hosts/%{fqdn}", "common"]
#
# this will result in /path/to/extdata/hosts/your.box.com.csv
# being searched.
#
# we parse the precedence here because the best place to specify
# it would be in site.pp but site.pp is only evaluated at startup
# so $fqdn etc would have no meaning there, this way it gets evaluated
# each run and has access to the right variables for that run
lookupvar('extlookup_precedence').each do |prec|
while prec =~ /%\{(.+?)\}/
prec.gsub!(/%\{#{$1}\}/, lookupvar($1))
end
extlookup_precedence << prec
end
datafiles = Array.new
# if we got a custom data file, put it first in the array of search files
if datafile != ""
if File.exists?(extlookup_datadir + "/#{datafile}.csv")
datafiles << extlookup_datadir + "/#{datafile}.csv"
end
end
extlookup_precedence.each do |d|
datafiles << extlookup_datadir + "/#{d}.csv"
end
desired = "_ExtUNSET_"
datafiles.each do |file|
parser.watch_file(file) if File.exists?(file)
if desired == "_ExtUNSET_"
if File.exists?(file)
result = CSV.read(file).find_all do |r|
r[0] == key
end
# return just the single result if theres just one,
# else take all the fields in the csv and build an array
if result.length > 0
if result[0].length == 2
val = result[0][1].to_s
# parse %{}'s in the CSV into local variables using lookupvar()
while val =~ /%\{(.+?)\}/
val.gsub!(/%\{#{$1}\}/, lookupvar($1))
end
desired = val
elsif result[0].length > 1
length = result[0].length
cells = result[0][1,length]
# Individual cells in a CSV result are a weird data type and throws
# puppets yaml parsing, so just map it all to plain old strings
desired = cells.map do |c|
# parse %{}'s in the CSV into local variables using lookupvar()
while c =~ /%\{(.+?)\}/
c.gsub!(/%\{#{$1}\}/, lookupvar($1))
end
c.to_s
end
end
end
end
end
end
# don't accidently return nil's and such rather throw a parse error
if desired == "_ExtUNSET_" && default == "_ExtUNSET_"
raise Puppet::ParseError, "No match found for '#{key}' in any data file during extlookup()"
else
desired = default if desired == "_ExtUNSET_"
end
desired
end
end
# vi:tabstop=4:expandtab:ai

View File

@ -0,0 +1,21 @@
#
# A thin wrapper around the ruby gsub function.
#
# gsub($string, $pattern, $replacement)
#
# will replace all occurrences of $pattern in $string with $replacement.
# $string can be either a single value or an array. In the latter case, each
# element of the array will be processed in turn.
#
module Puppet::Parser::Functions
newfunction(:gsub, :type => :rvalue) do |args|
if args[0].is_a?(Array)
args[0].collect do |val|
val.gsub(/#{args[1]}/, args[2])
end
else
args[0].gsub(/#{args[1]}/, args[2])
end
end
end

View File

@ -0,0 +1,19 @@
# Prefixes arguments 2..n with first argument.
#
# prefix_with(string prefix, string[] arguments) : string[]
#
# Example:
#
# prefix_with("php-", [ "blah", "foo" ])
#
# will result in this array:
#
# [ "php-blah", "php-foo" ]
#
module Puppet::Parser::Functions
newfunction(:prefix_with, :type => :rvalue) do |args|
prefix = args.shift
args.collect {|v| "%s%s" % [prefix, v] }
end
end

View File

@ -0,0 +1,7 @@
# apply ruby regexp escaping to a string
module Puppet::Parser::Functions
newfunction(:re_escape, :type => :rvalue) do |args|
Regexp.escape(args[0])
end
end

View File

@ -0,0 +1,23 @@
# This function has two modes of operation:
#
# split($string, $delimiter) : $string
#
# Split the first argument on every $delimiter. $delimiter is interpreted as
# Ruby regular expression.
#
# split($string[], $delimiter) : $string[][]
#
# Returns an array of split results with the result of applying split to each
# item from the first argument.
#
# For long-term portability it is recommended to refrain from using Ruby's
# extended RE features.
module Puppet::Parser::Functions
newfunction(:split, :type => :rvalue) do |args|
if args[0].is_a?(Array)
args.collect do |a| a.split(/#{args[1]}/) end
else
args[0].split(/#{args[1]}/)
end
end
end

View File

@ -0,0 +1,49 @@
# common/manifests/classes/lsb_release.pp -- request the installation of
# lsb_release to get to lsbdistcodename, which is used throughout the manifests
#
# Copyright (C) 2007 David Schmitt <david@schmitt.edv-bus.at>
# See LICENSE for the full license granted to you.
# Changelog:
# 2007-08-26: micah <micah@riseup.net> reported, that lsb_release can report
# nonsensical values for lsbdistcodename; assert_lsbdistcodename now
# recognises "n/a" and acts accordingly
# This lightweight class only asserts that $lsbdistcodename is set.
# If the assertion fails, an error is printed on the server
#
# To fail individual resources on a missing lsbdistcodename, require
# Exec[assert_lsbdistcodename] on the specific resource
#
# This is just one example of how you could avoid evaluation of parts of the
# manifest, before a bootstrapping class has enabled all the necessary goodies.
class assert_lsbdistcodename {
case $lsbdistcodename {
'': {
err("Please install lsb_release or set facter_lsbdistcodename in the environment of $fqdn")
exec { "false # assert_lsbdistcodename": alias => assert_lsbdistcodename, loglevel => err }
}
'n/a': {
case $operatingsystem {
"Debian": {
err("lsb_release was unable to report your distcodename; This seems to indicate a broken apt/sources.list on $fqdn")
}
default: {
err("lsb_release was unable to report your distcodename; please set facter_lsbdistcodename in the environment of $fqdn")
}
}
exec { "false # assert_lsbdistcodename": alias => assert_lsbdistcodename, loglevel => err }
}
default: {
exec { "true # assert_lsbdistcodename": alias => assert_lsbdistcodename, loglevel => debug }
exec { "true # require_lsbdistcodename": alias => require_lsbdistcodename, loglevel => debug }
}
}
}
# To fail the complete compilation on a missing $lsbdistcodename, include this class
class require_lsbdistcodename inherits assert_lsbdistcodename {
exec { "false # require_lsbdistcodename": require => Exec[require_lsbdistcodename], loglevel => err }
}

View File

@ -0,0 +1,14 @@
#
# This define is only for "CFEngine compatability". It is only a light
# wrapper around the "line" define, which is equally dangerous, but at
# least named according to a proper resource model.
#
define append_if_no_such_line($file, $line) {
line {
$name:
ensure => present,
file => $file,
line => $line;
}
}

View File

@ -0,0 +1,117 @@
# common/manifests/defines/concatenated_file.pp -- create a file from snippets
# stored in a directory
#
# Copyright (C) 2007 David Schmitt <david@schmitt.edv-bus.at>
# See LICENSE for the full license granted to you.
module_dir { "common/cf": }
# TODO:
# * create the directory in _part too
# This resource collects file snippets from a directory ($dir) and concatenates
# them in lexical order of their names into a new file ($name). This can be
# used to collect information from disparate sources, when the target file
# format doesn't allow includes.
#
# concatenated_file_part can be used to easily configure content for this.
#
# If no $dir is specified, the target name with '.d' appended will be used.
#
# The $dir is purged by puppet and will only contain explicitely configured
# files. This can be overridden by defining the directory before the
# concatenated_file.
#
# Depend on File[$name] to change if and only if its contents change. Notify
# Exec["concat_${name}"] if you want to force an update.
#
# Usage:
# concatenated_file { "/etc/some.conf":
# dir => "/etc/some.conf.d",
# }
define concatenated_file (
# where the snippets are located
$dir = '',
# a file with content to prepend
$header = '',
# a file with content to append
$footer = '',
# default permissions for the target file
$mode = 0644, $owner = root, $group = 0
)
{
$dir_real = $dir ? { '' => "${name}.d", default => $dir }
$tmp_file_name = regsubst($dir_real, '/', '_', 'G')
$tmp_file = "${module_dir_path}/${tmp_file_name}"
if defined(File[$dir_real]) {
debug("${dir_real} already defined")
} else {
file {
$dir_real:
source => "puppet:///modules/common/empty",
checksum => mtime,
ignore => '.ignore',
recurse => true, purge => true, force => true,
mode => $mode, owner => $owner, group => $group,
notify => Exec["concat_${name}"];
}
}
file {
$tmp_file:
ensure => present, checksum => md5,
mode => $mode, owner => $owner, group => $group;
# decouple the actual file from the generation process by using a
# temporary file and puppet's source mechanism. This ensures that events
# for notify/subscribe will only be generated when there is an actual
# change.
$name:
ensure => present, checksum => md5,
source => $tmp_file,
mode => $mode, owner => $owner, group => $group,
require => File[$tmp_file];
}
# if there is a header or footer file, add it
$additional_cmd = $header ? {
'' => $footer ? {
'' => '',
default => "| cat - '${footer}' "
},
default => $footer ? {
'' => "| cat '${header}' - ",
default => "| cat '${header}' - '${footer}' "
}
}
# use >| to force clobbering the target file
exec { "concat_${name}":
command => "/usr/bin/find ${dir_real} -maxdepth 1 -type f ! -name '*puppettmp' -print0 | sort -z | xargs -0 cat ${additional_cmd} >| ${tmp_file}",
subscribe => [ File[$dir_real] ],
before => File[$tmp_file],
alias => [ "concat_${dir_real}"],
loglevel => info
}
}
# Add a snippet called $name to the concatenated_file at $dir.
# The file can be referenced as File["cf_part_${name}"]
define concatenated_file_part (
$dir, $content = '', $ensure = present,
$mode = 0644, $owner = root, $group = 0
)
{
file { "${dir}/${name}":
ensure => $ensure, content => $content,
mode => $mode, owner => $owner, group => $group,
alias => "cf_part_${name}",
notify => Exec["concat_${dir}"],
}
}

View File

@ -0,0 +1,60 @@
# common/manifests/defines/config_file.pp -- create a config file with default permissions
# Copyright (C) 2007 David Schmitt <david@schmitt.edv-bus.at>
# See LICENSE for the full license granted to you.
# A simple wrapper to give all configuration files common defaults.
#
# Usage:
# config_file { filename:
# content => "....\n",
# }
#
# Examples:
#
# To create the file /etc/vservers/${vs_name}/context with specific
# content:
#
# config_file {
# "/etc/vservers/${vs_name}/context":
# content => "${context}\n",
# notify => Exec["vs_restart_${vs_name}"],
# require => Exec["vs_create_${vs_name}"];
# }
#
# To create the file /etc/apache2/sites-available/munin-stats with the
# content pulled from a template:
#
# config_file {
# "/etc/apache2/sites-available/munin-stats":
# content => template("apache/munin-stats"),
# require => Package["apache2"],
# notify => Exec["reload-apache2"];
# }
define config_file (
$content = '',
$source = '',
$ensure = 'present')
{
file { $name:
ensure => $ensure,
# keep old versions on the server
backup => server,
# default permissions for config files
mode => 0644, owner => root, group => 0,
# really detect changes to this file
checksum => md5,
}
case $source {
'': { }
default: { File[$name] { source => $source } }
}
case $content {
'': { }
default: { File[$name] { content => $content } }
}
}

View File

@ -0,0 +1,57 @@
# 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.
# Ensures that a specific line is present or absent in a file. This can
# be very brittle, since even small changes can throw this off.
#
# If the line is not present yet, it will be appended to the file.
#
# The name of the define is not used. Just keep it (globally) unique and
# descriptive.
#
# Use this only for very trivial stuff. Usually replacing the whole file
# is a more stable solution with less maintenance headaches afterwards.
#
# Usage:
# line {
# description:
# file => "filename",
# line => "content",
# ensure => {absent,*present*}
# }
#
# Example:
# The following ensures that the line "allow ^$munin_host$" exists in
# /etc/munin/munin-node.conf, and if there are any changes notify the
# service for a restart
#
# line {
# allow_munin_host:
# file => "/etc/munin/munin-node.conf",
# line => "allow ^$munin_host$",
# ensure => present,
# notify => Service[munin-node],
# require => Package[munin-node];
# }
define line(
$file,
$line,
$ensure = 'present'
) {
case $ensure {
default : { err ( "unknown ensure value '${ensure}'" ) }
present: {
exec { "echo '${line}' >> '${file}'":
unless => "grep -qFx '${line}' '${file}'"
}
}
absent: {
exec { "perl -ni -e 'print if \$_ ne \"${line}\n\";' '${file}'":
onlyif => "grep -qFx '${line}' '${file}'"
}
}
}
}

View File

@ -0,0 +1,43 @@
# common/manifests/defines/module_dir.pp -- create a default directory
# for storing module specific information
#
# Copyright (C) 2007 David Schmitt <david@schmitt.edv-bus.at>
# See LICENSE for the full license granted to you.
# Use this variable to reference the base path. Thus you are safe from any
# changes.
$module_dir_path = '/var/lib/puppet/modules'
# A module_dir is a storage place for all the stuff a module might want to
# store. According to the FHS, this should go to /var/lib. Since this is a part
# of puppet, the full path is /var/lib/puppet/modules/${name}. Every module
# should # prefix its module_dirs with its name.
#
# By default, the module_dir is loaded from "puppet:///${name}/module_dir". If
# that doesn't exist an empty directory is taken as source. The directory is
# purged so that modules do not have to worry about removing cruft.
#
# Usage:
# module_dir { ["common", "common/dir1", "common/dir2" ]: }
define module_dir (
$mode = 0644,
$owner = root,
$group = 0
)
{
$dir = "${module_dir_path}/${name}"
if defined(File[$dir]) {
debug("${dir} already defined")
} else {
file {
$dir:
source => [ "puppet:///modules/${name}/module_dir", "puppet:///modules/common/empty"],
checksum => md5,
# ignore the placeholder
ignore => '\.ignore',
recurse => true, purge => true, force => true,
mode => $mode, owner => $owner, group => $group;
}
}
}

View File

@ -0,0 +1,24 @@
# common/manifests/defines/module_file.pp -- use an already defined module_dir
# to store module specific files
#
# Copyright (C) 2007 David Schmitt <david@schmitt.edv-bus.at>
# See LICENSE for the full license granted to you.
# Put a file into module-local storage.
#
# Usage:
# module_file {
# "module/file":
# source => "puppet://..",
# }
define module_file (
$source,
$mode = 0644, $owner = root, $group = 0
)
{
file {
"${module_dir_path}/${name}":
source => $source,
mode => $mode, owner => $owner, group => $group;
}
}

View File

@ -0,0 +1,41 @@
# common/manifests/defines/replace.pp -- replace a pattern in a file with a string
# Copyright (C) 2007 David Schmitt <david@schmitt.edv-bus.at>
# See LICENSE for the full license granted to you.
# A hack to replace all ocurrances of a regular expression in a file with a
# specified string. Sometimes it can be less effort to replace only a single
# value in a huge config file instead of creating a template out of it. Still,
# creating a template is often better than this hack.
#
# This define uses perl regular expressions.
#
# Use this only for very trivial stuff. Usually replacing the whole file is a
# more stable solution with less maintenance headaches afterwards.
#
# Usage:
#
# replace { description:
# file => "filename",
# pattern => "regexp",
# replacement => "replacement"
#
# Example:
# To replace the current port in /etc/munin/munin-node.conf
# with a new port, but only disturbing the file when needed:
#
# replace {
# set_munin_node_port:
# file => "/etc/munin/munin-node.conf",
# pattern => "^port (?!$port)[0-9]*",
# replacement => "port $port"
# }
define replace($file, $pattern, $replacement) {
$pattern_no_slashes = regsubst($pattern, '/', '\\/', 'G', 'U')
$replacement_no_slashes = regsubst($replacement, '/', '\\/', 'G', 'U')
exec { "replace_${pattern}_${file}":
command => "/usr/bin/perl -pi -e 's/${pattern_no_slashes}/${replacement_no_slashes}/' '${file}'",
onlyif => "/usr/bin/perl -ne 'BEGIN { \$ret = 1; } \$ret = 0 if /${pattern_no_slashes}/ && ! /\\Q${replacement_no_slashes}\\E/; END { exit \$ret; }' '${file}'",
alias => "exec_$name",
}
}

View File

@ -0,0 +1,29 @@
# common/manifests/init.pp - Define common infrastructure for modules
# Copyright (C) 2007 David Schmitt <david@schmitt.edv-bus.at>
# See LICENSE for the full license granted to you.
import "defines/*.pp"
import "classes/*.pp"
class common {
module_dir { [ 'common' ]: }
file {
# Module programmers can use /var/lib/puppet/modules/$modulename to save
# module-local data, e.g. for constructing config files. See module_dir
# for details
"/var/lib/puppet/modules":
ensure => directory,
source => "puppet:///modules/common/modules",
ignore => ".ignore",
recurse => true, purge => true, force => true,
mode => 0755, owner => root, group => 0;
}
}
include common
# common packages
class pkg::openssl { package { openssl: ensure => installed } }
class pkg::rsync { package { rsync: ensure => installed } }