|
| 1 | +# vim: syntax=ruby tabstop=2 softtabstop=2 shiftwidth=2 |
| 2 | + |
| 3 | +require 'rexml/document' |
| 4 | + |
| 5 | +gluster_peers = {} |
| 6 | +gluster_volumes = {} |
1 | 7 | peer_count = 0 |
2 | 8 | peer_list = '' |
3 | | -volume_bricks = {} |
4 | 9 | volume_options = {} |
5 | 10 | volume_ports = {} |
6 | 11 |
|
|
19 | 24 | end |
20 | 25 |
|
21 | 26 | 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 |
23 | 30 | Facter.add(:gluster_binary) do |
24 | 31 | setcode do |
25 | 32 | binary |
26 | 33 | end |
27 | 34 | 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 |
34 | 49 | 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 |
51 | 89 | end |
52 | 90 | 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 |
53 | 105 | end |
54 | 106 |
|
55 | | - # Gluster facts don't make sense if the Gluster binary isn't present |
| 107 | + # Export our facts |
56 | 108 | Facter.add(:gluster_peer_count) do |
57 | 109 | setcode do |
58 | 110 | peer_count |
|
65 | 117 | end |
66 | 118 | end |
67 | 119 |
|
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? |
69 | 134 | Facter.add(:gluster_volume_list) do |
70 | 135 | setcode do |
71 | | - volume_bricks.keys.join(',') |
| 136 | + gluster_volumes.keys.join(',') |
72 | 137 | end |
73 | 138 | 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 |
76 | 141 | setcode do |
77 | | - bricks.join(',') |
| 142 | + gluster_volumes[volume]['bricks'].join(',') |
78 | 143 | end |
79 | 144 | end |
80 | 145 | end |
81 | 146 | if volume_options |
82 | 147 | volume_options.each do |vol, opts| |
| 148 | + # Create flat facts for each volume |
83 | 149 | Facter.add("gluster_volume_#{vol}_options".to_sym) do |
84 | 150 | setcode do |
85 | 151 | opts.join(',') |
|
0 commit comments