summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorJames Shubin <james@shubin.ca>2013-10-10 19:35:20 -0400
committerJames Shubin <james@shubin.ca>2013-10-10 22:54:33 -0400
commitdb0e06f48d09f94f92fb78087a717b79c9da6eac (patch)
treeb6045e23cf1c3615d20ed07cc25186d1c094cf80 /lib
parentb9153d8859b11d9672ba285dc0f281b0bdf78119 (diff)
downloadpuppet-gluster-db0e06f48d09f94f92fb78087a717b79c9da6eac.tar.gz
puppet-gluster-db0e06f48d09f94f92fb78087a717b79c9da6eac.tar.xz
puppet-gluster-db0e06f48d09f94f92fb78087a717b79c9da6eac.zip
Automatic brick ordering and finite state machine support.
This patch adds preliminary FSM support. This will be used and abused more extensively in later patches. Automatic brick ordering is an advanced feature and is meant for experienced puppet users. Changing the available bricks before the cluster is built is not currently supported. For that type of magic, please wait for gluster::elastic. This feature expects that you name your bricks and hosts intelligently. Future patches will recommend a specific nomenclature, but for now as long as the brick paths and hostnames follow a padded, incrementing integer pattern, with a common prefix, you shouldn't see any problems.
Diffstat (limited to 'lib')
-rw-r--r--lib/facter/gluster_bricks.rb148
-rw-r--r--lib/facter/gluster_fsm.rb162
2 files changed, 310 insertions, 0 deletions
diff --git a/lib/facter/gluster_bricks.rb b/lib/facter/gluster_bricks.rb
new file mode 100644
index 0000000..b8852bc
--- /dev/null
+++ b/lib/facter/gluster_bricks.rb
@@ -0,0 +1,148 @@
+# 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'
+
+# regexp to match a brick pattern, eg: annex1.example.com:/storage1a
+regexp = /^[a-zA-Z]{1}[a-zA-Z0-9\.\-]{0,}:\/[a-zA-Z0-9]{1}[a-zA-Z0-9\/\.\-]{0,}$/ # TODO: is this right ?
+
+# 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
+ uuidfile = nil
+else
+ module_vardir = var+'gluster/'
+ valid_brickdir = module_vardir.gsub(/\/$/, '')+'/brick/'
+end
+
+found = {}
+result = {}
+
+if not(valid_brickdir.nil?) and File.directory?(valid_brickdir)
+ 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 not found.key?(group)
+ found[group] = [] # initialize
+ end
+
+ if g.length >= 1
+ 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 brick.length > 0 and regexp.match(brick)
+ found[group].push({'host' => host, 'path' => path})
+ # TODO: print warning on else...
+ end
+ end
+ end
+end
+
+# sort the bricks in a logical manner... i think this is the optimal algorithm,
+# but i'd be happy if someone thinks they can do better! this assumes that the
+# bricks and hosts are named in a logical manner. alphanumeric sorting is used
+# to determine the default ordering...
+# TODO: maybe this should be a puppet function instead of a fact... that way,
+# if necessary, the function could also include the replica count, and other
+# data as input... is it even needed ?
+
+found.keys.each do |group|
+
+ collect = {}
+ found[group].each do |x|
+ key = x['host']
+ val = x['path']
+
+ if not collect.has_key?(key)
+ collect[key] = [] # initialize
+ end
+
+ collect[key].push(val) # save in array
+ # TODO: ensure this array is always sorted (we could also do this after
+ # or always insert elements in the correct sorted order too :P)
+ collect[key] = collect[key].sort
+ end
+
+ # we also could do this sort here...
+ collect.keys.each do |x|
+ collect[x] = collect[x].sort
+ end
+
+ final = [] # final order...
+ # TODO: here we can probably detect if this is an asymmetrical configurations, or maybe bad naming...
+ while collect.size > 0
+ collect.keys.sort.each do |x|
+
+ # NOTE: this array should already be sorted!
+ p = collect[x].shift # assume an array of at least 1 element
+ final.push( { 'host' => x, 'path' => p } ) # save
+
+ if collect[x].size == 0 # maybe the array is empty now
+ collect.delete(x) # remove that empty list's key
+ end
+
+ end
+ end
+
+ # build final result
+ result[group] = final.collect {|x| x['host']+':'+x['path'] }
+end
+
+# build the correctly sorted brick list...
+result.keys.each do |x|
+ Facter.add('gluster_brick_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 list types :)
+ result[x].join(',')
+ }
+ end
+end
+
+# list of generated gluster_ports_volume's
+Facter.add('gluster_brick_group_facts') do
+ #confine :operatingsystem => %w{CentOS, RedHat, Fedora}
+ setcode {
+ result.keys.collect {|x| 'gluster_brick_group_'+x }.join(',')
+ }
+end
+
+# vim: ts=8
diff --git a/lib/facter/gluster_fsm.rb b/lib/facter/gluster_fsm.rb
new file mode 100644
index 0000000..887e869
--- /dev/null
+++ b/lib/facter/gluster_fsm.rb
@@ -0,0 +1,162 @@
+# 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'
+require 'base64' # TODO: i wish facter worked with types instead of hacks
+
+# regexp to match gluster volume name, eg: testvolume
+volume_regexp = /^[a-z]{1}[a-z0-9]{0,}$/ # TODO: is this perfect ?
+
+# returns true if each brick in the list matches
+def brick_match(l)
+ # regexp to match a brick pattern, eg: annex1.example.com:/storage1a
+ brick_regexp = /^[a-zA-Z]{1}[a-zA-Z0-9\.\-]{0,}:\/[a-zA-Z0-9]{1}[a-zA-Z0-9\/\.\-]{0,}$/ # TODO: is this perfect ?
+ l.each do |x|
+ if not brick_regexp.match(x)
+ return false
+ end
+ end
+ return true
+end
+
+# 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 collect...
+ valid_dir = nil
+else
+ module_vardir = var+'gluster/'
+ valid_dir = module_vardir.gsub(/\/$/, '')+'/'
+ fsm_dir = valid_dir+'volume/fsm/'
+end
+
+state = {}
+stack = {}
+watch = {}
+
+if not(fsm_dir.nil?) and File.directory?(fsm_dir)
+ # loop through each sub directory in the gluster::volume fsm
+ Dir.glob(fsm_dir+'*').each do |d|
+ n = File.basename(d) # should be the gluster::volume name
+ if n.length > 0 and volume_regexp.match(n)
+
+ f = d.gsub(/\/$/, '')+'/state' # full file path
+ if File.exists?(f)
+ # TODO: future versions should unpickle (but with yaml)
+ v = File.open(f, 'r').read.strip # read into str
+ if v.length > 0 and brick_match(v.split(','))
+ state[n] = v
+ end
+ end
+
+ f = d.gsub(/\/$/, '')+'/stack' # full file path
+ if File.exists?(f)
+ stack[n] = [] # initialize empty array
+ File.readlines(f).each do |l|
+ l = l.strip # clean off /n's
+ # TODO: future versions should unpickle (but with yaml)
+ if l.length > 0 and brick_match(l.split(','))
+ #stack[n].push(l)
+ stack[n].push(Base64.encode64(l).delete("\n"))
+ end
+ end
+ end
+
+ f = d.gsub(/\/$/, '')+'/watch' # full file path
+ if File.exists?(f)
+ watch[n] = [] # initialize empty array
+ File.readlines(f).each do |l|
+ l = l.strip # clean off /n's
+ # TODO: future versions should unpickle (but with yaml)
+ if l.length > 0 and brick_match(l.split(','))
+ #watch[n].push(l)
+ watch[n].push(Base64.encode64(l).delete("\n"))
+ end
+ end
+ end
+
+ end
+ end
+end
+
+state.keys.each do |x|
+ Facter.add('gluster_volume_fsm_state_'+x) do
+ #confine :operatingsystem => %w{CentOS, RedHat, Fedora}
+ setcode {
+ state[x]
+ }
+ end
+
+ if stack.key?(x)
+ Facter.add('gluster_volume_fsm_stack_'+x) do
+ #confine :operatingsystem => %w{CentOS, RedHat, Fedora}
+ setcode {
+ stack[x].join(',')
+ }
+ end
+ end
+
+ if watch.key?(x)
+ Facter.add('gluster_volume_fsm_watch_'+x) do
+ #confine :operatingsystem => %w{CentOS, RedHat, Fedora}
+ setcode {
+ watch[x].join(',')
+ }
+ end
+ end
+end
+
+# list of gluster volume fsm state fact names
+Facter.add('gluster_volume_fsm_states') do
+ #confine :operatingsystem => %w{CentOS, RedHat, Fedora}
+ setcode {
+ state.keys.sort.collect {|x| 'gluster_volume_fsm_state_'+x }.join(',')
+ }
+end
+
+Facter.add('gluster_volume_fsm_stacks') do
+ #confine :operatingsystem => %w{CentOS, RedHat, Fedora}
+ setcode {
+ (state.keys & stack.keys).sort.collect {|x| 'gluster_volume_fsm_stack_'+x }.join(',')
+ }
+end
+
+Facter.add('gluster_volume_fsm_watchs') do
+ #confine :operatingsystem => %w{CentOS, RedHat, Fedora}
+ setcode {
+ (state.keys & watch.keys).sort.collect {|x| 'gluster_volume_fsm_watch_'+x }.join(',')
+ }
+end
+
+Facter.add('gluster_fsm_debug') do
+ #confine :operatingsystem => %w{CentOS, RedHat, Fedora}
+ setcode {
+ 'Oh cool, james added fsm support to puppet-gluster. Sweet!'
+ }
+end
+