Skip to content
This repository was archived by the owner on Jan 22, 2026. It is now read-only.

Commit 7de3935

Browse files
committed
virt-install: Add --numatune option
1 parent 8651b93 commit 7de3935

10 files changed

Lines changed: 225 additions & 62 deletions

File tree

man/en/virt-install.pod.in

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,20 @@ Set which physical cpus the guest can use. C<CPUSET> is a comma separated list o
126126
If the value 'auto' is passed, virt-install attempts to automatically determine
127127
an optimal cpu pinning using NUMA data, if available.
128128

129+
=item --numatune=NODESET,[mode=MODE]
130+
131+
Tune NUMA policy for the domain process. Example invocations
132+
133+
--numatune 1,2,3,4-7
134+
--numatune \"1-3,5\",mode=preferred
135+
136+
Specifies the numa nodes to allocate memory from. This has the same syntax
137+
as C<--cpuset> option. mode can be one of 'interleave', 'preferred', or
138+
'strict' (the default). See 'man 8 numactl' for information about each
139+
mode.
140+
141+
The nodeset string must use escaped-quotes if specifying any other option.
142+
129143
=item --cpu MODEL[,+feature][,-feature][,match=MATCH][,vendor=VENDOR]
130144

131145
Configure the CPU model and CPU features exposed to the guest. The only

tests/cli-test-xml/compare/many-devices.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
<on_reboot>destroy</on_reboot>
1717
<on_crash>destroy</on_crash>
1818
<vcpu>1</vcpu>
19+
<numatune>
20+
<memory mode='preferred' nodeset='1-3,5'/>
21+
</numatune>
1922
<devices>
2023
<emulator>/usr/bin/test-hv</emulator>
2124
<disk type='file' device='disk'>
@@ -79,6 +82,9 @@
7982
<on_reboot>restart</on_reboot>
8083
<on_crash>restart</on_crash>
8184
<vcpu>1</vcpu>
85+
<numatune>
86+
<memory mode='preferred' nodeset='1-3,5'/>
87+
</numatune>
8288
<devices>
8389
<emulator>/usr/bin/test-hv</emulator>
8490
<disk type='file' device='disk'>

tests/clitest.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,8 @@
448448
"--cpu somemodel",
449449
# Crazy --cpu
450450
"--cpu foobar,+x2apic,+x2apicagain,-distest,forbid=foo,forbid=bar,disable=distest2,optional=opttest,require=reqtest,match=strict,vendor=meee",
451+
# Simple --numatune
452+
"--numatune 1,2,3,5-7,^6",
451453
],
452454

453455
"invalid" : [
@@ -465,6 +467,8 @@
465467
"--vcpus foo=bar",
466468
# --cpu host, but no host CPU in caps
467469
"--cpu host",
470+
# Non-escaped numatune
471+
"--numatune 1-3,4,mode=strict",
468472
],
469473

470474
}, # category 'cpuram'
@@ -550,7 +554,8 @@
550554
"--network bridge=foobar,model=virtio "
551555
"--channel spicevmc "
552556
"--smartcard passthrough,type=spicevmc "
553-
"--security type=static,label='system_u:object_r:svirt_image_t:s0:c100,c200' ",
557+
"--security type=static,label='system_u:object_r:svirt_image_t:s0:c100,c200' "
558+
""" --numatune \\"1-3,5\\",mode=preferred """,
554559
"many-devices"),
555560
],
556561

tests/xmlparse-xml/change-guest-in.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
<features>
1111
<acpi/><apic/>
1212
</features>
13+
<numatune>
14+
<memory mode='interleave' nodeset='1-5,^3,7'/>
15+
</numatune>
1316
<cpu match='exact'>
1417
<model>footest</model>
1518
<vendor>Intel</vendor>

tests/xmlparse-xml/change-guest-out.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
<features>
1414
<apic/>
1515
</features>
16+
<numatune>
17+
<memory nodeset="2,4,6"/>
18+
</numatune>
1619
<cpu match="strict">
1720
<model>qemu64</model>
1821
<vendor>qemuvendor</vendor>

tests/xmlparse.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ def feature_checker(prop, origval, newval):
155155
guest.cpu.remove_feature(guest.cpu.features[1])
156156
guest.cpu.add_feature("addfeature")
157157

158+
check = self._make_checker(guest.numatune)
159+
check("memory_mode", "interleave", "strict", None)
160+
check("memory_nodeset", "1-5,^3,7", "2,4,6")
161+
158162
self._alter_compare(guest.get_config_xml(), outfile)
159163

160164
def testAlterMinimalGuest(self):

