Coverage for nova/virt/libvirt/cpu/api.py: 87%
121 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-24 11:16 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-24 11:16 +0000
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.
13from dataclasses import dataclass
14import typing as ty
16from oslo_log import log as logging
18import nova.conf
19from nova import exception
20from nova.i18n import _
21from nova import objects
22from nova.virt import hardware
23from nova.virt.libvirt.cpu import core
25LOG = logging.getLogger(__name__)
27CONF = nova.conf.CONF
30@dataclass
31class Core:
32 """Class to model a CPU core as reported by sysfs.
34 It may be a physical CPU core or a hardware thread on a shared CPU core
35 depending on if the system supports SMT.
36 """
38 # NOTE(sbauza): ident is a mandatory field.
39 # The CPU core id/number
40 ident: int
42 @property
43 def online(self) -> bool:
44 return core.get_online(self.ident)
46 @online.setter
47 def online(self, state: bool) -> None:
48 if state:
49 core.set_online(self.ident)
50 else:
51 core.set_offline(self.ident)
53 def __hash__(self):
54 return hash(self.ident)
56 def __eq__(self, other):
57 return self.ident == other.ident
59 def __str__(self):
60 return str(self.ident)
62 @property
63 def governor(self) -> ty.Optional[str]:
64 try:
65 return core.get_governor(self.ident)
66 # NOTE(sbauza): cpufreq/scaling_governor is not enabled for some OS
67 # platforms.
68 except exception.FileNotFound:
69 return None
71 def set_high_governor(self) -> None:
72 core.set_governor(self.ident, CONF.libvirt.cpu_power_governor_high)
74 def set_low_governor(self) -> None:
75 core.set_governor(self.ident, CONF.libvirt.cpu_power_governor_low)
78class API(object):
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.
90 See also nova.virt.libvirt.driver.LibvirtDriver.cpu_api.
91 """
92 return Core(i)
94 def power_up(self, cpus: ty.Set[int]) -> None:
95 if not CONF.libvirt.cpu_power_management:
96 return
97 cpu_dedicated_set = hardware.get_cpu_dedicated_set_nozero() or set()
98 powered_up = set()
99 for cpu in cpus:
100 if cpu in cpu_dedicated_set:
101 pcpu = self.core(cpu)
102 if CONF.libvirt.cpu_power_management_strategy == 'cpu_state':
103 pcpu.online = True
104 else:
105 pcpu.set_high_governor()
106 powered_up.add(str(pcpu))
107 LOG.debug("Cores powered up : %s", powered_up)
109 def power_up_for_instance(self, instance: objects.Instance) -> None:
110 if instance.numa_topology is None:
111 return
112 pcpus = instance.numa_topology.cpu_pinning.union(
113 instance.numa_topology.cpuset_reserved)
114 self.power_up(pcpus)
116 def power_up_for_migration(
117 self, dst_numa_info: objects.LibvirtLiveMigrateNUMAInfo
118 ) -> None:
119 pcpus = set()
120 if 'emulator_pins' in dst_numa_info and dst_numa_info.emulator_pins:
121 pcpus = dst_numa_info.emulator_pins
122 for pins in dst_numa_info.cpu_pins.values():
123 pcpus = pcpus.union(pins)
124 self.power_up(pcpus)
126 def _power_down(self, cpus: ty.Set[int]) -> None:
127 if not CONF.libvirt.cpu_power_management:
128 return
129 cpu_dedicated_set = hardware.get_cpu_dedicated_set_nozero() or set()
130 powered_down = set()
131 for cpu in cpus:
132 if cpu in cpu_dedicated_set:
133 pcpu = self.core(cpu)
134 if CONF.libvirt.cpu_power_management_strategy == 'cpu_state':
135 pcpu.online = False
136 else:
137 pcpu.set_low_governor()
138 powered_down.add(str(pcpu))
139 LOG.debug("Cores powered down : %s", powered_down)
141 def power_down_for_migration(
142 self, dst_numa_info: objects.LibvirtLiveMigrateNUMAInfo
143 ) -> None:
144 pcpus = set()
145 if 'emulator_pins' in dst_numa_info and dst_numa_info.emulator_pins:
146 pcpus = dst_numa_info.emulator_pins
147 for pins in dst_numa_info.cpu_pins.values():
148 pcpus = pcpus.union(pins)
149 self._power_down(pcpus)
151 def power_down_for_instance(self, instance: objects.Instance) -> None:
152 if instance.numa_topology is None:
153 return
154 pcpus = instance.numa_topology.cpu_pinning.union(
155 instance.numa_topology.cpuset_reserved)
156 self._power_down(pcpus)
158 def power_down_all_dedicated_cpus(self) -> None:
159 if not CONF.libvirt.cpu_power_management:
160 return
162 cpu_dedicated_set = hardware.get_cpu_dedicated_set_nozero() or set()
163 for pcpu in cpu_dedicated_set:
164 pcpu = self.core(pcpu)
165 if CONF.libvirt.cpu_power_management_strategy == 'cpu_state':
166 pcpu.online = False
167 else:
168 pcpu.set_low_governor()
169 LOG.debug("Cores powered down : %s", cpu_dedicated_set)
171 def validate_all_dedicated_cpus(self) -> None:
172 if not CONF.libvirt.cpu_power_management:
173 return
174 cpu_dedicated_set = hardware.get_cpu_dedicated_set() or set()
175 pcpus = []
176 for pcpu in cpu_dedicated_set:
177 if (pcpu == 0 and
178 CONF.libvirt.cpu_power_management_strategy == 'cpu_state'):
179 LOG.warning('CPU0 is in cpu_dedicated_set, '
180 'but it is not eligible for state management '
181 'and will be ignored')
182 continue
183 # we need to collect the governors strategy and the CPU states
184 pcpus.append(self.core(pcpu))
185 if CONF.libvirt.cpu_power_management_strategy == 'cpu_state':
186 # NOTE(sbauza): offline cores can't have a governor, it returns a
187 # DeviceBusy exception.
188 governors = set([pcpu.governor for pcpu in pcpus
189 if pcpu.online])
190 # all the cores need to have the same governor strategy
191 if len(governors) > 1:
192 msg = _("All the cores need to have the same governor strategy"
193 "before modifying the CPU states. You can reboot the "
194 "compute node if you prefer.")
195 raise exception.InvalidConfiguration(msg)
196 elif CONF.libvirt.cpu_power_management_strategy == 'governor': 196 ↛ exitline 196 didn't return from function 'validate_all_dedicated_cpus' because the condition on line 196 was always true
197 cpu_states = set([pcpu.online for pcpu in pcpus])
198 # all the cores need to be online
199 if False in cpu_states: 199 ↛ exitline 199 didn't return from function 'validate_all_dedicated_cpus' because the condition on line 199 was always true
200 msg = _("All the cores need to be online before modifying the "
201 "governor strategy.")
202 raise exception.InvalidConfiguration(msg)