diff --git a/releasenotes/notes/compute-cdm-include-all-instances-f7506ded2d57732f.yaml b/releasenotes/notes/compute-cdm-include-all-instances-f7506ded2d57732f.yaml new file mode 100644 index 000000000..e39f0e354 --- /dev/null +++ b/releasenotes/notes/compute-cdm-include-all-instances-f7506ded2d57732f.yaml @@ -0,0 +1,6 @@ +--- +features: + - Watcher has a whole scope of the cluster, when building + compute CDM which includes all instances. + It filters excluded instances when migration during the + audit. \ No newline at end of file diff --git a/watcher/decision_engine/model/model_root.py b/watcher/decision_engine/model/model_root.py index 07dc4da8e..a72f41d4b 100644 --- a/watcher/decision_engine/model/model_root.py +++ b/watcher/decision_engine/model/model_root.py @@ -16,6 +16,7 @@ Openstack implementation of the cluster graph. """ +import ast from lxml import etree import networkx as nx from oslo_concurrency import lockutils @@ -225,6 +226,8 @@ class ModelRoot(nx.DiGraph, base.Model): for inst in root.findall('.//Instance'): instance = element.Instance(**inst.attrib) + instance.watcher_exclude = ast.literal_eval( + inst.attrib["watcher_exclude"]) model.add_instance(instance) parent = inst.getparent() diff --git a/watcher/decision_engine/strategy/strategies/basic_consolidation.py b/watcher/decision_engine/strategy/strategies/basic_consolidation.py index c3fdcb4fa..5be5cf325 100644 --- a/watcher/decision_engine/strategy/strategies/basic_consolidation.py +++ b/watcher/decision_engine/strategy/strategies/basic_consolidation.py @@ -400,6 +400,11 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy): sorted_score): number_migrations = 0 for mig_instance, __ in sorted_instances: + # skip exclude instance when migrating + if mig_instance.watcher_exclude: + LOG.debug("Instance is excluded by scope, " + "skipped: %s", mig_instance.uuid) + continue for node_uuid, __ in sorted_score: mig_source_node = self.compute_model.get_node_by_uuid( node_to_release) diff --git a/watcher/decision_engine/strategy/strategies/outlet_temp_control.py b/watcher/decision_engine/strategy/strategies/outlet_temp_control.py index 467a5c2c4..fdc2fd790 100644 --- a/watcher/decision_engine/strategy/strategies/outlet_temp_control.py +++ b/watcher/decision_engine/strategy/strategies/outlet_temp_control.py @@ -212,6 +212,11 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy): mig_source_node) for instance in instances_of_src: try: + # NOTE: skip exclude instance when migrating + if instance.watcher_exclude: + LOG.debug("Instance is excluded by scope, " + "skipped: %s", instance.uuid) + continue # select the first active instance to migrate if (instance.state != element.InstanceState.ACTIVE.value): diff --git a/watcher/decision_engine/strategy/strategies/uniform_airflow.py b/watcher/decision_engine/strategy/strategies/uniform_airflow.py index 0a4a436a4..8dd439813 100644 --- a/watcher/decision_engine/strategy/strategies/uniform_airflow.py +++ b/watcher/decision_engine/strategy/strategies/uniform_airflow.py @@ -240,6 +240,11 @@ class UniformAirflow(base.BaseStrategy): else: # migrate the first active instance for instance in source_instances: + # NOTE: skip exclude instance when migrating + if instance.watcher_exclude: + LOG.debug("Instance is excluded by scope, " + "skipped: %s", instance.uuid) + continue if (instance.state != element.InstanceState.ACTIVE.value): LOG.info( diff --git a/watcher/decision_engine/strategy/strategies/vm_workload_consolidation.py b/watcher/decision_engine/strategy/strategies/vm_workload_consolidation.py index 2af96bab4..3b2959b3e 100644 --- a/watcher/decision_engine/strategy/strategies/vm_workload_consolidation.py +++ b/watcher/decision_engine/strategy/strategies/vm_workload_consolidation.py @@ -504,6 +504,11 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy): key=lambda x: self.get_instance_utilization( x)['cpu'] ): + # skip exclude instance when migrating + if instance.watcher_exclude: + LOG.debug("Instance is excluded by scope, " + "skipped: %s", instance.uuid) + continue for destination_node in reversed(sorted_nodes): if self.instance_fits( instance, destination_node, cc): @@ -536,6 +541,11 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy): self.compute_model.get_node_instances(node), key=lambda x: self.get_instance_utilization(x)['cpu']) for instance in reversed(instances): + # skip exclude instance when migrating + if instance.watcher_exclude: + LOG.debug("Instance is excluded by scope, " + "skipped: %s", instance.uuid) + continue dsc = len(sorted_nodes) - 1 for destination_node in reversed(sorted_nodes): if asc >= dsc: diff --git a/watcher/decision_engine/strategy/strategies/workload_balance.py b/watcher/decision_engine/strategy/strategies/workload_balance.py index 426983df6..c0f0917d5 100644 --- a/watcher/decision_engine/strategy/strategies/workload_balance.py +++ b/watcher/decision_engine/strategy/strategies/workload_balance.py @@ -208,6 +208,11 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy): instance_id = None for instance in source_instances: try: + # NOTE: skip exclude instance when migrating + if instance.watcher_exclude: + LOG.debug("Instance is excluded by scope, " + "skipped: %s", instance.uuid) + continue # select the first active VM to migrate if (instance.state != element.InstanceState.ACTIVE.value): diff --git a/watcher/decision_engine/strategy/strategies/workload_stabilization.py b/watcher/decision_engine/strategy/strategies/workload_stabilization.py index bf4d4edef..e657b8227 100644 --- a/watcher/decision_engine/strategy/strategies/workload_stabilization.py +++ b/watcher/decision_engine/strategy/strategies/workload_stabilization.py @@ -412,10 +412,15 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy): c_nodes.remove(src_host) node_list = yield_nodes(c_nodes) for instance in self.compute_model.get_node_instances(src_node): - min_sd_case = {'value': current_weighted_sd} + # NOTE: skip exclude instance when migrating + if instance.watcher_exclude: + LOG.debug("Instance is excluded by scope, " + "skipped: %s", instance.uuid) + continue if instance.state not in [element.InstanceState.ACTIVE.value, element.InstanceState.PAUSED.value]: continue + min_sd_case = {'value': current_weighted_sd} for dst_host in next(node_list): dst_node = self.compute_model.get_node_by_uuid(dst_host) sd_case = self.calculate_migration_case( diff --git a/watcher/tests/decision_engine/model/data/scenario_1.xml b/watcher/tests/decision_engine/model/data/scenario_1.xml index 7476af277..95cba68a9 100644 --- a/watcher/tests/decision_engine/model/data/scenario_1.xml +++ b/watcher/tests/decision_engine/model/data/scenario_1.xml @@ -1,47 +1,47 @@ - - + + - + - - - + + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/watcher/tests/decision_engine/model/data/scenario_1_with_1_node_unavailable.xml b/watcher/tests/decision_engine/model/data/scenario_1_with_1_node_unavailable.xml index ad95c2fda..fb19025f1 100644 --- a/watcher/tests/decision_engine/model/data/scenario_1_with_1_node_unavailable.xml +++ b/watcher/tests/decision_engine/model/data/scenario_1_with_1_node_unavailable.xml @@ -1,50 +1,50 @@ - - + + - + - - - + + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/watcher/tests/decision_engine/model/data/scenario_1_with_all_instances_exclude.xml b/watcher/tests/decision_engine/model/data/scenario_1_with_all_instances_exclude.xml new file mode 100644 index 000000000..6b4f87012 --- /dev/null +++ b/watcher/tests/decision_engine/model/data/scenario_1_with_all_instances_exclude.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/watcher/tests/decision_engine/model/data/scenario_1_with_metrics.xml b/watcher/tests/decision_engine/model/data/scenario_1_with_metrics.xml index 17c084c40..fffa2fcc2 100644 --- a/watcher/tests/decision_engine/model/data/scenario_1_with_metrics.xml +++ b/watcher/tests/decision_engine/model/data/scenario_1_with_metrics.xml @@ -1,8 +1,8 @@ - + - + diff --git a/watcher/tests/decision_engine/model/data/scenario_2_with_metrics.xml b/watcher/tests/decision_engine/model/data/scenario_2_with_metrics.xml index 668ef1985..6e94543f4 100644 --- a/watcher/tests/decision_engine/model/data/scenario_2_with_metrics.xml +++ b/watcher/tests/decision_engine/model/data/scenario_2_with_metrics.xml @@ -1,11 +1,11 @@ - - - - - - + + + + + + diff --git a/watcher/tests/decision_engine/model/data/scenario_3_with_2_nodes.xml b/watcher/tests/decision_engine/model/data/scenario_3_with_2_nodes.xml index 189d81ee9..209a83684 100644 --- a/watcher/tests/decision_engine/model/data/scenario_3_with_2_nodes.xml +++ b/watcher/tests/decision_engine/model/data/scenario_3_with_2_nodes.xml @@ -1,8 +1,8 @@ - + - + diff --git a/watcher/tests/decision_engine/model/data/scenario_3_with_metrics.xml b/watcher/tests/decision_engine/model/data/scenario_3_with_metrics.xml index 5c6079916..e734d3595 100644 --- a/watcher/tests/decision_engine/model/data/scenario_3_with_metrics.xml +++ b/watcher/tests/decision_engine/model/data/scenario_3_with_metrics.xml @@ -1,9 +1,9 @@ - - - - + + + + diff --git a/watcher/tests/decision_engine/model/data/scenario_5_with_instance_disk_0.xml b/watcher/tests/decision_engine/model/data/scenario_5_with_instance_disk_0.xml index a8bffab30..0d71f90f8 100644 --- a/watcher/tests/decision_engine/model/data/scenario_5_with_instance_disk_0.xml +++ b/watcher/tests/decision_engine/model/data/scenario_5_with_instance_disk_0.xml @@ -1,5 +1,5 @@ - + diff --git a/watcher/tests/decision_engine/model/data/scenario_6_with_2_nodes.xml b/watcher/tests/decision_engine/model/data/scenario_6_with_2_nodes.xml index 636827c63..a75be0abe 100644 --- a/watcher/tests/decision_engine/model/data/scenario_6_with_2_nodes.xml +++ b/watcher/tests/decision_engine/model/data/scenario_6_with_2_nodes.xml @@ -1,10 +1,10 @@ - - + + - - + + diff --git a/watcher/tests/decision_engine/model/data/scenario_7_with_2_nodes.xml b/watcher/tests/decision_engine/model/data/scenario_7_with_2_nodes.xml index cf86c00e2..2e9e2a134 100644 --- a/watcher/tests/decision_engine/model/data/scenario_7_with_2_nodes.xml +++ b/watcher/tests/decision_engine/model/data/scenario_7_with_2_nodes.xml @@ -1,10 +1,10 @@ - - + + - - + + diff --git a/watcher/tests/decision_engine/model/data/scenario_8_with_4_nodes.xml b/watcher/tests/decision_engine/model/data/scenario_8_with_4_nodes.xml index a646c6e90..dac56c1bf 100644 --- a/watcher/tests/decision_engine/model/data/scenario_8_with_4_nodes.xml +++ b/watcher/tests/decision_engine/model/data/scenario_8_with_4_nodes.xml @@ -1,16 +1,16 @@ - - - + + + - + - + - + diff --git a/watcher/tests/decision_engine/model/data/scenario_9_with_3_active_plus_1_disabled_nodes.xml b/watcher/tests/decision_engine/model/data/scenario_9_with_3_active_plus_1_disabled_nodes.xml index 488f1c7f0..c66239edb 100644 --- a/watcher/tests/decision_engine/model/data/scenario_9_with_3_active_plus_1_disabled_nodes.xml +++ b/watcher/tests/decision_engine/model/data/scenario_9_with_3_active_plus_1_disabled_nodes.xml @@ -1,16 +1,16 @@ - - - + + + - + - + - + diff --git a/watcher/tests/decision_engine/model/faker_cluster_state.py b/watcher/tests/decision_engine/model/faker_cluster_state.py index 043d4e11b..0e05714dc 100644 --- a/watcher/tests/decision_engine/model/faker_cluster_state.py +++ b/watcher/tests/decision_engine/model/faker_cluster_state.py @@ -121,6 +121,9 @@ class FakerModelCollector(base.BaseClusterDataModelCollector): def generate_scenario_1_with_1_node_unavailable(self): return self.load_model('scenario_1_with_1_node_unavailable.xml') + def generate_scenario_1_with_all_instances_exclude(self): + return self.load_model('scenario_1_with_all_instances_exclude.xml') + def generate_scenario_3_with_2_nodes(self): return self.load_model('scenario_3_with_2_nodes.xml') diff --git a/watcher/tests/decision_engine/strategy/strategies/test_workload_stabilization.py b/watcher/tests/decision_engine/strategy/strategies/test_workload_stabilization.py index f3be6c7ed..961edc92e 100644 --- a/watcher/tests/decision_engine/strategy/strategies/test_workload_stabilization.py +++ b/watcher/tests/decision_engine/strategy/strategies/test_workload_stabilization.py @@ -195,6 +195,15 @@ class TestWorkloadStabilization(base.TestCase): 10, len(self.strategy.simulate_migrations(self.hosts_load_assert))) + def test_simulate_migrations_with_all_instances_exclude(self): + model = \ + self.fake_cluster.generate_scenario_1_with_all_instances_exclude() + self.m_model.return_value = model + self.strategy.host_choice = 'fullsearch' + self.assertEqual( + 0, + len(self.strategy.simulate_migrations(self.hosts_load_assert))) + def test_check_threshold(self): self.m_model.return_value = self.fake_cluster.generate_scenario_1() self.strategy.thresholds = {'cpu_util': 0.001, 'memory.resident': 0.2}