Merge "Add instance.interface_attach notification"

This commit is contained in:
Jenkins 2017-10-05 11:29:53 +00:00 committed by Gerrit Code Review
commit b3c76529af
13 changed files with 319 additions and 37 deletions

View File

@ -0,0 +1,107 @@
{
"publisher_id": "nova-compute:compute",
"event_type": "instance.interface_attach.end",
"priority": "INFO",
"payload": {
"nova_object.data": {
"fault": null,
"ramdisk_id": "",
"kernel_id": "",
"progress": 0,
"deleted_at": null,
"reservation_id": "r-xweiyxxa",
"created_at": "2012-10-29T13:42:11Z",
"uuid": "cb968404-7797-4896-9164-bbb1a1f9530b",
"display_description": "some-server",
"node": "fake-mini",
"os_type": null,
"host_name": "some-server",
"locked": false,
"user_id": "fake",
"ip_addresses": [
{
"nova_object.data": {
"device_name": "tapce531f90-19",
"address": "192.168.1.3",
"version": 4,
"label": "private-network",
"port_uuid": "ce531f90-199f-48c0-816c-13e38010b442",
"mac": "fa:16:3e:4c:2c:30",
"meta": {}
},
"nova_object.name": "IpPayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.0"
},
{
"nova_object.data": {
"device_name": "tap88dae9fa-0d",
"address": "192.168.1.30",
"version": 4,
"label": "private-network",
"port_uuid": "88dae9fa-0dc6-49e3-8c29-3abc41e99ac9",
"mac": "00:0c:29:0d:11:74",
"meta": {}
},
"nova_object.name": "IpPayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.0"
}
],
"architecture": "x86_64",
"display_name": "some-server",
"launched_at": "2012-10-29T13:42:11Z",
"updated_at": "2012-10-29T13:42:11Z",
"key_name": "my-key",
"auto_disk_config": "MANUAL",
"block_devices": [
{
"nova_object.data": {
"boot_index": null,
"delete_on_termination": false,
"device_name": "/dev/sdb",
"tag": null,
"volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113"
},
"nova_object.name": "BlockDevicePayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.0"
}
],
"flavor": {
"nova_object.data": {
"swap": 0,
"rxtx_factor": 1.0,
"memory_mb": 512,
"name": "test_flavor",
"vcpu_weight": 0,
"root_gb": 1,
"vcpus": 1,
"is_public": true,
"ephemeral_gb": 0,
"extra_specs": {
"hw:watchdog_action": "disabled"
},
"disabled": false,
"projects": null,
"flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3"
},
"nova_object.name": "FlavorPayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.3"
},
"image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
"power_state": "running",
"metadata": {},
"tenant_id": "6f70656e737461636b20342065766572",
"terminated_at": null,
"task_state": null,
"host": "compute",
"state": "active",
"availability_zone": "nova"
},
"nova_object.name": "InstanceActionPayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.5"
}
}

View File

@ -0,0 +1,93 @@
{
"priority": "INFO",
"event_type": "instance.interface_attach.start",
"payload": {
"nova_object.name": "InstanceActionPayload",
"nova_object.version": "1.5",
"nova_object.namespace": "nova",
"nova_object.data": {
"power_state": "running",
"host_name": "some-server",
"reservation_id": "r-f8grvm0d",
"metadata": {},
"os_type": null,
"display_description": "some-server",
"ramdisk_id": "",
"deleted_at": null,
"launched_at": "2012-10-29T13:42:11Z",
"updated_at": "2012-10-29T13:42:11Z",
"key_name": "my-key",
"auto_disk_config": "MANUAL",
"block_devices": [
{
"nova_object.data": {
"boot_index": null,
"delete_on_termination": false,
"device_name": "/dev/sdb",
"tag": null,
"volume_id": "a07f71dc-8151-4e7d-a0cc-cd24a3f11113"
},
"nova_object.name": "BlockDevicePayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.0"
}
],
"availability_zone": "nova",
"display_name": "some-server",
"fault": null,
"locked": false,
"ip_addresses": [
{
"nova_object.name": "IpPayload",
"nova_object.version": "1.0",
"nova_object.namespace": "nova",
"nova_object.data": {
"label": "private-network",
"mac": "fa:16:3e:4c:2c:30",
"version": 4,
"address": "192.168.1.3",
"meta": {},
"port_uuid": "ce531f90-199f-48c0-816c-13e38010b442",
"device_name": "tapce531f90-19"
}
}
],
"kernel_id": "",
"progress": 0,
"tenant_id": "6f70656e737461636b20342065766572",
"state": "active",
"task_state": null,
"uuid": "edbe0f81-b150-4fce-9258-4e03bb2ecb41",
"user_id": "fake",
"created_at": "2012-10-29T13:42:11Z",
"image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
"node": "fake-mini",
"flavor": {
"nova_object.name": "FlavorPayload",
"nova_object.version": "1.3",
"nova_object.namespace": "nova",
"nova_object.data": {
"vcpu_weight": 0,
"memory_mb": 512,
"name": "test_flavor",
"root_gb": 1,
"extra_specs": {
"hw:watchdog_action": "disabled"
},
"disabled": false,
"rxtx_factor": 1.0,
"vcpus": 1,
"is_public": true,
"swap": 0,
"flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3",
"projects": null,
"ephemeral_gb": 0
}
},
"host": "compute",
"terminated_at": null,
"architecture": "x86_64"
}
},
"publisher_id": "nova-compute:compute"
}

