Andrew Bogott has uploaded a new change for review.
https://gerrit.wikimedia.org/r/299193
Change subject: Add diamond collector for rabbitmq stats
......................................................................
Add diamond collector for rabbitmq stats
Change-Id: I450c088980804ff5ef58e71f204a08b52abb1fd6
---
A modules/openstack/files/rabbitmq/rabbitmq.py
M modules/openstack/manifests/queue_server.pp
M modules/role/manifests/labs/openstack/nova.pp
3 files changed, 282 insertions(+), 3 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/operations/puppet
refs/changes/93/299193/1
diff --git a/modules/openstack/files/rabbitmq/rabbitmq.py
b/modules/openstack/files/rabbitmq/rabbitmq.py
new file mode 100644
index 0000000..76c6aa0
--- /dev/null
+++ b/modules/openstack/files/rabbitmq/rabbitmq.py
@@ -0,0 +1,264 @@
+# coding=utf-8
+
+"""
+Collects data from RabbitMQ through the admin interface
+
+#### Notes
+ ** With added support for breaking down queue metrics by vhost, we have
+ attempted to keep results generated by existing configurations from
+ changing. This means that the old behaviour of clobbering queue metrics
+ when a single queue name exists in multiple vhosts still exists if the
+ configuration is not updated. If no vhosts block is defined it will also
+ keep the metric path as it was historically with no vhost name in it.
+
+ old path => systems.myServer.rabbitmq.queues.myQueue.*
+ new path => systems.myServer.rabbitmq.myVhost.queues.myQueue.*
+
+ ** If a [vhosts] section exists but is empty, then no queues will be polled.
+ ** To poll all vhosts and all queues, add the following.
+ ** [vhosts]
+ ** * = *
+ **
+"""
+
+import diamond.collector
+import re
+from urlparse import urljoin
+from urllib import quote
+import urllib2
+from base64 import b64encode
+
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
+
+class RabbitMQClient(object):
+ """
+ Tiny interface into the rabbit http api
+ """
+
+ def __init__(self, host, user, password, timeout=5, scheme="http"):
+ self.base_url = '%s://%s/api/' % (scheme, host)
+ self.timeout = timeout
+ self._authorization = 'Basic ' + b64encode('%s:%s' % (user, password))
+
+ def do_call(self, path):
+ url = urljoin(self.base_url, path)
+ req = urllib2.Request(url)
+ req.add_header('Authorization', self._authorization)
+ return json.load(urllib2.urlopen(req, timeout=self.timeout))
+
+ def get_all_vhosts(self):
+ return self.do_call('vhosts')
+
+ def get_vhost_names(self):
+ return [i['name'] for i in self.get_all_vhosts()]
+
+ def get_queues(self, vhost=None):
+ path = 'queues'
+ if vhost:
+ vhost = quote(vhost, '')
+ path += '/%s' % vhost
+
+ queues = self.do_call(path)
+ return queues or []
+
+ def get_overview(self):
+ return self.do_call('overview')
+
+ def get_nodes(self):
+ return self.do_call('nodes')
+
+ def get_node(self, node):
+ return self.do_call('nodes/%s' % node)
+
+
+class RabbitMQCollector(diamond.collector.Collector):
+
+ def get_default_config_help(self):
+ config_help = super(RabbitMQCollector, self).get_default_config_help()
+ config_help.update({
+ 'host': 'Hostname and port to collect from',
+ 'user': 'Username',
+ 'password': 'Password',
+ 'replace_dot':
+ 'A value to replace dot in queue names and vhosts names by',
+ 'replace_slash':
+ 'A value to replace a slash in queue names and vhosts names by',
+ 'queues': 'Queues to publish. Leave empty to publish all.',
+ 'vhosts':
+ 'A list of vhosts and queues for which we want to collect',
+ 'queues_ignored':
+ 'A list of queues or regexes for queue names not to report on.',
+ 'cluster':
+ 'If this node is part of a cluster, will collect metrics on the'
+ ' cluster health'
+ })
+ return config_help
+
+ def get_default_config(self):
+ """
+ Returns the default collector settings
+ """
+ config = super(RabbitMQCollector, self).get_default_config()
+ config.update({
+ 'path': 'rabbitmq',
+ 'host': 'localhost:55672',
+ 'user': 'guest',
+ 'password': 'guest',
+ 'replace_dot': False,
+ 'replace_slash': False,
+ 'queues_ignored': '',
+ 'cluster': False,
+ 'scheme': 'http',
+ })
+ return config
+
+ def collect_health(self):
+ health_metrics = [
+ 'fd_used',
+ 'fd_total',
+ 'mem_used',
+ 'mem_limit',
+ 'sockets_used',
+ 'sockets_total',
+ 'disk_free_limit',
+ 'disk_free',
+ 'proc_used',
+ 'proc_total',
+ ]
+ try:
+ client = RabbitMQClient(self.config['host'],
+ self.config['user'],
+ self.config['password'],
+ scheme=self.config['scheme'])
+ node_name = client.get_overview()['node']
+ node_data = client.get_node(node_name)
+ for metric in health_metrics:
+ self.publish('health.{0}'.format(metric), node_data[metric])
+ if self.config['cluster']:
+ self.publish('cluster.partitions',
+ len(node_data['partitions']))
+ content = client.get_nodes()
+ self.publish('cluster.nodes', len(content))
+ except Exception, e:
+ self.log.error('Couldnt connect to rabbitmq %s', e)
+ return {}
+
+ def collect(self):
+ self.collect_health()
+ matchers = []
+ if self.config['queues_ignored']:
+ for reg in self.config['queues_ignored'].split():
+ matchers.append(re.compile(reg))
+ try:
+ client = RabbitMQClient(self.config['host'],
+ self.config['user'],
+ self.config['password'],
+ scheme=self.config['scheme'])
+
+ legacy = False
+
+ if 'vhosts' not in self.config:
+ legacy = True
+
+ if 'queues' in self.config:
+ vhost_conf = {"*": self.config['queues']}
+ else:
+ vhost_conf = {"*": ""}
+
+ # Legacy configurations, those that don't include the [vhosts]
+ # section require special care so that we do not break metric
+ # gathering for people that were using this collector before the
+ # update to support vhosts.
+
+ if not legacy:
+ vhost_names = client.get_vhost_names()
+ if "*" in self.config['vhosts']:
+ for vhost in vhost_names:
+ # Copy the glob queue list to each vhost not
+ # specifically defined in the configuration.
+ if vhost not in self.config['vhosts']:
+ self.config['vhosts'][vhost] = self.config[
+ 'vhosts']['*']
+
+ del self.config['vhosts']["*"]
+ vhost_conf = self.config['vhosts']
+
+ # Iterate all vhosts in our vhosts configuration. For legacy this
+ # is "*" to force a single run.
+ for vhost in vhost_conf:
+ vhost_name = vhost
+ if self.config['replace_dot']:
+ vhost_name = vhost_name.replace(
+ '.', self.config['replace_dot'])
+
+ if self.config['replace_slash']:
+ vhost_name = vhost_name.replace(
+ '/', self.config['replace_slash'])
+
+ queues = vhost_conf[vhost]
+
+ # Allow the use of a asterix to glob the queues, but replace
+ # with a empty string to match how legacy config was.
+ if queues == "*":
+ queues = ""
+ allowed_queues = queues.split()
+
+ # When we fetch queues, we do not want to define a vhost if
+ # legacy.
+ if legacy:
+ vhost = None
+
+ for queue in client.get_queues(vhost):
+ # If queues are defined and it doesn't match, then skip.
+ if ((queue['name'] not in allowed_queues and
+ len(allowed_queues) > 0)):
+ continue
+ if matchers and any(
+ [m.match(queue['name']) for m in matchers]):
+ continue
+ for key in queue:
+ prefix = "queues"
+ if not legacy:
+ prefix = "vhosts.%s.%s" % (vhost_name, "queues")
+
+ queue_name = queue['name']
+ if self.config['replace_dot']:
+ queue_name = queue_name.replace(
+ '.', self.config['replace_dot'])
+
+ if self.config['replace_slash']:
+ queue_name = queue_name.replace(
+ '/', self.config['replace_slash'])
+
+ name = '{0}.{1}'.format(prefix, queue_name)
+
+ self._publish_metrics(name, [], key, queue)
+
+ overview = client.get_overview()
+ for key in overview:
+ self._publish_metrics('', [], key, overview)
+ except Exception, e:
+ self.log.error('An error occurred collecting from RabbitMQ, %s', e)
+ return {}
+
+ def _publish_metrics(self, name, prev_keys, key, data):
+ """Recursively publish keys"""
+ value = data[key]
+ keys = prev_keys + [key]
+ if isinstance(value, dict):
+ for new_key in value:
+ self._publish_metrics(name, keys, new_key, value)
+ elif isinstance(value, (float, int, long)):
+ joined_keys = '.'.join(keys)
+ if name:
+ publish_key = '{0}.{1}'.format(name, joined_keys)
+ else:
+ publish_key = joined_keys
+ if isinstance(value, bool):
+ value = int(value)
+
+ self.publish(publish_key, value)
diff --git a/modules/openstack/manifests/queue_server.pp
b/modules/openstack/manifests/queue_server.pp
index b9503ce..f8611c8 100644
--- a/modules/openstack/manifests/queue_server.pp
+++ b/modules/openstack/manifests/queue_server.pp
@@ -1,5 +1,8 @@
# sets up rabbitmq on the nova controller
-class openstack::queue_server {
+class openstack::queue_server(
+ $rabbit_username,
+ $rabbit_password
+ )}
include openstack::repo
@@ -28,4 +31,13 @@
require => Package['rabbitmq-server'];
}
}
+
+ diamond::collector { 'rabbitmq':
+ settings => {
+ 'host' => 'localhost:15672',
+ 'user' => $rabbit_username,
+ 'password' => $rabbit_password,
+ }
+ source => 'puppet:///modules/openstack/rabbitmq/rabbitmq.py',
+ }
}
diff --git a/modules/role/manifests/labs/openstack/nova.pp
b/modules/role/manifests/labs/openstack/nova.pp
index 59f5891..17f3a90 100644
--- a/modules/role/manifests/labs/openstack/nova.pp
+++ b/modules/role/manifests/labs/openstack/nova.pp
@@ -138,12 +138,15 @@
include role::labs::openstack::keystone::server
include ::openstack::nova::conductor
include ::openstack::nova::scheduler
- include ::openstack::queue_server
-
include role::labs::openstack::nova::common
$novaconfig = $role::labs::openstack::nova::common::novaconfig
$designateconfig = hiera_hash('designateconfig', {})
+ class { ' ::openstack::queue_server':
+ rabbit_username => $novaconfig['rabbit_user'],
+ rabbit_password => $novaconfig['rabbit_pass'],
+ }
+
class { '::openstack::adminscripts':
novaconfig => $novaconfig
}
--
To view, visit https://gerrit.wikimedia.org/r/299193
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I450c088980804ff5ef58e71f204a08b52abb1fd6
Gerrit-PatchSet: 1
Gerrit-Project: operations/puppet
Gerrit-Branch: production
Gerrit-Owner: Andrew Bogott <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits