Skip to content

Commit d0a6cd0

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "pwr mgmt: make API into a per-driver object" into stable/2023.1
2 parents ffc95fe + 874acc1 commit d0a6cd0

File tree

3 files changed

+126
-102
lines changed

3 files changed

+126
-102
lines changed

nova/tests/unit/virt/libvirt/cpu/test_api.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class TestAPI(test.NoDBTestCase):
2424
def setUp(self):
2525
super(TestAPI, self).setUp()
2626
self.core_1 = api.Core(1)
27+
self.api = api.API()
2728

2829
# Create a fake instance with two pinned CPUs but only one is on the
2930
# dedicated set
@@ -82,7 +83,7 @@ def test_power_up_online(self, mock_online):
8283
self.flags(cpu_power_management=True, group='libvirt')
8384
self.flags(cpu_dedicated_set='1-2', group='compute')
8485

85-
api.power_up(self.fake_inst)
86+
self.api.power_up(self.fake_inst)
8687
# only core #2 can be set as core #0 is not on the dedicated set
8788
# As a reminder, core(i).online calls set_online(i)
8889
mock_online.assert_called_once_with(2)
@@ -93,29 +94,29 @@ def test_power_up_governor(self, mock_set_governor):
9394
self.flags(cpu_power_management_strategy='governor', group='libvirt')
9495
self.flags(cpu_dedicated_set='1-2', group='compute')
9596

96-
api.power_up(self.fake_inst)
97+
self.api.power_up(self.fake_inst)
9798
# only core #2 can be set as core #1 is not on the dedicated set
9899
# As a reminder, core(i).set_high_governor calls set_governor(i)
99100
mock_set_governor.assert_called_once_with(2, 'performance')
100101

101102
@mock.patch.object(core, 'set_online')
102103
def test_power_up_skipped(self, mock_online):
103104
self.flags(cpu_power_management=False, group='libvirt')
104-
api.power_up(self.fake_inst)
105+
self.api.power_up(self.fake_inst)
105106
mock_online.assert_not_called()
106107

107108
@mock.patch.object(core, 'set_online')
108109
def test_power_up_skipped_if_standard_instance(self, mock_online):
109110
self.flags(cpu_power_management=True, group='libvirt')
110-
api.power_up(objects.Instance(numa_topology=None))
111+
self.api.power_up(objects.Instance(numa_topology=None))
111112
mock_online.assert_not_called()
112113

113114
@mock.patch.object(core, 'set_offline')
114115
def test_power_down_offline(self, mock_offline):
115116
self.flags(cpu_power_management=True, group='libvirt')
116117
self.flags(cpu_dedicated_set='1-2', group='compute')
117118

118-
api.power_down(self.fake_inst)
119+
self.api.power_down(self.fake_inst)
119120
# only core #2 can be set as core #1 is not on the dedicated set
120121
# As a reminder, core(i).online calls set_online(i)
121122
mock_offline.assert_called_once_with(2)
@@ -126,7 +127,7 @@ def test_power_down_governor_cpu0_ignored(self, mock_set_governor):
126127
self.flags(cpu_power_management_strategy='governor', group='libvirt')
127128
self.flags(cpu_dedicated_set='0-1', group='compute')
128129

129-
api.power_down(self.fake_inst)
130+
self.api.power_down(self.fake_inst)
130131

131132
# Make sure that core #0 is ignored, since it is special and cannot
132133
# be powered down.
@@ -138,7 +139,7 @@ def test_power_down_governor(self, mock_set_governor):
138139
self.flags(cpu_power_management_strategy='governor', group='libvirt')
139140
self.flags(cpu_dedicated_set='1-2', group='compute')
140141

141-
api.power_down(self.fake_inst)
142+
self.api.power_down(self.fake_inst)
142143

