initial version

Change-Id: I699e0ab082657880998d8618fe29eb7f56c6c661
This commit is contained in:
David TARDIVEL
2015-06-04 15:26:55 +02:00
parent 073c6e49cb
commit d14e057da1
316 changed files with 27260 additions and 0 deletions

View File

@@ -0,0 +1 @@
__author__ = 'Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>'

View File

@@ -0,0 +1,103 @@
# -*- 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.
#
# FIXME(jed): remove this class due jenkins build failed
# The following librairies are removed from requirement.txt :
# - numpy
# - matplotlib
# These dependencies required a server x, jenkin's server has no
# server x
# import matplotlib.pyplot as plt
# import numpy as np
from watcher.decision_engine.strategies.basic_consolidation import \
BasicConsolidation
from watcher.tests.decision_engine.faker_cluster_state import \
FakerStateCollector
from watcher.tests.decision_engine.faker_metrics_collector import \
FakerMetricsCollector
class PlotConsolidationBasic(object):
def plot(self, sercon, orign_model, solution):
pass
# cluster_size = len(orign_model._hypervisors)
# labels = []
# before_score = []
# after_score = []
# for hypevisor_id in orign_model.get_all_hypervisors():
# labels.append(hypevisor_id)
# hypevisor = orign_model.get_hypervisor_from_id(hypevisor_id)
# result_before = sercon.calculate_score_node(hypevisor,
# orign_model)
# result_after = sercon.calculate_score_node(hypevisor,
# solution.get_model())
# before_score.append(float(result_before * 100))
# if result_after == 0:
# result_after = 0
# after_score.append(float(result_after * 100))
#
# ind = np.arange(cluster_size) # the x locations for the groups
# width = 0.35 # the width of the bars
#
# fig, ax = plt.subplots()
#
# rects1 = ax.bar(ind, before_score, width, color='b')
#
# rects2 = ax.bar(ind + width, after_score, width, color='r')
#
# # add some text for labels, title and axes ticks
# ax.set_ylabel(
# 'Score of each hypervisor that represent their \
# utilization level')
# ax.set_title('Watcher Basic Server consolidation (efficiency ' + str(
# sercon.get_solution().get_efficiency()) + " %)")
#
# ax.set_xticks(ind + width)
# ax.set_xticklabels(labels)
# ax.set_ylim([0, 140])
# ax.legend((rects1[0], rects2[0]),
# ('Before Consolidation', 'After Consolidation'))
# def autolabel(rects):
# # attach some text labels
# for rect in rects:
# height = rect.get_height()
# ax.text(rect.get_x() + rect.get_width() / 2., 1.05 * height,
# '%d' % int(height),
# ha='center', va='bottom')
#
# autolabel(rects1)
# autolabel(rects2)
# plt.show()
cluster = FakerStateCollector()
metrics = FakerMetricsCollector()
sercon = BasicConsolidation()
sercon.set_metrics_resource_collector(metrics)
# try overbooking ? :) 150 % cpu
sercon.set_threshold_cores(1)
model_cluster = cluster.generate_scenario_1()
solution = sercon.execute(model_cluster)
plot = PlotConsolidationBasic()
plot.plot(sercon, cluster.generate_scenario_1(), solution)

View File

@@ -0,0 +1,45 @@
# -*- 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.
from oslo_config import cfg
import time
from watcher.decision_engine.strategies.basic_consolidation import \
BasicConsolidation
from watcher.openstack.common import log
from watcher.tests.decision_engine.faker_cluster_state import \
FakerStateCollector
from watcher.tests.decision_engine.faker_metrics_collector import \
FakerMetricsCollector
LOG = log.getLogger(__name__)
cfg.CONF.debug = True
log.setup('metering-controller')
metrics = FakerMetricsCollector()
current_state_cluster = FakerStateCollector()
sercon = BasicConsolidation("basic", "Basic offline consolidation")
sercon.set_metrics_resource_collector(metrics)
start_time = time.clock()
solution = sercon.execute(current_state_cluster.generate_scenario_1())
print(time.clock() - start_time, "seconds")
print(solution)
# planner = DefaultPlanner()
# planner.schedule(solution)

View File

@@ -0,0 +1,43 @@
# -*- 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.
from oslo_config import cfg
import time
from watcher.decision_engine.strategies.basic_consolidation import \
BasicConsolidation
from watcher.openstack.common import log
from watcher.tests.decision_engine.faker_cluster_state import \
FakerStateCollector
from watcher.tests.decision_engine.faker_metrics_collector import \
FakerMetricsCollector
LOG = log.getLogger(__name__)
# debug on
cfg.CONF.debug = True
log.setup('metering-controller')
metrics = FakerMetricsCollector()
current_state_cluster = FakerStateCollector()
sercon = BasicConsolidation()
sercon.set_metrics_resource_collector(metrics)
start_time = time.clock()
solution = sercon.execute(current_state_cluster.generate_scenario_1())
print("duration =" + str((time.clock() - start_time)), "seconds")
LOG.debug(solution)

View File

@@ -0,0 +1,255 @@
# -*- 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 random
from watcher.decision_engine.api.collector.cluster_state_collector import \
ClusterStateCollector
from watcher.decision_engine.framework.model.hypervisor import Hypervisor
from watcher.decision_engine.framework.model.model_root import ModelRoot
from watcher.decision_engine.framework.model.resource import Resource
from watcher.decision_engine.framework.model.resource import ResourceType
from watcher.decision_engine.framework.model.vm import VM
class FakerStateCollector(ClusterStateCollector):
def __init__(self):
pass
def get_latest_state_cluster(self):
return self.generate_scenario_1()
def generate_random(self, count_nodes, number_of_vm_per_node):
vms = []
current_state_cluster = ModelRoot()
# number of nodes
count_node = count_nodes
# number max of vm per hypervisor
node_count_vm = number_of_vm_per_node
# total number of virtual machine
count_vm = (count_node * node_count_vm)
# define ressouce ( CPU, MEM disk, ... )
mem = Resource(ResourceType.memory)
# 2199.954 Mhz
num_cores = Resource(ResourceType.cpu_cores)
disk = Resource(ResourceType.disk)
current_state_cluster.create_resource(mem)
current_state_cluster.create_resource(num_cores)
current_state_cluster.create_resource(disk)
for i in range(0, count_node):
node_uuid = "Node_" + str(i)
hypervisor = Hypervisor()
hypervisor.set_uuid(node_uuid)
mem.set_capacity(hypervisor, 132)
disk.set_capacity(hypervisor, 250)
num_cores.set_capacity(hypervisor, 40)
# print("create "+str(hypervisor))
current_state_cluster.add_hypervisor(hypervisor)
for i in range(0, count_vm):
vm_uuid = "VM_" + str(i)
vm = VM()
vm.set_uuid(vm_uuid)
# print("create "+str(vm))
mem.set_capacity(vm, 8)
disk.set_capacity(vm, 10)
num_cores.set_capacity(vm, 10)
vms.append(vm)
current_state_cluster.add_vm(vm)
j = 0
for node_id in current_state_cluster.get_all_hypervisors():
for i in range(0, random.randint(0, node_count_vm)):
# todo(jed) check if enough capacity
current_state_cluster.get_mapping().map(
current_state_cluster.get_hypervisor_from_id(node_id),
vms[j])
j += 1
return current_state_cluster
def generate_scenario_1(self):
vms = []
current_state_cluster = ModelRoot()
# number of nodes
count_node = 5
# number max of vm per node
node_count_vm = 7
# total number of virtual machine
count_vm = (count_node * node_count_vm)
# define ressouce ( CPU, MEM disk, ... )
mem = Resource(ResourceType.memory)
# 2199.954 Mhz
num_cores = Resource(ResourceType.cpu_cores)
disk = Resource(ResourceType.disk)
current_state_cluster.create_resource(mem)
current_state_cluster.create_resource(num_cores)
current_state_cluster.create_resource(disk)
for i in range(0, count_node):
node_uuid = "Node_" + str(i)
node = Hypervisor()
node.set_uuid(node_uuid)
mem.set_capacity(node, 132)
disk.set_capacity(node, 250)
num_cores.set_capacity(node, 40)
# print("create "+str(node))
current_state_cluster.add_hypervisor(node)
for i in range(0, count_vm):
vm_uuid = "VM_" + str(i)
vm = VM()
vm.set_uuid(vm_uuid)
# print("create "+str(vm))
mem.set_capacity(vm, 2)
disk.set_capacity(vm, 20)
num_cores.set_capacity(vm, 10)
vms.append(vm)
current_state_cluster.add_vm(vm)
current_state_cluster.get_mapping().map(
current_state_cluster.get_hypervisor_from_id("Node_0"),
current_state_cluster.get_vm_from_id("VM_0"))
current_state_cluster.get_mapping().map(
current_state_cluster.get_hypervisor_from_id("Node_0"),
current_state_cluster.get_vm_from_id("VM_1"))
current_state_cluster.get_mapping().map(
current_state_cluster.get_hypervisor_from_id("Node_1"),
current_state_cluster.get_vm_from_id("VM_2"))
current_state_cluster.get_mapping().map(
current_state_cluster.get_hypervisor_from_id("Node_2"),
current_state_cluster.get_vm_from_id("VM_3"))
current_state_cluster.get_mapping().map(
current_state_cluster.get_hypervisor_from_id("Node_2"),
current_state_cluster.get_vm_from_id("VM_4"))
current_state_cluster.get_mapping().map(
current_state_cluster.get_hypervisor_from_id("Node_2"),
current_state_cluster.get_vm_from_id("VM_5"))
current_state_cluster.get_mapping().map(
current_state_cluster.get_hypervisor_from_id("Node_3"),
current_state_cluster.get_vm_from_id("VM_6"))
current_state_cluster.get_mapping().map(
current_state_cluster.get_hypervisor_from_id("Node_4"),
current_state_cluster.get_vm_from_id("VM_7"))
return current_state_cluster
def generate_scenario_2(self):
current_state_cluster = ModelRoot()
# number of nodes
count_node = 5
# define ressouce ( CPU, MEM disk, ... )
mem = Resource(ResourceType.memory)
# 2199.954 Mhz
num_cores = Resource(ResourceType.cpu_cores)
disk = Resource(ResourceType.disk)
current_state_cluster.create_resource(mem)
current_state_cluster.create_resource(num_cores)
current_state_cluster.create_resource(disk)
for i in range(0, count_node):
node_uuid = "Node_" + str(i)
node = Hypervisor()
node.set_uuid(node_uuid)
mem.set_capacity(node, 132)
disk.set_capacity(node, 250)
num_cores.set_capacity(node, 40)
# print("create "+str(node))
current_state_cluster.add_hypervisor(node)
return current_state_cluster
def map(self, model, h_id, vm_id):
model.get_mapping().map(
model.get_hypervisor_from_id(h_id),
model.get_vm_from_id(vm_id))
def generate_scenario_3(self):
vms = []
current_state_cluster = ModelRoot()
# number of nodes
count_node = 10
# number max of vm per node
node_count_vm = 7
# total number of virtual machine
count_vm = (count_node * node_count_vm)
# define ressouce ( CPU, MEM disk, ... )
mem = Resource(ResourceType.memory)
# 2199.954 Mhz
num_cores = Resource(ResourceType.cpu_cores)
disk = Resource(ResourceType.disk)
current_state_cluster.create_resource(mem)
current_state_cluster.create_resource(num_cores)
current_state_cluster.create_resource(disk)
for i in range(0, count_node):
node_uuid = "Node_" + str(i)
node = Hypervisor()
node.set_uuid(node_uuid)
mem.set_capacity(node, 132)
disk.set_capacity(node, 250)
num_cores.set_capacity(node, 40)
# print("create "+str(node))
current_state_cluster.add_hypervisor(node)
for i in range(0, count_vm):
vm_uuid = "VM_" + str(i)
vm = VM()
vm.set_uuid(vm_uuid)
# print("create "+str(vm))
mem.set_capacity(vm, 10)
disk.set_capacity(vm, 25)
num_cores.set_capacity(vm, 16)
vms.append(vm)
current_state_cluster.add_vm(vm)
print(count_vm)
indice = 0
for j in range(0, 2):
node_uuid = "Node_" + str(j)
for i in range(indice, 3):
vm_uuid = "VM_" + str(i)
self.map(current_state_cluster, node_uuid, vm_uuid)
for j in range(2, 5):
node_uuid = "Node_" + str(j)
for i in range(indice, 4):
vm_uuid = "VM_" + str(i)
self.map(current_state_cluster, node_uuid, vm_uuid)
for j in range(5, 10):
node_uuid = "Node_" + str(j)
for i in range(indice, 4):
vm_uuid = "VM_" + str(i)
self.map(current_state_cluster, node_uuid, vm_uuid)
return current_state_cluster

