chef-expander 0.10.0.beta.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 +201 -0
- data/README.rdoc +20 -0
- data/bin/chef-expander +30 -0
- data/bin/chef-expander-cluster +29 -0
- data/bin/chef-expanderctl +30 -0
- data/conf/chef-expander.rb.example +9 -0
- data/lib/chef/expander/cluster_supervisor.rb +127 -0
- data/lib/chef/expander/configuration.rb +307 -0
- data/lib/chef/expander/control.rb +206 -0
- data/lib/chef/expander/daemonizable.rb +150 -0
- data/lib/chef/expander/flattener.rb +79 -0
- data/lib/chef/expander/loggable.rb +40 -0
- data/lib/chef/expander/logger.rb +135 -0
- data/lib/chef/expander/node.rb +177 -0
- data/lib/chef/expander/solrizer.rb +275 -0
- data/lib/chef/expander/version.rb +37 -0
- data/lib/chef/expander/vnode.rb +106 -0
- data/lib/chef/expander/vnode_supervisor.rb +265 -0
- data/lib/chef/expander/vnode_table.rb +83 -0
- data/lib/chef/expander.rb +36 -0
- data/scripts/check_queue_size +93 -0
- data/scripts/check_queue_size_munin +51 -0
- data/scripts/make_solr_xml +58 -0
- data/scripts/traffic-creator +97 -0
- metadata +202 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Daniel DeLeo (<dan@opscode.com>)
|
3
|
+
# Author:: Seth Falcon (<seth@opscode.com>)
|
4
|
+
# Author:: Chris Walters (<cw@opscode.com>)
|
5
|
+
# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
6
|
+
# License:: Apache License, Version 2.0
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'open3'
|
22
|
+
|
23
|
+
module Chef
|
24
|
+
module Expander
|
25
|
+
|
26
|
+
VERSION = "0.10.0.beta.0"
|
27
|
+
|
28
|
+
def self.version
|
29
|
+
@rev ||= begin
|
30
|
+
rev = Open3.popen3("git rev-parse HEAD") {|stdin, stdout, stderr| stdout.read }.strip
|
31
|
+
rev.empty? ? nil : " (#{rev})"
|
32
|
+
end
|
33
|
+
"#{VERSION}#@rev"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Daniel DeLeo (<dan@opscode.com>)
|
3
|
+
# Author:: Seth Falcon (<seth@opscode.com>)
|
4
|
+
# Author:: Chris Walters (<cw@opscode.com>)
|
5
|
+
# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
6
|
+
# License:: Apache License, Version 2.0
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'eventmachine'
|
22
|
+
require 'amqp'
|
23
|
+
require 'mq'
|
24
|
+
|
25
|
+
require 'chef/expander/loggable'
|
26
|
+
require 'chef/expander/solrizer'
|
27
|
+
|
28
|
+
module Chef
|
29
|
+
module Expander
|
30
|
+
class VNode
|
31
|
+
include Loggable
|
32
|
+
|
33
|
+
attr_reader :vnode_number
|
34
|
+
|
35
|
+
attr_reader :supervise_interval
|
36
|
+
|
37
|
+
def initialize(vnode_number, supervisor, opts={})
|
38
|
+
@vnode_number = vnode_number.to_i
|
39
|
+
@supervisor = supervisor
|
40
|
+
@queue = nil
|
41
|
+
@stopped = false
|
42
|
+
@supervise_interval = opts[:supervise_interval] || 30
|
43
|
+
end
|
44
|
+
|
45
|
+
def start
|
46
|
+
@supervisor.vnode_added(self)
|
47
|
+
|
48
|
+
subscription_confirmed = Proc.new do
|
49
|
+
abort_on_multiple_subscribe
|
50
|
+
supervise_consumer_count
|
51
|
+
end
|
52
|
+
|
53
|
+
queue.subscribe(:ack => true, :confirm => subscription_confirmed) do |headers, payload|
|
54
|
+
log.debug {"got #{payload} size(#{payload.size} bytes) on queue #{queue_name}"}
|
55
|
+
solrizer = Solrizer.new(payload) { headers.ack }
|
56
|
+
solrizer.run
|
57
|
+
end
|
58
|
+
|
59
|
+
rescue MQ::Error => e
|
60
|
+
log.error {"Failed to start subscriber on #{queue_name} #{e.class.name}: #{e.message}"}
|
61
|
+
end
|
62
|
+
|
63
|
+
def supervise_consumer_count
|
64
|
+
EM.add_periodic_timer(supervise_interval) do
|
65
|
+
abort_on_multiple_subscribe
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def abort_on_multiple_subscribe
|
70
|
+
queue.status do |message_count, subscriber_count|
|
71
|
+
if subscriber_count.to_i > 1
|
72
|
+
log.error { "Detected extra consumers (#{subscriber_count} total) on queue #{queue_name}, cancelling subscription" }
|
73
|
+
stop
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def stop
|
79
|
+
log.debug {"Cancelling subscription on queue #{queue_name.inspect}"}
|
80
|
+
queue.unsubscribe if queue.subscribed?
|
81
|
+
@supervisor.vnode_removed(self)
|
82
|
+
@stopped = true
|
83
|
+
end
|
84
|
+
|
85
|
+
def stopped?
|
86
|
+
@stopped
|
87
|
+
end
|
88
|
+
|
89
|
+
def queue
|
90
|
+
@queue ||= begin
|
91
|
+
log.debug { "declaring queue #{queue_name}" }
|
92
|
+
MQ.queue(queue_name, :passive => false, :durable => true)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def queue_name
|
97
|
+
"vnode-#{@vnode_number}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def control_queue_name
|
101
|
+
"#{queue_name}-control"
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,265 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Daniel DeLeo (<dan@opscode.com>)
|
3
|
+
# Author:: Seth Falcon (<seth@opscode.com>)
|
4
|
+
# Author:: Chris Walters (<cw@opscode.com>)
|
5
|
+
# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
6
|
+
# License:: Apache License, Version 2.0
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'yajl'
|
22
|
+
require 'eventmachine'
|
23
|
+
require 'amqp'
|
24
|
+
require 'mq'
|
25
|
+
require 'chef/expander/version'
|
26
|
+
require 'chef/expander/loggable'
|
27
|
+
require 'chef/expander/node'
|
28
|
+
require 'chef/expander/vnode'
|
29
|
+
require 'chef/expander/vnode_table'
|
30
|
+
require 'chef/expander/configuration'
|
31
|
+
|
32
|
+
module ::AMQP
|
33
|
+
def self.hard_reset!
|
34
|
+
MQ.reset rescue nil
|
35
|
+
stop
|
36
|
+
EM.stop rescue nil
|
37
|
+
Thread.current[:mq], @conn = nil, nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module Chef
|
42
|
+
module Expander
|
43
|
+
class VNodeSupervisor
|
44
|
+
include Loggable
|
45
|
+
extend Loggable
|
46
|
+
|
47
|
+
COULD_NOT_CONNECT = /Could not connect to server/.freeze
|
48
|
+
|
49
|
+
def self.start_cluster_worker
|
50
|
+
@vnode_supervisor = new
|
51
|
+
@original_ppid = Process.ppid
|
52
|
+
trap_signals
|
53
|
+
|
54
|
+
vnodes = Expander.config.vnode_numbers
|
55
|
+
|
56
|
+
$0 = "chef-expander#{Expander.config.ps_tag} worker ##{Expander.config.index} (vnodes #{vnodes.min}-#{vnodes.max})"
|
57
|
+
|
58
|
+
AMQP.start(Expander.config.amqp_config) do
|
59
|
+
start_consumers
|
60
|
+
await_parent_death
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.await_parent_death
|
65
|
+
@awaiting_parent_death = EM.add_periodic_timer(1) do
|
66
|
+
unless Process.ppid == @original_ppid
|
67
|
+
@awaiting_parent_death.cancel
|
68
|
+
stop("master process death")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.start
|
74
|
+
@vnode_supervisor = new
|
75
|
+
trap_signals
|
76
|
+
|
77
|
+
Expander.init_config(ARGV)
|
78
|
+
|
79
|
+
log.info("Chef Search Expander #{Expander.version} starting up.")
|
80
|
+
|
81
|
+
begin
|
82
|
+
AMQP.start(Expander.config.amqp_config) do
|
83
|
+
start_consumers
|
84
|
+
end
|
85
|
+
rescue AMQP::Error => e
|
86
|
+
if e.message =~ COULD_NOT_CONNECT
|
87
|
+
log.error { "Could not connect to rabbitmq. Make sure it is running and correctly configured." }
|
88
|
+
log.error { e.message }
|
89
|
+
|
90
|
+
AMQP.hard_reset!
|
91
|
+
|
92
|
+
sleep 5
|
93
|
+
retry
|
94
|
+
else
|
95
|
+
raise
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.start_consumers
|
101
|
+
log.debug { "Setting prefetch count to 1"}
|
102
|
+
MQ.prefetch(1)
|
103
|
+
|
104
|
+
vnodes = Expander.config.vnode_numbers
|
105
|
+
log.info("Starting Consumers for vnodes #{vnodes.min}-#{vnodes.max}")
|
106
|
+
@vnode_supervisor.start(vnodes)
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.trap_signals
|
110
|
+
Kernel.trap(:INT) { stop_immediately(:INT) }
|
111
|
+
Kernel.trap(:TERM) { stop_gracefully(:TERM) }
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.stop_immediately(signal)
|
115
|
+
log.info { "Initiating immediate shutdown on signal (#{signal})" }
|
116
|
+
@vnode_supervisor.stop
|
117
|
+
EM.add_timer(1) do
|
118
|
+
AMQP.stop
|
119
|
+
EM.stop
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.stop_gracefully(signal)
|
124
|
+
log.info { "Initiating graceful shutdown on signal (#{signal})" }
|
125
|
+
@vnode_supervisor.stop
|
126
|
+
wait_for_http_requests_to_complete
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.wait_for_http_requests_to_complete
|
130
|
+
if Expander::Solrizer.http_requests_active?
|
131
|
+
log.info { "waiting for in progress HTTP Requests to complete"}
|
132
|
+
EM.add_timer(1) do
|
133
|
+
wait_for_http_requests_to_complete
|
134
|
+
end
|
135
|
+
else
|
136
|
+
log.info { "HTTP requests completed, shutting down"}
|
137
|
+
AMQP.stop
|
138
|
+
EM.stop
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
attr_reader :vnode_table
|
143
|
+
|
144
|
+
attr_reader :local_node
|
145
|
+
|
146
|
+
def initialize
|
147
|
+
@vnodes = {}
|
148
|
+
@vnode_table = VNodeTable.new(self)
|
149
|
+
@local_node = Node.local_node
|
150
|
+
@queue_name, @guid = nil, nil
|
151
|
+
end
|
152
|
+
|
153
|
+
def start(vnode_ids)
|
154
|
+
@local_node.start do |message|
|
155
|
+
process_control_message(message)
|
156
|
+
end
|
157
|
+
|
158
|
+
#start_vnode_table_publisher
|
159
|
+
|
160
|
+
Array(vnode_ids).each { |vnode_id| spawn_vnode(vnode_id) }
|
161
|
+
end
|
162
|
+
|
163
|
+
def stop
|
164
|
+
@local_node.stop
|
165
|
+
|
166
|
+
#log.debug { "stopping vnode table updater" }
|
167
|
+
#@vnode_table_publisher.cancel
|
168
|
+
|
169
|
+
log.info { "Stopping VNode queue subscribers"}
|
170
|
+
@vnodes.each do |vnode_number, vnode|
|
171
|
+
log.debug { "Stopping consumer on VNode #{vnode_number}"}
|
172
|
+
vnode.stop
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
def vnode_added(vnode)
|
178
|
+
log.debug { "vnode #{vnode.vnode_number} registered with supervisor" }
|
179
|
+
@vnodes[vnode.vnode_number.to_i] = vnode
|
180
|
+
end
|
181
|
+
|
182
|
+
def vnode_removed(vnode)
|
183
|
+
log.debug { "vnode #{vnode.vnode_number} unregistered from supervisor" }
|
184
|
+
@vnodes.delete(vnode.vnode_number.to_i)
|
185
|
+
end
|
186
|
+
|
187
|
+
def vnodes
|
188
|
+
@vnodes.keys.sort
|
189
|
+
end
|
190
|
+
|
191
|
+
def spawn_vnode(vnode_number)
|
192
|
+
VNode.new(vnode_number, self).start
|
193
|
+
end
|
194
|
+
|
195
|
+
def release_vnode
|
196
|
+
# TODO
|
197
|
+
end
|
198
|
+
|
199
|
+
def process_control_message(message)
|
200
|
+
control_message = parse_symbolic(message)
|
201
|
+
case control_message[:action]
|
202
|
+
when "claim_vnode"
|
203
|
+
spawn_vnode(control_message[:vnode_id])
|
204
|
+
when "recover_vnode"
|
205
|
+
recover_vnode(control_message[:vnode_id])
|
206
|
+
when "release_vnodes"
|
207
|
+
raise "todo"
|
208
|
+
release_vnode()
|
209
|
+
when "update_vnode_table"
|
210
|
+
@vnode_table.update_table(control_message[:data])
|
211
|
+
when "vnode_table_publish"
|
212
|
+
publish_vnode_table
|
213
|
+
when "status"
|
214
|
+
publish_status_to(control_message[:rsvp])
|
215
|
+
when "set_log_level"
|
216
|
+
set_log_level(control_message[:level], control_message[:rsvp])
|
217
|
+
else
|
218
|
+
log.error { "invalid control message #{control_message.inspect}" }
|
219
|
+
end
|
220
|
+
rescue Exception => e
|
221
|
+
log.error { "Error processing a control message."}
|
222
|
+
log.error { "#{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}" }
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
def start_vnode_table_publisher
|
227
|
+
@vnode_table_publisher = EM.add_periodic_timer(10) { publish_vnode_table }
|
228
|
+
end
|
229
|
+
|
230
|
+
def publish_vnode_table
|
231
|
+
status_update = @local_node.to_hash
|
232
|
+
status_update[:vnodes] = vnodes
|
233
|
+
status_update[:update] = :add
|
234
|
+
@local_node.broadcast_message(Yajl::Encoder.encode({:action => :update_vnode_table, :data => status_update}))
|
235
|
+
end
|
236
|
+
|
237
|
+
def publish_status_to(return_queue)
|
238
|
+
status_update = @local_node.to_hash
|
239
|
+
status_update[:vnodes] = vnodes
|
240
|
+
MQ.queue(return_queue).publish(Yajl::Encoder.encode(status_update))
|
241
|
+
end
|
242
|
+
|
243
|
+
def set_log_level(level, rsvp_to)
|
244
|
+
log.info { "setting log level to #{level} due to command from #{rsvp_to}" }
|
245
|
+
new_log_level = (Expander.config.log_level = level.to_sym)
|
246
|
+
reply = {:level => new_log_level, :node => @local_node.to_hash}
|
247
|
+
MQ.queue(rsvp_to).publish(Yajl::Encoder.encode(reply))
|
248
|
+
end
|
249
|
+
|
250
|
+
def recover_vnode(vnode_id)
|
251
|
+
if @vnode_table.local_node_is_leader?
|
252
|
+
log.debug { "Recovering vnode: #{vnode_id}" }
|
253
|
+
@local_node.shared_message(Yajl::Encoder.encode({:action => :claim_vnode, :vnode_id => vnode_id}))
|
254
|
+
else
|
255
|
+
log.debug { "Ignoring :recover_vnode message because this node is not the leader" }
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def parse_symbolic(message)
|
260
|
+
Yajl::Parser.new(:symbolize_keys => true).parse(message)
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Daniel DeLeo (<dan@opscode.com>)
|
3
|
+
# Author:: Seth Falcon (<seth@opscode.com>)
|
4
|
+
# Author:: Chris Walters (<cw@opscode.com>)
|
5
|
+
# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
6
|
+
# License:: Apache License, Version 2.0
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'yajl'
|
22
|
+
require 'chef/expander/node'
|
23
|
+
require 'chef/expander/loggable'
|
24
|
+
|
25
|
+
module Chef
|
26
|
+
module Expander
|
27
|
+
class VNodeTable
|
28
|
+
|
29
|
+
include Loggable
|
30
|
+
|
31
|
+
class InvalidVNodeTableUpdate < ArgumentError; end
|
32
|
+
|
33
|
+
attr_reader :vnodes_by_node
|
34
|
+
|
35
|
+
def initialize(vnode_supervisor)
|
36
|
+
@node_update_mutex = Mutex.new
|
37
|
+
@vnode_supervisor = vnode_supervisor
|
38
|
+
@vnodes_by_node = {}
|
39
|
+
end
|
40
|
+
|
41
|
+
def nodes
|
42
|
+
@vnodes_by_node.keys
|
43
|
+
end
|
44
|
+
|
45
|
+
def update_table(table_update)
|
46
|
+
case table_update[:update]
|
47
|
+
when "add", "update"
|
48
|
+
update_node(table_update)
|
49
|
+
when "remove"
|
50
|
+
remove_node(table_update)
|
51
|
+
else
|
52
|
+
raise InvalidVNodeTableUpdate, "no action or action not acceptable: #{table_update.inspect}"
|
53
|
+
end
|
54
|
+
log.debug { "current vnode table: #{@vnodes_by_node.inspect}" }
|
55
|
+
end
|
56
|
+
|
57
|
+
def update_node(node_info)
|
58
|
+
@node_update_mutex.synchronize do
|
59
|
+
@vnodes_by_node[Node.from_hash(node_info)] = node_info[:vnodes]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def remove_node(node_info)
|
64
|
+
@node_update_mutex.synchronize do
|
65
|
+
@vnodes_by_node.delete(Node.from_hash(node_info))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def leader_node
|
70
|
+
if @vnodes_by_node.empty?
|
71
|
+
nil
|
72
|
+
else
|
73
|
+
Array(@vnodes_by_node).reject { |node| node[1].empty? }.sort { |a,b| a[1].min <=> b[1].min }.first[0]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def local_node_is_leader?
|
78
|
+
(Node.local_node == leader_node) || (@vnodes_by_node[Node.local_node].include?(0))
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Daniel DeLeo (<dan@opscode.com>)
|
3
|
+
# Author:: Seth Falcon (<seth@opscode.com>)
|
4
|
+
# Author:: Chris Walters (<cw@opscode.com>)
|
5
|
+
# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
6
|
+
# License:: Apache License, Version 2.0
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
|
21
|
+
module Chef
|
22
|
+
module Expander
|
23
|
+
|
24
|
+
# VNODES is the number of queues in rabbit that are available for subscribing.
|
25
|
+
# The name comes from riak, where the data ring (160bits) is chunked into
|
26
|
+
# many vnodes; vnodes outnumber physical nodes, so one node hosts several
|
27
|
+
# vnodes. That is the same design we use here.
|
28
|
+
#
|
29
|
+
# See the notes on topic queue benchmarking before adjusting this value.
|
30
|
+
VNODES = 1024
|
31
|
+
|
32
|
+
SHARED_CONTROL_QUEUE_NAME = "chef-search-control--shared"
|
33
|
+
BROADCAST_CONTROL_EXCHANGE_NAME = 'chef-search-control--broadcast'
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Author:: Daniel DeLeo (<dan@opscode.com>)
|
4
|
+
# Author:: Seth Falcon (<seth@opscode.com>)
|
5
|
+
# Author:: Chris Walters (<cw@opscode.com>)
|
6
|
+
# Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
|
7
|
+
# License:: Apache License, Version 2.0
|
8
|
+
#
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
10
|
+
# you may not use this file except in compliance with the License.
|
11
|
+
# You may obtain a copy of the License at
|
12
|
+
#
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
14
|
+
#
|
15
|
+
# Unless required by applicable law or agreed to in writing, software
|
16
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
17
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
18
|
+
# See the License for the specific language governing permissions and
|
19
|
+
# limitations under the License.
|
20
|
+
#
|
21
|
+
|
22
|
+
###############################################################################
|
23
|
+
# check_queue_size
|
24
|
+
# A Nagios Check for Chef Server Queue Backlogs
|
25
|
+
###############################################################################
|
26
|
+
|
27
|
+
require 'rubygems'
|
28
|
+
|
29
|
+
Dir.chdir(File.join(File.expand_path(File.dirname(__FILE__)), "..")) do
|
30
|
+
|
31
|
+
require 'bunny'
|
32
|
+
|
33
|
+
$:.unshift(File.expand_path('./lib'))
|
34
|
+
require 'chef/expander'
|
35
|
+
require 'chef/expander/version'
|
36
|
+
require 'chef/expander/configuration'
|
37
|
+
|
38
|
+
include Chef
|
39
|
+
|
40
|
+
Expander.init_config([])
|
41
|
+
|
42
|
+
config = {:warn => 100, :crit => 200}
|
43
|
+
|
44
|
+
option_parser = OptionParser.new do |o|
|
45
|
+
o.banner = "Usage: check_queue_size [options]"
|
46
|
+
|
47
|
+
o.on('-w', '--warn WARN_THRESHOLD', 'number of messages to trigger a warning') do |i|
|
48
|
+
config[:warn] = i.to_i
|
49
|
+
end
|
50
|
+
|
51
|
+
o.on('-c', '--critical CRITICAL_THRESHOLD', 'the number of messages to trigger a critical') do |n|
|
52
|
+
config[:crit] = n.to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
o.on_tail('-h', '--help', 'show this message') do
|
56
|
+
puts "chef-expander #{Expander.version}"
|
57
|
+
puts "queue size monitor"
|
58
|
+
puts ''
|
59
|
+
puts o
|
60
|
+
exit 127
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
option_parser.parse!(ARGV.dup)
|
65
|
+
|
66
|
+
message_counts = []
|
67
|
+
|
68
|
+
begin
|
69
|
+
amqp_client = Bunny.new(Expander.config.amqp_config)
|
70
|
+
amqp_client.start
|
71
|
+
|
72
|
+
0.upto(Expander::VNODES - 1) do |vnode|
|
73
|
+
q = amqp_client.queue("vnode-#{vnode}", :durable => true)
|
74
|
+
message_counts << q.status[:message_count]
|
75
|
+
end
|
76
|
+
total_messages = message_counts.inject(:+)
|
77
|
+
|
78
|
+
if total_messages >= config[:crit]
|
79
|
+
puts "Chef Expander Queue Size CRITICAL - messages: #{total_messages}"
|
80
|
+
exit(2)
|
81
|
+
elsif total_messages >= config[:warn]
|
82
|
+
puts "Chef Expander Queue Size WARNING - messages: #{total_messages}"
|
83
|
+
exit(1)
|
84
|
+
else
|
85
|
+
puts "Chef Expander Queue Size OK - messages: #{total_messages}"
|
86
|
+
exit(0)
|
87
|
+
end
|
88
|
+
|
89
|
+
ensure
|
90
|
+
amqp_client.stop if defined?(amqp_client) && amqp_client && amqp_client.connected?
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'munin_plugin'
|
5
|
+
Dir.chdir(File.join(File.expand_path(File.dirname(__FILE__)), "..")) do
|
6
|
+
require 'bunny'
|
7
|
+
|
8
|
+
$:.unshift(File.expand_path('../../lib', __FILE__))
|
9
|
+
require 'chef/expander'
|
10
|
+
require 'chef/expander/version'
|
11
|
+
require 'chef/expander/configuration'
|
12
|
+
|
13
|
+
include Chef
|
14
|
+
|
15
|
+
munin_plugin do
|
16
|
+
graph_title "Expander Queue Size"
|
17
|
+
graph_vlabel "Events"
|
18
|
+
graph_category "Opscode"
|
19
|
+
graph_info "Events in the Expander Queue waiting to be consumed by Solr."
|
20
|
+
queuesize.label "events"
|
21
|
+
queuesize.draw "LINE"
|
22
|
+
queuesize.warning "100"
|
23
|
+
queuesize.critical "200"
|
24
|
+
|
25
|
+
collect do
|
26
|
+
|
27
|
+
Expander.init_config([])
|
28
|
+
|
29
|
+
message_counts = []
|
30
|
+
|
31
|
+
begin
|
32
|
+
amqp_client = Bunny.new(Expander.config.amqp_config)
|
33
|
+
amqp_client.start
|
34
|
+
|
35
|
+
0.upto(Expander::VNODES - 1) do |vnode|
|
36
|
+
q = amqp_client.queue("vnode-#{vnode}", :durable => true)
|
37
|
+
message_counts << q.status[:message_count]
|
38
|
+
end
|
39
|
+
total_messages = message_counts.inject(:+)
|
40
|
+
|
41
|
+
ensure
|
42
|
+
amqp_client.stop if defined?(amqp_client) && amqp_client && amqp_client.connected?
|
43
|
+
end
|
44
|
+
|
45
|
+
queuesize.value total_messages
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|