summaryrefslogtreecommitdiffstats
path: root/manifests/volume.pp
diff options
context:
space:
mode:
Diffstat (limited to 'manifests/volume.pp')
-rw-r--r--manifests/volume.pp193
1 files changed, 174 insertions, 19 deletions
diff --git a/manifests/volume.pp b/manifests/volume.pp
index 8c79dd8..5e8b7e1 100644
--- a/manifests/volume.pp
+++ b/manifests/volume.pp
@@ -16,12 +16,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
define gluster::volume(
- $bricks = [],
+ $bricks = true, # specify a list of bricks, or true for auto...
+ $group = 'default', # use this bricks group name if we auto collect
$transport = 'tcp',
$replica = 1,
$stripe = 1,
$vip = '', # vip of the cluster (optional but recommended)
$ping = true, # do we want to include fping checks ?
+ $settle = true, # do we want to run settle checks ?
$start = undef # start volume ? true, false (stop it) or undef
) {
include gluster::xml
@@ -34,6 +36,95 @@ define gluster::volume(
#$vardir = $::gluster::vardir::module_vardir # with trailing slash
$vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '')
+ $settle_count = 3 # three is a reasonable default!
+ $maxlength = 3
+ if $maxlength < $settle_count {
+ fail('The $maxlength needs to be greater than or equal to the $settle_count.')
+ }
+ $are_bricks_collected = (type($bricks) == 'boolean' and ($bricks == true))
+ # NOTE: settle checks are still useful even if we are using ping checks
+ # the reason why they are still useful, is that they can detect changes
+ # in the bricks, which might propagate slowly because of exported types
+ # the fping checks can only verify that the individual hosts are alive!
+ $settle_count_check = $are_bricks_collected ? {
+ false => false,
+ default => $settle ? {
+ false => false,
+ default => true,
+ }
+ }
+ # TODO: implement settle_time_check
+ $settle_time_check = $settle ? {
+ false => false,
+ default => true,
+ }
+
+ # clean up old fsm data when not in use, because parent $vardir purges!
+ if $are_bricks_collected {
+ include gluster::volume::fsm
+ file { "${vardir}/volume/fsm/${name}/":
+ ensure => directory, # make sure this is a directory
+ recurse => true, # recurse into directory
+ purge => false, # don't purge unmanaged files
+ force => false, # don't purge subdirs and links
+ require => File["${vardir}/volume/fsm/"],
+ }
+ }
+
+ $collected_bricks = split(getvar("gluster_brick_group_${group}"), ',')
+ $valid_bricks = type($bricks) ? {
+ 'boolean' => $bricks ? {
+ true => $collected_bricks, # an array...
+ default => [], # invalid type
+ },
+ 'list' => $bricks,
+ default => [], # invalid type
+ }
+
+ # helpful debugging!
+ notice(inline_template('valid_bricks: <%= valid_bricks.inspect %>'))
+
+ # NOTE: we're using the valid_bricks value here, and not the collected
+ # value. while we only need the $collected value for settle detection,
+ # the actual brick value is needed for future add/remove brick code...
+ $valid_input = join($valid_bricks, ',') # TODO: this should be pickled
+ if $are_bricks_collected and "${valid_input}" == '' {
+ notice('The gluster::brick collection is not ready yet.')
+ }
+ $last = getvar("gluster_volume_fsm_state_${name}") # fact !
+ $valid_last = "${last}" ? {
+ # initialize the $last var to match the $input if it's empty...
+ '' => "${valid_input}",
+ default => "${last}",
+ }
+ if $are_bricks_collected and ("${valid_input}" != '') and ("${valid_last}" == '') {
+ fail('Previous state is invalid.') # fact was tampered with
+ }
+
+ # NOTE: the stack lists are left base64 encoded since we only care if they change! :P
+ # NOTE: each element in stack is base64 encoded because they contain: ,
+ $stack_fact = getvar("gluster_volume_fsm_stack_${name}") # fact !
+ $stack_full = split("${stack_fact}", ',')
+ $stack_trim = "${maxlength}" ? {
+ '-1' => $stack_full, # unlimited
+ #default => split(inline_template('<%= stack_full[0,maxlength.to_i.abs].join(",") %>'), ','),
+ default => split(inline_template('<%= stack_full[[stack_full.size-maxlength.to_i.abs,0].max,maxlength.to_i.abs].join(",") %>'), ','),
+ }
+
+ $watch_fact = getvar("gluster_volume_fsm_watch_${name}") # fact !
+ $watch_full = split("${watch_fact}", ',')
+ $watch_trim = "${maxlength}" ? {
+ '-1' => $watch_full, # unlimited
+ #default => split(inline_template('<%= watch_full[0,maxlength.to_i.abs].join(",") %>'), ','),
+ default => split(inline_template('<%= watch_full[[watch_full.size-maxlength.to_i.abs,0].max,maxlength.to_i.abs].join(",") %>'), ','),
+ }
+
+ # if the last $settle_count elements are the same, the template
+ # should reduce down to the value '1'. check this and the size.
+ $one = inline_template('<%= watch_trim[[watch_trim.size-settle_count.to_i,0].max,settle_count.to_i].uniq.size %>')
+ $watch_trim_size = size($watch_trim)
+ $settled = ((! $settle_count_check) or ((size($watch_trim) >= $settle_count) and "${one}" == '1'))
+
# TODO: if using rdma, maybe we should pull in the rdma package... ?
$valid_transport = $transport ? {
'rdma' => 'rdma',
@@ -59,24 +150,24 @@ define gluster::volume(
# returns interface name that has vip, or '' if none are found.
$vipif = inline_template("<%= interfaces.split(',').find_all {|x| '${valid_vip}' == scope.lookupvar('ipaddress_'+x) }[0,1].join('') %>")
- #Gluster::Brick[$bricks] -> Gluster::Volume[$name] # volume requires bricks
+ #Gluster::Brick[$valid_bricks] -> Gluster::Volume[$name] # volume requires bricks
# get the bricks that match our fqdn, and append /$name to their path.
# return only these paths, which can be used to build the volume dirs.
# NOTE: gluster v3.4 won't create a volume if this dir already exists.
# TODO: is this needed when bricks are devices and not on filesystem ?
- #$volume_dirs = split(inline_template("<%= bricks.find_all{|x| x.split(':')[0] == '${fqdn}' }.collect {|y| y.split(':')[1].chomp('/')+'/${name}' }.join(' ') %>"), ' ')
+ #$volume_dirs = split(inline_template("<%= valid_bricks.find_all{|x| x.split(':')[0] == '${fqdn}' }.collect {|y| y.split(':')[1].chomp('/')+'/${name}' }.join(' ') %>"), ' ')
#file { $volume_dirs:
# 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
# before => Exec["gluster-volume-create-${name}"],
- # require => Gluster::Brick[$bricks],
+ # require => Gluster::Brick[$valid_bricks],
#}
# add /${name} to the end of each: brick:/path entry
- $brick_spec = inline_template("<%= bricks.collect {|x| ''+x.chomp('/')+'/${name}' }.join(' ') %>")
+ $brick_spec = inline_template("<%= valid_bricks.collect {|x| ''+x.chomp('/')+'/${name}' }.join(' ') %>")
# if volume creation fails for a stupid reason, in many cases, glusterd
# already did some of the work and left us with volume name directories
@@ -85,10 +176,10 @@ define gluster::volume(
# we error we should rmdir any empty volume dirs to keep it pristine...
# TODO: this should be a gluster bug... we must hope it doesn't happen!
# maybe related to: https://bugzilla.redhat.com/show_bug.cgi?id=835494
- $rmdir_volume_dirs = sprintf("/bin/rmdir '%s'", inline_template("<%= bricks.find_all{|x| x.split(':')[0] == '${fqdn}' }.collect {|y| y.split(':')[1].chomp('/')+'/${name}/' }.join('\' \'') %>"))
+ $rmdir_volume_dirs = sprintf("/bin/rmdir '%s'", inline_template("<%= valid_bricks.find_all{|x| x.split(':')[0] == '${fqdn}' }.collect {|y| y.split(':')[1].chomp('/')+'/${name}/' }.join('\' \'') %>"))
# get the list of bricks fqdn's that don't have our fqdn
- $others = inline_template("<%= bricks.find_all{|x| x.split(':')[0] != '${fqdn}' }.collect {|y| y.split(':')[0] }.join(' ') %>")
+ $others = inline_template("<%= valid_bricks.find_all{|x| x.split(':')[0] != '${fqdn}' }.collect {|y| y.split(':')[0] }.join(' ') %>")
$fping = sprintf("/usr/sbin/fping -q %s", $others)
$status = sprintf("/usr/sbin/gluster peer status --xml | ${vardir}/xml.py connected %s", $others)
@@ -106,14 +197,14 @@ define gluster::volume(
Service['glusterd'],
File["${vardir}/volume/create-${name}.sh"],
File["${vardir}/xml.py"], # status check
- Gluster::Brick[$bricks],
+ Gluster::Brick[$valid_bricks],
],
default => [
Service['glusterd'],
File["${vardir}/volume/create-${name}.sh"],
Package['fping'],
File["${vardir}/xml.py"], # status check
- Gluster::Brick[$bricks],
+ Gluster::Brick[$valid_bricks],
],
}
@@ -145,14 +236,17 @@ define gluster::volume(
# NOTE: in this command, we save the std{out,err} and pass them
# on too for puppet to consume. we save in /tmp for fast access
# EXAMPLE: gluster volume create test replica 2 transport tcp annex1.example.com:/storage1a/test annex2.example.com:/storage2a/test annex3.example.com:/storage3b/test annex4.example.com:/storage4b/test annex1.example.com:/storage1c/test annex2.example.com:/storage2c/test annex3.example.com:/storage3d/test annex4.example.com:/storage4d/test
- exec { "gluster-volume-create-${name}":
- command => "${vardir}/volume/create-${name}.sh",
- logoutput => on_failure,
- unless => "/usr/sbin/gluster volume list | /bin/grep -qxF '${name}' -", # add volume if it doesn't exist
- onlyif => $onlyif,
- #before => TODO?,
- require => $require,
- alias => "gluster-volume-create-${name}",
+ # TODO: add a timer similar to my puppet-runonce timer code... to wait X minutes after we've settled... useful if we Exec['again'] to speed things up...
+ if $settled {
+ exec { "gluster-volume-create-${name}":
+ command => "${vardir}/volume/create-${name}.sh",
+ logoutput => on_failure,
+ unless => "/usr/sbin/gluster volume list | /bin/grep -qxF '${name}' -", # add volume if it doesn't exist
+ onlyif => $onlyif,
+ #before => TODO?,
+ require => $require,
+ alias => "gluster-volume-create-${name}",
+ }
}
}
@@ -164,7 +258,10 @@ define gluster::volume(
logoutput => on_failure,
onlyif => "/usr/sbin/gluster volume list | /bin/grep -qxF '${name}' -",
unless => "/usr/sbin/gluster volume status ${name}", # returns false if stopped
- require => Exec["gluster-volume-create-${name}"],
+ require => $settled ? { # require if type exists
+ false => undef,
+ default => Exec["gluster-volume-create-${name}"],
+ },
alias => "gluster-volume-start-${name}",
}
} elsif ( $start == false ) {
@@ -177,7 +274,10 @@ define gluster::volume(
exec { "/usr/bin/yes | /usr/sbin/gluster volume stop ${name}":
logoutput => on_failure,
onlyif => "/usr/sbin/gluster volume status ${name}", # returns true if started
- require => Exec["gluster-volume-create-${name}"],
+ require => $settled ? { # require if type exists
+ false => undef,
+ default => Exec["gluster-volume-create-${name}"],
+ },
alias => "gluster-volume-stop-${name}",
}
} else { # 'undef'-ined
@@ -218,6 +318,61 @@ define gluster::volume(
source => "${zone}", # use our source zone
before => Service['glusterd'],
}
+
+ # fsm variables and boilerplate
+ $statefile = "${vardir}/volume/fsm/${name}/state"
+ $stackfile = "${vardir}/volume/fsm/${name}/stack"
+ $watchfile = "${vardir}/volume/fsm/${name}/watch"
+ $diff = "/usr/bin/test '${valid_input}' != '${valid_last}'"
+ $stack_truncate = "${maxlength}" ? {
+ '-1' => '', # unlimited
+ #default => sprintf("&& /bin/sed -i '%d,$ d' ${stackfile}", inline_template('<%= maxlength.to_i.abs+1 %>')),
+ default => sprintf(" && (/bin/grep -v '^$' ${stackfile} | /usr/bin/tail -%d | /usr/bin/tee ${stackfile})", inline_template('<%= maxlength.to_i.abs %>')),
+ }
+ $watch_truncate = "${maxlength}" ? {
+ '-1' => '', # unlimited
+ #default => sprintf("&& /bin/sed -i '%d,$ d' ${watchfile}", inline_template('<%= maxlength.to_i.abs+1 %>')),
+ default => sprintf(" && (/bin/grep -v '^$' ${watchfile} | /usr/bin/tail -%d | /usr/bin/tee ${watchfile})", inline_template('<%= maxlength.to_i.abs %>')),
+ }
+
+ if $are_bricks_collected and ("${valid_input}" != '') { # ready or not?
+
+ # TODO: future versions should pickle (but with yaml)
+ exec { "/bin/echo '${valid_input}' > '${statefile}'":
+ logoutput => on_failure,
+ onlyif => "/usr/bin/test ! -e '${statefile}' || ${diff}",
+ require => File["${vardir}/volume/fsm/${name}/"],
+ alias => "gluster-volume-fsm-state-${name}",
+ }
+
+ # NOTE: keep a stack of past transitions, and load them in as a list...
+ exec { "/bin/echo '${valid_input}' >> '${stackfile}'${stack_truncate}":
+ logoutput => on_failure,
+ onlyif => "/usr/bin/test ! -e '${stackfile}' || ${diff}",
+ require => [
+ File["${vardir}/volume/fsm/${name}/"],
+ # easy way to ensure the transition types don't need to
+ # add a before to both exec's since this one follows it
+ Exec["gluster-volume-fsm-state-${name}"],
+ ],
+ alias => "gluster-volume-fsm-stack-${name}",
+ }
+
+ # NOTE: watch *all* transitions, and load them in as a list...
+ exec { "/bin/echo '${valid_input}' >> '${watchfile}'${watch_truncate}":
+ logoutput => on_failure,
+ # we run this if the file doesn't exist, or there is a
+ # difference to record, or the sequence hasn't settled
+ # we also check that we have our minimum settle count!
+ onlyif => "/usr/bin/test ! -e '${watchfile}' || ${diff} || /usr/bin/test '1' != '${one}' || /usr/bin/test ${watch_trim_size} -lt ${settle_count}",
+ require => [
+ File["${vardir}/volume/fsm/${name}/"],
+ # easy way to ensure the transition types don't need to
+ # add a before to both exec's since this one follows it
+ Exec["gluster-volume-fsm-state-${name}"],
+ ],
+ alias => "gluster-volume-fsm-watch-${name}",
+ }
}
}