From c7a6ef2c0267540ab2b272872648476255557dc6 Mon Sep 17 00:00:00 2001 From: James Shubin Date: Thu, 13 Mar 2014 11:45:26 -0400 Subject: Add automatic generation of fs UUID's per brick filesystem. This automatically generates UUID's for each physical filesystem, or alternatively, you can specify one manually with the $fsuuid argument. This will make a _big_ difference when using gluster::simple to automatically deploy a large cluster of physical machines, since you don't have to manually generate one uuid per device (which is time consuming and could be a lot to do and a lot to maintain). --- lib/facter/gluster_fsuuid.rb | 144 +++++++++++++++++++++++++++++++++++++++++++ manifests/brick.pp | 91 +++++++++++++++++++++------ manifests/brick/base.pp | 9 +++ 3 files changed, 224 insertions(+), 20 deletions(-) create mode 100644 lib/facter/gluster_fsuuid.rb diff --git a/lib/facter/gluster_fsuuid.rb b/lib/facter/gluster_fsuuid.rb new file mode 100644 index 0000000..4ed48dc --- /dev/null +++ b/lib/facter/gluster_fsuuid.rb @@ -0,0 +1,144 @@ +# GlusterFS module by James +# Copyright (C) 2010-2013+ James Shubin +# Written by James Shubin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +require 'facter' +require 'digest/sha1' + +# TODO: the ruby uuid method can be used when newer ruby versions are used here +# require 'securerandom' +# SecureRandom.uuid + +# uuid regexp +regexp = /^[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}$/ +fqdn = Facter.value('fqdn') # this could be nil ! + +# find the module_vardir +dir = Facter.value('puppet_vardirtmp') # nil if missing +if dir.nil? # let puppet decide if present! + dir = Facter.value('puppet_vardir') + if dir.nil? + var = nil + else + var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash + end +else + var = dir.gsub(/\/$/, '')+'/' +end + +if var.nil? + # if we can't get a valid vardirtmp, then we can't continue + module_vardir = nil + valid_brickdir = nil + uuiddir = nil +else + module_vardir = var+'gluster/' + valid_brickdir = module_vardir.gsub(/\/$/, '')+'/brick/' + uuiddir = valid_brickdir+'fsuuid/' # safe dir that won't get purged... +end + +# NOTE: module specific mkdirs, needed to ensure there is no blocking/deadlock! +if not(var.nil?) and not File.directory?(var) + Dir::mkdir(var) +end + +if not(module_vardir.nil?) and not File.directory?(module_vardir) + Dir::mkdir(module_vardir) +end + +found = {} + +# generate uuid and parent directory if they don't already exist... +if not(module_vardir.nil?) and File.directory?(module_vardir) + if not File.directory?(uuiddir) + Dir::mkdir(uuiddir) + end + + # loop through brick dir, looking for brick names to make fsuuid's for! + if not(valid_brickdir.nil?) and File.directory?(valid_brickdir) and File.directory?(uuiddir) + Dir.glob(valid_brickdir+'*.*').each do |f| + b = File.basename(f) + g = b.split('.') # $name.group + + group = g.pop() # pop off suffix (the group name) + + if g.length >= 1 + # NOTE: some of this code is unnecessary, but i + # kept it because it matches the brick parsing. + + x = g.join('.') # in case value had dots in it. + + brick = File.open(f, 'r').read.strip # read into str + # eg: annex1.example.com:/storage1a + split = brick.split(':') # do some $name parsing + host = split[0] # host fqdn + # NOTE: technically $path should be everything BUT split[0]. This + # lets our $path include colons if for some reason they're needed. + #path = split[1] # brick mount or storage path + path = brick.slice(host.length+1, brick.length-host.length-1) + + # if fqdn is nil, generate for everyone... + # (other hosts data will just be unused...) + if not(fqdn.nil?) + # otherwise, skip hosts that aren't us! + if host != fqdn + next + end + end + + uuidfile = uuiddir + b + # we sha1 to prevent weird characters in facter + key = Digest::SHA1.hexdigest(host + ':' + path + '.' + group) + + # create an fsuuid for each brick and store it + # in our vardir if it doesn't already exist... + if not File.exist?(uuidfile) + result = system("/usr/bin/uuidgen > '" + uuidfile + "'") + if not(result) + # TODO: print warning + end + end + + # create facts from all the uuid files found... + uuid = File.open(uuidfile, 'r').read.strip.downcase # read into str + if uuid.length == 36 and regexp.match(uuid) + # avoid: http://projects.puppetlabs.com/issues/22455 + found[key] = uuid + # TODO: print warning on else... + end + end + end + end +end + +found.keys.each do |x| + Facter.add('gluster_brick_fsuuid_'+x) do + #confine :operatingsystem => %w{CentOS, RedHat, Fedora} + setcode { + found[x] + } + end +end + +# list of generated gluster_brick_fsuuid's +Facter.add('gluster_brick_fsuuid_facts') do + #confine :operatingsystem => %w{CentOS, RedHat, Fedora} + setcode { + found.keys.collect {|x| 'gluster_brick_fsuuid_'+x }.join(',') + } +end + +# vim: ts=8 diff --git a/manifests/brick.pp b/manifests/brick.pp index d753d5f..a44ce2e 100644 --- a/manifests/brick.pp +++ b/manifests/brick.pp @@ -39,6 +39,7 @@ define gluster::brick( $comment = '' ) { include gluster::brick::base + include gluster::again include gluster::vardir #$vardir = $::gluster::vardir::module_vardir # with trailing slash @@ -72,6 +73,42 @@ define gluster::brick( require => File["${vardir}/brick/"], } + # + # fsuuid... + # + if ("${fsuuid}" != '') and "${fsuuid}" =~ /^[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}$/ { + fail("The chosen fs uuid: '${fsuuid}' is not valid.") + } + + # if we manually *pick* a uuid, then store it too, so that it + # sticks if we ever go back to using automatic uuids. this is + # useful if a user wants to initially import uuids by picking + # them manually, and then letting puppet take over afterwards + if "${fsuuid}" != '' { + # $group is unnecessary, but i left it in for consistency... + file { "${vardir}/brick/fsuuid/${safename}.${group}": + content => "${fsuuid}\n", + owner => root, + group => root, + mode => 600, # might as well... + ensure => present, + require => File["${vardir}/brick/fsuuid/"], + } + } + + # we sha1 to prevent weird characters in facter + $fsuuid_safename = sha1("${name}.${group}") + $valid_fsuuid = "${fsuuid}" ? { + # fact from the data generated in: ${vardir}/brick/fsuuid/* + '' => getvar("gluster_brick_fsuuid_${fsuuid_safename}"), # fact! + default => "${fsuuid}", + } + + # you might see this on first run if the fsuuid isn't generated yet + if (type($dev) != 'boolean') and ("${valid_fsuuid}" == '') { + warning('An $fsuuid must be specified or generated.') + } + # # raid... # @@ -218,7 +255,7 @@ define gluster::brick( $xfs_cmd = join(delete($xfs_cmdlist, ''), ' ') # TODO: xfs_admin doesn't have a --quiet flag. silence it... - $xfs_admin = "/usr/sbin/xfs_admin -U '${fsuuid}' ${dev2}" + $xfs_admin = "/usr/sbin/xfs_admin -U '${valid_fsuuid}' ${dev2}" # mkfs w/ uuid command $mkfs_exec = "${xfs_cmd} && ${xfs_admin}" @@ -257,7 +294,7 @@ define gluster::brick( $exec_requires = [Package['e2fsprogs']] # mkfs w/ uuid command - $mkfs_exec = "/sbin/mkfs.${valid_fstype} -U '${fsuuid}' ${dev2}" + $mkfs_exec = "/sbin/mkfs.${valid_fstype} -U '${valid_fsuuid}' ${dev2}" # mount options $options_list = [] # TODO @@ -340,7 +377,8 @@ define gluster::brick( exec { "${lvm_lvcreate}": logoutput => on_failure, unless => [ # if one element is true, this *doesn't* run - "/sbin/lvdisplay ${lvm_lvname}", + #"/sbin/lvdisplay ${lvm_lvname}", # nope! + "/sbin/lvs --separator ':' | /usr/bin/tr -d ' ' | /bin/awk -F ':' '{print \$1}' | /bin/grep -q '${lvm_lvname}'", '/bin/false', # TODO: add more criteria ], require => $exec_requires, @@ -355,13 +393,24 @@ define gluster::brick( notify { "noop for mkfs: ${name}": message => "${mkfs_exec}", } + } else { + # if valid_fsuuid isn't ready, trigger an exec again... + exec { "gluster-brick-fsuuid-execagain-${name}": + command => '/bin/true', # do nothing but notify + logoutput => on_failure, + onlyif => "/usr/bin/test -z '${valid_fsuuid}'", + notify => Common::Again::Delta['gluster-exec-again'], + # this (optional) require makes it more logical + require => File["${vardir}/brick/fsuuid/"], + } } # mkfs... exec { "${mkfs_exec}": logoutput => on_failure, + onlyif => "/usr/bin/test -n '${valid_fsuuid}'", unless => [ # if one element is true, this *doesn't* run - "/usr/bin/test -e /dev/disk/by-uuid/${fsuuid}", + "/usr/bin/test -e /dev/disk/by-uuid/${valid_fsuuid}", '/bin/false', # TODO: add more criteria ], require => $exec_requires, @@ -380,22 +429,24 @@ define gluster::brick( } # mount points don't seem to like trailing slashes... - mount { "${short_path}": - atboot => true, - ensure => mounted, - device => "UUID=${fsuuid}", - fstype => "${valid_fstype}", - # noatime,nodiratime to save gluster from silly updates - options => "${mount_options},${ro_bool},noatime,nodiratime,noexec", # TODO: is nodev? nosuid? noexec? a good idea? - dump => '0', # fs_freq: 0 to skip file system dumps - # NOTE: technically this should be '2', to `fsck.xfs` - # after the rootfs ('1'), but fsck.xfs actually does - # 'nothing, successfully', so it's irrelevant, because - # xfs uses xfs_check and friends only when suspect. - pass => '2', # fs_passno: 0 to skip fsck on boot - require => [ - File["${valid_path}"], - ], + if "${valid_fsuuid}" != '' { # in case fsuuid isn't ready yet + mount { "${short_path}": + atboot => true, + ensure => mounted, + device => "UUID=${valid_fsuuid}", + fstype => "${valid_fstype}", + # noatime,nodiratime to save gluster from silly updates + options => "${mount_options},${ro_bool},noatime,nodiratime,noexec", # TODO: is nodev? nosuid? noexec? a good idea? + dump => '0', # fs_freq: 0 to skip file system dumps + # NOTE: technically this should be '2', to `fsck.xfs` + # after the rootfs ('1'), but fsck.xfs actually does + # 'nothing, successfully', so it's irrelevant, because + # xfs uses xfs_check and friends only when suspect. + pass => '2', # fs_passno: 0 to skip fsck on boot + require => [ + File["${valid_path}"], + ], + } } } elsif ((type($dev) == 'boolean') and (! $dev)) and ("${fqdn}" == "${host}") { diff --git a/manifests/brick/base.pp b/manifests/brick/base.pp index 83398e2..c39cb3f 100644 --- a/manifests/brick/base.pp +++ b/manifests/brick/base.pp @@ -28,5 +28,14 @@ class gluster::brick::base { force => true, # don't purge subdirs and links require => File["${vardir}/"], } + + # don't purge the fsuuid file's generated within + file { "${vardir}/brick/fsuuid/": + ensure => directory, # make sure this is a directory + recurse => false, # don't recurse into directory + purge => false, # don't purge unmanaged files + force => false, # don't purge subdirs and links + require => File["${vardir}/brick/"], + } } # vim: ts=8 -- cgit