View File

@ -5329,6 +5329,12 @@ class ComputeManager(manager.Manager):
self.driver.capabilities.get('supports_tagged_attach_interface',
False)):
raise exception.NetworkInterfaceTaggedAttachNotSupported()
compute_utils.notify_about_instance_action(
context, instance, self.host,
action=fields.NotificationAction.INTERFACE_ATTACH,
phase=fields.NotificationPhase.START)
bind_host_id = self.driver.network_binding_host_id(context, instance)
network_info = self.network_api.allocate_port_for_instance(
context, instance, port_id, network_id, requested_ip,
@ -5358,6 +5364,11 @@ class ComputeManager(manager.Manager):
raise exception.InterfaceAttachFailed(
instance_uuid=instance.uuid)
compute_utils.notify_about_instance_action(
context, instance, self.host,
action=fields.NotificationAction.INTERFACE_ATTACH,
phase=fields.NotificationPhase.END)
return network_info[0]
@wrap_exception()

View File

@ -48,7 +48,9 @@ class EventType(NotificationObject):
# NotificationActionField enum
# Version 1.5: Aggregate related values have been added to
# NotificationActionField enum
VERSION = '1.5'
# Version 1.6: ADD_FIX_IP replaced with INTERFACE_ATTACH in
# NotificationActionField enum
VERSION = '1.6'
fields = {
'object': fields.StringField(nullable=False),

View File

@ -401,8 +401,8 @@ class InstanceStateUpdatePayload(base.NotificationPayloadBase):
@base.notification_sample('instance-shutdown-end.json')
@base.notification_sample('instance-snapshot-start.json')
@base.notification_sample('instance-snapshot-end.json')
# @base.notification_sample('instance-add_fixed_ip-start.json')
# @base.notification_sample('instance-add_fixed_ip-end.json')
@base.notification_sample('instance-interface_attach-start.json')
@base.notification_sample('instance-interface_attach-end.json')
@base.notification_sample('instance-shelve-start.json')
@base.notification_sample('instance-shelve-end.json')
@base.notification_sample('instance-resume-start.json')

View File

@ -816,7 +816,7 @@ class NotificationAction(BaseNovaEnum):
REBOOT = 'reboot'
SHUTDOWN = 'shutdown'
SNAPSHOT = 'snapshot'
ADD_FIXED_IP = 'add_fixed_ip'
INTERFACE_ATTACH = 'interface_attach'
SHELVE = 'shelve'
RESUME = 'resume'
RESTORE = 'restore'
@ -847,7 +847,7 @@ class NotificationAction(BaseNovaEnum):
REMOVE_HOST = 'remove_host'
ALL = (UPDATE, EXCEPTION, DELETE, PAUSE, UNPAUSE, RESIZE, VOLUME_SWAP,
SUSPEND, POWER_ON, REBOOT, SHUTDOWN, SNAPSHOT, ADD_FIXED_IP,
SUSPEND, POWER_ON, REBOOT, SHUTDOWN, SNAPSHOT, INTERFACE_ATTACH,
POWER_OFF, SHELVE, RESUME, RESTORE, EXISTS, RESCUE, VOLUME_ATTACH,
VOLUME_DETACH, CREATE, EVACUATE, RESIZE_FINISH,
LIVE_MIGRATION_ABORT, LIVE_MIGRATION_POST_DEST, LIVE_MIGRATION_POST,

View File

@ -19,6 +19,7 @@ from __future__ import absolute_import
import collections
from contextlib import contextmanager
import copy
import logging as std_logging
import os
import warnings
@ -1099,7 +1100,6 @@ class NeutronFixture(fixtures.Fixture):
'tenant_id': tenant_id
}
# This port is only used if the fixture is created with multiple_ports=True
port_2 = {
'id': '88dae9fa-0dc6-49e3-8c29-3abc41e99ac9',
'network_id': network_1['id'],
@ -1164,10 +1164,14 @@ class NeutronFixture(fixtures.Fixture):
"qbg_params": None
}]
def __init__(self, test, multiple_ports=False):
def __init__(self, test):
super(NeutronFixture, self).__init__()
self.test = test
self.multiple_ports = multiple_ports
self._ports = [copy.deepcopy(NeutronFixture.port_1)]
self._extensions = []
self._networks = [NeutronFixture.network_1]
self._subnets = [NeutronFixture.subnet_1]
self._floatingips = []
def setUp(self):
super(NeutronFixture, self).setUp()
@ -1202,35 +1206,54 @@ class NeutronFixture(fixtures.Fixture):
'get_instances_security_groups_bindings',
lambda *args, **kwargs: {})
mock_neutron_client = mock.Mock()
mock_neutron_client.list_extensions.return_value = {'extensions': []}
self.test.stub_out('nova.network.neutronv2.api.get_client',
lambda *args, **kwargs: self)
def stub_show_port(port_id, *args, **kwargs):
if port_id == NeutronFixture.port_1['id']:
return {'port': NeutronFixture.port_1}
if port_id == NeutronFixture.port_2['id']:
return {'port': NeutronFixture.port_2}
def _get_first_id_match(self, id, list):
filtered_list = [p for p in list if p['id'] == id]
if len(filtered_list) > 0:
return filtered_list[0]
else:
return None
def list_extensions(self, *args, **kwargs):
return copy.deepcopy({'extensions': self._extensions})
def show_port(self, port_id, **_params):
port = self._get_first_id_match(port_id, self._ports)
if port is None:
raise exception.PortNotFound(port_id=port_id)
return {'port': port}
mock_neutron_client.show_port.side_effect = stub_show_port
mock_neutron_client.list_networks.return_value = {
'networks': [NeutronFixture.network_1]}
def delete_port(self, port, **_params):
for current_port in self._ports:
if current_port['id'] == port:
self._ports.remove(current_port)
def stub_list_ports(*args, **kwargs):
ports = {'ports': [NeutronFixture.port_1]}
if self.multiple_ports:
ports['ports'].append(NeutronFixture.port_2)
return ports
def list_networks(self, retrieve_all=True, **_params):
return copy.deepcopy({'networks': self._networks})
mock_neutron_client.list_ports.side_effect = stub_list_ports
mock_neutron_client.list_subnets.return_value = {
'subnets': [NeutronFixture.subnet_1]}
mock_neutron_client.list_floatingips.return_value = {'floatingips': []}
mock_neutron_client.update_port.side_effect = stub_show_port
def list_ports(self, retrieve_all=True, **_params):
return copy.deepcopy({'ports': self._ports})
self.test.stub_out(
'nova.network.neutronv2.api.get_client',
lambda *args, **kwargs: mock_neutron_client)
def list_subnets(self, retrieve_all=True, **_params):
return copy.deepcopy({'subnets': self._subnets})
def list_floatingips(self, retrieve_all=True, **_params):
return copy.deepcopy({'floatingips': self._floatingips})
def create_port(self, *args, **kwargs):
self._ports.append(copy.deepcopy(NeutronFixture.port_2))
return copy.deepcopy({'port': NeutronFixture.port_2})
def update_port(self, port_id, body=None):
new_port = self._get_first_id_match(port_id, self._ports)
if body is not None:
for k, v in body['port'].items():
new_port[k] = v
return {'port': new_port}
class _NoopConductor(object):

