dribbled 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +31 -0
- data/README.md +42 -0
- data/bin/check_drbd +16 -0
- data/bin/dribbled +16 -0
- data/lib/dribbled.rb +9 -0
- data/lib/dribbled/about.rb +5 -0
- data/lib/dribbled/cli.rb +244 -0
- data/lib/dribbled/drbd.rb +186 -0
- data/lib/dribbled/logger.rb +18 -0
- data/lib/dribbled/version.rb +3 -0
- metadata +106 -0
data/LICENSE
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2012 by Evernote Corporation, All rights reserved.
|
3
|
+
*
|
4
|
+
* Use of the source code and binary libraries included in this package
|
5
|
+
* is permitted under the following terms:
|
6
|
+
*
|
7
|
+
* Redistribution and use in source and binary forms, with or without
|
8
|
+
* modification, are permitted provided that the following conditions
|
9
|
+
* are met:
|
10
|
+
*
|
11
|
+
* 1. Redistributions of source code must retain the above copyright
|
12
|
+
* notice, this list of conditions and the following disclaimer.
|
13
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
14
|
+
* notice, this list of conditions and the following disclaimer in the
|
15
|
+
* documentation and/or other materials provided with the distribution.
|
16
|
+
*
|
17
|
+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
18
|
+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
19
|
+
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
20
|
+
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
21
|
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
22
|
+
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
23
|
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
24
|
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
25
|
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
26
|
+
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
*
|
28
|
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
29
|
+
* not use this file except in compliance with the License. You may obtain a
|
30
|
+
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
31
|
+
*/
|
data/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# OVERVIEW
|
2
|
+
|
3
|
+
*Dribbled* collects and displays information about [DRBD](http://www.drbd.org/home/what-is-drbd/ "DRBD") devices.
|
4
|
+
|
5
|
+
*Dribbled* is available as a Rubygem: [gem install dribbled](https://rubygems.org/gems/dribbled "Dribbled")
|
6
|
+
|
7
|
+
# SYNOPSIS
|
8
|
+
|
9
|
+
dribbled [options] <action> [options]
|
10
|
+
|
11
|
+
Options are as follows:
|
12
|
+
|
13
|
+
General options:
|
14
|
+
-D, --drbdadm DRBDADM Path to drbdadm binary
|
15
|
+
-P, --procdrbd PROCDRBD Path to /proc/drbd
|
16
|
+
-X, --xmldump XMLDUMP Path to output for drbdadm --dump-xml
|
17
|
+
-H, --hostname HOSTNAME Hostname
|
18
|
+
-d, --debug Enable debug mode
|
19
|
+
-a, --about Display dribbled information
|
20
|
+
-V, --version Display dribbled version
|
21
|
+
--help Show this messageSenedsa options
|
22
|
+
|
23
|
+
With no options or arguments, `dribbled` displays help (as shown above).
|
24
|
+
|
25
|
+
# PERSONALITIES
|
26
|
+
|
27
|
+
*Dribbled* has two personalities: when run as `dribbled`, it behaves according to the action and parameters used; when run as `check_drbd`, it assumes the `check` action, which enables Nagios plugin behavior.
|
28
|
+
|
29
|
+
gerir@boxie:~:dribbled show
|
30
|
+
0 r0 Connected UpToDate/UpToDate Secondary/Primary sourcehost /dev/drbd0 desthost /dev/drbd0
|
31
|
+
1 r1 Connected UpToDate/UpToDate Secondary/Primary sourcehost /dev/drbd1 desthost /dev/drbd1
|
32
|
+
5 r5 Connected UpToDate/UpToDate Secondary/Primary sourcehost /dev/drbd5 desthost /dev/drbd5
|
33
|
+
6 r6 SyncTarget [ 4%] Inconsistent/UpToDate Secondary/Primary sourcehost /dev/drbd6 desthost /dev/drbd6
|
34
|
+
|
35
|
+
gerir@boxie:~:dribbled check
|
36
|
+
warning: 6:cs:SyncTarget[4.6%%];ds:Inconsistent/UpToDate
|
37
|
+
|
38
|
+
gerir@boxie:~:dribbled check
|
39
|
+
warning: 6:cs:SyncTarget[4.6%%];ds:Inconsistent/UpToDate
|
40
|
+
|
41
|
+
Currently, there is no way to configure what constitutes alert conditions, but generally, a **critical** alert will be generated when a resource is found in non-`Connected` and non-`UpToDate` state, except when said resource is in `SyncSource`, `SyncTarget`, `VerifyS`, `VerifyT`, `PausedSyncS`, `PausedSyncT`, or `StandAlone` connection state, which causes a **warning**. Also, resources that are `Connected` and `UpToDate` but missing from the configuration will generate a **warning**.
|
42
|
+
|
data/bin/check_drbd
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'dribbled'
|
8
|
+
|
9
|
+
module Dribbled
|
10
|
+
|
11
|
+
ID = File.basename($PROGRAM_NAME).to_sym
|
12
|
+
|
13
|
+
app = CLI.new(ARGV)
|
14
|
+
app.run
|
15
|
+
|
16
|
+
end
|
data/bin/dribbled
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'dribbled'
|
8
|
+
|
9
|
+
module Dribbled
|
10
|
+
|
11
|
+
ID = File.basename($PROGRAM_NAME).to_sym
|
12
|
+
|
13
|
+
app = CLI.new(ARGV)
|
14
|
+
app.run
|
15
|
+
|
16
|
+
end
|
data/lib/dribbled.rb
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
require 'dribbled/version'
|
2
|
+
module Dribbled
|
3
|
+
ME = :dribbled
|
4
|
+
ABOUT = "#{ME} v#{VERSION}\nhttps://github.com/evernote/ops-#{ME}\nCopyright (c) 2012 by Evernote Corporation\nLicensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)"
|
5
|
+
end
|
data/lib/dribbled/cli.rb
ADDED
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'syslog'
|
3
|
+
require 'io/wait'
|
4
|
+
require 'senedsa'
|
5
|
+
require 'socket'
|
6
|
+
require 'dribbled'
|
7
|
+
|
8
|
+
module Dribbled
|
9
|
+
|
10
|
+
class CLI
|
11
|
+
|
12
|
+
include Senedsa
|
13
|
+
|
14
|
+
COMMANDS = %w(show check snap)
|
15
|
+
COMPONENTS = %w(resources)
|
16
|
+
DEFAULT_CONFIG_FILE = File.join(ENV['HOME'],"/.senedsa/config")
|
17
|
+
|
18
|
+
def initialize(arguments)
|
19
|
+
@arguments = arguments
|
20
|
+
@whoami = File.basename($PROGRAM_NAME).to_sym
|
21
|
+
|
22
|
+
@global_options = { :debug => false, :drbdadm => 'drbdadm', :xmldump => nil, :procdrbd => '/proc/drbd', :hostname => nil }
|
23
|
+
@action_options = { :monitor => :nagios, :mode => :active, :suffix => nil, :directory => '/tmp' }
|
24
|
+
@action = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def run
|
28
|
+
begin
|
29
|
+
parsed_options?
|
30
|
+
|
31
|
+
@log = Dribbled::Logger.instance.log
|
32
|
+
@log.level = Log4r::INFO unless @global_options[:debug]
|
33
|
+
@global_options[:log] = @log
|
34
|
+
|
35
|
+
config_options?
|
36
|
+
arguments_valid?
|
37
|
+
options_valid?
|
38
|
+
process_options
|
39
|
+
process_arguments
|
40
|
+
process_command
|
41
|
+
|
42
|
+
rescue => e
|
43
|
+
if @global_options[:debug]
|
44
|
+
output_message "#{e.message}\n #{e.backtrace.join("\n ")}",3
|
45
|
+
else
|
46
|
+
output_message e.message,3
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def parsed_options?
|
54
|
+
opts = OptionParser.new
|
55
|
+
|
56
|
+
opts.banner = "Usage: #{ID} [options] <action> [options]"
|
57
|
+
opts.separator ""
|
58
|
+
opts.separator "Actions:"
|
59
|
+
opts.separator " show Displays component information"
|
60
|
+
opts.separator " check Performs health checks"
|
61
|
+
opts.separator " snap Saves contents of /proc/drbd and 'drbdadm dump-xml'"
|
62
|
+
opts.separator ""
|
63
|
+
opts.separator "General options:"
|
64
|
+
opts.on('-D', '--drbdadm DRBDADM', String, "Path to drbdadm binary") { |drbdadm| @global_options[:drbdadm] = drbdadm }
|
65
|
+
opts.on('-P', '--procdrbd PROCDRBD', String, "Path to /proc/drbd") { |procdrbd| @global_options[:procdrbd] = procdrbd }
|
66
|
+
opts.on('-X', '--xmldump XMLDUMP', String, "Path to output for drbdadm --dump-xml") { |xmldump| @global_options[:xmldump] = xmldump}
|
67
|
+
opts.on('-H', '--hostname HOSTNAME', String, "Hostname") { |hostname| @global_options[:hostname] = hostname }
|
68
|
+
opts.on('-d', '--debug', "Enable debug mode") { @global_options[:debug] = true}
|
69
|
+
opts.on('-a', '--about', "Display #{ID} information") { output_message ABOUT, 0 }
|
70
|
+
opts.on('-V', '--version', "Display #{ID} version") { output_message VERSION, 0 }
|
71
|
+
opts.on_tail('--help', "Show this message") { @global_options[:HELP] = true }
|
72
|
+
|
73
|
+
actions = {
|
74
|
+
:show => OptionParser.new do |aopts|
|
75
|
+
aopts.banner = "Usage: #{ID} [options] show <component>"
|
76
|
+
aopts.separator ""
|
77
|
+
aopts.separator " <component> is resources|res"
|
78
|
+
end,
|
79
|
+
:check => OptionParser.new do |aopts|
|
80
|
+
aopts.banner = "Usage: #{ID} [options] check [check_options]"
|
81
|
+
aopts.separator ""
|
82
|
+
aopts.separator "Check Options"
|
83
|
+
aopts.on('-M', '--monitor [nagios]', [:nagios], "Monitoring system") { |monitor| @action_options[:monitor] = monitor }
|
84
|
+
aopts.on('-m', '--mode [active|passive]', [:active, :passive], "Monitoring mode") { |mode| @action_options[:mode] = mode }
|
85
|
+
aopts.on('-H', '--nsca_hostname HOSTNAME', String, "NSCA hostname to send passive checks") { |nsca_hostname| @action_options[:nsca_hostname] = nsca_hostname }
|
86
|
+
aopts.on('-c', '--config CONFIG', String, "Path to Senedsa (send_nsca) configuration" ) { |config| @action_options[:senedsa_config] = config }
|
87
|
+
aopts.on('-S', '--svc_descr SVC_DESR', String, "Nagios service description") { |svc_descr| @action_options[:svc_descr] = svc_descr }
|
88
|
+
aopts.on('-h', '--hostname HOSTNAME', String, "Service hostname") { |hostname| @action_options[:svc_hostname] = hostname }
|
89
|
+
end,
|
90
|
+
:snap => OptionParser.new do |aopts|
|
91
|
+
aopts.banner = "Usage: #{ID} [options] snap [snap_options]"
|
92
|
+
aopts.separator ""
|
93
|
+
aopts.separator "Snap Options"
|
94
|
+
aopts.on('-S','--suffix SUFFIX', String, "Suffix (defaults to PID)") { |suffix| @action_options[:suffix] = suffix }
|
95
|
+
aopts.on('-D', '--directory DIRECTORY', String, "Directory (defaults to /tmp)") { |directory| @action_options[:directory] = directory }
|
96
|
+
end
|
97
|
+
}
|
98
|
+
|
99
|
+
opts.order!(@arguments)
|
100
|
+
output_message opts, 0 if (@arguments.size == 0 and @whoami != :check_drbd) or @global_options[:HELP]
|
101
|
+
|
102
|
+
@action = @whoami == :check_drbd ? :check : @arguments.shift.to_sym
|
103
|
+
raise OptionParser::InvalidArgument, "invalid action #@action" if actions[@action].nil?
|
104
|
+
actions[@action].order!(@arguments)
|
105
|
+
end
|
106
|
+
|
107
|
+
def config_options?
|
108
|
+
cfg_file = nil
|
109
|
+
cfg_file = @action_options[:senedsa_config] unless @action_options[:senedsa_config].nil?
|
110
|
+
cfg_file = DEFAULT_CONFIG_FILE if @action_options[:senedsa_config].nil? and File.readable? DEFAULT_CONFIG_FILE
|
111
|
+
|
112
|
+
unless cfg_file.nil?
|
113
|
+
@action_options.merge!(Senedsa::SendNsca.configure(cfg_file))
|
114
|
+
@action_options[:senedsa_config] = cfg_file
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def arguments_valid?
|
119
|
+
true
|
120
|
+
end
|
121
|
+
|
122
|
+
def options_valid?
|
123
|
+
|
124
|
+
@global_options[:hostname] = Socket.gethostname if @global_options[:hostname].nil?
|
125
|
+
|
126
|
+
case @action
|
127
|
+
when :check
|
128
|
+
raise OptionParser::MissingArgument, "NSCA hostname (-H) must be specified" if @action_options[:nsca_hostname].nil? and @action_options[:mode] == 'passive'
|
129
|
+
raise OptionParser::MissingArgument, "service description (-S) must be specified" if @action_options[:svc_descr].nil? and @action_options[:mode] == 'passive'
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def process_options
|
134
|
+
true
|
135
|
+
end
|
136
|
+
|
137
|
+
def process_arguments
|
138
|
+
true
|
139
|
+
end
|
140
|
+
|
141
|
+
def process_command
|
142
|
+
|
143
|
+
@drbdset = DrbdSet.new @global_options
|
144
|
+
|
145
|
+
case @action
|
146
|
+
when :show then run_show
|
147
|
+
when :check then run_check
|
148
|
+
when :snap then run_snap
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
def run_show
|
154
|
+
#raise ArgumentError, "missing component" if @arguments.size == 0
|
155
|
+
component = 'res'
|
156
|
+
|
157
|
+
case component
|
158
|
+
when 'resource', 'res'
|
159
|
+
@drbdset.each do |r,resource|
|
160
|
+
puts resource.to_s
|
161
|
+
end
|
162
|
+
when 'version'
|
163
|
+
puts @drbdset.version
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def run_check
|
168
|
+
|
169
|
+
plugin_output = ""
|
170
|
+
plugin_status = ""
|
171
|
+
|
172
|
+
# check for configuration vs running resources
|
173
|
+
|
174
|
+
#unconfigured_resources = @drbdset.select { |k,v| v.cstate == "Unconfigured" }
|
175
|
+
|
176
|
+
# unless unconfigured_resources.empty?
|
177
|
+
# plugin_output = "DRBD unconfigured resources found: #{unconfigured_resources.keys.join(' ')}"
|
178
|
+
# plugin_status = :warning
|
179
|
+
# end
|
180
|
+
|
181
|
+
# check dstate, state and cstate for each resource
|
182
|
+
# + cstate should be: Connected
|
183
|
+
# + dstate should be: UpToDate/UpToDate
|
184
|
+
|
185
|
+
@drbdset.each do |r,res|
|
186
|
+
next if res.cstate == 'Unconfigured'
|
187
|
+
|
188
|
+
po_cstate = ""
|
189
|
+
po_dstate = ""
|
190
|
+
|
191
|
+
po_cstate = "cs:#{res.cstate}" unless res.cstate == "Connected" and res.in_kernel? and res.in_configuration?
|
192
|
+
po_dstate = "ds:#{res.dstate}" unless res.dstate == "UpToDate/UpToDate" and res.in_kernel? and res.in_configuration?
|
193
|
+
|
194
|
+
unless po_cstate.gsub('cs:','').empty? and po_dstate.gsub('ds:','').empty?
|
195
|
+
if ['SyncSource','SyncTarget','VerifyS','VerifyT','PausedSyncS','PausedSyncT','StandAlone'].include? res.cstate
|
196
|
+
plugin_status = :warning
|
197
|
+
plugin_output += res.percent.nil? ? " #{res.id}:#{po_cstate};#{po_dstate}" : " #{res.id}:#{po_cstate}[#{res.percent}%%];#{po_dstate}"
|
198
|
+
elsif not res.in_configuration?
|
199
|
+
plugin_status = :warning
|
200
|
+
plugin_output += " #{res.id}[unconfigured]>#{po_cstate}/;#{po_dstate}"
|
201
|
+
else
|
202
|
+
plugin_output += " #{res.id}>#{po_cstate};#{po_dstate}"
|
203
|
+
plugin_status = :critical
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
plugin_output = " all DRBD resources Connected, UpToDate/UpToDate" if plugin_output.empty? and plugin_status.empty?
|
209
|
+
plugin_status = :ok if plugin_status.empty?
|
210
|
+
|
211
|
+
case @action_options[:monitor]
|
212
|
+
when :nagios
|
213
|
+
case @action_options[:mode]
|
214
|
+
when :active
|
215
|
+
puts "#{plugin_status}:#{plugin_output}"
|
216
|
+
exit SendNsca::STATUS[plugin_status]
|
217
|
+
when :passive
|
218
|
+
sn = SendNsca.new @action_options
|
219
|
+
begin
|
220
|
+
sn.send plugin_status , plugin_output
|
221
|
+
rescue SendNsca::SendNscaError => e
|
222
|
+
output_message "send_nsca failed: #{e.message}", 1
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def run_snap
|
229
|
+
@action_options[:suffix] = $$ if @action_options[:suffix].nil?
|
230
|
+
procdrbd_file = "#{@action_options[:directory]}/procdrbd.#{@action_options[:suffix]}"
|
231
|
+
xmldump_file = "#{@action_options[:directory]}/xmldump.#{@action_options[:suffix]}"
|
232
|
+
File.open(procdrbd_file, 'w') {|f| f.write(@drbdset.resources_run_raw) }
|
233
|
+
File.open(xmldump_file, 'w') {|f| f.write(@drbdset.resources_cfg_raw) }
|
234
|
+
end
|
235
|
+
|
236
|
+
def output_message(message, exitstatus=nil)
|
237
|
+
m = (! exitstatus.nil? and exitstatus > 0) ? "%s: error: %s" % [ID, message] : message
|
238
|
+
Syslog.open(ID.to_s, Syslog::LOG_PID | Syslog::LOG_CONS) { |s| s.err "error: #{message}" } unless @global_options[:debug]
|
239
|
+
$stderr.write "#{m}\n" if STDIN.tty?
|
240
|
+
exit exitstatus unless exitstatus.nil?
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'xmlsimple'
|
2
|
+
require 'ostruct'
|
3
|
+
#require 'awesome_print'
|
4
|
+
|
5
|
+
module Dribbled
|
6
|
+
|
7
|
+
class DrbdSet < Hash
|
8
|
+
|
9
|
+
attr_reader :resources_cfg_raw, :resources_run_raw
|
10
|
+
|
11
|
+
PROCDRBD_VERSION_RE = /^version:\s+(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)\s+\(api:(?<api>\d+)\/proto:(?<proto>[0-9-]+)/
|
12
|
+
PROCDRBD_RESOURCE_RE = /^\s*(?<id>\d+):\s+cs:(?<cstate>[\w\/]+)\s+(st|ro):(?<state>[\w\/]+)\s+ds:(?<dstate>[\w\/]+)\s+/
|
13
|
+
PROCDRBD_URESOURCE_RE = /^\s*(?<id>\d+):\s+cs:(?<cstate>[\w\/]+)/
|
14
|
+
PROCDRBD_ACTIVITY_RE = /^\s+\[[.>=]+\]\s+(?<activity>[a-z']+):\s+(?<percent>[0-9.]+)%/
|
15
|
+
|
16
|
+
def initialize(options)
|
17
|
+
@log = options[:log].nil? ? nil : options[:log]
|
18
|
+
@hostname = options[:hostname]
|
19
|
+
|
20
|
+
resources_run_src = options[:procdrbd]
|
21
|
+
@resources_run_raw = nil
|
22
|
+
@resources_run = _read_procdrbd(resources_run_src) # sets @resources_run_raw as side-effect; it shouldn't
|
23
|
+
@resources_run.each do |r,res|
|
24
|
+
self[res.id] = res unless res.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
resources_cfg_src = options[:xmldump].nil? ? IO.popen("#{options[:drbdadm]} dump-xml 2>/dev/null") : options[:xmldump]
|
28
|
+
@resources_cfg_raw = nil
|
29
|
+
@resources_cfg = _read_xmldump(resources_cfg_src) # sets @resources_cfg_raw as side-effect; it shouldn't
|
30
|
+
|
31
|
+
@resources_cfg.each do |name,res|
|
32
|
+
_process_xml_resource(name,res)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def version
|
38
|
+
"#@version_major.#@version_minor.#@version_path"
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def _read_procdrbd(resources_run_src)
|
44
|
+
@log.debug "Running configuration source: #{resources_run_src}" unless @log.nil?
|
45
|
+
resources_run = {}
|
46
|
+
@resources_run_raw = File.open(resources_run_src,"r") { |f| f.read }
|
47
|
+
r = nil
|
48
|
+
@resources_run_raw.each_line do |line|
|
49
|
+
if /^\s*(\d+):/.match(line)
|
50
|
+
r = $1.to_i
|
51
|
+
resources_run[r] = DrbdResource.new r, @hostname
|
52
|
+
resources_run[r].in_kernel = true
|
53
|
+
if PROCDRBD_RESOURCE_RE.match(line)
|
54
|
+
m = PROCDRBD_RESOURCE_RE.match(line)
|
55
|
+
resources_run[r].cstate = m[:cstate]
|
56
|
+
resources_run[r].state = m[:state]
|
57
|
+
resources_run[r].dstate = m[:dstate]
|
58
|
+
elsif PROCDRBD_URESOURCE_RE.match(line)
|
59
|
+
resources_run[r].cstate = PROCDRBD_URESOURCE_RE.match(line)[:cstate]
|
60
|
+
end
|
61
|
+
@log.debug " #{resources_run[r].inspect}" unless @log.nil?
|
62
|
+
elsif PROCDRBD_ACTIVITY_RE.match(line)
|
63
|
+
m = PROCDRBD_ACTIVITY_RE.match(line)
|
64
|
+
resources_run[r].activity = m[:activity].gsub(/'/,"").to_sym
|
65
|
+
resources_run[r].percent = m[:percent].to_f
|
66
|
+
elsif PROCDRBD_VERSION_RE.match(line)
|
67
|
+
@version_major = $1
|
68
|
+
@version_minor = $2
|
69
|
+
@version_path = $3
|
70
|
+
end
|
71
|
+
end
|
72
|
+
resources_run
|
73
|
+
end
|
74
|
+
|
75
|
+
def _read_xmldump(resources_cfg_src)
|
76
|
+
@log.debug "Stable configuration source: #{resources_cfg_src}" unless @log.nil?
|
77
|
+
@resources_cfg_raw = resources_cfg_src.is_a?(String) ? File.open(resources_cfg_src,"r") { |f| f.read } : resources_cfg_src.read
|
78
|
+
XmlSimple.xml_in(@resources_cfg_raw, { 'KeyAttr' => 'name' })['resource']
|
79
|
+
end
|
80
|
+
|
81
|
+
def _process_xml_resource(name,res)
|
82
|
+
res['host'].each_key do |hostname|
|
83
|
+
@log.debug " resource xml processing: host #{hostname}"
|
84
|
+
if res['host'][hostname]['device'][0]['minor'].nil?
|
85
|
+
r = res['host'][hostname]['device'][0].split('drbd')[1].to_i
|
86
|
+
@log.debug " #{version}: #{res['host'][hostname]['device'][0]}; r = #{r}"
|
87
|
+
disk = res['host'][hostname]['disk'][0]
|
88
|
+
device = res['host'][hostname]['device'][0]
|
89
|
+
else
|
90
|
+
r = res['host'][hostname]['device'][0]['minor'].to_i
|
91
|
+
@log.debug " #{version}: #{res['host'][hostname]['device'][0]}; r = #{r}"
|
92
|
+
disk = res['host'][hostname]['disk'][0]
|
93
|
+
device = res['host'][hostname]['device'][0]['content']
|
94
|
+
end
|
95
|
+
if self[r].nil?
|
96
|
+
self[r] = DrbdResource.new r, @hostname
|
97
|
+
self[r].cstate = "StandAlone"
|
98
|
+
self[r].dstate = "DUnknown"
|
99
|
+
self[r].state = "Unknown"
|
100
|
+
end
|
101
|
+
self[r].name = name
|
102
|
+
self[r].in_configuration = true
|
103
|
+
@log.debug " resource: #{r}, state: #{self[r].state}"
|
104
|
+
if self[r].state == 'Primary/Secondary'
|
105
|
+
if hostname == @hostname
|
106
|
+
@log.debug " resource: #{r}, primary"
|
107
|
+
self[r].primary[:disk] = disk
|
108
|
+
self[r].primary[:device] = device
|
109
|
+
self[r].primary[:hostname] = hostname
|
110
|
+
else
|
111
|
+
@log.debug " resource: #{r}, secondary"
|
112
|
+
self[r].secondary[:disk] = disk
|
113
|
+
self[r].secondary[:device] = device
|
114
|
+
self[r].secondary[:hostname] = hostname
|
115
|
+
end
|
116
|
+
elsif self[r].state == 'Secondary/Primary' or self[r].state == 'Unknown'
|
117
|
+
if hostname == @hostname
|
118
|
+
@log.debug " resource: #{r}, secondary"
|
119
|
+
self[r].secondary[:disk] = disk
|
120
|
+
self[r].secondary[:device] = device
|
121
|
+
self[r].secondary[:hostname] = hostname
|
122
|
+
else
|
123
|
+
@log.debug " resource: #{r}, primary"
|
124
|
+
self[r].primary[:disk] = disk
|
125
|
+
self[r].primary[:device] = device
|
126
|
+
self[r].primary[:hostname] = hostname
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class DrbdResource
|
134
|
+
|
135
|
+
attr_reader :id
|
136
|
+
attr_accessor :name, :cstate, :dstate, :state, :config, :primary, :secondary, :activity, :percent, :in_kernel, :in_configuration
|
137
|
+
|
138
|
+
def initialize(res,hostname)
|
139
|
+
@id = res
|
140
|
+
@name = nil
|
141
|
+
@config = nil
|
142
|
+
@dstate = nil
|
143
|
+
@cstate = nil
|
144
|
+
@state = nil
|
145
|
+
@activity = nil
|
146
|
+
@percent = nil
|
147
|
+
@primary = { :hostname => nil, :disk => nil, :device => nil }
|
148
|
+
@secondary = { :hostname => nil, :disk => nil, :device => nil }
|
149
|
+
@in_kernel = false
|
150
|
+
@in_configuration = false
|
151
|
+
end
|
152
|
+
|
153
|
+
def in_kernel?
|
154
|
+
@in_kernel
|
155
|
+
end
|
156
|
+
|
157
|
+
def in_configuration?
|
158
|
+
@in_configuration
|
159
|
+
end
|
160
|
+
|
161
|
+
def to_s
|
162
|
+
ph = @primary[:hostname].gsub(/\.[a-z0-9-]+\.[a-z0-9-]+$/,"") unless @primary[:hostname].nil?
|
163
|
+
sh = @secondary[:hostname].gsub(/\.[a-z0-9-]+\.[a-z0-9-]+$/,"") unless @secondary[:hostname].nil?
|
164
|
+
|
165
|
+
if @state == 'Primary/Secondary'
|
166
|
+
h1 = ph; dev1 = @primary[:device]
|
167
|
+
h2 = sh; dev2 = @secondary[:device]
|
168
|
+
else
|
169
|
+
h1 = sh; dev1 = @secondary[:device]
|
170
|
+
h2 = ph; dev2 = @primary[:device]
|
171
|
+
end
|
172
|
+
|
173
|
+
percent = @activity.nil? ? nil : "[%3d%%]" % @percent
|
174
|
+
|
175
|
+
"%2d %6s %-13s %6s %-22s %-20s %10s %-11s %10s %-11s" % [@id,@name,@cstate,percent,@dstate,@state,h1,dev1,h2,dev2]
|
176
|
+
end
|
177
|
+
|
178
|
+
def inspect
|
179
|
+
"#{self.class}: #@id[#@name]: #@cstate,#@dstate,#@state"
|
180
|
+
end
|
181
|
+
|
182
|
+
def check
|
183
|
+
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'log4r'
|
3
|
+
|
4
|
+
module Dribbled
|
5
|
+
|
6
|
+
class Logger
|
7
|
+
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
attr_reader :log
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@log = Log4r::Logger.new("dribbled")
|
14
|
+
@log.add Log4r::StderrOutputter.new('console', :formatter => Log4r::PatternFormatter.new(:pattern => "%c [%l] %m"), :level => Log4r::DEBUG)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dribbled
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Gerardo López-Fernádez
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: xml-simple
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: log4r
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: senedsa
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.2.9
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.2.9
|
62
|
+
description: Provides a wrapper around DRBD to gather information and perform monitoring
|
63
|
+
checks
|
64
|
+
email: gerir@evernote.com
|
65
|
+
executables:
|
66
|
+
- dribbled
|
67
|
+
- check_drbd
|
68
|
+
extensions: []
|
69
|
+
extra_rdoc_files: []
|
70
|
+
files:
|
71
|
+
- lib/dribbled/about.rb
|
72
|
+
- lib/dribbled/cli.rb
|
73
|
+
- lib/dribbled/drbd.rb
|
74
|
+
- lib/dribbled/logger.rb
|
75
|
+
- lib/dribbled/version.rb
|
76
|
+
- lib/dribbled.rb
|
77
|
+
- bin/check_drbd
|
78
|
+
- bin/dribbled
|
79
|
+
- LICENSE
|
80
|
+
- README.md
|
81
|
+
homepage: https://github.com/evernote/ops-dribbled
|
82
|
+
licenses:
|
83
|
+
- Apache License, Version 2.0
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: 1.3.5
|
100
|
+
requirements: []
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 1.8.23
|
103
|
+
signing_key:
|
104
|
+
specification_version: 3
|
105
|
+
summary: DRBD tool
|
106
|
+
test_files: []
|