Dynamic dnsmasq config for admin network ranges

In order to successfully PXE/boot nodes from new racks we need to update
cobbler's dnsmasq configuration on changes in admin networks of any
nodegroup in any Fuel environment. Such changes should survive cobbler
container restarts and rebuilds.

In order to do so we need to execute the following on every change in
admin netwoks configuration of any nodegroup we have in Fuel:
- dump list of admin networks into /etc/hiera/networks.yaml file
  in mcollective container
- run 'puppet apply nailgin/examples/dhcp-ranges.pp' inside mcollective
  container to update config files in shared /etc/dnsmasq.d/ dir
- run 'cobbler sync' inside cobbler container to reload dnsmasq

Co-Authored-By: Aleksandr Didenko <adidenko@mirantis.com>

Blueprint: dynamic-dnsmasq
Partial-bug: #1495593
Change-Id: I9d7428c9fc21c705a1aee7fbca8003829a81e1d0
This commit is contained in:
Nikita Koshikov 2015-09-22 17:11:05 -05:00 committed by Aleksandr Didenko
parent cf54da03b1
commit 4343f1ab8b
17 changed files with 285 additions and 6 deletions

View File

@ -6,6 +6,7 @@ read-ethers
log-queries
log-facility=/var/log/dnsmasq.log
addn-hosts = /var/lib/cobbler/cobbler_hosts
conf-dir=/etc/dnsmasq.d
domain=<%= @domain_name %>
dhcp-lease-max=<%= @dhcp_lease_max %>
<% if @domain_name != 'local' %>
@ -33,9 +34,4 @@ dhcp-no-override
dhcp-sequential-ip
dhcp-option=6,<%= @name_server %>
dhcp-range=internal,<%= @dhcp_start_address %>,<%= @dhcp_end_address %>,<%= @dhcp_netmask %>,<%= @lease_time %>
dhcp-option=net:internal,option:router,<%= @dhcp_gateway %>
dhcp-boot=net:internal,pxelinux.0,boothost,<%= @next_server %>
$insert_cobbler_system_definitions

View File

@ -80,6 +80,8 @@ HOST_VOL['ibp']="-v /var/lib/fuel/ibp:/var/lib/fuel/ibp:rw"
HOST_VOL['astute_data']="-v /var/lib/fuel/container_data/$VERSION/astute:/var/lib/astute"
HOST_VOL['cobbler_data']="-v /var/lib/fuel/container_data/$VERSION/cobbler:/var/lib/cobbler"
HOST_VOL['postgres_data']="-v /var/lib/fuel/container_data/$VERSION/postgres:/var/lib/pgsql"
HOST_VOL['dnsmasq']="-v /var/lib/fuel/container_data/$VERSION/dnsmasq:/etc/dnsmasq.d"
HOST_VOL['mcollective_data']="-v /var/lib/fuel/container_data/$VERSION/mcollective:/etc/hiera"
#Storage container volume mounts
declare -A CONTAINER_VOLUMES
@ -113,7 +115,9 @@ REQS["${HOST_VOL['puppet']}"]="rsync"
#REQS["${HOST_VOL['puppet']}"]="cobbler postgres rabbitmq rsync astute nailgun ostf nginx rsyslog mcollective keystone"
REQS["${HOST_VOL['astute_data']}"]="astute"
REQS["${HOST_VOL['cobbler_data']}"]="cobbler"
REQS["${HOST_VOL['dnsmasq']}"]="cobbler mcollective"
REQS["${HOST_VOL['postgres_data']}"]="postgres"
REQS["${HOST_VOL['mcollective_data']}"]="mcollective"
REQS["${HOST_VOL['sshkey']}"]="nailgun cobbler astute mcollective ostf"
REQS["${HOST_VOL['ibp']}"]="mcollective"
REQS["$NAILGUN_VOL"]="nginx"

View File

@ -0,0 +1,3 @@
fixtures:
symlinks:
'nailgun': "#{source_dir}"

View File

