Rework collectd plugins for OpenStack
* Split out object/workers stats collection for Nova, Cinder and Neutron plugins * Use the common interface exposed by collectd_base.Base Change-Id: I59f698b8f09fd0d3ce375327d9e4d81d767d961c
This commit is contained in:
parent
ec842363e0
commit
7c248af9fa
|
@ -162,11 +162,14 @@ if hiera('lma::collector::influxdb::server', false) {
|
|||
require => Class['lma_collector::collectd::base'],
|
||||
}
|
||||
$openstack_services = {
|
||||
'nova' => $openstack_service_config,
|
||||
'cinder' => $openstack_service_config,
|
||||
'glance' => $openstack_service_config,
|
||||
'keystone' => $openstack_service_config,
|
||||
'neutron' => $openstack_service_config,
|
||||
'nova' => $openstack_service_config,
|
||||
'nova_services' => $openstack_service_config,
|
||||
'cinder' => $openstack_service_config,
|
||||
'cinder_services' => $openstack_service_config,
|
||||
'glance' => $openstack_service_config,
|
||||
'keystone' => $openstack_service_config,
|
||||
'neutron' => $openstack_service_config,
|
||||
'neutron_agents' => $openstack_service_config,
|
||||
}
|
||||
create_resources(lma_collector::collectd::openstack, $openstack_services)
|
||||
|
||||
|
|
|
@ -49,6 +49,11 @@ class APICheckPlugin(openstack.CollectdPlugin):
|
|||
'path': 'healthcheck', 'expect': [200], 'name': 'swift-s3-api'},
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(APICheckPlugin, self).__init__(*args, **kwargs)
|
||||
self.plugin = PLUGIN_NAME
|
||||
self.interval = INTERVAL
|
||||
|
||||
def _service_url(self, endpoint, path):
|
||||
url = urlparse(endpoint)
|
||||
u = '%s://%s' % (url.scheme, url.netloc)
|
||||
|
@ -94,21 +99,15 @@ class APICheckPlugin(openstack.CollectdPlugin):
|
|||
'region': service['region']
|
||||
}
|
||||
|
||||
def collect(self):
|
||||
def itermetrics(self):
|
||||
for item in self.check_api():
|
||||
if item['status'] == self.UNKNOWN:
|
||||
if item['status'] != self.UNKNOWN:
|
||||
# skip if status is UNKNOWN
|
||||
continue
|
||||
|
||||
value = collectd.Values(
|
||||
plugin=PLUGIN_NAME,
|
||||
plugin_instance=item['service'],
|
||||
type='gauge',
|
||||
interval=INTERVAL,
|
||||
values=[item['status']],
|
||||
meta={'region': item['region']}
|
||||
)
|
||||
value.dispatch()
|
||||
yield {
|
||||
'plugin_instance': item['service'],
|
||||
'values': item['status'],
|
||||
'meta': {'region': item['region']},
|
||||
}
|
||||
|
||||
|
||||
plugin = APICheckPlugin(collectd, PLUGIN_NAME)
|
||||
|
|
|
@ -141,11 +141,12 @@ class Base(object):
|
|||
(self.plugin, type_instance[:24], len(type_instance),
|
||||
self.MAX_IDENTIFIER_LENGTH))
|
||||
|
||||
plugin_instance = metric.get('plugin_instance', self.plugin_instance)
|
||||
v = self.collectd.Values(
|
||||
plugin=self.plugin,
|
||||
host=metric.get('hostname', ''),
|
||||
type=metric.get('type', 'gauge'),
|
||||
plugin_instance=self.plugin_instance,
|
||||
plugin_instance=plugin_instance,
|
||||
type_instance=type_instance,
|
||||
values=values,
|
||||
# w/a for https://github.com/collectd/collectd/issues/716
|
||||
|
|
|
@ -18,7 +18,6 @@ import dateutil.parser
|
|||
import dateutil.tz
|
||||
import requests
|
||||
import simplejson as json
|
||||
import traceback
|
||||
|
||||
import collectd_base as base
|
||||
|
||||
|
@ -276,28 +275,6 @@ class CollectdPlugin(base.Base):
|
|||
keystone_url, self.timeout, self.logger,
|
||||
self.max_retries)
|
||||
|
||||
def read_callback(self):
|
||||
""" Wrapper method
|
||||
|
||||
This method calls the actual method which performs
|
||||
collection.
|
||||
"""
|
||||
|
||||
try:
|
||||
self.collect()
|
||||
except Exception as e:
|
||||
msg = '{}: fail to get metrics: {}: {}'.format(
|
||||
self.service_name or self.plugin, e, traceback.format_exc())
|
||||
self.logger.error(msg)
|
||||
|
||||
def collect(self):
|
||||
""" Read metrics and dispatch values
|
||||
|
||||
This method should be overriden by the derived classes.
|
||||
"""
|
||||
|
||||
raise 'collect() method needs to be overriden!'
|
||||
|
||||
def get_objects(self, project, object_name, api_version='',
|
||||
params='all_tenants=1'):
|
||||
""" Return a list of OpenStack objects
|
||||
|
|
|
@ -34,6 +34,11 @@ class HypervisorStatsPlugin(openstack.CollectdPlugin):
|
|||
'vcpus_used': 'used_vcpus',
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HypervisorStatsPlugin, self).__init__(*args, **kwargs)
|
||||
self.plugin = PLUGIN_NAME
|
||||
self.interval = INTERVAL
|
||||
|
||||
def config_callback(self, config):
|
||||
super(HypervisorStatsPlugin, self).config_callback(config)
|
||||
for node in config.children:
|
||||
|
@ -42,19 +47,7 @@ class HypervisorStatsPlugin(openstack.CollectdPlugin):
|
|||
if 'cpu_ratio' not in self.extra_config:
|
||||
self.logger.warning('CpuAllocationRatio parameter not set')
|
||||
|
||||
def dispatch_value(self, name, value, meta=None):
|
||||
v = collectd.Values(
|
||||
plugin=PLUGIN_NAME,
|
||||
type='gauge',
|
||||
type_instance=name,
|
||||
interval=INTERVAL,
|
||||
# w/a for https://github.com/collectd/collectd/issues/716
|
||||
meta=meta or {'0': True},
|
||||
values=[value]
|
||||
)
|
||||
v.dispatch()
|
||||
|
||||
def collect(self):
|
||||
def itermetrics(self):
|
||||
nova_aggregates = {}
|
||||
r = self.get('nova', 'os-aggregates')
|
||||
if not r:
|
||||
|
@ -84,7 +77,11 @@ class HypervisorStatsPlugin(openstack.CollectdPlugin):
|
|||
host = stats['hypervisor_hostname'].split('.')[0]
|
||||
for k, v in self.VALUE_MAP.iteritems():
|
||||
m_val = stats.get(k, 0)
|
||||
self.dispatch_value(v, m_val, {'host': host})
|
||||
yield {
|
||||
'type_instance': v,
|
||||
'values': m_val,
|
||||
'meta': {'host': host},
|
||||
}
|
||||
total_stats[v] += m_val
|
||||
for agg in nova_aggregates.keys():
|
||||
agg_hosts = nova_aggregates[agg]['hosts']
|
||||
|
@ -95,7 +92,11 @@ class HypervisorStatsPlugin(openstack.CollectdPlugin):
|
|||
m_vcpus_used = stats.get('vcpus_used', 0)
|
||||
free = (int(self.extra_config['cpu_ratio'] *
|
||||
m_vcpus)) - m_vcpus_used
|
||||
self.dispatch_value('free_vcpus', free, {'host': host})
|
||||
yield {
|
||||
'type_instance': 'free_vcpus',
|
||||
'values': free,
|
||||
'meta': {'host': host},
|
||||
}
|
||||
total_stats['free_vcpus'] += free
|
||||
for agg in nova_aggregates.keys():
|
||||
agg_hosts = nova_aggregates[agg]['hosts']
|
||||
|
@ -122,12 +123,20 @@ class HypervisorStatsPlugin(openstack.CollectdPlugin):
|
|||
agg_total_free_ram,
|
||||
2)
|
||||
for k, v in nova_aggregates[agg]['metrics'].iteritems():
|
||||
self.dispatch_value('aggregate_{}'.format(k), v,
|
||||
{'aggregate': agg,
|
||||
'aggregate_id': agg_id})
|
||||
yield {
|
||||
'type_instance': 'aggregate_{}'.format(k),
|
||||
'values': v,
|
||||
'meta': {
|
||||
'aggregate': agg,
|
||||
'aggregate_id': agg_id,
|
||||
}
|
||||
}
|
||||
# Dispatch the global metrics
|
||||
for k, v in total_stats.iteritems():
|
||||
self.dispatch_value('total_{}'.format(k), v)
|
||||
yield {
|
||||
'type_instance': 'total_{}'.format(k),
|
||||
'values': v,
|
||||
}
|
||||
|
||||
plugin = HypervisorStatsPlugin(collectd, PLUGIN_NAME)
|
||||
|
||||
|
|
|
@ -15,9 +15,6 @@
|
|||
#
|
||||
# Collectd plugin for getting statistics from Cinder
|
||||
import collectd
|
||||
from collections import Counter
|
||||
from collections import defaultdict
|
||||
import re
|
||||
|
||||
import collectd_openstack as openstack
|
||||
|
||||
|
@ -26,46 +23,18 @@ INTERVAL = openstack.INTERVAL
|
|||
|
||||
|
||||
class CinderStatsPlugin(openstack.CollectdPlugin):
|
||||
""" Class to report the statistics on Cinder service.
|
||||
""" Class to report the statistics on Cinder objects.
|
||||
|
||||
state of agents
|
||||
number of volumes broken down by state
|
||||
total size of volumes usable and in error state
|
||||
"""
|
||||
|
||||
states = {'up': 0, 'down': 1, 'disabled': 2}
|
||||
cinder_re = re.compile('^cinder-')
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CinderStatsPlugin, self).__init__(*args, **kwargs)
|
||||
self.plugin = PLUGIN_NAME
|
||||
self.interval = INTERVAL
|
||||
|
||||
def collect(self):
|
||||
|
||||
# Get information of the state per service
|
||||
# State can be: 'up', 'down' or 'disabled'
|
||||
aggregated_workers = defaultdict(Counter)
|
||||
|
||||
for worker in self.iter_workers('cinder'):
|
||||
host = worker['host'].split('.')[0]
|
||||
service = self.cinder_re.sub('', worker['service'])
|
||||
state = worker['state']
|
||||
|
||||
aggregated_workers[service][state] += 1
|
||||
self.dispatch_value('cinder_service', '',
|
||||
self.states[state],
|
||||
{'host': host,
|
||||
'service': service,
|
||||
'state': state})
|
||||
|
||||
for service in aggregated_workers:
|
||||
totalw = sum(aggregated_workers[service].values())
|
||||
|
||||
for state in self.states:
|
||||
prct = (100.0 * aggregated_workers[service][state]) / totalw
|
||||
self.dispatch_value('cinder_services_percent', '',
|
||||
prct,
|
||||
{'state': state, 'service': service})
|
||||
|
||||
self.dispatch_value('cinder_services', '',
|
||||
aggregated_workers[service][state],
|
||||
{'state': state, 'service': service})
|
||||
def itermetrics(self):
|
||||
|
||||
volumes_details = self.get_objects_details('cinder', 'volumes')
|
||||
|
||||
|
@ -78,38 +47,42 @@ class CinderStatsPlugin(openstack.CollectdPlugin):
|
|||
status = self.count_objects_group_by(volumes_details,
|
||||
group_by_func=groupby)
|
||||
for s, nb in status.iteritems():
|
||||
self.dispatch_value('volumes', s, nb)
|
||||
yield {
|
||||
'plugin_instance': 'volumes',
|
||||
'type_instance': s,
|
||||
'values': nb
|
||||
}
|
||||
|
||||
sizes = self.count_objects_group_by(volumes_details,
|
||||
group_by_func=groupby,
|
||||
count_func=count_size_bytes)
|
||||
for n, size in sizes.iteritems():
|
||||
self.dispatch_value('volumes_size', n, size)
|
||||
yield {
|
||||
'plugin_instance': 'volumes_size',
|
||||
'type_instance': n,
|
||||
'values': size
|
||||
}
|
||||
|
||||
snaps_details = self.get_objects_details('cinder', 'snapshots')
|
||||
status_snaps = self.count_objects_group_by(snaps_details,
|
||||
group_by_func=groupby)
|
||||
for s, nb in status_snaps.iteritems():
|
||||
self.dispatch_value('snapshots', s, nb)
|
||||
yield {
|
||||
'plugin_instance': 'snapshots',
|
||||
'type_instance': s,
|
||||
'values': nb
|
||||
}
|
||||
|
||||
sizes = self.count_objects_group_by(snaps_details,
|
||||
group_by_func=groupby,
|
||||
count_func=count_size_bytes)
|
||||
for n, size in sizes.iteritems():
|
||||
self.dispatch_value('snapshots_size', n, size)
|
||||
yield {
|
||||
'plugin_instance': 'snapshots_size',
|
||||
'type_instance': n,
|
||||
'values': size
|
||||
}
|
||||
|
||||
def dispatch_value(self, plugin_instance, name, value, meta=None):
|
||||
v = collectd.Values(
|
||||
plugin=PLUGIN_NAME, # metric source
|
||||
plugin_instance=plugin_instance,
|
||||
type='gauge',
|
||||
type_instance=name,
|
||||
interval=INTERVAL,
|
||||
# w/a for https://github.com/collectd/collectd/issues/716
|
||||
meta=meta or {'0': True},
|
||||
values=[value]
|
||||
)
|
||||
v.dispatch()
|
||||
|
||||
plugin = CinderStatsPlugin(collectd, PLUGIN_NAME)
|
||||
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright 2017 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Collectd plugin for getting statistics from Cinder
|
||||
import collectd
|
||||
from collections import Counter
|
||||
from collections import defaultdict
|
||||
import re
|
||||
|
||||
import collectd_openstack as openstack
|
||||
|
||||
PLUGIN_NAME = 'cinder'
|
||||
INTERVAL = openstack.INTERVAL
|
||||
|
||||
|
||||
class CinderServiceStatsPlugin(openstack.CollectdPlugin):
|
||||
""" Class to report the statistics on Cinder services.
|
||||
|
||||
state of workers broken down by state
|
||||
"""
|
||||
|
||||
states = {'up': 0, 'down': 1, 'disabled': 2}
|
||||
cinder_re = re.compile('^cinder-')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CinderServiceStatsPlugin, self).__init__(*args, **kwargs)
|
||||
self.plugin = PLUGIN_NAME
|
||||
self.interval = INTERVAL
|
||||
|
||||
def itermetrics(self):
|
||||
|
||||
# Get information of the state per service
|
||||
# State can be: 'up', 'down' or 'disabled'
|
||||
aggregated_workers = defaultdict(Counter)
|
||||
|
||||
for worker in self.iter_workers('cinder'):
|
||||
host = worker['host'].split('.')[0]
|
||||
service = self.cinder_re.sub('', worker['service'])
|
||||
state = worker['state']
|
||||
|
||||
aggregated_workers[service][state] += 1
|
||||
yield {
|
||||
'plugin_instance': 'cinder_service',
|
||||
'values': self.states[state],
|
||||
'meta': {'host': host, 'service': service, 'state': state},
|
||||
}
|
||||
|
||||
for service in aggregated_workers:
|
||||
totalw = sum(aggregated_workers[service].values())
|
||||
|
||||
for state in self.states:
|
||||
prct = (100.0 * aggregated_workers[service][state]) / totalw
|
||||
yield {
|
||||
'plugin_instance': 'cinder_services_percent',
|
||||
'values': prct,
|
||||
'meta': {'state': state, 'service': service}
|
||||
}
|
||||
yield {
|
||||
'plugin_instance': 'cinder_services',
|
||||
'values': aggregated_workers[service][state],
|
||||
'meta': {'state': state, 'service': service},
|
||||
}
|
||||
|
||||
|
||||
plugin = CinderServiceStatsPlugin(collectd, PLUGIN_NAME)
|
||||
|
||||
|
||||
def config_callback(conf):
|
||||
plugin.config_callback(conf)
|
||||
|
||||
|
||||
def notification_callback(notification):
|
||||
plugin.notification_callback(notification)
|
||||
|
||||
|
||||
def read_callback():
|
||||
plugin.conditional_read_callback()
|
||||
|
||||
collectd.register_config(config_callback)
|
||||
collectd.register_notification(notification_callback)
|
||||
collectd.register_read(read_callback, INTERVAL)
|
|
@ -29,7 +29,12 @@ class GlanceStatsPlugin(openstack.CollectdPlugin):
|
|||
total size of images usable and in error state
|
||||
"""
|
||||
|
||||
def collect(self):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(GlanceStatsPlugin, self).__init__(*args, **kwargs)
|
||||
self.plugin = PLUGIN_NAME
|
||||
self.interval = INTERVAL
|
||||
|
||||
def itermetrics(self):
|
||||
|
||||
def is_snap(d):
|
||||
return d.get('properties', {}).get('image_type') == 'snapshot'
|
||||
|
@ -48,8 +53,11 @@ class GlanceStatsPlugin(openstack.CollectdPlugin):
|
|||
group_by_func=groupby)
|
||||
for s, nb in status.iteritems():
|
||||
(name, visibility, status) = s.split('.')
|
||||
self.dispatch_value(name, nb, meta={'visibility': visibility,
|
||||
'status': status})
|
||||
yield {
|
||||
'type_instance': name,
|
||||
'values': nb,
|
||||
'meta': {'visibility': visibility, 'status': status}
|
||||
}
|
||||
|
||||
# sizes
|
||||
def count_size_bytes(d):
|
||||
|
@ -67,20 +75,11 @@ class GlanceStatsPlugin(openstack.CollectdPlugin):
|
|||
count_func=count_size_bytes)
|
||||
for s, nb in sizes.iteritems():
|
||||
(name, visibility, status) = s.split('.')
|
||||
self.dispatch_value(name, nb, meta={'visibility': visibility,
|
||||
'status': status})
|
||||
|
||||
def dispatch_value(self, name, value, meta=None):
|
||||
v = collectd.Values(
|
||||
plugin=PLUGIN_NAME, # metric source
|
||||
type='gauge',
|
||||
type_instance=name,
|
||||
interval=INTERVAL,
|
||||
# w/a for https://github.com/collectd/collectd/issues/716
|
||||
meta=meta or {'0': True},
|
||||
values=[value]
|
||||
)
|
||||
v.dispatch()
|
||||
yield {
|
||||
'type_instance': name,
|
||||
'values': nb,
|
||||
'meta': {'visibility': visibility, 'status': status},
|
||||
}
|
||||
|
||||
plugin = GlanceStatsPlugin(collectd, PLUGIN_NAME)
|
||||
|
||||
|
|
|
@ -29,7 +29,12 @@ class KeystoneStatsPlugin(openstack.CollectdPlugin):
|
|||
number of roles
|
||||
"""
|
||||
|
||||
def collect(self):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(KeystoneStatsPlugin, self).__init__(*args, **kwargs)
|
||||
self.plugin = PLUGIN_NAME
|
||||
self.interval = INTERVAL
|
||||
|
||||
def itermetrics(self):
|
||||
|
||||
def groupby(d):
|
||||
return 'enabled' if d.get('enabled') else 'disabled'
|
||||
|
@ -43,7 +48,11 @@ class KeystoneStatsPlugin(openstack.CollectdPlugin):
|
|||
status = self.count_objects_group_by(tenants_details,
|
||||
group_by_func=groupby)
|
||||
for s, nb in status.iteritems():
|
||||
self.dispatch_value('tenants', nb, meta={'state': s})
|
||||
yield {
|
||||
'type_instance': 'tenants',
|
||||
'values': nb,
|
||||
'meta': {'state': s},
|
||||
}
|
||||
|
||||
# users
|
||||
r = self.get('keystone', 'users')
|
||||
|
@ -54,7 +63,11 @@ class KeystoneStatsPlugin(openstack.CollectdPlugin):
|
|||
status = self.count_objects_group_by(users_details,
|
||||
group_by_func=groupby)
|
||||
for s, nb in status.iteritems():
|
||||
self.dispatch_value('users', nb, meta={'state': s})
|
||||
yield {
|
||||
'type_instance': 'users',
|
||||
'values': nb,
|
||||
'meta': {'state': s},
|
||||
}
|
||||
|
||||
# roles
|
||||
r = self.get('keystone', 'OS-KSADM/roles')
|
||||
|
@ -62,19 +75,10 @@ class KeystoneStatsPlugin(openstack.CollectdPlugin):
|
|||
self.logger.warning('Could not find Keystone roles')
|
||||
return
|
||||
roles = r.json().get('roles', [])
|
||||
self.dispatch_value('roles', len(roles))
|
||||
|
||||
def dispatch_value(self, name, value, meta=None):
|
||||
v = collectd.Values(
|
||||
plugin=PLUGIN_NAME, # metric source
|
||||
type='gauge',
|
||||
type_instance=name,
|
||||
interval=INTERVAL,
|
||||
# w/a for https://github.com/collectd/collectd/issues/716
|
||||
meta=meta or {'0': True},
|
||||
values=[value]
|
||||
)
|
||||
v.dispatch()
|
||||
yield {
|
||||
'type_instance': 'roles',
|
||||
'values': len(roles),
|
||||
}
|
||||
|
||||
plugin = KeystoneStatsPlugin(collectd, PLUGIN_NAME)
|
||||
|
||||
|
|
|
@ -15,9 +15,6 @@
|
|||
#
|
||||
# Collectd plugin for getting resource statistics from Neutron
|
||||
import collectd
|
||||
from collections import Counter
|
||||
from collections import defaultdict
|
||||
import re
|
||||
|
||||
import collectd_openstack as openstack
|
||||
|
||||
|
@ -26,9 +23,8 @@ INTERVAL = openstack.INTERVAL
|
|||
|
||||
|
||||
class NeutronStatsPlugin(openstack.CollectdPlugin):
|
||||
""" Class to report the statistics on Neutron service.
|
||||
""" Class to report the statistics on Neutron objects.
|
||||
|
||||
state of agents
|
||||
number of networks broken down by status
|
||||
number of subnets
|
||||
number of ports broken down by owner and status
|
||||
|
@ -36,11 +32,12 @@ class NeutronStatsPlugin(openstack.CollectdPlugin):
|
|||
number of floating IP addresses broken down by free/associated
|
||||
"""
|
||||
|
||||
neutron_re = re.compile('^neutron-')
|
||||
agent_re = re.compile('-agent$')
|
||||
states = {'up': 0, 'down': 1, 'disabled': 2}
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(NeutronStatsPlugin, self).__init__(*args, **kwargs)
|
||||
self.plugin = PLUGIN_NAME
|
||||
self.interval = INTERVAL
|
||||
|
||||
def collect(self):
|
||||
def itermetrics(self):
|
||||
|
||||
def groupby_network(x):
|
||||
return "networks.%s" % x.get('status', 'unknown').lower()
|
||||
|
@ -67,63 +64,33 @@ class NeutronStatsPlugin(openstack.CollectdPlugin):
|
|||
status = 'free'
|
||||
return "floatingips.%s" % status
|
||||
|
||||
# Get information of the state per agent
|
||||
# State can be up or down
|
||||
aggregated_agents = defaultdict(Counter)
|
||||
|
||||
for agent in self.iter_workers('neutron'):
|
||||
host = agent['host'].split('.')[0]
|
||||
service = self.agent_re.sub(
|
||||
'', self.neutron_re.sub('', agent['service']))
|
||||
state = agent['state']
|
||||
|
||||
aggregated_agents[service][state] += 1
|
||||
self.dispatch_value('neutron_agent',
|
||||
self.states[state],
|
||||
{'host': host,
|
||||
'service': service,
|
||||
'state': state})
|
||||
|
||||
for service in aggregated_agents:
|
||||
totala = sum(aggregated_agents[service].values())
|
||||
|
||||
for state in self.states:
|
||||
prct = (100.0 * aggregated_agents[service][state]) / totala
|
||||
self.dispatch_value('neutron_agents_percent',
|
||||
prct,
|
||||
{'service': service, 'state': state})
|
||||
|
||||
self.dispatch_value('neutron_agents',
|
||||
aggregated_agents[service][state],
|
||||
{'service': service, 'state': state})
|
||||
|
||||
# Networks
|
||||
networks = self.get_objects('neutron', 'networks', api_version='v2.0')
|
||||
status = self.count_objects_group_by(networks,
|
||||
group_by_func=groupby_network)
|
||||
for s, nb in status.iteritems():
|
||||
self.dispatch_value(s, nb)
|
||||
self.dispatch_value('networks', len(networks))
|
||||
yield {'type_instance': s, 'values': nb}
|
||||
yield {'type_instance': 'networks', 'values': len(networks)}
|
||||
|
||||
# Subnets
|
||||
subnets = self.get_objects('neutron', 'subnets', api_version='v2.0')
|
||||
self.dispatch_value('subnets', len(subnets))
|
||||
yield {'type_instance': 'subnets', 'values': len(subnets)}
|
||||
|
||||
# Ports
|
||||
ports = self.get_objects('neutron', 'ports', api_version='v2.0')
|
||||
status = self.count_objects_group_by(ports,
|
||||
group_by_func=groupby_port)
|
||||
for s, nb in status.iteritems():
|
||||
self.dispatch_value(s, nb)
|
||||
self.dispatch_value('ports', len(ports))
|
||||
yield {'type_instance': s, 'values': nb}
|
||||
yield {'type_instance': 'ports', 'values': len(ports)}
|
||||
|
||||
# Routers
|
||||
routers = self.get_objects('neutron', 'routers', api_version='v2.0')
|
||||
status = self.count_objects_group_by(routers,
|
||||
group_by_func=groupby_router)
|
||||
for s, nb in status.iteritems():
|
||||
self.dispatch_value(s, nb)
|
||||
self.dispatch_value('routers', len(routers))
|
||||
yield {'type_instance': s, 'values': nb}
|
||||
yield {'type_instance': 'routers', 'values': len(routers)}
|
||||
|
||||
# Floating IP addresses
|
||||
floatingips = self.get_objects('neutron', 'floatingips',
|
||||
|
@ -131,20 +98,9 @@ class NeutronStatsPlugin(openstack.CollectdPlugin):
|
|||
status = self.count_objects_group_by(floatingips,
|
||||
group_by_func=groupby_floating)
|
||||
for s, nb in status.iteritems():
|
||||
self.dispatch_value(s, nb)
|
||||
self.dispatch_value('floatingips', len(floatingips))
|
||||
yield {'type_instance': s, 'values': nb}
|
||||
yield {'type_instance': 'floatingips', 'values': len(routers)}
|
||||
|
||||
def dispatch_value(self, name, value, meta=None):
|
||||
v = collectd.Values(
|
||||
plugin=PLUGIN_NAME, # metric source
|
||||
type='gauge',
|
||||
type_instance=name,
|
||||
interval=INTERVAL,
|
||||
# w/a for https://github.com/collectd/collectd/issues/716
|
||||
meta=meta or {'0': True},
|
||||
values=[value]
|
||||
)
|
||||
v.dispatch()
|
||||
|
||||
plugin = NeutronStatsPlugin(collectd, PLUGIN_NAME)
|
||||
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright 2015 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Collectd plugin for getting resource statistics from Neutron
|
||||
import collectd
|
||||
from collections import Counter
|
||||
from collections import defaultdict
|
||||
import re
|
||||
|
||||
import collectd_openstack as openstack
|
||||
|
||||
PLUGIN_NAME = 'neutron'
|
||||
INTERVAL = openstack.INTERVAL
|
||||
|
||||
|
||||
class NeutronAgentStatsPlugin(openstack.CollectdPlugin):
|
||||
""" Class to report the statistics on Neutron agents.
|
||||
|
||||
state of agents
|
||||
"""
|
||||
|
||||
neutron_re = re.compile('^neutron-')
|
||||
agent_re = re.compile('-agent$')
|
||||
states = {'up': 0, 'down': 1, 'disabled': 2}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(NeutronAgentStatsPlugin, self).__init__(*args, **kwargs)
|
||||
self.plugin = PLUGIN_NAME
|
||||
self.interval = INTERVAL
|
||||
|
||||
def itermetrics(self):
|
||||
|
||||
# Get information of the state per agent
|
||||
# State can be up or down
|
||||
aggregated_agents = defaultdict(Counter)
|
||||
|
||||
for agent in self.iter_workers('neutron'):
|
||||
host = agent['host'].split('.')[0]
|
||||
service = self.agent_re.sub(
|
||||
'', self.neutron_re.sub('', agent['service']))
|
||||
state = agent['state']
|
||||
|
||||
aggregated_agents[service][state] += 1
|
||||
|
||||
yield {
|
||||
'type_instance': 'neutron_agent',
|
||||
'values': self.states[state],
|
||||
'meta': {'host': host, 'service': service, 'state': state}
|
||||
}
|
||||
|
||||
for service in aggregated_agents:
|
||||
totala = sum(aggregated_agents[service].values())
|
||||
|
||||
for state in self.states:
|
||||
prct = (100.0 * aggregated_agents[service][state]) / totala
|
||||
yield {
|
||||
'type_instance': 'neutron_agents_percent',
|
||||
'values': prct,
|
||||
'meta': {'service': service, 'state': state},
|
||||
}
|
||||
yield {
|
||||
'type_instance': 'neutron_agents',
|
||||
'values': aggregated_agents[service][state],
|
||||
'meta': {'service': service, 'state': state},
|
||||
}
|
||||
|
||||
|
||||
plugin = NeutronAgentStatsPlugin(collectd, PLUGIN_NAME)
|
||||
|
||||
|
||||
def config_callback(conf):
|
||||
plugin.config_callback(conf)
|
||||
|
||||
|
||||
def notification_callback(notification):
|
||||
plugin.notification_callback(notification)
|
||||
|
||||
|
||||
def read_callback():
|
||||
plugin.conditional_read_callback()
|
||||
|
||||
collectd.register_config(config_callback)
|
||||
collectd.register_notification(notification_callback)
|
||||
collectd.register_read(read_callback, INTERVAL)
|
|
@ -15,9 +15,6 @@
|
|||
#
|
||||
# Collectd plugin for getting statistics from Nova
|
||||
import collectd
|
||||
from collections import Counter
|
||||
from collections import defaultdict
|
||||
import re
|
||||
|
||||
import collectd_openstack as openstack
|
||||
|
||||
|
@ -25,73 +22,33 @@ PLUGIN_NAME = 'nova'
|
|||
INTERVAL = openstack.INTERVAL
|
||||
|
||||
|
||||
class NovaStatsPlugin(openstack.CollectdPlugin):
|
||||
""" Class to report the statistics on Nova service.
|
||||
class NovaInstanceStatsPlugin(openstack.CollectdPlugin):
|
||||
""" Class to report the statistics on Nova instances.
|
||||
|
||||
status per service and number of instances broken down by state
|
||||
Number of instances broken down by state
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(NovaInstanceStatsPlugin, self).__init__(*args, **kwargs)
|
||||
self.plugin = PLUGIN_NAME
|
||||
self.interval = INTERVAL
|
||||
|
||||
states = {'up': 0, 'down': 1, 'disabled': 2}
|
||||
nova_re = re.compile('^nova-')
|
||||
|
||||
def collect(self):
|
||||
|
||||
# Get information of the state per service
|
||||
# State can be: 'up', 'down' or 'disabled'
|
||||
aggregated_workers = defaultdict(Counter)
|
||||
|
||||
for worker in self.iter_workers('nova'):
|
||||
host = worker['host'].split('.')[0]
|
||||
service = self.nova_re.sub('', worker['service'])
|
||||
state = worker['state']
|
||||
|
||||
aggregated_workers[service][state] += 1
|
||||
self.dispatch_value('nova_service', '',
|
||||
self.states[state],
|
||||
{'host': host,
|
||||
'service': service,
|
||||
'state': state})
|
||||
|
||||
for service in set(aggregated_workers.keys()).union(
|
||||
('compute', 'scheduler', 'conductor', 'cert', 'consoleauth')):
|
||||
|
||||
total = sum(aggregated_workers[service].values())
|
||||
|
||||
for state in self.states:
|
||||
prct = 0
|
||||
if total > 0:
|
||||
prct = (100.0 * aggregated_workers[service][state]) / total
|
||||
|
||||
self.dispatch_value('nova_services_percent', '',
|
||||
prct,
|
||||
{'state': state, 'service': service})
|
||||
|
||||
self.dispatch_value('nova_services', '',
|
||||
aggregated_workers[service][state],
|
||||
{'state': state, 'service': service})
|
||||
def itermetrics(self):
|
||||
servers_details = self.get_objects_details('nova', 'servers')
|
||||
|
||||
def groupby(d):
|
||||
return d.get('status', 'unknown').lower()
|
||||
|
||||
status = self.count_objects_group_by(servers_details,
|
||||
group_by_func=groupby)
|
||||
for s, nb in status.iteritems():
|
||||
self.dispatch_value('instances', s, nb)
|
||||
yield {
|
||||
'plugin_instance': 'instances',
|
||||
'values': nb,
|
||||
'type_instance': s,
|
||||
}
|
||||
|
||||
def dispatch_value(self, plugin_instance, name, value, meta=None):
|
||||
v = collectd.Values(
|
||||
plugin=PLUGIN_NAME, # metric source
|
||||
plugin_instance=plugin_instance,
|
||||
type='gauge',
|
||||
type_instance=name,
|
||||
interval=INTERVAL,
|
||||
# w/a for https://github.com/collectd/collectd/issues/716
|
||||
meta=meta or {'0': True},
|
||||
values=[value]
|
||||
)
|
||||
v.dispatch()
|
||||
|
||||
plugin = NovaStatsPlugin(collectd, PLUGIN_NAME)
|
||||
plugin = NovaInstanceStatsPlugin(collectd, PLUGIN_NAME)
|
||||
|
||||
|
||||
def config_callback(conf):
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright 2017 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Collectd plugin for getting statistics from Nova
|
||||
import collectd
|
||||
from collections import Counter
|
||||
from collections import defaultdict
|
||||
import re
|
||||
|
||||
import collectd_openstack as openstack
|
||||
|
||||
PLUGIN_NAME = 'nova'
|
||||
INTERVAL = openstack.INTERVAL
|
||||
|
||||
|
||||
class NovaServiceStatsPlugin(openstack.CollectdPlugin):
|
||||
""" Class to report the statistics on Nova services.
|
||||
|
||||
status per service broken down by state
|
||||
"""
|
||||
|
||||
states = {'up': 0, 'down': 1, 'disabled': 2}
|
||||
nova_re = re.compile('^nova-')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(NovaServiceStatsPlugin, self).__init__(*args, **kwargs)
|
||||
self.plugin = PLUGIN_NAME
|
||||
self.interval = INTERVAL
|
||||
|
||||
def itermetrics(self):
|
||||
|
||||
# Get information of the state per service
|
||||
# State can be: 'up', 'down' or 'disabled'
|
||||
aggregated_workers = defaultdict(Counter)
|
||||
|
||||
for worker in self.iter_workers('nova'):
|
||||
host = worker['host'].split('.')[0]
|
||||
service = self.nova_re.sub('', worker['service'])
|
||||
state = worker['state']
|
||||
|
||||
aggregated_workers[service][state] += 1
|
||||
yield {
|
||||
'plugin_instance': 'nova_service',
|
||||
'values': self.states[state],
|
||||
'meta': {'host': host, 'service': service, 'state': state}
|
||||
}
|
||||
|
||||
for service in set(aggregated_workers.keys()).union(
|
||||
('compute', 'scheduler', 'conductor', 'cert', 'consoleauth')):
|
||||
|
||||
total = sum(aggregated_workers[service].values())
|
||||
|
||||
for state in self.states:
|
||||
prct = 0
|
||||
if total > 0:
|
||||
prct = (100.0 * aggregated_workers[service][state]) / total
|
||||
|
||||
yield {
|
||||
'plugin_instance': 'nova_services_percent',
|
||||
'values': prct,
|
||||
'meta': {'state': state, 'service': service},
|
||||
}
|
||||
yield {
|
||||
'plugin_instance': 'nova_services',
|
||||
'values': aggregated_workers[service][state],
|
||||
'meta': {'state': state, 'service': service},
|
||||
}
|
||||
|
||||
|
||||
plugin = NovaServiceStatsPlugin(collectd, PLUGIN_NAME)
|
||||
|
||||
|
||||
def config_callback(conf):
|
||||
plugin.config_callback(conf)
|
||||
|
||||
|
||||
def notification_callback(notification):
|
||||
plugin.notification_callback(notification)
|
||||
|
||||
|
||||
def read_callback():
|
||||
plugin.conditional_read_callback()
|
||||
|
||||
collectd.register_config(config_callback)
|
||||
collectd.register_notification(notification_callback)
|
||||
collectd.register_read(read_callback, INTERVAL)
|
|
@ -28,7 +28,9 @@ define lma_collector::collectd::openstack (
|
|||
|
||||
$service = $title
|
||||
|
||||
$supported_services = ['nova', 'cinder', 'glance', 'keystone', 'neutron']
|
||||
$supported_services = ['nova', 'nova_services',
|
||||
'cinder', 'cinder_services', 'glance', 'keystone',
|
||||
'neutron', 'neutron_agents']
|
||||
if ! member($supported_services, $service) {
|
||||
fail("service '${service}' is not supported")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue