summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Shubin <james@shubin.ca>2014-03-08 01:06:22 -0500
committerJames Shubin <james@shubin.ca>2014-03-16 22:39:07 -0400
commitdd44a559d820c19f6df1cc66019fa8fb7cecb2c1 (patch)
tree3b04dfb85e78f6b79d826e05ff2cef8651b7b279
parent2dd6acd1eeffbfdcf0436847ee115310198526f4 (diff)
Add support for volume set groups.
This adds support for setting volume set groups which are groups of properties that are set all at once on a volume. This is managed in a clever way, so that if the definition of what a certain group contains gets updated by the package manager, your volumes will get updated too, on the next puppet run.
-rw-r--r--examples/gluster-simple-example.pp1
-rw-r--r--files/xml.py9
-rw-r--r--lib/facter/gluster_property.rb69
-rw-r--r--manifests/simple.pp2
-rw-r--r--manifests/volume.pp7
-rw-r--r--manifests/volume/property.pp24
-rw-r--r--manifests/volume/property/data.pp7
-rw-r--r--manifests/volume/property/group.pp60
8 files changed, 175 insertions, 4 deletions
diff --git a/examples/gluster-simple-example.pp b/examples/gluster-simple-example.pp
index a050343..02e59f3 100644
--- a/examples/gluster-simple-example.pp
+++ b/examples/gluster-simple-example.pp
@@ -10,6 +10,7 @@ node /^annex\d+$/ { # annex{1,2,..N}
# NOTE: this is mostly intended for fast gluster testing. for more
# complex setups, you might want to look at the other examples.
class { '::gluster::simple':
+ setgroup => 'virt', # or: 'small-file-perf', or others too!
}
}
diff --git a/files/xml.py b/files/xml.py
index 27f39df..0650127 100644
--- a/files/xml.py
+++ b/files/xml.py
@@ -168,7 +168,14 @@ elif args.mode == 'stuck':
elif args.mode == 'property':
store = []
for i in root.findall('.//option'):
- if str(i.find('name').text) == args.key:
+
+ key = str(i.find('name').text)
+ # if the key requested has no '.' in the name, we match loosely
+ if args.key.find('.') == -1: # no '.' have been found
+ # try and match key without a '.' in the name...
+ key = key[key.find('.')+1:] # remove prefix!
+
+ if key == args.key:
store.append(i.find('value').text)
if len(store) == 1:
diff --git a/lib/facter/gluster_property.rb b/lib/facter/gluster_property.rb
new file mode 100644
index 0000000..4c47dd4
--- /dev/null
+++ b/lib/facter/gluster_property.rb
@@ -0,0 +1,69 @@
+# GlusterFS module by James
+# Copyright (C) 2010-2013+ James Shubin
+# Written by James Shubin <james@shubin.ca>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+require 'facter'
+
+groupdir = '/var/lib/glusterd/groups/'
+
+found = {}
+
+if File.directory?(groupdir)
+ Dir.glob(groupdir+'*.*').each do |f|
+ b = File.basename(f)
+
+ if not found.key?(b)
+ found[b] = {} # initialize
+ end
+
+ groups = File.open(f, 'r').read # read into str
+ groups.each_line do |line|
+ split = line.split('=') # split key=value pairs
+ if split.length == 2
+ key = split[0]
+ value = split[1]
+ if found[b].key?(key)
+ # NOTE: error found in file...
+ print "There is a duplicate key in the '#{b}' group."
+ end
+ found[b][key] = value
+ end
+ end
+ end
+end
+
+# list of available property groups
+Facter.add('gluster_property_groups') do
+ #confine :operatingsystem => %w{CentOS, RedHat, Fedora}
+ setcode {
+ found.keys.sort.join(',')
+ }
+end
+
+# each group's list of key value pairs
+found.keys.each do |x|
+ Facter.add('gluster_property_group_'+x) do
+ #confine :operatingsystem => %w{CentOS, RedHat, Fedora}
+ setcode {
+ # don't reuse single variable to avoid bug #:
+ # http://projects.puppetlabs.com/issues/22455
+ # TODO: facter should support native hash types :)
+ found[x].collect{|k,v| k+'='+v}.join(',')
+ }
+ end
+end
+
+# vim: ts=8
diff --git a/manifests/simple.pp b/manifests/simple.pp
index 06cbc52..60e6ae4 100644
--- a/manifests/simple.pp
+++ b/manifests/simple.pp
@@ -43,6 +43,7 @@ class gluster::simple(
# xfs_inode64 => true,
# force => true,
# }
+ $setgroup = '', # pick a volume property group to set, eg: virt
$ping = true, # use fping or not?
$baseport = '', # specify base port option as used in glusterd.vol file
$rpcauthallowinsecure = false, # needed in some setups in glusterd.vol
@@ -159,6 +160,7 @@ class gluster::simple(
# the only semi-safe way is the new built in automatic collect:
bricks => true, # automatic brick collection...
ping => $ping,
+ setgroup => $setgroup,
start => true,
}
Gluster::Volume <<||>>
diff --git a/manifests/volume.pp b/manifests/volume.pp
index 645c090..f946a54 100644
--- a/manifests/volume.pp
+++ b/manifests/volume.pp
@@ -24,6 +24,7 @@ define gluster::volume(
$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 ?
+ $setgroup = '', # pick a volume property group to set, eg: virt
$start = undef # start volume ? true, false (stop it) or undef
) {
include gluster::xml
@@ -411,6 +412,12 @@ define gluster::volume(
alias => "gluster-volume-fsm-watch-${name}",
}
}
+
+ # set a group of volume properties
+ if "${setgroup}" != '' {
+ gluster::volume::property::group { "${name}#${setgroup}":
+ }
+ }
}
# vim: ts=8
diff --git a/manifests/volume/property.pp b/manifests/volume/property.pp
index d7650f1..a7f6218 100644
--- a/manifests/volume/property.pp
+++ b/manifests/volume/property.pp
@@ -40,12 +40,31 @@ define gluster::volume::property(
$etypes = $::gluster::volume::property::data::etypes
$jchars = $::gluster::volume::property::data::jchars
+ # transform our etypes into short etypes (missing the prefix)
+ # TODO: we should see if there are duplicates (collisions)
+ # if there are collisions, and a gluster volume set group type contains
+ # one of these keys, then it's ambiguous and it's clearly a gluster bug
+ $short_etypes_yaml = inline_template('<%= @etypes.inject({}) {|h, (x,y)| h[ (x.index(".").nil?? x : x[x.index(".")+1..-1]) ] = y; h }.to_yaml %>')
+ $short_jchars_yaml = inline_template('<%= @jchars.inject({}) {|h, (x,y)| h[ (x.index(".").nil?? x : x[x.index(".")+1..-1]) ] = y; h }.to_yaml %>')
+ $short_etypes = parseyaml($short_etypes_yaml)
+ $short_jchars = parseyaml($short_jchars_yaml)
+
+ # FIXME: a short key should lookup the equivalent in the normal table,
+ # and vice-versa, and set an alias so that you can't define a short
+ # key and a long key at the same time which refer to the same variable!
+
# expected type
if has_key($etypes, "${key}") {
$etype = $etypes["${key}"] ? {
'' => 'undefined',
default => $etypes["${key}"],
}
+ # the keys of these etypes are missing their prefix up to the first '.'
+ } elsif has_key($short_etypes, "${key}") {
+ $etype = $short_etypes["${key}"] ? {
+ '' => 'undefined',
+ default => $short_etypes["${key}"],
+ }
} else {
$etype = 'undefined'
}
@@ -102,6 +121,9 @@ define gluster::volume::property(
fail("Gluster::Volume::Property[${key}] must be type: ${etype}.")
}
+ } elsif $etype == 'string' {
+ $safe_value = shellquote($value) # TODO: is this the safe thing?
+
# if it's not a string and it's not the expected type, fail
} elsif ( type($value) != $etype ) { # type() from puppetlabs-stdlib
fail("Gluster::Volume::Property[${key}] must be type: ${etype}.")
@@ -116,6 +138,8 @@ define gluster::volume::property(
# join char
if has_key($jchars, "${key}") {
$jchar = $jchars["${key}"]
+ } elsif has_key($short_jchars, "${key}") {
+ $jchar = $short_jchars["${key}"]
} else {
$jchar = ''
}
diff --git a/manifests/volume/property/data.pp b/manifests/volume/property/data.pp
index 0ebd2c9..e876b5d 100644
--- a/manifests/volume/property/data.pp
+++ b/manifests/volume/property/data.pp
@@ -20,6 +20,7 @@ class gluster::volume::property::data() {
# expected type
$etypes = {
# FIXME: the empty '' strings need to be filled in...
+ # FIXME: some of the 'string' values could probably be booleans
# Allow a comma separated list of addresses and/or hostnames to connect to the server. By default, all connections are allowed.
'auth.allow' => 'array', # default: (null)
@@ -73,7 +74,7 @@ class gluster::volume::property::data() {
'cluster.quorum-count' => '', # default: (null)
# If value is "fixed" only allow writes if quorum-count bricks are present. If value is "auto" only allow writes if more than half of bricks, or exactly half including the first, are present.
- 'cluster.quorum-type' => '', # default: none
+ 'cluster.quorum-type' => 'string', # default: none
# readdir(p) will not failover if this option is off
'cluster.readdir-failover' => 'onoff', # default: on
@@ -106,7 +107,7 @@ class gluster::volume::property::data() {
'cluster.server-quorum-ratio' => '', # default: (null)
# If set to server, enables the specified volume to participate in quorum.
- 'cluster.server-quorum-type' => '', # default: (null)
+ 'cluster.server-quorum-type' => 'string', # default: (null)
# Size of the stripe unit that would be read from or written to the striped servers.
'cluster.stripe-block-size' => '', # default: 128KB
@@ -154,7 +155,7 @@ class gluster::volume::property::data() {
'network.ping-timeout' => 'integer', # default: 42
# If enabled, in open() and creat() calls, O_DIRECT flag will be filtered at the client protocol level so server will still continue to cache the file. This works similar to NFS's behavior of O_DIRECT
- 'network.remote-dio' => '', # default: disable
+ 'network.remote-dio' => 'string', # default: disable
# XXX: this appears twice
# Specifies the window size for tcp socket.
diff --git a/manifests/volume/property/group.pp b/manifests/volume/property/group.pp
new file mode 100644
index 0000000..8901553
--- /dev/null
+++ b/manifests/volume/property/group.pp
@@ -0,0 +1,60 @@
+# GlusterFS module by James
+# Copyright (C) 2012-2013+ James Shubin
+# Written by James Shubin <james@shubin.ca>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+# NOTE: The most well known group is 'virt', and is a collection of properties.
+# NOTE: This type is particularly useful, because if you've set a certain group
+# for your volume, and your package updates the group properties, then this can
+# notice those changes and keep your volume in sync with the latest properties!
+# NOTE: This intentionally conflicts with properties that are defined manually.
+# NOTE: this does the equivalent of: gluster volume set <VOLNAME> group <GROUP>
+
+define gluster::volume::property::group(
+) {
+ include gluster::xml
+ include gluster::vardir
+ include gluster::volume::property
+
+ #$vardir = $::gluster::vardir::module_vardir # with trailing slash
+ $vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '')
+
+ $split = split($name, '#') # do some $name parsing
+ $volume = $split[0] # volume name
+ $group = $split[1] # group name
+
+ if ! ( "${volume}#${group}" == "${name}" ) {
+ fail('The property $name must match a $volume#$group pattern.')
+ }
+
+ $groups = split($gluster_property_groups, ',') # fact
+
+ if ! ("${group}" in $groups) {
+ fail("The group named '${group}' is not available.")
+ }
+
+ # read the fact that comes from the data in: /var/lib/glusterd/groups/*
+ $group_data_string = getvar("gluster_property_group_${name}") # fact!
+ # each element in this list is a key=value string
+ $group_data_list = split("${group_data_string}", ',')
+ # split into the correct hash to create all the properties
+ $group_data_yaml = inline_template("<%= @group_data_list.inject(Hash.new) { |h,i| { '${volume}#'+((i.split('=').length == 2) ? i.split('=')[0] : '') => {'value' => ((i.split('=').length == 2) ? i.split('=')[1] : '')} }.merge(h) }.to_yaml %>")
+ # build into a hash
+ $group_data_hash = parseyaml($group_data_yaml)
+ # create the properties
+ create_resources('gluster::volume::property', $group_data_hash)
+}
+
+# vim: ts=8