Merge "Rework collectd plugins for OpenStack"

This commit is contained in:
Jenkins 2017-02-07 08:25:37 +00:00 committed by Gerrit Code Review
commit 03113b8a3a
14 changed files with 432 additions and 265 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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):

View File

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

View File

@ -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")
}