Permalink
Fetching contributors…
Cannot retrieve contributors at this time
599 lines (519 sloc) 19.7 KB
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile for GlusterFS using Puppet-Gluster
# Copyright (C) 2010-2013+ James Shubin
# Written by James Shubin <james@shubin.ca>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# NOTE: vagrant-libvirt needs to run in series (not in parallel) to avoid trying
# to create the network twice, eg: 'vagrant up --no-parallel'. alternatively you
# can build the first host (puppet server) manually, and then the hosts next eg:
# 'vagrant up puppet && vagrant up' which ensures the puppet server is up first!
# NOTE: https://github.com/pradels/vagrant-libvirt/issues/104 is the open bug...
# README: https://ttboj.wordpress.com/2013/12/09/vagrant-on-fedora-with-libvirt/
# README: https://ttboj.wordpress.com/2013/12/21/vagrant-vsftp-and-other-tricks/
# ALSO: https://ttboj.wordpress.com/2014/01/02/vagrant-clustered-ssh-and-screen/
# README: https://ttboj.wordpress.com/2014/01/08/automatically-deploying-glusterfs-with-puppet-gluster-vagrant
# NOTE: this will not work properly on Fedora 19 or anything that does not have
# the libvirt broadcast patch included. You can check which libvirt version you
# have and see if that version tag is in the libvirt git or in your distro. eg:
# git tag --contains 51e184e9821c3740ac9b52055860d683f27b0ab6 | grep <version#>
# this is because old libvirt broke the vrrp broadcast packets from keepalived!
# TODO: the /etc/hosts DNS setup is less than ideal, but I didn't implement
# anything better yet. Please feel free to suggest something else!
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = '2'
require 'ipaddr'
require 'yaml'
#
# globals
#
domain = 'example.com'
network = IPAddr.new '192.168.142.0/24'
range = network.to_range.to_a
cidr = (32-(Math.log(range.length)/Math.log(2))).to_i
offset = 100 # start gluster hosts after here
#puts range[0].to_s # network
#puts range[1].to_s # router (reserved)
#puts range[2].to_s # puppetmaster
#puts range[3].to_s # vip
network2 = IPAddr.new '192.168.143.0/24'
range2 = network2.to_range.to_a
cidr2 = (32-(Math.log(range2.length)/Math.log(2))).to_i
offset2 = 2
netmask2 = IPAddr.new('255.255.255.255').mask(cidr2).to_s
# mutable by ARGV and settings file
count = 4 # default number of gluster hosts to build
disks = 0 # default number of disks to attach (after the host os)
bricks = 0 # default number of bricks to build (0 defaults to 1)
fstype = '' # default filesystem for bricks (requires bricks > 0)
version = '' # default gluster version (empty string means latest!)
firewall = false # default firewall enabled (FIXME: default to true when keepalived bug is fixed)
replica = 1 # default replica count
clients = 1 # default number of gluster clients to build
layout = '' # default brick layout
setgroup = '' # default setgroup value
sync = 'rsync' # default sync type
cachier = false # default cachier usage
#
# ARGV parsing
#
projectdir = File.expand_path File.dirname(__FILE__) # vagrant project dir!!
f = File.join(projectdir, 'puppet-gluster.yaml')
# load settings
if File.exist?(f)
settings = YAML::load_file f
count = settings[:count]
disks = settings[:disks]
bricks = settings[:bricks]
fstype = settings[:fstype]
version = settings[:version]
firewall = settings[:firewall]
replica = settings[:replica]
clients = settings[:clients]
layout = settings[:layout]
setgroup = settings[:setgroup]
sync = settings[:sync]
cachier = settings[:cachier]
end
# ARGV parser
skip = 0
while skip < ARGV.length
#puts "#{skip}, #{ARGV[skip]}" # debug
if ARGV[skip].start_with?(arg='--gluster-count=')
v = ARGV.delete_at(skip).dup
v.slice! arg
#puts "#{arg}, #{v}" # debug
count = v.to_i # set gluster host count
elsif ARGV[skip].start_with?(arg='--gluster-disks=')
v = ARGV.delete_at(skip).dup
v.slice! arg
disks = v.to_i # set gluster disk count
elsif ARGV[skip].start_with?(arg='--gluster-bricks=')
v = ARGV.delete_at(skip).dup
v.slice! arg
bricks = v.to_i # set gluster brick count
elsif ARGV[skip].start_with?(arg='--gluster-fstype=')
v = ARGV.delete_at(skip).dup
v.slice! arg
fstype = v.to_s # set gluster fstype
elsif ARGV[skip].start_with?(arg='--gluster-version=')
v = ARGV.delete_at(skip).dup
v.slice! arg
version = v.to_s # set gluster version
elsif ARGV[skip].start_with?(arg='--gluster-firewall=')
v = ARGV.delete_at(skip).dup
v.slice! arg
firewall = v.to_s # set firewall flag
if ['false', 'no'].include?(firewall.downcase)
firewall = false
else
firewall = true
end
elsif ARGV[skip].start_with?(arg='--gluster-replica=')
v = ARGV.delete_at(skip).dup
v.slice! arg
replica = v.to_i # set gluster replica
elsif ARGV[skip].start_with?(arg='--gluster-clients=')
v = ARGV.delete_at(skip).dup
v.slice! arg
clients = v.to_i # set gluster client count
elsif ARGV[skip].start_with?(arg='--gluster-layout=')
v = ARGV.delete_at(skip).dup
v.slice! arg
layout = v.to_s # set gluster brick layout
elsif ARGV[skip].start_with?(arg='--gluster-setgroup=')
v = ARGV.delete_at(skip).dup
v.slice! arg
setgroup = v.to_s # set gluster setgroup
elsif ARGV[skip].start_with?(arg='--gluster-sync=')
v = ARGV.delete_at(skip).dup
v.slice! arg
sync = v.to_s # set sync type
elsif ARGV[skip].start_with?(arg='--gluster-cachier=')
v = ARGV.delete_at(skip).dup
v.slice! arg
cachier = v.to_s # set cachier flag
if ['true', 'yes'].include?(cachier.downcase)
cachier = true
else
cachier = false
end
else # skip over "official" vagrant args
skip = skip + 1
end
end
# save settings (ARGV overrides)
settings = {
:count => count,
:disks => disks,
:bricks => bricks,
:fstype => fstype,
:version => version,
:firewall => firewall,
:replica => replica,
:clients => clients,
:layout => layout,
:setgroup => setgroup,
:sync => sync,
:cachier => cachier
}
File.open(f, 'w') do |file|
file.write settings.to_yaml
end
#puts "ARGV: #{ARGV}" # debug
# erase host information from puppet so that the user can do partial rebuilds
snoop = ARGV.select { |x| !x.start_with?('-') }
if snoop.length > 1 and snoop[0] == 'destroy'
snoop.shift # left over array snoop should be list of hosts
if snoop.include?('puppet') # doesn't matter then...
snoop = []
end
else
# important! clear snoop because we're not using 'destroy'
snoop = []
end
# figure out which hosts are getting destroyed
destroy = ARGV.select { |x| !x.start_with?('-') }
if destroy.length > 0 and destroy[0] == 'destroy'
destroy.shift # left over array destroy should be list of hosts or []
if destroy.length == 0
destroy = true # destroy everything
end
else
destroy = false # destroy nothing
end
# figure out which hosts are getting provisioned
provision = ARGV.select { |x| !x.start_with?('-') }
if provision.length > 0 and ['up', 'provision'].include?(provision[0])
provision.shift # left over array provision should be list of hosts or []
if provision.length == 0
provision = true # provision everything
end
else
provision = false # provision nothing
end
# XXX: workaround for: https://github.com/mitchellh/vagrant/issues/2447
# only run on 'vagrant init' or if it's the first time running vagrant
if sync == 'nfs' and ((ARGV.length > 0 and ARGV[0] == 'init') or not(File.exist?(f)))
`sudo systemctl restart nfs-server`
`firewall-cmd --permanent --zone public --add-service mountd`
`firewall-cmd --permanent --zone public --add-service rpc-bind`
`firewall-cmd --permanent --zone public --add-service nfs`
`firewall-cmd --reload`
end
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
#config.landrush.enable # TODO ?
#
# box (pre-built base image)
#
config.vm.box = 'centos-6' # i built it
# box source url
# TODO: this box should be GPG signed
config.vm.box_url = 'https://dl.fedoraproject.org/pub/alt/purpleidea/vagrant/centos-6/centos-6.box'
#
# sync type
#
config.vm.synced_folder './', '/vagrant', type: sync # nfs, rsync
#
# cache
#
# NOTE: you should probably erase the cache between rebuilds if you are
# installing older package versions. This is because the newer packages
# will get cached, and then subsequently might get silently installed!!
if cachier
# TODO: this doesn't cache metadata, full offline operation not possible
config.cache.auto_detect = true
config.cache.enable :yum
#config.cache.enable :apt
if not ARGV.include?('--no-parallel') # when running in parallel,
config.cache.scope = :machine # use the per machine cache
end
if sync == 'nfs' # TODO: support other sync types here...
config.cache.enable_nfs = true # sets nfs => true on the synced_folder
# the nolock option is required, otherwise the NFSv3 client will try to
# access the NLM sideband protocol to lock files needed for /var/cache/
# all of this can be avoided by using NFSv4 everywhere. die NFSv3, die!
config.cache.mount_options = ['rw', 'vers=3', 'tcp', 'nolock']
end
end
#
# vip
#
vip_ip = range[3].to_s
vip_hostname = 'annex'
#
# puppetmaster
#
puppet_ip = range[2].to_s
puppet_hostname = 'puppet'
fv = File.join(projectdir, '.vagrant', "#{puppet_hostname}-hosts.done")
if destroy.is_a?(TrueClass) or (destroy.is_a?(Array) and destroy.include?(puppet_hostname))
if File.exists?(fv) # safety
puts "Unlocking shell provisioning for: #{puppet_hostname}..."
File.delete(fv) # delete hosts token
end
end
#puppet_fqdn = "#{puppet_hostname}.#{domain}"
config.vm.define :puppet, :primary => true do |vm|
vm.vm.hostname = puppet_hostname
# red herring network so that management happens here...
vm.vm.network :private_network,
:ip => range2[2].to_s,
:libvirt__netmask => netmask2,
#:libvirt__dhcp_enabled => false, # XXX: not allowed here
:libvirt__network_name => 'default'
# this is the real network that we'll use...
vm.vm.network :private_network,
:ip => puppet_ip,
:libvirt__dhcp_enabled => false,
:libvirt__network_name => 'gluster'
#vm.landrush.host puppet_hostname, puppet_ip # TODO ?
# ensure the gluster module is present for provisioning...
if provision.is_a?(TrueClass) or (provision.is_a?(Array) and provision.include?(puppet_hostname))
cwd = `pwd`
mod = File.join(projectdir, 'puppet', 'modules')
`cd #{mod} && make gluster &> /dev/null; cd #{cwd}`
end
#
# shell
#
if not File.exists?(fv) # only modify /etc/hosts once
if provision.is_a?(TrueClass) or (provision.is_a?(Array) and provision.include?(puppet_hostname))
File.open(fv, 'w') {} # touch
end
vm.vm.provision 'shell', inline: 'puppet resource host localhost.localdomain ip=127.0.0.1 host_aliases=localhost'
vm.vm.provision 'shell', inline: "puppet resource host #{puppet_hostname} ensure=absent" # so that fqdn works
vm.vm.provision 'shell', inline: "puppet resource host #{vip_hostname}.#{domain} ip=#{vip_ip} host_aliases=#{vip_hostname} ensure=present"
vm.vm.provision 'shell', inline: "puppet resource host #{puppet_hostname}.#{domain} ip=#{puppet_ip} host_aliases=#{puppet_hostname} ensure=present"
(1..count).each do |i|
h = "annex#{i}"
ip = range[offset+i].to_s
vm.vm.provision 'shell', inline: "puppet resource host #{h}.#{domain} ip=#{ip} host_aliases=#{h} ensure=present"
end
# hosts entries for all clients
(1..clients).each do |i|
h = "client#{i}"
ip = range[offset+count+i].to_s
vm.vm.provision 'shell', inline: "puppet resource host #{h}.#{domain} ip=#{ip} host_aliases=#{h} ensure=present"
end
end
#
# puppet (apply)
#
vm.vm.provision :puppet do |puppet|
puppet.module_path = 'puppet/modules'
puppet.manifests_path = 'puppet/manifests'
puppet.manifest_file = 'site.pp'
# custom fact
puppet.facter = {
'vagrant' => '1',
'vagrant_gluster_firewall' => firewall ? 'true' : 'false',
'vagrant_gluster_allow' => (1..count).map{|z| range[offset+z].to_s}.join(','),
}
puppet.synced_folder_type = sync
end
vm.vm.provider :libvirt do |libvirt|
libvirt.cpus = 2
libvirt.memory = 1024
end
end
#
# annex
#
(1..count).each do |i|
h = "annex#{i}"
ip = range[offset+i].to_s # eg: "192.168.142.#{100+i}"
#fqdn = "annex#{i}.#{domain}"
fvx = File.join(projectdir, '.vagrant', "#{h}-hosts.done")
if destroy.is_a?(TrueClass) or (destroy.is_a?(Array) and destroy.include?(h))
if File.exists?(fvx) # safety
puts "Unlocking shell provisioning for: #{h}..."
File.delete(fvx) # delete hosts token
end
end
if snoop.include?(h) # should we clean this machine?
cmd = "puppet cert clean #{h}.#{domain}"
puts "Running 'puppet cert clean' for: #{h}..."
`vagrant ssh #{puppet_hostname} -c 'sudo #{cmd}'`
cmd = "puppet node deactivate #{h}.#{domain}"
puts "Running 'puppet node deactivate' for: #{h}..."
`vagrant ssh #{puppet_hostname} -c 'sudo #{cmd}'`
end
config.vm.define h.to_sym do |vm|
vm.vm.hostname = h
# red herring network so that management happens here...
vm.vm.network :private_network,
:ip => range2[offset2+i].to_s,
:libvirt__netmask => netmask2,
:libvirt__network_name => 'default'
# this is the real network that we'll use...
vm.vm.network :private_network,
:ip => ip,
:libvirt__dhcp_enabled => false,
:libvirt__network_name => 'gluster'
#vm.landrush.host h, ip # TODO ?
#
# shell
#
if not File.exists?(fvx) # only modify /etc/hosts once
if provision.is_a?(TrueClass) or (provision.is_a?(Array) and provision.include?(h))
File.open(fvx, 'w') {} # touch
end
vm.vm.provision 'shell', inline: 'puppet resource host localhost.localdomain ip=127.0.0.1 host_aliases=localhost'
vm.vm.provision 'shell', inline: "puppet resource host #{h} ensure=absent" # so that fqdn works
vm.vm.provision 'shell', inline: "puppet resource host #{vip_hostname}.#{domain} ip=#{vip_ip} host_aliases=#{vip_hostname} ensure=present"
vm.vm.provision 'shell', inline: "puppet resource host #{puppet_hostname}.#{domain} ip=#{puppet_ip} host_aliases=#{puppet_hostname} ensure=present"
#vm.vm.provision 'shell', inline: "[ ! -e /root/puppet-cert-is-clean ] && ssh -o 'StrictHostKeyChecking=no' #{puppet_hostname} puppet cert clean #{h}.#{domain} ; touch /root/puppet-cert-is-clean"
# hosts entries for all hosts
(1..count).each do |j|
oh = "annex#{j}"
oip = range[offset+j].to_s # eg: "192.168.142.#{100+i}"
vm.vm.provision 'shell', inline: "puppet resource host #{oh}.#{domain} ip=#{oip} host_aliases=#{oh} ensure=present"
end
# hosts entries for all clients
(1..clients).each do |j|
oh = "client#{j}"
oip = range[offset+count+j].to_s
vm.vm.provision 'shell', inline: "puppet resource host #{oh}.#{domain} ip=#{oip} host_aliases=#{oh} ensure=present"
end
end
#
# puppet (agent)
#
vm.vm.provision :puppet_server do |puppet|
#puppet.puppet_node = "#{h}" # redundant
#puppet.puppet_server = "#{puppet_hostname}.#{domain}"
puppet.puppet_server = puppet_hostname
#puppet.options = '--verbose --debug'
puppet.options = '--test' # see the output
puppet.facter = {
'vagrant' => '1',
'vagrant_gluster_disks' => disks.to_s,
'vagrant_gluster_bricks' => bricks.to_s,
'vagrant_gluster_fstype' => fstype.to_s,
'vagrant_gluster_replica' => replica.to_s,
'vagrant_gluster_layout' => layout,
'vagrant_gluster_setgroup' => setgroup,
'vagrant_gluster_vip' => vip_ip,
'vagrant_gluster_vip_fqdn' => "#{vip_hostname}.#{domain}",
'vagrant_gluster_firewall' => firewall ? 'true' : 'false',
'vagrant_gluster_version' => version,
}
end
vm.vm.provider :libvirt do |libvirt|
# add additional disks to the os
(1..disks).each do |j| # if disks is 0, this passes :)
#print "disk: #{j}"
libvirt.storage :file,
#:path => '', # auto!
#:device => 'vdb', # auto!
#:size => '10G', # auto!
:type => 'qcow2'
end
end
end
end
#
# client
#
(1..clients).each do |i|
h = "client#{i}"
ip = range[offset+count+i].to_s
#fqdn = "annex#{i}.#{domain}"
fvy = File.join(projectdir, '.vagrant', "#{h}-hosts.done")
if destroy.is_a?(TrueClass) or (destroy.is_a?(Array) and destroy.include?(h))
if File.exists?(fvy) # safety
puts "Unlocking shell provisioning for: #{h}..."
File.delete(fvy) # delete hosts token
end
end
if snoop.include?(h) # should we clean this machine?
cmd = "puppet cert clean #{h}.#{domain}"
puts "Running 'puppet cert clean' for: #{h}..."
`vagrant ssh #{puppet_hostname} -c 'sudo #{cmd}'`
cmd = "puppet node deactivate #{h}.#{domain}"
puts "Running 'puppet node deactivate' for: #{h}..."
`vagrant ssh #{puppet_hostname} -c 'sudo #{cmd}'`
end
config.vm.define h.to_sym do |vm|
vm.vm.hostname = h
# red herring network so that management happens here...
vm.vm.network :private_network,
:ip => range2[offset2+count+i].to_s,
:libvirt__netmask => netmask2,
:libvirt__network_name => 'default'
# this is the real network that we'll use...
vm.vm.network :private_network,
:ip => ip,
:libvirt__dhcp_enabled => false,
:libvirt__network_name => 'gluster'
#vm.landrush.host h, ip # TODO ?
#
# shell
#
if not File.exists?(fvy) # only modify /etc/hosts once
if provision.is_a?(TrueClass) or (provision.is_a?(Array) and provision.include?(h))
File.open(fvy, 'w') {} # touch
end
vm.vm.provision 'shell', inline: 'puppet resource host localhost.localdomain ip=127.0.0.1 host_aliases=localhost'
vm.vm.provision 'shell', inline: "puppet resource host #{h} ensure=absent" # so that fqdn works
vm.vm.provision 'shell', inline: "puppet resource host #{vip_hostname}.#{domain} ip=#{vip_ip} host_aliases=#{vip_hostname} ensure=present"
vm.vm.provision 'shell', inline: "puppet resource host #{puppet_hostname}.#{domain} ip=#{puppet_ip} host_aliases=#{puppet_hostname} ensure=present"
# hosts entries for all hosts
(1..count).each do |j|
oh = "annex#{j}"
oip = range[offset+j].to_s # eg: "192.168.142.#{100+i}"
vm.vm.provision 'shell', inline: "puppet resource host #{oh}.#{domain} ip=#{oip} host_aliases=#{oh} ensure=present"
end
# hosts entries for all clients
(1..clients).each do |j|
oh = "client#{j}"
oip = range[offset+count+j].to_s
vm.vm.provision 'shell', inline: "puppet resource host #{oh}.#{domain} ip=#{oip} host_aliases=#{oh} ensure=present"
end
end
#
# puppet (agent)
#
vm.vm.provision :puppet_server do |puppet|
#puppet.puppet_node = "#{h}" # redundant
#puppet.puppet_server = "#{puppet_hostname}.#{domain}"
puppet.puppet_server = puppet_hostname
#puppet.options = '--verbose --debug'
puppet.options = '--test' # see the output
puppet.facter = {
'vagrant' => '1',
'vagrant_gluster_vip' => vip_ip,
'vagrant_gluster_vip_fqdn' => "#{vip_hostname}.#{domain}",
'vagrant_gluster_firewall' => firewall ? 'true' : 'false',
'vagrant_gluster_version' => version,
}
end
end
end
#
# libvirt
#
config.vm.provider :libvirt do |libvirt|
libvirt.driver = 'kvm' # needed for kvm performance benefits !
# leave out to connect directly with qemu:///system
#libvirt.host = 'localhost'
libvirt.connect_via_ssh = false
libvirt.username = 'root'
libvirt.storage_pool_name = 'default'
#libvirt.default_network = 'default' # XXX: this does nothing
libvirt.default_prefix = 'gluster' # prefix for your vm's!
end
end