Merge "Add gnocchi support in outlet_temp_control strategy"

This commit is contained in:
Jenkins
2017-04-07 12:49:33 +00:00
committed by Gerrit Code Review
2 changed files with 100 additions and 18 deletions

View File

@@ -28,11 +28,14 @@ Outlet (Exhaust Air) Temperature is one of the important thermal
telemetries to measure thermal/workload status of server. telemetries to measure thermal/workload status of server.
""" """
import datetime
from oslo_log import log from oslo_log import log
from watcher._i18n import _ from watcher._i18n import _
from watcher.common import exception as wexc from watcher.common import exception as wexc
from watcher.datasource import ceilometer as ceil from watcher.datasource import ceilometer as ceil
from watcher.datasource import gnocchi as gnoc
from watcher.decision_engine.model import element from watcher.decision_engine.model import element
from watcher.decision_engine.strategy.strategies import base from watcher.decision_engine.strategy.strategies import base
@@ -71,9 +74,15 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
""" # noqa """ # noqa
# The meter to report outlet temperature in ceilometer # The meter to report outlet temperature in ceilometer
METER_NAME = "hardware.ipmi.node.outlet_temperature"
MIGRATION = "migrate" MIGRATION = "migrate"
METRIC_NAMES = dict(
ceilometer=dict(
host_outlet_temp='hardware.ipmi.node.outlet_temperature'),
gnocchi=dict(
host_outlet_temp='hardware.ipmi.node.outlet_temperature'),
)
def __init__(self, config, osc=None): def __init__(self, config, osc=None):
"""Outlet temperature control using live migration """Outlet temperature control using live migration
@@ -83,8 +92,8 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
:type osc: :py:class:`~.OpenStackClients` instance, optional :type osc: :py:class:`~.OpenStackClients` instance, optional
""" """
super(OutletTempControl, self).__init__(config, osc) super(OutletTempControl, self).__init__(config, osc)
self._meter = self.METER_NAME
self._ceilometer = None self._ceilometer = None
self._gnocchi = None
@classmethod @classmethod
def get_name(cls): def get_name(cls):
@@ -118,6 +127,12 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
"type": "number", "type": "number",
"default": 30 "default": 30
}, },
"granularity": {
"description": "The time between two measures in an "
"aggregated timeseries of a metric.",
"type": "number",
"default": 300
},
}, },
} }
@@ -131,6 +146,20 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
def ceilometer(self, c): def ceilometer(self, c):
self._ceilometer = c self._ceilometer = c
@property
def gnocchi(self):
if self._gnocchi is None:
self._gnocchi = gnoc.GnocchiHelper(osc=self.osc)
return self._gnocchi
@gnocchi.setter
def gnocchi(self, g):
self._gnocchi = g
@property
def granularity(self):
return self.input_parameters.get('granularity', 300)
def calc_used_resource(self, node): def calc_used_resource(self, node):
"""Calculate the used vcpus, memory and disk based on VM flavors""" """Calculate the used vcpus, memory and disk based on VM flavors"""
instances = self.compute_model.get_node_instances(node) instances = self.compute_model.get_node_instances(node)
@@ -153,14 +182,31 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
hosts_need_release = [] hosts_need_release = []
hosts_target = [] hosts_target = []
metric_name = self.METRIC_NAMES[
self.config.datasource]['host_outlet_temp']
for node in nodes.values(): for node in nodes.values():
resource_id = node.uuid resource_id = node.uuid
outlet_temp = None
outlet_temp = self.ceilometer.statistic_aggregation( if self.config.datasource == "ceilometer":
resource_id=resource_id, outlet_temp = self.ceilometer.statistic_aggregation(
meter_name=self._meter, resource_id=resource_id,
period=self.period, meter_name=metric_name,
aggregate='avg') period=self.period,
aggregate='avg'
)
elif self.config.datasource == "gnocchi":
stop_time = datetime.datetime.utcnow()
start_time = stop_time - datetime.timedelta(
seconds=int(self.period))
outlet_temp = self.gnocchi.statistic_aggregation(
resource_id=resource_id,
metric=metric_name,
granularity=self.granularity,
start_time=start_time,
stop_time=stop_time,
aggregation='mean'
)
# some hosts may not have outlet temp meters, remove from target # some hosts may not have outlet temp meters, remove from target
if outlet_temp is None: if outlet_temp is None:
LOG.warning("%s: no outlet temp data", resource_id) LOG.warning("%s: no outlet temp data", resource_id)

View File

