change rabbitmqctl to http api

rabbitmqctl fail to spawn in python plugin environment somehow so
switch to http api to get metrics from rabbitmq.

Change-Id: Iff4b8a67a7d113bb664883057118897672503a8c
This commit is contained in:
Jerry Zhao 2014-08-21 14:52:22 -07:00
parent 1349af2356
commit 9b6bfcb921
6 changed files with 102 additions and 29 deletions
chef
cookbooks
collectd
openstack-common/recipes
databags/openstack

@ -44,4 +44,5 @@ default[:collectd][:plugins] = {"cpu"=>{},
default[:collectd][:included_plugins] = {"kairosdb"=>{}} default[:collectd][:included_plugins] = {"kairosdb"=>{}}
default[:collectd][:server][:host] = "10.145.81.250" default[:collectd][:server][:host] = "10.145.81.250"
default[:collectd][:server][:port] = "4242" default[:collectd][:server][:port] = "4242"
default[:collectd][:server][:protocol] = "tcp" default[:collectd][:server][:protocol] = "udp"
default[:collectd][:mq][:vhost] = "/"

@ -1,9 +1,10 @@
# Name: rabbitmq-collectd-plugin - rabbitmq_info.py # Name: rabbitmq-collectd-plugin - rabbitmq_info.py
# Author: https://github.com/phrawzty/rabbitmq-collectd-plugin/commits/master # Author: https://github.com/phrawzty/rabbitmq-collectd-plugin/commits/master
# Description: This plugin uses Collectd's Python plugin to obtain RabbitMQ metrics. # Description: This plugin uses Collectd's Python plugin to obtain RabbitMQ
# metrics.
# #
# Copyright 2012 Daniel Maher # Copyright 2012 Daniel Maher
# # Copyright 2014 Xinyu Zhao
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # You may obtain a copy of the License at
@ -16,26 +17,27 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
## copied from https://github.com/phrawzty/rabbitmq-collectd-plugin
import collectd import collectd
import subprocess import subprocess
import re import re
import requests
NAME = 'rabbitmq_info' NAME = 'rabbitmq_info'
# Override in config by specifying 'RmqcBin'. # Override in config by specifying 'RmqcBin'.
RABBITMQCTL_BIN = '/usr/sbin/rabbitmqctl' RABBITMQCTL_BIN = '/usr/sbin/rabbitmqctl'
RABBITMQ_API = 'http://localhost:15672/api/queues'
# Override in config by specifying 'PmapBin' # Override in config by specifying 'PmapBin'
PMAP_BIN = '/usr/bin/pmap' PMAP_BIN = '/usr/bin/pmap'
# Override in config by specifying 'PidofBin'. # Override in config by specifying 'PidofBin'.
PIDOF_BIN = '/bin/pidof' PIDOF_BIN = '/bin/pidof'
# Override in config by specifying 'PidFile.
PID_FILE = "/var/run/rabbitmq/pid"
# Override in config by specifying 'Vhost'.
VHOST = "/"
# Override in config by specifying 'Verbose'. # Override in config by specifying 'Verbose'.
VERBOSE_LOGGING = False VERBOSE_LOGGING = False
USER = 'guest'
# Wasn't specified for some reason... PASS = 'guest'
PID_FILE = '/var/run/rabbitmq/pid'
# Obtain the interesting statistical info # Obtain the interesting statistical info
@ -48,34 +50,62 @@ def get_stats():
stats['pmap_used'] = 0 stats['pmap_used'] = 0
stats['pmap_shared'] = 0 stats['pmap_shared'] = 0
# call rabbitmqctl # call http api instead of rabbitmqctl to collect statistics due to issue:
# https://github.com/phrawzty/rabbitmq-collectd-plugin/issues/5
try: try:
p = subprocess.Popen([RABBITMQCTL_BIN, '-q', 'list_queues', 'messages', 'memory', 'consumers'], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) r = requests.get('%s/%s' % (RABBITMQ_API, VHOST),
auth=('%s' % USER, '%s' % PASS))
# p = subprocess.Popen([RABBITMQCTL_BIN, '-q', '-p', VHOST,
# 'list_queues', 'name', 'messages', 'memory', 'consumers'],
# shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
except: except:
logger('err', 'Failed to run %s' % RABBITMQCTL_BIN) logger('err', 'Failed to run curl %s/%s' % (RABBITMQ_API, VHOST))
return None return None
for line in p.stdout.readlines(): # for line in p.stdout.readlines():
if re.match('\d', line): # ctl_stats = line.split()
ctl_stats = line.split() # try:
stats['ctl_messages'] += int(ctl_stats[0]) # ctl_stats[1] = int(ctl_stats[1])
stats['ctl_memory'] += int(ctl_stats[1]) # ctl_stats[2] = int(ctl_stats[2])
stats['ctl_consumers'] += int(ctl_stats[2]) # ctl_stats[3] = int(ctl_stats[3])
# except:
# continue
# queue_name = ctl_stats[0]
# stats['ctl_messages'] += ctl_stats[1]
# stats['ctl_memory'] += ctl_stats[2]
# stats['ctl_consumers'] += ctl_stats[3]
# stats['ctl_messages_%s' % queue_name] = ctl_stats[1]
# stats['ctl_memory_%s' % queue_name] = ctl_stats[2]
# stats['ctl_consumers_%s' % queue_name] = ctl_stats[3]
try:
resp = r.json()
except:
logger('err', 'No result found for this vhost')
return None
for i in resp:
if "messages" in i:
stats['ctl_messages'] += i['messages']
stats['ctl_memory'] += i['memory']
stats['ctl_consumers'] += i['consumers']
stats['ctl_messages_%s' % i['name']] = i['messages']
stats['ctl_memory_%s' % i['name']] = i['memory']
stats['ctl_consumers_%s' % i['name']] = i['consumers']
if not stats['ctl_memory'] > 0: if not stats['ctl_memory'] > 0:
logger('warn', '%s reports 0 memory usage. This is probably incorrect.' % RABBITMQCTL_BIN) logger('warn', '%s reports 0 memory usage. This is probably incorrect.'
% RABBITMQ_API)
# get the pid of rabbitmq # get the pid of rabbitmq
try: try:
with open(PID_FILE, 'r') as f: with open(PID_FILE, 'r') as f:
pid = f.read().strip() pid = f.read().strip()
except: except:
logger('err', 'Unable to read %s' % PID_FILE) logger('err', 'Unable to read %s' % PID_FILE)
return None return None
# use pmap to get proper memory stats # use pmap to get proper memory stats
try: try:
p = subprocess.Popen([PMAP_BIN, '-d', pid], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p = subprocess.Popen([PMAP_BIN, '-d', pid], shell=False,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
except: except:
logger('err', 'Failed to run %s' % PMAP_BIN) logger('err', 'Failed to run %s' % PMAP_BIN)
return None return None
@ -91,8 +121,11 @@ def get_stats():
return None return None
# Verbose output # Verbose output
logger('verb', '[rmqctl] Messages: %i, Memory: %i, Consumers: %i' % (stats['ctl_messages'], stats['ctl_memory'], stats['ctl_consumers'])) logger('verb', '[rmqctl] Messages: %i, Memory: %i, Consumers: %i' %
logger('verb', '[pmap] Mapped: %i, Used: %i, Shared: %i' % (stats['pmap_mapped'], stats['pmap_used'], stats['pmap_shared'])) (stats['ctl_messages'], stats['ctl_memory'],
stats['ctl_consumers']))
logger('verb', '[pmap] Mapped: %i, Used: %i, Shared: %i' %
(stats['pmap_mapped'], stats['pmap_used'], stats['pmap_shared']))
return stats return stats
@ -100,6 +133,7 @@ def get_stats():
# Config data from collectd # Config data from collectd
def configure_callback(conf): def configure_callback(conf):
global RABBITMQCTL_BIN, PMAP_BIN, PID_FILE, VERBOSE_LOGGING global RABBITMQCTL_BIN, PMAP_BIN, PID_FILE, VERBOSE_LOGGING
global VHOST, RABBITMQ_API, USER, PASS
for node in conf.children: for node in conf.children:
if node.key == 'RmqcBin': if node.key == 'RmqcBin':
RABBITMQCTL_BIN = node.values[0] RABBITMQCTL_BIN = node.values[0]
@ -109,6 +143,14 @@ def configure_callback(conf):
PID_FILE = node.values[0] PID_FILE = node.values[0]
elif node.key == 'Verbose': elif node.key == 'Verbose':
VERBOSE_LOGGING = bool(node.values[0]) VERBOSE_LOGGING = bool(node.values[0])
elif node.key == 'Vhost':
VHOST = node.values[0]
elif node.key == 'User':
USER = node.values[0]
elif node.key == 'Pass':
PASS = node.values[0]
elif node.key == 'Api':
RABBITMQ_API == node.values[0]
else: else:
logger('warn', 'Unknown config key: %s' % node.key) logger('warn', 'Unknown config key: %s' % node.key)
@ -140,7 +182,7 @@ def logger(t, msg):
collectd.error('%s: %s' % (NAME, msg)) collectd.error('%s: %s' % (NAME, msg))
if t == 'warn': if t == 'warn':
collectd.warning('%s: %s' % (NAME, msg)) collectd.warning('%s: %s' % (NAME, msg))
elif t == 'verb' and VERBOSE_LOGGING == True: elif t == 'verb' and VERBOSE_LOGGING is True:
collectd.info('%s: %s' % (NAME, msg)) collectd.info('%s: %s' % (NAME, msg))

@ -22,6 +22,7 @@ cookbook_file "#{node['collectd']['plugin_dir']}/kairosdb_writer.py" do
group "root" group "root"
mode 00644 mode 00644
action :create_if_missing action :create_if_missing
notifies :restart, resources(:service => "collectd")
end end
if ! node['cluster'] if ! node['cluster']

@ -17,12 +17,40 @@
# limitations under the License. # limitations under the License.
# #
defaultbag = "openstack"
if !Chef::DataBag.list.key?(defaultbag)
Chef::Application.fatal!("databag '#{defaultbag}' doesn't exist.")
return
end
myitem = node.attribute?('cluster')? node['cluster']:"env_default"
if !search(defaultbag, "id:#{myitem}")
Chef::Application.fatal!("databagitem '#{myitem}' doesn't exist.")
return
end
package "python-requests" do
action :install
end
mydata = data_bag_item(defaultbag, myitem)
cookbook_file File.join(node['collectd']['plugin_dir'], "rabbitmq_info.py") do cookbook_file File.join(node['collectd']['plugin_dir'], "rabbitmq_info.py") do
source "rabbitmq_info.py" source "rabbitmq_info.py"
owner "root" owner "root"
group "root" group "root"
mode "0755" mode "0755"
notifies :restart, resources(:service => "collectd")
end end
collectd_python_plugin "rabbitmq_info" node.override["collectd"]["mq"]["vhost"] = mydata["mq"]["rabbitmq"]["vhost"]
collectd_python_plugin "rabbitmq_info" do
opts = { "Vhost" => node["collectd"]["mq"]["vhost"],
"Api" => "http://localhost:15672/api/queues",
"User" => "#{mydata["credential"]["mq"]["rabbitmq"]["username"]}",
"Pass" => "#{mydata["credential"]["mq"]["rabbitmq"]["password"]}"
}
options(opts)
end

@ -244,7 +244,7 @@ node.override['openstack']['mq']['bind_address'] = mydata['mq']["#{node['opensta
node.override['openstack']['mq']['port'] = mydata['mq']["#{node['openstack']['mq']['service_type']}"]['port'] node.override['openstack']['mq']['port'] = mydata['mq']["#{node['openstack']['mq']['service_type']}"]['port']
node.override['openstack']['mq']['user'] = mydata['credential']['mq']["#{node['openstack']['mq']['service_type']}"]['username'] node.override['openstack']['mq']['user'] = mydata['credential']['mq']["#{node['openstack']['mq']['service_type']}"]['username']
node.override['openstack']['mq']['password'] = mydata['credential']['mq']["#{node['openstack']['mq']['service_type']}"]['password'] node.override['openstack']['mq']['password'] = mydata['credential']['mq']["#{node['openstack']['mq']['service_type']}"]['password']
#node.override['openstack']['mq']['vhost'] = "/" node.override['openstack']['mq']['vhost'] = mydata['mq']["#{node['openstack']['mq']['service_type']}"]['vhost']

@ -112,7 +112,8 @@
}, },
"metadata" : { "password" : "Hello_Openstack" }, "metadata" : { "password" : "Hello_Openstack" },
"mq" : { "rabbitmq" : { "password" : "guest", "mq" : { "rabbitmq" : { "password" : "guest",
"username" : "guest" "username" : "guest",
"vhost" : "/"
} }, } },
"mysql" : { "compute" : { "password" : "admin", "mysql" : { "compute" : { "password" : "admin",
"username" : "nova" "username" : "nova"