View File

@@ -0,0 +1,113 @@
# -*- 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 random
from watcher.decision_engine.api.collector.metrics_resource_collector import \
MetricsResourceCollector
class FakerMetricsCollector(MetricsResourceCollector):
def __init__(self):
pass
def get_average_usage_vm_cpu(self, uuid):
"""The last VM CPU usage values to average
:param uuid:00
:return:
"""
# query influxdb stream
# compute in stream
# Normalize
mock = {}
# node 0
mock['VM_0'] = 7
mock['VM_1'] = 7
# node 1
mock['VM_2'] = 10
# node 2
mock['VM_3'] = 5
mock['VM_4'] = 5
mock['VM_5'] = 10
# node 3
mock['VM_6'] = 8
# node 4
mock['VM_7'] = 4
if uuid not in mock.keys():
# mock[uuid] = random.randint(1, 4)
mock[uuid] = 8
return mock[str(uuid)]
def get_average_usage_vm_memory(self, uuid):
mock = {}
# node 0
mock['VM_0'] = 2
mock['VM_1'] = 5
# node 1
mock['VM_2'] = 5
# node 2
mock['VM_3'] = 8
mock['VM_4'] = 5
mock['VM_5'] = 16
# node 3
mock['VM_6'] = 8
# node 4
mock['VM_7'] = 4
if uuid not in mock.keys():
# mock[uuid] = random.randint(1, 4)
mock[uuid] = 10
return mock[str(uuid)]
def get_average_usage_vm_disk(self, uuid):
mock = {}
# node 0
mock['VM_0'] = 2
mock['VM_1'] = 2
# node 1
mock['VM_2'] = 2
# node 2
mock['VM_3'] = 10
mock['VM_4'] = 15
mock['VM_5'] = 20
# node 3
mock['VM_6'] = 8
# node 4
mock['VM_7'] = 4
if uuid not in mock.keys():
# mock[uuid] = random.randint(1, 4)
mock[uuid] = 4
return mock[str(uuid)]
def get_virtual_machine_capacity(self, vm_uuid):
return random.randint(1, 4)
def get_average_network_incomming(self, node):
pass
def get_average_network_outcomming(self, node):
pass

