Skip to content

Commit b3fe6ba

Browse files
authored
Merge pull request #186 from tparkercbn/xml-structured-facts
Create new structured facts for `gluster_peers` and `gluster_volumes`
2 parents 61a380e + a56abdf commit b3fe6ba

File tree

3 files changed

+606
-37
lines changed

3 files changed

+606
-37
lines changed

lib/facter/gluster.rb

Lines changed: 96 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
# vim: syntax=ruby tabstop=2 softtabstop=2 shiftwidth=2
2+
3+
require 'rexml/document'
4+
5+
gluster_peers = {}
6+
gluster_volumes = {}
17
peer_count = 0
28
peer_list = ''
3-
volume_bricks = {}
49
volume_options = {}
510
volume_ports = {}
611

@@ -19,40 +24,87 @@
1924
end
2025

2126
if binary
22-
# the Gluster binary command to use
27+
# Gluster facts don't make sense if the Gluster binary isn't present
28+
29+
# The Gluster binary command to use
2330
Facter.add(:gluster_binary) do
2431
setcode do
2532
binary
2633
end
2734
end
28-
output = Facter::Util::Resolution.exec("#{binary} peer status")
29-
peer_count = Regexp.last_match[1].to_i if output =~ %r{^Number of Peers: (\d+)$}
30-
if peer_count > 0
31-
peer_list = output.scan(%r{^Hostname: (.+)$}).flatten.join(',')
32-
other_names = output.scan(%r{^Other names:\n((.+\n)+)}).flatten.join.scan(%r{(.+)\n?}).sort.uniq.flatten.join(',')
33-
peer_list += ',' + other_names if other_names
35+
36+
# Get our peer information from gluster peer status --xml (Code credit to github user: coder-hugo)
37+
peer_status_xml = REXML::Document.new(Facter::Util::Resolution.exec("#{binary} peer status --xml"))
38+
REXML::XPath.match(peer_status_xml, '/cliOutput/peerStatus/peer').each do |peer_xml|
39+
# Get the peer hostname
40+
peer = peer_xml.elements['hostname'].text.to_s
41+
42+
# Define a per-peer hash to contain our data elements
43+
gluster_peers[peer] = {}
44+
45+
gluster_peers[peer]['uuid'] = peer_xml.elements['uuid'].text.to_s
46+
gluster_peers[peer]['connected'] = peer_xml.elements['connected'].text.to_i
47+
gluster_peers[peer]['state'] = peer_xml.elements['state'].text.to_i
48+
gluster_peers[peer]['status'] = peer_xml.elements['stateStr'].text.to_s
3449
end
35-
# note the stderr redirection here
36-
# `gluster volume list` spits to stderr :(
37-
output = Facter::Util::Resolution.exec("#{binary} volume list 2>&1")
38-
if output != 'No volumes present in cluster'
39-
output.split.each do |vol|
40-
# If a brick has trailing informaion such as (arbiter) remove it
41-
info = Facter::Util::Resolution.exec("#{binary} volume info #{vol} | sed 's/ (arbiter)//g'")
42-
vol_status = Regexp.last_match[1] if info =~ %r{^Status: (.+)$}
43-
bricks = info.scan(%r{^Brick[^:]+: (.+)$}).flatten
44-
volume_bricks[vol] = bricks
45-
options = info.scan(%r{^(\w+\.[^:]+: .+)$}).flatten
46-
volume_options[vol] = options if options
47-
next unless vol_status == 'Started'
48-
status = Facter::Util::Resolution.exec("#{binary} volume status #{vol} 2>/dev/null")
49-
if status =~ %r{^Brick}
50-
volume_ports[vol] = status.scan(%r{^Brick [^\t]+\t+(\d+)}).flatten.uniq.sort
50+
51+
# Extract and format the data needed for the legacy peer facts.
52+
peer_count = gluster_peers.size
53+
peer_list = gluster_peers.keys.join(',')
54+
55+
# Get our volume information from gluster volume info
56+
volume_info_xml = REXML::Document.new(Facter::Util::Resolution.exec("#{binary} volume info --xml"))
57+
REXML::XPath.match(volume_info_xml, '/cliOutput/volInfo/volumes/volume').each do |volume_xml|
58+
volume = volume_xml.elements['name'].text.to_s
59+
60+
# Create hash entry for each volume in a structured fact.
61+
gluster_volumes[volume] = {}
62+
63+
vol_status = volume_xml.elements['statusStr'].text.to_s
64+
gluster_volumes[volume]['status'] = vol_status
65+
66+
# Define gluster_volumes[volume]['bricks'] as an array so we can .push() to it.
67+
gluster_volumes[volume]['bricks'] = []
68+
69+
REXML::XPath.match(volume_xml, 'bricks/brick').each do |brick_xml|
70+
# We need to loop over the bricks so that we can change the text from :REXML::Text. to String
71+
brick_name = brick_xml.elements['name'].text.to_s
72+
gluster_volumes[volume]['bricks'].push(brick_name)
73+
end
74+
75+
options = REXML::XPath.match(volume_xml, 'options/option').map { |option| "#{option.elements['name'].text}: #{option.elements['value'].text}" }
76+
if options
77+
volume_options[volume] = options
78+
gluster_volumes[volume]['features'] = {}
79+
gluster_volumes[volume]['options'] = {}
80+
# Convert options into key: value pairs for easy retrieval if needed.
81+
options.each do |option|
82+
option_name, set_value = option.split(': ', 2)
83+
84+
if option_name =~ %r{^features\.}
85+
gluster_volumes[volume]['features'][option_name] = set_value
86+
else
87+
gluster_volumes[volume]['options'][option_name] = set_value
88+
end
5189
end
5290
end
91+
92+
next unless vol_status == 'Started'
93+
94+
volume_status_xml = REXML::Document.new(Facter::Util::Resolution.exec("#{binary} volume status #{volume} --xml"))
95+
volume_ports[volume] = REXML::XPath.match(volume_status_xml, "/cliOutput/volStatus/volumes/volume/node[starts-with(hostname/text(), '#{Facter.value('hostname')}')]/port/text()")
96+
97+
# Define gluster_volumes[volume]['ports'] as an array so we can .push() to it.
98+
gluster_volumes[volume]['ports'] = []
99+
100+
volume_ports[volume].each do |port|
101+
# port is of type: :REXML::Text. Convert it to String and then Integer
102+
port_number = port.to_s.to_i
103+
gluster_volumes[volume]['ports'].push(port_number)
104+
end
53105
end
54106

