Externalize gluster-probe provision script
[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     },
40   },
41 }
42
43
44 vms = [
45   {
46     #:hostname => 'gluno1',
47     :hostname => 'node1',
48     #:box => 'local-fedora-rawhide-64',
49     #:box => 'purpleidea-fedora-21',
50     #:box => 'local-fedora-21.2',
51     :provider => {
52       :lxc => {
53         :container_name => 'gluno1',
54         #:container_name => 'node1',
55       },
56       :libvirt => {
57         :box => 'local-fedora-21.2',
58         :prefix => 'gluster',
59       }, 
60     },
61     :internal_if => 'virbr1',
62     :networks => [
63       {
64         :link => 'virbr1',
65         :ipv4 => '172.20.10.30',
66       },
67       #{
68       #  :link => 'virbr2',
69       #  #:ipv4 => '10.111.222.201',
70       #},
71     ],
72   },
73 ]
74
75 #
76 # Load the config, if it exists,
77 # possibly override with commandline args,
78 # (currently none supported yet)
79 # and then store the config.
80 #
81
82 projectdir = File.expand_path File.dirname(__FILE__)
83 f = File.join(projectdir, 'vagrant.yaml')
84 if File.exists?(f)
85   settings = YAML::load_file f
86
87   if settings[:vms].is_a?(Array)
88     vms = settings[:vms]
89   end
90   puts "Loaded settings from #{f}."
91 end
92
93 # TODO(?): ARGV-processing
94
95 settings = {
96   :vms  => vms,
97 }
98
99 File.open(f, 'w') do |file|
100   file.write settings.to_yaml
101 end
102 puts "Wrote settings to #{f}."
103
104
105 # apply defaults:
106
107 vms.each do |vm|
108   defaults.keys.each do |cat|
109     next if not vm.has_key?(cat)
110     defaults[cat].keys.each do |subcat|
111       next if not vm[cat].has_key?(subcat)
112       defaults[cat][subcat].keys.each do |key|
113         if not vm[cat][subcat].has_key?(key)
114           vm[cat][subcat][key] = defaults[cat][subcat][key]
115         end
116       end
117     end
118   end
119
120   #if not vm[:provider][:libvirt].has_key?(:prefix)
121   #  vm[:provider][:libvirt][:prefix] = default_libvirt_prefix
122   #end
123
124   vm[:networks].each do |net|
125     net_default.keys.each do |key|
126       if not net.has_key?(key)
127         net[key] = net_default[key]
128       end
129     end
130   end
131 end
132
133
134 # compose the list of cluster internal ips
135 #
136 cluster_internal_ips = vms.map do |vm|
137   net = nil
138   vm[:networks].each do |n|
139     if n[:link] == vm[:internal_if]
140       net = n
141       break
142     end
143   end
144   if net != nil
145     net[:ipv4]
146   end
147 end
148
149 #print "internal ips: "
150 #print cluster_internal_ips
151 #print "\n"
152
153 #PROVISION_SCRIPT = <<SCRIPT
154 #yum -y install make samba
155 #SCRIPT
156
157
158 NET_FIX_ALWAYS_SCRIPT = <<SCRIPT
159 set -e
160
161 # eth1 is not brought up automatically
162 # by 'vagrant up' of the existing vm.
163 # because eth1 is not up, glusterd can
164 # not be started and gluster volumes can
165 # not be mounted. fix it all up here until
166 # we have a correctly working environment.
167 ifdown eth1
168 ifup eth1
169
170 MOUNTPTS="$@"
171
172 for MOUNTPT in $MOUNTPTS
173 do
174   grep -q -s "${MOUNTPT}" /etc/fstab && {
175     # already provisioned...
176     systemctl start glusterd
177     # sleep to give glusterd some time to start up
178     sleep 2
179
180     mount | grep -q -s "${MOUNTPT}" && {
181       echo "${MOUNTPT} is already mounted."
182     } || {
183       echo "Mounting ${MOUNTPT}."
184       mount ${MOUNTPT}
185     }
186
187     systemctl start ctdb
188   } || {
189     # not provisioned yet
190     echo "${MOUNTPT} not set up yet. Not mounting."
191   }
192 done
193
194 SCRIPT
195
196 NET_FIX_INITIAL_SCRIPT = <<SCRIPT
197 set -e
198 # Fix dhclient running on private network IF
199 ifdown eth1
200 systemctl restart NetworkManager
201 ifdown eth1
202 ifup eth1
203 SCRIPT
204
205
206 GLUSTER_WAIT_PEERS_SCRIPT = <<SCRIPT
207 set -e
208
209 NUM_NODES="$1"
210 TIMEOUT=$2
211
212 echo "Waiting for $NUM_NODES peers."
213
214 for count in $(seq 1 ${TIMEOUT})
215 do
216   PEERS=$(gluster pool list | grep -v ^UUID | wc -l)
217   [ "$PEERS" = "$NUM_NODES" ] && {
218     echo "Done waiting: $NUM_NODES peers connected."
219     exit 0
220   } || {
221     sleep 1
222   }
223 done
224
225 echo "TIMEOUT waiting for $NUM_NODES peers."
226 exit 1
227
228 SCRIPT
229
230 GLUSTER_CREATEVOL_SCRIPT = <<SCRIPT
231 #set -e
232
233 VOLNAME=$1
234 shift
235 REP=$1
236 shift
237
238 while true; do
239   MSG="$(gluster volume status ${VOLNAME} 2>&1 1>/dev/null)"
240   RET=$?
241   [ $RET -eq 0 ] && break
242   [ "${MSG}" != "${MSG#Another transaction is in progress}" ] || break
243   sleep 1
244 done
245
246 [ $RET -eq 0 ] && {
247   echo "gluster volume ${VOLNAME} already exists and is active."
248   exit 0
249 }
250
251 [ "$MSG" = "Volume ${VOLNAME} does not exist" ] && {
252   echo "Creating gluster volume ${VOLNAME}."
253   echo "cmd: gluster volume create $VOLNAME rep $REP transport tcp $@"
254   while true; do
255     MSG=$(gluster volume create $VOLNAME rep $REP transport tcp $@ 2>&1 1>/dev/null)
256     RET=$?
257     [ $RET -eq 0 ] && break
258     [ "$MSG" = "volume create: ${VOLNAME}: failed: Volume ${VOLNAME} already exists" ] && {
259       RET=0
260       break
261     }
262     [ "${MSG}" != "${MSG#Another transaction is in progress}" ] || break
263   done
264
265   [ $RET -eq 0 ] || {
266     echo "gluster volume create $VOLNAME failed ('$MSG')- trying to force."
267
268     while true; do
269       MSG=$(gluster volume create $VOLNAME rep $REP transport tcp $@ force 2>&1 1>/dev/null)
270       RET=$?
271       [ $RET -eq 0 ] && break
272       [ "$MSG" = "volume create: ${VOLNAME}: failed: Volume ${VOLNAME} already exists" ] && {
273         RET=0
274         break
275       }
276       [ "${MSG}" != "${MSG#Another transaction is in progress}" ] || break
277     done
278   }
279
280   [ $RET -eq 0 ] || {
281     echo "gluster volume create $VOLNAME failed with force ('$MSG')- giving up"
282     exit 1
283   }
284
285   while true; do
286     MSG="$(gluster volume status ${VOLNAME} 2>&1 1>/dev/null)"
287     RET=$?
288     [ $RET -eq 0 ] && break
289     [ "${MSG}" != "${MSG#Another transaction is in progress}" ] || break
290     sleep 1
291   done
292
293   [ $RET -eq 0 ] && {
294     echo "gluster volume ${VOLNAME} is already started."
295     exit 0
296   }
297 }
298
299 [ "$MSG" = "Volume ${VOLNAME} is not started" ] && {
300   echo "starting gluster volume ${VOLNAME}."
301   while true; do
302     MSG=$(gluster volume start ${VOLNAME} 2>&1 1> /dev/null)
303     RET=$?
304     [ $RET -eq 0 ] && break
305     [ "$MSG" = "volume start: ${VOLNAME}: failed: Volume ${VOLNAME} already started" ] && {
306       RET=0
307       break
308     }
309     [ "${MSG}" != "${MSG#Another transaction is in progress}" ] || break
310   done
311
312   [ $RET -eq 0 ] || {
313     echo "gluster volume start ${VOLNAME} failed ('$MSG')."
314     exit 1
315   }
316 } || {
317   echo "Error: 'gluster volume status ${VOLNAME}' gave '$MSG' ($RET)"
318   exit 1
319 }
320
321 exit 0
322
323 SCRIPT
324
325 GLUSTER_MOUNT_SCRIPT = <<SCRIPT
326 set -e
327
328 VOLNAME=$1
329 shift
330 MOUNTPT=$1
331 shift
332
333 MOUNTDEV="127.0.0.1:/${VOLNAME}"
334
335 mkdir -p ${MOUNTPT}
336
337 BACKUP_SUFFIX=".orig.$(date +%Y%m%d-%H%M%S)"
338
339 FILE=/etc/fstab
340
341 grep -q -s "${MOUNTPT}" ${FILE} || {
342   test -f ${FILE} || touch ${FILE}
343   cp -f -a ${FILE} ${FILE}${BACKUP_SUFFIX}
344
345   cat <<EOF >> ${FILE}
346 ${MOUNTDEV} ${MOUNTPT} glusterfs defaults,selinux 0 0
347 EOF
348 }
349
350 mount | grep -q -s ${MOUNTPT} && {
351   echo "${MOUNTPT} is already mounted."
352 } || {
353   echo "Mounting ${MOUNTPT}."
354   mount ${MOUNTPT}
355 }
356 SCRIPT
357
358
359 CTDB_CREATE_NODES_SCRIPT = <<SCRIPT
360 set -e
361
362 BACKUP_SUFFIX=".orig.$(date +%Y%m%d-%H%M%S)"
363
364 NODES_IPS="$@"
365
366 FILE=/etc/ctdb/nodes
367 test -f ${FILE} || touch ${FILE}
368 cp -f -a ${FILE} ${FILE}${BACKUP_SUFFIX}
369
370 echo -n > ${FILE}
371 for IP in ${NODES_IPS}
372 do
373   echo "$IP" >> ${FILE}
374 done
375 SCRIPT
376
377 CTDB_CREATE_PUBADDRS_SCRIPT = <<SCRIPT
378 set -e
379
380 BACKUP_SUFFIX=".orig.$(date +%Y%m%d-%H%M%S)"
381
382 PUB_IPS="$@"
383
384 FILE=/etc/ctdb/public_addresses
385 test -f ${FILE} || touch ${FILE}
386 cp -f -a ${FILE} ${FILE}${BACKUP_SUFFIX}
387
388 echo -n > ${FILE}
389 for IP in ${PUB_IPS}
390 do
391   echo ${IP} >> ${FILE}
392 done
393 SCRIPT
394
395 CTDB_CREATE_CONF_SCRIPT = <<SCRIPT
396 set -e
397
398 BACKUP_SUFFIX=".orig.$(date +%Y%m%d-%H%M%S)"
399
400 RECLOCKDIR=/gluster/gv0/ctdb
401 mkdir -p ${RECLOCKDIR}
402 RECLOCKFILE=${RECLOCKDIR}/reclock
403
404 PUBLIC_ADDRESSES_FILE=/etc/ctdb/public_addresses
405 NODES_FILE=/etc/ctdb/nodes
406
407 FILE=/etc/sysconfig/ctdb
408 test -f ${FILE} || touch ${FILE}
409 cp -f -a ${FILE} ${FILE}${BACKUP_SUFFIX}
410
411 echo -n > ${FILE}
412 cat <<EOF >> ${FILE}
413 CTDB_NODES=${NODES_FILE}
414 #CTDB_PUBLIC_ADDRESSES=${PUBLIC_ADDRESSES_FILE}
415 CTDB_RECOVERY_LOCK=${RECLOCKFILE}
416 CTDB_MANAGES_SAMBA="yes"
417 CTDB_SAMBA_SKIP_SHARE_CHECK="yes"
418 #CTDB_MANAGES_WINBIND="yes"
419 EOF
420 SCRIPT
421
422 SAMBA_CREATE_CONF_SCRIPT = <<SCRIPT
423 set -e
424
425 BACKUP_SUFFIX=".orig.$(date +%Y%m%d-%H%M%S)"
426
427 GLUSTER_VOL=$1
428
429 GLUSTER_VOL_MOUNT=$2
430
431 mkdir -p ${GLUSTER_VOL_MOUNT}/share1
432 chmod -R 0777 ${GLUSTER_VOL_MOUNT}/share1
433
434 mkdir -p ${GLUSTER_VOL_MOUNT}/share2
435 chmod -R 0777 ${GLUSTER_VOL_MOUNT}/share2
436
437 FILE=/etc/samba/smb.conf
438 test -f ${FILE} || touch ${FILE}
439 cp -f -a ${FILE} ${FILE}${BACKUP_SUFFIX}
440
441 echo -n > ${FILE}
442 cat <<EOF >> ${FILE}
443 [global]
444     netbios name = sambacluster
445     workgroup = vagrant
446     security = user
447
448     clustering = yes
449     #include = registry
450
451 [share1]
452     path = /share1
453     vfs objects = acl_xattr glusterfs
454     glusterfs:volume = ${GLUSTER_VOL}
455     kernel share modes = no
456     read only = no
457
458 [share2]
459     path = ${GLUSTER_VOL_MOUNT}/share2
460     vfs objects = acl_xattr
461     read only = no
462 EOF
463 SCRIPT
464
465 #
466 # The vagrant machine definitions
467 #
468
469 Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
470
471   config.vm.synced_folder ".", "/vagrant", disabled: true
472
473   #if Vagrant.has_plugin?("vagrant-cachier")
474   #  config.cache.scope = :machine
475   #  #config.cache.scope = :box
476
477   #  config.cache.synced_folder_opts = {
478   #    type: :nfs,
479   #    # The nolock option can be useful for an NFSv3 client that wants to avoid the
480   #    # NLM sideband protocol. Without this option, apt-get might hang if it tries
481   #    # to lock files needed for /var/cache/* operations. All of this can be avoided
482   #    # by using NFSv4 everywhere. Please note that the tcp option is not the default.
483   #    #mount_options: ['rw', 'vers=3', 'tcp', 'nolock']
484   #  }
485   #end
486
487   # just let one node do the probing
488   probing = false
489
490   vms.each do |machine|
491     config.vm.define machine[:hostname] do |node|
492       node.vm.box = machine[:provider][:libvirt][:box]
493       node.vm.hostname = machine[:hostname]
494
495       node.vm.provider :libvirt do |libvirt|
496         libvirt.default_prefix = machine[:provider][:libvirt][:prefix]
497         libvirt.memory = 1024
498         libvirt.storage :file, :size => '64M', :device => 'vdb'
499         libvirt.storage :file, :size => '10G', :device => 'vdc'
500
501         machine[:networks].each do |net|
502           if not net[:ipv4] == ''
503             node.vm.network :private_network, :ip => net[:ipv4]
504           end
505         end
506       end
507
508
509       node.vm.provision "selinux", type: "shell" do |s|
510         s.path = "provision/shell/sys/selinux-off.sh"
511       end
512
513       # There is some problem with the fedora base box:
514       # Upon first boot, ifdown eth1 fails and the dhclient
515       # keep being active. Simply bringing down and up again
516       # the interface is not sufficient. We need to restart
517       # NetworkManager in order to teach it to not feel
518       # responsible for the interface any more.
519       ###node.vm.provision "net_fix_initial", type: "shell" do |s|
520       ###  s.inline = NET_FIX_INITIAL_SCRIPT
521       ###end
522
523       node.vm.provision "install", type: "shell" do |s|
524         s.path = "provision/shell/sys/install-yum.sh"
525         s.args = [ "xfsprogs",
526                    "glusterfs",
527                    "glusterfs-server",
528                    "glusterfs-fuse",
529                    "glusterfs-geo-replication",
530                    "ctdb",
531                    "samba",
532                    "samba-client",
533                    "samba-vfs-glusterfs" ]
534       end
535
536       # There is some problem with the fedora base box:
537       # We need to up the interface on reboots.
538       # It does not come up automatically.
539       ###node.vm.provision "net_fix_always", type: "shell", run: "always" do |s|
540       ###  s.inline = NET_FIX_ALWAYS_SCRIPT
541       ###  s.args = [ '/gluster/gv0', '/gluster/gv1' ]
542       ###end
543
544       # multiple privisioners with same name possible?
545       node.vm.provision "xfs_0", type: "shell" do |s|
546         s.path = "provision/shell/gluster/create-brick.sh"
547         s.args = [ "vdb", "/export" ]
548       end
549
550       node.vm.provision "xfs_1", type: "shell" do |s|
551         s.path = "provision/shell/gluster/create-brick.sh"
552         s.args = [ "vdc", "/export" ]
553       end
554
555       node.vm.provision "gluster_start", type: "shell" do |s|
556         s.path = "provision/shell/gluster/gluster-start.sh"
557       end
558
559       if !probing
560         probing = true
561         node.vm.provision "gluster_probe", type: "shell" do |s|
562           s.path = "provision/shell/gluster/gluster-probe.sh"
563           s.args = cluster_internal_ips
564         end
565         probing = false
566       end
567
568       node.vm.provision "gluster_wait_peers", type: "shell" do |s|
569         s.inline = GLUSTER_WAIT_PEERS_SCRIPT
570         s.args = [ cluster_internal_ips.length, 300]
571       end
572
573       node.vm.provision "gluster_createvol_0", type: "shell" do |s|
574         mount_points = cluster_internal_ips.map do |ip|
575           "#{ip}:/export/vdb1/brick"
576         end
577         s.inline = GLUSTER_CREATEVOL_SCRIPT
578         s.args = [ "gv0", "3" ] + mount_points
579       end
580
581       node.vm.provision "gluster_mount_0", type: "shell" do |s|
582         s.inline = GLUSTER_MOUNT_SCRIPT
583         s.args = [ "gv0", "/gluster/gv0" ]
584       end
585
586       node.vm.provision "gluster_createvol_1", type: "shell" do |s|
587         mount_points = cluster_internal_ips.map do |ip|
588           "#{ip}:/export/vdc1/brick"
589         end
590         s.inline = GLUSTER_CREATEVOL_SCRIPT
591         s.args = [ "gv1", "3" ] + mount_points
592       end
593
594       node.vm.provision "gluster_mount_1", type: "shell" do |s|
595         s.inline = GLUSTER_MOUNT_SCRIPT
596         s.args = [ "gv1", "/gluster/gv1" ]
597       end
598
599       #
600       # ctdb / samba config
601       #
602
603       node.vm.provision "ctdb_stop", type: "shell" do |s|
604         s.path = "provision/shell/ctdb/ctdb-stop.sh"
605       end
606
607       node.vm.provision "ctdb_create_nodes", type: "shell" do |s|
608         s.inline = CTDB_CREATE_NODES_SCRIPT
609         s.args = cluster_internal_ips
610       end
611
612       #node.vm.provision "ctdb_create_pubaddrs", type: "shell" do |s|
613       #  s.inline = CTDB_CREATE_PUBADDRS_SCRIPT
614       #  s.arg =
615       #end
616
617       node.vm.provision "ctdb_create_conf", type: "shell" do |s|
618         s.inline = CTDB_CREATE_CONF_SCRIPT
619       end
620
621       node.vm.provision "samba_create_conf", type: "shell" do |s|
622         s.inline = SAMBA_CREATE_CONF_SCRIPT
623         s.args = [ "gv1", "/gluster/gv1" ]
624       end
625
626       node.vm.provision "ctdb_start", type: "shell" do |s|
627         s.path = "provision/shell/ctdb/ctdb-start.sh"
628       end
629
630     end
631   end
632
633 end