@ -0,0 +1,16 @@
source 'https://rubygems.org'
group :development, :test do
gem 'rake', :require => false
gem 'rspec', '~>3.3', :require => false
gem 'rspec-puppet', '~>2.1.0', :require => false
gem 'puppetlabs_spec_helper', :require => false
end
if puppetversion = ENV['PUPPET_GEM_VERSION']
gem 'puppet', puppetversion, :require => false
else
gem 'puppet', '<4.0', :require => false
end
# vim:ft=ruby

View File

@ -0,0 +1 @@
require 'puppetlabs_spec_helper/rake_tasks'

View File

@ -0,0 +1,10 @@
$fuel_settings = parseyaml($astute_settings_yaml)
$admin_network = $::fuel_settings['ADMIN_NETWORK']
nailgun::dnsmasq::dhcp_range {'default':
dhcp_start_address => $admin_network['dhcp_pool_start'],
dhcp_end_address => $admin_network['dhcp_pool_end'],
dhcp_netmask => $admin_network['netmask'],
dhcp_gateway => $admin_network['dhcp_gateway'],
next_server => $admin_network['ipaddress'],
}

View File

@ -0,0 +1,18 @@
$admin_networks = hiera('admin_networks')
$admin_network = hiera('ADMIN_NETWORK')
Nailgun::Dnsmasq::Dhcp_range <||> {
next_server => $admin_network['ipaddress'],
}
# Ensure dir with purge and recurse to remove configs for
# non-existing (removed) nodegroups and ip ranges
file { '/etc/dnsmasq.d':
ensure => 'directory',
recurse => true,
purge => true,
}
# Create admin networks dhcp-range files except for 'default' nodegroup
# by creating Nailgun::Dnsmasq::Dhcp_range puppet resources
create_dnsmasq_dhcp_ranges($admin_networks)

View File

