diff --git a/watcher/decision_engine/goal/efficacy/base.py b/watcher/decision_engine/goal/efficacy/base.py index 364c377a0..19463c1a8 100644 --- a/watcher/decision_engine/goal/efficacy/base.py +++ b/watcher/decision_engine/goal/efficacy/base.py @@ -72,7 +72,8 @@ class EfficacySpecification(object): } for indicator in self.indicators_specs: schema["properties"][indicator.name] = indicator.schema - schema["required"].append(indicator.name) + if indicator.required: + schema["required"].append(indicator.name) return schema def validate_efficacy_indicators(self, indicators_map): diff --git a/watcher/decision_engine/goal/efficacy/indicators.py b/watcher/decision_engine/goal/efficacy/indicators.py index 73c152260..713291cfa 100644 --- a/watcher/decision_engine/goal/efficacy/indicators.py +++ b/watcher/decision_engine/goal/efficacy/indicators.py @@ -90,7 +90,7 @@ class ComputeNodesCount(IndicatorSpecification): return { "type": "integer", "minimum": 0 - } + } class ReleasedComputeNodesCount(IndicatorSpecification): @@ -106,7 +106,25 @@ class ReleasedComputeNodesCount(IndicatorSpecification): return { "type": "integer", "minimum": 0 - } + } + + +class InstancesCount(IndicatorSpecification): + def __init__(self): + super(InstancesCount, self).__init__( + name="instances_count", + description=_("The total number of audited instances in " + "strategy."), + unit=None, + required=False, + ) + + @property + def schema(self): + return { + "type": "integer", + "minimum": 0 + } class InstanceMigrationsCount(IndicatorSpecification): @@ -122,7 +140,7 @@ class InstanceMigrationsCount(IndicatorSpecification): return { "type": "integer", "minimum": 0 - } + } class LiveInstanceMigrateCount(IndicatorSpecification): @@ -138,7 +156,7 @@ class LiveInstanceMigrateCount(IndicatorSpecification): return { "type": "integer", "minimum": 0 - } + } class PlannedLiveInstanceMigrateCount(IndicatorSpecification): @@ -154,7 +172,7 @@ class PlannedLiveInstanceMigrateCount(IndicatorSpecification): return { "type": "integer", "minimum": 0 - } + } class ColdInstanceMigrateCount(IndicatorSpecification): @@ -170,7 +188,7 @@ class ColdInstanceMigrateCount(IndicatorSpecification): return { "type": "integer", "minimum": 0 - } + } class PlannedColdInstanceMigrateCount(IndicatorSpecification): @@ -186,7 +204,7 @@ class PlannedColdInstanceMigrateCount(IndicatorSpecification): return { "type": "integer", "minimum": 0 - } + } class VolumeMigrateCount(IndicatorSpecification): @@ -202,7 +220,7 @@ class VolumeMigrateCount(IndicatorSpecification): return { "type": "integer", "minimum": 0 - } + } class PlannedVolumeMigrateCount(IndicatorSpecification): @@ -219,7 +237,7 @@ class PlannedVolumeMigrateCount(IndicatorSpecification): return { "type": "integer", "minimum": 0 - } + } class VolumeUpdateCount(IndicatorSpecification): @@ -236,7 +254,7 @@ class VolumeUpdateCount(IndicatorSpecification): return { "type": "integer", "minimum": 0 - } + } class PlannedVolumeUpdateCount(IndicatorSpecification): @@ -253,4 +271,38 @@ class PlannedVolumeUpdateCount(IndicatorSpecification): return { "type": "integer", "minimum": 0 - } + } + + +class StandardDeviationValue(IndicatorSpecification): + def __init__(self): + super(StandardDeviationValue, self).__init__( + name="standard_deviation_after_audit", + description=_("The value of resulted standard deviation."), + unit=None, + required=False, + ) + + @property + def schema(self): + return { + "type": "number", + "minimum": 0 + } + + +class OriginalStandardDeviationValue(IndicatorSpecification): + def __init__(self): + super(OriginalStandardDeviationValue, self).__init__( + name="standard_deviation_before_audit", + description=_("The value of original standard deviation."), + unit=None, + required=False, + ) + + @property + def schema(self): + return { + "type": "number", + "minimum": 0 + } diff --git a/watcher/decision_engine/goal/efficacy/specs.py b/watcher/decision_engine/goal/efficacy/specs.py index f6b7c437c..66a9b1a97 100644 --- a/watcher/decision_engine/goal/efficacy/specs.py +++ b/watcher/decision_engine/goal/efficacy/specs.py @@ -55,6 +55,32 @@ class ServerConsolidation(base.EfficacySpecification): return global_efficacy +class WorkloadBalancing(base.EfficacySpecification): + + def get_indicators_specifications(self): + return [ + indicators.InstanceMigrationsCount(), + indicators.InstancesCount(), + indicators.StandardDeviationValue(), + indicators.OriginalStandardDeviationValue() + ] + + def get_global_efficacy_indicator(self, indicators_map=None): + gl_indicators = [] + mig_value = 0 + if indicators_map and indicators_map.instance_migrations_count > 0: + mig_value = ( + indicators_map.instance_migrations_count / + float(indicators_map.instances_count) * 100) + gl_indicators.append(efficacy.Indicator( + name="live_migrations_count", + description=_("Ratio of migrated virtual machines to audited " + "virtual machines"), + unit='%', + value=mig_value)) + return gl_indicators + + class HardwareMaintenance(base.EfficacySpecification): def get_indicators_specifications(self): diff --git a/watcher/decision_engine/goal/goals.py b/watcher/decision_engine/goal/goals.py index 2a8748035..54b9e7945 100644 --- a/watcher/decision_engine/goal/goals.py +++ b/watcher/decision_engine/goal/goals.py @@ -141,7 +141,7 @@ class WorkloadBalancing(base.Goal): @classmethod def get_efficacy_specification(cls): """The efficacy spec for the current goal""" - return specs.Unclassified() + return specs.WorkloadBalancing() class AirflowOptimization(base.Goal): diff --git a/watcher/decision_engine/strategy/strategies/storage_capacity_balance.py b/watcher/decision_engine/strategy/strategies/storage_capacity_balance.py index ec431bdb8..44f1f6c1a 100644 --- a/watcher/decision_engine/strategy/strategies/storage_capacity_balance.py +++ b/watcher/decision_engine/strategy/strategies/storage_capacity_balance.py @@ -399,4 +399,7 @@ class StorageCapacityBalance(base.WorkloadStabilizationBaseStrategy): """Post-execution phase """ - pass + self.solution.set_efficacy_indicators( + instance_migrations_count=0, + instances_count=0, + ) diff --git a/watcher/decision_engine/strategy/strategies/workload_balance.py b/watcher/decision_engine/strategy/strategies/workload_balance.py index 07247bb41..1021948d1 100644 --- a/watcher/decision_engine/strategy/strategies/workload_balance.py +++ b/watcher/decision_engine/strategy/strategies/workload_balance.py @@ -81,6 +81,7 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy): # the migration plan will be triggered when the CPU or RAM # utilization % reaches threshold self._meter = None + self.instance_migrations_count = 0 @classmethod def get_name(cls): @@ -377,6 +378,7 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy): self.solution.add_action(action_type=self.MIGRATION, resource_id=instance_src.uuid, input_parameters=parameters) + self.instance_migrations_count += 1 def post_execute(self): """Post-execution phase @@ -384,5 +386,9 @@ class WorkloadBalance(base.WorkloadStabilizationBaseStrategy): This can be used to compute the global efficacy """ self.solution.model = self.compute_model + self.solution.set_efficacy_indicators( + instance_migrations_count=self.instance_migrations_count, + instances_count=len(self.compute_model.get_all_instances()) + ) LOG.debug(self.compute_model.to_string()) diff --git a/watcher/decision_engine/strategy/strategies/workload_stabilization.py b/watcher/decision_engine/strategy/strategies/workload_stabilization.py index 10822ba7a..dcdaf6dca 100644 --- a/watcher/decision_engine/strategy/strategies/workload_stabilization.py +++ b/watcher/decision_engine/strategy/strategies/workload_stabilization.py @@ -80,6 +80,10 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy): self.retry_count = None self.periods = None self.aggregation_method = None + self.sd_before_audit = 0 + self.sd_after_audit = 0 + self.instance_migrations_count = 0 + self.instances_count = 0 @classmethod def get_name(cls): @@ -460,6 +464,7 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy): 'threshold': float(self.thresholds[metric]), 'sd': metric_sd}) LOG.info("Launching workload optimization...") + self.sd_before_audit = metric_sd return self.simulate_migrations(hosts_load) def add_migration(self, @@ -482,6 +487,7 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy): self.add_migration(mig_instance.uuid, 'live', mig_source_node.uuid, mig_destination_node.uuid) + self.instance_migrations_count += 1 def migrate(self, instance_uuid, src_host, dst_host): mig_instance = self.compute_model.get_instance_by_uuid(instance_uuid) @@ -545,6 +551,7 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy): self.migrate(instance_host['instance'], instance_host['s_host'], instance_host['host']) + self.sd_after_audit = min_sd for metric, value in zip(self.metrics, instance_load[:-1]): if value < float(self.thresholds[metric]): @@ -564,4 +571,11 @@ class WorkloadStabilization(base.WorkloadStabilizationBaseStrategy): """ self.fill_solution() + self.solution.set_efficacy_indicators( + instance_migrations_count=self.instance_migrations_count, + standard_deviation_before_audit=self.sd_before_audit, + standard_deviation_after_audit=self.sd_after_audit, + instances_count=len(self.compute_model.get_all_instances()), + ) + LOG.debug(self.compute_model.to_string()) 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 961edc92e..828dc2c05 100644 --- a/watcher/tests/decision_engine/strategy/strategies/test_workload_stabilization.py +++ b/watcher/tests/decision_engine/strategy/strategies/test_workload_stabilization.py @@ -243,6 +243,7 @@ class TestWorkloadStabilization(base.TestCase): self.strategy.thresholds = {'cpu_util': 0.042, 'memory.resident': 0.0001} self.strategy.simulate_migrations = mock.Mock(return_value=False) + self.strategy.instance_migrations_count = 0 with mock.patch.object(self.strategy, 'migrate') as mock_migrate: self.strategy.execute() mock_migrate.assert_not_called()