View File

@@ -0,0 +1,32 @@
# -*- 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 exceptions
from watcher.decision_engine.framework.events.event_consumer_factory import \
EventConsumerFactory
from watcher.decision_engine.framework.messaging.events import Events
from watcher.tests import base
class TestEventConsumerFactory(base.TestCase):
event_consumer_factory = EventConsumerFactory()
def test_factory_with_unknown_type(self):
self.assertRaises(exceptions.AssertionError,
self.event_consumer_factory.factory,
Events.ALL)

View File

@@ -0,0 +1,75 @@
# -*- 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.
from mock import call
from mock import MagicMock
from watcher.decision_engine.framework.command.trigger_audit_command import \
TriggerAuditCommand
from watcher.decision_engine.framework.messaging.events import Events
from watcher.objects.audit import Audit
from watcher.objects.audit import AuditStatus
from watcher.tests.db.base import DbTestCase
from watcher.tests.decision_engine.faker_cluster_state import \
FakerStateCollector
from watcher.tests.decision_engine.faker_metrics_collector import \
FakerMetricsCollector
from watcher.tests.objects import utils as obj_utils
class TestTriggerAuditCommand(DbTestCase):
def setUp(self):
super(TestTriggerAuditCommand, self).setUp()
self.audit_template = obj_utils.create_test_audit_template(
self.context)
self.audit = obj_utils.create_test_audit(
self.context,
audit_template_id=self.audit_template.id)
def test_trigger_audit_wihout_errors(self):
try:
statedb = FakerStateCollector()
ressourcedb = FakerMetricsCollector()
command = TriggerAuditCommand(MagicMock(), statedb, ressourcedb)
command.execute(self.audit.uuid, self.context)
except Exception:
self.fail("The audit should be trigged wihtour error")
def test_trigger_audit_state_succes(self):
statedb = FakerStateCollector()
ressourcedb = FakerMetricsCollector()
command = TriggerAuditCommand(MagicMock(), statedb, ressourcedb)
command.execute(self.audit.uuid, self.context)
audit = Audit.get_by_uuid(self.context, self.audit.uuid)
self.assertEqual(AuditStatus.SUCCESS, audit.state)
def test_trigger_audit_send_notification(self):
messaging = MagicMock()
statedb = FakerStateCollector()
ressourcedb = FakerMetricsCollector()
command = TriggerAuditCommand(messaging, statedb, ressourcedb)
command.execute(self.audit.uuid, self.context)
call_on_going = call(Events.TRIGGER_AUDIT.name, {
'audit_status': AuditStatus.ONGOING,
'audit_uuid': self.audit.uuid})
call_success = call(Events.TRIGGER_AUDIT.name, {
'audit_status': AuditStatus.SUCCESS,
'audit_uuid': self.audit.uuid})
calls = [call_on_going, call_success]
messaging.topic_status.publish_event.assert_has_calls(calls)
self.assertEqual(2, messaging.topic_status.publish_event.call_count)

