diff options
22 files changed, 270 insertions, 90 deletions
diff --git a/nova/api/openstack/compute/personality.py b/nova/api/openstack/compute/personality.py index 2d0aecfe21..4d15fb851d 100644 --- a/nova/api/openstack/compute/personality.py +++ b/nova/api/openstack/compute/personality.py @@ -49,9 +49,8 @@ class Personality(extensions.V21APIExtensionBase): # server_update & server_rebuild def server_create(self, server_dict, create_kwargs, body_deprecated_param=None): - if 'personality' in server_dict: - create_kwargs['injected_files'] = self._get_injected_files( - server_dict['personality']) + create_kwargs['injected_files'] = self._get_injected_files( + server_dict.get('personality', [])) def server_rebuild(self, server_dict, create_kwargs, body_deprecated_param=None): diff --git a/nova/compute/api.py b/nova/compute/api.py index 32ca41fd73..f16f6c4cad 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -937,7 +937,13 @@ class API(base.Base): block_device.properties_root_device_name( boot_meta.get('properties', {}))) - image_meta = objects.ImageMeta.from_dict(boot_meta) + try: + image_meta = objects.ImageMeta.from_dict(boot_meta) + except ValueError as e: + # there must be invalid values in the image meta properties so + # consider this an invalid request + msg = _('Invalid image metadata. Error: %s') % six.text_type(e) + raise exception.InvalidRequest(msg) numa_topology = hardware.numa_get_constraints( instance_type, image_meta) @@ -2526,7 +2532,7 @@ class API(base.Base): elevated, instance.uuid, 'finished') # reverse quota reservation for increased resource usage - deltas = compute_utils.reverse_upsize_quota_delta(context, migration) + deltas = compute_utils.reverse_upsize_quota_delta(context, instance) quotas = compute_utils.reserve_quota_delta(context, deltas, instance) instance.task_state = task_states.RESIZE_REVERTING diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 88a878e4bb..69841d2431 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -2296,10 +2296,19 @@ class ComputeManager(manager.Manager): instance=instance) except (cinder_exception.EndpointNotFound, keystone_exception.EndpointNotFound) as exc: - LOG.warning(_LW('Ignoring EndpointNotFound: %s'), exc, + LOG.warning(_LW('Ignoring EndpointNotFound for ' + 'volume %(volume_id)s: %(exc)s'), + {'exc': exc, 'volume_id': bdm.volume_id}, instance=instance) except cinder_exception.ClientException as exc: - LOG.warning(_LW('Ignoring Unknown cinder exception: %s'), exc, + LOG.warning(_LW('Ignoring unknown cinder exception for ' + 'volume %(volume_id)s: %(exc)s'), + {'exc': exc, 'volume_id': bdm.volume_id}, + instance=instance) + except Exception as exc: + LOG.warning(_LW('Ignoring unknown exception for ' + 'volume %(volume_id)s: %(exc)s'), + {'exc': exc, 'volume_id': bdm.volume_id}, instance=instance) if notify: diff --git a/nova/compute/utils.py b/nova/compute/utils.py index df820e5f7a..3f8a8cafa0 100644 --- a/nova/compute/utils.py +++ b/nova/compute/utils.py @@ -455,16 +455,12 @@ def upsize_quota_delta(context, new_flavor, old_flavor): return resize_quota_delta(context, new_flavor, old_flavor, 1, 1) -def reverse_upsize_quota_delta(context, migration_ref): +def reverse_upsize_quota_delta(context, instance): """Calculate deltas required to reverse a prior upsizing quota adjustment. """ - old_flavor = objects.Flavor.get_by_id( - context, migration_ref['old_instance_type_id']) - new_flavor = objects.Flavor.get_by_id( - context, migration_ref['new_instance_type_id']) - - return resize_quota_delta(context, new_flavor, old_flavor, -1, -1) + return resize_quota_delta(context, instance.new_flavor, + instance.old_flavor, -1, -1) def downsize_quota_delta(context, instance): diff --git a/nova/exception.py b/nova/exception.py index a8cc90f099..5e9faefb99 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -1121,7 +1121,7 @@ class FlavorAccessNotFound(NotFound): class FlavorExtraSpecUpdateCreateFailed(NovaException): - msg_fmt = _("Flavor %(id)d extra spec cannot be updated or created " + msg_fmt = _("Flavor %(id)s extra spec cannot be updated or created " "after %(retries)d retries.") diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 6487580a28..a0eb22d192 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -37,6 +37,7 @@ import six from nova import exception from nova.i18n import _, _LE, _LW +from nova.network import model as network_model from nova import objects from nova import paths from nova.pci import utils as pci_utils @@ -1342,7 +1343,7 @@ def _set_device_mtu(dev, mtu=None): check_exit_code=[0, 2, 254]) -def _create_veth_pair(dev1_name, dev2_name): +def _create_veth_pair(dev1_name, dev2_name, mtu=None): """Create a pair of veth devices with the specified names, deleting any previous devices with those names. """ @@ -1355,7 +1356,7 @@ def _create_veth_pair(dev1_name, dev2_name): utils.execute('ip', 'link', 'set', dev, 'up', run_as_root=True) utils.execute('ip', 'link', 'set', dev, 'promisc', 'on', run_as_root=True) - _set_device_mtu(dev) + _set_device_mtu(dev, mtu) def _ovs_vsctl(args): @@ -1368,15 +1369,34 @@ def _ovs_vsctl(args): raise exception.AgentError(method=full_args) -def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id): - _ovs_vsctl(['--', '--if-exists', 'del-port', dev, '--', - 'add-port', bridge, dev, - '--', 'set', 'Interface', dev, - 'external-ids:iface-id=%s' % iface_id, - 'external-ids:iface-status=active', - 'external-ids:attached-mac=%s' % mac, - 'external-ids:vm-uuid=%s' % instance_id]) - _set_device_mtu(dev) +def _create_ovs_vif_cmd(bridge, dev, iface_id, mac, + instance_id, interface_type=None): + cmd = ['--', '--if-exists', 'del-port', dev, '--', + 'add-port', bridge, dev, + '--', 'set', 'Interface', dev, + 'external-ids:iface-id=%s' % iface_id, + 'external-ids:iface-status=active', + 'external-ids:attached-mac=%s' % mac, + 'external-ids:vm-uuid=%s' % instance_id] + if interface_type: + cmd += ['type=%s' % interface_type] + return cmd + + +def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id, + mtu=None, interface_type=None): + _ovs_vsctl(_create_ovs_vif_cmd(bridge, dev, iface_id, + mac, instance_id, + interface_type)) + # Note at present there is no support for setting the + # mtu for vhost-user type ports. + if interface_type != network_model.OVS_VHOSTUSER_INTERFACE_TYPE: + _set_device_mtu(dev, mtu) + else: + LOG.debug("MTU not set on %(interface_name)s interface " + "of type %(interface_type)s.", + {'interface_name': dev, + 'interface_type': interface_type}) def delete_ovs_vif_port(bridge, dev): @@ -1384,10 +1404,6 @@ def delete_ovs_vif_port(bridge, dev): delete_net_dev(dev) -def ovs_set_vhostuser_port_type(dev): - _ovs_vsctl(['--', 'set', 'Interface', dev, 'type=dpdkvhostuser']) - - def create_ivs_vif_port(dev, iface_id, mac, instance_id): utils.execute('ivs-ctl', 'add-port', dev, run_as_root=True) diff --git a/nova/network/model.py b/nova/network/model.py index c7376f862d..c7979d7c2a 100644 --- a/nova/network/model.py +++ b/nova/network/model.py @@ -77,6 +77,8 @@ VIF_DETAILS_VHOSTUSER_SOCKET = 'vhostuser_socket' # Specifies whether vhost-user socket should be plugged # into ovs bridge. Valid values are True and False VIF_DETAILS_VHOSTUSER_OVS_PLUG = 'vhostuser_ovs_plug' +# ovs vhost user interface type name +OVS_VHOSTUSER_INTERFACE_TYPE = 'dpdkvhostuser' # Constants for dictionary keys in the 'vif_details' field that are # valid for VIF_TYPE_TAP. diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py index c04b60f722..7fb31c947b 100644 --- a/nova/network/neutronv2/api.py +++ b/nova/network/neutronv2/api.py @@ -1602,10 +1602,12 @@ class API(base_api.NetworkAPI): def _nw_info_build_network(self, port, networks, subnets): network_name = None + network_mtu = None for net in networks: if port['network_id'] == net['id']: network_name = net['name'] tenant_id = net['tenant_id'] + network_mtu = net.get('mtu') break else: tenant_id = port['tenant_id'] @@ -1648,7 +1650,8 @@ class API(base_api.NetworkAPI): bridge=bridge, injected=CONF.flat_injected, label=network_name, - tenant_id=tenant_id + tenant_id=tenant_id, + mtu=network_mtu ) network['subnets'] = subnets port_profile = port.get('binding:profile') diff --git a/nova/tests/functional/regressions/test_bug_1558866.py b/nova/tests/functional/regressions/test_bug_1558866.py new file mode 100644 index 0000000000..378b629aa8 --- /dev/null +++ b/nova/tests/functional/regressions/test_bug_1558866.py @@ -0,0 +1,77 @@ +# Copyright 2016 IBM Corp. +# +# 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 datetime + +from oslo_config import cfg + +from nova import test +from nova.tests import fixtures as nova_fixtures +from nova.tests.functional.api import client as api_client +from nova.tests.unit.image import fake as fake_image +from nova.tests.unit import policy_fixture + +CONF = cfg.CONF +CONF.import_opt('null_kernel', 'nova.compute.api') + + +class TestServerGet(test.TestCase): + + def setUp(self): + super(TestServerGet, self).setUp() + self.useFixture(policy_fixture.RealPolicyFixture()) + api_fixture = self.useFixture(nova_fixtures.OSAPIFixture( + api_version='v2.1')) + + self.api = api_fixture.api + + # the image fake backend needed for image discovery + image_service = fake_image.stub_out_image_service(self) + self.addCleanup(fake_image.FakeImageService_reset) + + # NOTE(mriedem): This image has an invalid architecture metadata value + # and is used for negative testing in the functional stack. + timestamp = datetime.datetime(2011, 1, 1, 1, 2, 3) + image = {'id': 'c456eb30-91d7-4f43-8f46-2efd9eccd744', + 'name': 'fake-image-invalid-arch', + 'created_at': timestamp, + 'updated_at': timestamp, + 'deleted_at': None, + 'deleted': False, + 'status': 'active', + 'is_public': False, + 'container_format': 'raw', + 'disk_format': 'raw', + 'size': '25165824', + 'properties': {'kernel_id': CONF.null_kernel, + 'ramdisk_id': CONF.null_kernel, + 'architecture': 'x64'}} + self.image_id = image_service.create(None, image)['id'] + self.flavor_id = self.api.get_flavors()[0]['id'] + + def test_boot_server_with_invalid_image_meta(self): + """Regression test for bug #1558866. + + Glance allows you to provide any architecture value for image meta + properties but nova validates the image metadata against the + nova.compute.arch.ALL values during the conversion to the ImageMeta + object. This test ensures we get a 400 back in that case rather than + a 500. + """ + server = dict(name='server1', + imageRef=self.image_id, + flavorRef=self.flavor_id) + ex = self.assertRaises(api_client.OpenStackApiException, + self.api.post_server, {'server': server}) + self.assertEqual(400, ex.response.status_code) diff --git a/nova/tests/unit/api/openstack/compute/test_flavors_extra_specs.py b/nova/tests/unit/api/openstack/compute/test_flavors_extra_specs.py index 783b3af289..b70a97a8ce 100644 --- a/nova/tests/unit/api/openstack/compute/test_flavors_extra_specs.py +++ b/nova/tests/unit/api/openstack/compute/test_flavors_extra_specs.py @@ -190,7 +190,8 @@ class FlavorsExtraSpecsTestV21(test.TestCase): def test_create_flavor_db_duplicate(self): def fake_instance_type_extra_specs_update_or_create(*args, **kwargs): - raise exception.FlavorExtraSpecUpdateCreateFailed(id=1, retries=5) + raise exception.FlavorExtraSpecUpdateCreateFailed(id='1', + retries=5) self.stubs.Set(nova.db, 'flavor_extra_specs_update_or_create', @@ -348,7 +349,8 @@ class FlavorsExtraSpecsTestV21(test.TestCase): def test_update_flavor_db_duplicate(self): def fake_instance_type_extra_specs_update_or_create(*args, **kwargs): - raise exception.FlavorExtraSpecUpdateCreateFailed(id=1, retries=5) + raise exception.FlavorExtraSpecUpdateCreateFailed(id='1', + retries=5) self.stubs.Set(nova.db, 'flavor_extra_specs_update_or_create', diff --git a/nova/tests/unit/api/openstack/compute/test_serversV21.py b/nova/tests/unit/api/openstack/compute/test_serversV21.py index 5b36a7828c..3ec79c3d78 100644 --- a/nova/tests/unit/api/openstack/compute/test_serversV21.py +++ b/nova/tests/unit/api/openstack/compute/test_serversV21.py @@ -2984,6 +2984,18 @@ class ServersControllerCreateTest(test.TestCase): self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.req, body=self.body) + def test_create_instance_without_personality_should_get_empty_list(self): + old_create = compute_api.API.create + del self.body['server']['personality'] + + def create(*args, **kwargs): + self.assertEqual([], kwargs['injected_files']) + return old_create(*args, **kwargs) + + self.stubs.Set(compute_api.API, 'create', create) + + self._test_create_instance() + def test_create_instance_with_extra_personality_arg(self): self.body['server']['personality'] = [ { diff --git a/nova/tests/unit/compute/test_compute_api.py b/nova/tests/unit/compute/test_compute_api.py index 2f20e2323b..d7278bea2b 100644 --- a/nova/tests/unit/compute/test_compute_api.py +++ b/nova/tests/unit/compute/test_compute_api.py @@ -1207,7 +1207,7 @@ class _ComputeAPIUnitTestMixIn(object): self.context, fake_inst['uuid'], 'finished').AndReturn( fake_mig) compute_utils.reverse_upsize_quota_delta( - self.context, fake_mig).AndReturn('deltas') + self.context, fake_inst).AndReturn('deltas') resvs = ['resvs'] fake_quotas = objects.Quotas.from_reservations(self.context, resvs) @@ -1265,7 +1265,7 @@ class _ComputeAPIUnitTestMixIn(object): delta = ['delta'] compute_utils.reverse_upsize_quota_delta( - self.context, fake_mig).AndReturn(delta) + self.context, fake_inst).AndReturn(delta) resvs = ['resvs'] fake_quotas = objects.Quotas.from_reservations(self.context, resvs) compute_utils.reserve_quota_delta( diff --git a/nova/tests/unit/compute/test_compute_mgr.py b/nova/tests/unit/compute/test_compute_mgr.py index 9dfb886f50..33b06709a3 100644 --- a/nova/tests/unit/compute/test_compute_mgr.py +++ b/nova/tests/unit/compute/test_compute_mgr.py @@ -1105,6 +1105,10 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase): exc = exception.DiskNotFound self._test_shutdown_instance_exception(exc) + def test_shutdown_instance_other_exception(self): + exc = Exception('some other exception') + self._test_shutdown_instance_exception(exc) + def _test_init_instance_retries_reboot(self, instance, reboot_type, return_power_state): instance.host = self.compute.host diff --git a/nova/tests/unit/compute/test_compute_utils.py b/nova/tests/unit/compute/test_compute_utils.py index 187d13232b..b6fc319c58 100644 --- a/nova/tests/unit/compute/test_compute_utils.py +++ b/nova/tests/unit/compute/test_compute_utils.py @@ -723,8 +723,7 @@ class ComputeUtilsQuotaDeltaTestCase(test.TestCase): deltas = compute_utils.downsize_quota_delta(self.context, inst) self.assertEqual(expected_deltas, deltas) - @mock.patch.object(objects.Flavor, 'get_by_id') - def test_reverse_quota_delta(self, mock_get_flavor): + def test_reverse_quota_delta(self): inst = create_instance(self.context, params=None) inst.old_flavor = flavors.get_flavor_by_name('m1.tiny') inst.new_flavor = flavors.get_flavor_by_name('m1.medium') @@ -735,20 +734,8 @@ class ComputeUtilsQuotaDeltaTestCase(test.TestCase): 'ram': -1 * (inst.new_flavor['memory_mb'] - inst.old_flavor['memory_mb']) } - updates = {'old_instance_type_id': inst.old_flavor['id'], - 'new_instance_type_id': inst.new_flavor['id']} - - fake_migration = test_migration.fake_db_migration(**updates) - - def _flavor_get_by_id(context, type_id): - if type_id == updates['old_instance_type_id']: - return inst.old_flavor - else: - return inst.new_flavor - mock_get_flavor.side_effect = _flavor_get_by_id - deltas = compute_utils.reverse_upsize_quota_delta(self.context, - fake_migration) + deltas = compute_utils.reverse_upsize_quota_delta(self.context, inst) self.assertEqual(expected_deltas, deltas) @mock.patch.object(objects.Quotas, 'reserve') diff --git a/nova/tests/unit/network/test_linux_net.py b/nova/tests/unit/network/test_linux_net.py index a75dc100be..26a531f2ca 100644 --- a/nova/tests/unit/network/test_linux_net.py +++ b/nova/tests/unit/network/test_linux_net.py @@ -34,6 +34,7 @@ from nova import db from nova import exception from nova.network import driver from nova.network import linux_net +from nova.network import model as network_model from nova import objects from nova import test from nova import utils @@ -1210,13 +1211,37 @@ class LinuxNetworkTestCase(test.NoDBTestCase): linux_net._set_device_mtu('fake-dev') ex.assert_has_calls(calls) - def _ovs_vif_port(self, calls): + def _ovs_vif_port(self, calls, interface_type=None): with mock.patch.object(utils, 'execute', return_value=('', '')) as ex: linux_net.create_ovs_vif_port('fake-bridge', 'fake-dev', 'fake-iface-id', 'fake-mac', - 'fake-instance-uuid') + 'fake-instance-uuid', + interface_type=interface_type) ex.assert_has_calls(calls) + def test_ovs_vif_port_cmd(self): + expected = ['--', '--if-exists', + 'del-port', 'fake-dev', '--', 'add-port', + 'fake-bridge', 'fake-dev', + '--', 'set', 'Interface', 'fake-dev', + 'external-ids:iface-id=fake-iface-id', + 'external-ids:iface-status=active', + 'external-ids:attached-mac=fake-mac', + 'external-ids:vm-uuid=fake-instance-uuid' + ] + cmd = linux_net._create_ovs_vif_cmd('fake-bridge', 'fake-dev', + 'fake-iface-id', 'fake-mac', + 'fake-instance-uuid') + + self.assertEqual(expected, cmd) + + expected += ['type=fake-type'] + cmd = linux_net._create_ovs_vif_cmd('fake-bridge', 'fake-dev', + 'fake-iface-id', 'fake-mac', + 'fake-instance-uuid', + 'fake-type') + self.assertEqual(expected, cmd) + def test_ovs_vif_port(self): calls = [ mock.call('ovs-vsctl', '--timeout=120', '--', '--if-exists', @@ -1231,6 +1256,22 @@ class LinuxNetworkTestCase(test.NoDBTestCase): ] self._ovs_vif_port(calls) + @mock.patch.object(linux_net, '_ovs_vsctl') + @mock.patch.object(linux_net, '_create_ovs_vif_cmd') + @mock.patch.object(linux_net, '_set_device_mtu') + def test_ovs_vif_port_with_type_vhostuser(self, mock_set_device_mtu, + mock_create_cmd, mock_vsctl): + linux_net.create_ovs_vif_port( + 'fake-bridge', + 'fake-dev', 'fake-iface-id', 'fake-mac', + "fake-instance-uuid", mtu=1500, + interface_type=network_model.OVS_VHOSTUSER_INTERFACE_TYPE) + mock_create_cmd.assert_called_once_with('fake-bridge', + 'fake-dev', 'fake-iface-id', 'fake-mac', + "fake-instance-uuid", network_model.OVS_VHOSTUSER_INTERFACE_TYPE) + self.assertFalse(mock_set_device_mtu.called) + self.assertTrue(mock_vsctl.called) + def test_ovs_vif_port_with_mtu(self): self.flags(network_device_mtu=10000) calls = [ @@ -1371,16 +1412,6 @@ class LinuxNetworkTestCase(test.NoDBTestCase): self.assertEqual(2, len(executes)) self.mox.UnsetStubs() - def test_ovs_set_vhostuser_type(self): - calls = [ - mock.call('ovs-vsctl', '--timeout=120', '--', 'set', - 'Interface', 'fake-dev', 'type=dpdkvhostuser', - run_as_root=True) - ] - with mock.patch.object(utils, 'execute', return_value=('', '')) as ex: - linux_net.ovs_set_vhostuser_port_type('fake-dev') - ex.assert_has_calls(calls) - @mock.patch('os.path.exists', return_value=True) @mock.patch('nova.utils.execute') def test_remove_bridge(self, mock_execute, mock_exists): diff --git a/nova/tests/unit/network/test_neutronv2.py b/nova/tests/unit/network/test_neutronv2.py index 7424cbb63f..295c672909 100644 --- a/nova/tests/unit/network/test_neutronv2.py +++ b/nova/tests/unit/network/test_neutronv2.py @@ -2391,7 +2391,8 @@ class TestNeutronv2(TestNeutronv2Base): 'binding:vif_type': vif_type, } fake_subnets = [model.Subnet(cidr='1.0.0.0/8')] - fake_nets = [{'id': 'net-id', 'name': 'foo', 'tenant_id': 'tenant'}] + fake_nets = [{'id': 'net-id', 'name': 'foo', 'tenant_id': 'tenant', + 'mtu': 9000}] api = neutronapi.API() self.mox.ReplayAll() neutronapi.get_client('fake') @@ -2401,6 +2402,7 @@ class TestNeutronv2(TestNeutronv2Base): self.assertEqual(net['id'], 'net-id') self.assertEqual(net['label'], 'foo') self.assertEqual(net.get_meta('tenant_id'), 'tenant') + self.assertEqual(9000, net.get_meta('mtu')) self.assertEqual(net.get_meta('injected'), CONF.flat_injected) return net, iid diff --git a/nova/tests/unit/virt/libvirt/test_vif.py b/nova/tests/unit/virt/libvirt/test_vif.py index 3bc8ab27f2..d7c18b615d 100644 --- a/nova/tests/unit/virt/libvirt/test_vif.py +++ b/nova/tests/unit/virt/libvirt/test_vif.py @@ -59,7 +59,7 @@ class LibvirtVifTestCase(test.NoDBTestCase): subnets=[subnet_bridge_4, subnet_bridge_6], bridge_interface='eth0', - vlan=99) + vlan=99, mtu=9000) vif_bridge = network_model.VIF(id='vif-xxx-yyy-zzz', address='ca:fe:de:ad:be:ef', @@ -89,7 +89,7 @@ class LibvirtVifTestCase(test.NoDBTestCase): subnets=[subnet_bridge_4, subnet_bridge_6], bridge_interface=None, - vlan=99) + vlan=99, mtu=1000) network_ivs = network_model.Network(id='network-id-xxx-yyy-zzz', bridge='br0', @@ -312,7 +312,7 @@ class LibvirtVifTestCase(test.NoDBTestCase): network_model.VIF_DETAILS_VHOSTUSER_SOCKET: '/tmp/usv-xxx-yyy-zzz', network_model.VIF_DETAILS_VHOSTUSER_OVS_PLUG: True}, - ovs_interfaceid='aaa-bbb-ccc' + ovs_interfaceid='aaa-bbb-ccc', mtu=1500 ) vif_vhostuser_no_path = network_model.VIF(id='vif-xxx-yyy-zzz', @@ -665,7 +665,7 @@ class LibvirtVifTestCase(test.NoDBTestCase): 'device_exists': [mock.call('qbrvif-xxx-yyy'), mock.call('qvovif-xxx-yyy')], '_create_veth_pair': [mock.call('qvbvif-xxx-yyy', - 'qvovif-xxx-yyy')], + 'qvovif-xxx-yyy', 1000)], 'execute': [mock.call('brctl', 'addbr', 'qbrvif-xxx-yyy', run_as_root=True), mock.call('brctl', 'setfd', 'qbrvif-xxx-yyy', 0, @@ -679,7 +679,8 @@ class LibvirtVifTestCase(test.NoDBTestCase): 'create_ovs_vif_port': [mock.call('br0', 'qvovif-xxx-yyy', 'aaa-bbb-ccc', 'ca:fe:de:ad:be:ef', - 'instance-uuid')] + 'instance-uuid', + 1000)] } # The disable_ipv6 call needs to be added in the middle, if required if ipv6_exists: @@ -799,7 +800,7 @@ class LibvirtVifTestCase(test.NoDBTestCase): 'device_exists': [mock.call('qbrvif-xxx-yyy'), mock.call('qvovif-xxx-yyy')], '_create_veth_pair': [mock.call('qvbvif-xxx-yyy', - 'qvovif-xxx-yyy')], + 'qvovif-xxx-yyy', None)], 'execute': [mock.call('brctl', 'addbr', 'qbrvif-xxx-yyy', run_as_root=True), mock.call('brctl', 'setfd', 'qbrvif-xxx-yyy', 0, @@ -1311,22 +1312,18 @@ class LibvirtVifTestCase(test.NoDBTestCase): def test_vhostuser_ovs_plug(self): calls = { - 'create_ovs_vif_port': [mock.call('br0', - 'usv-xxx-yyy-zzz', - 'aaa-bbb-ccc', - 'ca:fe:de:ad:be:ef', - 'instance-uuid')], - 'ovs_set_vhostuser_port_type': [mock.call('usv-xxx-yyy-zzz')] + 'create_ovs_vif_port': [ + mock.call( + 'br0', 'usv-xxx-yyy-zzz', 'aaa-bbb-ccc', + 'ca:fe:de:ad:be:ef', 'instance-uuid', 9000, + interface_type=network_model.OVS_VHOSTUSER_INTERFACE_TYPE + )] } - with contextlib.nested( - mock.patch.object(linux_net, 'create_ovs_vif_port'), - mock.patch.object(linux_net, 'ovs_set_vhostuser_port_type') - ) as (create_ovs_vif_port, ovs_set_vhostuser_port_type): + with mock.patch.object(linux_net, + 'create_ovs_vif_port') as create_ovs_vif_port: d = vif.LibvirtGenericVIFDriver() d.plug_vhostuser(self.instance, self.vif_vhostuser_ovs) create_ovs_vif_port.assert_has_calls(calls['create_ovs_vif_port']) - ovs_set_vhostuser_port_type.assert_has_calls( - calls['ovs_set_vhostuser_port_type']) def test_vhostuser_ovs_unplug(self): calls = { diff --git a/nova/tests/unit/volume/test_cinder.py b/nova/tests/unit/volume/test_cinder.py index 120a31e285..4334632268 100644 --- a/nova/tests/unit/volume/test_cinder.py +++ b/nova/tests/unit/volume/test_cinder.py @@ -293,6 +293,25 @@ class CinderApiTestCase(test.NoDBTestCase): mock_cinderclient.return_value.volumes. \ initialize_connection.assert_called_once_with(volume_id, connector) + @mock.patch('nova.volume.cinder.LOG') + @mock.patch('nova.volume.cinder.cinderclient') + def test_initialize_connection_exception_no_code( + self, mock_cinderclient, mock_log): + mock_cinderclient.return_value.volumes. \ + initialize_connection.side_effect = ( + cinder_exception.ClientException(500, "500")) + mock_cinderclient.return_value.volumes. \ + terminate_connection.side_effect = ( + test.TestingException) + + connector = {'host': 'fakehost1'} + self.assertRaises(cinder_exception.ClientException, + self.api.initialize_connection, + self.ctx, + 'id1', + connector) + self.assertIsNone(mock_log.error.call_args_list[1][0][1]['code']) + @mock.patch('nova.volume.cinder.cinderclient') def test_initialize_connection_rollback(self, mock_cinderclient): mock_cinderclient.return_value.volumes.\ diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py index 5fcf492580..54a445faef 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -512,13 +512,15 @@ class LibvirtGenericVIFDriver(object): check_exit_code=[0, 1]) if not linux_net.device_exists(v2_name): - linux_net._create_veth_pair(v1_name, v2_name) + mtu = vif['network'].get_meta('mtu') + linux_net._create_veth_pair(v1_name, v2_name, mtu) utils.execute('ip', 'link', 'set', br_name, 'up', run_as_root=True) utils.execute('brctl', 'addif', br_name, v1_name, run_as_root=True) if port == 'ovs': linux_net.create_ovs_vif_port(self.get_bridge_name(vif), v2_name, iface_id, - vif['address'], instance.uuid) + vif['address'], instance.uuid, + mtu) elif port == 'ivs': linux_net.create_ivs_vif_port(v2_name, iface_id, vif['address'], instance.uuid) @@ -660,7 +662,9 @@ class LibvirtGenericVIFDriver(object): dev = self.get_vif_devname(vif) mac = vif['details'].get(network_model.VIF_DETAILS_TAP_MAC_ADDRESS) linux_net.create_tap_dev(dev, mac) - linux_net._set_device_mtu(dev) + network = vif.get('network') + mtu = network.get_meta('mtu') if network else None + linux_net._set_device_mtu(dev, mtu) def plug_vhostuser(self, instance, vif): ovs_plug = vif['details'].get( @@ -670,10 +674,12 @@ class LibvirtGenericVIFDriver(object): iface_id = self.get_ovs_interfaceid(vif) port_name = os.path.basename( vif['details'][network_model.VIF_DETAILS_VHOSTUSER_SOCKET]) - linux_net.create_ovs_vif_port(self.get_bridge_name(vif), - port_name, iface_id, vif['address'], - instance.uuid) - linux_net.ovs_set_vhostuser_port_type(port_name) + mtu = vif['network'].get_meta('mtu') + linux_net.create_ovs_vif_port( + self.get_bridge_name(vif), + port_name, iface_id, vif['address'], + instance.uuid, mtu, + interface_type=network_model.OVS_VHOSTUSER_INTERFACE_TYPE) def plug_vrouter(self, instance, vif): """Plug into Contrail's network port diff --git a/nova/volume/cinder.py b/nova/volume/cinder.py index 5a7c5aa106..e8748d1bc7 100644 --- a/nova/volume/cinder.py +++ b/nova/volume/cinder.py @@ -366,7 +366,8 @@ class API(object): {'vol': volume_id, 'host': connector.get('host'), 'msg': six.text_type(exc), - 'code': exc.code}) + 'code': ( + exc.code if hasattr(exc, 'code') else None)}) @translate_volume_exception def terminate_connection(self, context, volume_id, connector): diff --git a/releasenotes/notes/vhost-user-mtu-23d0af36a8adfa56.yaml b/releasenotes/notes/vhost-user-mtu-23d0af36a8adfa56.yaml new file mode 100644 index 0000000000..642a7d2318 --- /dev/null +++ b/releasenotes/notes/vhost-user-mtu-23d0af36a8adfa56.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - When plugging virtual interfaces of type vhost-user the MTU value will + not be applied to the interface by nova. vhost-user ports exist only in + userspace and are not backed by kernel netdevs, for this reason it is + not possible to set the mtu on a vhost-user interface using standard + tools such as ifconfig or ip link. @@ -1,5 +1,5 @@ [tox] -minversion = 1.8 +minversion = 2.0 envlist = py34,py27,functional,pep8,pip-missing-reqs skipsdist = True @@ -156,6 +156,10 @@ commands = coverage html --include='nova/*' --omit='nova/openstack/common/*' -d covhtml -i [testenv:venv] +# NOTE(jaegerandi): This target does not use constraints because +# upstream infra does not yet support it. Once that's fixed, we can +# drop the install_command. +install_command = pip install -U --force-reinstall {opts} {packages} commands = {posargs} [testenv:docs] |