143144
# only core #2 can be set as core #0 is not on the dedicated set
144145
# As a reminder, core(i).set_high_governor calls set_governor(i)
@@ -147,21 +148,21 @@ def test_power_down_governor(self, mock_set_governor):
147148
@mock.patch.object(core, 'set_offline')
148149
def test_power_down_skipped(self, mock_offline):
149150
self.flags(cpu_power_management=False, group='libvirt')
150-
api.power_down(self.fake_inst)
151+
self.api.power_down(self.fake_inst)
151152
mock_offline.assert_not_called()
152153

153154
@mock.patch.object(core, 'set_offline')
154155
def test_power_down_skipped_if_standard_instance(self, mock_offline):
155156
self.flags(cpu_power_management=True, group='libvirt')
156-
api.power_down(objects.Instance(numa_topology=None))
157+
self.api.power_down(objects.Instance(numa_topology=None))
157158
mock_offline.assert_not_called()
158159

159160
@mock.patch.object(core, 'set_offline')
160161
def test_power_down_all_dedicated_cpus_offline(self, mock_offline):
161162
self.flags(cpu_power_management=True, group='libvirt')
162163
self.flags(cpu_dedicated_set='0-2', group='compute')
163164

164-
api.power_down_all_dedicated_cpus()
165+
self.api.power_down_all_dedicated_cpus()
165166
# All dedicated CPUs are turned offline, except CPU0
166167
mock_offline.assert_has_calls([mock.call(1), mock.call(2)])
167168

@@ -171,15 +172,15 @@ def test_power_down_all_dedicated_cpus_governor(self, mock_set_governor):
171172
self.flags(cpu_power_management_strategy='governor', group='libvirt')
172173
self.flags(cpu_dedicated_set='0-2', group='compute')
173174

174-
api.power_down_all_dedicated_cpus()
175+
self.api.power_down_all_dedicated_cpus()
175176
# All dedicated CPUs are turned offline, except CPU0
176177
mock_set_governor.assert_has_calls([mock.call(1, 'powersave'),
177178
mock.call(2, 'powersave')])
178179

179180
@mock.patch.object(core, 'set_offline')
180181
def test_power_down_all_dedicated_cpus_skipped(self, mock_offline):
181182
self.flags(cpu_power_management=False, group='libvirt')
182-
api.power_down_all_dedicated_cpus()
183+
self.api.power_down_all_dedicated_cpus()
183184
mock_offline.assert_not_called()
184185

185186
@mock.patch.object(core, 'set_offline')
@@ -188,7 +189,7 @@ def test_power_down_all_dedicated_cpus_no_dedicated_cpus_configured(
188189
):
189190
self.flags(cpu_power_management=True, group='libvirt')
190191
self.flags(cpu_dedicated_set=None, group='compute')
191-
api.power_down_all_dedicated_cpus()
192+
self.api.power_down_all_dedicated_cpus()
192193
mock_offline.assert_not_called()
193194

194195
@mock.patch.object(core, 'get_governor')
@@ -201,7 +202,7 @@ def test_validate_all_dedicated_cpus_for_governor(self, mock_get_online,
201202
mock_get_governor.return_value = 'performance'
202203
mock_get_online.side_effect = (True, False)
203204
self.assertRaises(exception.InvalidConfiguration,
204-
api.validate_all_dedicated_cpus)
205+
self.api.validate_all_dedicated_cpus)
205206

206207
@mock.patch.object(core, 'get_governor')
207208
@mock.patch.object(core, 'get_online')
@@ -213,7 +214,7 @@ def test_validate_all_dedicated_cpus_for_cpu_state(self, mock_get_online,
213214
mock_get_online.return_value = True
214215
mock_get_governor.side_effect = ('powersave', 'performance')
215216
self.assertRaises(exception.InvalidConfiguration,
216-
api.validate_all_dedicated_cpus)
217+
self.api.validate_all_dedicated_cpus)
217218