virt-install

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ def build_guest_instance(conn, options):
468468
cli.get_uuid(options.uuid, guest)
469469
cli.get_vcpus(options.vcpus, options.check_cpu, guest)
470470
cli.get_cpuset(options.cpuset, guest.memory, guest)
471+
cli.parse_numatune(guest, options.numatune)
471472
cli.parse_cpu(guest, options.cpu)
472473
cli.parse_security(options.security, guest)
473474
cli.parse_boot(guest, options.bootopts)
@@ -802,6 +803,8 @@ def parse_args():
802803
"the generated XML."))
803804
geng.add_option("", "--security", dest="security",
804805
help=_("Set domain security driver configuration."))
806+
geng.add_option("", "--numatune", dest="numatune",
807+
help=_("Tune NUMA policy for the domain process."))
805808
parser.add_option_group(geng)
806809

807810
insg = optparse.OptionGroup(parser, _("Installation Method Options"))

virtinst/DomainNumatune.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#
2+
# Copyright 2010 Red Hat, Inc.
3+
# Cole Robinson <crobinso@redhat.com>
4+
#
5+
# This program is free software; you can redistribute it and/or modify
6+
# it under the terms of the GNU General Public License as published by
7+
# the Free Software Foundation; either version 2 of the License, or
8+
# (at your option) any later version.
9+
#
10+
# This program is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU General Public License
16+
# along with this program; if not, write to the Free Software
17+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18+
# MA 02110-1301 USA.
19+
20+
import re
21+
22+
import libxml2
23+
24+
import _util
25+
import XMLBuilderDomain
26+
from XMLBuilderDomain import _xml_property
27+
from virtinst import _gettext as _
28+
29+
class DomainNumatune(XMLBuilderDomain.XMLBuilderDomain):
30+
"""
31+
Class for generating <numatune> XML
32+
"""
33+
34+
@staticmethod
35+
def validate_cpuset(conn, val):
36+
if val is None or val == "":
37+
return
38+
39+
if type(val) is not type("string") or len(val) == 0:
40+
raise ValueError(_("cpuset must be string"))
41+
if re.match("^[0-9,-^]*$", val) is None:
42+
raise ValueError(_("cpuset can only contain numeric, ',', or "
43+
"'-' characters"))
44+
45+
pcpus = _util.get_phy_cpus(conn)
46+
for c in val.split(','):
47+
# Redundant commas
48+
if not c:
49+
continue
50+
51+
if "-" in c:
52+
(x, y) = c.split('-', 1)
53+
x = int(x)
54+
y = int(y)
55+
if x > y:
56+
raise ValueError(_("cpuset contains invalid format."))
57+
if x >= pcpus or y >= pcpus:
58+
raise ValueError(_("cpuset's pCPU numbers must be less "
59+
"than pCPUs."))
60+
else:
61+
if c.startswith("^"):
62+
c = c[1:]
63+
c = int(c)
64+
65+
if c >= pcpus:
66+
raise ValueError(_("cpuset's pCPU numbers must be less "
67+
"than pCPUs."))
68+
69+
@staticmethod
70+
def cpuset_str_to_tuple(conn, cpuset):
71+
DomainNumatune.validate_cpuset(conn, cpuset)
72+
pinlist = [False] * _util.get_phy_cpus(conn)
73+
74+
entries = cpuset.split(",")
75+
for e in entries:
76+
series = e.split("-", 1)
77+
78+
if len(series) == 1:
79+
pinlist[int(series[0])] = True
80+
continue
81+
82+
start = int(series[0])
83+
end = int(series[1])
84+
85+
for i in range(start, end + 1):
86+
pinlist[i] = True
87+
88+
return tuple(pinlist)
89+
90+
_dumpxml_xpath = "/domain/numatune"
91+
92+
MEMORY_MODES = ["interleave", "strict", "preferred"]
93+
94+
def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None):
95+
self._memory_nodeset = None
96+
self._memory_mode = None
97+
98+
XMLBuilderDomain.XMLBuilderDomain.__init__(self, conn, parsexml,
99+
parsexmlnode, caps)
100+
if self._is_parse():
101+
return
102+
103+
def _get_memory_nodeset(self):
104+
return self._memory_nodeset
105+
def _set_memory_nodeset(self, val):
106+
self._memory_nodeset = val
107+
memory_nodeset = _xml_property(_get_memory_nodeset,
108+
_set_memory_nodeset,
109+
xpath="./numatune/memory/@nodeset")
110+
111+
def _get_memory_mode(self):
112+
return self._memory_mode
113+
def _set_memory_mode(self, val):
114+
self._memory_mode = val
115+
memory_mode = _xml_property(_get_memory_mode,
116+
_set_memory_mode,
117+
xpath="./numatune/memory/@mode")
118+
119+
def _get_memory_xml(self):
120+
if not self.memory_nodeset:
121+
return ""
122+
123+
xml = " <memory"
124+
if self.memory_mode:
125+
xml += " mode='%s'" % self.memory_mode
126+
if self.memory_nodeset:
127+
xml += " nodeset='%s'" % self.memory_nodeset
128+
xml += "/>\n"
129+
return xml
130+
131+
def _get_xml_config(self):
132+
mem_xml = self._get_memory_xml()
133+
if not mem_xml:
134+
return ""
135+
136+
xml = " <numatune>\n"
137+
xml += mem_xml
138+
xml += " </numatune>"
139+
return xml