55-
# Gluster facts don't make sense if the Gluster binary isn't present
107+
# Export our facts
56108
Facter.add(:gluster_peer_count) do
57109
setcode do
58110
peer_count
@@ -65,21 +117,35 @@
65117
end
66118
end
67119

68-
unless volume_bricks.empty?
120+
# Create a new structured facts containing all peer and volume info.
121+
Facter.add(:gluster_peers) do
122+
setcode do
123+
gluster_peers
124+
end
125+
end
126+
127+
Facter.add(:gluster_volumes) do
128+
setcode do
129+
gluster_volumes
130+
end
131+
end
132+
133+
unless gluster_volumes.empty?
69134
Facter.add(:gluster_volume_list) do
70135
setcode do
71-
volume_bricks.keys.join(',')
136+
gluster_volumes.keys.join(',')
72137
end
73138
end
74-
volume_bricks.each do |vol, bricks|
75-
Facter.add("gluster_volume_#{vol}_bricks".to_sym) do
139+
gluster_volumes.keys.each do |volume|
140+
Facter.add("gluster_volume_#{volume}_bricks".to_sym) do
76141
setcode do
77-
bricks.join(',')
142+
gluster_volumes[volume]['bricks'].join(',')
78143
end
79144
end
80145
end
81146
if volume_options
82147
volume_options.each do |vol, opts|
148+
# Create flat facts for each volume
83149
Facter.add("gluster_volume_#{vol}_options".to_sym) do
84150
setcode do
85151
opts.join(',')

manifests/volume.pp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,9 @@
222222
}
223223

224224
# did the options change?
225-
$current_options = getvar("gluster_volume_${title}_options")
226-
if $current_options {
227-
$_current = sort( split($current_options, ',') )
228-
} else {
229-
$_current = []
230-
}
225+
$current_options_hash = pick(fact("gluster_volumes.${title}.options"), {})
226+
$_current = sort(join_keys_to_values($current_options_hash, ': '))
227+
231228
if $_current != $_options {
232229
#
233230
# either of $current_options or $_options may be empty.
@@ -258,7 +255,7 @@
258255
create_resources( ::gluster::volume::option, $remove )
259256
} else {
260257
$remove_str = join( keys($remove), ', ' )
261-
notice("NOT REMOVING the following options for volume ${title}:${remove_str}.")
258+
notice("NOT REMOVING the following options for volume ${title}: ${remove_str}.")
262259
}
263260
}
264261
if ! empty($to_add) {

0 commit comments

Comments
 (0)