Added Monasca Helper
In this changeset, I implemented a Helper class to deal with Monasca requests. Change-Id: I14cfab2c45451b8bb2ea5f1f48254b41fa5abae8 Partially-Implements: blueprint monasca-support
This commit is contained in:
parent
dad90b63fd
commit
a015af1bd2
@ -31,6 +31,7 @@ python-ceilometerclient>=2.5.0 # Apache-2.0
|
||||
python-cinderclient!=1.7.0,!=1.7.1,>=1.6.0 # Apache-2.0
|
||||
python-glanceclient>=2.5.0 # Apache-2.0
|
||||
python-keystoneclient>=3.8.0 # Apache-2.0
|
||||
python-monascaclient>=1.1.0 # Apache-2.0
|
||||
python-neutronclient>=5.1.0 # Apache-2.0
|
||||
python-novaclient!=2.33.0,>=2.29.0 # Apache-2.0
|
||||
python-openstackclient>=3.3.0 # Apache-2.0
|
||||
|
@ -15,9 +15,9 @@ from cinderclient import client as ciclient
|
||||
from glanceclient import client as glclient
|
||||
from keystoneauth1 import loading as ka_loading
|
||||
from keystoneclient import client as keyclient
|
||||
from monascaclient import client as monclient
|
||||
from neutronclient.neutron import client as netclient
|
||||
from novaclient import client as nvclient
|
||||
from oslo_config import cfg
|
||||
|
||||
from watcher.common import exception
|
||||
|
||||
@ -41,12 +41,13 @@ class OpenStackClients(object):
|
||||
self._glance = None
|
||||
self._cinder = None
|
||||
self._ceilometer = None
|
||||
self._monasca = None
|
||||
self._neutron = None
|
||||
|
||||
def _get_keystone_session(self):
|
||||
auth = ka_loading.load_auth_from_conf_options(cfg.CONF,
|
||||
auth = ka_loading.load_auth_from_conf_options(CONF,
|
||||
_CLIENTS_AUTH_GROUP)
|
||||
sess = ka_loading.load_session_from_conf_options(cfg.CONF,
|
||||
sess = ka_loading.load_session_from_conf_options(CONF,
|
||||
_CLIENTS_AUTH_GROUP,
|
||||
auth=auth)
|
||||
return sess
|
||||
@ -62,7 +63,7 @@ class OpenStackClients(object):
|
||||
return self._session
|
||||
|
||||
def _get_client_option(self, client, option):
|
||||
return getattr(getattr(cfg.CONF, '%s_client' % client), option)
|
||||
return getattr(getattr(CONF, '%s_client' % client), option)
|
||||
|
||||
@exception.wrap_keystone_exception
|
||||
def keystone(self):
|
||||
@ -112,6 +113,35 @@ class OpenStackClients(object):
|
||||
session=self.session)
|
||||
return self._ceilometer
|
||||
|
||||
@exception.wrap_keystone_exception
|
||||
def monasca(self):
|
||||
if self._monasca:
|
||||
return self._monasca
|
||||
|
||||
monascaclient_version = self._get_client_option(
|
||||
'monasca', 'api_version')
|
||||
token = self.session.get_token()
|
||||
watcher_clients_auth_config = CONF.get(_CLIENTS_AUTH_GROUP)
|
||||
service_type = 'monitoring'
|
||||
monasca_kwargs = {
|
||||
'auth_url': watcher_clients_auth_config.auth_url,
|
||||
'cert_file': watcher_clients_auth_config.certfile,
|
||||
'insecure': watcher_clients_auth_config.insecure,
|
||||
'key_file': watcher_clients_auth_config.keyfile,
|
||||
'keystone_timeout': watcher_clients_auth_config.timeout,
|
||||
'os_cacert': watcher_clients_auth_config.cafile,
|
||||
'service_type': service_type,
|
||||
'token': token,
|
||||
'username': watcher_clients_auth_config.username,
|
||||
'password': watcher_clients_auth_config.password,
|
||||
}
|
||||
endpoint = self.session.get_endpoint(service_type=service_type)
|
||||
|
||||
self._monasca = monclient.Client(
|
||||
monascaclient_version, endpoint, **monasca_kwargs)
|
||||
|
||||
return self._monasca
|
||||
|
||||
@exception.wrap_keystone_exception
|
||||
def neutron(self):
|
||||
if self._neutron:
|
||||
|
@ -28,6 +28,7 @@ from watcher.conf import db
|
||||
from watcher.conf import decision_engine
|
||||
from watcher.conf import exception
|
||||
from watcher.conf import glance_client
|
||||
from watcher.conf import monasca_client
|
||||
from watcher.conf import neutron_client
|
||||
from watcher.conf import nova_client
|
||||
from watcher.conf import paths
|
||||
@ -46,6 +47,7 @@ db.register_opts(CONF)
|
||||
planner.register_opts(CONF)
|
||||
applier.register_opts(CONF)
|
||||
decision_engine.register_opts(CONF)
|
||||
monasca_client.register_opts(CONF)
|
||||
nova_client.register_opts(CONF)
|
||||
glance_client.register_opts(CONF)
|
||||
cinder_client.register_opts(CONF)
|
||||
|
36
watcher/conf/monasca_client.py
Normal file
36
watcher/conf/monasca_client.py
Normal file
@ -0,0 +1,36 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2016 Intel Corp
|
||||
#
|
||||
# Authors: Prudhvi Rao Shedimbi <prudhvi.rao.shedimbi@intel.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 oslo_config import cfg
|
||||
|
||||
monasca_client = cfg.OptGroup(name='monasca_client',
|
||||
title='Configuration Options for Monasca')
|
||||
|
||||
MONASCA_CLIENT_OPTS = [
|
||||
cfg.StrOpt('api_version',
|
||||
default='2_0',
|
||||
help='Version of Monasca API to use in monascaclient.')]
|
||||
|
||||
|
||||
def register_opts(conf):
|
||||
conf.register_group(monasca_client)
|
||||
conf.register_opts(MONASCA_CLIENT_OPTS, group=monasca_client)
|
||||
|
||||
|
||||
def list_opts():
|
||||
return [('monasca_client', MONASCA_CLIENT_OPTS)]
|
@ -165,9 +165,9 @@ class CeilometerHelper(object):
|
||||
values = []
|
||||
for index, sample in enumerate(samples):
|
||||
values.append(
|
||||
{'sample_%s' % index: {'timestamp': sample._info['timestamp'],
|
||||
'value': sample._info[
|
||||
'counter_volume']}})
|
||||
{'sample_%s' % index: {
|
||||
'timestamp': sample._info['timestamp'],
|
||||
'value': sample._info['counter_volume']}})
|
||||
return values
|
||||
|
||||
def get_last_sample_value(self, resource_id, meter_name):
|
124
watcher/datasource/monasca.py
Normal file
124
watcher/datasource/monasca.py
Normal file
@ -0,0 +1,124 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2016 b<>com
|
||||
#
|
||||
# Authors: Vincent FRANCOISE <vincent.francoise@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 monascaclient import exc
|
||||
|
||||
from watcher.common import clients
|
||||
|
||||
|
||||
class MonascaHelper(object):
|
||||
|
||||
def __init__(self, osc=None):
|
||||
""":param osc: an OpenStackClients instance"""
|
||||
self.osc = osc if osc else clients.OpenStackClients()
|
||||
self.monasca = self.osc.monasca()
|
||||
|
||||
def query_retry(self, f, *args, **kwargs):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except exc.HTTPUnauthorized:
|
||||
self.osc.reset_clients()
|
||||
self.monasca = self.osc.monasca()
|
||||
return f(*args, **kwargs)
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def _format_time_params(self, start_time, end_time, period):
|
||||
"""Format time-related params to the correct Monasca format
|
||||
|
||||
:param start_time: Start datetime from which metrics will be used
|
||||
:param end_time: End datetime from which metrics will be used
|
||||
:param period: interval in seconds (int)
|
||||
:return: start ISO time, end ISO time, period
|
||||
"""
|
||||
|
||||
if not period:
|
||||
period = int(datetime.timedelta(hours=3).total_seconds())
|
||||
if not start_time:
|
||||
start_time = (
|
||||
datetime.datetime.utcnow() -
|
||||
datetime.timedelta(seconds=period))
|
||||
|
||||
start_timestamp = None if not start_time else start_time.isoformat()
|
||||
end_timestamp = None if not end_time else end_time.isoformat()
|
||||
|
||||
return start_timestamp, end_timestamp, period
|
||||
|
||||
def statistics_list(self, meter_name, dimensions, start_time=None,
|
||||
end_time=None, period=None,):
|
||||
"""List of statistics."""
|
||||
start_timestamp, end_timestamp, period = self._format_time_params(
|
||||
start_time, end_time, period
|
||||
)
|
||||
raw_kwargs = dict(
|
||||
name=meter_name,
|
||||
start_time=start_timestamp,
|
||||
end_time=end_timestamp,
|
||||
dimensions=dimensions,
|
||||
)
|
||||
|
||||
kwargs = {k: v for k, v in raw_kwargs.items() if k and v}
|
||||
|
||||
statistics = self.query_retry(
|
||||
f=self.monasca.metrics.list_measurements, **kwargs)
|
||||
|
||||
return statistics
|
||||
|
||||
def statistic_aggregation(self,
|
||||
meter_name,
|
||||
dimensions,
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
period=None,
|
||||
aggregate='avg',
|
||||
group_by='*'):
|
||||
"""Representing a statistic aggregate by operators
|
||||
|
||||
:param meter_name: meter names of which we want the statistics
|
||||
:param dimensions: dimensions (dict)
|
||||
:param start_time: Start datetime from which metrics will be used
|
||||
:param end_time: End datetime from which metrics will be used
|
||||
:param period: Sampling `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: Should be either 'avg', 'count', 'min' or 'max'
|
||||
:return: A list of dict with each dict being a distinct result row
|
||||
"""
|
||||
start_timestamp, end_timestamp, period = self._format_time_params(
|
||||
start_time, end_time, period
|
||||
)
|
||||
|
||||
raw_kwargs = dict(
|
||||
name=meter_name,
|
||||
start_time=start_timestamp,
|
||||
end_time=end_timestamp,
|
||||
dimensions=dimensions,
|
||||
period=period,
|
||||
statistics=aggregate,
|
||||
group_by=group_by,
|
||||
)
|
||||
|
||||
kwargs = {k: v for k, v in raw_kwargs.items() if k and v}
|
||||
|
||||
statistics = self.query_retry(
|
||||
f=self.monasca.metrics.list_statistics, **kwargs)
|
||||
|
||||
return statistics
|
@ -1,80 +0,0 @@
|
||||
# -*- 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.
|
||||
#
|
||||
|
||||
"""
|
||||
The :ref:`Cluster History <cluster_history_definition>` contains all the
|
||||
previously collected timestamped data such as metrics and events associated
|
||||
to any :ref:`managed resource <managed_resource_definition>` of the
|
||||
:ref:`Cluster <cluster_definition>`.
|
||||
|
||||
Just like the :ref:`Cluster Data Model <cluster_data_model_definition>`, this
|
||||
history may be used by any :ref:`Strategy <strategy_definition>` in order to
|
||||
find the most optimal :ref:`Solution <solution_definition>` during an
|
||||
:ref:`Audit <audit_definition>`.
|
||||
|
||||
In the Watcher project, a generic
|
||||
:ref:`Cluster History <cluster_history_definition>`
|
||||
API is proposed with some helper classes in order to :
|
||||
|
||||
- share a common measurement (events or metrics) naming based on what is
|
||||
defined in Ceilometer.
|
||||
See `the full list of available measurements <http://docs.openstack.org/admin-guide/telemetry-measurements.html>`_
|
||||
- share common meter types (Cumulative, Delta, Gauge) based on what is
|
||||
defined in Ceilometer.
|
||||
See `the full list of meter types <http://docs.openstack.org/admin-guide/telemetry-measurements.html>`_
|
||||
- simplify the development of a new :ref:`Strategy <strategy_definition>`
|
||||
- avoid duplicating the same code in several
|
||||
:ref:`Strategies <strategy_definition>`
|
||||
- have a better consistency between the different
|
||||
:ref:`Strategies <strategy_definition>`
|
||||
- avoid any strong coupling with any external metrics/events storage system
|
||||
(the proposed API and measurement naming system acts as a pivot format)
|
||||
|
||||
Note however that a developer can use his/her own history management system if
|
||||
the Ceilometer system does not fit his/her needs as long as the
|
||||
:ref:`Strategy <strategy_definition>` is able to produce a
|
||||
:ref:`Solution <solution_definition>` for the requested
|
||||
:ref:`Goal <goal_definition>`.
|
||||
|
||||
The :ref:`Cluster History <cluster_history_definition>` data may be persisted
|
||||
in any appropriate storage system (InfluxDB, OpenTSDB, MongoDB,...).
|
||||
""" # noqa
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
"""Work in progress Helper to query metrics"""
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseClusterHistory(object):
|
||||
@abc.abstractmethod
|
||||
def statistic_aggregation(self, resource_id, meter_name, period,
|
||||
aggregate='avg'):
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_last_sample_values(self, resource_id, meter_name, limit=1):
|
||||
raise NotImplementedError()
|
||||
|
||||
def query_sample(self, meter_name, query, limit=1):
|
||||
raise NotImplementedError()
|
||||
|
||||
def statistic_list(self, meter_name, query=None, period=None):
|
||||
raise NotImplementedError()
|
@ -1,45 +0,0 @@
|
||||
# -*- 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 watcher.common import ceilometer_helper
|
||||
|
||||
from watcher.decision_engine.cluster.history import base
|
||||
|
||||
|
||||
class CeilometerClusterHistory(base.BaseClusterHistory):
|
||||
def __init__(self, osc=None):
|
||||
""":param osc: an OpenStackClients instance"""
|
||||
super(CeilometerClusterHistory, self).__init__()
|
||||
self.ceilometer = ceilometer_helper.CeilometerHelper(osc=osc)
|
||||
|
||||
def statistic_list(self, meter_name, query=None, period=None):
|
||||
return self.ceilometer.statistic_list(meter_name, query, period)
|
||||
|
||||
def query_sample(self, meter_name, query, limit=1):
|
||||
return self.ceilometer.query_sample(meter_name, query, limit)
|
||||
|
||||
def get_last_sample_values(self, resource_id, meter_name, limit=1):
|
||||
return self.ceilometer.get_last_sample_values(resource_id, meter_name,
|
||||
limit)
|
||||
|
||||
def statistic_aggregation(self, resource_id, meter_name, period,
|
||||
aggregate='avg'):
|
||||
return self.ceilometer.statistic_aggregation(resource_id, meter_name,
|
||||
period,
|
||||
aggregate)
|
@ -39,7 +39,7 @@ from oslo_log import log
|
||||
|
||||
from watcher._i18n import _, _LE, _LI, _LW
|
||||
from watcher.common import exception
|
||||
from watcher.decision_engine.cluster.history import ceilometer as cch
|
||||
from watcher.datasource import ceilometer as ceil
|
||||
from watcher.decision_engine.model import element
|
||||
from watcher.decision_engine.strategy.strategies import base
|
||||
|
||||
@ -114,7 +114,7 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
|
||||
@property
|
||||
def ceilometer(self):
|
||||
if self._ceilometer is None:
|
||||
self._ceilometer = cch.CeilometerClusterHistory(osc=self.osc)
|
||||
self._ceilometer = ceil.CeilometerHelper(osc=self.osc)
|
||||
return self._ceilometer
|
||||
|
||||
@ceilometer.setter
|
||||
|
@ -32,7 +32,7 @@ from oslo_log import log
|
||||
|
||||
from watcher._i18n import _, _LW, _LI
|
||||
from watcher.common import exception as wexc
|
||||
from watcher.decision_engine.cluster.history import ceilometer as ceil
|
||||
from watcher.datasource import ceilometer as ceil
|
||||
from watcher.decision_engine.model import element
|
||||
from watcher.decision_engine.strategy.strategies import base
|
||||
|
||||
@ -114,7 +114,7 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
|
||||
@property
|
||||
def ceilometer(self):
|
||||
if self._ceilometer is None:
|
||||
self._ceilometer = ceil.CeilometerClusterHistory(osc=self.osc)
|
||||
self._ceilometer = ceil.CeilometerHelper(osc=self.osc)
|
||||
return self._ceilometer
|
||||
|
||||
@ceilometer.setter
|
||||
|
@ -47,7 +47,7 @@ from oslo_log import log
|
||||
|
||||
from watcher._i18n import _, _LE, _LI, _LW
|
||||
from watcher.common import exception as wexc
|
||||
from watcher.decision_engine.cluster.history import ceilometer as ceil
|
||||
from watcher.datasource import ceilometer as ceil
|
||||
from watcher.decision_engine.model import element
|
||||
from watcher.decision_engine.strategy.strategies import base
|
||||
|
||||
@ -110,7 +110,7 @@ class UniformAirflow(base.BaseStrategy):
|
||||
@property
|
||||
def ceilometer(self):
|
||||
if self._ceilometer is None:
|
||||
self._ceilometer = ceil.CeilometerClusterHistory(osc=self.osc)
|
||||
self._ceilometer = ceil.CeilometerHelper(osc=self.osc)
|
||||
return self._ceilometer
|
||||
|
||||
@ceilometer.setter
|
||||
|
@ -58,8 +58,7 @@ import six
|
||||
|
||||
from watcher._i18n import _, _LE, _LI
|
||||
from watcher.common import exception
|
||||
from watcher.decision_engine.cluster.history import ceilometer \
|
||||
as ceilometer_cluster_history
|
||||
from watcher.datasource import ceilometer as ceil
|
||||
from watcher.decision_engine.model import element
|
||||
from watcher.decision_engine.strategy.strategies import base
|
||||
|
||||
@ -91,8 +90,7 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
|
||||
@property
|
||||
def ceilometer(self):
|
||||
if self._ceilometer is None:
|
||||
self._ceilometer = (ceilometer_cluster_history.
|
||||
CeilometerClusterHistory(osc=self.osc))
|
||||
self._ceilometer = ceil.CeilometerHelper(osc=self.osc)
|
||||
return self._ceilometer
|
||||
|
||||
@ceilometer.setter
|
||||
|
@ -51,7 +51,7 @@ from oslo_log import log
|
||||
|
||||
from watcher._i18n import _, _LE, _LI, _LW
|
||||
from watcher.common import exception as wexc
|
||||
from watcher.decision_engine.cluster.history import ceilometer as ceil
|
||||
from watcher.datasource import ceilometer as ceil
|
||||
from watcher.decision_engine.model import element
|
||||
from watcher.decision_engine.strategy.strategies import base
|
||||
|
||||
@ -108,7 +108,7 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
|
||||
@property
|
||||
def ceilometer(self):
|
||||
if self._ceilometer is None:
|
||||
self._ceilometer = ceil.CeilometerClusterHistory(osc=self.osc)
|
||||
self._ceilometer = ceil.CeilometerHelper(osc=self.osc)
|
||||
return self._ceilometer
|
||||
|
||||
@ceilometer.setter
|
||||
|
@ -40,8 +40,7 @@ import oslo_utils
|
||||
|
||||
from watcher._i18n import _LI, _LW, _
|
||||
from watcher.common import exception
|
||||
from watcher.decision_engine.cluster.history import ceilometer as \
|
||||
ceilometer_cluster_history
|
||||
from watcher.datasource import ceilometer as ceil
|
||||
from watcher.decision_engine.model import element
|
||||
from watcher.decision_engine.strategy.strategies import base
|
||||
|
||||
@ -157,8 +156,7 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy):
|
||||
@property
|
||||
def ceilometer(self):
|
||||
if self._ceilometer is None:
|
||||
self._ceilometer = (ceilometer_cluster_history.
|
||||
CeilometerClusterHistory(osc=self.osc))
|
||||
self._ceilometer = ceil.CeilometerHelper(osc=self.osc)
|
||||
return self._ceilometer
|
||||
|
||||
@property
|
||||
|
@ -17,38 +17,31 @@ from cinderclient.v1 import client as ciclient_v1
|
||||
from glanceclient import client as glclient
|
||||
from keystoneauth1 import loading as ka_loading
|
||||
import mock
|
||||
from monascaclient import client as monclient
|
||||
from monascaclient.v2_0 import client as monclient_v2
|
||||
from neutronclient.neutron import client as netclient
|
||||
from neutronclient.v2_0 import client as netclient_v2
|
||||
from novaclient import client as nvclient
|
||||
from oslo_config import cfg
|
||||
|
||||
from watcher.common import clients
|
||||
from watcher import conf
|
||||
from watcher.tests import base
|
||||
|
||||
CONF = conf.CONF
|
||||
|
||||
|
||||
class TestClients(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestClients, self).setUp()
|
||||
|
||||
cfg.CONF.import_opt('api_version', 'watcher.common.clients',
|
||||
group='nova_client')
|
||||
cfg.CONF.import_opt('api_version', 'watcher.common.clients',
|
||||
group='glance_client')
|
||||
cfg.CONF.import_opt('api_version', 'watcher.common.clients',
|
||||
group='cinder_client')
|
||||
cfg.CONF.import_opt('api_version', 'watcher.common.clients',
|
||||
group='ceilometer_client')
|
||||
cfg.CONF.import_opt('api_version', 'watcher.common.clients',
|
||||
group='neutron_client')
|
||||
|
||||
def test_get_keystone_session(self):
|
||||
def _register_watcher_clients_auth_opts(self):
|
||||
_AUTH_CONF_GROUP = 'watcher_clients_auth'
|
||||
ka_loading.register_auth_conf_options(cfg.CONF, _AUTH_CONF_GROUP)
|
||||
ka_loading.register_session_conf_options(cfg.CONF, _AUTH_CONF_GROUP)
|
||||
ka_loading.register_auth_conf_options(CONF, _AUTH_CONF_GROUP)
|
||||
ka_loading.register_session_conf_options(CONF, _AUTH_CONF_GROUP)
|
||||
CONF.set_override('auth_type', 'password', group=_AUTH_CONF_GROUP)
|
||||
|
||||
cfg.CONF.set_override('auth_type', 'password',
|
||||
group=_AUTH_CONF_GROUP)
|
||||
# ka_loading.load_auth_from_conf_options(CONF, _AUTH_CONF_GROUP)
|
||||
# ka_loading.load_session_from_conf_options(CONF, _AUTH_CONF_GROUP)
|
||||
# CONF.set_override(
|
||||
# 'auth-url', 'http://server.ip:35357', group=_AUTH_CONF_GROUP)
|
||||
|
||||
# If we don't clean up the _AUTH_CONF_GROUP conf options, then other
|
||||
# tests that run after this one will fail, complaining about required
|
||||
@ -56,12 +49,18 @@ class TestClients(base.TestCase):
|
||||
def cleanup_conf_from_loading():
|
||||
# oslo_config doesn't seem to allow unregistering groups through a
|
||||
# single method, so we do this instead
|
||||
cfg.CONF.reset()
|
||||
del cfg.CONF._groups[_AUTH_CONF_GROUP]
|
||||
CONF.reset()
|
||||
del CONF._groups[_AUTH_CONF_GROUP]
|
||||
|
||||
self.addCleanup(cleanup_conf_from_loading)
|
||||
|
||||
osc = clients.OpenStackClients()
|
||||
def reset_register_opts_mock(conf_obj, original_method):
|
||||
conf_obj.register_opts = original_method
|
||||
|
||||
original_register_opts = CONF.register_opts
|
||||
self.addCleanup(reset_register_opts_mock,
|
||||
CONF,
|
||||
original_register_opts)
|
||||
|
||||
expected = {'username': 'foousername',
|
||||
'password': 'foopassword',
|
||||
@ -69,14 +68,6 @@ class TestClients(base.TestCase):
|
||||
'user_domain_id': 'foouserdomainid',
|
||||
'project_domain_id': 'fooprojdomainid'}
|
||||
|
||||
def reset_register_opts_mock(conf_obj, original_method):
|
||||
conf_obj.register_opts = original_method
|
||||
|
||||
original_register_opts = cfg.CONF.register_opts
|
||||
self.addCleanup(reset_register_opts_mock,
|
||||
cfg.CONF,
|
||||
original_register_opts)
|
||||
|
||||
# Because some of the conf options for auth plugins are not registered
|
||||
# until right before they are loaded, and because the method that does
|
||||
# the actual loading of the conf option values is an anonymous method
|
||||
@ -88,10 +79,21 @@ class TestClients(base.TestCase):
|
||||
ret = original_register_opts(*args, **kwargs)
|
||||
if 'group' in kwargs and kwargs['group'] == _AUTH_CONF_GROUP:
|
||||
for key, value in expected.items():
|
||||
cfg.CONF.set_override(key, value, group=_AUTH_CONF_GROUP)
|
||||
CONF.set_override(key, value, group=_AUTH_CONF_GROUP)
|
||||
return ret
|
||||
|
||||
cfg.CONF.register_opts = mock_register_opts
|
||||
CONF.register_opts = mock_register_opts
|
||||
|
||||
def test_get_keystone_session(self):
|
||||
self._register_watcher_clients_auth_opts()
|
||||
|
||||
osc = clients.OpenStackClients()
|
||||
|
||||
expected = {'username': 'foousername',
|
||||
'password': 'foopassword',
|
||||
'auth_url': 'http://server.ip:35357',
|
||||
'user_domain_id': 'foouserdomainid',
|
||||
'project_domain_id': 'fooprojdomainid'}
|
||||
|
||||
sess = osc.session
|
||||
self.assertEqual(expected['auth_url'], sess.auth.auth_url)
|
||||
@ -107,13 +109,12 @@ class TestClients(base.TestCase):
|
||||
osc = clients.OpenStackClients()
|
||||
osc._nova = None
|
||||
osc.nova()
|
||||
mock_call.assert_called_once_with(cfg.CONF.nova_client.api_version,
|
||||
mock_call.assert_called_once_with(CONF.nova_client.api_version,
|
||||
session=mock_session)
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_nova_diff_vers(self, mock_session):
|
||||
cfg.CONF.set_override('api_version', '2.3',
|
||||
group='nova_client')
|
||||
CONF.set_override('api_version', '2.3', group='nova_client')
|
||||
osc = clients.OpenStackClients()
|
||||
osc._nova = None
|
||||
osc.nova()
|
||||
@ -133,13 +134,12 @@ class TestClients(base.TestCase):
|
||||
osc = clients.OpenStackClients()
|
||||
osc._glance = None
|
||||
osc.glance()
|
||||
mock_call.assert_called_once_with(cfg.CONF.glance_client.api_version,
|
||||
mock_call.assert_called_once_with(CONF.glance_client.api_version,
|
||||
session=mock_session)
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_glance_diff_vers(self, mock_session):
|
||||
cfg.CONF.set_override('api_version', '1',
|
||||
group='glance_client')
|
||||
CONF.set_override('api_version', '1', group='glance_client')
|
||||
osc = clients.OpenStackClients()
|
||||
osc._glance = None
|
||||
osc.glance()
|
||||
@ -159,13 +159,12 @@ class TestClients(base.TestCase):
|
||||
osc = clients.OpenStackClients()
|
||||
osc._cinder = None
|
||||
osc.cinder()
|
||||
mock_call.assert_called_once_with(cfg.CONF.cinder_client.api_version,
|
||||
mock_call.assert_called_once_with(CONF.cinder_client.api_version,
|
||||
session=mock_session)
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_cinder_diff_vers(self, mock_session):
|
||||
cfg.CONF.set_override('api_version', '1',
|
||||
group='cinder_client')
|
||||
CONF.set_override('api_version', '1', group='cinder_client')
|
||||
osc = clients.OpenStackClients()
|
||||
osc._cinder = None
|
||||
osc.cinder()
|
||||
@ -186,7 +185,7 @@ class TestClients(base.TestCase):
|
||||
osc._ceilometer = None
|
||||
osc.ceilometer()
|
||||
mock_call.assert_called_once_with(
|
||||
cfg.CONF.ceilometer_client.api_version,
|
||||
CONF.ceilometer_client.api_version,
|
||||
None,
|
||||
session=mock_session)
|
||||
|
||||
@ -196,8 +195,8 @@ class TestClients(base.TestCase):
|
||||
mock_session):
|
||||
'''ceilometerclient currently only has one version (v2)'''
|
||||
mock_get_alarm_client.return_value = [mock.Mock(), mock.Mock()]
|
||||
cfg.CONF.set_override('api_version', '2',
|
||||
group='ceilometer_client')
|
||||
CONF.set_override('api_version', '2',
|
||||
group='ceilometer_client')
|
||||
osc = clients.OpenStackClients()
|
||||
osc._ceilometer = None
|
||||
osc.ceilometer()
|
||||
@ -221,14 +220,14 @@ class TestClients(base.TestCase):
|
||||
osc = clients.OpenStackClients()
|
||||
osc._neutron = None
|
||||
osc.neutron()
|
||||
mock_call.assert_called_once_with(cfg.CONF.neutron_client.api_version,
|
||||
mock_call.assert_called_once_with(CONF.neutron_client.api_version,
|
||||
session=mock_session)
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_neutron_diff_vers(self, mock_session):
|
||||
'''neutronclient currently only has one version (v2)'''
|
||||
cfg.CONF.set_override('api_version', '2.0',
|
||||
group='neutron_client')
|
||||
CONF.set_override('api_version', '2.0',
|
||||
group='neutron_client')
|
||||
osc = clients.OpenStackClients()
|
||||
osc._neutron = None
|
||||
osc.neutron()
|
||||
@ -242,3 +241,51 @@ class TestClients(base.TestCase):
|
||||
neutron = osc.neutron()
|
||||
neutron_cached = osc.neutron()
|
||||
self.assertEqual(neutron, neutron_cached)
|
||||
|
||||
@mock.patch.object(monclient, 'Client')
|
||||
@mock.patch.object(ka_loading, 'load_session_from_conf_options')
|
||||
def test_clients_monasca(self, mock_session, mock_call):
|
||||
mock_session.return_value = mock.Mock(
|
||||
get_endpoint=mock.Mock(return_value='test_endpoint'),
|
||||
get_token=mock.Mock(return_value='test_token'),)
|
||||
|
||||
self._register_watcher_clients_auth_opts()
|
||||
|
||||
osc = clients.OpenStackClients()
|
||||
osc._monasca = None
|
||||
osc.monasca()
|
||||
mock_call.assert_called_once_with(
|
||||
CONF.monasca_client.api_version,
|
||||
'test_endpoint',
|
||||
auth_url='http://server.ip:35357', cert_file=None, insecure=False,
|
||||
key_file=None, keystone_timeout=None, os_cacert=None,
|
||||
password='foopassword', service_type='monitoring',
|
||||
token='test_token', username='foousername')
|
||||
|
||||
@mock.patch.object(ka_loading, 'load_session_from_conf_options')
|
||||
def test_clients_monasca_diff_vers(self, mock_session):
|
||||
mock_session.return_value = mock.Mock(
|
||||
get_endpoint=mock.Mock(return_value='test_endpoint'),
|
||||
get_token=mock.Mock(return_value='test_token'),)
|
||||
|
||||
self._register_watcher_clients_auth_opts()
|
||||
|
||||
CONF.set_override('api_version', '2_0', group='monasca_client')
|
||||
osc = clients.OpenStackClients()
|
||||
osc._monasca = None
|
||||
osc.monasca()
|
||||
self.assertEqual(monclient_v2.Client, type(osc.monasca()))
|
||||
|
||||
@mock.patch.object(ka_loading, 'load_session_from_conf_options')
|
||||
def test_clients_monasca_cached(self, mock_session):
|
||||
mock_session.return_value = mock.Mock(
|
||||
get_endpoint=mock.Mock(return_value='test_endpoint'),
|
||||
get_token=mock.Mock(return_value='test_token'),)
|
||||
|
||||
self._register_watcher_clients_auth_opts()
|
||||
|
||||
osc = clients.OpenStackClients()
|
||||
osc._monasca = None
|
||||
monasca = osc.monasca()
|
||||
monasca_cached = osc.monasca()
|
||||
self.assertEqual(monasca, monasca_cached)
|
||||
|
@ -31,7 +31,7 @@ class TestListOpts(base.TestCase):
|
||||
'DEFAULT', 'api', 'database', 'watcher_decision_engine',
|
||||
'watcher_applier', 'watcher_planner', 'nova_client',
|
||||
'glance_client', 'cinder_client', 'ceilometer_client',
|
||||
'neutron_client', 'watcher_clients_auth']
|
||||
'monasca_client', 'neutron_client', 'watcher_clients_auth']
|
||||
self.opt_sections = list(dict(opts.list_opts()).keys())
|
||||
|
||||
def test_run_list_opts(self):
|
||||
|
@ -19,8 +19,8 @@ from __future__ import unicode_literals
|
||||
|
||||
import mock
|
||||
|
||||
from watcher.common import ceilometer_helper
|
||||
from watcher.common import clients
|
||||
from watcher.datasource import ceilometer as ceilometer_helper
|
||||
from watcher.tests import base
|
||||
|
||||
|
102
watcher/tests/datasource/test_monasca_helper.py
Normal file
102
watcher/tests/datasource/test_monasca_helper.py
Normal file
@ -0,0 +1,102 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>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 mock
|
||||
from monascaclient import exc
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from watcher.common import clients
|
||||
from watcher.datasource import monasca as monasca_helper
|
||||
from watcher.tests import base
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'monasca')
|
||||
class TestMonascaHelper(base.BaseTestCase):
|
||||
|
||||
def test_monasca_statistic_aggregation(self, mock_monasca):
|
||||
monasca = mock.MagicMock()
|
||||
expected_result = [{
|
||||
'columns': ['timestamp', 'avg'],
|
||||
'dimensions': {
|
||||
'hostname': 'rdev-indeedsrv001',
|
||||
'service': 'monasca'},
|
||||
'id': '0',
|
||||
'name': 'cpu.percent',
|
||||
'statistics': [
|
||||
['2016-07-29T12:45:00Z', 0.0],
|
||||
['2016-07-29T12:50:00Z', 0.9100000000000001],
|
||||
['2016-07-29T12:55:00Z', 0.9111111111111112]]}]
|
||||
|
||||
monasca.metrics.list_statistics.return_value = expected_result
|
||||
mock_monasca.return_value = monasca
|
||||
|
||||
helper = monasca_helper.MonascaHelper()
|
||||
result = helper.statistic_aggregation(
|
||||
meter_name='cpu.percent',
|
||||
dimensions={'hostname': 'NODE_UUID'},
|
||||
start_time=timeutils.parse_isotime("2016-06-06T10:33:22.063176"),
|
||||
end_time=None,
|
||||
period=7200,
|
||||
aggregate='avg',
|
||||
group_by='*',
|
||||
)
|
||||
self.assertEqual(expected_result, result)
|
||||
|
||||
def test_monasca_statistic_list(self, mock_monasca):
|
||||
monasca = mock.MagicMock()
|
||||
expected_result = [{
|
||||
'columns': ['timestamp', 'value', 'value_meta'],
|
||||
'dimensions': {
|
||||
'hostname': 'rdev-indeedsrv001',
|
||||
'service': 'monasca'},
|
||||
'id': '0',
|
||||
'measurements': [
|
||||
['2016-07-29T12:54:06.000Z', 0.9, {}],
|
||||
['2016-07-29T12:54:36.000Z', 0.9, {}],
|
||||
['2016-07-29T12:55:06.000Z', 0.9, {}],
|
||||
['2016-07-29T12:55:36.000Z', 0.8, {}]],
|
||||
'name': 'cpu.percent'}]
|
||||
|
||||
monasca.metrics.list_measurements.return_value = expected_result
|
||||
mock_monasca.return_value = monasca
|
||||
helper = monasca_helper.MonascaHelper()
|
||||
val = helper.statistics_list(meter_name="cpu.percent", dimensions={})
|
||||
self.assertEqual(expected_result, val)
|
||||
|
||||
def test_monasca_statistic_list_query_retry(self, mock_monasca):
|
||||
monasca = mock.MagicMock()
|
||||
expected_result = [{
|
||||
'columns': ['timestamp', 'value', 'value_meta'],
|
||||
'dimensions': {
|
||||
'hostname': 'rdev-indeedsrv001',
|
||||
'service': 'monasca'},
|
||||
'id': '0',
|
||||
'measurements': [
|
||||
['2016-07-29T12:54:06.000Z', 0.9, {}],
|
||||
['2016-07-29T12:54:36.000Z', 0.9, {}],
|
||||
['2016-07-29T12:55:06.000Z', 0.9, {}],
|
||||
['2016-07-29T12:55:36.000Z', 0.8, {}]],
|
||||
'name': 'cpu.percent'}]
|
||||
|
||||
monasca.metrics.list_measurements.side_effect = [
|
||||
exc.HTTPUnauthorized, expected_result]
|
||||
mock_monasca.return_value = monasca
|
||||
helper = monasca_helper.MonascaHelper()
|
||||
val = helper.statistics_list(meter_name="cpu.percent", dimensions={})
|
||||
self.assertEqual(expected_result, val)
|
Loading…
Reference in New Issue
Block a user