Compare commits

...

12 Commits
3.0.0 ... 1.4.1

Author SHA1 Message Date
licanwei
9d2f8d11ec Fix KeyError exception
During the strategy sync process,
if goal_id can't be found in the goals table,
will throw a KeyError exception.

Change-Id: I62800ac5c69f4f5c7820908f2e777094a51a5541
Closes-Bug: #1711086
2017-08-24 12:34:13 +00:00
Jenkins
f1d064c759 Merge "workload balance base on cpu or ram util" into stable/pike 2017-08-24 10:09:30 +00:00
Jenkins
6cb02c18a7 Merge "Remove pbr warnerrors" into stable/pike 2017-08-24 10:09:19 +00:00
Jenkins
37fc37e138 Merge "Update the documention for doc migration" into stable/pike 2017-08-24 10:09:13 +00:00
Jenkins
b68685741e Merge "Adjust the action state judgment logic" into stable/pike 2017-08-24 08:37:55 +00:00
zhengwei6082
6721977f74 Update the documention for doc migration
Change-Id: I22dc18e6f2f7471f5c804d4d19c631f81a6e196b
(cherry picked from commit d5bcd37478)
2017-08-23 10:04:55 +00:00
Alexander Chadin
c303ad4cdc Remove pbr warnerrors
This change removes the now unused "warnerrors" setting,
which is replaced by "warning-is-error" in sphinx
releases >= 1.5 [1].

[1] http://lists.openstack.org/pipermail/openstack-dev/2017-March/113085.html

Change-Id: I32f078169668be08737e47cd15edbdfba42904dc
(cherry picked from commit f76a628d1f)
2017-08-23 10:03:58 +00:00
licanwei
51c9db2936 Adjust the action state judgment logic
Only when True is returned, the action state is set to SUCCEEDED
some actions(such as migrate) will return None if exception raised

Change-Id: I52e7a1ffb68f54594f2b00d9843e8e0a4c985667
(cherry picked from commit 965af1b6fd)
2017-08-23 10:03:32 +00:00
suzhengwei
1e003d4153 workload balance base on cpu or ram util
By the input parameter "metrics", it makes decision to migrate a VM
base on cpu or memory utilization.

Change-Id: I35cce3495c8dacad64ea6c6ee71082a85e9e0a83
(cherry picked from commit 5c86a54d20)
2017-08-23 10:03:15 +00:00
Hidekazu Nakamura
bab89fd769 Fix gnocchi repository URL in local.conf.controller
This patch set updates gnocchi repository URL in local.conf.controller
bacause it moved from under openstack to their own repository.

Change-Id: I53c6efcb40b26f83bc1867564b9067ae5f50938d
(cherry picked from commit 5cc4716a95)
2017-08-23 10:02:57 +00:00
OpenStack Release Bot
e1e17ab0b9 Update UPPER_CONSTRAINTS_FILE for stable/pike
Change-Id: I453ae1575d2dad4b724d96cfc6ebf5c5b2a2a3be
2017-08-11 01:09:58 +00:00
OpenStack Release Bot
3d542472f6 Update .gitreview for stable/pike
Change-Id: I685c0bc8773d2b5f9e747a53d870a92dd6baea36
2017-08-11 01:09:57 +00:00
16 changed files with 220 additions and 51 deletions

View File

@@ -2,3 +2,4 @@
host=review.openstack.org host=review.openstack.org
port=29418 port=29418
project=openstack/watcher.git project=openstack/watcher.git
defaultbranch=stable/pike

View File

@@ -46,7 +46,7 @@ disable_service ceilometer-acompute
enable_service ceilometer-api enable_service ceilometer-api
# Enable the Gnocchi plugin # Enable the Gnocchi plugin
enable_plugin gnocchi https://git.openstack.org/openstack/gnocchi enable_plugin gnocchi https://github.com/gnocchixyz/gnocchi
LOGFILE=$DEST/logs/stack.sh.log LOGFILE=$DEST/logs/stack.sh.log
LOGDAYS=2 LOGDAYS=2

View File

@@ -178,7 +178,7 @@ Here below is how you would proceed to register ``DummyAction`` using pbr_:
watcher_actions = watcher_actions =
dummy = thirdparty.dummy:DummyAction dummy = thirdparty.dummy:DummyAction
.. _pbr: http://docs.openstack.org/developer/pbr/ .. _pbr: https://docs.openstack.org/pbr/latest
Using action plugins Using action plugins

