Skip to content

Commit 06ddd20

Browse files
authored
Merge pull request #68 from stackhpc/upstream/2023.1-2024-01-22
Synchronise 2023.1 with upstream
2 parents 0c68e3c + 683ecc0 commit 06ddd20

File tree

6 files changed

+122
-9
lines changed

6 files changed

+122
-9
lines changed

nova/compute/manager.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6918,9 +6918,9 @@ def _shelve_offload_instance(self, context, instance, clean_shutdown,
69186918

69196919
instance.power_state = current_power_state
69206920
# NOTE(mriedem): The vm_state has to be set before updating the
6921-
# resource tracker, see vm_states.ALLOW_RESOURCE_REMOVAL. The host/node
6922-
# values cannot be nulled out until after updating the resource tracker
6923-
# though.
6921+
# resource tracker, see vm_states.allow_resource_removal(). The
6922+
# host/node values cannot be nulled out until after updating the
6923+
# resource tracker though.
69246924
instance.vm_state = vm_states.SHELVED_OFFLOADED
69256925
instance.task_state = None
69266926
instance.save(expected_task_state=[task_states.SHELVING,

nova/compute/resource_tracker.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,7 +1539,8 @@ def _update_usage_from_instance(self, context, instance, nodename,
15391539
# NOTE(sfinucan): Both brand new instances as well as instances that
15401540
# are being unshelved will have is_new_instance == True
15411541
is_removed_instance = not is_new_instance and (is_removed or
1542-
instance['vm_state'] in vm_states.ALLOW_RESOURCE_REMOVAL)
1542+
vm_states.allow_resource_removal(
1543+
vm_state=instance['vm_state'], task_state=instance.task_state))
15431544

15441545
if is_new_instance:
15451546
self.tracked_instances.add(uuid)
@@ -1598,7 +1599,9 @@ def _update_usage_from_instances(self, context, instances, nodename):
15981599

15991600
instance_by_uuid = {}
16001601
for instance in instances:
1601-
if instance.vm_state not in vm_states.ALLOW_RESOURCE_REMOVAL:
1602+
if not vm_states.allow_resource_removal(
1603+
vm_state=instance['vm_state'],
1604+
task_state=instance.task_state):
16021605
self._update_usage_from_instance(context, instance, nodename)
16031606
instance_by_uuid[instance.uuid] = instance
16041607
return instance_by_uuid

nova/compute/stats.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ def update_stats_for_instance(self, instance, is_removed=False):
105105
(vm_state, task_state, os_type, project_id) = \
106106
self._extract_state_from_instance(instance)
107107

108-
if is_removed or vm_state in vm_states.ALLOW_RESOURCE_REMOVAL:
108+
if is_removed or vm_states.allow_resource_removal(
109+
vm_state=vm_state, task_state=task_state):
109110
self._decrement("num_instances")
110111
self.states.pop(uuid)
111112
else:

nova/compute/vm_states.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
See http://wiki.openstack.org/VMState
2828
"""
2929

30+
from nova.compute import task_states
3031
from nova.objects import fields
3132

3233

@@ -74,8 +75,14 @@
7475
# states we allow to trigger crash dump
7576
ALLOW_TRIGGER_CRASH_DUMP = [ACTIVE, PAUSED, RESCUED, RESIZED, ERROR]
7677

77-
# states we allow resources to be freed in
78-
ALLOW_RESOURCE_REMOVAL = [DELETED, SHELVED_OFFLOADED]
79-
8078
# states we allow for evacuate instance
8179
ALLOW_TARGET_STATES = [STOPPED]
80+
81+
82+
def allow_resource_removal(vm_state, task_state=None):
83+
"""(vm_state, task_state) combinations we allow resources to be freed in"""
84+
85+
return (
86+
vm_state == DELETED or
87+
vm_state == SHELVED_OFFLOADED and task_state != task_states.SPAWNING
88+
)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
from unittest import mock
13+
14+
from nova import context
15+
from nova.objects import compute_node
16+
from nova import test
17+
from nova.tests import fixtures as nova_fixtures
18+
from nova.tests.functional import fixtures as func_fixtures
19+
from nova.tests.functional import integrated_helpers
20+
21+
22+
class UnshelveUpdateAvailableResourcesPeriodicRace(
23+
test.TestCase, integrated_helpers.InstanceHelperMixin):
24+
def setUp(self):
25+
super(UnshelveUpdateAvailableResourcesPeriodicRace, self).setUp()
26+
27+
placement = func_fixtures.PlacementFixture()
28+
self.useFixture(placement)
29+
self.placement = placement.api
30+
self.neutron = nova_fixtures.NeutronFixture(self)
31+
self.useFixture(self.neutron)
32+
self.useFixture(nova_fixtures.GlanceFixture(self))
33+
# Start nova services.
34+
self.api = self.useFixture(nova_fixtures.OSAPIFixture(
35+
api_version='v2.1')).admin_api
36+
self.api.microversion = 'latest'
37+
self.notifier = self.useFixture(
38+
nova_fixtures.NotificationFixture(self))
39+
40+
self.start_service('conductor')
41+
self.start_service('scheduler')
42+
43+
def test_unshelve_spawning_update_available_resources(self):
44+
compute = self._start_compute('compute1')
45+
46+
server = self._create_server(
47+
networks=[{'port': self.neutron.port_1['id']}])
48+
49+
node = compute_node.ComputeNode.get_by_nodename(
50+
context.get_admin_context(), 'compute1')
51+
self.assertEqual(1, node.vcpus_used)
52+
53+
# with default config shelve means immediate offload as well
54+
req = {
55+
'shelve': {}
56+
}
57+
self.api.post_server_action(server['id'], req)
58+
self._wait_for_server_parameter(
59+
server, {'status': 'SHELVED_OFFLOADED',
60+
'OS-EXT-SRV-ATTR:host': None})
61+
62+
node = compute_node.ComputeNode.get_by_nodename(
63+
context.get_admin_context(), 'compute1')
64+
self.assertEqual(0, node.vcpus_used)
65+
66+
def fake_spawn(*args, **kwargs):
67+
self._run_periodics()
68+
69+
with mock.patch.object(
70+
compute.driver, 'spawn', side_effect=fake_spawn):
71+
req = {'unshelve': None}
72+
self.api.post_server_action(server['id'], req)
73+
self.notifier.wait_for_versioned_notifications(
74+
'instance.unshelve.start')
75+
self._wait_for_server_parameter(
76+
server,
77+
{
78+
'status': 'ACTIVE',
79+
'OS-EXT-STS:task_state': None,
80+
'OS-EXT-SRV-ATTR:host': 'compute1',
81+
})
82+
83+
node = compute_node.ComputeNode.get_by_nodename(
84+
context.get_admin_context(), 'compute1')
85+
# After the fix, the instance should have resources claimed
86+
self.assertEqual(1, node.vcpus_used)

nova/tests/unit/compute/test_stats.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,22 @@ def test_update_stats_for_instance_offloaded(self):
208208
self.assertEqual(0, self.stats.num_os_type("Linux"))
209209
self.assertEqual(0, self.stats["num_vm_" + vm_states.BUILDING])
210210

211+
def test_update_stats_for_instance_being_unshelved(self):
212+
instance = self._create_instance()
213+
self.stats.update_stats_for_instance(instance)
214+
self.assertEqual(1, self.stats.num_instances_for_project("1234"))
215+
216+
instance["vm_state"] = vm_states.SHELVED_OFFLOADED
217+
instance["task_state"] = task_states.SPAWNING
218+
self.stats.update_stats_for_instance(instance)
219+
220+
self.assertEqual(1, self.stats.num_instances)
221+
self.assertEqual(1, self.stats.num_instances_for_project(1234))
222+
self.assertEqual(1, self.stats["num_os_type_Linux"])
223+
self.assertEqual(1, self.stats["num_vm_%s" %
224+
vm_states.SHELVED_OFFLOADED])
225+
self.assertEqual(1, self.stats["num_task_%s" % task_states.SPAWNING])
226+
211227
def test_io_workload(self):
212228
vms = [vm_states.ACTIVE, vm_states.BUILDING, vm_states.PAUSED]
213229
tasks = [task_states.RESIZE_MIGRATING, task_states.REBUILDING,

0 commit comments

Comments
 (0)