summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nova/api/openstack/compute/personality.py5
-rw-r--r--nova/cmd/novnc.py8
-rw-r--r--nova/compute/api.py10
-rw-r--r--nova/compute/manager.py13
-rw-r--r--nova/compute/utils.py10
-rw-r--r--nova/exception.py2
-rw-r--r--nova/image/glance.py8
-rw-r--r--nova/network/linux_net.py46
-rw-r--r--nova/network/model.py2
-rw-r--r--nova/network/neutronv2/api.py5
-rw-r--r--nova/objects/monitor_metric.py14
-rw-r--r--nova/tests/functional/regressions/test_bug_1558866.py77
-rw-r--r--nova/tests/unit/api/openstack/compute/test_flavors_extra_specs.py6
-rw-r--r--nova/tests/unit/api/openstack/compute/test_serversV21.py12
-rw-r--r--nova/tests/unit/cmd/test_baseproxy.py2
-rw-r--r--nova/tests/unit/compute/test_compute_api.py4
-rw-r--r--nova/tests/unit/compute/test_compute_mgr.py4
-rw-r--r--nova/tests/unit/compute/test_compute_utils.py17
-rw-r--r--nova/tests/unit/image/test_glance.py39
-rw-r--r--nova/tests/unit/network/test_linux_net.py55
-rw-r--r--nova/tests/unit/network/test_neutronv2.py4
-rw-r--r--nova/tests/unit/objects/test_monitor_metric.py12
-rw-r--r--nova/tests/unit/virt/libvirt/test_vif.py33
-rw-r--r--nova/tests/unit/virt/vmwareapi/fake.py3
-rw-r--r--nova/tests/unit/virt/vmwareapi/test_vm_util.py45
-rw-r--r--nova/tests/unit/volume/test_cinder.py19
-rw-r--r--nova/virt/libvirt/vif.py20
-rw-r--r--nova/virt/vmwareapi/constants.py6
-rw-r--r--nova/virt/vmwareapi/vm_util.py31
-rw-r--r--nova/volume/cinder.py3
-rw-r--r--releasenotes/notes/bug-1559026-47c3fa3468d66b07.yaml6
-rw-r--r--releasenotes/notes/vhost-user-mtu-23d0af36a8adfa56.yaml7
-rw-r--r--requirements.txt6
33 files changed, 427 insertions, 107 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/cmd/novnc.py b/nova/cmd/novnc.py
index dc96e2fac1..0b69e5f3b6 100644
--- a/nova/cmd/novnc.py
+++ b/nova/cmd/novnc.py
@@ -16,9 +16,11 @@
from oslo_config import cfg
opts = [
- cfg.BoolOpt('record',
- default=False,
- help='Record sessions to FILE.[session_number]'),
+ cfg.StrOpt('record',
+ help='This is the filename that will be used for storing '
+ 'websocket frames received and sent by a proxy service '
+ '(like VNC, spice, serial) running on this host. If this '
+ 'is not set (default), no recording will be done.'),
cfg.BoolOpt('daemon',
default=False,
help='Become a daemon (background process)'),
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/image/glance.py b/nova/image/glance.py
index b00c5da169..6344a3cb11 100644
--- a/nova/image/glance.py
+++ b/nova/image/glance.py
@@ -18,6 +18,7 @@
from __future__ import absolute_import
import copy
+import inspect
import itertools
import random
import sys
@@ -227,7 +228,12 @@ class GlanceClientWrapper(object):
client = self.client or self._create_onetime_client(context,
version)
try:
- return getattr(client.images, method)(*args, **kwargs)
+ result = getattr(client.images, method)(*args, **kwargs)
+ if inspect.isgenerator(result):
+ # Convert generator results to a list, so that we can
+ # catch any potential exceptions now and retry the call.
+ return list(result)
+ return result
except retry_excs as e:
host = self.host
port = self.port
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/objects/monitor_metric.py b/nova/objects/monitor_metric.py
index 7d6c350a41..0ad599b366 100644
--- a/nova/objects/monitor_metric.py
+++ b/nova/objects/monitor_metric.py
@@ -97,8 +97,18 @@ class MonitorMetricList(base.ObjectListBase, base.NovaObject):
:returns: a MonitorMetricList Object.
"""
metrics = jsonutils.loads(metrics) if metrics else []
- metric_list = [
- MonitorMetric(**metric) for metric in metrics]
+
+ # NOTE(suro-patz): While instantiating the MonitorMetric() from
+ # JSON-ified string, we need to re-convert the
+ # normalized metrics to avoid truncation to 0 by
+ # typecasting into an integer.
+ metric_list = []
+ for metric in metrics:
+ if ('value' in metric and metric['name'] in
+ FIELDS_REQUIRING_CONVERSION):
+ metric['value'] = metric['value'] * 100
+ metric_list.append(MonitorMetric(**metric))
+
return MonitorMetricList(objects=metric_list)
# NOTE(jaypipes): This method exists to convert the object to the
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/cmd/test_baseproxy.py b/nova/tests/unit/cmd/test_baseproxy.py
index d3cec2fc12..38421f7a46 100644
--- a/nova/tests/unit/cmd/test_baseproxy.py
+++ b/nova/tests/unit/cmd/test_baseproxy.py
@@ -61,7 +61,7 @@ class BaseProxyTestCase(test.NoDBTestCase):
mock_init.assert_called_once_with(
listen_host='0.0.0.0', listen_port='6080', source_is_ipv6=False,
verbose=False, cert='self.pem', key=None, ssl_only=False,
- daemon=False, record=False, traffic=False,
+ daemon=False, record=None, traffic=False,
web='/usr/share/spice-html5', file_only=True,
RequestHandlerClass=websocketproxy.NovaProxyRequestHandler)
mock_start.assert_called_once_with()
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/image/test_glance.py b/nova/tests/unit/image/test_glance.py
index 15ab0674ad..59bed419ae 100644
--- a/nova/tests/unit/image/test_glance.py
+++ b/nova/tests/unit/image/test_glance.py
@@ -415,6 +415,45 @@ class TestGlanceClientWrapper(test.NoDBTestCase):
)
sleep_mock.assert_called_once_with(1)
+ @mock.patch('random.shuffle')
+ @mock.patch('time.sleep')
+ @mock.patch('nova.image.glance._create_glance_client')
+ def test_retry_works_with_generators(self, create_client_mock,
+ sleep_mock, shuffle_mock):
+ def some_generator(exception):
+ if exception:
+ raise glanceclient.exc.CommunicationError('Boom!')
+ yield 'something'
+
+ api_servers = [
+ 'host1:9292',
+ 'https://host2:9293',
+ 'http://host3:9294'
+ ]
+ client_mock = mock.MagicMock()
+ images_mock = mock.MagicMock()
+ images_mock.list.side_effect = [
+ some_generator(exception=True),
+ some_generator(exception=False),
+ ]
+ type(client_mock).images = mock.PropertyMock(return_value=images_mock)
+ create_client_mock.return_value = client_mock
+
+ self.flags(num_retries=1, group='glance')
+ self.flags(api_servers=api_servers, group='glance')
+
+ ctx = context.RequestContext('fake', 'fake')
+ client = glance.GlanceClientWrapper()
+ client.call(ctx, 1, 'list', 'meow')
+
+ create_client_mock.assert_has_calls(
+ [
+ mock.call(ctx, 'host1', 9292, False, 1),
+ mock.call(ctx, 'host2', 9293, True, 1),
+ ]
+ )
+ sleep_mock.assert_called_once_with(1)
+
@mock.patch('oslo_service.sslutils.is_enabled')
@mock.patch('glanceclient.Client')
def test_create_glance_client_with_ssl(self, client_mock,
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/objects/test_monitor_metric.py b/nova/tests/unit/objects/test_monitor_metric.py
index 30cea2ded7..a1df3ef2f0 100644
--- a/nova/tests/unit/objects/test_monitor_metric.py
+++ b/nova/tests/unit/objects/test_monitor_metric.py
@@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from oslo_serialization import jsonutils
from oslo_utils import timeutils
from nova import objects
@@ -73,6 +74,17 @@ class _TestMonitorMetricObject(object):
source='nova.virt.libvirt.driver')
self.assertEqual(_monitor_numa_metric_spec, obj.to_dict())
+ def test_conversion_in_monitor_metric_list_from_json(self):
+ spec_list = [_monitor_metric_spec, _monitor_metric_perc_spec]
+ metrics = objects.MonitorMetricList.from_json(
+ jsonutils.dumps(spec_list))
+ for metric, spec in zip(metrics, spec_list):
+ exp = spec['value']
+ if (spec['name'] in
+ objects.monitor_metric.FIELDS_REQUIRING_CONVERSION):
+ exp = spec['value'] * 100
+ self.assertEqual(exp, metric.value)
+
class TestMonitorMetricObject(test_objects._LocalTest,
_TestMonitorMetricObject):
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/virt/vmwareapi/fake.py b/nova/tests/unit/virt/vmwareapi/fake.py
index 72ecd1cec4..ad375f65ba 100644
--- a/nova/tests/unit/virt/vmwareapi/fake.py
+++ b/nova/tests/unit/virt/vmwareapi/fake.py
@@ -370,8 +370,9 @@ class VirtualIDEController(DataObject):
class VirtualLsiLogicController(DataObject):
"""VirtualLsiLogicController class."""
- def __init__(self, key=0, scsiCtlrUnitNumber=0):
+ def __init__(self, key=0, scsiCtlrUnitNumber=0, busNumber=0):
self.key = key
+ self.busNumber = busNumber
self.scsiCtlrUnitNumber = scsiCtlrUnitNumber
self.device = []
diff --git a/nova/tests/unit/virt/vmwareapi/test_vm_util.py b/nova/tests/unit/virt/vmwareapi/test_vm_util.py
index d4930965c2..c20fa6de9d 100644
--- a/nova/tests/unit/virt/vmwareapi/test_vm_util.py
+++ b/nova/tests/unit/virt/vmwareapi/test_vm_util.py
@@ -214,6 +214,13 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
self.assertEqual("ns0:ParaVirtualSCSIController",
config_spec.device.obj_name)
+ def test_create_controller_spec_with_specfic_bus_number(self):
+ # Test controller spec with specifc bus number rather default 0
+ config_spec = vm_util.create_controller_spec(fake.FakeFactory(), -101,
+ adapter_type=constants.ADAPTER_TYPE_LSILOGICSAS,
+ bus_number=1)
+ self.assertEqual(1, config_spec.device.busNumber)
+
def _vmdk_path_and_adapter_type_devices(self, filename, parent=None):
# Test the adapter_type returned for a lsiLogic sas controller
controller_key = 1000
@@ -332,6 +339,27 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
self.assertEqual([1], taken[201])
self.assertEqual([7], taken[1000])
+ def test_get_bus_number_for_scsi_controller(self):
+ devices = [fake.VirtualLsiLogicController(1000, scsiCtlrUnitNumber=7,
+ busNumber=0),
+ fake.VirtualLsiLogicController(1002, scsiCtlrUnitNumber=7,
+ busNumber=2)]
+ bus_number = vm_util._get_bus_number_for_scsi_controller(devices)
+ self.assertEqual(1, bus_number)
+
+ def test_get_bus_number_for_scsi_controller_buses_used_up(self):
+ devices = [fake.VirtualLsiLogicController(1000, scsiCtlrUnitNumber=7,
+ busNumber=0),
+ fake.VirtualLsiLogicController(1001, scsiCtlrUnitNumber=7,
+ busNumber=1),
+ fake.VirtualLsiLogicController(1002, scsiCtlrUnitNumber=7,
+ busNumber=2),
+ fake.VirtualLsiLogicController(1003, scsiCtlrUnitNumber=7,
+ busNumber=3)]
+ self.assertRaises(vexc.VMwareDriverException,
+ vm_util._get_bus_number_for_scsi_controller,
+ devices)
+
def test_allocate_controller_key_and_unit_number_ide_default(self):
# Test that default IDE controllers are used when there is a free slot
# on them
@@ -386,6 +414,23 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
self.assertEqual(8, unit_number)
self.assertIsNone(controller_spec)
+ def test_allocate_controller_key_and_unit_number_scsi_new_controller(self):
+ # Test that we allocate on existing SCSI controller if there is a free
+ # slot on it
+ devices = [fake.VirtualLsiLogicController(1000, scsiCtlrUnitNumber=15)]
+ for unit_number in range(15):
+ disk = fake.VirtualDisk(1000, unit_number)
+ devices.append(disk)
+ factory = fake.FakeFactory()
+ (controller_key, unit_number,
+ controller_spec) = vm_util.allocate_controller_key_and_unit_number(
+ factory,
+ devices,
+ constants.DEFAULT_ADAPTER_TYPE)
+ self.assertEqual(-101, controller_key)
+ self.assertEqual(0, unit_number)
+ self.assertEqual(1, controller_spec.device.busNumber)
+
def test_get_vnc_config_spec(self):
fake_factory = fake.FakeFactory()
result = vm_util.get_vnc_config_spec(fake_factory,
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/virt/vmwareapi/constants.py b/nova/virt/vmwareapi/constants.py
index 7842b17d9e..5d76ba61b1 100644
--- a/nova/virt/vmwareapi/constants.py
+++ b/nova/virt/vmwareapi/constants.py
@@ -54,6 +54,9 @@ ADAPTER_TYPE_IDE = "ide"
ADAPTER_TYPE_LSILOGICSAS = "lsiLogicsas"
ADAPTER_TYPE_PARAVIRTUAL = "paraVirtual"
+SCSI_ADAPTER_TYPES = [DEFAULT_ADAPTER_TYPE, ADAPTER_TYPE_LSILOGICSAS,
+ ADAPTER_TYPE_BUSLOGIC, ADAPTER_TYPE_PARAVIRTUAL]
+
SUPPORTED_FLAT_VARIANTS = ["thin", "preallocated", "thick", "eagerZeroedThick"]
EXTENSION_KEY = 'org.openstack.compute'
@@ -63,6 +66,9 @@ EXTENSION_TYPE_INSTANCE = 'instance'
# One adapter has 16 slots but one reserved for controller
SCSI_MAX_CONNECT_NUMBER = 15
+# The max number of SCSI adaptors that could be created on one instance.
+SCSI_MAX_CONTROLLER_NUMBER = 4
+
# This list was extracted from the installation iso image for ESX 6.0.
# It is contained in s.v00, which is gzipped. The list was obtained by
# searching for the string 'otherGuest' in the uncompressed contents of that
diff --git a/nova/virt/vmwareapi/vm_util.py b/nova/virt/vmwareapi/vm_util.py
index 5e19cb0a97..bf0782fd23 100644
--- a/nova/virt/vmwareapi/vm_util.py
+++ b/nova/virt/vmwareapi/vm_util.py
@@ -355,7 +355,8 @@ def get_vm_resize_spec(client_factory, vcpus, memory_mb, extra_specs,
def create_controller_spec(client_factory, key,
- adapter_type=constants.DEFAULT_ADAPTER_TYPE):
+ adapter_type=constants.DEFAULT_ADAPTER_TYPE,
+ bus_number=0):
"""Builds a Config Spec for the LSI or Bus Logic Controller's addition
which acts as the controller for the virtual hard disk to be attached
to the VM.
@@ -377,7 +378,7 @@ def create_controller_spec(client_factory, key,
virtual_controller = client_factory.create(
'ns0:VirtualLsiLogicController')
virtual_controller.key = key
- virtual_controller.busNumber = 0
+ virtual_controller.busNumber = bus_number
virtual_controller.sharedBus = "noSharing"
virtual_device_config.device = virtual_controller
return virtual_device_config
@@ -713,6 +714,19 @@ def _find_allocated_slots(devices):
return taken
+def _get_bus_number_for_scsi_controller(devices):
+ """Return usable bus number when create new SCSI controller."""
+ # Every SCSI controller will take a unique bus number
+ taken = [dev.busNumber for dev in devices if _is_scsi_controller(dev)]
+ # The max bus number for SCSI controllers is 3
+ for i in range(constants.SCSI_MAX_CONTROLLER_NUMBER):
+ if i not in taken:
+ return i
+ msg = _('Only %d SCSI controllers are allowed to be '
+ 'created on this instance.') % constants.SCSI_MAX_CONTROLLER_NUMBER
+ raise vexc.VMwareDriverException(msg)
+
+
def allocate_controller_key_and_unit_number(client_factory, devices,
adapter_type):
"""This function inspects the current set of hardware devices and returns
@@ -728,10 +742,7 @@ def allocate_controller_key_and_unit_number(client_factory, devices,
if adapter_type == constants.ADAPTER_TYPE_IDE:
ide_keys = [dev.key for dev in devices if _is_ide_controller(dev)]
ret = _find_controller_slot(ide_keys, taken, 2)
- elif adapter_type in [constants.DEFAULT_ADAPTER_TYPE,
- constants.ADAPTER_TYPE_LSILOGICSAS,
- constants.ADAPTER_TYPE_BUSLOGIC,
- constants.ADAPTER_TYPE_PARAVIRTUAL]:
+ elif adapter_type in constants.SCSI_ADAPTER_TYPES:
scsi_keys = [dev.key for dev in devices if _is_scsi_controller(dev)]
ret = _find_controller_slot(scsi_keys, taken, 16)
if ret:
@@ -739,8 +750,14 @@ def allocate_controller_key_and_unit_number(client_factory, devices,
# create new controller with the specified type and return its spec
controller_key = -101
+
+ # Get free bus number for new SCSI controller.
+ bus_number = 0
+ if adapter_type in constants.SCSI_ADAPTER_TYPES:
+ bus_number = _get_bus_number_for_scsi_controller(devices)
+
controller_spec = create_controller_spec(client_factory, controller_key,
- adapter_type)
+ adapter_type, bus_number)
return controller_key, 0, controller_spec
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/bug-1559026-47c3fa3468d66b07.yaml b/releasenotes/notes/bug-1559026-47c3fa3468d66b07.yaml
new file mode 100644
index 0000000000..62543bf945
--- /dev/null
+++ b/releasenotes/notes/bug-1559026-47c3fa3468d66b07.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+ - The ``record`` configuration option for the console proxy
+ services (like VNC, serial, spice) is changed from
+ boolean to string. It specifies the filename that will
+ be used for recording websocket frames.
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.
diff --git a/requirements.txt b/requirements.txt
index 494b09d0aa..796cf5fe6c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,8 +10,8 @@ eventlet>=0.17.4
Jinja2>=2.6 # BSD License (3 clause)
keystonemiddleware!=2.4.0,>=2.0.0
lxml>=2.3
-Routes!=2.0,!=2.1,>=1.12.3;python_version=='2.7'
-Routes!=2.0,>=1.12.3;python_version!='2.7'
+Routes!=2.0,!=2.1,!=2.3.0,>=1.12.3;python_version=='2.7' # MIT
+Routes!=2.0,!=2.3.0,>=1.12.3;python_version!='2.7' # MIT
cryptography>=1.0 # Apache-2.0
WebOb>=1.2.3
greenlet>=0.3.2
@@ -22,7 +22,7 @@ sqlalchemy-migrate>=0.9.6
netaddr!=0.7.16,>=0.7.12
netifaces>=0.10.4
paramiko>=1.13.0
-Babel>=1.3
+Babel!=2.3.0,!=2.3.1,!=2.3.2,!=2.3.3,>=1.3 # BSD
iso8601>=0.1.9
jsonschema!=2.5.0,<3.0.0,>=2.0.0
python-cinderclient>=1.3.1