Merge "Transform libvirt.error notification"

This commit is contained in:
Zuul 2018-09-24 13:56:53 +00:00 committed by Gerrit Code Review
commit 337b24ca41
9 changed files with 180 additions and 4 deletions

View File

@ -0,0 +1,25 @@
{
"event_type": "libvirt.connect.error",
"payload": {
"nova_object.data": {
"reason": {
"nova_object.data": {
"exception": "libvirtError",
"exception_message": "Sample exception for versioned notification test.",
"function_name": "_get_connection",
"module_name": "nova.virt.libvirt.host",
"traceback": "Traceback (most recent call last):\n File \"nova/virt/libvirt/host.py\", line ..."
},
"nova_object.name": "ExceptionPayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.1"
},
"ip": "10.0.2.15"
},
"nova_object.name": "LibvirtErrorPayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.0"
},
"priority": "ERROR",
"publisher_id": "nova-compute:fake-mini"
}

View File

@ -40,6 +40,7 @@ from nova.notifications.objects import exception as notification_exception
from nova.notifications.objects import flavor as flavor_notification
from nova.notifications.objects import instance as instance_notification
from nova.notifications.objects import keypair as keypair_notification
from nova.notifications.objects import libvirt as libvirt_notification
from nova.notifications.objects import metrics as metrics_notification
from nova.notifications.objects import server_group as sg_notification
from nova import objects
@ -810,6 +811,29 @@ def notify_about_metrics_update(context, host, host_ip, nodename,
notification.emit(context)
@rpc.if_notifications_enabled
def notify_about_libvirt_connect_error(context, ip, exception, tb):
"""Send a versioned notification about libvirt connect error.
:param context: the request context
:param ip: the IP address of the host
:param exception: the thrown exception
:param tb: the traceback
"""
fault, _ = _get_fault_and_priority_from_exc_and_tb(exception, tb)
payload = libvirt_notification.LibvirtErrorPayload(ip=ip, reason=fault)
notification = libvirt_notification.LibvirtErrorNotification(
priority=fields.NotificationPriority.ERROR,
publisher=notification_base.NotificationPublisher(
host=CONF.host, source=fields.NotificationSource.COMPUTE),
event_type=notification_base.EventType(
object='libvirt',
action=fields.NotificationAction.CONNECT,
phase=fields.NotificationPhase.ERROR),
payload=payload)
notification.emit(context)
def refresh_info_cache_for_instance(context, instance):
"""Refresh the info cache for an instance.

View File

@ -65,7 +65,8 @@ class EventType(NotificationObject):
# enum
# Version 1.15: LIVE_MIGRATION_FORCE_COMPLETE is added to the
# NotificationActionField enum
VERSION = '1.15'
# Version 1.16: CONNECT is added to NotificationActionField enum
VERSION = '1.16'
fields = {
'object': fields.StringField(nullable=False),

View File

@ -0,0 +1,40 @@
# 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 nova.notifications.objects import base
from nova.objects import base as nova_base
from nova.objects import fields
@nova_base.NovaObjectRegistry.register_notification
class LibvirtErrorPayload(base.NotificationPayloadBase):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'ip': fields.StringField(),
'reason': fields.ObjectField('ExceptionPayload'),
}
def __init__(self, ip, reason):
super(LibvirtErrorPayload, self).__init__()
self.ip = ip
self.reason = reason
@base.notification_sample('libvirt-connect-error.json')
@nova_base.NovaObjectRegistry.register_notification
class LibvirtErrorNotification(base.NotificationBase):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'payload': fields.ObjectField('LibvirtErrorPayload')
}

View File

@ -831,6 +831,7 @@ class NotificationAction(BaseNovaEnum):
LOCK = 'lock'
UNLOCK = 'unlock'
UPDATE_PROP = 'update_prop'
CONNECT = 'connect'
ALL = (UPDATE, EXCEPTION, DELETE, PAUSE, UNPAUSE, RESIZE, VOLUME_SWAP,
SUSPEND, POWER_ON, REBOOT, SHUTDOWN, SNAPSHOT, INTERFACE_ATTACH,
@ -842,7 +843,8 @@ class NotificationAction(BaseNovaEnum):
RESIZE_CONFIRM, RESIZE_PREP, RESIZE_REVERT, SHELVE_OFFLOAD,
SOFT_DELETE, TRIGGER_CRASH_DUMP, UNRESCUE, UNSHELVE, ADD_HOST,
REMOVE_HOST, ADD_MEMBER, UPDATE_METADATA, LOCK, UNLOCK,
REBUILD_SCHEDULED, UPDATE_PROP, LIVE_MIGRATION_FORCE_COMPLETE)
REBUILD_SCHEDULED, UPDATE_PROP, LIVE_MIGRATION_FORCE_COMPLETE,
CONNECT)
# TODO(rlrossit): These should be changed over to be a StateMachine enum from

View File

@ -0,0 +1,56 @@
# Copyright 2018 NTT Corporation
#
# 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 fixtures
import mock
import nova.conf
from nova import exception
from nova.tests.functional.notification_sample_tests \
import notification_sample_base
from nova.tests.unit import fake_notifier
from nova.tests.unit.virt.libvirt import fakelibvirt
from nova.virt.libvirt import host
CONF = nova.conf.CONF
class TestLibvirtErrorNotificationSample(
notification_sample_base.NotificationSampleTestBase):
def setUp(self):
self.flags(compute_driver='libvirt.LibvirtDriver')
self.useFixture(fakelibvirt.FakeLibvirtFixture())
self.useFixture(fixtures.MockPatchObject(host.Host, 'initialize'))
super(TestLibvirtErrorNotificationSample, self).setUp()
@mock.patch('nova.virt.libvirt.host.Host._get_connection')
def test_libvirt_connect_error(self, mock_get_conn):
mock_get_conn.side_effect = fakelibvirt.libvirtError(
'Sample exception for versioned notification test.')
# restart the compute service
self.assertRaises(exception.HypervisorUnavailable,
self.restart_compute_service, self.compute)
self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self._verify_notification(
'libvirt-connect-error',
replacements={
'ip': CONF.my_ip,
'reason.function_name': self.ANY,
'reason.module_name': self.ANY,
'reason.traceback': self.ANY
},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[0])

View File

@ -370,7 +370,7 @@ notification_object_data = {
'AuditPeriodPayload': '1.0-2b429dd307b8374636703b843fa3f9cb',
'BandwidthPayload': '1.0-ee2616a7690ab78406842a2b68e34130',
'BlockDevicePayload': '1.0-29751e1b6d41b1454e36768a1e764df8',
'EventType': '1.15-a93b5b3b54ebf6c5a158dfcd985d15c5',
'EventType': '1.16-0da423d66218567962410921f2542c41',
'ExceptionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
'ExceptionPayload': '1.1-6c43008bd81885a63bc7f7c629f0793b',
'FlavorNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
@ -404,6 +404,8 @@ notification_object_data = {
'IpPayload': '1.0-8ecf567a99e516d4af094439a7632d34',
'KeypairNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
'KeypairPayload': '1.0-6daebbbde0e1bf35c1556b1ecd9385c1',
'LibvirtErrorNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
'LibvirtErrorPayload': '1.0-9e7a8f0b895dd15531d5a6f3aa95d58e',
'MetricPayload': '1.0-bcdbe85048f335132e4c82a1b8fa3da8',
'MetricsNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
'MetricsPayload': '1.0-65c69b15b4de5a8c01971cb5bb9ab650',

View File

@ -1654,6 +1654,27 @@ class LibvirtConnTestCase(test.NoDBTestCase,
mock_lookup.side_effect = lambda x: fakelibvirt.NodeDevice(conn)
drvr._prepare_pci_devices_for_use(pci_devices)
@mock.patch('nova.context.get_admin_context')
@mock.patch('nova.compute.utils.notify_about_libvirt_connect_error')
def test_versioned_notification(self, mock_notify, mock_get):
mock_get.return_value = self.context
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
fake_error = fakelibvirt.make_libvirtError(
fakelibvirt.libvirtError, "Failed to connect to host",
error_code=fakelibvirt.VIR_ERR_INTERNAL_ERROR)
with mock.patch('nova.virt.libvirt.host.Host._get_connection',
side_effect=fake_error):
self.assertRaises(exception.HypervisorUnavailable,
drvr._host.get_connection)
mock_get.assert_called_once_with()
mock_notify.assert_called_once_with(self.context, ip=CONF.my_ip,
exception=fake_error, tb=mock.ANY)
_, kwargs = mock_notify.call_args
self.assertIn('Traceback (most recent call last):', kwargs['tb'])
@mock.patch.object(fakelibvirt.virConnect, "nodeDeviceLookupByName")
@mock.patch.object(fakelibvirt.virNodeDevice, "dettach")
def test_prepare_pci_device_exception(self, mock_detach, mock_lookup):

View File

@ -32,6 +32,7 @@ import os
import socket
import sys
import threading
import traceback
from eventlet import greenio
from eventlet import greenthread
@ -45,6 +46,7 @@ from oslo_utils import units
from oslo_utils import versionutils
import six
from nova.compute import utils as compute_utils
import nova.conf
from nova import context as nova_context
from nova import exception
@ -463,9 +465,12 @@ class Host(object):
payload = dict(ip=CONF.my_ip,
method='_connect',
reason=ex)
rpc.get_notifier('compute').error(nova_context.get_admin_context(),
ctxt = nova_context.get_admin_context()
rpc.get_notifier('compute').error(ctxt,
'compute.libvirt.error',
payload)
compute_utils.notify_about_libvirt_connect_error(
ctxt, ip=CONF.my_ip, exception=ex, tb=traceback.format_exc())
raise exception.HypervisorUnavailable(host=CONF.host)
return conn