View File

@@ -25,6 +25,7 @@ The *workload_balance* strategy requires the following metrics:
metric service name plugins comment metric service name plugins comment
======================= ============ ======= ======= ======================= ============ ======= =======
``cpu_util`` ceilometer_ none ``cpu_util`` ceilometer_ none
``memory.resident`` ceilometer_ none
======================= ============ ======= ======= ======================= ============ ======= =======
.. _ceilometer: http://docs.openstack.org/admin-guide/telemetry-measurements.html#openstack-compute .. _ceilometer: http://docs.openstack.org/admin-guide/telemetry-measurements.html#openstack-compute
@@ -66,6 +67,9 @@ Strategy parameters are:
============== ====== ============= ==================================== ============== ====== ============= ====================================
parameter type default Value description parameter type default Value description
============== ====== ============= ==================================== ============== ====== ============= ====================================
``metrics`` String 'cpu_util' Workload balance base on cpu or ram
utilization. choice: ['cpu_util',
'memory.resident']
``threshold`` Number 25.0 Workload threshold for migration ``threshold`` Number 25.0 Workload threshold for migration
``period`` Number 300 Aggregate time period of ceilometer ``period`` Number 300 Aggregate time period of ceilometer
============== ====== ============= ==================================== ============== ====== ============= ====================================
@@ -90,7 +94,7 @@ How to use it ?
at1 workload_balancing --strategy workload_balance at1 workload_balancing --strategy workload_balance
$ openstack optimize audit create -a at1 -p threshold=26.0 \ $ openstack optimize audit create -a at1 -p threshold=26.0 \
-p period=310 -p period=310 -p metrics=cpu_util
External Links External Links
-------------- --------------

View File

@@ -0,0 +1,7 @@
---
features:
- Existing workload_balance strategy based on
the VM workloads of CPU. This feature improves
the strategy. By the input parameter "metrics",
it makes decision to migrate a VM base on CPU
or memory utilization.

View File

@@ -98,7 +98,6 @@ watcher_cluster_data_model_collectors =
[pbr] [pbr]
warnerrors = true
autodoc_index_modules = true autodoc_index_modules = true
autodoc_exclude_modules = autodoc_exclude_modules =
watcher.db.sqlalchemy.alembic.env watcher.db.sqlalchemy.alembic.env

View File

@@ -7,7 +7,7 @@ skipsdist = True
usedevelop = True usedevelop = True
whitelist_externals = find whitelist_externals = find
rm rm
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/pike} {opts} {packages}
setenv = setenv =
VIRTUAL_ENV={envdir} VIRTUAL_ENV={envdir}
deps = -r{toxinidir}/test-requirements.txt deps = -r{toxinidir}/test-requirements.txt

View File

@@ -120,14 +120,15 @@ class TaskFlowActionContainer(base.BaseTaskFlowActionContainer):
def do_execute(self, *args, **kwargs): def do_execute(self, *args, **kwargs):
LOG.debug("Running action: %s", self.name) LOG.debug("Running action: %s", self.name)
# NOTE: For result is False, set action state fail # NOTE:Some actions(such as migrate) will return None when exception
# Only when True is returned, the action state is set to SUCCEEDED
result = self.action.execute() result = self.action.execute()
if result is False: if result is True:
return self.engine.notify(self._db_action,
objects.action.State.FAILED)
else:
return self.engine.notify(self._db_action, return self.engine.notify(self._db_action,
objects.action.State.SUCCEEDED) objects.action.State.SUCCEEDED)
else:
return self.engine.notify(self._db_action,
objects.action.State.FAILED)
def do_post_execute(self): def do_post_execute(self):
LOG.debug("Post-condition action: %s", self.name) LOG.debug("Post-condition action: %s", self.name)

View File