@ -0,0 +1,61 @@
$data_dir = '/etc/hiera'
$data = [
'override/common',
'class/%{calling_class}',
'module/%{calling_module}',
'nodes',
'networks',
'astute',
'common',
]
$astute_data_file = '/etc/fuel/astute.yaml'
$hiera_main_config = '/etc/hiera.yaml'
$hiera_puppet_config = '/etc/puppet/hiera.yaml'
$hiera_data_file = "${data_dir}/astute.yaml"
File {
owner => 'root',
group => 'root',
mode => '0644',
}
$hiera_config_content = inline_template('
---
:backends:
- yaml
:hierarchy:
<% @data.each do |name| -%>
- <%= name %>
<% end -%>
:yaml:
:datadir: <%= @data_dir %>
:merge_behavior: deeper
:logger: noop
')
file { 'hiera_data_dir' :
ensure => 'directory',
path => $data_dir,
}
file { 'hiera_config' :
ensure => 'present',
path => $hiera_main_config,
content => $hiera_config_content,
}
file { 'hiera_data_astute' :
ensure => 'symlink',
path => $hiera_data_file,
target => $astute_data_file,
}
file { 'hiera_puppet_config' :
ensure => 'symlink',
path => $hiera_puppet_config,
target => $hiera_main_config,
}

View File

@ -0,0 +1,33 @@
require 'ipaddr'
require 'zlib'
module Puppet::Parser::Functions
newfunction(:create_dnsmasq_dhcp_ranges, :doc => <<-EOS
Creates nailgun::dnsmasq::dhcp_range puppet resources from list of admin networks.
EOS
) do |args|
admin_nets = args[0]
unless admin_nets.is_a?(Array) and admin_nets[0].is_a?(Hash)
raise(Puppet::ParseError, 'Should pass list of hashes as a parameter')
end
admin_nets.each do |net|
net['ip_ranges'].each do |ip_range|
netmask = IPAddr.new('255.255.255.255').mask(net['cidr'].split('/')[1]).to_s
print_range = ip_range.join('_')
resource_name = sprintf("range_%08x", Zlib::crc32("#{print_range}_#{net['cidr']}").to_i)
range_comment = "# Environment: #{net['cluster_name']}\n# Nodegroup: #{net['node_group_name']}\n# IP range: #{ip_range}"
dhcp_range_resource = {
resource_name => {
'file_header' => "# Generated automatically by puppet\n#{range_comment}",
'dhcp_start_address' => ip_range[0],
'dhcp_end_address' => ip_range[1],
'dhcp_netmask' => netmask,
'dhcp_gateway' => net['gateway'],
}
}
debug("Trying to create nailgun::dnsmasq::dhcp_range resource #{dhcp_range_resource}")
function_create_resources(['nailgun::dnsmasq::dhcp_range', dhcp_range_resource])
end
end
end
end

View File

@ -0,0 +1,28 @@
#
# nailgun::dnsmasq::dhcp_range creates config files in /etc/dnsmasq.d/
#
# [next_server] IP address that will be used as PXE tftp server
# [dhcp_start_address] First address of dhcp range
# [dhcp_end_address] Last address of dhcp range
# [dhcp_netmask] Netmask of the network
# [dhcp_gateway] Gateway address for installed nodes
# [lease_time] DHCP lease time
# [file_header] File header for comments
define nailgun::dnsmasq::dhcp_range(
$dhcp_start_address = '10.0.0.201',
$dhcp_end_address = '10.0.0.254',
$dhcp_netmask = '255.255.255.0',
$dhcp_gateway = $::ipaddress,
$file_header = undef,
$lease_time = '120m',
$next_server = $::ipaddress,
){
$range_name = $name
file { "/etc/dnsmasq.d/${name}.conf":
content => template('nailgun/dnsmasq.dhcp-range.erb'),
owner => 'root',
group => 'root',
mode => '0644',
}
}

View File

@ -0,0 +1,40 @@
node 'default' {
$admin_networks = [
{"id"=>1,
"node_group_name"=>nil,
"node_group_id"=>nil,
"cluster_name"=>nil,
"cluster_id"=>nil,
"cidr"=>"10.145.0.0/24",
"gateway"=>"10.145.0.2",
"ip_ranges"=>[["10.145.0.3", "10.145.0.250"]]},
{"id"=>2,
"node_group_name"=>"default2",
"node_group_id"=>22,
"cluster_name"=>"default2",
"cluster_id"=>2,
"cidr"=>"10.144.0.0/24",
"gateway"=>"10.144.0.5",
"ip_ranges"=>[["10.144.0.10", "10.144.0.254"]]}]
$admin_network = {"interface"=>"eth0",
"ipaddress"=>"10.145.0.2",
"netmask"=>"255.255.255.0",
"cidr"=>"10.20.0.0/24",
"size"=>"256",
"dhcp_pool_start"=>"10.145.0.3",
"dhcp_pool_end"=>"10.145.0.254",
"mac"=>"64:42:d3:10:64:68",
"dhcp_gateway"=>"10.145.0.1"}
Nailgun::Dnsmasq::Dhcp_range <||> {
next_server => $admin_network['ipaddress'],
}
file { '/etc/dnsmasq.d':
ensure => 'directory',
recurse => true,
purge => true,
}
create_dnsmasq_dhcp_ranges($admin_networks, ['default'])
}

View File

@ -0,0 +1,10 @@
require 'spec_helper'
describe 'create_dnsmasq_dhcp_ranges' do
it 'refuses String' do
is_expected.to run.with_params('foo').\
and_raise_error(Puppet::ParseError, /Should pass list of hashes as a parameter/)
end
end

View File

@ -0,0 +1,44 @@
require 'spec_helper'
require 'zlib'
require 'ipaddr'
describe 'default' do
admin_nets = [
{"id"=>1,
"node_group_name"=>nil,
"node_group_id"=>nil,
"cluster_name"=>nil,
"cluster_id"=>nil,
"cidr"=>"10.145.0.0/24",
"gateway"=>"10.145.0.2",
"ip_ranges"=>[["10.145.0.3", "10.145.0.250"]]},
{"id"=>2,
"node_group_name"=>"default2",
"node_group_id"=>22,
"cluster_name"=>"default2",
"cluster_id"=>2,
"cidr"=>"10.144.0.0/24",
"gateway"=>"10.144.0.5",
"ip_ranges"=>[["10.144.0.10", "10.144.0.254"]]}
]
admin_network = {"interface"=>"eth0",
"ipaddress"=>"10.145.0.2",
"netmask"=>"255.255.255.0",
"cidr"=>"10.20.0.0/24",
"size"=>"256",
"dhcp_pool_start"=>"10.145.0.3",
"dhcp_pool_end"=>"10.145.0.254",
"mac"=>"64:42:d3:10:64:68",
"dhcp_gateway"=>"10.145.0.1"}
admin_nets.each do |net|
net['ip_ranges'].each do |ip_range|
netmask = IPAddr.new('255.255.255.255').mask(net['cidr'].split('/')[1]).to_s
print_range = ip_range.join('_')
resource_name = sprintf("range_%08x", Zlib::crc32("#{print_range}_#{net['cidr']}").to_i)
it { should contain_file("/etc/dnsmasq.d/#{resource_name}.conf") \
.with_content(/^dhcp-range=#{resource_name}.*#{netmask},120m\n|,boothost,#{admin_network['ipaddress']}\n/)
}
end
end
end

View File

@ -0,0 +1,8 @@
require 'rspec-puppet'
fixture_path = File.expand_path(File.join(__FILE__, '..', 'fixtures'))
RSpec.configure do |c|
c.module_path = File.join(fixture_path, 'modules')
c.manifest_dir = File.join(fixture_path, 'manifests')
end

View File

@ -0,0 +1,4 @@
<% if @file_header %><%= @file_header %><% end %>
dhcp-range=<%= @range_name %>,<%= @dhcp_start_address %>,<%= @dhcp_end_address %>,<%= @dhcp_netmask %>,<%= @lease_time %>
dhcp-option=net:<%= @range_name %>,option:router,<%= @dhcp_gateway %>
dhcp-boot=net:<%= @range_name %>,pxelinux.0,boothost,<%= @next_server %>

View File

@ -81,6 +81,8 @@ HOST_VOL['ibp']="-v /var/lib/fuel/ibp:/var/lib/fuel/ibp:rw"
HOST_VOL['astute_data']="-v /var/lib/fuel/container_data/$VERSION/astute:/var/lib/astute"
HOST_VOL['cobbler_data']="-v /var/lib/fuel/container_data/$VERSION/cobbler:/var/lib/cobbler"
HOST_VOL['postgres_data']="-v /var/lib/fuel/container_data/$VERSION/postgres:/var/lib/pgsql"
HOST_VOL['dnsmasq']="-v /var/lib/fuel/container_data/$VERSION/dnsmasq:/etc/dnsmasq.d"
HOST_VOL['mcollective_data']="-v /var/lib/fuel/container_data/$VERSION/mcollective:/etc/hiera"
#Storage container volume mounts
declare -A CONTAINER_VOLUMES
@ -114,7 +116,9 @@ REQS["${HOST_VOL['puppet']}"]="rsync"
#REQS["${HOST_VOL['puppet']}"]="cobbler postgres rabbitmq rsync astute nailgun ostf nginx rsyslog mcollective keystone"
REQS["${HOST_VOL['astute_data']}"]="astute"
REQS["${HOST_VOL['cobbler_data']}"]="cobbler"
REQS["${HOST_VOL['dnsmasq']}"]="cobbler mcollective"
REQS["${HOST_VOL['postgres_data']}"]="postgres"
REQS["${HOST_VOL['mcollective_data']}"]="mcollective"
REQS["${HOST_VOL['sshkey']}"]="nailgun cobbler astute mcollective ostf"
REQS["${HOST_VOL['ibp']}"]="mcollective"
REQS["$NAILGUN_VOL"]="nginx"

View File

@ -18,7 +18,6 @@ memcached
mongodb
monit
mysql
nailgun
neutron
nova
ntp