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