View File

@ -421,6 +421,9 @@ class TestOpenStackClient(object):
return self.api_get('/servers/%s/os-interface' %
(server_id)).body['interfaceAttachments']
def attach_interface(self, server_id, post):
return self.api_post('/servers/%s/os-interface' % server_id, post)
def detach_interface(self, server_id, port_id):
return self.api_delete('/servers/%s/os-interface/%s' %
(server_id, port_id))

View File

@ -149,6 +149,7 @@ class TestInstanceNotificationSample(
self._test_unrescue_server,
self._test_soft_delete_server,
self._test_attach_volume_error,
self._test_interface_attach,
]
for action in actions:
@ -1143,3 +1144,25 @@ class TestInstanceNotificationSample(
'volume_id': self.cinder.SWAP_NEW_VOL,
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
def _test_interface_attach(self, server):
post = {
'interfaceAttachment': {
'net_id': fixtures.NeutronFixture.network_1['id']
}
}
self.api.attach_interface(server['id'], post)
self._wait_for_notification('instance.interface_attach.end')
self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self._verify_notification(
'instance-interface_attach-start',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[0])
self._verify_notification(
'instance-interface_attach-end',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])

View File

@ -29,7 +29,9 @@ class TestListServersIpFilter(test.TestCase):
super(TestListServersIpFilter, self).setUp()
self.useFixture(policy_fixture.RealPolicyFixture())
self.neutron = self.useFixture(
nova_fixtures.NeutronFixture(self, multiple_ports=True))
nova_fixtures.NeutronFixture(self))
# Add a 2nd port to the neutron fixture to have multiple ports
self.neutron.create_port(self.neutron.port_2)
api_fixture = self.useFixture(nova_fixtures.OSAPIFixture(
api_version='v2.1'))
self.api = api_fixture.api

