Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| # -*- mode: ruby -*- | |
| # vi: set ft=ruby : | |
| # Vagrantfile for FreeIPA using Puppet-Ipa | |
| # Copyright (C) 2012-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/ | |
| # 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.144.0/24' | |
| range = network.to_range.to_a | |
| cidr = (32-(Math.log(range.length)/Math.log(2))).to_i | |
| offset = 100 # start 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.145.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 = 1 # default number of ipa hosts to build | |
| # NOTE: this is the default topology that makes sense for puppet-ipa+vagrant... | |
| # FIXME: what topology makes the most sense for most replica host counts of N ? | |
| topology = 'ring' # default topology | |
| version = '' # default ipa version (empty string means latest!) | |
| firewall = false # default firewall enabled (FIXME: default to true when keepalived bug is fixed) | |
| recipient = '' # default gpg recipient to use | |
| clients = 1 # default number of XXX clients to build | |
| 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-ipa.yaml') | |
| # load settings | |
| if File.exist?(f) | |
| settings = YAML::load_file f | |
| count = settings[:count] | |
| topology = settings[:topology] | |
| version = settings[:version] | |
| firewall = settings[:firewall] | |
| recipient = settings[:recipient] | |
| clients = settings[:clients] | |
| 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='--ipa-count=') | |
| v = ARGV.delete_at(skip).dup | |
| v.slice! arg | |
| #puts "#{arg}, #{v}" # debug | |
| count = v.to_i # set ipa host count | |
| elsif ARGV[skip].start_with?(arg='--ipa-topology=') | |
| v = ARGV.delete_at(skip).dup | |
| v.slice! arg | |
| topology = v.to_s # set ipa topology | |
| elsif ARGV[skip].start_with?(arg='--ipa-version=') | |
| v = ARGV.delete_at(skip).dup | |
| v.slice! arg | |
| version = v.to_s # set ipa version | |
| elsif ARGV[skip].start_with?(arg='--ipa-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='--ipa-recipient=') | |
| v = ARGV.delete_at(skip).dup | |
| v.slice! arg | |
| recipient = v.to_s # set gpg recipient | |
| elsif ARGV[skip].start_with?(arg='--ipa-clients=') | |
| v = ARGV.delete_at(skip).dup | |
| v.slice! arg | |
| clients = v.to_i # set ipa client count | |
| elsif ARGV[skip].start_with?(arg='--ipa-sync=') | |
| v = ARGV.delete_at(skip).dup | |
| v.slice! arg | |
| sync = v.to_s # set sync type | |
| elsif ARGV[skip].start_with?(arg='--ipa-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, | |
| :topology => topology, | |
| :version => version, | |
| :firewall => firewall, | |
| :recipient => recipient, | |
| :clients => clients, | |
| :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 = 'ipa' | |
| # | |
| # 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 => 'ipa' | |
| #vm.landrush.host puppet_hostname, puppet_ip # TODO ? | |
| # ensure the ipa 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 ipa &> /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 = "ipa#{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_ipa_firewall' => firewall ? 'true' : 'false', | |
| 'vagrant_ipa_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 | |
| # | |
| # ipa, et al... | |
| # | |
| (1..count).each do |i| | |
| h = "ipa#{i}" | |
| ip = range[offset+i].to_s | |
| #fqdn = "#{h}.#{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 => 'ipa' | |
| # use a specialized freeipa box if it exists :) | |
| if `vagrant box list | grep -q '^centos-6-freeipa' && echo -n found` != '' | |
| vm.vm.box = 'centos-6-freeipa' | |
| end | |
| # vagrant won't bind to these ports if not run as root! | |
| # if vagrant-libvirt includes this patch, it is better: | |
| # https://github.com/purpleidea/vagrant-libvirt/tree/feat/sudo-forward | |
| vm.vm.network 'forwarded_port', guest: 80, host: 80 | |
| vm.vm.network 'forwarded_port', guest: 443, host: 443 | |
| #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 = "ipa#{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_ipa_vip' => vip_ip, | |
| 'vagrant_ipa_vip_fqdn' => "#{vip_hostname}.#{domain}", | |
| 'vagrant_ipa_firewall' => firewall ? 'true' : 'false', | |
| 'vagrant_ipa_topology' => topology.to_s, | |
| 'vagrant_ipa_recipient' => recipient.to_s, | |
| 'vagrant_ipa_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 = "ipa#{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 => 'ipa' | |
| #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 = "ipa#{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_ipa_vip' => vip_ip, | |
| 'vagrant_ipa_vip_fqdn' => "#{vip_hostname}.#{domain}", | |
| 'vagrant_ipa_firewall' => firewall ? 'true' : 'false', | |
| 'vagrant_ipa_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 = 'ipa' # set a prefix for your vm's... | |
| end | |
| end | |