2 Library to perform pre/post test setup for KVM autotest.
4 import os
, logging
, time
, re
, sre
, random
5 from autotest_lib
.client
.common_lib
import error
6 from autotest_lib
.client
.bin
import utils
9 class THPError(Exception):
11 Base exception for Transparent Hugepage setup.
16 class THPNotSupportedError(THPError
):
18 Thrown when host does not support tansparent hugepages.
23 class THPWriteConfigError(THPError
):
25 Thrown when host does not support tansparent hugepages.
30 class THPKhugepagedError(THPError
):
32 Thrown when khugepaged is not behaving as expected.
37 class TransparentHugePageConfig(object):
38 def __init__(self
, test
, params
):
40 Find paths for transparent hugepages and kugepaged configuration. Also,
41 back up original host configuration so it can be restored during
46 RH_THP_PATH
= "/sys/kernel/mm/redhat_transparent_hugepage"
47 UPSTREAM_THP_PATH
= "/sys/kernel/mm/transparent_hugepage"
48 if os
.path
.isdir(RH_THP_PATH
):
49 self
.thp_path
= RH_THP_PATH
50 elif os
.path
.isdir(UPSTREAM_THP_PATH
):
51 self
.thp_path
= UPSTREAM_THP_PATH
53 raise THPNotSupportedError("System doesn't support transparent "
58 test_config
= self
.params
.get("test_config", None)
59 if test_config
is not None:
60 tmp_list
= re
.split(';', test_config
)
61 while len(tmp_list
) > 0:
62 tmp_cfg
= tmp_list
.pop()
63 test_cfg
[re
.split(":", tmp_cfg
)[0]] = sre
.split(":", tmp_cfg
)[1]
64 # Save host current config, so we can restore it during cleanup
65 # We will only save the writeable part of the config files
67 # List of files that contain string config values
68 self
.file_list_str
= []
69 # List of files that contain integer config values
70 self
.file_list_num
= []
71 for f
in os
.walk(self
.thp_path
):
75 f_dir
= os
.path
.join(base_dir
, name
)
76 parameter
= file(f_dir
, 'r').read()
78 # Verify if the path in question is writable
81 if re
.findall("\[(.*)\]", parameter
):
82 original_config
[f_dir
] = re
.findall("\[(.*)\]",
84 self
.file_list_str
.append(f_dir
)
86 original_config
[f_dir
] = int(parameter
)
87 self
.file_list_num
.append(f_dir
)
91 self
.test_config
= test_cfg
92 self
.original_config
= original_config
97 Applies test configuration on the host.
100 for path
in self
.test_config
.keys():
101 file(path
, 'w').write(self
.test_config
[path
])
104 def value_listed(self
, value
):
106 Get a parameters list from a string
109 for i
in re
.split("\[|\]|\n+|\s+", value
):
115 def khugepaged_test(self
):
117 Start, stop and frequency change test for khugepaged.
119 def check_status_with_value(action_list
, file_name
):
121 Check the status of khugepaged when set value to specify file.
123 for (a
, r
) in action_list
:
124 open(file_name
, "w").write(a
)
127 utils
.run('pgrep khugepaged')
129 raise THPKhugepagedError("Khugepaged still alive when"
130 "transparent huge page is "
132 except error
.CmdError
:
134 raise THPKhugepagedError("Khugepaged could not be set to"
138 for file_path
in self
.file_list_str
:
140 if re
.findall("enabled", file_path
):
141 # Start and stop test for khugepaged
142 value_list
= self
.value_listed(open(file_path
,"r").read())
144 if re
.match("n", i
, re
.I
):
145 action_stop
= (i
, 256)
147 if re
.match("[^n]", i
, re
.I
):
149 action_list
+= [action_stop
, action
, action_stop
]
150 action_list
+= [action
]
152 check_status_with_value(action_list
, file_path
)
154 value_list
= self
.value_listed(open(file_path
,"r").read())
157 action_list
.append(action
)
158 check_status_with_value(action_list
, file_path
)
160 for file_path
in self
.file_list_num
:
162 value
= int(open(file_path
, "r").read())
163 if value
!= 0 and value
!= 1:
164 new_value
= random
.random()
165 action_list
.append((str(int(value
* new_value
)),0))
166 action_list
.append((str(int(value
* ( new_value
+ 1))),0))
168 action_list
.append(("0", 0))
169 action_list
.append(("1", 0))
171 check_status_with_value(action_list
, file_path
)
176 Configure host for testing. Also, check that khugepaged is working as
180 self
.khugepaged_test()
185 Restore the host's original configuration after test
187 for path
in self
.original_config
:
188 p_file
= open(path
, 'w')
189 p_file
.write(str(self
.original_config
[path
]))
193 class HugePageConfig(object):
194 def __init__(self
, params
):
196 Gets environment variable values and calculates the target number
197 of huge memory pages.
199 @param params: Dict like object containing parameters for the test.
201 self
.vms
= len(params
.objects("vms"))
202 self
.mem
= int(params
.get("mem"))
203 self
.max_vms
= int(params
.get("max_vms", 0))
204 self
.hugepage_path
= '/mnt/kvm_hugepage'
205 self
.hugepage_size
= self
.get_hugepage_size()
206 self
.target_hugepages
= self
.get_target_hugepages()
207 self
.kernel_hp_file
= '/proc/sys/vm/nr_hugepages'
210 def get_hugepage_size(self
):
212 Get the current system setting for huge memory page size.
214 meminfo
= open('/proc/meminfo', 'r').readlines()
215 huge_line_list
= [h
for h
in meminfo
if h
.startswith("Hugepagesize")]
217 return int(huge_line_list
[0].split()[1])
218 except ValueError, e
:
219 raise ValueError("Could not get huge page size setting from "
220 "/proc/meminfo: %s" % e
)
223 def get_target_hugepages(self
):
225 Calculate the target number of hugepages for testing purposes.
227 if self
.vms
< self
.max_vms
:
228 self
.vms
= self
.max_vms
229 # memory of all VMs plus qemu overhead of 64MB per guest
230 vmsm
= (self
.vms
* self
.mem
) + (self
.vms
* 64)
231 return int(vmsm
* 1024 / self
.hugepage_size
)
235 def set_hugepages(self
):
237 Sets the hugepage limit to the target hugepage value calculated.
239 error
.context("setting hugepages limit to %s" % self
.target_hugepages
)
240 hugepage_cfg
= open(self
.kernel_hp_file
, "r+")
241 hp
= hugepage_cfg
.readline()
242 while int(hp
) < self
.target_hugepages
:
244 hugepage_cfg
.write(str(self
.target_hugepages
))
247 hp
= int(hugepage_cfg
.readline())
249 raise ValueError("Cannot set the kernel hugepage setting "
250 "to the target value of %d hugepages." %
251 self
.target_hugepages
)
253 logging
.debug("Successfuly set %s large memory pages on host ",
254 self
.target_hugepages
)
258 def mount_hugepage_fs(self
):
260 Verify if there's a hugetlbfs mount set. If there's none, will set up
261 a hugetlbfs mount using the class attribute that defines the mount
264 error
.context("mounting hugepages path")
265 if not os
.path
.ismount(self
.hugepage_path
):
266 if not os
.path
.isdir(self
.hugepage_path
):
267 os
.makedirs(self
.hugepage_path
)
268 cmd
= "mount -t hugetlbfs none %s" % self
.hugepage_path
273 logging
.debug("Number of VMs this test will use: %d", self
.vms
)
274 logging
.debug("Amount of memory used by each vm: %s", self
.mem
)
275 logging
.debug("System setting for large memory page size: %s",
277 logging
.debug("Number of large memory pages needed for this test: %s",
278 self
.target_hugepages
)
280 self
.mount_hugepage_fs()
285 error
.context("trying to dealocate hugepage memory")
287 utils
.system("umount %s" % self
.hugepage_path
)
288 except error
.CmdError
:
290 utils
.system("echo 0 > %s" % self
.kernel_hp_file
)
291 logging
.debug("Hugepage memory successfuly dealocated")
294 class PrivateBridgeError(Exception):
295 def __init__(self
, brname
):
299 return "Bridge %s not available after setup" % self
.brname
302 class PrivateBridgeConfig(object):
304 def __init__(self
, params
=None):
305 self
.__dict
__ = self
.__shared
_state
306 if params
is not None:
307 self
.brname
= params
.get("priv_brname", 'atbr0')
308 self
.subnet
= params
.get("priv_subnet", '192.168.58')
309 self
.ip_version
= params
.get("bridge_ip_version", "ipv4")
310 self
.dhcp_server_pid
= None
311 ports
= params
.get("priv_bridge_ports", '53 67').split()
312 s_port
= params
.get("guest_port_remote_shell", "10022")
313 if s_port
not in ports
:
315 ft_port
= params
.get("guest_port_file_transfer", "10023")
316 if ft_port
not in ports
:
317 ports
.append(ft_port
)
318 u_port
= params
.get("guest_port_unattended_install", "13323")
319 if u_port
not in ports
:
321 self
.iptables_rules
= self
._assemble
_iptables
_rules
(ports
)
324 def _assemble_iptables_rules(self
, port_list
):
327 for port
in port_list
:
329 rules
.append("INPUT %s -i %s -p tcp -m tcp --dport %s -j ACCEPT" %
330 (index
, self
.brname
, port
))
332 rules
.append("INPUT %s -i %s -p udp -m udp --dport %s -j ACCEPT" %
333 (index
, self
.brname
, port
))
334 rules
.append("FORWARD 1 -m physdev --physdev-is-bridged -j ACCEPT")
335 rules
.append("FORWARD 2 -d %s.0/24 -o %s -m state "
336 "--state RELATED,ESTABLISHED -j ACCEPT" %
337 (self
.subnet
, self
.brname
))
338 rules
.append("FORWARD 3 -s %s.0/24 -i %s -j ACCEPT" %
339 (self
.subnet
, self
.brname
))
340 rules
.append("FORWARD 4 -i %s -o %s -j ACCEPT" %
341 (self
.brname
, self
.brname
))
345 def _add_bridge(self
):
346 utils
.system("brctl addbr %s" % self
.brname
)
347 ip_fwd_path
= "/proc/sys/net/%s/ip_forward" % self
.ip_version
348 ip_fwd
= open(ip_fwd_path
, "w")
350 utils
.system("brctl stp %s on" % self
.brname
)
351 utils
.system("brctl setfd %s 0" % self
.brname
)
354 def _bring_bridge_up(self
):
355 utils
.system("ifconfig %s %s.1 up" % (self
.brname
, self
.subnet
))
358 def _iptables_add(self
, cmd
):
359 return utils
.system("iptables -I %s" % cmd
)
362 def _iptables_del(self
, cmd
):
363 return utils
.system("iptables -D %s" % cmd
)
366 def _enable_nat(self
):
367 for rule
in self
.iptables_rules
:
368 self
._iptables
_add
(rule
)
371 def _start_dhcp_server(self
):
372 utils
.run("service dnsmasq stop")
373 utils
.run("dnsmasq --strict-order --bind-interfaces "
374 "--listen-address %s.1 --dhcp-range %s.2,%s.254 "
375 "--dhcp-lease-max=253 "
376 "--dhcp-no-override "
377 "--pid-file=/tmp/dnsmasq.pid "
378 "--log-facility=/tmp/dnsmasq.log" %
379 (self
.subnet
, self
.subnet
, self
.subnet
))
380 self
.dhcp_server_pid
= None
382 self
.dhcp_server_pid
= int(open('/tmp/dnsmasq.pid', 'r').read())
384 raise PrivateBridgeError(self
.brname
)
385 logging
.debug("Started internal DHCP server with PID %s",
386 self
.dhcp_server_pid
)
389 def _verify_bridge(self
):
390 brctl_output
= utils
.system_output("brctl show")
391 if self
.brname
not in brctl_output
:
392 raise PrivateBridgeError(self
.brname
)
396 brctl_output
= utils
.system_output("brctl show")
397 if self
.brname
not in brctl_output
:
398 logging
.debug("Configuring KVM test private bridge %s", self
.brname
)
402 self
._remove
_bridge
()
405 self
._bring
_bridge
_up
()
407 self
._bring
_bridge
_down
()
408 self
._remove
_bridge
()
414 self
._bring
_bridge
_down
()
415 self
._remove
_bridge
()
418 self
._start
_dhcp
_server
()
420 self
._stop
_dhcp
_server
()
422 self
._bring
_bridge
_down
()
423 self
._remove
_bridge
()
425 self
._verify
_bridge
()
428 def _stop_dhcp_server(self
):
429 if self
.dhcp_server_pid
is not None:
431 os
.kill(self
.dhcp_server_pid
, 15)
436 dhcp_server_pid
= int(open('/tmp/dnsmasq.pid', 'r').read())
440 os
.kill(dhcp_server_pid
, 15)
445 def _bring_bridge_down(self
):
446 utils
.system("ifconfig %s down" % self
.brname
, ignore_status
=True)
449 def _disable_nat(self
):
450 for rule
in self
.iptables_rules
:
451 split_list
= rule
.split(' ')
452 # We need to remove numbering here
454 rule
= " ".join(split_list
)
455 self
._iptables
_del
(rule
)
458 def _remove_bridge(self
):
459 utils
.system("brctl delbr %s" % self
.brname
, ignore_status
=True)
463 brctl_output
= utils
.system_output("brctl show")
465 for line
in brctl_output
.split("\n"):
466 if line
.startswith(self
.brname
):
467 # len == 4 means there is a TAP using the bridge
468 # so don't try to clean it up
469 if len(line
.split()) < 4:
473 logging
.debug("Cleaning up KVM test private bridge %s", self
.brname
)
474 self
._stop
_dhcp
_server
()
476 self
._bring
_bridge
_down
()
477 self
._remove
_bridge
()