@@ -17,6 +17,7 @@
# limitations under the License. # limitations under the License.
# #
import collections import collections
import datetime
import mock import mock
from watcher.applier.loading import default from watcher.applier.loading import default
@@ -27,14 +28,25 @@ from watcher.decision_engine.strategy import strategies
from watcher.tests import base from watcher.tests import base
from watcher.tests.decision_engine.model import ceilometer_metrics from watcher.tests.decision_engine.model import ceilometer_metrics
from watcher.tests.decision_engine.model import faker_cluster_state from watcher.tests.decision_engine.model import faker_cluster_state
from watcher.tests.decision_engine.model import gnocchi_metrics
class TestOutletTempControl(base.TestCase): class TestOutletTempControl(base.TestCase):
scenarios = [
("Ceilometer",
{"datasource": "ceilometer",
"fake_datasource_cls": ceilometer_metrics.FakeCeilometerMetrics}),
("Gnocchi",
{"datasource": "gnocchi",
"fake_datasource_cls": gnocchi_metrics.FakeGnocchiMetrics}),
]
def setUp(self): def setUp(self):
super(TestOutletTempControl, self).setUp() super(TestOutletTempControl, self).setUp()
# fake metrics # fake metrics
self.fake_metrics = ceilometer_metrics.FakeCeilometerMetrics() self.fake_metrics = self.fake_datasource_cls()
# fake cluster # fake cluster
self.fake_cluster = faker_cluster_state.FakerModelCollector() self.fake_cluster = faker_cluster_state.FakerModelCollector()
@@ -44,11 +56,11 @@ class TestOutletTempControl(base.TestCase):
self.m_model = p_model.start() self.m_model = p_model.start()
self.addCleanup(p_model.stop) self.addCleanup(p_model.stop)
p_ceilometer = mock.patch.object( p_datasource = mock.patch.object(
strategies.OutletTempControl, "ceilometer", strategies.OutletTempControl, self.datasource,
new_callable=mock.PropertyMock) new_callable=mock.PropertyMock)
self.m_ceilometer = p_ceilometer.start() self.m_datasource = p_datasource.start()
self.addCleanup(p_ceilometer.stop) self.addCleanup(p_datasource.stop)
p_audit_scope = mock.patch.object( p_audit_scope = mock.patch.object(
strategies.OutletTempControl, "audit_scope", strategies.OutletTempControl, "audit_scope",
@@ -60,9 +72,10 @@ class TestOutletTempControl(base.TestCase):
self.m_audit_scope.return_value = mock.Mock() self.m_audit_scope.return_value = mock.Mock()
self.m_model.return_value = model_root.ModelRoot() self.m_model.return_value = model_root.ModelRoot()
self.m_ceilometer.return_value = mock.Mock( self.m_datasource.return_value = mock.Mock(
statistic_aggregation=self.fake_metrics.mock_get_statistics) statistic_aggregation=self.fake_metrics.mock_get_statistics)
self.strategy = strategies.OutletTempControl(config=mock.Mock()) self.strategy = strategies.OutletTempControl(
config=mock.Mock(datasource=self.datasource))
self.strategy.input_parameters = utils.Struct() self.strategy.input_parameters = utils.Struct()
self.strategy.input_parameters.update({'threshold': 34.3}) self.strategy.input_parameters.update({'threshold': 34.3})
@@ -159,13 +172,36 @@ class TestOutletTempControl(base.TestCase):
strategies.OutletTempControl, "ceilometer") strategies.OutletTempControl, "ceilometer")
m_ceilometer = p_ceilometer.start() m_ceilometer = p_ceilometer.start()
self.addCleanup(p_ceilometer.stop) self.addCleanup(p_ceilometer.stop)
p_gnocchi = mock.patch.object(strategies.OutletTempControl, "gnocchi")
m_gnocchi = p_gnocchi.start()
self.addCleanup(p_gnocchi.stop)
datetime_patcher = mock.patch.object(
datetime, 'datetime',
mock.Mock(wraps=datetime.datetime)
)
mocked_datetime = datetime_patcher.start()
mocked_datetime.utcnow.return_value = datetime.datetime(
2017, 3, 19, 18, 53, 11, 657417)
self.addCleanup(datetime_patcher.stop)
m_ceilometer.statistic_aggregation = mock.Mock( m_ceilometer.statistic_aggregation = mock.Mock(
side_effect=self.fake_metrics.mock_get_statistics) side_effect=self.fake_metrics.mock_get_statistics)
m_gnocchi.statistic_aggregation = mock.Mock(
side_effect=self.fake_metrics.mock_get_statistics)
node = model.get_node_by_uuid('Node_0') node = model.get_node_by_uuid('Node_0')
self.strategy.input_parameters.update({'threshold': 35.0}) self.strategy.input_parameters.update({'threshold': 35.0})
self.strategy.threshold = 35.0 self.strategy.threshold = 35.0
self.strategy.group_hosts_by_outlet_temp() self.strategy.group_hosts_by_outlet_temp()
m_ceilometer.statistic_aggregation.assert_any_call( if self.strategy.config.datasource == "ceilometer":
aggregate='avg', m_ceilometer.statistic_aggregation.assert_any_call(
meter_name='hardware.ipmi.node.outlet_temperature', aggregate='avg',
period=30, resource_id=node.uuid) meter_name='hardware.ipmi.node.outlet_temperature',
period=30, resource_id=node.uuid)
elif self.strategy.config.datasource == "gnocchi":
stop_time = datetime.datetime.utcnow()
start_time = stop_time - datetime.timedelta(
seconds=int('30'))
m_gnocchi.statistic_aggregation.assert_called_with(
resource_id=mock.ANY,
metric='hardware.ipmi.node.outlet_temperature',
granularity=300, start_time=start_time, stop_time=stop_time,
aggregation='mean')