summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.zuul.yaml5
-rw-r--r--doc/source/admin/upgrades.rst20
-rw-r--r--nova/compute/manager.py3
-rw-r--r--nova/network/neutron.py21
-rw-r--r--nova/objects/service.py23
-rw-r--r--nova/pci/request.py4
-rw-r--r--nova/pci/stats.py2
-rw-r--r--nova/pci/whitelist.py2
-rw-r--r--nova/scheduler/client/report.py2
-rw-r--r--nova/scheduler/manager.py35
-rw-r--r--nova/scheduler/utils.py11
-rw-r--r--nova/test.py7
-rw-r--r--nova/tests/fixtures/nova.py26
-rw-r--r--nova/tests/functional/libvirt/test_pci_sriov_servers.py41
-rw-r--r--nova/tests/functional/libvirt/test_power_manage.py6
-rw-r--r--nova/tests/functional/test_servers.py38
-rw-r--r--nova/tests/unit/compute/test_shelve.py6
-rw-r--r--nova/tests/unit/scheduler/test_manager.py36
-rw-r--r--nova/tests/unit/virt/libvirt/test_driver.py3
-rw-r--r--nova/tests/unit/virt/test_hardware.py21
-rw-r--r--nova/virt/hardware.py18
-rw-r--r--nova/virt/libvirt/cpu/__init__.py22
-rw-r--r--nova/virt/libvirt/driver.py24
-rw-r--r--nova/virt/libvirt/event.py7
-rw-r--r--nova/virt/libvirt/utils.py6
-rw-r--r--releasenotes/notes/antelope-prelude-4a99907b00e739f8.yaml51
-rw-r--r--releasenotes/notes/port-binding-removed-shelved-offloaded-f1772a64be007b24.yaml6
-rw-r--r--releasenotes/source/2023.1.rst6
-rw-r--r--releasenotes/source/index.rst1
-rw-r--r--releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po89
-rw-r--r--tox.ini2
31 files changed, 400 insertions, 144 deletions
diff --git a/.zuul.yaml b/.zuul.yaml
index 29918cafc8..abe4d2fa4a 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -419,6 +419,7 @@
# Added in Yoga.
NOVNC_FROM_PACKAGE: False
NOVA_USE_UNIFIED_LIMITS: True
+ MYSQL_REDUCE_MEMORY: True
devstack_services:
# Disable OVN services
br-ex-tcpdump: false
@@ -756,7 +757,7 @@
irrelevant-files: *policies-irrelevant-files
- tempest-integrated-compute-enforce-scope-new-defaults:
irrelevant-files: *policies-irrelevant-files
- - grenade-skip-level:
+ - grenade-skip-level-always:
irrelevant-files: *policies-irrelevant-files
- nova-grenade-multinode:
irrelevant-files: *policies-irrelevant-files
@@ -793,6 +794,8 @@
irrelevant-files: *policies-irrelevant-files
- tempest-integrated-compute-enforce-scope-new-defaults:
irrelevant-files: *policies-irrelevant-files
+ - grenade-skip-level-always:
+ irrelevant-files: *policies-irrelevant-files
- nova-grenade-multinode:
irrelevant-files: *policies-irrelevant-files
- tempest-ipv6-only:
diff --git a/doc/source/admin/upgrades.rst b/doc/source/admin/upgrades.rst
index 00a714970b..61fd0cf258 100644
--- a/doc/source/admin/upgrades.rst
+++ b/doc/source/admin/upgrades.rst
@@ -41,21 +41,27 @@ Rolling upgrade process
To reduce downtime, the compute services can be upgraded in a rolling fashion.
It means upgrading a few services at a time. This results in a condition where
both old (N) and new (N+1) nova-compute services co-exist for a certain time
-period. Note that, there is no upgrade of the hypervisor here, this is just
+period (or even N with N+2 upgraded nova-compute services, see below).
+Note that, there is no upgrade of the hypervisor here, this is just
upgrading the nova services. If reduced downtime is not a concern (or lower
complexity is desired), all services may be taken down and restarted at the
same time.
.. important::
- Nova does not currently support the coexistence of N and N+2 or greater
- :program:`nova-compute` or :program:`nova-conductor` services in the same
- deployment. The `nova-conductor`` service will fail to start when a
- ``nova-compute`` service that is older than the previous release (N-2 or
- greater) is detected. Similarly, in a :doc:`deployment with multiple cells
+ As of OpenStack 2023.1 (Antelope), Nova supports the coexistence of N and
+ N-2 (Yoga) :program:`nova-compute` or :program:`nova-conductor` services in
+ the same deployment. The `nova-conductor`` service will fail to start when
+ a ``nova-compute`` service that is older than the support envelope is
+ detected. This varies by release and the support envelope will be explained
+ in the release notes. Similarly, in a :doc:`deployment with multiple cells
</admin/cells>`, neither the super conductor service nor any per-cell
conductor service will start if any other conductor service in the
- deployment is older than the previous release.
+ deployment is older than the N-2 release.
+
+ Releases older than 2023.1 will only support rolling upgrades for a single
+ release difference between :program:`nova-compute` and
+ :program:`nova-conductor` services.
#. Before maintenance window:
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index efcdece81a..5ea71827fc 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -6874,6 +6874,9 @@ class ComputeManager(manager.Manager):
current_power_state = self._get_power_state(instance)
network_info = self.network_api.get_instance_nw_info(context, instance)
+ ports_id = [vif['id'] for vif in network_info]
+ self.network_api.unbind_ports(context, ports_id, detach=False)
+
block_device_info = self._get_instance_block_device_info(context,
instance,
bdms=bdms)
diff --git a/nova/network/neutron.py b/nova/network/neutron.py
index 27e7d06455..6c9f19f010 100644
--- a/nova/network/neutron.py
+++ b/nova/network/neutron.py
@@ -612,10 +612,22 @@ class API:
raise exception.ExternalNetworkAttachForbidden(
network_uuid=net['id'])
+ def unbind_ports(self, context, ports, detach=True):
+ """Unbind and detach the given ports by clearing their
+ device_owner and dns_name.
+ The device_id will also be cleaned if detach=True.
+
+ :param context: The request context.
+ :param ports: list of port IDs.
+ """
+ neutron = get_client(context)
+ self._unbind_ports(context, ports, neutron, detach=detach)
+
def _unbind_ports(self, context, ports,
- neutron, port_client=None):
- """Unbind the given ports by clearing their device_id,
+ neutron, port_client=None, detach=True):
+ """Unbind and detach the given ports by clearing their
device_owner and dns_name.
+ The device_id will also be cleaned if detach=True.
:param context: The request context.
:param ports: list of port IDs.
@@ -638,11 +650,12 @@ class API:
port_req_body: ty.Dict[str, ty.Any] = {
'port': {
- 'device_id': '',
- 'device_owner': '',
constants.BINDING_HOST_ID: None,
}
}
+ if detach:
+ port_req_body['port']['device_id'] = ''
+ port_req_body['port']['device_owner'] = ''
try:
port = self._show_port(
context, port_id, neutron_client=neutron,
diff --git a/nova/objects/service.py b/nova/objects/service.py
index 0ed443ef17..1a4629cc84 100644
--- a/nova/objects/service.py
+++ b/nova/objects/service.py
@@ -237,15 +237,30 @@ SERVICE_VERSION_HISTORY = (
# local node identity for single-node systems.
NODE_IDENTITY_VERSION = 65
-# This is used to raise an error at service startup if older than N-1 computes
-# are detected. Update this at the beginning of every release cycle to point to
-# the smallest service version that was added in N-1.
-OLDEST_SUPPORTED_SERVICE_VERSION = 'Yoga'
+# This is used to raise an error at service startup if older than supported
+# computes are detected.
+# NOTE(sbauza) : Please modify it this way :
+# * At the beginning of a non-SLURP release (eg. 2023.2 Bobcat) (or just after
+# the previous SLURP release RC1, like 2023.1 Antelope), please bump
+# OLDEST_SUPPORTED_SERVICE_VERSION to the previous SLURP release (in that
+# example, Antelope)
+# * At the beginning of a SLURP release (eg. 2024.1 C) (or just after the
+# previous non-SLURP release RC1, like 2023.2 Bobcat), please keep the
+# OLDEST_SUPPORTED_SERVICE_VERSION value using the previous SLURP release
+# (in that example, Antelope)
+# * At the end of any release (SLURP or non-SLURP), please modify
+# SERVICE_VERSION_ALIASES to add a key/value with key being the release name
+# and value be the latest service version that the release supports (for
+# example, before Bobcat RC1, please add 'Bobcat': XX where X is the latest
+# servion version that was added)
+OLDEST_SUPPORTED_SERVICE_VERSION = 'Antelope'
SERVICE_VERSION_ALIASES = {
'Victoria': 52,
'Wallaby': 54,
'Xena': 57,
'Yoga': 61,
+ 'Zed': 64,
+ 'Antelope': 66,
}
diff --git a/nova/pci/request.py b/nova/pci/request.py
index 27ada6c045..8ae2385549 100644
--- a/nova/pci/request.py
+++ b/nova/pci/request.py
@@ -168,7 +168,7 @@ def _get_alias_from_config() -> Alias:
def _translate_alias_to_requests(
- alias_spec: str, affinity_policy: str = None,
+ alias_spec: str, affinity_policy: ty.Optional[str] = None,
) -> ty.List['objects.InstancePCIRequest']:
"""Generate complete pci requests from pci aliases in extra_spec."""
pci_aliases = _get_alias_from_config()
@@ -255,7 +255,7 @@ def get_instance_pci_request_from_vif(
def get_pci_requests_from_flavor(
- flavor: 'objects.Flavor', affinity_policy: str = None,
+ flavor: 'objects.Flavor', affinity_policy: ty.Optional[str] = None,
) -> 'objects.InstancePCIRequests':
"""Validate and return PCI requests.
diff --git a/nova/pci/stats.py b/nova/pci/stats.py
index 5c5f7c669c..c6e4844b34 100644
--- a/nova/pci/stats.py
+++ b/nova/pci/stats.py
@@ -82,7 +82,7 @@ class PciDeviceStats(object):
self,
numa_topology: 'objects.NUMATopology',
stats: 'objects.PCIDevicePoolList' = None,
- dev_filter: whitelist.Whitelist = None,
+ dev_filter: ty.Optional[whitelist.Whitelist] = None,
) -> None:
self.numa_topology = numa_topology
self.pools = (
diff --git a/nova/pci/whitelist.py b/nova/pci/whitelist.py
index 8862a0ef4f..152cc29ca6 100644
--- a/nova/pci/whitelist.py
+++ b/nova/pci/whitelist.py
@@ -33,7 +33,7 @@ class Whitelist(object):
assignable.
"""
- def __init__(self, whitelist_spec: str = None) -> None:
+ def __init__(self, whitelist_spec: ty.Optional[str] = None) -> None:
"""White list constructor
For example, the following json string specifies that devices whose
diff --git a/nova/scheduler/client/report.py b/nova/scheduler/client/report.py
index 1242752be1..7c14f3d7ef 100644
--- a/nova/scheduler/client/report.py
+++ b/nova/scheduler/client/report.py
@@ -1047,7 +1047,7 @@ class SchedulerReportClient(object):
context: nova_context.RequestContext,
rp_uuid: str,
traits: ty.Iterable[str],
- generation: int = None
+ generation: ty.Optional[int] = None
):
"""Replace a provider's traits with those specified.
diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py
index 11581c4f2d..620519d403 100644
--- a/nova/scheduler/manager.py
+++ b/nova/scheduler/manager.py
@@ -23,6 +23,7 @@ import collections
import copy
import random
+from keystoneauth1 import exceptions as ks_exc
from oslo_log import log as logging
import oslo_messaging as messaging
from oslo_serialization import jsonutils
@@ -67,10 +68,42 @@ class SchedulerManager(manager.Manager):
self.host_manager = host_manager.HostManager()
self.servicegroup_api = servicegroup.API()
self.notifier = rpc.get_notifier('scheduler')
- self.placement_client = report.report_client_singleton()
+ self._placement_client = None
+
+ try:
+ # Test our placement client during initialization
+ self.placement_client
+ except (ks_exc.EndpointNotFound,
+ ks_exc.DiscoveryFailure,
+ ks_exc.RequestTimeout,
+ ks_exc.GatewayTimeout,
+ ks_exc.ConnectFailure) as e:
+ # Non-fatal, likely transient (although not definitely);
+ # continue startup but log the warning so that when things
+ # fail later, it will be clear why we can not do certain
+ # things.
+ LOG.warning('Unable to initialize placement client (%s); '
+ 'Continuing with startup, but scheduling '
+ 'will not be possible.', e)
+ except (ks_exc.MissingAuthPlugin,
+ ks_exc.Unauthorized) as e:
+ # This is almost definitely fatal mis-configuration. The
+ # Unauthorized error might be transient, but it is
+ # probably reasonable to consider it fatal.
+ LOG.error('Fatal error initializing placement client; '
+ 'config is incorrect or incomplete: %s', e)
+ raise
+ except Exception as e:
+ # Unknown/unexpected errors here are fatal
+ LOG.error('Fatal error initializing placement client: %s', e)
+ raise
super().__init__(service_name='scheduler', *args, **kwargs)
+ @property
+ def placement_client(self):
+ return report.report_client_singleton()
+
@periodic_task.periodic_task(
spacing=CONF.scheduler.discover_hosts_in_cells_interval,
run_immediately=True)
diff --git a/nova/scheduler/utils.py b/nova/scheduler/utils.py
index c7e6ffed97..02c44093bd 100644
--- a/nova/scheduler/utils.py
+++ b/nova/scheduler/utils.py
@@ -1080,6 +1080,17 @@ _SUPPORTS_SOFT_AFFINITY = None
_SUPPORTS_SOFT_ANTI_AFFINITY = None
+def reset_globals():
+ global _SUPPORTS_AFFINITY
+ _SUPPORTS_AFFINITY = None
+ global _SUPPORTS_ANTI_AFFINITY
+ _SUPPORTS_ANTI_AFFINITY = None
+ global _SUPPORTS_SOFT_AFFINITY
+ _SUPPORTS_SOFT_AFFINITY = None
+ global _SUPPORTS_SOFT_ANTI_AFFINITY
+ _SUPPORTS_SOFT_ANTI_AFFINITY = None
+
+
def _get_group_details(context, instance_uuid, user_group_hosts=None):
"""Provide group_hosts and group_policies sets related to instances if
those instances are belonging to a group and if corresponding filters are
diff --git a/nova/test.py b/nova/test.py
index 0f7965ea33..e37967b06d 100644
--- a/nova/test.py
+++ b/nova/test.py
@@ -62,6 +62,7 @@ from nova import objects
from nova.objects import base as objects_base
from nova import quota
from nova.scheduler.client import report
+from nova.scheduler import utils as scheduler_utils
from nova.tests import fixtures as nova_fixtures
from nova.tests.unit import matchers
from nova import utils
@@ -310,6 +311,12 @@ class TestCase(base.BaseTestCase):
if self.STUB_COMPUTE_ID:
self.useFixture(nova_fixtures.ComputeNodeIdFixture())
+ # Reset globals indicating affinity filter support. Some tests may set
+ # self.flags(enabled_filters=...) which could make the affinity filter
+ # support globals get set to a non-default configuration which affects
+ # all other tests.
+ scheduler_utils.reset_globals()
+
def _setup_cells(self):
"""Setup a normal cellsv2 environment.
diff --git a/nova/tests/fixtures/nova.py b/nova/tests/fixtures/nova.py
index 9a652c02cb..5fd893e7dc 100644
--- a/nova/tests/fixtures/nova.py
+++ b/nova/tests/fixtures/nova.py
@@ -1822,24 +1822,6 @@ class ImportModulePoisonFixture(fixtures.Fixture):
def find_spec(self, fullname, path, target=None):
if fullname in self.modules:
- current = eventlet.getcurrent()
- # NOTE(gibi) not all eventlet spawn is under our control, so
- # there can be senders without test_case_id set, find the first
- # ancestor that was spawned from nova.utils.spawn[_n] and
- # therefore has the id set.
- while (
- current is not None and
- not getattr(current, 'test_case_id', None)
- ):
- current = current.parent
-
- if current is not None:
- self.test.tc_id = current.test_case_id
- LOG.warning(
- "!!!---!!! TestCase ID %s hit the import poison while "
- "importing %s. If you see this in a failed functional "
- "test then please let #openstack-nova on IRC know "
- "about it. !!!---!!!", current.test_case_id, fullname)
self.test.fail_message = (
f"This test imports the '{fullname}' module, which it "
f'should not in the test environment. Please add '
@@ -1850,7 +1832,6 @@ class ImportModulePoisonFixture(fixtures.Fixture):
def __init__(self, module_names):
self.module_names = module_names
self.fail_message = ''
- self.tc_id = None
if isinstance(module_names, str):
self.module_names = {module_names}
self.meta_path_finder = self.ForbiddenModules(self, self.module_names)
@@ -1868,13 +1849,6 @@ class ImportModulePoisonFixture(fixtures.Fixture):
# there (which is also what self.assert* and self.fail() do underneath)
# will not work to cause a failure in the test.
if self.fail_message:
- if self.tc_id is not None:
- LOG.warning(
- "!!!---!!! TestCase ID %s hit the import poison. If you "
- "see this in a failed functional test then please let "
- "#openstack-nova on IRC know about it. !!!---!!!",
- self.tc_id
- )
raise ImportError(self.fail_message)
diff --git a/nova/tests/functional/libvirt/test_pci_sriov_servers.py b/nova/tests/functional/libvirt/test_pci_sriov_servers.py
index 6b8b254af9..098a0e857b 100644
--- a/nova/tests/functional/libvirt/test_pci_sriov_servers.py
+++ b/nova/tests/functional/libvirt/test_pci_sriov_servers.py
@@ -1549,7 +1549,11 @@ class VDPAServersTest(_PCIServersWithMigrationTestBase):
'not supported for instance with vDPA ports',
ex.response.text)
+ # NOTE(sbauza): Now we're post-Antelope release, we don't need to support
+ # this test
def test_attach_interface_service_version_61(self):
+ self.flags(disable_compute_service_check_for_ffu=True,
+ group='workarounds')
with mock.patch(
"nova.objects.service.get_minimum_version_all_cells",
return_value=61
@@ -1578,7 +1582,11 @@ class VDPAServersTest(_PCIServersWithMigrationTestBase):
self.assertEqual(hostname, port['binding:host_id'])
self.assertEqual(server['id'], port['device_id'])
+ # NOTE(sbauza): Now we're post-Antelope release, we don't need to support
+ # this test
def test_detach_interface_service_version_61(self):
+ self.flags(disable_compute_service_check_for_ffu=True,
+ group='workarounds')
with mock.patch(
"nova.objects.service.get_minimum_version_all_cells",
return_value=61
@@ -1612,10 +1620,7 @@ class VDPAServersTest(_PCIServersWithMigrationTestBase):
# to any host but should still be owned by the vm
port = self.neutron.show_port(vdpa_port['id'])['port']
self.assertEqual(server['id'], port['device_id'])
- # FIXME(sean-k-mooney): we should be unbinding the port from
- # the host when we shelve offload but we don't today.
- # This is unrelated to vdpa port and is a general issue.
- self.assertEqual(hostname, port['binding:host_id'])
+ self.assertIsNone(port['binding:host_id'])
self.assertIn('binding:profile', port)
self.assertIsNone(server['OS-EXT-SRV-ATTR:hypervisor_hostname'])
self.assertIsNone(server['OS-EXT-SRV-ATTR:host'])
@@ -1637,9 +1642,7 @@ class VDPAServersTest(_PCIServersWithMigrationTestBase):
self.assertPCIDeviceCounts(hostname, total=num_pci, free=num_pci)
self.assertIsNone(server['OS-EXT-SRV-ATTR:hypervisor_hostname'])
port = self.neutron.show_port(vdpa_port['id'])['port']
- # FIXME(sean-k-mooney): shelve offload should unbind the port
- # self.assertEqual('', port['binding:host_id'])
- self.assertEqual(hostname, port['binding:host_id'])
+ self.assertIsNone(port['binding:host_id'])
server = self._unshelve_server(server)
self.assertPCIDeviceCounts(hostname, total=num_pci, free=num_pci - 2)
@@ -1670,9 +1673,7 @@ class VDPAServersTest(_PCIServersWithMigrationTestBase):
self.assertPCIDeviceCounts(source, total=num_pci, free=num_pci)
self.assertIsNone(server['OS-EXT-SRV-ATTR:hypervisor_hostname'])
port = self.neutron.show_port(vdpa_port['id'])['port']
- # FIXME(sean-k-mooney): shelve should unbind the port
- # self.assertEqual('', port['binding:host_id'])
- self.assertEqual(source, port['binding:host_id'])
+ self.assertIsNone(port['binding:host_id'])
# force the unshelve to the other host
self.api.put_service(
@@ -1871,7 +1872,11 @@ class VDPAServersTest(_PCIServersWithMigrationTestBase):
self.assertEqual(
dest, server['OS-EXT-SRV-ATTR:hypervisor_hostname'])
+ # NOTE(sbauza): Now we're post-Antelope release, we don't need to support
+ # this test
def test_suspend_and_resume_service_version_62(self):
+ self.flags(disable_compute_service_check_for_ffu=True,
+ group='workarounds')
with mock.patch(
"nova.objects.service.get_minimum_version_all_cells",
return_value=62
@@ -1890,7 +1895,11 @@ class VDPAServersTest(_PCIServersWithMigrationTestBase):
self.assertPCIDeviceCounts(source, total=num_pci, free=num_pci - 2)
self.assertEqual('ACTIVE', server['status'])
+ # NOTE(sbauza): Now we're post-Antelope release, we don't need to support
+ # this test
def test_live_migrate_service_version_62(self):
+ self.flags(disable_compute_service_check_for_ffu=True,
+ group='workarounds')
with mock.patch(
"nova.objects.service.get_minimum_version_all_cells",
return_value=62
@@ -3926,17 +3935,7 @@ class RemoteManagedServersTest(_PCIServersWithMigrationTestBase):
port = self.neutron.show_port(uuids.dpu_tunnel_port)['port']
self.assertIn('binding:profile', port)
- self.assertEqual(
- {
- 'pci_vendor_info': '15b3:101e',
- 'pci_slot': '0000:82:00.4',
- 'physical_network': None,
- 'pf_mac_address': '52:54:00:1e:59:02',
- 'vf_num': 3,
- 'card_serial_number': 'MT0000X00002',
- },
- port['binding:profile'],
- )
+ self.assertEqual({}, port['binding:profile'])
def test_suspend(self):
self.start_compute()
diff --git a/nova/tests/functional/libvirt/test_power_manage.py b/nova/tests/functional/libvirt/test_power_manage.py
index fb1ac7d0cd..9f80446bd6 100644
--- a/nova/tests/functional/libvirt/test_power_manage.py
+++ b/nova/tests/functional/libvirt/test_power_manage.py
@@ -21,7 +21,7 @@ from nova.tests import fixtures as nova_fixtures
from nova.tests.fixtures import libvirt as fakelibvirt
from nova.tests.functional.libvirt import base
from nova.virt import hardware
-from nova.virt.libvirt import cpu
+from nova.virt.libvirt.cpu import api as cpu_api
class PowerManagementTestsBase(base.ServersTestBase):
@@ -73,7 +73,7 @@ class PowerManagementTestsBase(base.ServersTestBase):
def _assert_cpu_set_state(self, cpu_set, expected='online'):
for i in cpu_set:
- core = cpu.Core(i)
+ core = cpu_api.Core(i)
if expected == 'online':
self.assertTrue(core.online, f'{i} is not online')
elif expected == 'offline':
@@ -212,7 +212,7 @@ class PowerManagementTestsGovernor(PowerManagementTestsBase):
def test_changing_strategy_fails(self):
# Arbitratly set a core governor strategy to be performance
- cpu.Core(1).set_high_governor()
+ cpu_api.Core(1).set_high_governor()
# and then forget about it while changing the strategy.
self.flags(cpu_power_management_strategy='cpu_state', group='libvirt')
# This time, this wouldn't be acceptable as some core would have a
diff --git a/nova/tests/functional/test_servers.py b/nova/tests/functional/test_servers.py
index 43208aa812..5887c99081 100644
--- a/nova/tests/functional/test_servers.py
+++ b/nova/tests/functional/test_servers.py
@@ -6526,3 +6526,41 @@ class PortAndFlavorAccelsServerCreateTest(AcceleratorServerBase):
binding_profile = neutronapi.get_binding_profile(updated_port)
self.assertNotIn('arq_uuid', binding_profile)
self.assertNotIn('pci_slot', binding_profile)
+
+
+class PortBindingShelvedServerTest(integrated_helpers._IntegratedTestBase):
+ """Tests for servers with ports."""
+
+ compute_driver = 'fake.SmallFakeDriver'
+
+ def setUp(self):
+ super(PortBindingShelvedServerTest, self).setUp()
+ self.flavor_id = self._create_flavor(
+ disk=10, ephemeral=20, swap=5 * 1024)
+
+ def test_shelve_offload_with_port(self):
+ # Do not wait before offloading
+ self.flags(shelved_offload_time=0)
+
+ server = self._create_server(
+ flavor_id=self.flavor_id,
+ networks=[{'port': self.neutron.port_1['id']}])
+
+ port = self.neutron.show_port(self.neutron.port_1['id'])['port']
+
+ # Assert that the port is actually associated to the instance
+ self.assertEqual(port['device_id'], server['id'])
+ self.assertEqual(port['binding:host_id'], 'compute')
+ self.assertEqual(port['binding:status'], 'ACTIVE')
+
+ # Do shelve
+ server = self._shelve_server(server, 'SHELVED_OFFLOADED')
+
+ # Retrieve the updated port
+ port = self.neutron.show_port(self.neutron.port_1['id'])['port']
+
+ # Assert that the port is still associated to the instance
+ # but the binding is not on the compute anymore
+ self.assertEqual(port['device_id'], server['id'])
+ self.assertIsNone(port['binding:host_id'])
+ self.assertNotIn('binding:status', port)
diff --git a/nova/tests/unit/compute/test_shelve.py b/nova/tests/unit/compute/test_shelve.py
index f95a722ced..0a1e3f54fc 100644
--- a/nova/tests/unit/compute/test_shelve.py
+++ b/nova/tests/unit/compute/test_shelve.py
@@ -209,6 +209,7 @@ class ShelveComputeManagerTestCase(test_compute.BaseTestCase):
instance = self._shelve_offload(clean_shutdown=False)
mock_power_off.assert_called_once_with(instance, 0, 0)
+ @mock.patch.object(neutron_api.API, 'unbind_ports')
@mock.patch.object(compute_utils, 'EventReporter')
@mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid')
@mock.patch.object(nova.compute.manager.ComputeManager,
@@ -225,7 +226,7 @@ class ShelveComputeManagerTestCase(test_compute.BaseTestCase):
def _shelve_offload(self, mock_notify, mock_notify_instance_usage,
mock_get_power_state, mock_update_resource_tracker,
mock_delete_alloc, mock_terminate, mock_get_bdms,
- mock_event, clean_shutdown=True):
+ mock_event, mock_unbind_ports, clean_shutdown=True):
host = 'fake-mini'
instance = self._create_fake_instance_obj(params={'host': host})
instance.task_state = task_states.SHELVING
@@ -278,6 +279,9 @@ class ShelveComputeManagerTestCase(test_compute.BaseTestCase):
instance.uuid,
graceful_exit=False)
+ mock_unbind_ports.assert_called_once_with(
+ self.context, mock.ANY, detach=False)
+
return instance
@mock.patch('nova.compute.utils.'
diff --git a/nova/tests/unit/scheduler/test_manager.py b/nova/tests/unit/scheduler/test_manager.py
index e7866069b3..e992fe6034 100644
--- a/nova/tests/unit/scheduler/test_manager.py
+++ b/nova/tests/unit/scheduler/test_manager.py
@@ -19,6 +19,7 @@ Tests For Scheduler
from unittest import mock
+from keystoneauth1 import exceptions as ks_exc
import oslo_messaging as messaging
from oslo_serialization import jsonutils
from oslo_utils.fixture import uuidsentinel as uuids
@@ -1688,6 +1689,41 @@ class SchedulerManagerTestCase(test.NoDBTestCase):
mock_log_warning.assert_not_called()
mock_log_debug.assert_called_once_with(msg)
+ @mock.patch('nova.scheduler.client.report.report_client_singleton')
+ @mock.patch.object(manager, 'LOG')
+ @mock.patch('nova.scheduler.host_manager.HostManager')
+ @mock.patch('nova.servicegroup.API')
+ @mock.patch('nova.rpc.get_notifier')
+ def test_init_lazy_placement_client(self, mock_rpc, mock_sg, mock_hm,
+ mock_log, mock_report):
+ # Simulate keytone or placement being offline at startup
+ mock_report.side_effect = ks_exc.RequestTimeout
+ mgr = manager.SchedulerManager()
+ mock_report.assert_called_once_with()
+ self.assertTrue(mock_log.warning.called)
+
+ # Make sure we're raising the actual error to subsequent callers
+ self.assertRaises(ks_exc.RequestTimeout, lambda: mgr.placement_client)
+
+ # Simulate recovery of the keystone or placement service
+ mock_report.reset_mock(side_effect=True)
+ mgr.placement_client
+ mock_report.assert_called_once_with()
+
+ @mock.patch('nova.scheduler.client.report.report_client_singleton')
+ @mock.patch('nova.scheduler.host_manager.HostManager')
+ @mock.patch('nova.servicegroup.API')
+ @mock.patch('nova.rpc.get_notifier')
+ def test_init_lazy_placement_client_failures(self, mock_rpc, mock_sg,
+ mock_hm, mock_report):
+ # Certain keystoneclient exceptions are fatal
+ mock_report.side_effect = ks_exc.Unauthorized
+ self.assertRaises(ks_exc.Unauthorized, manager.SchedulerManager)
+
+ # Anything else is fatal
+ mock_report.side_effect = test.TestingException
+ self.assertRaises(test.TestingException, manager.SchedulerManager)
+
class SchedulerManagerAllocationCandidateTestCase(test.NoDBTestCase):
diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py
index 04c80d662b..2b58c7df8b 100644
--- a/nova/tests/unit/virt/libvirt/test_driver.py
+++ b/nova/tests/unit/virt/libvirt/test_driver.py
@@ -3402,7 +3402,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
self.assertEqual(
"Memory encryption requested by hw:mem_encryption extra spec in "
"m1.fake flavor but image fake_image doesn't have "
- "'hw_firmware_type' property set to 'uefi'", str(exc))
+ "'hw_firmware_type' property set to 'uefi' or volume-backed "
+ "instance was requested", str(exc))
def test_sev_enabled_host_extra_spec_no_machine_type(self):
exc = self.assertRaises(exception.InvalidMachineType,
diff --git a/nova/tests/unit/virt/test_hardware.py b/nova/tests/unit/virt/test_hardware.py
index 016c478f8c..753ee41550 100644
--- a/nova/tests/unit/virt/test_hardware.py
+++ b/nova/tests/unit/virt/test_hardware.py
@@ -5364,7 +5364,7 @@ class MemEncryptionRequestedWithoutUEFITestCase(
expected_error = (
"Memory encryption requested by %(requesters)s but image "
"%(image_name)s doesn't have 'hw_firmware_type' property "
- "set to 'uefi'"
+ "set to 'uefi' or volume-backed instance was requested"
)
def _test_encrypted_memory_support_no_uefi(self, enc_extra_spec,
@@ -5491,6 +5491,25 @@ class MemEncryptionRequiredTestCase(test.NoDBTestCase):
(self.flavor_name, self.image_id)
)
+ def test_encrypted_memory_support_flavor_for_volume(self):
+ extra_specs = {'hw:mem_encryption': True}
+
+ flavor = objects.Flavor(name=self.flavor_name,
+ extra_specs=extra_specs)
+ # Following image_meta is typical for root Cinder volume
+ image_meta = objects.ImageMeta.from_dict({
+ 'min_disk': 0,
+ 'min_ram': 0,
+ 'properties': {},
+ 'size': 0,
+ 'status': 'active'})
+ # Confirm that exception.FlavorImageConflict is raised when
+ # flavor with hw:mem_encryption flag is used to create
+ # volume-backed instance
+ self.assertRaises(exception.FlavorImageConflict,
+ hw.get_mem_encryption_constraint, flavor,
+ image_meta)
+
class PCINUMAAffinityPolicyTest(test.NoDBTestCase):
diff --git a/nova/virt/hardware.py b/nova/virt/hardware.py
index 96a7198db2..c8f8bb2481 100644
--- a/nova/virt/hardware.py
+++ b/nova/virt/hardware.py
@@ -1213,10 +1213,13 @@ def _check_for_mem_encryption_requirement_conflicts(
"image %(image_name)s which has hw_mem_encryption property "
"explicitly set to %(image_val)s"
)
+ # image_meta.name is not set if image object represents root
+ # Cinder volume.
+ image_name = (image_meta.name if 'name' in image_meta else None)
data = {
'flavor_name': flavor.name,
'flavor_val': flavor_mem_enc_str,
- 'image_name': image_meta.name,
+ 'image_name': image_name,
'image_val': image_mem_enc,
}
raise exception.FlavorImageConflict(emsg % data)
@@ -1228,10 +1231,15 @@ def _check_mem_encryption_uses_uefi_image(requesters, image_meta):
emsg = _(
"Memory encryption requested by %(requesters)s but image "
- "%(image_name)s doesn't have 'hw_firmware_type' property set to 'uefi'"
+ "%(image_name)s doesn't have 'hw_firmware_type' property set to "
+ "'uefi' or volume-backed instance was requested"
)
+ # image_meta.name is not set if image object represents root Cinder
+ # volume, for this case FlavorImageConflict should be raised, but
+ # image_meta.name can't be extracted.
+ image_name = (image_meta.name if 'name' in image_meta else None)
data = {'requesters': " and ".join(requesters),
- 'image_name': image_meta.name}
+ 'image_name': image_name}
raise exception.FlavorImageConflict(emsg % data)
@@ -1260,12 +1268,14 @@ def _check_mem_encryption_machine_type(image_meta, machine_type=None):
if mach_type is None:
return
+ # image_meta.name is not set if image object represents root Cinder volume.
+ image_name = (image_meta.name if 'name' in image_meta else None)
# Could be something like pc-q35-2.11 if a specific version of the
# machine type is required, so do substring matching.
if 'q35' not in mach_type:
raise exception.InvalidMachineType(
mtype=mach_type,
- image_id=image_meta.id, image_name=image_meta.name,
+ image_id=image_meta.id, image_name=image_name,
reason=_("q35 type is required for SEV to work"))
diff --git a/nova/virt/libvirt/cpu/__init__.py b/nova/virt/libvirt/cpu/__init__.py
index 4410a4e579..e69de29bb2 100644
--- a/nova/virt/libvirt/cpu/__init__.py
+++ b/nova/virt/libvirt/cpu/__init__.py
@@ -1,22 +0,0 @@
-# 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.virt.libvirt.cpu import api
-
-
-Core = api.Core
-
-
-power_up = api.power_up
-power_down = api.power_down
-validate_all_dedicated_cpus = api.validate_all_dedicated_cpus
-power_down_all_dedicated_cpus = api.power_down_all_dedicated_cpus
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index 869996f615..fe48960296 100644
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -114,7 +114,7 @@ from nova.virt.image import model as imgmodel
from nova.virt import images
from nova.virt.libvirt import blockinfo
from nova.virt.libvirt import config as vconfig
-from nova.virt.libvirt import cpu as libvirt_cpu
+from nova.virt.libvirt.cpu import api as libvirt_cpu
from nova.virt.libvirt import designer
from nova.virt.libvirt import event as libvirtevent
from nova.virt.libvirt import guest as libvirt_guest
@@ -7636,7 +7636,7 @@ class LibvirtDriver(driver.ComputeDriver):
instance: 'objects.Instance',
power_on: bool = True,
pause: bool = False,
- post_xml_callback: ty.Callable = None,
+ post_xml_callback: ty.Optional[ty.Callable] = None,
) -> libvirt_guest.Guest:
"""Create a Guest from XML.
@@ -7697,7 +7697,7 @@ class LibvirtDriver(driver.ComputeDriver):
block_device_info: ty.Optional[ty.Dict[str, ty.Any]],
power_on: bool = True,
vifs_already_plugged: bool = False,
- post_xml_callback: ty.Callable = None,
+ post_xml_callback: ty.Optional[ty.Callable] = None,
external_events: ty.Optional[ty.List[ty.Tuple[str, str]]] = None,
cleanup_instance_dir: bool = False,
cleanup_instance_disks: bool = False,
@@ -10086,24 +10086,6 @@ class LibvirtDriver(driver.ComputeDriver):
:param instance: instance object that is in migration
"""
- current = eventlet.getcurrent()
- # NOTE(gibi) not all eventlet spawn is under our control, so
- # there can be senders without test_case_id set, find the first
- # ancestor that was spawned from nova.utils.spawn[_n] and
- # therefore has the id set.
- while (
- current is not None and
- not getattr(current, 'test_case_id', None)
- ):
- current = current.parent
-
- if current is not None:
- LOG.warning(
- "!!!---!!! live_migration_abort thread was spawned by "
- "TestCase ID: %s. If you see this in a failed functional test "
- "then please let #openstack-nova on IRC know about it. "
- "!!!---!!!", current.test_case_id
- )
guest = self._host.get_guest(instance)
dom = guest._domain
diff --git a/nova/virt/libvirt/event.py b/nova/virt/libvirt/event.py
index a7d2a3624f..56951dc11c 100644
--- a/nova/virt/libvirt/event.py
+++ b/nova/virt/libvirt/event.py
@@ -9,6 +9,8 @@
# 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 typing as ty
+
from nova.virt import event
@@ -22,7 +24,10 @@ class LibvirtEvent(event.InstanceEvent):
class DeviceEvent(LibvirtEvent):
"""Base class for device related libvirt events"""
- def __init__(self, uuid: str, dev: str, timestamp: float = None):
+ def __init__(self,
+ uuid: str,
+ dev: str,
+ timestamp: ty.Optional[float] = None):
super().__init__(uuid, timestamp)
self.dev = dev
diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py
index adb2ec45a1..e1298ee5c8 100644
--- a/nova/virt/libvirt/utils.py
+++ b/nova/virt/libvirt/utils.py
@@ -261,8 +261,8 @@ def copy_image(
dest: str,
host: ty.Optional[str] = None,
receive: bool = False,
- on_execute: ty.Callable = None,
- on_completion: ty.Callable = None,
+ on_execute: ty.Optional[ty.Callable] = None,
+ on_completion: ty.Optional[ty.Callable] = None,
compression: bool = True,
) -> None:
"""Copy a disk image to an existing directory
@@ -639,7 +639,7 @@ def mdev_name2uuid(mdev_name: str) -> str:
return str(uuid.UUID(mdev_uuid))
-def mdev_uuid2name(mdev_uuid: str, parent: str = None) -> str:
+def mdev_uuid2name(mdev_uuid: str, parent: ty.Optional[str] = None) -> str:
"""Convert an mdev uuid (of the form 8-4-4-4-12) and optionally its parent
device to a name (of the form mdev_<uuid_with_underscores>[_<pciid>]).
diff --git a/releasenotes/notes/antelope-prelude-4a99907b00e739f8.yaml b/releasenotes/notes/antelope-prelude-4a99907b00e739f8.yaml
new file mode 100644
index 0000000000..66890684af
--- /dev/null
+++ b/releasenotes/notes/antelope-prelude-4a99907b00e739f8.yaml
@@ -0,0 +1,51 @@
+---
+prelude: |
+ The OpenStack 2023.1 (Nova 27.0.0) release includes many new features and
+ bug fixes. Please be sure to read the upgrade section which describes the
+ required actions to upgrade your cloud from 26.0.0 (Zed) to 27.0.0 (2023.1).
+ As a reminder, OpenStack 2023.1 is our first `Skip-Level-Upgrade Release`__
+ (starting from now, we name it a `SLURP release`) where you can
+ rolling-upgrade your compute services from OpenStack Yoga as an experimental
+ feature. Next SLURP release will be 2024.1.
+
+ .. __: https://governance.openstack.org/tc/resolutions/20220210-release-cadence-adjustment.html
+
+ There are a few major changes worth mentioning. This is not an exhaustive
+ list:
+
+ - The latest Compute API microversion supported for 2023.1 is `v2.95`__.
+
+ .. __: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-2023.1
+
+ - `PCI devices can now be scheduled <https://docs.openstack.org/nova/latest/admin/pci-passthrough.html#pci-tracking-in-placement>`_
+ by Nova using the Placement API on a opt-in basis. This will help the
+ nova-scheduler service to better schedule flavors that use PCI
+ (non-Neutron related) resources, will generate less reschedules if an
+ instance cannot be created on a candidate and will help the nova-scheduler
+ to not miss valid candidates if the list was too large.
+
+ - Operators can now ask Nova to `manage the power consumption of dedicated
+ CPUs <https://docs.openstack.org/nova/latest/admin/cpu-topologies.html#configuring-cpu-power-management-for-dedicated-cores>`_
+ so as to either offline them or change their governor if they're
+ currently not in use by any instance or if the instance is stopped.
+
+ - Nova will prevent unexpected compute service renames by `persisting a unique
+ compute UUID on local disk <https://docs.openstack.org/nova/latest/admin/compute-node-identification.html>`_.
+ This stored UUID will be considered the source of truth for knowing whether
+ the compute service hostame has been modified or not. As a reminder,
+ changing a compute hostname is forbidden, particularly when this compute is
+ currently running instances on top of it.
+
+ - `SPICE consoles <https://docs.openstack.org/nova/latest/admin/remote-console-access.html#spice-console>`_
+ can now be configured with compression settings which include choices of the
+ compression algorithm and the compression mode.
+
+ - Fully-Qualified Domain Names are now considered valid for an instance
+ hostname if you use the 2.94 API microversion.
+
+ - By opting into 2.95 API microversion, evacuated instances will remain
+ stopped on the destination host until manually started.
+
+ - Nova APIs now `by default support new RBAC policies <https://docs.openstack.org/nova/latest/configuration/policy.html>`
+ and scopes. See our `Policy Concepts documention <https://docs.openstack.org/nova/latest/configuration/policy-concepts.html>`
+ for further details.
diff --git a/releasenotes/notes/port-binding-removed-shelved-offloaded-f1772a64be007b24.yaml b/releasenotes/notes/port-binding-removed-shelved-offloaded-f1772a64be007b24.yaml
new file mode 100644
index 0000000000..7e2dccbbf4
--- /dev/null
+++ b/releasenotes/notes/port-binding-removed-shelved-offloaded-f1772a64be007b24.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+ - |
+ [`bug 1983471 <https://bugs.launchpad.net/nova/+bug/1983471>`_]
+ When offloading a shelved instance, the compute will now remove the
+ binding so instance ports will appear as "unbound" in neutron.
diff --git a/releasenotes/source/2023.1.rst b/releasenotes/source/2023.1.rst
new file mode 100644
index 0000000000..d1238479ba
--- /dev/null
+++ b/releasenotes/source/2023.1.rst
@@ -0,0 +1,6 @@
+===========================
+2023.1 Series Release Notes
+===========================
+
+.. release-notes::
+ :branch: stable/2023.1
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 6bff00e25a..ed6f8c2d07 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@ Nova Release Notes
:maxdepth: 1
unreleased
+ 2023.1
zed
yoga
xena
diff --git a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po
index d90391af7c..c0bd8bc9a8 100644
--- a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po
+++ b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po
@@ -2,15 +2,16 @@
# Andi Chandler <andi@gowling.com>, 2018. #zanata
# Andi Chandler <andi@gowling.com>, 2020. #zanata
# Andi Chandler <andi@gowling.com>, 2022. #zanata
+# Andi Chandler <andi@gowling.com>, 2023. #zanata
msgid ""
msgstr ""
"Project-Id-Version: nova\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-09-16 12:59+0000\n"
+"POT-Creation-Date: 2023-03-06 19:02+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2022-09-15 09:04+0000\n"
+"PO-Revision-Date: 2023-01-26 10:17+0000\n"
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
"Language-Team: English (United Kingdom)\n"
"Language: en_GB\n"
@@ -382,9 +383,6 @@ msgstr "20.5.0"
msgid "20.6.1"
msgstr "20.6.1"
-msgid "20.6.1-29"
-msgstr "20.6.1-29"
-
msgid "204 NoContent on success"
msgstr "204 NoContent on success"
@@ -409,9 +407,6 @@ msgstr "21.2.2"
msgid "21.2.3"
msgstr "21.2.3"
-msgid "21.2.4-12"
-msgstr "21.2.4-12"
-
msgid "22.0.0"
msgstr "22.0.0"
@@ -433,9 +428,6 @@ msgstr "22.3.0"
msgid "22.4.0"
msgstr "22.4.0"
-msgid "22.4.0-6"
-msgstr "22.4.0-6"
-
msgid "23.0.0"
msgstr "23.0.0"
@@ -451,8 +443,8 @@ msgstr "23.2.0"
msgid "23.2.1"
msgstr "23.2.1"
-msgid "23.2.1-13"
-msgstr "23.2.1-13"
+msgid "23.2.2"
+msgstr "23.2.2"
msgid "24.0.0"
msgstr "24.0.0"
@@ -463,8 +455,8 @@ msgstr "24.1.0"
msgid "24.1.1"
msgstr "24.1.1"
-msgid "24.1.1-7"
-msgstr "24.1.1-7"
+msgid "24.2.0"
+msgstr "24.2.0"
msgid "25.0.0"
msgstr "25.0.0"
@@ -472,8 +464,14 @@ msgstr "25.0.0"
msgid "25.0.1"
msgstr "25.0.1"
-msgid "25.0.1-5"
-msgstr "25.0.1-5"
+msgid "25.1.0"
+msgstr "25.1.0"
+
+msgid "26.0.0"
+msgstr "26.0.0"
+
+msgid "26.1.0"
+msgstr "26.1.0"
msgid "400 for unknown param for query param and for request body."
msgstr "400 for unknown param for query param and for request body."
@@ -488,6 +486,24 @@ msgid "409 Conflict if inventory in use or if some other request concurrently"
msgstr "409 Conflict if inventory in use or if some other request concurrently"
msgid ""
+"A ``--dry-run`` option has been added to the ``nova-manage placement "
+"heal_allocations`` CLI which allows running the command to get output "
+"without committing any changes to placement."
+msgstr ""
+"A ``--dry-run`` option has been added to the ``nova-manage placement "
+"heal_allocations`` CLI which allows running the command to get output "
+"without committing any changes to placement."
+
+msgid ""
+"A ``--force`` flag is provided to skip the above checks but caution should "
+"be taken as this could easily lead to the underlying ABI of the instance "
+"changing when moving between machine types."
+msgstr ""
+"A ``--force`` flag is provided to skip the above checks but caution should "
+"be taken as this could easily lead to the underlying ABI of the instance "
+"changing when moving between machine types."
+
+msgid ""
"A ``default_floating_pool`` configuration option has been added in the "
"``[neutron]`` group. The existing ``default_floating_pool`` option in the "
"``[DEFAULT]`` group is retained and should be used by nova-network users. "
@@ -571,6 +587,24 @@ msgid "Stein Series Release Notes"
msgstr "Stein Series Release Notes"
msgid ""
+"The XenServer configuration option 'iqn_prefix' has been removed. It was not "
+"used anywhere and has no effect on any code, so there should be no impact."
+msgstr ""
+"The XenServer configuration option 'iqn_prefix' has been removed. It was not "
+"used anywhere and has no effect on any code, so there should be no impact."
+
+msgid ""
+"The ``api_rate_limit`` configuration option has been removed. The option was "
+"disabled by default back in the Havana release since it's effectively broken "
+"for more than one API worker. It has been removed because the legacy v2 API "
+"code that was using it has also been removed."
+msgstr ""
+"The ``api_rate_limit`` configuration option has been removed. The option was "
+"disabled by default back in the Havana release since it's effectively broken "
+"for more than one API worker. It has been removed because the legacy v2 API "
+"code that was using it has also been removed."
+
+msgid ""
"The ``nova-manage vm list`` command is deprecated and will be removed in the "
"15.0.0 Ocata release. Use the ``nova list`` command from python-novaclient "
"instead."
@@ -580,6 +614,24 @@ msgstr ""
"instead."
msgid ""
+"The default flavors that nova has previously had are no longer created as "
+"part of the first database migration. New deployments will need to create "
+"appropriate flavors before first use."
+msgstr ""
+"The default flavours that Nova previously had are no longer created as part "
+"of the first database migration. New deployments will need to create "
+"appropriate flavours before first use."
+
+msgid ""
+"The network configuration option 'fake_call' has been removed. It hasn't "
+"been used for several cycles, and has no effect on any code, so there should "
+"be no impact."
+msgstr ""
+"The network configuration option 'fake_call' has been removed. It hasn't "
+"been used for several cycles, and has no effect on any code, so there should "
+"be no impact."
+
+msgid ""
"These commands only work with nova-network which is itself deprecated in "
"favor of Neutron."
msgstr ""
@@ -611,6 +663,9 @@ msgstr "Xena Series Release Notes"
msgid "Yoga Series Release Notes"
msgstr "Yoga Series Release Notes"
+msgid "Zed Series Release Notes"
+msgstr "Zed Series Release Notes"
+
msgid "kernels 3.x: 8"
msgstr "kernels 3.x: 8"
diff --git a/tox.ini b/tox.ini
index 097edbe827..77c0d9b9d3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -42,7 +42,7 @@ commands =
env TEST_OSPROFILER=1 stestr run --combine --no-discover 'nova.tests.unit.test_profiler'
stestr slowest
-[testenv:functional{,-py38,-py39,-py310}]
+[testenv:functional{,-py38,-py39,-py310,-py311}]
description =
Run functional tests.
# As nova functional tests import the PlacementFixture from the placement