View File

@@ -0,0 +1,42 @@
# -*- 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 mock import MagicMock
from watcher.common import utils
from watcher.decision_engine.framework.command.trigger_audit_command import \
TriggerAuditCommand
from watcher.decision_engine.framework.messaging.audit_endpoint import \
AuditEndpoint
from watcher.tests import base
class TestAuditEndpoint(base.TestCase):
def setUp(self):
super(TestAuditEndpoint, self).setUp()
self.endpoint = AuditEndpoint(MagicMock())
def test_trigger_audit(self):
audit_uuid = utils.generate_uuid()
# todo() add
with mock.patch.object(TriggerAuditCommand, 'execute') as mock_call:
expected_uuid = self.endpoint.trigger_audit(
self.context, audit_uuid)
self.assertEqual(audit_uuid, expected_uuid)
mock_call.assert_called_once_with(audit_uuid, self.context)
"""

View File

@@ -0,0 +1,36 @@
# -*- 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.
from watcher.decision_engine.framework.strategy.strategy_loader import \
StrategyLoader
from watcher.tests import base
class TestStrategySelector(base.BaseTestCase):
strategy_loader = StrategyLoader()
def test_load_strategy_with_empty_model(self):
selected_strategy = self.strategy_loader.load(None)
self.assertIsNotNone(selected_strategy,
'The default strategy be must not none')
def test_load_strategy_is_basic(self):
exptected_strategy = 'basic'
selected_strategy = self.strategy_loader.load(exptected_strategy)
self.assertEqual(
selected_strategy.get_name(),
exptected_strategy,
'The default strategy should be basic')

View File

@@ -0,0 +1,47 @@
# -*- 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 oslo_config import cfg
from watcher.decision_engine.framework.strategy.strategy_loader import \
StrategyLoader
from watcher.decision_engine.framework.strategy.strategy_selector import \
StrategySelector
from watcher.objects.audit_template import Goal
from watcher.tests import base
CONF = cfg.CONF
class TestStrategySelector(base.BaseTestCase):
strategy_selector = StrategySelector()
def test_define_from_with_empty(self):
expected_goal = None
expected_strategy = \
CONF.watcher_goals.goals[Goal.SERVERS_CONSOLIDATION]
with mock.patch.object(StrategyLoader, 'load') as \
mock_call:
self.strategy_selector.define_from_goal(expected_goal)
mock_call.assert_called_once_with(expected_strategy)
def test_define_from_goal(self):
expected_goal = Goal.BALANCE_LOAD
expected_strategy = CONF.watcher_goals.goals[expected_goal]
with mock.patch.object(StrategyLoader, 'load') as \
mock_call:
self.strategy_selector.define_from_goal(expected_goal)
mock_call.assert_called_once_with(expected_strategy)

View File

@@ -0,0 +1,75 @@
# -*- 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 watcher.common import utils
from watcher.db import api as db_api
from watcher.decision_engine.framework.default_planner import DefaultPlanner
from watcher.decision_engine.strategies.basic_consolidation import \
BasicConsolidation
from watcher.tests.db import base
from watcher.tests.db import utils as db_utils
from watcher.tests.decision_engine.faker_cluster_state import \
FakerStateCollector
from watcher.tests.decision_engine.faker_metrics_collector import \
FakerMetricsCollector
from watcher.tests.objects import utils as obj_utils
class SolutionFaker(object):
@staticmethod
def build():
metrics = FakerMetricsCollector()
current_state_cluster = FakerStateCollector()
sercon = BasicConsolidation("basic", "Basic offline consolidation")
sercon.set_metrics_resource_collector(metrics)
return sercon.execute(current_state_cluster.generate_scenario_1())
class TestDefaultPlanner(base.DbTestCase):
default_planner = DefaultPlanner()
def setUp(self):
super(TestDefaultPlanner, self).setUp()
obj_utils.create_test_audit_template(self.context)
p = mock.patch.object(db_api.Connection, 'create_action_plan')
self.mock_create_action_plan = p.start()
self.mock_create_action_plan.side_effect = (
self._simulate_action_plan_create)
self.addCleanup(p.stop)
q = mock.patch.object(db_api.Connection, 'create_action')
self.mock_create_action = q.start()
self.mock_create_action.side_effect = (
self._simulate_action_create)
self.addCleanup(q.stop)
def _simulate_action_plan_create(self, action_plan):
action_plan.create()
return action_plan
def _simulate_action_create(self, action):
action.create()
return action
def test_scheduler_w(self):
audit = db_utils.create_test_audit(uuid=utils.generate_uuid())
fake_solution = SolutionFaker.build()
action_plan = self.default_planner.schedule(self.context,
audit.id, fake_solution)
self.assertIsNotNone(action_plan.uuid)

View File

@@ -0,0 +1,45 @@
# -*- 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 watcher.common import utils
from watcher.decision_engine.framework.events.event_consumer_factory import \
EventConsumerFactory
from watcher.common.messaging.events.event import Event
from watcher.decision_engine.framework.manager_decision_engine import \
DecisionEngineManager
from watcher.decision_engine.framework.messaging.events import Events
from watcher.tests import base
class TestDecisionEngineManager(base.TestCase):
def setUp(self):
super(TestDecisionEngineManager, self).setUp()
self.manager = DecisionEngineManager()
def test_event_receive(self):
# todo(jed) remove useless
with mock.patch.object(EventConsumerFactory, 'factory') as mock_call:
data = {"key1": "value"}
request_id = utils.generate_uuid()
event_type = Events.TRIGGER_AUDIT
event = Event(event_type, data, request_id)
self.manager.event_receive(event)
mock_call.assert_called_once_with(event_type)

View File

@@ -0,0 +1,57 @@
# -*- 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
import oslo.messaging as om
from watcher.common import exception
from watcher.common import utils
from watcher.decision_engine.framework.rpcapi import DecisionEngineAPI
from watcher.tests import base
class TestDecisionEngineAPI(base.TestCase):
def setUp(self):
super(TestDecisionEngineAPI, self).setUp()
api = DecisionEngineAPI()
def test_get_version(self):
expected_version = self.api.API_VERSION
self.assertEqual(expected_version, self.api.get_version())
def test_get_api_version(self):
with mock.patch.object(om.RPCClient, 'call') as mock_call:
expected_context = self.context
self.api.check_api_version(expected_context)
mock_call.assert_called_once_with(
expected_context.to_dict(),
'check_api_version',
api_version=DecisionEngineAPI().API_VERSION)
def test_execute_audit_throw_exception(self):
audit_uuid = "uuid"
self.assertRaises(exception.InvalidUuidOrName,
self.api.trigger_audit,
audit_uuid)
def test_execute_audit_without_error(self):
with mock.patch.object(om.RPCClient, 'call') as mock_call:
audit_uuid = utils.generate_uuid()
self.api.trigger_audit(self.context, audit_uuid)
mock_call.assert_called_once_with(self.context.to_dict(),
'trigger_audit',
audit_uuid=audit_uuid)

View File

@@ -0,0 +1,184 @@
# -*- 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.
#
from watcher.common import exception
from watcher.decision_engine.framework.meta_actions.hypervisor_state import \
ChangeHypervisorState
from watcher.decision_engine.framework.meta_actions.power_state import \
ChangePowerState
from watcher.decision_engine.framework.meta_actions.migrate import Migrate
from watcher.decision_engine.framework.model.model_root import ModelRoot
from watcher.decision_engine.strategies.basic_consolidation import \
BasicConsolidation
from watcher.tests import base
from watcher.tests.decision_engine.faker_cluster_state import \
FakerStateCollector
from watcher.tests.decision_engine.faker_metrics_collector import \
FakerMetricsCollector
class TestBasicConsolidation(base.BaseTestCase):
# fake metrics
fake_metrics = FakerMetricsCollector()
# fake cluster
fake_cluster = FakerStateCollector()
def test_cluster_size(self):
size_cluster = len(
self.fake_cluster.generate_scenario_1().get_all_hypervisors())
size_cluster_assert = 5
self.assertEqual(size_cluster, size_cluster_assert)
def test_basic_consolidation_score_hypervisor(self):
cluster = self.fake_cluster.generate_scenario_1()
sercon = BasicConsolidation()
sercon.set_metrics_resource_collector(self.fake_metrics)
node_1_score = 0.09862626262626262
self.assertEqual(
sercon.calculate_score_node(
cluster.get_hypervisor_from_id("Node_1"),
cluster), node_1_score)
node_2_score = 0.29989898989898994
self.assertEqual(
sercon.calculate_score_node(
cluster.get_hypervisor_from_id("Node_2"),
cluster), node_2_score)
node_0_score = 0.13967676767676765
self.assertEqual(
sercon.calculate_score_node(
cluster.get_hypervisor_from_id("Node_0"),
cluster), node_0_score)
def test_basic_consolidation_score_vm(self):
cluster = self.fake_cluster.generate_scenario_1()
sercon = BasicConsolidation()
sercon.set_metrics_resource_collector(self.fake_metrics)
vm_0 = cluster.get_vm_from_id("VM_0")
vm_0_score = 0.6
self.assertEqual(sercon.calculate_score_vm(vm_0, cluster), vm_0_score)
vm_1 = cluster.get_vm_from_id("VM_1")
vm_1_score = 1.0999999999999999
self.assertEqual(sercon.calculate_score_vm(vm_1, cluster),
vm_1_score)
vm_2 = cluster.get_vm_from_id("VM_2")
vm_2_score = 1.2
self.assertEqual(sercon.calculate_score_vm(vm_2, cluster), vm_2_score)
def test_basic_consolidation_weight(self):
cluster = self.fake_cluster.generate_scenario_1()
sercon = BasicConsolidation()
sercon.set_metrics_resource_collector(self.fake_metrics)
vm_0 = cluster.get_vm_from_id("VM_0")
cores = 16
# 80 Go
disk = 80
# mem 8 Go
mem = 8
vm_0_weight_assert = 3.1999999999999997
self.assertEqual(sercon.calculate_weight(cluster, vm_0, cores, disk,
mem),
vm_0_weight_assert)
def test_basic_consolidation_efficiency(self):
sercon = BasicConsolidation()
sercon.set_metrics_resource_collector(self.fake_metrics)
efficient_assert = 100
solution = sercon.execute(self.fake_cluster.generate_scenario_1())
self.assertEqual(solution.get_efficiency(), efficient_assert)
def test_exception_model(self):
sercon = BasicConsolidation()
self.assertRaises(exception.ClusteStateNotDefined, sercon.execute,
None)
def test_exception_cluster_empty(self):
sercon = BasicConsolidation()
model = ModelRoot()
self.assertRaises(exception.ClusterEmpty, sercon.execute,
model)
def test_exception_metric_collector(self):
sercon = BasicConsolidation()
self.assertRaises(exception.MetricCollectorNotDefined,
sercon.calculate_score_vm, "VM_1", None)
def check_migration(self, array, indice, vm, src, dest):
"""Helper to check migration
:param array:
:param indice:
:param vm:
:param src:
:param dest:
:return:
"""
self.assertEqual(array[indice].get_vm().get_uuid(), vm)
self.assertEqual(array[indice].get_source_hypervisor().get_uuid(), src)
self.assertEqual(array[indice].get_dest_hypervisor().get_uuid(), dest)
def test_basic_consolidation_migration(self):
sercon = BasicConsolidation()
sercon.set_metrics_resource_collector(self.fake_metrics)
solution = sercon.execute(self.fake_cluster.generate_scenario_1())
count_migration = 0
change_hypervisor_state = 0
change_power_state = 0
migrate = []
for action in solution.meta_actions:
if isinstance(action, Migrate):
count_migration += 1
migrate.append(action)
if isinstance(action, ChangeHypervisorState):
change_hypervisor_state += 1
if isinstance(action, ChangePowerState):
change_power_state += 1
self.assertEqual(change_hypervisor_state, 3)
self.assertEqual(count_migration, 3)
# check migration
self.check_migration(migrate, 0, "VM_7", "Node_4", "Node_2")
self.check_migration(migrate, 1, "VM_6", "Node_3", "Node_0")
self.check_migration(migrate, 2, "VM_2", "Node_1", "Node_0")
def test_basic_consolidation_random(self):
metrics = FakerMetricsCollector()
current_state_cluster = FakerStateCollector()
sercon = BasicConsolidation("sercon", "Basic offline consolidation")
sercon.set_metrics_resource_collector(metrics)
solution = sercon.execute(
current_state_cluster.generate_random(25, 2))
count_migration = 0
change_hypervisor_state = 0
change_power_state = 0
migrate = []
for action in solution.meta_actions:
if isinstance(action, Migrate):
count_migration += 1
migrate.append(action)
if isinstance(action, ChangeHypervisorState):
change_hypervisor_state += 1
if isinstance(action, ChangePowerState):
change_power_state += 1

View File

@@ -0,0 +1,22 @@
# -*- 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.
from watcher.tests import base
class TestLoader(base.BaseTestCase):
def test_loader(self):
pass

View File

@@ -0,0 +1,54 @@
# -*- 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 uuid
from watcher.common import exception
from watcher.decision_engine.framework.model.hypervisor import Hypervisor
from watcher.decision_engine.framework.model.model_root import ModelRoot
from watcher.tests.decision_engine.faker_cluster_state import \
FakerStateCollector
from watcher.tests import base
class TestModel(base.BaseTestCase):
def test_model(self):
fake_cluster = FakerStateCollector()
model = fake_cluster.generate_scenario_1()
self.assertEqual(len(model._hypervisors), 5)
self.assertEqual(len(model._vms), 35)
self.assertEqual(len(model.get_mapping().get_mapping()), 5)
def test_add_hypervisor(self):
model = ModelRoot()
id = str(uuid.uuid4())
hypervisor = Hypervisor()
hypervisor.set_uuid(id)
model.add_hypervisor(hypervisor)
self.assertEqual(model.get_hypervisor_from_id(id), hypervisor)
def test_delete_hypervisor(self):
model = ModelRoot()
id = str(uuid.uuid4())
hypervisor = Hypervisor()
hypervisor.set_uuid(id)
model.add_hypervisor(hypervisor)
self.assertEqual(model.get_hypervisor_from_id(id), hypervisor)
model.remove_hypervisor(hypervisor)
self.assertRaises(exception.HypervisorNotFound,
model.get_hypervisor_from_id, id)

View File

@@ -0,0 +1,21 @@
# -*- 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.
from watcher.tests import base
class TestPlanner(base.BaseTestCase):
def test_planner(self):
pass