Merge "Prevent unnecessary nova-compute restarts on ceph_changed"
This commit is contained in:
commit
0dcb3d0dfb
|
@ -32,6 +32,7 @@ from charmhelpers.core.hookenv import (
|
|||
is_relation_made,
|
||||
local_unit,
|
||||
log,
|
||||
DEBUG,
|
||||
relation_ids,
|
||||
remote_service_name,
|
||||
related_units,
|
||||
|
@ -72,11 +73,13 @@ from charmhelpers.contrib.openstack.utils import (
|
|||
from charmhelpers.contrib.storage.linux.ceph import (
|
||||
ensure_ceph_keyring,
|
||||
CephBrokerRq,
|
||||
CephBrokerRsp,
|
||||
delete_keyring,
|
||||
send_request_if_needed,
|
||||
is_request_complete,
|
||||
is_broker_action_done,
|
||||
mark_broker_action_done,
|
||||
get_broker_rsp_key,
|
||||
get_request_states,
|
||||
get_previous_request,
|
||||
)
|
||||
from charmhelpers.payload.execd import execd_preinstall
|
||||
from nova_compute_utils import (
|
||||
|
@ -408,17 +411,102 @@ def ceph_changed(rid=None, unit=None):
|
|||
create_libvirt_secret(secret_file=CEPH_SECRET,
|
||||
secret_uuid=CEPH_SECRET_UUID, key=key)
|
||||
|
||||
if is_request_complete(get_ceph_request()):
|
||||
log('Request complete')
|
||||
_handle_ceph_request()
|
||||
|
||||
|
||||
# TODO: Refactor this method moving part of this logic to charmhelpers,
|
||||
# refacting the existing ones.
|
||||
def _handle_ceph_request():
|
||||
"""Handles the logic for sending and acknowledging Ceph broker requests."""
|
||||
|
||||
# First, we create a request. We will test if this request is equivalent
|
||||
# to a previous one. If it is not, we will send it.
|
||||
request = get_ceph_request()
|
||||
|
||||
log("New ceph request {} created.".format(request.request_id), level=DEBUG)
|
||||
|
||||
# Here we will know if the new request is equivalent, and if it is, whether
|
||||
# it has completed, or just sent.
|
||||
states = get_request_states(request, relation='ceph')
|
||||
|
||||
log("Request states: {}.".format(states), level=DEBUG)
|
||||
|
||||
complete = True
|
||||
sent = True
|
||||
|
||||
# According to existing ceph broker messaging logic, we are expecting only
|
||||
# 1 rid.
|
||||
for rid in states.keys():
|
||||
if not states[rid]['complete']:
|
||||
complete = False
|
||||
if not states[rid]['sent']:
|
||||
sent = False
|
||||
if not sent and not complete:
|
||||
break
|
||||
|
||||
# If either complete or sent is True, then get_request_states has validated
|
||||
# that the current request is equivalent to a previously sent request.
|
||||
if complete:
|
||||
log('Previous request complete.')
|
||||
|
||||
# If the request is complete, we need to restart nova once and mark it
|
||||
# restarted. The broker response comes from a specific unit, and can
|
||||
# only be read when this hook is invoked by the remote unit (the
|
||||
# broker), unless specifically queried for the given unit. Therefore,
|
||||
# we iterate across all units to find which has the broker response,
|
||||
# and we process the response regardless of this execution context.
|
||||
broker_rid, broker_unit = _get_broker_rid_unit_for_previous_request()
|
||||
|
||||
# If we cannot determine which unit has the response, then it means
|
||||
# there is no response yet.
|
||||
if (broker_rid, broker_unit) == (None, None):
|
||||
log("Aborting because there is no broker response "
|
||||
"for any unit at the moment.", level=DEBUG)
|
||||
return
|
||||
|
||||
# Ensure that nova-compute is restarted since only now can we
|
||||
# guarantee that ceph resources are ready, but only if not paused.
|
||||
if (not is_unit_paused_set() and
|
||||
not is_broker_action_done('nova_compute_restart', rid,
|
||||
unit)):
|
||||
not is_broker_action_done('nova_compute_restart', broker_rid,
|
||||
broker_unit)):
|
||||
log('Restarting Nova Compute as per request '
|
||||
'{}.'.format(request.request_id), level=DEBUG)
|
||||
service_restart('nova-compute')
|
||||
mark_broker_action_done('nova_compute_restart', rid, unit)
|
||||
mark_broker_action_done('nova_compute_restart',
|
||||
broker_rid, broker_unit)
|
||||
else:
|
||||
send_request_if_needed(get_ceph_request())
|
||||
if sent:
|
||||
log("Request {} already sent, not sending "
|
||||
"another.".format(request.request_id), level=DEBUG)
|
||||
else:
|
||||
log("Request {} not sent, sending it "
|
||||
"now.".format(request.request_id), level=DEBUG)
|
||||
for rid in relation_ids('ceph'):
|
||||
log('Sending request {}'.format(request.request_id),
|
||||
level=DEBUG)
|
||||
relation_set(relation_id=rid, broker_req=request.request)
|
||||
|
||||
|
||||
# TODO: Move this method to charmhelpers while refactoring the existing ones
|
||||
def _get_broker_rid_unit_for_previous_request():
|
||||
"""Gets the broker rid and unit combination that has a response for the
|
||||
previous sent request."""
|
||||
broker_key = get_broker_rsp_key()
|
||||
|
||||
log("Broker key is {}.".format(broker_key), level=DEBUG)
|
||||
|
||||
for rid in relation_ids('ceph'):
|
||||
previous_request = get_previous_request(rid)
|
||||
for unit in related_units(rid):
|
||||
rdata = relation_get(rid=rid, unit=unit)
|
||||
if rdata.get(broker_key):
|
||||
rsp = CephBrokerRsp(rdata.get(broker_key))
|
||||
if rsp.request_id == previous_request.request_id:
|
||||
log("Found broker rid/unit: {}/{}".format(rid, unit),
|
||||
level=DEBUG)
|
||||
return rid, unit
|
||||
log("There is no broker response for any unit at the moment.", level=DEBUG)
|
||||
return None, None
|
||||
|
||||
|
||||
@hooks.hook('ceph-relation-broken')
|
||||
|
|
|
@ -86,8 +86,6 @@ TO_PATCH = [
|
|||
'ensure_ceph_keyring',
|
||||
'execd_preinstall',
|
||||
'assert_libvirt_rbd_imagebackend_allowed',
|
||||
'is_request_complete',
|
||||
'send_request_if_needed',
|
||||
'remove_libvirt_network',
|
||||
# socket
|
||||
'gethostname',
|
||||
|
@ -457,74 +455,225 @@ class NovaComputeRelationsTests(CharmTestCase):
|
|||
'Could not create ceph keyring: peer not ready?'
|
||||
)
|
||||
|
||||
@patch.object(hooks, 'mark_broker_action_done')
|
||||
@patch.object(hooks, 'is_broker_action_done')
|
||||
@patch.object(hooks, '_handle_ceph_request')
|
||||
@patch.object(hooks, 'create_libvirt_secret')
|
||||
@patch('nova_compute_context.service_name')
|
||||
@patch.object(hooks, 'CONFIGS')
|
||||
def test_ceph_changed_with_key_and_relation_data(self, configs,
|
||||
service_name,
|
||||
is_broker_action_done,
|
||||
mark_broker_action_done):
|
||||
self.test_config.set('libvirt-image-backend', 'rbd')
|
||||
self.is_request_complete.return_value = True
|
||||
self.assert_libvirt_rbd_imagebackend_allowed.return_value = True
|
||||
create_libvirt_secret,
|
||||
_handle_ceph_request):
|
||||
configs.complete_contexts = MagicMock()
|
||||
configs.complete_contexts.return_value = ['ceph']
|
||||
self.ensure_ceph_keyring.return_value = True
|
||||
configs.write = MagicMock()
|
||||
service_name.return_value = 'nova-compute'
|
||||
self.ensure_ceph_keyring.return_value = True
|
||||
is_broker_action_done.return_value = False
|
||||
key = {'data': 'key'}
|
||||
self.relation_get.return_value = key
|
||||
|
||||
hooks.ceph_changed()
|
||||
self.assertTrue(mark_broker_action_done.called)
|
||||
ex = [
|
||||
call('/var/lib/charm/nova-compute/ceph.conf'),
|
||||
call('/etc/ceph/secret.xml'),
|
||||
call('/etc/nova/nova.conf'),
|
||||
]
|
||||
self.assertEqual(ex, configs.write.call_args_list)
|
||||
self.service_restart.assert_called_with('nova-compute')
|
||||
create_libvirt_secret.assert_called_once_with(
|
||||
secret_file='/etc/ceph/secret.xml', key=key,
|
||||
secret_uuid=hooks.CEPH_SECRET_UUID)
|
||||
|
||||
@patch.object(hooks, 'get_ceph_request')
|
||||
@patch.object(hooks, 'get_request_states')
|
||||
def test__handle_ceph_request_send_request(
|
||||
self, get_request_states, get_ceph_request):
|
||||
request = hooks.CephBrokerRq()
|
||||
get_ceph_request.return_value = request
|
||||
get_request_states.return_value = {
|
||||
'ceph:43': {'complete': False, 'sent': False}
|
||||
}
|
||||
self.relation_ids.return_value = ['ceph:43']
|
||||
hooks._handle_ceph_request()
|
||||
|
||||
get_ceph_request.assert_called_once_with()
|
||||
get_request_states.assert_called_once_with(request, relation='ceph')
|
||||
self.relation_set.assert_called_once_with(
|
||||
relation_id='ceph:43', broker_req=request.request)
|
||||
|
||||
@patch.object(hooks, 'get_ceph_request')
|
||||
@patch.object(hooks, 'get_request_states')
|
||||
def test__handle_ceph_request_already_sent(
|
||||
self, get_request_states, get_ceph_request):
|
||||
request = hooks.CephBrokerRq()
|
||||
get_ceph_request.return_value = request
|
||||
get_request_states.return_value = {
|
||||
'ceph:43': {'complete': False, 'sent': True}
|
||||
}
|
||||
hooks._handle_ceph_request()
|
||||
|
||||
get_ceph_request.assert_called_once_with()
|
||||
get_request_states.assert_called_once_with(request, relation='ceph')
|
||||
self.relation_set.assert_not_called()
|
||||
|
||||
@patch.object(hooks, 'is_broker_action_done')
|
||||
@patch.object(hooks, 'get_ceph_request')
|
||||
@patch.object(hooks, 'get_request_states')
|
||||
@patch.object(hooks, '_get_broker_rid_unit_for_previous_request')
|
||||
def test__handle_ceph_request_complete_no_broker(
|
||||
self, _get_broker_rid_unit_for_previous_request,
|
||||
get_request_states, get_ceph_request, is_broker_action_done):
|
||||
request = hooks.CephBrokerRq()
|
||||
get_ceph_request.return_value = request
|
||||
get_request_states.return_value = {
|
||||
'ceph:43': {'complete': True, 'sent': True}
|
||||
}
|
||||
_get_broker_rid_unit_for_previous_request.return_value = None, None
|
||||
hooks._handle_ceph_request()
|
||||
|
||||
is_broker_action_done.assert_not_called()
|
||||
get_ceph_request.assert_called_once_with()
|
||||
get_request_states.assert_called_once_with(request, relation='ceph')
|
||||
|
||||
@patch.object(hooks, 'service_restart')
|
||||
@patch.object(hooks, 'mark_broker_action_done')
|
||||
@patch.object(hooks, 'is_broker_action_done')
|
||||
@patch.object(hooks, 'get_ceph_request')
|
||||
@patch.object(hooks, 'get_request_states')
|
||||
@patch.object(hooks, '_get_broker_rid_unit_for_previous_request')
|
||||
def test__handle_ceph_request_complete_not_action_done(
|
||||
self, _get_broker_rid_unit_for_previous_request,
|
||||
get_request_states, get_ceph_request, is_broker_action_done,
|
||||
mark_broker_action_done, service_restart):
|
||||
request = hooks.CephBrokerRq()
|
||||
get_ceph_request.return_value = request
|
||||
get_request_states.return_value = {
|
||||
'ceph:43': {'complete': True, 'sent': True}
|
||||
}
|
||||
_get_broker_rid_unit_for_previous_request.return_value = 'ceph:43',\
|
||||
'ceph-mon/0'
|
||||
is_broker_action_done.return_value = False
|
||||
hooks._handle_ceph_request()
|
||||
|
||||
mark_broker_action_done.assert_called_once_with(
|
||||
'nova_compute_restart', 'ceph:43', 'ceph-mon/0')
|
||||
service_restart.assert_called_once_with('nova-compute')
|
||||
is_broker_action_done.assert_called_once_with(
|
||||
'nova_compute_restart', 'ceph:43', 'ceph-mon/0')
|
||||
get_ceph_request.assert_called_once_with()
|
||||
get_request_states.assert_called_once_with(request, relation='ceph')
|
||||
|
||||
@patch.object(hooks, 'service_restart')
|
||||
@patch.object(hooks, 'is_broker_action_done')
|
||||
@patch.object(hooks, 'get_ceph_request')
|
||||
@patch.object(hooks, 'get_request_states')
|
||||
@patch.object(hooks, '_get_broker_rid_unit_for_previous_request')
|
||||
def test__handle_ceph_request_complete_no_restart(
|
||||
self, _get_broker_rid_unit_for_previous_request,
|
||||
get_request_states, get_ceph_request, is_broker_action_done,
|
||||
service_restart):
|
||||
request = hooks.CephBrokerRq()
|
||||
get_ceph_request.return_value = request
|
||||
get_request_states.return_value = {
|
||||
'ceph:43': {'complete': True, 'sent': True}
|
||||
}
|
||||
_get_broker_rid_unit_for_previous_request.return_value = 'ceph:43',\
|
||||
'ceph-mon/0'
|
||||
is_broker_action_done.return_value = True
|
||||
mark_broker_action_done.reset_mock()
|
||||
hooks.ceph_changed()
|
||||
self.assertFalse(mark_broker_action_done.called)
|
||||
hooks._handle_ceph_request()
|
||||
|
||||
service_restart.assert_not_called()
|
||||
is_broker_action_done.assert_called_once_with(
|
||||
'nova_compute_restart', 'ceph:43', 'ceph-mon/0')
|
||||
get_ceph_request.assert_called_once_with()
|
||||
get_request_states.assert_called_once_with(request, relation='ceph')
|
||||
|
||||
@patch.object(hooks, 'get_broker_rsp_key')
|
||||
@patch.object(hooks, 'get_previous_request')
|
||||
def test__get_broker_rid_unit_for_previous_request(
|
||||
self, get_previous_request, get_broker_rsp_key):
|
||||
request = hooks.CephBrokerRq()
|
||||
get_broker_rsp_key.return_value = "my_key"
|
||||
get_previous_request.return_value = request
|
||||
self.relation_ids.return_value = ['ceph:43']
|
||||
self.related_units.return_value = ['ceph-mon/0', 'ceph-mon/1']
|
||||
self.relation_get.side_effect = [{}, {
|
||||
'my_key': '{"api-version": 1, "ops": [], '
|
||||
'"request-id": "' + request.request_id + '"}'
|
||||
}]
|
||||
rid, unit = hooks._get_broker_rid_unit_for_previous_request()
|
||||
get_previous_request.assert_called_once_with('ceph:43')
|
||||
get_broker_rsp_key.assert_called_once_with()
|
||||
self.relation_get.assert_has_calls = [
|
||||
call(rid='ceph:43', unit='ceph-mon/0'),
|
||||
call(rid='ceph:43', unit='ceph-mon/1')
|
||||
]
|
||||
self.assertEqual('ceph-mon/1', unit)
|
||||
self.assertEqual('ceph:43', rid)
|
||||
|
||||
@patch.object(hooks, 'get_broker_rsp_key')
|
||||
@patch.object(hooks, 'get_previous_request')
|
||||
def test__get_broker_rid_unit_for_previous_request_not_found(
|
||||
self, get_previous_request, get_broker_rsp_key):
|
||||
request = hooks.CephBrokerRq()
|
||||
get_broker_rsp_key.return_value = "my_key"
|
||||
get_previous_request.return_value = request
|
||||
self.relation_ids.return_value = ['ceph:43']
|
||||
self.related_units.return_value = ['ceph-mon/0', 'ceph-mon/1']
|
||||
self.relation_get.side_effect = [{}, {}]
|
||||
rid, unit = hooks._get_broker_rid_unit_for_previous_request()
|
||||
get_previous_request.assert_called_once_with('ceph:43')
|
||||
get_broker_rsp_key.assert_called_once_with()
|
||||
self.relation_get.assert_has_calls = [
|
||||
call(rid='ceph:43', unit='ceph-mon/0'),
|
||||
call(rid='ceph:43', unit='ceph-mon/1')
|
||||
]
|
||||
self.assertIsNone(unit)
|
||||
self.assertIsNone(rid)
|
||||
|
||||
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
|
||||
'.add_op_request_access_to_group')
|
||||
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
|
||||
'.add_op_create_pool')
|
||||
def test_get_ceph_request(self, mock_create_pool,
|
||||
@patch('uuid.uuid1')
|
||||
def test_get_ceph_request(self, uuid1, mock_create_pool,
|
||||
mock_request_access):
|
||||
self.assert_libvirt_rbd_imagebackend_allowed.return_value = True
|
||||
self.test_config.set('rbd-pool', 'nova')
|
||||
self.test_config.set('ceph-osd-replication-count', 3)
|
||||
self.test_config.set('ceph-pool-weight', 28)
|
||||
hooks.get_ceph_request()
|
||||
uuid1.return_value = 'my_uuid'
|
||||
expected = hooks.CephBrokerRq(request_id="my_uuid")
|
||||
result = hooks.get_ceph_request()
|
||||
mock_create_pool.assert_not_called()
|
||||
mock_request_access.assert_not_called()
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
|
||||
'.add_op_request_access_to_group')
|
||||
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
|
||||
'.add_op_create_pool')
|
||||
def test_get_ceph_request_rbd(self, mock_create_pool,
|
||||
@patch('uuid.uuid1')
|
||||
def test_get_ceph_request_rbd(self, uuid1, mock_create_pool,
|
||||
mock_request_access):
|
||||
self.assert_libvirt_rbd_imagebackend_allowed.return_value = True
|
||||
self.test_config.set('rbd-pool', 'nova')
|
||||
self.test_config.set('ceph-osd-replication-count', 3)
|
||||
self.test_config.set('ceph-pool-weight', 28)
|
||||
self.test_config.set('libvirt-image-backend', 'rbd')
|
||||
hooks.get_ceph_request()
|
||||
uuid1.return_value = 'my_uuid'
|
||||
expected = hooks.CephBrokerRq(request_id="my_uuid")
|
||||
result = hooks.get_ceph_request()
|
||||
mock_create_pool.assert_called_with(name='nova', replica_count=3,
|
||||
weight=28,
|
||||
group='vms', app_name='rbd')
|
||||
mock_request_access.assert_not_called()
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
|
||||
'.add_op_request_access_to_group')
|
||||
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
|
||||
'.add_op_create_pool')
|
||||
def test_get_ceph_request_perms(self, mock_create_pool,
|
||||
@patch('uuid.uuid1')
|
||||
def test_get_ceph_request_perms(self, uuid1, mock_create_pool,
|
||||
mock_request_access):
|
||||
self.assert_libvirt_rbd_imagebackend_allowed.return_value = True
|
||||
self.test_config.set('rbd-pool', 'nova')
|
||||
|
@ -532,7 +681,9 @@ class NovaComputeRelationsTests(CharmTestCase):
|
|||
self.test_config.set('ceph-pool-weight', 28)
|
||||
self.test_config.set('libvirt-image-backend', 'rbd')
|
||||
self.test_config.set('restrict-ceph-pools', True)
|
||||
hooks.get_ceph_request()
|
||||
uuid1.return_value = 'my_uuid'
|
||||
expected = hooks.CephBrokerRq(request_id="my_uuid")
|
||||
result = hooks.get_ceph_request()
|
||||
mock_create_pool.assert_called_with(name='nova', replica_count=3,
|
||||
weight=28,
|
||||
group='vms', app_name='rbd')
|
||||
|
@ -547,6 +698,7 @@ class NovaComputeRelationsTests(CharmTestCase):
|
|||
object_prefix_permissions={'class-read': ['rbd_children']},
|
||||
permission='rwx'),
|
||||
])
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@patch.object(hooks, 'service_restart_handler')
|
||||
@patch.object(hooks, 'CONFIGS')
|
||||
|
|
Loading…
Reference in New Issue