Add Neutron metrics

This change adds the following metrics:
- number of networks broken down by status + total
- number of subnets
- number of ports broken down by owner and status + total
- number of routers broken down by status + total
- number of floating IP addresses broken down by status + total
- number of agents broken down by type and status + type + total

Change-Id: If6cc694cdd84e525f0747c6f6c78356acbaa77c8
This commit is contained in:
Simon Pasquier 2015-03-26 14:46:02 +01:00
parent b58577dc8f
commit aa81110c29
8 changed files with 225 additions and 18 deletions

View File

@ -194,27 +194,42 @@ class CollectdPlugin(object):
def read_callback(self):
raise "read_callback method needs to be overriden!"
def get_objects_details(self, project, object_name,
api_version='',
params='all_tenants=1'):
""" Return object details list
def get_objects(self, project, object_name, api_version='',
params='all_tenants=1'):
""" Return a list of OpenStack objects
the version is not always included in url endpoint (glance)
use api_version param to include the version in resource url
See get_objects_details()
"""
return self._get_objects(project, object_name, api_version, params,
False)
def get_objects_details(self, project, object_name, api_version='',
params='all_tenants=1'):
""" Return a list of details about OpenStack objects
The API version is not always included in the URL endpoint
registered in Keystone (eg Glance). In this case, use the
api_version parameter to specify which version should be used.
"""
return self._get_objects(project, object_name, api_version, params,
True)
def _get_objects(self, project, object_name, api_version, params, detail):
if api_version:
resource = '%s/%s/detail?%s' % (api_version, object_name, params)
resource = '%s/%s' % (api_version, object_name)
else:
resource = '%s/detail?%s' % (object_name, params)
resource = '%s' % (object_name)
if detail:
resource = '%s/detail' % (resource)
if params:
resource = '%s?%s' % (resource, params)
# TODO(scroiset): use pagination to handle large collection
r = self.get(project, resource)
if not r:
if not r or object_name not in r.json():
self.logger.warning('Could not find %s %s' % (project,
object_name))
return []
return r.json().get(object_name, [])
return r.json().get(object_name)
def count_objects_group_by(self,
list_object,

View File

@ -0,0 +1,154 @@
#!/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
import openstack
PLUGIN_NAME = 'neutron'
INTERVAL = 60
class NeutronStatsPlugin(openstack.CollectdPlugin):
""" Class to report the statistics on Neutron service.
number of networks broken down by status
number of subnets
number of ports broken down by owner and status
number of routers broken down by status
number of floating IP addresses broken down by free/associated
number of agents broken down by agent name and status
number of agents broken down by status
"""
def config_callback(self, config):
super(NeutronStatsPlugin, self).config_callback(config)
def read_callback(self):
def groupby_network(x):
return "networks.%s" % x.get('status', 'unknown').lower()
def groupby_router(x):
return "routers.%s" % x.get('status', 'unknown').lower()
def groupby_port(x):
owner = x.get('device_owner', 'unknown')
if owner.startswith('network:'):
owner = owner.replace('network:', '')
elif owner.startswith('compute:'):
# The part after 'compute:' is the name of the Nova AZ
owner = 'compute'
status = x.get('status', 'unknown').lower()
return "ports.%s.%s" % (owner, status)
def groupby_floating(x):
if x.get('port_id', None):
status = 'associated'
else:
status = 'free'
return "floatingips.%s" % status
def agent_status(x):
if not x.get('admin_state_up', False):
return 'disabled'
elif not x.get('alive', False):
return 'down'
else:
return 'up'
def groupby_agent_and_status(x):
# agent_type is like 'L3 agent', 'Open vSwitch agent', ...
agent = x.get('agent_type', 'unknown').lower()
agent = agent.replace(' agent', '').replace(' ', '_')
return "agents.%s.%s" % (agent, agent_status(x))
def groupby_agent(x):
return "agents.%s" % (agent_status(x))
# 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))
# Subnets
subnets = self.get_objects('neutron', 'subnets', api_version='v2.0')
self.dispatch_value('subnets', 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))
# 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))
# Floating IP addresses
floatingips = self.get_objects('neutron', 'floatingips',
api_version='v2.0')
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))
# Agents
agents = self.get_objects('neutron', 'agents',
api_version='v2.0')
status = self.count_objects_group_by(agents,
group_by_func=groupby_agent)
for s, nb in status.iteritems():
self.dispatch_value(s, nb)
status = self.count_objects_group_by(
agents,
group_by_func=groupby_agent_and_status)
for s, nb in status.iteritems():
self.dispatch_value(s, nb)
self.dispatch_value('agents', len(agents))
def dispatch_value(self, name, value):
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},
values=[value]
)
v.dispatch()
plugin = NeutronStatsPlugin(collectd)
def config_callback(conf):
plugin.config_callback(conf)
def read_callback():
plugin.read_callback()
collectd.register_config(config_callback)
collectd.register_read(read_callback, INTERVAL)

View File

@ -116,6 +116,8 @@ function process_message ()
msg['Fields']['name'] = 'openstack.glance' .. sep .. sample['type_instance']
elseif metric_source == 'keystone' then
msg['Fields']['name'] = 'openstack.keystone' .. sep .. sample['type_instance']
elseif metric_source == 'neutron' then
msg['Fields']['name'] = 'openstack.neutron' .. sep .. sample['type_instance']
elseif metric_source == 'memcached' then
msg['Fields']['name'] = 'memcached' .. sep .. string.gsub(metric_name, 'memcached_', '')
elseif metric_source == 'haproxy' then

View File

@ -1,5 +1,5 @@
class lma_collector::collectd::controller (
$haproxy_socket,
$haproxy_socket = undef,
$service_user = $lma_collector::params::openstack_user,
$service_password = $lma_collector::params::openstack_password,
$service_tenant = $lma_collector::params::openstack_tenant,
@ -60,6 +60,13 @@ class lma_collector::collectd::controller (
'KeystoneUrl' => $keystone_url,
'Timeout' => $lma_collector::params::openstack_client_timeout,
},
'openstack_neutron' => {
'Username' => $service_user,
'Password' => $service_password,
'Tenant' => $service_tenant,
'KeystoneUrl' => $keystone_url,
'Timeout' => $lma_collector::params::openstack_client_timeout,
},
}
if $haproxy_socket {
@ -100,6 +107,9 @@ class lma_collector::collectd::controller (
lma_collector::collectd::python_script { 'openstack_keystone.py':
}
lma_collector::collectd::python_script { 'openstack_neutron.py':
}
if $haproxy_socket {
lma_collector::collectd::python_script { 'haproxy.py':
}

View File

@ -69,6 +69,8 @@ release = '1.0'
# directories to ignore when looking for source files.
exclude_patterns = [
'metrics/apache.rst',
'metrics/haproxy.rst',
'metrics/memcached.rst',
'metrics/mysql.rst',
'metrics/openstack.rst',
'metrics/rabbitmq.rst',

View File

@ -89,6 +89,11 @@ RabbitMQ
.. include:: metrics/rabbitmq.rst
HAProxy
-------
.. include:: metrics/haproxy.rst
Memcached
---------
@ -98,8 +103,3 @@ OpenStack
---------
.. include:: metrics/openstack.rst
HAproxy
-------
.. include:: metrics/haproxy.rst

View File

@ -1,4 +1,4 @@
.. _HAproxy_metrics:
.. _haproxy_metrics:
Server
^^^^^^

View File

@ -63,6 +63,30 @@ These metrics are retrieved from the Glance API.
``<state>`` is one of 'queued', 'saving', 'active', 'killed', 'deleted', 'pending_delete'.
Network
^^^^^^^
These metrics are retrieved from the Neutron API.
* ``openstack.neutron.agents.<agent_type>.<agent_state>``, the total number of Neutron agents by agent type and state.
* ``openstack.neutron.agents.<agent_state>``, the total number of Neutron agents by state.
* ``openstack.neutron.agents``, the total number of Neutron agents.
* ``openstack.neutron.networks.<state>``, the number of virtual networks by state.
* ``openstack.neutron.networks``, the total number of virtual networks.
* ``openstack.neutron.subnets``, the number of virtual subnets.
* ``openstack.neutron.ports.<owner>.<state>``, the number of virtual ports by owner and state.
* ``openstack.neutron.ports``, the total number of virtual ports.
* ``openstack.neutron.routers.<state>``, the number of virtual routers by state.
* ``openstack.neutron.routers``, the total number of virtual routers.
* ``openstack.neutron.floatingips.free``, the number of floating IP addresses which aren't associated.
* ``openstack.neutron.floatingips.associated``, the number of floating IP addresses which are associated.
* ``openstack.neutron.floatingips``, the total number of floating IP addresses.
``<agent_type>`` is one of 'dhcp', 'l3', 'metadata' or 'open_vswitch'.
``<agent_state>`` is one of 'up', 'down' or 'disabled'.
``<state>`` is one of 'active', 'build', 'down' or 'error'.
``<owner>`` is one of 'compute', 'dhcp', 'floatingip', 'floatingip_agent_gateway', 'router_interface', 'router_gateway', 'router_ha_interface', 'router_interface_distributed' or 'router_centralized_snat'.
API response times
^^^^^^^^^^^^^^^^^^