Allow probing again by other nodes if probing provisioner is done.
[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 mountd. 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 XFS_SCRIPT = <<SCRIPT
206 set -e
207
208 DEVICE=$1
209 PARTDEV=${DEVICE}1
210 DISKDEV="/dev/${DEVICE}"
211 DISKPARTDEV="/dev/${PARTDEV}"
212 ##MOUNTP=$2
213 MOUNTP=/export/${PARTDEV}
214 BRICKD=${MOUNTP}/brick
215
216 BACKUP_SUFFIX=".orig.$(date +%Y%m%d-%H%M%S)"
217
218 parted -s ${DISKDEV} print > /dev/null 2>&1 && {
219   echo "Label exists on ${DISKDEV}."
220 } || {
221   echo "Creating label on ${DISKDEV}."
222   parted -s ${DISKDEV} mklabel msdos
223 }
224
225 parted -s ${DISKDEV} print 1 > /dev/null 2>&1 && {
226   echo "Partition ${DISKPARTDEV} exists."
227 } || {
228   echo "Creating partition ${DISKPARTDEV}."
229   parted -s ${DISKDEV} mkpart primary 1 100%
230 }
231
232 blkid -s TYPE ${DISKPARTDEV} | grep -q -s 'TYPE="xfs"' && {
233   echo "Partition ${DISKPARTDEV} contains xfs file system."
234 } || {
235   echo "Creating xfs filesystem on ${DISKPARTDEV}."
236   mkfs.xfs -f ${DISKPARTDEV}
237 }
238
239 mkdir -p ${MOUNTP}
240
241 FILE=/etc/fstab
242
243 grep -q -s ${DISKPARTDEV} ${FILE} && {
244   echo "Mount entry for ${DISKPARTDEV} is present in ${FILE}."
245 } || {
246   echo "Creating mount entry for ${DISKPARTDEV} in ${FILE}."
247   test -f ${FILE} || touch ${FILE}
248   cp -f -a ${FILE} ${FILE}${BACKUP_SUFFIX}
249   cat <<EOF >> ${FILE}
250 ${DISKPARTDEV} ${MOUNTP} xfs defaults 0 0
251 EOF
252 }
253
254 mount | grep ${MOUNTP} && {
255   echo "${MOUNTP} is already mounted."
256 } || {
257   echo "Mounting ${MOUNTP}."
258   mount ${MOUNTP}
259 }
260
261 mkdir -p ${BRICKD}
262 SCRIPT
263
264
265 GLUSTER_PROBE_SCRIPT = <<SCRIPT
266 set -e
267
268 PEER_IPS="$@"
269
270 echo "peer probing for [${PEER_IPS}]"
271
272 for PEER_IP in ${PEER_IPS}
273 do
274   echo "peer probing for '${PEER_IP}'"
275
276   for COUNT in $(seq 1 120)
277   do
278     gluster peer probe ${PEER_IP} 2> /dev/null && {
279       echo "reached node '${PEER_IP}'"
280       break
281     } || {
282       sleep 1
283     }
284   done
285
286   gluster peer probe ${PEER_IP} 2> /dev/null || {
287     echo "did not reach node '${PEER_IP}' - stopping here"
288     break
289   }
290 done
291 exit 0
292 SCRIPT
293
294 GLUSTER_WAIT_PEERS_SCRIPT = <<SCRIPT
295 set -e
296
297 NUM_NODES="$1"
298
299 echo "Waiting for $NUM_NODES peers."
300
301 for count in $(seq 1 300)
302 do
303   PEERS=$(gluster pool list | grep -v ^UUID | wc -l)
304   [ "$PEERS" = "$NUM_NODES" ] && {
305     echo "Done waiting: $NUM_NODES peers connected."
306     exit 0
307   } || {
308     sleep 1
309   }
310 done
311
312 echo "TIMEOUT waiting for $NUM_NODES peers."
313 exit 1
314
315 SCRIPT
316
317 GLUSTER_CREATEVOL_SCRIPT = <<SCRIPT
318 #set -e
319
320 VOLNAME=$1
321 shift
322 REP=$1
323 shift
324
325 while true; do
326   MSG="$(gluster volume status ${VOLNAME} 2>&1 1>/dev/null)"
327   RET=$?
328   [ $RET -eq 0 ] && break
329   [ "$MSG" = 'Another transaction is in progress. Please try again after sometime.' ] || break
330   sleep 1
331 done
332
333 [ $RET -eq 0 ] && {
334   echo "gluster volume ${VOLNAME} already exists and is active."
335   exit 0
336 }
337
338 [ "$MSG" = "Volume ${VOLNAME} does not exist" ] && {
339   echo "Creating gluster volume ${VOLNAME}."
340   echo "cmd: gluster volume create $VOLNAME rep $REP transport tcp $@"
341   while true; do
342     MSG=$(gluster volume create $VOLNAME rep $REP transport tcp $@ 2>&1 1>/dev/null)
343     RET=$?
344     [ $RET -eq 0 ] && break
345     [ "$MSG" = "volume create: ${VOLNAME}: failed: Volume ${VOLNAME} already exists" ] && {
346       RET=0
347       break
348     }
349     [ "$MSG" = "volume create: $VOLNAME: failed: Another transaction is in progress. Please try again after sometime." ] || break
350   done
351
352   [ $RET -eq 0 ] || {
353     echo "gluster volume create $VOLNAME failed ('$MSG')- trying to force."
354
355     while true; do
356       MSG=$(gluster volume create $VOLNAME rep $REP transport tcp $@ force 2>&1 1>/dev/null)
357       RET=$?
358       [ $RET -eq 0 ] && break
359       [ "$MSG" = "volume create: ${VOLNAME}: failed: Volume ${VOLNAME} already exists" ] && {
360         RET=0
361         break
362       }
363       [ "$MSG" = "volume create: $VOLNAME: failed: Another transaction is in progress. Please try again after sometime." ] || break
364     done
365   }
366
367   [ $RET -eq 0 ] || {
368     echo "gluster volume create $VOLNAME failed with force ('$MSG')- giving up"
369     exit 1
370   }
371
372   while true; do
373     MSG="$(gluster volume status ${VOLNAME} 2>&1 1>/dev/null)"
374     RET=$?
375     [ $RET -eq 0 ] && break
376     [ "${MSG}" != "${MSG#Another transaction}" ] || break
377     sleep 1
378   done
379
380   [ $RET -eq 0 ] && {
381     echo "gluster volume ${VOLNAME} is already started."
382     exit 0
383   }
384 }
385
386 [ "$MSG" = "Volume ${VOLNAME} is not started" ] && {
387   echo "starting gluster volume ${VOLNAME}."
388   while true; do
389     MSG=$(gluster volume start ${VOLNAME} 2>&1 1> /dev/null)
390     RET=$?
391     [ $RET -eq 0 ] && break
392     [ "$MSG" = "volume start: ${VOLNAME}: failed: Volume ${VOLNAME} already started" ] && {
393       RET=0
394       break
395     }
396     [ "$MSG" = "volume start: ${VOLNAME}: failed: Another transaction is in progress. Please try again after sometime." ] || break
397   done
398
399   [ $RET -eq 0 ] || {
400     echo "gluster volume start ${VOLNAME} failed ('$MSG')."
401     exit 1
402   }
403 } || {
404   echo "Error: 'gluster volume status ${VOLNAME}' gave '$MSG' ($RET)"
405   exit 1
406 }
407
408 exit 0
409
410 SCRIPT
411
412 GLUSTER_MOUNT_SCRIPT = <<SCRIPT
413 set -e
414
415 VOLNAME=$1
416 shift
417 MOUNTPT=$1
418 shift
419
420 MOUNTDEV="127.0.0.1:/${VOLNAME}"
421
422 mkdir -p ${MOUNTPT}
423
424 BACKUP_SUFFIX=".orig.$(date +%Y%m%d-%H%M%S)"
425
426 FILE=/etc/fstab
427
428 grep -q -s "${MOUNTPT}" ${FILE} || {
429   test -f ${FILE} || touch ${FILE}
430   cp -f -a ${FILE} ${FILE}${BACKUP_SUFFIX}
431
432   cat <<EOF >> ${FILE}
433 ${MOUNTDEV} ${MOUNTPT} glusterfs defaults,selinux 0 0
434 EOF
435 }
436
437 mount | grep -q -s ${MOUNTPT} && {
438   echo "${MOUNTPT} is already mounted."
439 } || {
440   echo "Mounting ${MOUNTPT}."
441   mount ${MOUNTPT}
442 }
443 SCRIPT
444
445
446 CTDB_STOP_SCRIPT = <<SCRIPT
447 set -e
448 systemctl stop ctdb.service
449 SCRIPT
450
451 CTDB_CREATE_NODES_SCRIPT = <<SCRIPT
452 set -e
453
454 BACKUP_SUFFIX=".orig.$(date +%Y%m%d-%H%M%S)"
455
456 NODES_IPS="$@"
457
458 FILE=/etc/ctdb/nodes
459 test -f ${FILE} || touch ${FILE}
460 cp -f -a ${FILE} ${FILE}${BACKUP_SUFFIX}
461
462 echo -n > ${FILE}
463 for IP in ${NODES_IPS}
464 do
465   echo "$IP" >> ${FILE}
466 done
467 SCRIPT
468
469 CTDB_CREATE_PUBADDRS_SCRIPT = <<SCRIPT
470 set -e
471
472 BACKUP_SUFFIX=".orig.$(date +%Y%m%d-%H%M%S)"
473
474 PUB_IPS="$@"
475
476 FILE=/etc/ctdb/public_addresses
477 test -f ${FILE} || touch ${FILE}
478 cp -f -a ${FILE} ${FILE}${BACKUP_SUFFIX}
479
480 echo -n > ${FILE}
481 for IP in ${PUB_IPS}
482 do
483   echo ${IP} >> ${FILE}
484 done
485 SCRIPT
486
487 CTDB_CREATE_CONF_SCRIPT = <<SCRIPT
488 set -e
489
490 BACKUP_SUFFIX=".orig.$(date +%Y%m%d-%H%M%S)"
491
492 RECLOCKDIR=/gluster/gv0/ctdb
493 mkdir -p ${RECLOCKDIR}
494 RECLOCKFILE=${RECLOCKDIR}/reclock
495
496 PUBLIC_ADDRESSES_FILE=/etc/ctdb/public_addresses
497 NODES_FILE=/etc/ctdb/nodes
498
499 FILE=/etc/sysconfig/ctdb
500 test -f ${FILE} || touch ${FILE}
501 cp -f -a ${FILE} ${FILE}${BACKUP_SUFFIX}
502
503 echo -n > ${FILE}
504 cat <<EOF >> ${FILE}
505 CTDB_NODES=${NODES_FILE}
506 #CTDB_PUBLIC_ADDRESSES=${PUBLIC_ADDRESSES_FILE}
507 CTDB_RECOVERY_LOCK=${RECLOCKFILE}
508 CTDB_MANAGES_SAMBA="yes"
509 CTDB_SAMBA_SKIP_SHARE_CHECK="yes"
510 #CTDB_MANAGES_WINBIND="yes"
511 EOF
512 SCRIPT
513
514 SAMBA_CREATE_CONF_SCRIPT = <<SCRIPT
515 set -e
516
517 BACKUP_SUFFIX=".orig.$(date +%Y%m%d-%H%M%S)"
518
519 GLUSTER_VOL=$1
520
521 GLUSTER_VOL_MOUNT=$2
522
523 mkdir -p ${GLUSTER_VOL_MOUNT}/share1
524 chmod -R 0777 ${GLUSTER_VOL_MOUNT}/share1
525
526 mkdir -p ${GLUSTER_VOL_MOUNT}/share2
527 chmod -R 0777 ${GLUSTER_VOL_MOUNT}/share2
528
529 FILE=/etc/samba/smb.conf
530 test -f ${FILE} || touch ${FILE}
531 cp -f -a ${FILE} ${FILE}${BACKUP_SUFFIX}
532
533 echo -n > ${FILE}
534 cat <<EOF >> ${FILE}
535 [global]
536     netbios name = sambacluster
537     workgroup = vagrant
538     security = user
539
540     clustering = yes
541     #include = registry
542
543 [share1]
544     path = /share1
545     vfs objects = acl_xattr glusterfs
546     glusterfs:volume = ${GLUSTER_VOL}
547     kernel share modes = no
548     read only = no
549
550 [share2]
551     path = ${GLUSTER_VOL_MOUNT}/share2
552     vfs objects = acl_xattr
553     read only = no
554 EOF
555 SCRIPT
556
557 CTDB_START_SCRIPT = <<SCRIPT
558 set -e
559 systemctl start ctdb.service
560 SCRIPT
561 #
562 # The vagrant machine definitions
563 #
564
565 Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
566
567   if Vagrant.has_plugin?("vagrant-cachier")
568     config.cache.scope = :machine
569     #config.cache.scope = :box
570
571     config.cache.synced_folder_opts = {
572       type: :nfs,
573       # The nolock option can be useful for an NFSv3 client that wants to avoid the
574       # NLM sideband protocol. Without this option, apt-get might hang if it tries
575       # to lock files needed for /var/cache/* operations. All of this can be avoided
576       # by using NFSv4 everywhere. Please note that the tcp option is not the default.
577       #mount_options: ['rw', 'vers=3', 'tcp', 'nolock']
578     }
579   end
580
581   # just let one node do the probing
582   probing = false
583
584   vms.each do |machine|
585     config.vm.define machine[:hostname] do |node|
586       node.vm.box = machine[:provider][:libvirt][:box]
587       node.vm.hostname = machine[:hostname]
588
589       node.vm.provider :libvirt do |libvirt|
590         libvirt.default_prefix = machine[:provider][:libvirt][:prefix]
591         libvirt.memory = 1024
592         libvirt.storage :file, :size => '64M', :device => 'vdb'
593         libvirt.storage :file, :size => '10G', :device => 'vdc'
594
595         machine[:networks].each do |net|
596           if not net[:ipv4] == ''
597             node.vm.network :private_network, :ip => net[:ipv4]
598           end
599         end
600       end
601
602
603       node.vm.provision "selinux", type: "shell" do |s|
604         s.path = "provision/shell/sys/selinux-off.sh"
605       end
606
607       # There is some problem with the fedora base box:
608       # Upon first boot, ifdown eth1 fails and the dhclient
609       # keep being active. Simply bringing down and up again
610       # the interface is not sufficient. We need to restart
611       # NetworkManager in order to teach it to not feel
612       # responsible for the interface any more.
613       node.vm.provision "net_fix_initial", type: "shell" do |s|
614         s.inline = NET_FIX_INITIAL_SCRIPT
615       end
616
617       node.vm.provision "install", type: "shell" do |s|
618         s.path = "provision/shell/sys/install-yum.sh"
619         s.args = [ "xfsprogs",
620                    "glusterfs",
621                    "glusterfs-server",
622                    "glusterfs-fuse",
623                    "glusterfs-geo-replication",
624                    "ctdb",
625                    "samba",
626                    "samba-client",
627                    "samba-vfs-glusterfs" ]
628       end
629
630       # There is some problem with the fedora base box:
631       # We need to up the interface on reboots.
632       # It does not come up automatically.
633       node.vm.provision "net_fix_always", type: "shell", run: "always" do |s|
634         s.inline = NET_FIX_ALWAYS_SCRIPT
635         s.args = [ '/gluster/gv0', '/gluster/gv1' ]
636       end
637
638       # multiple privisioners with same name possible?
639       node.vm.provision "xfs_0", type: "shell" do |s|
640         s.inline = XFS_SCRIPT
641         #s.args = [ "vdb", "/export/gluster/brick1" ]
642         s.args = [ "vdb" ]
643       end
644
645       node.vm.provision "xfs_1", type: "shell" do |s|
646         s.inline = XFS_SCRIPT
647         #s.args = [ "vdc" , "/export/gluster/brick2" ]
648         s.args = [ "vdc" ]
649       end
650
651       node.vm.provision "gluster_start", type: "shell" do |s|
652         s.path = "provision/shell/gluster/gluster-start.sh"
653       end
654
655       if !probing
656         probing = true
657         node.vm.provision "gluster_probe", type: "shell" do |s|
658           s.inline = GLUSTER_PROBE_SCRIPT
659           s.args = cluster_internal_ips
660         end
661         probing = false
662       end
663
664       node.vm.provision "gluster_wait_peers", type: "shell" do |s|
665         s.inline = GLUSTER_WAIT_PEERS_SCRIPT
666         s.args = [ cluster_internal_ips.length ]
667       end
668
669       node.vm.provision "gluster_createvol_0", type: "shell" do |s|
670         mount_points = cluster_internal_ips.map do |ip|
671           "#{ip}:/export/vdb1/brick"
672         end
673         s.inline = GLUSTER_CREATEVOL_SCRIPT
674         s.args = [ "gv0", "3" ] + mount_points
675       end
676
677       node.vm.provision "gluster_mount_0", type: "shell" do |s|
678         s.inline = GLUSTER_MOUNT_SCRIPT
679         s.args = [ "gv0", "/gluster/gv0" ]
680       end
681
682       node.vm.provision "gluster_createvol_1", type: "shell" do |s|
683         mount_points = cluster_internal_ips.map do |ip|
684           "#{ip}:/export/vdc1/brick"
685         end
686         s.inline = GLUSTER_CREATEVOL_SCRIPT
687         s.args = [ "gv1", "3" ] + mount_points
688       end
689
690       node.vm.provision "gluster_mount_1", type: "shell" do |s|
691         s.inline = GLUSTER_MOUNT_SCRIPT
692         s.args = [ "gv1", "/gluster/gv1" ]
693       end
694
695       #
696       # ctdb / samba config
697       #
698
699       node.vm.provision "ctdb_stop", type: "shell" do |s|
700         s.inline = CTDB_STOP_SCRIPT
701       end
702
703       node.vm.provision "ctdb_create_nodes", type: "shell" do |s|
704         s.inline = CTDB_CREATE_NODES_SCRIPT
705         s.args = cluster_internal_ips
706       end
707
708       #node.vm.provision "ctdb_create_pubaddrs", type: "shell" do |s|
709       #  s.inline = CTDB_CREATE_PUBADDRS_SCRIPT
710       #  s.arg =
711       #end
712
713       node.vm.provision "ctdb_create_conf", type: "shell" do |s|
714         s.inline = CTDB_CREATE_CONF_SCRIPT
715       end
716
717       node.vm.provision "samba_create_conf", type: "shell" do |s|
718         s.inline = SAMBA_CREATE_CONF_SCRIPT
719         s.args = [ "gv1", "/gluster/gv1" ]
720       end
721
722       node.vm.provision "ctdb_start", type: "shell" do |s|
723         s.inline = CTDB_START_SCRIPT
724       end
725
726     end
727   end
728
729 end