Use OpenStack API to collect the status of Neutron agents

This change uses the Neutron API to get the status of the Neutron
agents instead of querying the MySQL database.

Change-Id: I60fa2386a887e9dac2fe4f1234d225ad6402bf2d
Partial-Bug: #1546188
This commit is contained in:
Guillaume Thouvenin 2016-03-11 15:23:13 +01:00
parent e60e187f32
commit bcdedbf1aa
4 changed files with 68 additions and 47 deletions

View File

@ -364,16 +364,6 @@ if $influxdb_mode != 'disabled' {
password => $nova['db_password'],
}
unless $contrail {
lma_collector::collectd::dbi_services { 'neutron':
username => 'neutron',
dbname => 'neutron',
password => $neutron['database']['passwd'],
report_interval => 15,
downtime_factor => 4,
}
}
class { 'lma_collector::collectd::haproxy':
socket => $haproxy_socket,
# Ignore internal stats ('Stats' for 6.1, 'stats' for 7.0) and lma proxies

View File

@ -191,26 +191,40 @@ class CollectdPlugin(base.Base):
where 'state' can be 'up', 'down' or 'disabled'
"""
ost_services_r = self.get(service, 'os-services')
if service == 'neutron':
endpoint = 'v2.0/agents'
entry = 'agents'
else:
endpoint = 'os-services'
entry = 'services'
ost_services_r = self.get(service, endpoint)
r_status = ost_services_r.status_code
try:
r_json = ost_services_r.json()
except ValueError:
r_json = {}
if r_status == 200 and 'services' in r_json:
for val in r_json['services']:
if r_status == 200 and entry in r_json:
for val in r_json[entry]:
data = {'host': val['host'], 'service': val['binary']}
if val['status'] == 'disabled':
data['state'] = 'disabled'
elif val['state'] == 'up' or val['state'] == 'down':
data['state'] = val['state']
if service == 'neutron':
if not val['admin_state_up']:
data['state'] = 'disabled'
else:
data['state'] = 'up' if val['alive'] else 'down'
else:
msg = "Unknown state for {} workers:{}".format(
service, val['state'])
self.logger.warning(msg)
continue
if val['status'] == 'disabled':
data['state'] = 'disabled'
elif val['state'] == 'up' or val['state'] == 'down':
data['state'] = val['state']
else:
msg = "Unknown state for {} workers:{}".format(
service, val['state'])
self.logger.warning(msg)
continue
yield data
else:

View File

@ -15,6 +15,9 @@
#
# Collectd plugin for getting resource statistics from Neutron
import collectd
from collections import Counter
from collections import defaultdict
import re
import base
import collectd_openstack as openstack
@ -26,6 +29,7 @@ INTERVAL = openstack.INTERVAL
class NeutronStatsPlugin(openstack.CollectdPlugin):
""" Class to report the statistics on Neutron service.
state of agents
number of networks broken down by status
number of subnets
number of ports broken down by owner and status
@ -33,8 +37,13 @@ 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}
@base.read_callback_wrapper
def read_callback(self):
def groupby_network(x):
return "networks.%s" % x.get('status', 'unknown').lower()
@ -60,6 +69,29 @@ 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:
for state in self.states:
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,
@ -97,14 +129,14 @@ class NeutronStatsPlugin(openstack.CollectdPlugin):
self.dispatch_value(s, nb)
self.dispatch_value('floatingips', len(floatingips))
def dispatch_value(self, name, value):
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={'0': True},
meta=meta or {'0': True},
values=[value]
)
v.dispatch()

View File

@ -39,19 +39,6 @@ function replace_dot_by_sep (str)
return string.gsub(str, '%.', sep)
end
function split_service_and_state_and_hostname(str)
-- Possible values:
-- services.compute.down.node-1.test.domain.local
-- services.scheduler.up
-- agents.dhcp.down.node-44
-- agents.dhcp.up
-- services.scheduler.disabled.rbd:volumes
local service, state, hostname = string.match(str, '^%w+%.([%w-]+)%.([%w-]+)%.?(.-)$')
-- remove domain part of the hostname or nil if string is empty
hostname = string.match(hostname, '^([^.]+)')
return replace_dot_by_sep(service), state, hostname
end
function process_message ()
local ok, samples = pcall(cjson.decode, read_message("Payload"))
if not ok then
@ -266,6 +253,15 @@ function process_message ()
skip_it = true
elseif sample['type_instance'] == 'subnets' then
msg['Fields']['name'] = 'openstack' .. sep .. 'neutron' .. sep .. 'subnets'
elseif sample['type_instance'] == 'neutron_agents' or
sample['type_instance'] == 'neutron_agent' then
msg['Fields']['name'] = 'openstack_' .. sample['type_instance']
msg['Fields']['tag_fields'] = { 'service', 'state' }
msg['Fields']['service'] = sample['meta']['service']
msg['Fields']['state'] = sample['meta']['state']
if sample['type_instance'] == 'neutron_agent' then
msg['Fields']['hostname'] = sample['meta']['host']
end
elseif string.match(sample['type_instance'], '^ports') then
local resource, owner, state = string.match(sample['type_instance'], '^([^.]+)%.([^.]+)%.(.+)$')
msg['Fields']['name'] = 'openstack' .. sep .. 'neutron' .. sep .. replace_dot_by_sep(resource)
@ -331,17 +327,6 @@ function process_message ()
msg['Fields'][additional_tag] = sample['type_instance']
end
end
elseif metric_source == 'dbi' and sample['plugin_instance'] == 'agents_neutron' then
local service, state, hostname = split_service_and_state_and_hostname(sample['type_instance'])
if hostname then
msg['Fields']['name'] = 'openstack' .. sep .. 'neutron' .. sep .. 'agent'
msg['Fields']['hostname'] = hostname
else
msg['Fields']['name'] = 'openstack' .. sep .. 'neutron' .. sep .. 'agents'
end
msg['Fields']['tag_fields'] = { 'service', 'state' }
msg['Fields']['service'] = service
msg['Fields']['state'] = state
elseif metric_source == 'pacemaker_resource' then
msg['Fields']['name'] = 'pacemaker_local_resource_active'
msg['Fields']['tag_fields'] = { 'resource' }