replace windows line endings with unix line endings

The python 3 version of the linter does not allow Windows-style line
endings (\r\n) so replace them with UNIX-style endings (\n).

Change-Id: Ifb97491323d3df92bb1520e373552aeb5e1919a4
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
Doug Hellmann
2018-06-13 14:34:03 -04:00
parent a330576eae
commit ae0918488d
2 changed files with 537 additions and 537 deletions

View File

@@ -1,331 +1,331 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# Copyright (c) 2017 chinac.com # Copyright (c) 2017 chinac.com
# #
# Authors: suzhengwei<suzhengwei@chinac.com> # Authors: suzhengwei<suzhengwei@chinac.com>
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, # distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. # implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
from oslo_log import log from oslo_log import log
import six import six
from watcher._i18n import _ from watcher._i18n import _
from watcher.common import exception as wexc from watcher.common import exception as wexc
from watcher.decision_engine.model import element from watcher.decision_engine.model import element
from watcher.decision_engine.strategy.strategies import base from watcher.decision_engine.strategy.strategies import base
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
class HostMaintenance(base.HostMaintenanceBaseStrategy): class HostMaintenance(base.HostMaintenanceBaseStrategy):
"""[PoC]Host Maintenance """[PoC]Host Maintenance
*Description* *Description*
It is a migration strategy for one compute node maintenance, It is a migration strategy for one compute node maintenance,
without having the user's application been interruptted. without having the user's application been interruptted.
If given one backup node, the strategy will firstly If given one backup node, the strategy will firstly
migrate all instances from the maintenance node to migrate all instances from the maintenance node to
the backup node. If the backup node is not provided, the backup node. If the backup node is not provided,
it will migrate all instances, relying on nova-scheduler. it will migrate all instances, relying on nova-scheduler.
*Requirements* *Requirements*
* 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*
- This is a proof of concept that is not meant to be used in production - This is a proof of concept that is not meant to be used in production
- It migrates all instances from one host to other hosts. It's better to - It migrates all instances from one host to other hosts. It's better to
execute such strategy when load is not heavy, and use this algorithm execute such strategy when load is not heavy, and use this algorithm
with `ONESHOT` audit. with `ONESHOT` audit.
- It assume that cold and live migrations are possible - It assume that cold and live migrations are possible
""" """
INSTANCE_MIGRATION = "migrate" INSTANCE_MIGRATION = "migrate"
CHANGE_NOVA_SERVICE_STATE = "change_nova_service_state" CHANGE_NOVA_SERVICE_STATE = "change_nova_service_state"
REASON_FOR_DISABLE = 'watcher_disabled' REASON_FOR_DISABLE = 'watcher_disabled'
def __init__(self, config, osc=None): def __init__(self, config, osc=None):
super(HostMaintenance, self).__init__(config, osc) super(HostMaintenance, self).__init__(config, osc)
@classmethod @classmethod
def get_name(cls): def get_name(cls):
return "host_maintenance" return "host_maintenance"
@classmethod @classmethod
def get_display_name(cls): def get_display_name(cls):
return _("Host Maintenance Strategy") return _("Host Maintenance Strategy")
@classmethod @classmethod
def get_translatable_display_name(cls): def get_translatable_display_name(cls):
return "Host Maintenance Strategy" return "Host Maintenance Strategy"
@classmethod @classmethod
def get_schema(cls): def get_schema(cls):
return { return {
"properties": { "properties": {
"maintenance_node": { "maintenance_node": {
"description": "The name of the compute node which " "description": "The name of the compute node which "
"need maintenance", "need maintenance",
"type": "string", "type": "string",
}, },
"backup_node": { "backup_node": {
"description": "The name of the compute node which " "description": "The name of the compute node which "
"will backup the maintenance node.", "will backup the maintenance node.",
"type": "string", "type": "string",
}, },
}, },
"required": ["maintenance_node"], "required": ["maintenance_node"],
} }
def get_disabled_compute_nodes_with_reason(self, reason=None): def get_disabled_compute_nodes_with_reason(self, reason=None):
return {uuid: cn for uuid, cn in return {uuid: cn for uuid, cn in
self.compute_model.get_all_compute_nodes().items() self.compute_model.get_all_compute_nodes().items()
if cn.state == element.ServiceState.ONLINE.value and if cn.state == element.ServiceState.ONLINE.value and
cn.status == element.ServiceState.DISABLED.value and cn.status == element.ServiceState.DISABLED.value and
cn.disabled_reason == reason} cn.disabled_reason == reason}
def get_disabled_compute_nodes(self): def get_disabled_compute_nodes(self):
return self.get_disabled_compute_nodes_with_reason( return self.get_disabled_compute_nodes_with_reason(
self.REASON_FOR_DISABLE) self.REASON_FOR_DISABLE)
def get_instance_state_str(self, instance): def get_instance_state_str(self, instance):
"""Get instance state in string format""" """Get instance state in string format"""
if isinstance(instance.state, six.string_types): if isinstance(instance.state, six.string_types):
return instance.state return instance.state
elif isinstance(instance.state, element.InstanceState): elif isinstance(instance.state, element.InstanceState):
return instance.state.value return instance.state.value
else: else:
LOG.error('Unexpected instance state type, ' LOG.error('Unexpected instance state type, '
'state=%(state)s, state_type=%(st)s.', 'state=%(state)s, state_type=%(st)s.',
dict(state=instance.state, dict(state=instance.state,
st=type(instance.state))) st=type(instance.state)))
raise wexc.WatcherException raise wexc.WatcherException
def get_node_status_str(self, node): def get_node_status_str(self, node):
"""Get node status in string format""" """Get node status in string format"""
if isinstance(node.status, six.string_types): if isinstance(node.status, six.string_types):
return node.status return node.status
elif isinstance(node.status, element.ServiceState): elif isinstance(node.status, element.ServiceState):
return node.status.value return node.status.value
else: else:
LOG.error('Unexpected node status type, ' LOG.error('Unexpected node status type, '
'status=%(status)s, status_type=%(st)s.', 'status=%(status)s, status_type=%(st)s.',
dict(status=node.status, dict(status=node.status,
st=type(node.status))) st=type(node.status)))
raise wexc.WatcherException raise wexc.WatcherException
def get_node_capacity(self, node): def get_node_capacity(self, node):
"""Collect cpu, ram and disk capacity of a node. """Collect cpu, ram and disk capacity of a node.
:param node: node object :param node: node object
:return: dict(cpu(cores), ram(MB), disk(B)) :return: dict(cpu(cores), ram(MB), disk(B))
""" """
return dict(cpu=node.vcpus, return dict(cpu=node.vcpus,
ram=node.memory, ram=node.memory,
disk=node.disk_capacity) disk=node.disk_capacity)
def get_node_used(self, node): def get_node_used(self, node):
"""Collect cpu, ram and disk used of a node. """Collect cpu, ram and disk used of a node.
:param node: node object :param node: node object
:return: dict(cpu(cores), ram(MB), disk(B)) :return: dict(cpu(cores), ram(MB), disk(B))
""" """
vcpus_used = 0 vcpus_used = 0
memory_used = 0 memory_used = 0
disk_used = 0 disk_used = 0
for instance in self.compute_model.get_node_instances(node): for instance in self.compute_model.get_node_instances(node):
vcpus_used += instance.vcpus vcpus_used += instance.vcpus
memory_used += instance.memory memory_used += instance.memory
disk_used += instance.disk disk_used += instance.disk
return dict(cpu=vcpus_used, return dict(cpu=vcpus_used,
ram=memory_used, ram=memory_used,
disk=disk_used) disk=disk_used)
def get_node_free(self, node): def get_node_free(self, node):
"""Collect cpu, ram and disk free of a node. """Collect cpu, ram and disk free of a node.
:param node: node object :param node: node object
:return: dict(cpu(cores), ram(MB), disk(B)) :return: dict(cpu(cores), ram(MB), disk(B))
""" """
node_capacity = self.get_node_capacity(node) node_capacity = self.get_node_capacity(node)
node_used = self.get_node_used(node) node_used = self.get_node_used(node)
return dict(cpu=node_capacity['cpu']-node_used['cpu'], return dict(cpu=node_capacity['cpu']-node_used['cpu'],
ram=node_capacity['ram']-node_used['ram'], ram=node_capacity['ram']-node_used['ram'],
disk=node_capacity['disk']-node_used['disk'], disk=node_capacity['disk']-node_used['disk'],
) )
def host_fits(self, source_node, destination_node): def host_fits(self, source_node, destination_node):
"""check host fits """check host fits
return True if VMs could intensively migrate return True if VMs could intensively migrate
from source_node to destination_node. from source_node to destination_node.
""" """
source_node_used = self.get_node_used(source_node) source_node_used = self.get_node_used(source_node)
destination_node_free = self.get_node_free(destination_node) destination_node_free = self.get_node_free(destination_node)
metrics = ['cpu', 'ram'] metrics = ['cpu', 'ram']
for m in metrics: for m in metrics:
if source_node_used[m] > destination_node_free[m]: if source_node_used[m] > destination_node_free[m]:
return False return False
return True return True
def add_action_enable_compute_node(self, node): def add_action_enable_compute_node(self, node):
"""Add an action for node enabler into the solution.""" """Add an action for node enabler into the solution."""
params = {'state': element.ServiceState.ENABLED.value} params = {'state': element.ServiceState.ENABLED.value}
self.solution.add_action( self.solution.add_action(
action_type=self.CHANGE_NOVA_SERVICE_STATE, action_type=self.CHANGE_NOVA_SERVICE_STATE,
resource_id=node.uuid, resource_id=node.uuid,
input_parameters=params) input_parameters=params)
def add_action_maintain_compute_node(self, node): def add_action_maintain_compute_node(self, node):
"""Add an action for node maintenance into the solution.""" """Add an action for node maintenance into the solution."""
params = {'state': element.ServiceState.DISABLED.value, params = {'state': element.ServiceState.DISABLED.value,
'disabled_reason': self.REASON_FOR_MAINTAINING} 'disabled_reason': self.REASON_FOR_MAINTAINING}
self.solution.add_action( self.solution.add_action(
action_type=self.CHANGE_NOVA_SERVICE_STATE, action_type=self.CHANGE_NOVA_SERVICE_STATE,
resource_id=node.uuid, resource_id=node.uuid,
input_parameters=params) input_parameters=params)
def enable_compute_node_if_disabled(self, node): def enable_compute_node_if_disabled(self, node):
node_status_str = self.get_node_status_str(node) node_status_str = self.get_node_status_str(node)
if node_status_str != element.ServiceState.ENABLED.value: if node_status_str != element.ServiceState.ENABLED.value:
self.add_action_enable_compute_node(node) self.add_action_enable_compute_node(node)
def instance_migration(self, instance, src_node, des_node=None): def instance_migration(self, instance, src_node, des_node=None):
"""Add an action for instance migration into the solution. """Add an action for instance migration into the solution.
:param instance: instance object :param instance: instance object
:param src_node: node object :param src_node: node object
:param des_node: node object. if None, the instance will be :param des_node: node object. if None, the instance will be
migrated relying on nova-scheduler migrated relying on nova-scheduler
:return: None :return: None
""" """
instance_state_str = self.get_instance_state_str(instance) instance_state_str = self.get_instance_state_str(instance)
if instance_state_str == element.InstanceState.ACTIVE.value: if instance_state_str == element.InstanceState.ACTIVE.value:
migration_type = 'live' migration_type = 'live'
else: else:
migration_type = 'cold' migration_type = 'cold'
params = {'migration_type': migration_type, params = {'migration_type': migration_type,
'source_node': src_node.uuid} 'source_node': src_node.uuid}
if des_node: if des_node:
params['destination_node'] = des_node.uuid params['destination_node'] = des_node.uuid
self.solution.add_action(action_type=self.INSTANCE_MIGRATION, self.solution.add_action(action_type=self.INSTANCE_MIGRATION,
resource_id=instance.uuid, resource_id=instance.uuid,
input_parameters=params) input_parameters=params)
def host_migration(self, source_node, destination_node): def host_migration(self, source_node, destination_node):
"""host migration """host migration
Migrate all instances from source_node to destination_node. Migrate all instances from source_node to destination_node.
Active instances use "live-migrate", Active instances use "live-migrate",
and other instances use "cold-migrate" and other instances use "cold-migrate"
""" """
instances = self.compute_model.get_node_instances(source_node) instances = self.compute_model.get_node_instances(source_node)
for instance in instances: for instance in instances:
self.instance_migration(instance, source_node, destination_node) self.instance_migration(instance, source_node, destination_node)
def safe_maintain(self, maintenance_node, backup_node=None): def safe_maintain(self, maintenance_node, backup_node=None):
"""safe maintain one compute node """safe maintain one compute node
Migrate all instances of the maintenance_node intensively to the Migrate all instances of the maintenance_node intensively to the
backup host. If users didn't give the backup host, it will select backup host. If users didn't give the backup host, it will select
one unused node to backup the maintaining node. one unused node to backup the maintaining node.
It calculate the resource both of the backup node and maintaining It calculate the resource both of the backup node and maintaining
node to evaluate the migrations from maintaining node to backup node. node to evaluate the migrations from maintaining node to backup node.
If all instances of the maintaining node can migrated to If all instances of the maintaining node can migrated to
the backup node, it will set the maintaining node in the backup node, it will set the maintaining node in
'watcher_maintaining' status., and add the migrations to solution. 'watcher_maintaining' status., and add the migrations to solution.
""" """
# If user gives a backup node with required capacity, then migrate # If user gives a backup node with required capacity, then migrate
# all instances from the maintaining node to the backup node. # all instances from the maintaining node to the backup node.
if backup_node: if backup_node:
if self.host_fits(maintenance_node, backup_node): if self.host_fits(maintenance_node, backup_node):
self.enable_compute_node_if_disabled(backup_node) self.enable_compute_node_if_disabled(backup_node)
self.add_action_maintain_compute_node(maintenance_node) self.add_action_maintain_compute_node(maintenance_node)
self.host_migration(maintenance_node, backup_node) self.host_migration(maintenance_node, backup_node)
return True return True
# If uses didn't give the backup host, select one unused node # If uses didn't give the backup host, select one unused node
# with required capacity, then migrate all instances # with required capacity, then migrate all instances
# from maintaining node to it. # from maintaining node to it.
nodes = sorted( nodes = sorted(
self.get_disabled_compute_nodes().values(), self.get_disabled_compute_nodes().values(),
key=lambda x: self.get_node_capacity(x)['cpu']) key=lambda x: self.get_node_capacity(x)['cpu'])
if maintenance_node in nodes: if maintenance_node in nodes:
nodes.remove(maintenance_node) nodes.remove(maintenance_node)
for node in nodes: for node in nodes:
if self.host_fits(maintenance_node, node): if self.host_fits(maintenance_node, node):
self.enable_compute_node_if_disabled(node) self.enable_compute_node_if_disabled(node)
self.add_action_maintain_compute_node(maintenance_node) self.add_action_maintain_compute_node(maintenance_node)
self.host_migration(maintenance_node, node) self.host_migration(maintenance_node, node)
return True return True
return False return False
def try_maintain(self, maintenance_node): def try_maintain(self, maintenance_node):
"""try to maintain one compute node """try to maintain one compute node
It firstly set the maintenance_node in 'watcher_maintaining' status. It firstly set the maintenance_node in 'watcher_maintaining' status.
Then try to migrate all instances of the maintenance node, rely Then try to migrate all instances of the maintenance node, rely
on nova-scheduler. on nova-scheduler.
""" """
self.add_action_maintain_compute_node(maintenance_node) self.add_action_maintain_compute_node(maintenance_node)
instances = self.compute_model.get_node_instances(maintenance_node) instances = self.compute_model.get_node_instances(maintenance_node)
for instance in instances: for instance in instances:
self.instance_migration(instance, maintenance_node) self.instance_migration(instance, maintenance_node)
def pre_execute(self): def pre_execute(self):
LOG.debug(self.compute_model.to_string()) LOG.debug(self.compute_model.to_string())
if not self.compute_model: if not self.compute_model:
raise wexc.ClusterStateNotDefined() raise wexc.ClusterStateNotDefined()
if self.compute_model.stale: if self.compute_model.stale:
raise wexc.ClusterStateStale() raise wexc.ClusterStateStale()
def do_execute(self): def do_execute(self):
LOG.info(_('Executing Host Maintenance Migration Strategy')) LOG.info(_('Executing Host Maintenance Migration Strategy'))
maintenance_node = self.input_parameters.get('maintenance_node') maintenance_node = self.input_parameters.get('maintenance_node')
backup_node = self.input_parameters.get('backup_node') backup_node = self.input_parameters.get('backup_node')
# if no VMs in the maintenance_node, just maintain the compute node # if no VMs in the maintenance_node, just maintain the compute node
src_node = self.compute_model.get_node_by_uuid(maintenance_node) src_node = self.compute_model.get_node_by_uuid(maintenance_node)
if len(self.compute_model.get_node_instances(src_node)) == 0: if len(self.compute_model.get_node_instances(src_node)) == 0:
if (src_node.disabled_reason != if (src_node.disabled_reason !=
self.REASON_FOR_MAINTAINING): self.REASON_FOR_MAINTAINING):
self.add_action_maintain_compute_node(src_node) self.add_action_maintain_compute_node(src_node)
return return
if backup_node: if backup_node:
des_node = self.compute_model.get_node_by_uuid(backup_node) des_node = self.compute_model.get_node_by_uuid(backup_node)
else: else:
des_node = None des_node = None
if not self.safe_maintain(src_node, des_node): if not self.safe_maintain(src_node, des_node):
self.try_maintain(src_node) self.try_maintain(src_node)
def post_execute(self): def post_execute(self):
"""Post-execution phase """Post-execution phase
This can be used to compute the global efficacy This can be used to compute the global efficacy
""" """
LOG.debug(self.solution.actions) LOG.debug(self.solution.actions)
LOG.debug(self.compute_model.to_string()) LOG.debug(self.compute_model.to_string())

