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 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,9 @@
1
+ require 'rubygems'
2
+ require 'dribbled/version'
3
+ require 'dribbled/about'
4
+ require 'dribbled/logger'
5
+ require 'dribbled/drbd'
6
+ require 'dribbled/cli'
7
+
8
+ module Dribbled
9
+ end
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Dribbled
2
+ VERSION = "0.6.0"
3
+ 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: []