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.
@@ -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