@@ -22,7 +22,7 @@
*Description* *Description*
This strategy migrates a VM based on the VM workload of the hosts. This strategy migrates a VM based on the VM workload of the hosts.
It makes decision to migrate a workload whenever a host's CPU It makes decision to migrate a workload whenever a host's CPU or RAM
utilization % is higher than the specified threshold. The VM to utilization % is higher than the specified threshold. The VM to
be moved should make the host close to average workload of all be moved should make the host close to average workload of all
hosts nodes. hosts nodes.
@@ -32,7 +32,7 @@ hosts nodes.
* Hardware: compute node should use the same physical CPUs * Hardware: compute node should use the same physical CPUs
* Software: Ceilometer component ceilometer-agent-compute * Software: Ceilometer component ceilometer-agent-compute
running in each compute node, and Ceilometer API can running in each compute node, and Ceilometer API can
report such telemetry "cpu_util" successfully. report such telemetry "cpu_util" and "memory.resident" successfully.
* You must have at least 2 physical compute nodes to run * You must have at least 2 physical compute nodes to run
this strategy. this strategy.
@@ -69,16 +69,16 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
It is a migration strategy based on the VM workload of physical It is a migration strategy based on the VM workload of physical
servers. It generates solutions to move a workload whenever a server's servers. It generates solutions to move a workload whenever a server's
CPU utilization % is higher than the specified threshold. CPU or RAM utilization % is higher than the specified threshold.
The VM to be moved should make the host close to average workload The VM to be moved should make the host close to average workload
of all compute nodes. of all compute nodes.
*Requirements* *Requirements*
* Hardware: compute node should use the same physical CPUs * Hardware: compute node should use the same physical CPUs/RAMs
* Software: Ceilometer component ceilometer-agent-compute running * Software: Ceilometer component ceilometer-agent-compute running
in each compute node, and Ceilometer API can report such telemetry in each compute node, and Ceilometer API can report such telemetry
"cpu_util" successfully. "cpu_util" and "memory.resident" successfully.
* You must have at least 2 physical compute nodes to run this strategy * You must have at least 2 physical compute nodes to run this strategy
*Limitations* *Limitations*
@@ -91,8 +91,12 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
""" """
# The meter to report CPU utilization % of VM in ceilometer # The meter to report CPU utilization % of VM in ceilometer
METER_NAME = "cpu_util"
# Unit: %, value range is [0 , 100] # Unit: %, value range is [0 , 100]
CPU_METER_NAME = "cpu_util"
# The meter to report memory resident of VM in ceilometer
# Unit: MB
MEM_METER_NAME = "memory.resident"
MIGRATION = "migrate" MIGRATION = "migrate"
@@ -104,9 +108,9 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
:param osc: :py:class:`~.OpenStackClients` instance :param osc: :py:class:`~.OpenStackClients` instance
""" """
super(WorkloadBalance, self).__init__(config, osc) super(WorkloadBalance, self).__init__(config, osc)
# the migration plan will be triggered when the CPU utilization % # the migration plan will be triggered when the CPU or RAM
# reaches threshold # utilization % reaches threshold
self._meter = self.METER_NAME self._meter = None
self._ceilometer = None self._ceilometer = None
self._gnocchi = None self._gnocchi = None
@@ -151,6 +155,13 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
# Mandatory default setting for each element # Mandatory default setting for each element
return { return {
"properties": { "properties": {
"metrics": {
"description": "Workload balance based on metrics: "
"cpu or ram utilization",
"type": "string",
"choice": ["cpu_util", "memory.resident"],
"default": "cpu_util"
},
"threshold": { "threshold": {
"description": "workload threshold for migration", "description": "workload threshold for migration",
"type": "number", "type": "number",
@@ -251,18 +262,21 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
cores_available = host.vcpus - cores_used cores_available = host.vcpus - cores_used
disk_available = host.disk - disk_used disk_available = host.disk - disk_used
mem_available = host.memory - mem_used mem_available = host.memory - mem_used
if ( if (cores_available >= required_cores and
cores_available >= required_cores and
disk_available >= required_disk and
mem_available >= required_mem and mem_available >= required_mem and
disk_available >= required_disk):
if (self._meter == self.CPU_METER_NAME and
((src_instance_workload + workload) < ((src_instance_workload + workload) <
self.threshold / 100 * host.vcpus) self.threshold / 100 * host.vcpus)):
): destination_hosts.append(instance_data)
destination_hosts.append(instance_data) if (self._meter == self.MEM_METER_NAME and
((src_instance_workload + workload) <
self.threshold / 100 * host.memory)):
destination_hosts.append(instance_data)
return destination_hosts return destination_hosts
def group_hosts_by_cpu_util(self): def group_hosts_by_cpu_or_ram_util(self):
"""Calculate the workloads of each node """Calculate the workloads of each node
try to find out the nodes which have reached threshold try to find out the nodes which have reached threshold
@@ -286,10 +300,10 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
instances = self.compute_model.get_node_instances(node) instances = self.compute_model.get_node_instances(node)
node_workload = 0.0 node_workload = 0.0
for instance in instances: for instance in instances:
cpu_util = None instance_util = None
try: try:
if self.config.datasource == "ceilometer": if self.config.datasource == "ceilometer":
cpu_util = self.ceilometer.statistic_aggregation( instance_util = self.ceilometer.statistic_aggregation(
resource_id=instance.uuid, resource_id=instance.uuid,
meter_name=self._meter, meter_name=self._meter,
period=self._period, period=self._period,
@@ -298,7 +312,7 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
stop_time = datetime.datetime.utcnow() stop_time = datetime.datetime.utcnow()
start_time = stop_time - datetime.timedelta( start_time = stop_time - datetime.timedelta(
seconds=int(self._period)) seconds=int(self._period))
cpu_util = self.gnocchi.statistic_aggregation( instance_util = self.gnocchi.statistic_aggregation(
resource_id=instance.uuid, resource_id=instance.uuid,
metric=self._meter, metric=self._meter,
granularity=self.granularity, granularity=self.granularity,
@@ -308,23 +322,32 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
) )
except Exception as exc: except Exception as exc:
LOG.exception(exc) LOG.exception(exc)
LOG.error("Can not get cpu_util from %s", LOG.error("Can not get %s from %s", self._meter,
self.config.datasource) self.config.datasource)
continue continue
if cpu_util is None: if instance_util is None:
LOG.debug("Instance (%s): cpu_util is None", instance.uuid) LOG.debug("Instance (%s): %s is None",
instance.uuid, self._meter)
continue continue
workload_cache[instance.uuid] = cpu_util * instance.vcpus / 100 if self._meter == self.CPU_METER_NAME:
workload_cache[instance.uuid] = (instance_util *
instance.vcpus / 100)
else:
workload_cache[instance.uuid] = instance_util
node_workload += workload_cache[instance.uuid] node_workload += workload_cache[instance.uuid]
LOG.debug("VM (%s): cpu_util %f", instance.uuid, cpu_util) LOG.debug("VM (%s): %s %f", instance.uuid, self._meter,
node_cpu_util = node_workload / node.vcpus * 100 instance_util)
cluster_workload += node_workload cluster_workload += node_workload
if self._meter == self.CPU_METER_NAME:
node_util = node_workload / node.vcpus * 100
else:
node_util = node_workload / node.memory * 100
instance_data = { instance_data = {
'node': node, "cpu_util": node_cpu_util, 'node': node, self._meter: node_util,
'workload': node_workload} 'workload': node_workload}
if node_cpu_util >= self.threshold: if node_util >= self.threshold:
# mark the node to release resources # mark the node to release resources
overload_hosts.append(instance_data) overload_hosts.append(instance_data)
else: else:
@@ -356,8 +379,9 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
""" """
self.threshold = self.input_parameters.threshold self.threshold = self.input_parameters.threshold
self._period = self.input_parameters.period self._period = self.input_parameters.period
self._meter = self.input_parameters.metrics
source_nodes, target_nodes, avg_workload, workload_cache = ( source_nodes, target_nodes, avg_workload, workload_cache = (
self.group_hosts_by_cpu_util()) self.group_hosts_by_cpu_or_ram_util())
if not source_nodes: if not source_nodes:
LOG.debug("No hosts require optimization") LOG.debug("No hosts require optimization")
@@ -373,7 +397,7 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
# choose the server with largest cpu_util # choose the server with largest cpu_util
source_nodes = sorted(source_nodes, source_nodes = sorted(source_nodes,
reverse=True, reverse=True,
key=lambda x: (x[self.METER_NAME])) key=lambda x: (x[self._meter]))
instance_to_migrate = self.choose_instance_to_migrate( instance_to_migrate = self.choose_instance_to_migrate(
source_nodes, avg_workload, workload_cache) source_nodes, avg_workload, workload_cache)
@@ -391,7 +415,7 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy):
"be because of there's no enough CPU/Memory/DISK") "be because of there's no enough CPU/Memory/DISK")
return self.solution return self.solution
destination_hosts = sorted(destination_hosts, destination_hosts = sorted(destination_hosts,
key=lambda x: (x["cpu_util"])) key=lambda x: (x[self._meter]))
# always use the host with lowerest CPU utilization # always use the host with lowerest CPU utilization
mig_destination_node = destination_hosts[0]['node'] mig_destination_node = destination_hosts[0]['node']
# generate solution to migrate the instance to the dest server, # generate solution to migrate the instance to the dest server,

View File

@@ -78,6 +78,14 @@ class Syncer(object):
"""Strategies loaded from DB""" """Strategies loaded from DB"""
if self._available_strategies is None: if self._available_strategies is None:
self._available_strategies = objects.Strategy.list(self.ctx) self._available_strategies = objects.Strategy.list(self.ctx)
goal_ids = [g.id for g in self.available_goals]
stale_strategies = [s for s in self._available_strategies
if s.goal_id not in goal_ids]
for s in stale_strategies:
LOG.info("Can't find Goal id %d of strategy %s",
s.goal_id, s.name)
s.soft_delete()
self._available_strategies.remove(s)
return self._available_strategies return self._available_strategies
@property @property

View File

@@ -20,6 +20,8 @@ import eventlet
import mock import mock
from watcher.applier.workflow_engine import default as tflow from watcher.applier.workflow_engine import default as tflow
from watcher.common import clients
from watcher.common import nova_helper
from watcher import objects from watcher import objects
from watcher.tests.db import base from watcher.tests.db import base
from watcher.tests.objects import utils as obj_utils from watcher.tests.objects import utils as obj_utils
@@ -55,6 +57,32 @@ class TestTaskFlowActionContainer(base.DbTestCase):
self.assertTrue(action.state, objects.action.State.SUCCEEDED) self.assertTrue(action.state, objects.action.State.SUCCEEDED)
@mock.patch.object(clients.OpenStackClients, 'nova', mock.Mock())
def test_execute_with_failed(self):
nova_util = nova_helper.NovaHelper()
instance = "31b9dd5c-b1fd-4f61-9b68-a47096326dac"
nova_util.nova.servers.get.return_value = instance
action_plan = obj_utils.create_test_action_plan(
self.context, audit_id=self.audit.id,
strategy_id=self.strategy.id,
state=objects.action.State.ONGOING)
action = obj_utils.create_test_action(
self.context, action_plan_id=action_plan.id,
state=objects.action.State.ONGOING,
action_type='migrate',
input_parameters={"resource_id":
instance,
"migration_type": "live",
"destination_node": "host2",
"source_node": "host1"})
action_container = tflow.TaskFlowActionContainer(
db_action=action,
engine=self.engine)
action_container.execute()
self.assertTrue(action.state, objects.action.State.FAILED)
@mock.patch('eventlet.spawn') @mock.patch('eventlet.spawn')
def test_execute_with_cancel_action_plan(self, mock_eventlet_spawn): def test_execute_with_cancel_action_plan(self, mock_eventlet_spawn):
action_plan = obj_utils.create_test_action_plan( action_plan = obj_utils.create_test_action_plan(

View File

@@ -54,6 +54,8 @@ class FakeCeilometerMetrics(object):
result = 0.0 result = 0.0
if meter_name == "cpu_util": if meter_name == "cpu_util":
result = self.get_average_usage_instance_cpu_wb(resource_id) result = self.get_average_usage_instance_cpu_wb(resource_id)
elif meter_name == "memory.resident":
result = self.get_average_usage_instance_memory_wb(resource_id)
return result return result
def mock_get_statistics_nn(self, resource_id, meter_name, period, def mock_get_statistics_nn(self, resource_id, meter_name, period,
@@ -211,6 +213,20 @@ class FakeCeilometerMetrics(object):
mock['INSTANCE_4'] = 10 mock['INSTANCE_4'] = 10
return float(mock[str(uuid)]) return float(mock[str(uuid)])
@staticmethod
def get_average_usage_instance_memory_wb(uuid):
mock = {}
# node 0
mock['INSTANCE_1'] = 30
# node 1
mock['INSTANCE_3'] = 12
mock['INSTANCE_4'] = 12
if uuid not in mock.keys():
# mock[uuid] = random.randint(1, 4)
mock[uuid] = 12
return mock[str(uuid)]
@staticmethod @staticmethod
def get_average_usage_instance_cpu(uuid): def get_average_usage_instance_cpu(uuid):
"""The last VM CPU usage values to average """The last VM CPU usage values to average

View File

@@ -1,10 +1,10 @@
<ModelRoot> <ModelRoot>
<ComputeNode human_id="" uuid="Node_0" status="enabled" state="up" id="0" hostname="hostname_0" vcpus="40" disk="250" disk_capacity="250" memory="132"> <ComputeNode human_id="" uuid="Node_0" status="enabled" state="up" id="0" hostname="hostname_0" vcpus="40" disk="250" disk_capacity="250" memory="132">
<Instance state="active" human_id="" uuid="73b09e16-35b7-4922-804e-e8f5d9b740fc" vcpus="10" disk="20" disk_capacity="20" memory="2" metadata='{"optimize": true,"top": "floor", "nested": {"x": "y"}}'/> <Instance state="active" human_id="" uuid="73b09e16-35b7-4922-804e-e8f5d9b740fc" vcpus="10" disk="20" disk_capacity="20" memory="32" metadata='{"optimize": true,"top": "floor", "nested": {"x": "y"}}'/>
<Instance state="active" human_id="" uuid="INSTANCE_1" vcpus="10" disk="20" disk_capacity="20" memory="2" metadata='{"optimize": true,"top": "floor", "nested": {"x": "y"}}'/> <Instance state="active" human_id="" uuid="INSTANCE_1" vcpus="10" disk="20" disk_capacity="20" memory="32" metadata='{"optimize": true,"top": "floor", "nested": {"x": "y"}}'/>
</ComputeNode> </ComputeNode>
<ComputeNode human_id="" uuid="Node_1" status="enabled" state="up" id="1" hostname="hostname_1" vcpus="40" disk="250" disk_capacity="250" memory="132"> <ComputeNode human_id="" uuid="Node_1" status="enabled" state="up" id="1" hostname="hostname_1" vcpus="40" disk="250" disk_capacity="250" memory="132">
<Instance state="active" human_id="" uuid="INSTANCE_3" vcpus="10" disk="20" disk_capacity="20" memory="2" metadata='{"optimize": true,"top": "floor", "nested": {"x": "y"}}'/> <Instance state="active" human_id="" uuid="INSTANCE_3" vcpus="10" disk="20" disk_capacity="20" memory="32" metadata='{"optimize": true,"top": "floor", "nested": {"x": "y"}}'/>
<Instance state="active" human_id="" uuid="INSTANCE_4" vcpus="10" disk="20" disk_capacity="20" memory="2" metadata='{"optimize": true,"top": "floor", "nested": {"x": "y"}}'/> <Instance state="active" human_id="" uuid="INSTANCE_4" vcpus="10" disk="20" disk_capacity="20" memory="32" metadata='{"optimize": true,"top": "floor", "nested": {"x": "y"}}'/>
</ComputeNode> </ComputeNode>
</ModelRoot> </ModelRoot>

View File

@@ -50,6 +50,8 @@ class FakeGnocchiMetrics(object):
result = 0.0 result = 0.0
if metric == "cpu_util": if metric == "cpu_util":
result = self.get_average_usage_instance_cpu_wb(resource_id) result = self.get_average_usage_instance_cpu_wb(resource_id)
elif metric == "memory.resident":
result = self.get_average_usage_instance_memory_wb(resource_id)
return result return result
@staticmethod @staticmethod
@@ -242,3 +244,17 @@ class FakeGnocchiMetrics(object):
mock['INSTANCE_3'] = 20 mock['INSTANCE_3'] = 20
mock['INSTANCE_4'] = 10 mock['INSTANCE_4'] = 10
return float(mock[str(uuid)]) return float(mock[str(uuid)])
@staticmethod
def get_average_usage_instance_memory_wb(uuid):
mock = {}
# node 0
mock['INSTANCE_1'] = 30
# node 1
mock['INSTANCE_3'] = 12
mock['INSTANCE_4'] = 12
if uuid not in mock.keys():
# mock[uuid] = random.randint(1, 4)
mock[uuid] = 12
return mock[str(uuid)]

View File

@@ -74,10 +74,12 @@ class TestWorkloadBalance(base.TestCase):
self.strategy = strategies.WorkloadBalance( self.strategy = strategies.WorkloadBalance(
config=mock.Mock(datasource=self.datasource)) config=mock.Mock(datasource=self.datasource))
self.strategy.input_parameters = utils.Struct() self.strategy.input_parameters = utils.Struct()
self.strategy.input_parameters.update({'threshold': 25.0, self.strategy.input_parameters.update({'metrics': 'cpu_util',
'threshold': 25.0,
'period': 300}) 'period': 300})
self.strategy.threshold = 25.0 self.strategy.threshold = 25.0
self.strategy._period = 300 self.strategy._period = 300
self.strategy._meter = "cpu_util"
def test_calc_used_resource(self): def test_calc_used_resource(self):
model = self.fake_cluster.generate_scenario_6_with_2_nodes() model = self.fake_cluster.generate_scenario_6_with_2_nodes()
@@ -86,21 +88,31 @@ class TestWorkloadBalance(base.TestCase):
cores_used, mem_used, disk_used = ( cores_used, mem_used, disk_used = (
self.strategy.calculate_used_resource(node)) self.strategy.calculate_used_resource(node))
self.assertEqual((cores_used, mem_used, disk_used), (20, 4, 40)) self.assertEqual((cores_used, mem_used, disk_used), (20, 64, 40))
def test_group_hosts_by_cpu_util(self): def test_group_hosts_by_cpu_util(self):
model = self.fake_cluster.generate_scenario_6_with_2_nodes() model = self.fake_cluster.generate_scenario_6_with_2_nodes()
self.m_model.return_value = model self.m_model.return_value = model
self.strategy.threshold = 30 self.strategy.threshold = 30
n1, n2, avg, w_map = self.strategy.group_hosts_by_cpu_util() n1, n2, avg, w_map = self.strategy.group_hosts_by_cpu_or_ram_util()
self.assertEqual(n1[0]['node'].uuid, 'Node_0') self.assertEqual(n1[0]['node'].uuid, 'Node_0')
self.assertEqual(n2[0]['node'].uuid, 'Node_1') self.assertEqual(n2[0]['node'].uuid, 'Node_1')
self.assertEqual(avg, 8.0) self.assertEqual(avg, 8.0)
def test_group_hosts_by_ram_util(self):
model = self.fake_cluster.generate_scenario_6_with_2_nodes()
self.m_model.return_value = model
self.strategy._meter = "memory.resident"
self.strategy.threshold = 30
n1, n2, avg, w_map = self.strategy.group_hosts_by_cpu_or_ram_util()
self.assertEqual(n1[0]['node'].uuid, 'Node_0')
self.assertEqual(n2[0]['node'].uuid, 'Node_1')
self.assertEqual(avg, 33.0)
def test_choose_instance_to_migrate(self): def test_choose_instance_to_migrate(self):
model = self.fake_cluster.generate_scenario_6_with_2_nodes() model = self.fake_cluster.generate_scenario_6_with_2_nodes()
self.m_model.return_value = model self.m_model.return_value = model
n1, n2, avg, w_map = self.strategy.group_hosts_by_cpu_util() n1, n2, avg, w_map = self.strategy.group_hosts_by_cpu_or_ram_util()
instance_to_mig = self.strategy.choose_instance_to_migrate( instance_to_mig = self.strategy.choose_instance_to_migrate(
n1, avg, w_map) n1, avg, w_map)
self.assertEqual(instance_to_mig[0].uuid, 'Node_0') self.assertEqual(instance_to_mig[0].uuid, 'Node_0')
@@ -110,7 +122,7 @@ class TestWorkloadBalance(base.TestCase):
def test_choose_instance_notfound(self): def test_choose_instance_notfound(self):
model = self.fake_cluster.generate_scenario_6_with_2_nodes() model = self.fake_cluster.generate_scenario_6_with_2_nodes()
self.m_model.return_value = model self.m_model.return_value = model
n1, n2, avg, w_map = self.strategy.group_hosts_by_cpu_util() n1, n2, avg, w_map = self.strategy.group_hosts_by_cpu_or_ram_util()
instances = model.get_all_instances() instances = model.get_all_instances()
[model.remove_instance(inst) for inst in instances.values()] [model.remove_instance(inst) for inst in instances.values()]
instance_to_mig = self.strategy.choose_instance_to_migrate( instance_to_mig = self.strategy.choose_instance_to_migrate(
@@ -122,7 +134,7 @@ class TestWorkloadBalance(base.TestCase):
self.m_model.return_value = model self.m_model.return_value = model
self.strategy.datasource = mock.MagicMock( self.strategy.datasource = mock.MagicMock(
statistic_aggregation=self.fake_metrics.mock_get_statistics_wb) statistic_aggregation=self.fake_metrics.mock_get_statistics_wb)
n1, n2, avg, w_map = self.strategy.group_hosts_by_cpu_util() n1, n2, avg, w_map = self.strategy.group_hosts_by_cpu_or_ram_util()
instance_to_mig = self.strategy.choose_instance_to_migrate( instance_to_mig = self.strategy.choose_instance_to_migrate(
n1, avg, w_map) n1, avg, w_map)
dest_hosts = self.strategy.filter_destination_hosts( dest_hosts = self.strategy.filter_destination_hosts(
@@ -202,7 +214,7 @@ class TestWorkloadBalance(base.TestCase):
m_gnocchi.statistic_aggregation = mock.Mock( m_gnocchi.statistic_aggregation = mock.Mock(
side_effect=self.fake_metrics.mock_get_statistics_wb) side_effect=self.fake_metrics.mock_get_statistics_wb)
instance0 = model.get_instance_by_uuid("INSTANCE_0") instance0 = model.get_instance_by_uuid("INSTANCE_0")
self.strategy.group_hosts_by_cpu_util() self.strategy.group_hosts_by_cpu_or_ram_util()
if self.strategy.config.datasource == "ceilometer": if self.strategy.config.datasource == "ceilometer":
m_ceilometer.statistic_aggregation.assert_any_call( m_ceilometer.statistic_aggregation.assert_any_call(
aggregate='avg', meter_name='cpu_util', aggregate='avg', meter_name='cpu_util',

View File

@@ -659,3 +659,56 @@ class TestSyncer(base.DbTestCase):
all(ap.state == objects.action_plan.State.CANCELLED all(ap.state == objects.action_plan.State.CANCELLED
for ap in modified_action_plans.values())) for ap in modified_action_plans.values()))
self.assertEqual(set([action_plan1.id]), set(unmodified_action_plans)) self.assertEqual(set([action_plan1.id]), set(unmodified_action_plans))
def test_sync_strategies_with_removed_goal(self):
# ### Setup ### #
goal1 = objects.Goal(
self.ctx, id=1, uuid=utils.generate_uuid(),
name="dummy_1", display_name="Dummy 1",
efficacy_specification=self.goal1_spec.serialize_indicators_specs()
)
goal2 = objects.Goal(
self.ctx, id=2, uuid=utils.generate_uuid(),
name="dummy_2", display_name="Dummy 2",
efficacy_specification=self.goal2_spec.serialize_indicators_specs()
)
goal1.create()
goal2.create()
strategy1 = objects.Strategy(
self.ctx, id=1, name="strategy_1", uuid=utils.generate_uuid(),
display_name="Strategy 1", goal_id=goal1.id)
strategy2 = objects.Strategy(
self.ctx, id=2, name="strategy_2", uuid=utils.generate_uuid(),
display_name="Strategy 2", goal_id=goal2.id)
strategy1.create()
strategy2.create()
# to be removed by some reasons
goal2.soft_delete()
before_goals = objects.Goal.list(self.ctx)
before_strategies = objects.Strategy.list(self.ctx)
# ### Action under test ### #
try:
self.syncer.sync()
except Exception as exc:
self.fail(exc)
# ### Assertions ### #
after_goals = objects.Goal.list(self.ctx)
after_strategies = objects.Strategy.list(self.ctx)
self.assertEqual(1, len(before_goals))
self.assertEqual(2, len(before_strategies))
self.assertEqual(2, len(after_goals))
self.assertEqual(4, len(after_strategies))
self.assertEqual(
{"dummy_1", "dummy_2"},
set([g.name for g in after_goals]))
self.assertEqual(
{"strategy_1", "strategy_2", "strategy_3", "strategy_4"},
set([s.name for s in after_strategies]))