#
# Copyright 2014 Cisco Systems,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.
import abc
import collections
from oslo_config import cfg
from oslo_log import log
from oslo_utils import timeutils
import six
from ceilometer.i18n import _
from ceilometer.network.services import base
from ceilometer import neutron_client
from ceilometer import sample
LOG = log.getLogger(__name__)
LBStatsData = collections.namedtuple(
'LBStats',
['active_connections', 'total_connections', 'bytes_in', 'bytes_out']
)
LOAD_BALANCER_STATUS_V2 = {
'offline': 0,
'online': 1,
'no_monitor': 3,
'error': 4,
'degraded': 5
}
[docs]class BaseLBPollster(base.BaseServicesPollster):
"""Base Class for Load Balancer pollster"""
def __init__(self):
super(BaseLBPollster, self).__init__()
self.lb_version = cfg.CONF.service_types.neutron_lbaas_version
[docs] def get_load_balancer_status_id(self, value):
if self.lb_version == 'v1':
resource_status = self.get_status_id(value)
elif self.lb_version == 'v2':
status = value.lower()
resource_status = LOAD_BALANCER_STATUS_V2.get(status, -1)
return resource_status
[docs]class LBPoolPollster(BaseLBPollster):
"""Pollster to capture Load Balancer pool status samples."""
FIELDS = ['admin_state_up',
'description',
'lb_method',
'name',
'protocol',
'provider',
'status',
'status_description',
'subnet_id',
'vip_id'
]
@property
def default_discovery(self):
return 'lb_pools'
[docs] def get_samples(self, manager, cache, resources):
resources = resources or []
for pool in resources:
LOG.debug("Load Balancer Pool : %s" % pool)
status = self.get_load_balancer_status_id(pool['status'])
if status == -1:
# unknown status, skip this sample
LOG.warning(_("Unknown status %(stat)s received on pool "
"%(id)s, skipping sample")
% {'stat': pool['status'], 'id': pool['id']})
continue
yield sample.Sample(
name='network.services.lb.pool',
type=sample.TYPE_GAUGE,
unit='pool',
volume=status,
user_id=None,
project_id=pool['tenant_id'],
resource_id=pool['id'],
timestamp=timeutils.utcnow().isoformat(),
resource_metadata=self.extract_metadata(pool)
)
[docs]class LBVipPollster(base.BaseServicesPollster):
"""Pollster to capture Load Balancer Vip status samples."""
FIELDS = ['admin_state_up',
'address',
'connection_limit',
'description',
'name',
'pool_id',
'port_id',
'protocol',
'protocol_port',
'status',
'status_description',
'subnet_id',
'session_persistence',
]
@property
def default_discovery(self):
return 'lb_vips'
[docs] def get_samples(self, manager, cache, resources):
resources = resources or []
for vip in resources:
LOG.debug("Load Balancer Vip : %s" % vip)
status = self.get_status_id(vip['status'])
if status == -1:
# unknown status, skip this sample
LOG.warning(_("Unknown status %(stat)s received on vip "
"%(id)s, skipping sample")
% {'stat': vip['status'], 'id': vip['id']})
continue
yield sample.Sample(
name='network.services.lb.vip',
type=sample.TYPE_GAUGE,
unit='vip',
volume=status,
user_id=None,
project_id=vip['tenant_id'],
resource_id=vip['id'],
timestamp=timeutils.utcnow().isoformat(),
resource_metadata=self.extract_metadata(vip)
)
[docs]class LBMemberPollster(BaseLBPollster):
"""Pollster to capture Load Balancer Member status samples."""
FIELDS = ['admin_state_up',
'address',
'pool_id',
'protocol_port',
'status',
'status_description',
'weight',
]
@property
def default_discovery(self):
return 'lb_members'
[docs] def get_samples(self, manager, cache, resources):
resources = resources or []
for member in resources:
LOG.debug("Load Balancer Member : %s" % member)
status = self.get_load_balancer_status_id(member['status'])
if status == -1:
LOG.warning(_("Unknown status %(stat)s received on member "
"%(id)s, skipping sample")
% {'stat': member['status'], 'id': member['id']})
continue
yield sample.Sample(
name='network.services.lb.member',
type=sample.TYPE_GAUGE,
unit='member',
volume=status,
user_id=None,
project_id=member['tenant_id'],
resource_id=member['id'],
timestamp=timeutils.utcnow().isoformat(),
resource_metadata=self.extract_metadata(member)
)
[docs]class LBHealthMonitorPollster(base.BaseServicesPollster):
"""Pollster to capture Load Balancer Health probes status samples."""
FIELDS = ['admin_state_up',
'delay',
'max_retries',
'pools',
'timeout',
'type'
]
@property
def default_discovery(self):
return 'lb_health_probes'
[docs] def get_samples(self, manager, cache, resources):
for probe in resources:
LOG.debug("Load Balancer Health probe : %s" % probe)
yield sample.Sample(
name='network.services.lb.health_monitor',
type=sample.TYPE_GAUGE,
unit='health_monitor',
volume=1,
user_id=None,
project_id=probe['tenant_id'],
resource_id=probe['id'],
timestamp=timeutils.utcnow().isoformat(),
resource_metadata=self.extract_metadata(probe)
)
@six.add_metaclass(abc.ABCMeta)
class _LBStatsPollster(base.BaseServicesPollster):
"""Base Statistics pollster.
It is capturing the statistics info and yielding samples for connections
and bandwidth.
"""
def __init__(self):
super(_LBStatsPollster, self).__init__()
self.client = neutron_client.Client()
self.lb_version = cfg.CONF.service_types.neutron_lbaas_version
@staticmethod
def make_sample_from_pool(pool, name, type, unit, volume,
resource_metadata=None):
if not resource_metadata:
resource_metadata = {}
return sample.Sample(
name=name,
type=type,
unit=unit,
volume=volume,
user_id=None,
project_id=pool['tenant_id'],
resource_id=pool['id'],
timestamp=timeutils.utcnow().isoformat(),
resource_metadata=resource_metadata,
)
def _populate_stats_cache(self, pool_id, cache):
i_cache = cache.setdefault("lbstats", {})
if pool_id not in i_cache:
stats = self.client.pool_stats(pool_id)['stats']
i_cache[pool_id] = LBStatsData(
active_connections=stats['active_connections'],
total_connections=stats['total_connections'],
bytes_in=stats['bytes_in'],
bytes_out=stats['bytes_out'],
)
return i_cache[pool_id]
def _populate_stats_cache_v2(self, loadbalancer_id, cache):
i_cache = cache.setdefault("lbstats", {})
if loadbalancer_id not in i_cache:
stats = self.client.get_loadbalancer_stats(loadbalancer_id)
i_cache[loadbalancer_id] = LBStatsData(
active_connections=stats['active_connections'],
total_connections=stats['total_connections'],
bytes_in=stats['bytes_in'],
bytes_out=stats['bytes_out'],
)
return i_cache[loadbalancer_id]
@property
def default_discovery(self):
discovery_resource = 'lb_pools'
if self.lb_version == 'v2':
discovery_resource = 'lb_loadbalancers'
return discovery_resource
@abc.abstractmethod
def _get_sample(pool, c_data):
"""Return one Sample."""
def get_samples(self, manager, cache, resources):
if self.lb_version == 'v1':
for pool in resources:
try:
c_data = self._populate_stats_cache(pool['id'], cache)
yield self._get_sample(pool, c_data)
except Exception:
LOG.exception(_('Ignoring pool %(pool_id)s'),
{'pool_id': pool['id']})
elif self.lb_version == 'v2':
for loadbalancer in resources:
try:
c_data = self._populate_stats_cache_v2(loadbalancer['id'],
cache)
yield self._get_sample(loadbalancer, c_data)
except Exception:
LOG.exception(
_('Ignoring '
'loadbalancer %(loadbalancer_id)s'),
{'loadbalancer_id': loadbalancer['id']})
[docs]class LBActiveConnectionsPollster(_LBStatsPollster):
"""Pollster to capture Active Load Balancer connections."""
@staticmethod
def _get_sample(pool, data):
return make_sample_from_pool(
pool,
name='network.services.lb.active.connections',
type=sample.TYPE_GAUGE,
unit='connection',
volume=data.active_connections,
)
[docs]class LBTotalConnectionsPollster(_LBStatsPollster):
"""Pollster to capture Total Load Balancer connections."""
@staticmethod
def _get_sample(pool, data):
return make_sample_from_pool(
pool,
name='network.services.lb.total.connections',
type=sample.TYPE_CUMULATIVE,
unit='connection',
volume=data.total_connections,
)
[docs]class LBBytesInPollster(_LBStatsPollster):
"""Pollster to capture incoming bytes."""
@staticmethod
def _get_sample(pool, data):
return make_sample_from_pool(
pool,
name='network.services.lb.incoming.bytes',
type=sample.TYPE_GAUGE,
unit='B',
volume=data.bytes_in,
)
[docs]class LBBytesOutPollster(_LBStatsPollster):
"""Pollster to capture outgoing bytes."""
@staticmethod
def _get_sample(pool, data):
return make_sample_from_pool(
pool,
name='network.services.lb.outgoing.bytes',
type=sample.TYPE_GAUGE,
unit='B',
volume=data.bytes_out,
)
def make_sample_from_pool(pool, name, type, unit, volume,
resource_metadata=None):
resource_metadata = resource_metadata or {}
return sample.Sample(
name=name,
type=type,
unit=unit,
volume=volume,
user_id=None,
project_id=pool['tenant_id'],
resource_id=pool['id'],
timestamp=timeutils.utcnow().isoformat(),
resource_metadata=resource_metadata,
)
[docs]class LBListenerPollster(BaseLBPollster):
"""Pollster to capture Load Balancer Listener status samples."""
FIELDS = ['admin_state_up',
'connection_limit',
'description',
'name',
'default_pool_id',
'protocol',
'protocol_port',
'operating_status',
'loadbalancers'
]
@property
def default_discovery(self):
return 'lb_listeners'
[docs] def get_samples(self, manager, cache, resources):
resources = resources or []
for listener in resources:
LOG.debug("Load Balancer Listener : %s" % listener)
status = self.get_load_balancer_status_id(
listener['operating_status'])
if status == -1:
# unknown status, skip this sample
LOG.warning(_("Unknown status %(stat)s received on listener "
"%(id)s, skipping sample")
% {'stat': listener['operating_status'],
'id': listener['id']})
continue
yield sample.Sample(
name='network.services.lb.listener',
type=sample.TYPE_GAUGE,
unit='listener',
volume=status,
user_id=None,
project_id=listener['tenant_id'],
resource_id=listener['id'],
timestamp=timeutils.utcnow().isoformat(),
resource_metadata=self.extract_metadata(listener)
)
[docs]class LBLoadBalancerPollster(BaseLBPollster):
"""Pollster to capture Load Balancer status samples."""
FIELDS = ['admin_state_up',
'description',
'vip_address',
'listeners',
'name',
'vip_subnet_id',
'operating_status',
]
@property
def default_discovery(self):
return 'lb_loadbalancers'
[docs] def get_samples(self, manager, cache, resources):
resources = resources or []
for loadbalancer in resources:
LOG.debug("Load Balancer: %s" % loadbalancer)
status = self.get_load_balancer_status_id(
loadbalancer['operating_status'])
if status == -1:
# unknown status, skip this sample
LOG.warning(_("Unknown status %(stat)s received "
"on Load Balancer "
"%(id)s, skipping sample")
% {'stat': loadbalancer['operating_status'],
'id': loadbalancer['id']})
continue
yield sample.Sample(
name='network.services.lb.loadbalancer',
type=sample.TYPE_GAUGE,
unit='loadbalancer',
volume=status,
user_id=None,
project_id=loadbalancer['tenant_id'],
resource_id=loadbalancer['id'],
timestamp=timeutils.utcnow().isoformat(),
resource_metadata=self.extract_metadata(loadbalancer)
)