Integration of Ceilometer in Watcher

In current implementation is not easy to use ceilometer.
Watcher must query metrics from the Telemetry v2 API to allow an easiest integration with OpenStack components (especially devstack).

blueprint telemetry-integration
Change-Id: Ide515472f1d160925d9f4aabf48c96dea4f6bc05
This commit is contained in:
Jean-Emile DARTOIS
2015-11-18 14:23:40 +01:00
parent 6a55914b05
commit 827563608f
86 changed files with 924 additions and 1590 deletions

View File

@@ -0,0 +1,173 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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.
#
from urlparse import urlparse
from ceilometerclient import client
from ceilometerclient.exc import HTTPUnauthorized
from watcher.common import keystone
class Client(object):
# todo olso conf: this must be sync with ceilometer
CEILOMETER_API_VERSION = '2'
def __init__(self):
ksclient = keystone.Client()
self.creds = ksclient.get_credentials()
self.creds['os_auth_token'] = ksclient.get_token()
self.creds['token'] = ksclient.get_token()
self.creds['ceilometer_url'] = "http://" + urlparse(
ksclient.get_endpoint(
service_type='metering',
endpoint_type='publicURL')).netloc
self.connect()
def connect(self):
"""Initialization of Ceilometer client."""
self.cmclient = client.get_client(self.CEILOMETER_API_VERSION,
**self.creds)
def build_query(user_id=None, tenant_id=None, resource_id=None,
user_ids=None, tenant_ids=None, resource_ids=None):
"""Returns query built from given parameters.
This query can be then used for querying resources, meters and
statistics.
:Parameters:
- `user_id`: user_id, has a priority over list of ids
- `tenant_id`: tenant_id, has a priority over list of ids
- `resource_id`: resource_id, has a priority over list of ids
- `user_ids`: list of user_ids
- `tenant_ids`: list of tenant_ids
- `resource_ids`: list of resource_ids
"""
user_ids = user_ids or []
tenant_ids = tenant_ids or []
resource_ids = resource_ids or []
query = []
if user_id:
user_ids = [user_id]
for u_id in user_ids:
query.append({"field": "user_id", "op": "eq", "value": u_id})
if tenant_id:
tenant_ids = [tenant_id]
for t_id in tenant_ids:
query.append({"field": "project_id", "op": "eq", "value": t_id})
if resource_id:
resource_ids = [resource_id]
for r_id in resource_ids:
query.append({"field": "resource_id", "op": "eq", "value": r_id})
return query
def query_sample(self, meter_name, query, limit=1):
try:
samples = self.ceilometerclient().samples.list(
meter_name=meter_name,
limit=limit,
q=query)
except HTTPUnauthorized:
self.connect()
samples = self.ceilometerclient().samples.list(
meter_name=meter_name,
limit=limit,
q=query)
except Exception:
raise
return samples
def get_endpoint(self, service_type, endpoint_type=None):
ksclient = keystone.Client()
endpoint = ksclient.get_endpoint(service_type=service_type,
endpoint_type=endpoint_type)
return endpoint
def statistic_list(self, meter_name, query=None, period=None):
"""List of statistics."""
statistics = self.ceilometerclient().statistics.list(
meter_name=meter_name, q=query, period=period)
return statistics
def meter_list(self, query=None):
"""List the user's meters."""
meters = self.ceilometerclient().meters.list(query)
return meters
def statistic_aggregation(self,
resource_id,
meter_name,
period,
aggregate='avg'):
"""
:param resource_id: id
:param meter_name: meter names of which we want the statistics
:param period: `period`: In seconds. If no period is given, only one
aggregate statistic is returned. If given, a faceted
result will be returned, divided into given periods.
Periods with no data are ignored.
:param aggregate:
:return:
"""
"""Representing a statistic aggregate by operators"""
query = self.build_query(resource_id=resource_id)
try:
statistic = self.cmclient.statistics.list(
meter_name=meter_name,
q=query,
period=period,
aggregates=[
{'func': aggregate}],
groupby=['resource_id'])
except HTTPUnauthorized:
self.connect()
statistic = self.cmclient.statistics.list(
meter_name=meter_name,
q=query,
period=period,
aggregates=[
{'func': aggregate}],
groupby=['resource_id'])
except Exception:
raise
item_value = None
if statistic:
item_value = statistic[-1]._info.get('aggregate').get('avg')
return item_value
def get_last_sample_values(self, resource_id, meter_name, limit=1):
samples = self.query_sample(meter_name=meter_name,
query=self.build_query(resource_id))
values = []
for index, sample in enumerate(samples):
values.append(
{'sample_%s' % index: {'timestamp': sample._info['timestamp'],
'value': sample._info[
'counter_volume']}})
return values
def get_last_sample_value(self, resource_id, meter_name):
samples = self.query_sample(meter_name=meter_name,
query=self.build_query(resource_id))
if samples:
return samples[-1]._info['counter_volume']
else:
return False

View File

@@ -0,0 +1,80 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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 datetime
from keystoneclient.auth.identity import v3
from keystoneclient import session
import keystoneclient.v3.client as ksclient
from oslo_config import cfg
from oslo_log import log
LOG = log.getLogger(__name__)
CONF = cfg.CONF
CONF.import_opt('admin_user', 'keystonemiddleware.auth_token',
group='keystone_authtoken')
CONF.import_opt('admin_tenant_name', 'keystonemiddleware.auth_token',
group='keystone_authtoken')
CONF.import_opt('admin_password', 'keystonemiddleware.auth_token',
group='keystone_authtoken')
CONF.import_opt('auth_uri', 'keystonemiddleware.auth_token',
group='keystone_authtoken')
class Client(object):
def __init__(self):
ks_args = self.get_credentials()
self.ks_client = ksclient.Client(**ks_args)
def get_endpoint(self, **kwargs):
attr = None
filter_value = None
if kwargs.get('region_name'):
attr = 'region'
filter_value = kwargs.get('region_name')
return self.ks_client.service_catalog.url_for(
service_type=kwargs.get('service_type') or 'metering',
attr=attr,
filter_value=filter_value,
endpoint_type=kwargs.get('endpoint_type') or 'publicURL')
def get_token(self):
return self.ks_client.auth_token
@staticmethod
def get_credentials():
creds = \
{'auth_url': CONF.keystone_authtoken.auth_uri,
'username': CONF.keystone_authtoken.admin_user,
'password': CONF.keystone_authtoken.admin_password,
'project_name': CONF.keystone_authtoken.admin_tenant_name,
'user_domain_name': "default",
'project_domain_name': "default"}
LOG.debug(creds)
return creds
def get_session(self):
creds = self.get_credentials()
auth = v3.Password(**creds)
return session.Session(auth=auth)
def is_token_expired(self, token):
expires = datetime.datetime.strptime(token['expires'],
'%Y-%m-%dT%H:%M:%SZ')
return datetime.datetime.now() >= expires