# -*- mode: ruby -*- # vi: set ft=ruby : # Vagrantfile for GlusterFS using Puppet-Gluster # Copyright (C) 2010-2013+ James Shubin # Written by James Shubin # # 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 . # 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 # 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://download.gluster.org/pub/gluster/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