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