extend-node-status
add 'disabled_reason' filed into 'ComputeNode' resource, to distinguish which nodes are disabled by Watcher and which are not by Watcher. Implements:blueprint extend-node-status Change-Id: I7175f14870834a4582e45309529d7e8d9fbb2e6f
This commit is contained in:
@@ -36,15 +36,20 @@ class ChangeNovaServiceState(base.BaseAction):
|
||||
schema = Schema({
|
||||
'resource_id': str,
|
||||
'state': str,
|
||||
'disabled_reason': str,
|
||||
})
|
||||
|
||||
The `resource_id` references a nova-compute service name (list of available
|
||||
nova-compute services is returned by this command: ``nova service-list
|
||||
--binary nova-compute``).
|
||||
The `state` value should either be `ONLINE` or `OFFLINE`.
|
||||
The `disabled_reason` references the reason why Watcher disables this
|
||||
nova-compute service. The value should be with `watcher_` prefix, such as
|
||||
`watcher_disabled`, `watcher_maintaining`.
|
||||
"""
|
||||
|
||||
STATE = 'state'
|
||||
REASON = 'disabled_reason'
|
||||
|
||||
@property
|
||||
def schema(self):
|
||||
@@ -61,6 +66,10 @@ class ChangeNovaServiceState(base.BaseAction):
|
||||
element.ServiceState.OFFLINE.value,
|
||||
element.ServiceState.ENABLED.value,
|
||||
element.ServiceState.DISABLED.value]
|
||||
},
|
||||
'disabled_reason': {
|
||||
'type': 'string',
|
||||
"minlength": 1
|
||||
}
|
||||
},
|
||||
'required': ['resource_id', 'state'],
|
||||
@@ -75,6 +84,10 @@ class ChangeNovaServiceState(base.BaseAction):
|
||||
def state(self):
|
||||
return self.input_parameters.get(self.STATE)
|
||||
|
||||
@property
|
||||
def reason(self):
|
||||
return self.input_parameters.get(self.REASON)
|
||||
|
||||
def execute(self):
|
||||
target_state = None
|
||||
if self.state == element.ServiceState.DISABLED.value:
|
||||
@@ -100,7 +113,7 @@ class ChangeNovaServiceState(base.BaseAction):
|
||||
if state is True:
|
||||
return nova.enable_service_nova_compute(self.host)
|
||||
else:
|
||||
return nova.disable_service_nova_compute(self.host)
|
||||
return nova.disable_service_nova_compute(self.host, self.reason)
|
||||
|
||||
def pre_condition(self):
|
||||
pass
|
||||
|
||||
@@ -547,9 +547,10 @@ class NovaHelper(object):
|
||||
else:
|
||||
return False
|
||||
|
||||
def disable_service_nova_compute(self, hostname):
|
||||
if self.nova.services.disable(host=hostname,
|
||||
binary='nova-compute'). \
|
||||
def disable_service_nova_compute(self, hostname, reason=None):
|
||||
if self.nova.services.disable_log_reason(host=hostname,
|
||||
binary='nova-compute',
|
||||
reason=reason). \
|
||||
status == 'disabled':
|
||||
return True
|
||||
else:
|
||||
|
||||
@@ -139,7 +139,8 @@ class ModelBuilder(object):
|
||||
"disk_capacity": node.local_gb,
|
||||
"vcpus": node.vcpus,
|
||||
"state": node.state,
|
||||
"status": node.status}
|
||||
"status": node.status,
|
||||
"disabled_reason": compute_service.disabled_reason}
|
||||
|
||||
compute_node = element.ComputeNode(**node_attributes)
|
||||
# compute_node = self._build_node("physical", "compute", "hypervisor",
|
||||
|
||||
@@ -36,8 +36,8 @@ class ComputeNode(compute_resource.ComputeResource):
|
||||
"id": wfields.NonNegativeIntegerField(),
|
||||
"hostname": wfields.StringField(),
|
||||
"status": wfields.StringField(default=ServiceState.ENABLED.value),
|
||||
"disabled_reason": wfields.StringField(nullable=True),
|
||||
"state": wfields.StringField(default=ServiceState.ONLINE.value),
|
||||
|
||||
"memory": wfields.NonNegativeIntegerField(),
|
||||
"disk": wfields.IntegerField(),
|
||||
"disk_capacity": wfields.NonNegativeIntegerField(),
|
||||
|
||||
@@ -122,11 +122,15 @@ class NovaNotification(base.NotificationEndpoint):
|
||||
node_status = (
|
||||
element.ServiceState.DISABLED.value
|
||||
if node_data['disabled'] else element.ServiceState.ENABLED.value)
|
||||
disabled_reason = (
|
||||
node_data['disabled_reason']
|
||||
if node_data['disabled'] else None)
|
||||
|
||||
node.update({
|
||||
'hostname': node_data['host'],
|
||||
'state': node_state,
|
||||
'status': node_status,
|
||||
'disabled_reason': disabled_reason,
|
||||
})
|
||||
|
||||
def create_compute_node(self, node_hostname):
|
||||
|
||||
@@ -331,6 +331,8 @@ class UnclassifiedStrategy(BaseStrategy):
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ServerConsolidationBaseStrategy(BaseStrategy):
|
||||
|
||||
REASON_FOR_DISABLE = 'watcher_disabled'
|
||||
|
||||
@classmethod
|
||||
def get_goal_name(cls):
|
||||
return "server_consolidation"
|
||||
|
||||
@@ -407,8 +407,9 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
|
||||
|
||||
return self.calculate_weight(instance, total_cores_used, 0, 0)
|
||||
|
||||
def add_change_service_state(self, resource_id, state):
|
||||
parameters = {'state': state}
|
||||
def add_action_disable_node(self, resource_id):
|
||||
parameters = {'state': element.ServiceState.DISABLED.value,
|
||||
'disabled_reason': self.REASON_FOR_DISABLE}
|
||||
self.solution.add_action(action_type=self.CHANGE_NOVA_SERVICE_STATE,
|
||||
resource_id=resource_id,
|
||||
input_parameters=parameters)
|
||||
@@ -464,9 +465,7 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
|
||||
mig_destination_node.uuid)
|
||||
|
||||
if len(self.compute_model.get_node_instances(mig_source_node)) == 0:
|
||||
self.add_change_service_state(mig_source_node.
|
||||
uuid,
|
||||
element.ServiceState.DISABLED.value)
|
||||
self.add_action_disable_node(mig_source_node.uuid)
|
||||
self.number_of_released_nodes += 1
|
||||
|
||||
def calculate_num_migrations(self, sorted_instances, node_to_release,
|
||||
|
||||
@@ -220,7 +220,8 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
|
||||
:param node: node object
|
||||
:return: None
|
||||
"""
|
||||
params = {'state': element.ServiceState.DISABLED.value}
|
||||
params = {'state': element.ServiceState.DISABLED.value,
|
||||
'disabled_reason': self.REASON_FOR_DISABLE}
|
||||
self.solution.add_action(
|
||||
action_type=self.CHANGE_NOVA_SERVICE_STATE,
|
||||
resource_id=node.uuid,
|
||||
|
||||
@@ -110,18 +110,22 @@ class TestChangeNovaServiceState(base.TestCase):
|
||||
def test_execute_change_service_state_with_disable_target(self):
|
||||
self.action.input_parameters["state"] = (
|
||||
element.ServiceState.DISABLED.value)
|
||||
self.action.input_parameters["disabled_reason"] = (
|
||||
"watcher_disabled")
|
||||
self.action.execute()
|
||||
|
||||
self.m_helper_cls.assert_called_once_with(osc=self.m_osc)
|
||||
self.m_helper.disable_service_nova_compute.assert_called_once_with(
|
||||
"compute-1")
|
||||
"compute-1", "watcher_disabled")
|
||||
|
||||
def test_revert_change_service_state_with_enable_target(self):
|
||||
self.action.input_parameters["disabled_reason"] = (
|
||||
"watcher_disabled")
|
||||
self.action.revert()
|
||||
|
||||
self.m_helper_cls.assert_called_once_with(osc=self.m_osc)
|
||||
self.m_helper.disable_service_nova_compute.assert_called_once_with(
|
||||
"compute-1")
|
||||
"compute-1", "watcher_disabled")
|
||||
|
||||
def test_revert_change_service_state_with_disable_target(self):
|
||||
self.action.input_parameters["state"] = (
|
||||
|
||||
@@ -328,13 +328,13 @@ class TestNovaHelper(base.TestCase):
|
||||
mock_neutron, mock_nova):
|
||||
nova_util = nova_helper.NovaHelper()
|
||||
nova_services = nova_util.nova.services
|
||||
nova_services.disable.return_value = mock.MagicMock(
|
||||
nova_services.disable_log_reason.return_value = mock.MagicMock(
|
||||
status='enabled')
|
||||
|
||||
result = nova_util.disable_service_nova_compute('nanjing')
|
||||
self.assertFalse(result)
|
||||
|
||||
nova_services.disable.return_value = mock.MagicMock(
|
||||
nova_services.disable_log_reason.return_value = mock.MagicMock(
|
||||
status='disabled')
|
||||
|
||||
result = nova_util.disable_service_nova_compute('nanjing')
|
||||
|
||||
@@ -37,7 +37,13 @@ class TestNovaClusterDataModelCollector(base.TestCase):
|
||||
m_nova_helper = mock.Mock(name="nova_helper")
|
||||
m_nova_helper_cls.return_value = m_nova_helper
|
||||
m_nova_helper.get_service.return_value = mock.Mock(
|
||||
host="test_hostname")
|
||||
id=1355,
|
||||
host='test_hostname',
|
||||
binary='nova-compute',
|
||||
status='enabled',
|
||||
state='up',
|
||||
disabled_reason='',
|
||||
)
|
||||
|
||||
fake_compute_node = mock.Mock(
|
||||
id=1337,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<ComputeNode hostname="hostname_2" uuid="Node_2" id="2" state="up" human_id="" status="enabled" vcpus="16" disk="250" disk_capacity="250" memory="64">
|
||||
<Instance human_id="" state="active" uuid="INSTANCE_4" vcpus="2" disk="20" disk_capacity="20" memory="2" metadata='{"optimize": true,"top": "floor", "nested": {"x": "y"}}'/>
|
||||
</ComputeNode>
|
||||
<ComputeNode hostname="hostname_3" uuid="Node_3" id="3" state="up" human_id="" status="disabled" vcpus="16" disk="250" disk_capacity="250" memory="64">
|
||||
<ComputeNode hostname="hostname_3" uuid="Node_3" id="3" state="up" human_id="" status="disabled" disabled_reason='watcher_disabled' vcpus="16" disk="250" disk_capacity="250" memory="64">
|
||||
<Instance human_id="" state="active" uuid="INSTANCE_5" vcpus="2" disk="20" disk_capacity="20" memory="2" metadata='{"optimize": true,"top": "floor", "nested": {"x": "y"}}'/>
|
||||
</ComputeNode>
|
||||
</ModelRoot>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"last_seen_up": "2012-10-29T13:42:05Z",
|
||||
"binary": "nova-compute",
|
||||
"topic": "compute",
|
||||
"disabled_reason": null,
|
||||
"disabled_reason": "watcher_disabled",
|
||||
"report_count": 1,
|
||||
"forced_down": true,
|
||||
"version": 15
|
||||
|
||||
@@ -197,8 +197,10 @@ class TestVMWorkloadConsolidation(base.TestCase):
|
||||
n = model.get_node_by_uuid('Node_0')
|
||||
self.strategy.add_action_disable_node(n)
|
||||
expected = [{'action_type': 'change_nova_service_state',
|
||||
'input_parameters': {'state': 'disabled',
|
||||
'resource_id': 'Node_0'}}]
|
||||
'input_parameters': {
|
||||
'state': 'disabled',
|
||||
'disabled_reason': 'watcher_disabled',
|
||||
'resource_id': 'Node_0'}}]
|
||||
self.assertEqual(expected, self.strategy.solution.actions)
|
||||
|
||||
def test_disable_unused_nodes(self):
|
||||
@@ -217,8 +219,10 @@ class TestVMWorkloadConsolidation(base.TestCase):
|
||||
|
||||
self.strategy.disable_unused_nodes()
|
||||
expected = {'action_type': 'change_nova_service_state',
|
||||
'input_parameters': {'state': 'disabled',
|
||||
'resource_id': 'Node_0'}}
|
||||
'input_parameters': {
|
||||
'state': 'disabled',
|
||||
'disabled_reason': 'watcher_disabled',
|
||||
'resource_id': 'Node_0'}}
|
||||
self.assertEqual(2, len(self.strategy.solution.actions))
|
||||
self.assertEqual(expected, self.strategy.solution.actions[1])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user