View File

@@ -1,206 +1,206 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# Copyright (c) 2017 chinac.com # Copyright (c) 2017 chinac.com
# #
# Authors: suzhengwei<suzhengwei@chinac.com> # Authors: suzhengwei<suzhengwei@chinac.com>
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, # distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. # implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
import mock import mock
from watcher.common import exception from watcher.common import exception
from watcher.decision_engine.model import model_root from watcher.decision_engine.model import model_root
from watcher.decision_engine.strategy import strategies from watcher.decision_engine.strategy import strategies
from watcher.tests import base from watcher.tests import base
from watcher.tests.decision_engine.model import faker_cluster_state from watcher.tests.decision_engine.model import faker_cluster_state
class TestHostMaintenance(base.TestCase): class TestHostMaintenance(base.TestCase):
def setUp(self): def setUp(self):
super(TestHostMaintenance, self).setUp() super(TestHostMaintenance, self).setUp()
# fake cluster # fake cluster
self.fake_cluster = faker_cluster_state.FakerModelCollector() self.fake_cluster = faker_cluster_state.FakerModelCollector()
p_model = mock.patch.object( p_model = mock.patch.object(
strategies.HostMaintenance, "compute_model", strategies.HostMaintenance, "compute_model",
new_callable=mock.PropertyMock) new_callable=mock.PropertyMock)
self.m_model = p_model.start() self.m_model = p_model.start()
self.addCleanup(p_model.stop) self.addCleanup(p_model.stop)
p_audit_scope = mock.patch.object( p_audit_scope = mock.patch.object(
strategies.HostMaintenance, "audit_scope", strategies.HostMaintenance, "audit_scope",
new_callable=mock.PropertyMock new_callable=mock.PropertyMock
) )
self.m_audit_scope = p_audit_scope.start() self.m_audit_scope = p_audit_scope.start()
self.addCleanup(p_audit_scope.stop) self.addCleanup(p_audit_scope.stop)
self.m_audit_scope.return_value = mock.Mock() self.m_audit_scope.return_value = mock.Mock()
self.m_model.return_value = model_root.ModelRoot() self.m_model.return_value = model_root.ModelRoot()
self.strategy = strategies.HostMaintenance(config=mock.Mock()) self.strategy = strategies.HostMaintenance(config=mock.Mock())
def test_exception_stale_cdm(self): def test_exception_stale_cdm(self):
self.fake_cluster.set_cluster_data_model_as_stale() self.fake_cluster.set_cluster_data_model_as_stale()
self.m_model.return_value = self.fake_cluster.cluster_data_model self.m_model.return_value = self.fake_cluster.cluster_data_model
self.assertRaises( self.assertRaises(
exception.ClusterStateNotDefined, exception.ClusterStateNotDefined,
self.strategy.execute) self.strategy.execute)
def test_get_node_capacity(self): def test_get_node_capacity(self):
model = self.fake_cluster.generate_scenario_1() model = self.fake_cluster.generate_scenario_1()
self.m_model.return_value = model self.m_model.return_value = model
node_0 = model.get_node_by_uuid("Node_0") node_0 = model.get_node_by_uuid("Node_0")
node_capacity = dict(cpu=40, ram=132, disk=250) node_capacity = dict(cpu=40, ram=132, disk=250)
self.assertEqual(node_capacity, self.assertEqual(node_capacity,
self.strategy.get_node_capacity(node_0)) self.strategy.get_node_capacity(node_0))
def test_get_node_used(self): def test_get_node_used(self):
model = self.fake_cluster.generate_scenario_1() model = self.fake_cluster.generate_scenario_1()
self.m_model.return_value = model self.m_model.return_value = model
node_0 = model.get_node_by_uuid("Node_0") node_0 = model.get_node_by_uuid("Node_0")
node_used = dict(cpu=20, ram=4, disk=40) node_used = dict(cpu=20, ram=4, disk=40)
self.assertEqual(node_used, self.assertEqual(node_used,
self.strategy.get_node_used(node_0)) self.strategy.get_node_used(node_0))
def test_get_node_free(self): def test_get_node_free(self):
model = self.fake_cluster.generate_scenario_1() model = self.fake_cluster.generate_scenario_1()
self.m_model.return_value = model self.m_model.return_value = model
node_0 = model.get_node_by_uuid("Node_0") node_0 = model.get_node_by_uuid("Node_0")
node_free = dict(cpu=20, ram=128, disk=210) node_free = dict(cpu=20, ram=128, disk=210)
self.assertEqual(node_free, self.assertEqual(node_free,
self.strategy.get_node_free(node_0)) self.strategy.get_node_free(node_0))
def test_host_fits(self): def test_host_fits(self):
model = self.fake_cluster.generate_scenario_1() model = self.fake_cluster.generate_scenario_1()
self.m_model.return_value = model self.m_model.return_value = model
node_0 = model.get_node_by_uuid("Node_0") node_0 = model.get_node_by_uuid("Node_0")
node_1 = model.get_node_by_uuid("Node_1") node_1 = model.get_node_by_uuid("Node_1")
self.assertTrue(self.strategy.host_fits(node_0, node_1)) self.assertTrue(self.strategy.host_fits(node_0, node_1))
def test_add_action_enable_compute_node(self): def test_add_action_enable_compute_node(self):
model = self.fake_cluster.generate_scenario_1() model = self.fake_cluster.generate_scenario_1()
self.m_model.return_value = model self.m_model.return_value = model
node_0 = model.get_node_by_uuid('Node_0') node_0 = model.get_node_by_uuid('Node_0')
self.strategy.add_action_enable_compute_node(node_0) self.strategy.add_action_enable_compute_node(node_0)
expected = [{'action_type': 'change_nova_service_state', expected = [{'action_type': 'change_nova_service_state',
'input_parameters': { 'input_parameters': {
'state': 'enabled', 'state': 'enabled',
'resource_id': 'Node_0'}}] 'resource_id': 'Node_0'}}]
self.assertEqual(expected, self.strategy.solution.actions) self.assertEqual(expected, self.strategy.solution.actions)
def test_add_action_maintain_compute_node(self): def test_add_action_maintain_compute_node(self):
model = self.fake_cluster.generate_scenario_1() model = self.fake_cluster.generate_scenario_1()
self.m_model.return_value = model self.m_model.return_value = model
node_0 = model.get_node_by_uuid('Node_0') node_0 = model.get_node_by_uuid('Node_0')
self.strategy.add_action_maintain_compute_node(node_0) self.strategy.add_action_maintain_compute_node(node_0)
expected = [{'action_type': 'change_nova_service_state', expected = [{'action_type': 'change_nova_service_state',
'input_parameters': { 'input_parameters': {
'state': 'disabled', 'state': 'disabled',
'disabled_reason': 'watcher_maintaining', 'disabled_reason': 'watcher_maintaining',
'resource_id': 'Node_0'}}] 'resource_id': 'Node_0'}}]
self.assertEqual(expected, self.strategy.solution.actions) self.assertEqual(expected, self.strategy.solution.actions)
def test_instance_migration(self): def test_instance_migration(self):
model = self.fake_cluster.generate_scenario_1() model = self.fake_cluster.generate_scenario_1()
self.m_model.return_value = model self.m_model.return_value = model
node_0 = model.get_node_by_uuid('Node_0') node_0 = model.get_node_by_uuid('Node_0')
node_1 = model.get_node_by_uuid('Node_1') node_1 = model.get_node_by_uuid('Node_1')
instance_0 = model.get_instance_by_uuid("INSTANCE_0") instance_0 = model.get_instance_by_uuid("INSTANCE_0")
self.strategy.instance_migration(instance_0, node_0, node_1) self.strategy.instance_migration(instance_0, node_0, node_1)
self.assertEqual(1, len(self.strategy.solution.actions)) self.assertEqual(1, len(self.strategy.solution.actions))
expected = [{'action_type': 'migrate', expected = [{'action_type': 'migrate',
'input_parameters': {'destination_node': node_1.uuid, 'input_parameters': {'destination_node': node_1.uuid,
'source_node': node_0.uuid, 'source_node': node_0.uuid,
'migration_type': 'live', 'migration_type': 'live',
'resource_id': instance_0.uuid}}] 'resource_id': instance_0.uuid}}]
self.assertEqual(expected, self.strategy.solution.actions) self.assertEqual(expected, self.strategy.solution.actions)
def test_instance_migration_without_dest_node(self): def test_instance_migration_without_dest_node(self):
model = self.fake_cluster.generate_scenario_1() model = self.fake_cluster.generate_scenario_1()
self.m_model.return_value = model self.m_model.return_value = model
node_0 = model.get_node_by_uuid('Node_0') node_0 = model.get_node_by_uuid('Node_0')
instance_0 = model.get_instance_by_uuid("INSTANCE_0") instance_0 = model.get_instance_by_uuid("INSTANCE_0")
self.strategy.instance_migration(instance_0, node_0) self.strategy.instance_migration(instance_0, node_0)
self.assertEqual(1, len(self.strategy.solution.actions)) self.assertEqual(1, len(self.strategy.solution.actions))
expected = [{'action_type': 'migrate', expected = [{'action_type': 'migrate',
'input_parameters': {'source_node': node_0.uuid, 'input_parameters': {'source_node': node_0.uuid,
'migration_type': 'live', 'migration_type': 'live',
'resource_id': instance_0.uuid}}] 'resource_id': instance_0.uuid}}]
self.assertEqual(expected, self.strategy.solution.actions) self.assertEqual(expected, self.strategy.solution.actions)
def test_host_migration(self): def test_host_migration(self):
model = self.fake_cluster.generate_scenario_1() model = self.fake_cluster.generate_scenario_1()
self.m_model.return_value = model self.m_model.return_value = model
node_0 = model.get_node_by_uuid('Node_0') node_0 = model.get_node_by_uuid('Node_0')
node_1 = model.get_node_by_uuid('Node_1') node_1 = model.get_node_by_uuid('Node_1')
instance_0 = model.get_instance_by_uuid("INSTANCE_0") instance_0 = model.get_instance_by_uuid("INSTANCE_0")
instance_1 = model.get_instance_by_uuid("INSTANCE_1") instance_1 = model.get_instance_by_uuid("INSTANCE_1")
self.strategy.host_migration(node_0, node_1) self.strategy.host_migration(node_0, node_1)
self.assertEqual(2, len(self.strategy.solution.actions)) self.assertEqual(2, len(self.strategy.solution.actions))
expected = [{'action_type': 'migrate', expected = [{'action_type': 'migrate',
'input_parameters': {'destination_node': node_1.uuid, 'input_parameters': {'destination_node': node_1.uuid,
'source_node': node_0.uuid, 'source_node': node_0.uuid,
'migration_type': 'live', 'migration_type': 'live',
'resource_id': instance_0.uuid}}, 'resource_id': instance_0.uuid}},
{'action_type': 'migrate', {'action_type': 'migrate',
'input_parameters': {'destination_node': node_1.uuid, 'input_parameters': {'destination_node': node_1.uuid,
'source_node': node_0.uuid, 'source_node': node_0.uuid,
'migration_type': 'live', 'migration_type': 'live',
'resource_id': instance_1.uuid}}] 'resource_id': instance_1.uuid}}]
self.assertIn(expected[0], self.strategy.solution.actions) self.assertIn(expected[0], self.strategy.solution.actions)
self.assertIn(expected[1], self.strategy.solution.actions) self.assertIn(expected[1], self.strategy.solution.actions)
def test_safe_maintain(self): def test_safe_maintain(self):
model = self.fake_cluster.generate_scenario_1() model = self.fake_cluster.generate_scenario_1()
self.m_model.return_value = model self.m_model.return_value = model
node_0 = model.get_node_by_uuid('Node_0') node_0 = model.get_node_by_uuid('Node_0')
node_1 = model.get_node_by_uuid('Node_1') node_1 = model.get_node_by_uuid('Node_1')
self.assertFalse(self.strategy.safe_maintain(node_0)) self.assertFalse(self.strategy.safe_maintain(node_0))
self.assertFalse(self.strategy.safe_maintain(node_1)) self.assertFalse(self.strategy.safe_maintain(node_1))
def test_try_maintain(self): def test_try_maintain(self):
model = self.fake_cluster.generate_scenario_1() model = self.fake_cluster.generate_scenario_1()
self.m_model.return_value = model self.m_model.return_value = model
node_1 = model.get_node_by_uuid('Node_1') node_1 = model.get_node_by_uuid('Node_1')
self.strategy.try_maintain(node_1) self.strategy.try_maintain(node_1)
self.assertEqual(2, len(self.strategy.solution.actions)) self.assertEqual(2, len(self.strategy.solution.actions))
def test_strategy(self): def test_strategy(self):
model = self.fake_cluster. \ model = self.fake_cluster. \
generate_scenario_9_with_3_active_plus_1_disabled_nodes() generate_scenario_9_with_3_active_plus_1_disabled_nodes()
self.m_model.return_value = model self.m_model.return_value = model
node_2 = model.get_node_by_uuid('Node_2') node_2 = model.get_node_by_uuid('Node_2')
node_3 = model.get_node_by_uuid('Node_3') node_3 = model.get_node_by_uuid('Node_3')
instance_4 = model.get_instance_by_uuid("INSTANCE_4") instance_4 = model.get_instance_by_uuid("INSTANCE_4")
if not self.strategy.safe_maintain(node_2, node_3): if not self.strategy.safe_maintain(node_2, node_3):
self.strategy.try_maintain(node_2) self.strategy.try_maintain(node_2)
expected = [{'action_type': 'change_nova_service_state', expected = [{'action_type': 'change_nova_service_state',
'input_parameters': { 'input_parameters': {
'resource_id': 'Node_3', 'resource_id': 'Node_3',
'state': 'enabled'}}, 'state': 'enabled'}},
{'action_type': 'change_nova_service_state', {'action_type': 'change_nova_service_state',
'input_parameters': { 'input_parameters': {
'resource_id': 'Node_2', 'resource_id': 'Node_2',
'state': 'disabled', 'state': 'disabled',
'disabled_reason': 'watcher_maintaining'}}, 'disabled_reason': 'watcher_maintaining'}},
{'action_type': 'migrate', {'action_type': 'migrate',
'input_parameters': { 'input_parameters': {
'destination_node': node_3.uuid, 'destination_node': node_3.uuid,
'source_node': node_2.uuid, 'source_node': node_2.uuid,
'migration_type': 'live', 'migration_type': 'live',
'resource_id': instance_4.uuid}}] 'resource_id': instance_4.uuid}}]
self.assertEqual(expected, self.strategy.solution.actions) self.assertEqual(expected, self.strategy.solution.actions)