Vagrantfile: systematic use of name lv for libvirt provider
[obnox/vagrant/vagrant-gluster-samba-cluster.git] / Vagrantfile
1 # -*- mode: ruby -*-
2 # vi: ft=ruby:et:ts=2:sts=2:sw=2
3
4 VAGRANTFILE_API_VERSION = 2
5
6
7 require 'yaml'
8
9 #
10 # Defaults for Configuration data.
11 # Will be overridden from the settings file
12 # and (possibly later) from commandline parameters.
13 #
14
15 net_default = {
16   :type   => 'veth',
17   :flags  => 'up',
18   :hwaddr => '',
19   :name   => '',
20   :ipv4   => '',
21   :ipv6   => '',
22 }
23
24 network_opts = [ :type, :link, :flags, :hwaddr, :name, :ipv4, :ipv6 ]
25
26 libvirt_network_parms = {
27   :hwaddr => :mac,
28   :ipv4   => :ip,
29   :ipv6   => '',
30   :link   => '',
31   :flags  => '',
32   :type   => '',
33 }
34
35 defaults = {
36   :provider => {
37     :libvirt => {
38       :prefix => 'vagrant',
39       :box => 'fedora/23-cloud-base',
40     },
41     :virtualbox => {
42       :prefix => 'vagrant',
43       :box => 'fedora/23-cloud-base',
44     },
45   },
46 }
47
48
49 vms = [
50   {
51     #:hostname => 'gluno1',
52     :hostname => 'node1',
53     #:box => 'local-fedora-rawhide-64',
54     #:box => 'purpleidea-fedora-21',
55     #:box => 'local-fedora-21.2',
56     :provider => {
57       :lxc => {
58         :container_name => 'gluno1',
59         #:container_name => 'node1',
60       },
61       :libvirt => {
62         :prefix => 'gluster',
63       }, 
64     },
65     :internal_if => 'virbr1',
66     :networks => [
67       {
68         :link => 'virbr1',
69         :ipv4 => '172.20.10.30',
70       },
71       #{
72       #  :link => 'virbr2',
73       #  #:ipv4 => '10.111.222.201',
74       #},
75     ],
76   },
77 ]
78
79
80 #
81 # Load the config, if it exists,
82 # possibly override with commandline args,
83 # (currently none supported yet)
84 # and then store the config.
85 #
86
87 projectdir = File.expand_path File.dirname(__FILE__)
88 f = File.join(projectdir, 'vagrant.yaml')
89 if File.exists?(f)
90   settings = YAML::load_file f
91
92   if settings[:vms].is_a?(Array)
93     vms = settings[:vms]
94   end
95   puts "Loaded settings from #{f}."
96 end
97
98 # TODO(?): ARGV-processing
99
100 settings = {
101   :vms  => vms,
102 }
103
104 File.open(f, 'w') do |file|
105   file.write settings.to_yaml
106 end
107 puts "Wrote settings to #{f}."
108
109
110 # apply defaults:
111
112 vms.each do |vm|
113   defaults.keys.each do |cat|
114     next if not vm.has_key?(cat)
115     defaults[cat].keys.each do |subcat|
116       next if not vm[cat].has_key?(subcat)
117       defaults[cat][subcat].keys.each do |key|
118         if not vm[cat][subcat].has_key?(key)
119           vm[cat][subcat][key] = defaults[cat][subcat][key]
120         end
121       end
122     end
123   end
124
125   #if not vm[:provider][:libvirt].has_key?(:prefix)
126   #  vm[:provider][:libvirt][:prefix] = default_libvirt_prefix
127   #end
128
129   vm[:networks].each do |net|
130     net_default.keys.each do |key|
131       if not net.has_key?(key)
132         net[key] = net_default[key]
133       end
134     end
135   end
136 end
137
138
139 # compose the list of cluster internal ips
140 #
141 cluster_internal_ips = vms.map do |vm|
142   net = nil
143   vm[:networks].each do |n|
144     if n[:link] == vm[:internal_if]
145       net = n
146       break
147     end
148   end
149   if net != nil
150     net[:ipv4]
151   end
152 end
153
154 #print "internal ips: "
155 #print cluster_internal_ips
156 #print "\n"
157
158
159 NET_FIX_ALWAYS_SCRIPT = <<SCRIPT
160 set -e
161
162 # eth1 is not brought up automatically
163 # by 'vagrant up' of the existing vm.
164 # because eth1 is not up, glusterd can
165 # not be started and gluster volumes can
166 # not be mounted. fix it all up here until
167 # we have a correctly working environment.
168 ifdown eth1
169 ifup eth1
170
171 MOUNTPTS="$@"
172
173 for MOUNTPT in $MOUNTPTS
174 do
175   grep -q -s "${MOUNTPT}" /etc/fstab && {
176     # already provisioned...
177     systemctl start glusterd
178     # sleep to give glusterd some time to start up
179     sleep 2
180
181     mount | grep -q -s "${MOUNTPT}" && {
182       echo "${MOUNTPT} is already mounted."
183     } || {
184       echo "Mounting ${MOUNTPT}."
185       mount ${MOUNTPT}
186     }
187
188     systemctl start ctdb
189   } || {
190     # not provisioned yet
191     echo "${MOUNTPT} not set up yet. Not mounting."
192   }
193 done
194
195 SCRIPT
196
197 NET_FIX_INITIAL_SCRIPT = <<SCRIPT
198 set -e
199 # Fix dhclient running on private network IF
200 ifdown eth1
201 systemctl restart NetworkManager
202 ifdown eth1
203 ifup eth1
204 SCRIPT
205
206
207 SAMBA_CREATE_CONF_SCRIPT = <<SCRIPT
208 set -e
209
210 BACKUP_SUFFIX=".orig.$(date +%Y%m%d-%H%M%S)"
211
212 GLUSTER_VOL=$1
213
214 GLUSTER_VOL_MOUNT=$2
215
216 mkdir -p ${GLUSTER_VOL_MOUNT}/share1
217 chmod -R 0777 ${GLUSTER_VOL_MOUNT}/share1
218
219 mkdir -p ${GLUSTER_VOL_MOUNT}/share2
220 chmod -R 0777 ${GLUSTER_VOL_MOUNT}/share2
221
222 FILE=/etc/samba/smb.conf
223 test -f ${FILE} || touch ${FILE}
224 cp -f -a ${FILE} ${FILE}${BACKUP_SUFFIX}
225
226 echo -n > ${FILE}
227 cat <<EOF >> ${FILE}
228 [global]
229     netbios name = sambacluster
230     workgroup = vagrant
231     security = user
232
233     clustering = yes
234     #include = registry
235
236 [share1]
237     path = /share1
238     vfs objects = acl_xattr glusterfs
239     glusterfs:volume = ${GLUSTER_VOL}
240     kernel share modes = no
241     read only = no
242
243 [share2]
244     path = ${GLUSTER_VOL_MOUNT}/share2
245     vfs objects = acl_xattr
246     read only = no
247 EOF
248 SCRIPT
249
250
251 #
252 # disks: hard-coded for all nodes for now:
253 # TODO: make (some of) these configurable ...
254 #
255 disks = [
256       {
257         :size => 1, # in GB
258         #:volname => "gv0",
259       },
260       {
261         :size => 10,
262         #:volname => "gv1",
263       },
264 ]
265
266 driveletters = ('b'..'z').to_a
267
268 #brick_mount_prefix = "/export"
269 brick_mount_prefix = "/bricks"
270 brick_path_suffix = "brick"
271 gluster_volume_prefix = "gv"
272 gluster_mount_prefix = "/gluster"
273
274 disks.each_with_index do |disk,disk_num|
275   disk[:number] = disk_num
276   disk[:volume_name] = "#{gluster_volume_prefix}#{disk[:number]}"
277   disk[:volume_mount_point] = "#{gluster_mount_prefix}/#{disk[:volume_name]}"
278   disk[:dev_names] = {
279     :libvirt => "vd#{driveletters[disk[:number]]}",
280     :virtualbox => "sd#{driveletters[disk[:number]]}",
281   }
282   disk[:dev_name] = "sd#{driveletters[disk[:number]]}"
283   disk[:brick_name] = "brick0"
284   disk[:label] = "#{disk[:volume_name]}-#{disk[:brick_name]}"
285   disk[:brick_mount_point] = "#{brick_mount_prefix}/#{disk[:label]}"
286   disk[:brick_path] = "#{disk[:brick_mount_point]}/#{brick_path_suffix}"
287 end
288
289 # /dev/{sv}db --> xfs filesys (on /dev/{sv}db1)
290 #  --> mount unter /bricks/gv0
291 #    --> dir /bricks/gv0/brick --> dir for gluster createvol gv0
292 #      --> gluster/fuse mount /gluster/gv0
293
294
295 my_config = {
296   :provider => :libvirt,
297 }
298
299 #
300 # The vagrant machine definitions
301 #
302
303 Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
304
305   config.vm.synced_folder ".", "/vagrant", disabled: true
306   #config.vm.synced_folder './', '/vagrant', type: '9p', disabled: false, accessmode: "squash", owner: "vagrant"
307
308   #if Vagrant.has_plugin?("vagrant-cachier")
309   #  config.cache.scope = :machine
310   #  #config.cache.scope = :box
311
312   #  config.cache.synced_folder_opts = {
313   #    type: :nfs,
314   #    # The nolock option can be useful for an NFSv3 client that wants to avoid the
315   #    # NLM sideband protocol. Without this option, apt-get might hang if it tries
316   #    # to lock files needed for /var/cache/* operations. All of this can be avoided
317   #    # by using NFSv4 everywhere. Please note that the tcp option is not the default.
318   #    #mount_options: ['rw', 'vers=3', 'tcp', 'nolock']
319   #  }
320   #end
321
322   # just let one node do the probing
323   probing = false
324
325   vms.each_with_index do |machine,machine_num|
326     config.vm.define machine[:hostname] do |node|
327       node.vm.box = machine[:provider][:libvirt][:box]
328       node.vm.hostname = machine[:hostname]
329
330       print "machine #{machine_num}: #{machine[:hostname]}\n"
331
332       node.vm.provider :libvirt do |lv|
333         lv.default_prefix = machine[:provider][:libvirt][:prefix]
334         lv.memory = 1024
335       end
336
337       node.vm.provider :virtualbox do |vb|
338         vb.memory = 1024
339       end
340
341       disks.each do |disk|
342         node.vm.provider :libvirt do |lv|
343           print " [libvirt] attaching disk ##{disk[:number]}: #{disk[:dev_name]}\n"
344           lv.storage :file, :size => "#{disk[:size]}G", :device => "#{disk[:dev_names][:libvirt]}"
345           #lv.storage :file, :size => "#{disk[:size]}G", :bus => "sata" , :device => "#{disk[:dev_name]}"
346         end
347         node.vm.provider :virtualbox do |vb|
348           disk_size = disk[:size]*1024
349           #disk_file = "disk-#{machine_num}-#{disk[:dev_names][:virtualbox]}.vdi"
350           #print " [virtualbox] disk ##{disk[:number]}: #{disk[:dev_names][:virtualbox]}\n"
351           disk_file = "disk-#{machine_num}-#{disk[:dev_name]}.vdi"
352           print " [virtualbox] attaching disk ##{disk[:number]}: #{disk[:dev_name]}\n"
353           vb.customize [ "createhd", "--filename", disk_file, "--size", disk_size ]
354           vb.customize [ "storageattach", :id, "--storagectl", "SATA Controller", "--port", 3+disk[:number], "--device", 0, "--type", "hdd", "--medium", disk_file ]
355         end
356       end
357
358       machine[:networks].each do |net|
359         if not net[:ipv4] == ''
360           node.vm.network :private_network, :ip => net[:ipv4]
361         end
362       end
363
364       node.vm.provision "selinux", type: "shell" do |s|
365         s.path = "provision/shell/sys/selinux-off.sh"
366       end
367
368       # There is some problem with the fedora base box:
369       # Upon first boot, ifdown eth1 fails and the dhclient
370       # keep being active. Simply bringing down and up again
371       # the interface is not sufficient. We need to restart
372       # NetworkManager in order to teach it to not feel
373       # responsible for the interface any more.
374       ###node.vm.provision "net_fix_initial", type: "shell" do |s|
375       ###  s.inline = NET_FIX_INITIAL_SCRIPT
376       ###end
377
378       node.vm.provision "install", type: "shell" do |s|
379         s.path = "provision/shell/sys/install-yum.sh"
380         s.args = [ "xfsprogs",
381                    "glusterfs",
382                    "glusterfs-server",
383                    "glusterfs-fuse",
384                    "glusterfs-geo-replication",
385                    "ctdb",
386                    "samba",
387                    "samba-client",
388                    "samba-vfs-glusterfs" ]
389       end
390
391       # There is some problem with the fedora base box:
392       # We need to up the interface on reboots.
393       # It does not come up automatically.
394       ###node.vm.provision "net_fix_always", type: "shell", run: "always" do |s|
395       ###  s.inline = NET_FIX_ALWAYS_SCRIPT
396       ###  s.args = [ '/gluster/gv0', '/gluster/gv1' ]
397       ###end
398
399       disks.each do |disk|
400         print " create_brick: size #{disk[:size]}G, label #{disk[:label]} under #{disk[:brick_mount_point]}\n"
401         node.vm.provision "create_brick_#{disk[:number]}", type: "shell" do |s|
402           s.path = "provision/shell/gluster/create-brick.v2.sh"
403           s.args = [ "#{disk[:size]}G", disk[:label], disk[:brick_mount_point], brick_path_suffix ]
404         end
405       end
406       
407
408       node.vm.provision "gluster_start", type: "shell" do |s|
409         s.path = "provision/shell/gluster/gluster-start.sh"
410       end
411
412       if !probing
413         probing = true
414         node.vm.provision "gluster_probe", type: "shell" do |s|
415           s.path = "provision/shell/gluster/gluster-probe.sh"
416           s.args = cluster_internal_ips
417         end
418       end
419
420       node.vm.provision "gluster_wait_peers", type: "shell" do |s|
421         s.path = "provision/shell/gluster/gluster-wait-peers.sh"
422         s.args = [ cluster_internal_ips.length, 300 ]
423       end
424
425
426       disks.each do |disk|
427         brick_mount_points = cluster_internal_ips.map do |ip|
428           "#{ip}:#{disk[:brick_path]}"
429         end
430         
431         print " brick directories: #{brick_mount_points}\n"
432
433         node.vm.provision "gluster_createvol_#{disk[:number]}", type: "shell" do |s|
434           s.path = "provision/shell/gluster/gluster-create-volume.sh"
435           s.args = [ disk[:volume_name], "3" ] + brick_mount_points
436         end
437
438         node.vm.provision "gluster_mount_#{disk[:number]}", type: "shell" do |s|
439           s.path = "provision/shell/gluster/gluster-mount-volume.sh"
440           s.args = [ disk[:volume_name], disk[:volume_mount_point] ]
441         end
442       end
443
444       #
445       # ctdb / samba config
446       #
447
448       node.vm.provision "ctdb_stop", type: "shell" do |s|
449         s.path = "provision/shell/ctdb/ctdb-stop.sh"
450       end
451
452       node.vm.provision "ctdb_create_nodes", type: "shell" do |s|
453         s.path = "provision/shell/ctdb/ctdb-create-nodes.sh"
454         s.args = cluster_internal_ips
455       end
456
457       #node.vm.provision "ctdb_create_pubaddrs", type: "shell" do |s|
458       #  s.path = "provision/shell/ctdb/ctdb-create-pubaddrs.sh"
459       #  s.arg =
460       #end
461
462       node.vm.provision "ctdb_create_conf", type: "shell" do |s|
463         s.path = "provision/shell/ctdb/ctdb-create-conf.sh"
464         s.args = [ "/gluster/gv0/ctdb" ]
465       end
466
467       node.vm.provision "samba_create_conf", type: "shell" do |s|
468         s.inline = SAMBA_CREATE_CONF_SCRIPT
469         s.args = [ "gv1", "/gluster/gv1" ]
470       end
471
472       node.vm.provision "ctdb_start", type: "shell" do |s|
473         s.path = "provision/shell/ctdb/ctdb-start.sh"
474       end
475
476     end
477   end
478
479 end