View File

@ -9840,7 +9840,8 @@ class ComputeAPITestCase(BaseTestCase):
self.compute_api.get_console_output,
self.context, instance)
def test_attach_interface(self):
@mock.patch.object(compute_utils, 'notify_about_instance_action')
def test_attach_interface(self, mock_notify):
new_type = flavors.get_flavor_by_flavor_id('4')
instance = objects.Instance(image_ref=uuids.image_instance,
system_metadata={},
@ -9864,9 +9865,15 @@ class ComputeAPITestCase(BaseTestCase):
mock_allocate.assert_called_once_with(
self.context, instance, port_id, network_id, req_ip,
bind_host_id='fake-host', tag=None)
mock_notify.assert_has_calls([
mock.call(self.context, instance, self.compute.host,
action='interface_attach', phase='start'),
mock.call(self.context, instance, self.compute.host,
action='interface_attach', phase='end')])
return nwinfo, port_id
def test_interface_tagged_attach(self):
@mock.patch.object(compute_utils, 'notify_about_instance_action')
def test_interface_tagged_attach(self, mock_notify):
new_type = flavors.get_flavor_by_flavor_id('4')
instance = objects.Instance(image_ref=uuids.image_instance,
system_metadata={},
@ -9891,6 +9898,11 @@ class ComputeAPITestCase(BaseTestCase):
mock_allocate.assert_called_once_with(
self.context, instance, port_id, network_id, req_ip,
bind_host_id='fake-host', tag='foo')
mock_notify.assert_has_calls([
mock.call(self.context, instance, self.compute.host,
action='interface_attach', phase='start'),
mock.call(self.context, instance, self.compute.host,
action='interface_attach', phase='end')])
return nwinfo, port_id
def test_tagged_attach_interface_raises(self):
@ -9918,6 +9930,7 @@ class ComputeAPITestCase(BaseTestCase):
req_ip = '1.2.3.4'
with test.nested(
mock.patch.object(compute_utils, 'notify_about_instance_action'),
mock.patch.object(self.compute.driver, 'attach_interface'),
mock.patch.object(self.compute.network_api,
'allocate_port_for_instance'),
@ -9925,7 +9938,8 @@ class ComputeAPITestCase(BaseTestCase):
'deallocate_port_for_instance'),
mock.patch.dict(self.compute.driver.capabilities,
supports_attach_interface=True)) as (
mock_attach, mock_allocate, mock_deallocate, mock_dict):
mock_notify, mock_attach, mock_allocate, mock_deallocate,
mock_dict):
mock_allocate.return_value = nwinfo
mock_attach.side_effect = exception.NovaException("attach_failed")
@ -9938,6 +9952,9 @@ class ComputeAPITestCase(BaseTestCase):
tag=None)
mock_deallocate.assert_called_once_with(self.context, instance,
port_id)
mock_notify.assert_has_calls([
mock.call(self.context, instance, self.compute.host,
action='interface_attach', phase='start')])
def test_detach_interface(self):
nwinfo, port_id = self.test_attach_interface()

View File

@ -1768,13 +1768,14 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
db_instance)
e = exception.InterfaceAttachFailed(instance_uuid=f_instance.uuid)
@mock.patch.object(compute_utils, 'notify_about_instance_action')
@mock.patch.object(compute_utils, 'add_instance_fault_from_exc')
@mock.patch.object(self.compute.network_api,
'allocate_port_for_instance',
side_effect=e)
@mock.patch.object(self.compute, '_instance_update',
side_effect=lambda *a, **k: {})
def do_test(update, meth, add_fault):
def do_test(update, meth, add_fault, notify):
self.assertRaises(exception.InterfaceAttachFailed,
self.compute.attach_interface,
self.context, f_instance, 'net_id', 'port_id',

View File

@ -373,7 +373,7 @@ notification_object_data = {
'AuditPeriodPayload': '1.0-2b429dd307b8374636703b843fa3f9cb',
'BandwidthPayload': '1.0-ee2616a7690ab78406842a2b68e34130',
'BlockDevicePayload': '1.0-29751e1b6d41b1454e36768a1e764df8',
'EventType': '1.5-ffa6d332f4462c45a2a363356a14165f',
'EventType': '1.6-7d6ac2f1335a814202c2f7878b2ef978',
'ExceptionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
'ExceptionPayload': '1.0-27db46ee34cd97e39f2643ed92ad0cc5',
'FlavorNotification': '1.0-a73147b93b520ff0061865849d3dfa56',