virtinst/Guest.py

Lines changed: 10 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
import os
2323
import time
24-
import re
2524
import logging
2625
import signal
2726

@@ -44,47 +43,13 @@
4443
from Clock import Clock
4544
from Seclabel import Seclabel
4645
from CPU import CPU
46+
from DomainNumatune import DomainNumatune
4747
from DomainFeatures import DomainFeatures
4848

4949
import osdict
5050
from virtinst import _gettext as _
5151

5252

53-
def _validate_cpuset(conn, val):
54-
if val is None or val == "":
55-
return
56-
57-
if type(val) is not type("string") or len(val) == 0:
58-
raise ValueError(_("cpuset must be string"))
59-
if re.match("^[0-9,-^]*$", val) is None:
60-
raise ValueError(_("cpuset can only contain numeric, ',', or "
61-
"'-' characters"))
62-
63-
pcpus = _util.get_phy_cpus(conn)
64-
for c in val.split(','):
65-
# Redundant commas
66-
if not c:
67-
continue
68-
69-
if "-" in c:
70-
(x, y) = c.split('-', 1)
71-
x = int(x)
72-
y = int(y)
73-
if x > y:
74-
raise ValueError(_("cpuset contains invalid format."))
75-
if x >= pcpus or y >= pcpus:
76-
raise ValueError(_("cpuset's pCPU numbers must be less "
77-
"than pCPUs."))
78-
else:
79-
if c.startswith("^"):
80-
c = c[1:]
81-
c = int(c)
82-
83-
if c >= pcpus:
84-
raise ValueError(_("cpuset's pCPU numbers must be less "
85-
"than pCPUs."))
86-
return
87-
8853
class Guest(XMLBuilderDomain.XMLBuilderDomain):
8954

9055
# OS Dictionary static variables and methods
@@ -151,24 +116,7 @@ def get_os_variant_label(type, variant):
151116

152117
@staticmethod
153118
def cpuset_str_to_tuple(conn, cpuset):
154-
_validate_cpuset(conn, cpuset)
155-
pinlist = [False] * _util.get_phy_cpus(conn)
156-
157-
entries = cpuset.split(",")
158-
for e in entries:
159-
series = e.split("-", 1)
160-
161-
if len(series) == 1:
162-
pinlist[int(series[0])] = True
163-
continue
164-
165-
start = int(series[0])
166-
end = int(series[1])
167-
168-
for i in range(start, end + 1):
169-
pinlist[i] = True
170-
171-
return tuple(pinlist)
119+
return DomainNumatune.cpuset_str_to_tuple(conn, cpuset)
172120

173121
@staticmethod
174122
def generate_cpuset(conn, mem):
@@ -307,6 +255,7 @@ def __init__(self, type=None, connection=None, hypervisorURI=None,
307255
self._clock = Clock(self.conn)
308256
self._seclabel = Seclabel(self.conn)
309257
self._cpu = CPU(self.conn)
258+
self._numatune = DomainNumatune(self.conn)
310259

311260
def _open_uri(self, uri):
312261
# This is here so test suite can overwrite it, to make sure
@@ -332,6 +281,9 @@ def get_seclabel(self):
332281
def get_cpu(self):
333282
return self._cpu
334283
cpu = property(get_cpu)
284+
def get_numatune(self):
285+
return self._numatune
286+
numatune = property(get_numatune)
335287

336288
def _get_features(self):
337289
return self._features
@@ -447,7 +399,7 @@ def set_cpuset(self, val):
447399
self._cpuset = None
448400
return
449401

450-
_validate_cpuset(self.conn, val)
402+
DomainNumatune.validate_cpuset(self.conn, val)
451403
self._cpuset = val
452404
cpuset = _xml_property(get_cpuset, set_cpuset,
453405
xpath="./vcpu/@cpuset")
@@ -852,6 +804,8 @@ def _parsexml(self, xml, node):
852804
self._seclabel = Seclabel(self.conn, parsexmlnode=self._xml_node,
853805
caps=caps)
854806
self._cpu = CPU(self.conn, parsexmlnode=self._xml_node, caps=caps)
807+
self._numatune = DomainNumatune(self.conn,
808+
parsexmlnode=self._xml_node, caps=caps)
855809

856810
def _get_default_input_device(self):
857811
"""
@@ -1097,6 +1051,7 @@ def remove_transient_device(device):
10971051
xml = add(" <on_reboot>%s</on_reboot>" % action)
10981052
xml = add(" <on_crash>%s</on_crash>" % action)
10991053
xml = add(self._get_vcpu_xml())
1054+
xml = add(self.numatune.get_xml_config())
11001055
xml = add(" <devices>")
11011056
xml = add(self._get_device_xml(devs, install))
11021057
xml = add(" </devices>")

0 commit comments

Comments
 (0)