218219
@mock.patch.object(core, 'get_governor')
219220
@mock.patch.object(core, 'get_online')
@@ -227,7 +228,7 @@ def test_validate_all_dedicated_cpus_for_cpu_state_warning(
227228
mock_get_online.return_value = True
228229
mock_get_governor.return_value = 'performance'
229230

230-
api.validate_all_dedicated_cpus()
231+
self.api.validate_all_dedicated_cpus()
231232

232233
# Make sure we skipped CPU0
233234
mock_get_online.assert_has_calls([mock.call(1), mock.call(2)])
@@ -240,6 +241,6 @@ def test_validate_all_dedicated_cpus_for_cpu_state_warning(
240241
def test_validate_all_dedicated_cpus_no_cpu(self):
241242
self.flags(cpu_power_management=True, group='libvirt')
242243
self.flags(cpu_dedicated_set=None, group='compute')
243-
api.validate_all_dedicated_cpus()
244+
self.api.validate_all_dedicated_cpus()
244245
# no assert we want to make sure the validation won't raise if
245246
# no dedicated cpus are configured

nova/virt/libvirt/cpu/api.py

Lines changed: 94 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -75,88 +75,102 @@ def set_low_governor(self) -> None:
7575
core.set_governor(self.ident, CONF.libvirt.cpu_power_governor_low)
7676

7777

78-
def power_up(instance: objects.Instance) -> None:
79-
if not CONF.libvirt.cpu_power_management:
80-
return
81-
if instance.numa_topology is None:
82-
return
83-
84-
cpu_dedicated_set = hardware.get_cpu_dedicated_set_nozero() or set()
85-
pcpus = instance.numa_topology.cpu_pinning.union(
86-
instance.numa_topology.cpuset_reserved)
87-
powered_up = set()
88-
for pcpu in pcpus:
89-
if pcpu in cpu_dedicated_set:
90-
pcpu = Core(pcpu)
91-
if CONF.libvirt.cpu_power_management_strategy == 'cpu_state':
92-
pcpu.online = True
93-
else:
94-
pcpu.set_high_governor()
95-
powered_up.add(str(pcpu))
96-
LOG.debug("Cores powered up : %s", powered_up)
97-
98-
99-
def power_down(instance: objects.Instance) -> None:
100-
if not CONF.libvirt.cpu_power_management:
101-
return
102-
if instance.numa_topology is None:
103-
return
104-
105-
cpu_dedicated_set = hardware.get_cpu_dedicated_set_nozero() or set()
106-
pcpus = instance.numa_topology.cpu_pinning.union(
107-
instance.numa_topology.cpuset_reserved)
108-
powered_down = set()
109-
for pcpu in pcpus:
110-
if pcpu in cpu_dedicated_set:
111-
pcpu = Core(pcpu)
78+
class API(object):
79+
80+
def core(self, i):
81+
"""From a purely functional point of view, there is no need for this
82+
method. However, we want to test power management in multinode
83+
scenarios (ex: live migration) in our functional tests. If we
84+
instantiated the Core class directly in the methods below, the
85+
functional tests would not be able to distinguish between cores on the
86+
source and destination hosts. In functional tests we can replace this
87+
helper method by a stub that returns a fixture, allowing us to maintain
88+
distinct core power state for each host.
89+
90+
See also nova.virt.libvirt.driver.LibvirtDriver.cpu_api.
91+
"""
92+
return Core(i)
93+
94+
def power_up(self, instance: objects.Instance) -> None:
95+
if not CONF.libvirt.cpu_power_management:
96+
return
97+
if instance.numa_topology is None:
98+
return
99+
100+
cpu_dedicated_set = hardware.get_cpu_dedicated_set_nozero() or set()
101+
pcpus = instance.numa_topology.cpu_pinning.union(
102+
instance.numa_topology.cpuset_reserved)
103+
powered_up = set()
104+
for pcpu in pcpus:
105+
if pcpu in cpu_dedicated_set:
106+
pcpu = self.core(pcpu)
107+
if CONF.libvirt.cpu_power_management_strategy == 'cpu_state':
108+
pcpu.online = True
109+
else:
110+
pcpu.set_high_governor()
111+
powered_up.add(str(pcpu))
112+
LOG.debug("Cores powered up : %s", powered_up)
113+
114+
def power_down(self, instance: objects.Instance) -> None:
115+
if not CONF.libvirt.cpu_power_management:
116+
return
117+
if instance.numa_topology is None:
118+
return
119+
120+
cpu_dedicated_set = hardware.get_cpu_dedicated_set_nozero() or set()
121+
pcpus = instance.numa_topology.cpu_pinning.union(
122+
instance.numa_topology.cpuset_reserved)
123+
powered_down = set()
124+
for pcpu in pcpus:
125+
if pcpu in cpu_dedicated_set:
126+
pcpu = self.core(pcpu)
127+
if CONF.libvirt.cpu_power_management_strategy == 'cpu_state':
128+
pcpu.online = False
129+
else:
130+
pcpu.set_low_governor()
131+
powered_down.add(str(pcpu))
132+
LOG.debug("Cores powered down : %s", powered_down)
133+
134+
def power_down_all_dedicated_cpus(self) -> None:
135+
if not CONF.libvirt.cpu_power_management:
136+
return
137+
138+
cpu_dedicated_set = hardware.get_cpu_dedicated_set_nozero() or set()
139+
for pcpu in cpu_dedicated_set:
140+
pcpu = self.core(pcpu)
112141
if CONF.libvirt.cpu_power_management_strategy == 'cpu_state':
113142
pcpu.online = False
114143
else:
115144
pcpu.set_low_governor()
116-
powered_down.add(str(pcpu))
117-
LOG.debug("Cores powered down : %s", powered_down)
118-
119-
120-
def power_down_all_dedicated_cpus() -> None:
121-
if not CONF.libvirt.cpu_power_management:
122-
return
123-
124-
cpu_dedicated_set = hardware.get_cpu_dedicated_set_nozero() or set()
125-
for pcpu in cpu_dedicated_set:
126-
pcpu = Core(pcpu)
145+
LOG.debug("Cores powered down : %s", cpu_dedicated_set)
146+
147+
def validate_all_dedicated_cpus(self) -> None:
148+
if not CONF.libvirt.cpu_power_management:
149+
return
150+
cpu_dedicated_set = hardware.get_cpu_dedicated_set() or set()
151+
governors = set()
152+
cpu_states = set()
153+
for pcpu in cpu_dedicated_set:
154+
if (pcpu == 0 and
155+
CONF.libvirt.cpu_power_management_strategy == 'cpu_state'):
156+
LOG.warning('CPU0 is in cpu_dedicated_set, '
157+
'but it is not eligible for state management '
158+
'and will be ignored')
159+
continue
160+
pcpu = self.core(pcpu)
161+
# we need to collect the governors strategy and the CPU states
162+
governors.add(pcpu.governor)
163+
cpu_states.add(pcpu.online)
127164
if CONF.libvirt.cpu_power_management_strategy == 'cpu_state':
128-
pcpu.online = False
129-
else:
130-
pcpu.set_low_governor()
131-
LOG.debug("Cores powered down : %s", cpu_dedicated_set)
132-
133-
134-
def validate_all_dedicated_cpus() -> None:
135-
if not CONF.libvirt.cpu_power_management:
136-
return
137-
cpu_dedicated_set = hardware.get_cpu_dedicated_set() or set()
138-
governors = set()
139-
cpu_states = set()
140-
for pcpu in cpu_dedicated_set:
141-
if (pcpu == 0 and
142-
CONF.libvirt.cpu_power_management_strategy == 'cpu_state'):
143-
LOG.warning('CPU0 is in cpu_dedicated_set, but it is not eligible '
144-
'for state management and will be ignored')
145-
continue
146-
pcpu = Core(pcpu)
147-
# we need to collect the governors strategy and the CPU states
148-
governors.add(pcpu.governor)
149-
cpu_states.add(pcpu.online)
150-
if CONF.libvirt.cpu_power_management_strategy == 'cpu_state':
151-
# all the cores need to have the same governor strategy
152-
if len(governors) > 1:
153-
msg = _("All the cores need to have the same governor strategy"
154-
"before modifying the CPU states. You can reboot the "
155-
"compute node if you prefer.")
156-
raise exception.InvalidConfiguration(msg)
157-
elif CONF.libvirt.cpu_power_management_strategy == 'governor':
158-
# all the cores need to be online
159-
if False in cpu_states:
160-
msg = _("All the cores need to be online before modifying the "
161-
"governor strategy.")
162-
raise exception.InvalidConfiguration(msg)
165+
# all the cores need to have the same governor strategy
166+
if len(governors) > 1:
167+
msg = _("All the cores need to have the same governor strategy"
168+
"before modifying the CPU states. You can reboot the "
169+
"compute node if you prefer.")
170+
raise exception.InvalidConfiguration(msg)
171+
elif CONF.libvirt.cpu_power_management_strategy == 'governor':
172+
# all the cores need to be online
173+
if False in cpu_states:
174+
msg = _("All the cores need to be online before modifying the "
175+
"governor strategy.")
176+
raise exception.InvalidConfiguration(msg)

nova/virt/libvirt/driver.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,15 @@ def __init__(self, virtapi, read_only=False):
545545
# events about success or failure.
546546
self._device_event_handler = AsyncDeviceEventsHandler()
547547

548+
# NOTE(artom) From a pure functionality point of view, there's no need
549+
# for this to be an attribute of self. However, we want to test power
550+
# management in multinode scenarios (ex: live migration) in our
551+
# functional tests. If the power management code was just a bunch of
552+
# module level functions, the functional tests would not be able to
553+
# distinguish between cores on the source and destination hosts.
554+
# See also nova.virt.libvirt.cpu.api.API.core().
555+
self.cpu_api = libvirt_cpu.API()
556+
548557
def _discover_vpmems(self, vpmem_conf=None):
549558
"""Discover vpmems on host and configuration.
550559

@@ -822,13 +831,13 @@ def init_host(self, host):
822831
# modified by Nova before. Note that it can provide an exception if
823832
# either the governor strategies are different between the cores or if
824833
# the cores are offline.
825-
libvirt_cpu.validate_all_dedicated_cpus()
834+
self.cpu_api.validate_all_dedicated_cpus()
826835
# NOTE(sbauza): We powerdown all dedicated CPUs but if some instances
827836
# exist that are pinned for some CPUs, then we'll later powerup those
828837
# CPUs when rebooting the instance in _init_instance()
829838
# Note that it can provide an exception if the config options are
830839
# wrongly modified.
831-
libvirt_cpu.power_down_all_dedicated_cpus()
840+
self.cpu_api.power_down_all_dedicated_cpus()
832841

833842
# TODO(sbauza): Remove this code once mediated devices are persisted
834843
# across reboots.
@@ -1526,7 +1535,7 @@ def _wait_for_destroy(expected_domid):
15261535
if CONF.libvirt.virt_type == 'lxc':
15271536
self._teardown_container(instance)
15281537
# We're sure the instance is gone, we can shutdown the core if so
1529-
libvirt_cpu.power_down(instance)
1538+
self.cpu_api.power_down(instance)
15301539

15311540
def destroy(self, context, instance, network_info, block_device_info=None,
15321541
destroy_disks=True, destroy_secrets=True):
@@ -3180,7 +3189,7 @@ def _resume_guest_after_snapshot(
31803189

31813190
current_power_state = guest.get_power_state(self._host)
31823191

3183-
libvirt_cpu.power_up(instance)
3192+
self.cpu_api.power_up(instance)
31843193
# TODO(stephenfin): Any reason we couldn't use 'self.resume' here?
31853194
guest.launch(pause=current_power_state == power_state.PAUSED)
31863195

@@ -7661,7 +7670,7 @@ def _create_guest(
76617670
post_xml_callback()
76627671

76637672
if power_on or pause:
7664-
libvirt_cpu.power_up(instance)
7673+
self.cpu_api.power_up(instance)
76657674
guest.launch(pause=pause)
76667675

76677676
return guest

0 commit comments

Comments
 (0)