071426c223
These tests are meant to address issue [1]. It adds three new testcases: * test_hugepage_resize_keyword_large_to_small * test_hugepage_resize_explicit_pagesize_to_small * test_hugepage_resize_explicit_size_to_size All three tests follow the same basic procedure, spawn a guest with a flavor using hw:mem_page_size:<size_a>, resize the guest to a flavor with a different size hw:mem_page_size:<size_b>, and then resize the guest back to the original flavor. Throughout the tests XML checks are conducted to ensure the page size is accurate for the present flavor. Instead of trying to dynamically determine the hugepage sizes configured on the computes, a new config parameter was added to define what hugepage sizes are available on the host. To avoid dynamic ram calculation sizes for the guest based on available hugepages, a guest ram parameter was also added so users may define the size to use when spawning guests. We also need a new job that has multiple hugepage sizes configured. We cannot use our existing whitebox-devstack-multinode job because that one runs tests that dynamically turn on file backed memory, which is incompatible with hugepages. This commit adds tasks into job setup that allows for the setup of hugepages. In our devstack plugin.sh, we set track_instance_changes to True (devstack defaults it to False) to make sure the scheduler has the latest information about available huge pages, and avoid a race whereing instances failed to schedule because our lone 1G page still appeared used by an instance that had actually beed fully deleted. [1] https://bugs.launchpad.net/nova/+bug/1831269 Change-Id: I5282df3b20c24a909f3b7bb97214206bc07e5b91
197 lines
8.6 KiB
Python
197 lines
8.6 KiB
Python
# Copyright 2022 Red Hat Inc.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from tempest import config
|
|
import testtools
|
|
from whitebox_tempest_plugin.api.compute import base
|
|
|
|
from oslo_log import log as logging
|
|
|
|
CONF = config.CONF
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class HugePageResize(base.BaseWhiteboxComputeTest):
|
|
|
|
@classmethod
|
|
def skip_checks(cls):
|
|
super(HugePageResize, cls).skip_checks()
|
|
if len(getattr(CONF.whitebox_hardware,
|
|
'configured_hugepage_sizes')) == 0:
|
|
msg = "configured_hugepage_sizes in whitebox-hardware is not " \
|
|
"present"
|
|
raise cls.skipException(msg)
|
|
|
|
def _get_xml_hugepage_size(self, server_id):
|
|
"""Analyze the hugepage xml element(s) from a provided instance. Expect
|
|
to find only one hugepage element in the domain. Return boolean result
|
|
comparing if the found page size is equal to the expected page size.
|
|
"""
|
|
huge_pages_list = self._get_hugepage_xml_element(server_id)
|
|
self.assertEqual(1, len(huge_pages_list), "Expected to find 1 "
|
|
"hugepage XML element on server %s but found %s"
|
|
% (server_id, len(huge_pages_list)))
|
|
huge_page_xml = huge_pages_list[0]
|
|
return int(huge_page_xml.attrib['size'])
|
|
|
|
def test_hugepage_resize_large_to_small(self):
|
|
"""Resize a guest with large hugepages to small hugepages and back
|
|
|
|
Create a guest using a flavor with hw:mem_page_size:large, resize it
|
|
to a flavor with hw:mem_page_size:small, and then resize it back to
|
|
the original flavor
|
|
"""
|
|
flavor_a = self.create_flavor(
|
|
ram=str(CONF.whitebox.hugepage_guest_ram_size),
|
|
extra_specs={'hw:mem_page_size': 'large'})
|
|
|
|
server = self.create_test_server(flavor=flavor_a['id'],
|
|
wait_until='ACTIVE')
|
|
|
|
# Cannot assume the exact pagesize of the guest, verify the backing
|
|
# memory element is present on the guest and the found size is greater
|
|
# than or equal to the smallest potential size configured in the
|
|
# environment
|
|
large_page_size = self._get_xml_hugepage_size(server['id'])
|
|
minimum_pagesize_threshold = \
|
|
min(CONF.whitebox_hardware.configured_hugepage_sizes)
|
|
self.assertTrue(
|
|
large_page_size >= minimum_pagesize_threshold,
|
|
"Pagesize found %s should be greater than or equal to pagesize "
|
|
"of %s for server %s" %
|
|
(large_page_size, minimum_pagesize_threshold, server['id'])
|
|
)
|
|
|
|
# Resize the guest using a flavor with hw:mem_page_size:small,
|
|
# memory backing element should not be present on guest currently so
|
|
# no need for XML verification
|
|
flavor_b = self.create_flavor(
|
|
ram=str(CONF.whitebox.hugepage_guest_ram_size),
|
|
extra_specs={'hw:mem_page_size': 'small'})
|
|
self.resize_server(server['id'], flavor_b['id'])
|
|
|
|
# Resize instance back to staring flavor size and repeat XML check of
|
|
# the guest
|
|
self.resize_server(server['id'], flavor_a['id'])
|
|
large_page_size = self._get_xml_hugepage_size(server['id'])
|
|
self.assertTrue(
|
|
large_page_size >= minimum_pagesize_threshold,
|
|
"After resizing back to original flavor, pagesize found %s should "
|
|
"be greater than or equal to pagesize of %s for server %s" %
|
|
(large_page_size, minimum_pagesize_threshold, server['id'])
|
|
)
|
|
|
|
def test_hugepage_resize_size_to_small(self):
|
|
"""Resize a guest with a specified hugepage size to small hugepages
|
|
|
|
Create a guest using a flavor with using an explicit hugepage size(s),
|
|
based on what is configured in whitebox_hardware. Resize the guest to a
|
|
flavor with hw:mem_page_size:small, and then resize it back to the
|
|
original flavor. Repeat this process for every hugepage size configured
|
|
in in whitebox_hardware.configured_hugepage_sizes
|
|
"""
|
|
flavor_small = self.create_flavor(
|
|
ram=str(CONF.whitebox.hugepage_guest_ram_size),
|
|
extra_specs={'hw:mem_page_size': 'small'})
|
|
|
|
# Create a flavor and launch an instance based on every configured
|
|
# hugepage size in the deployment.
|
|
for page_size in CONF.whitebox_hardware.configured_hugepage_sizes:
|
|
flavor_a = self.create_flavor(
|
|
ram=str(CONF.whitebox.hugepage_guest_ram_size),
|
|
extra_specs={'hw:mem_page_size': str(page_size)})
|
|
|
|
server = self.create_test_server(flavor=flavor_a['id'],
|
|
wait_until='ACTIVE')
|
|
|
|
size_found = self._get_xml_hugepage_size(server['id'])
|
|
self.assertTrue(
|
|
page_size == size_found,
|
|
"Expected pagesize of %s not found on server %s instead "
|
|
"found %s" % (page_size, server['id'], size_found)
|
|
)
|
|
|
|
# Resize the guest using a flavor with hw:mem_page_size:small,
|
|
# memory backing will not be present in with guest so follow up
|
|
# XML verification is not necessary
|
|
self.resize_server(server['id'], flavor_small['id'])
|
|
|
|
# Resize back to its original size and confirm memory backing
|
|
# element is present and has the correct size
|
|
self.resize_server(server['id'], flavor_a['id'])
|
|
size_found = self._get_xml_hugepage_size(server['id'])
|
|
self.assertTrue(
|
|
page_size == size_found,
|
|
"Expected pagesize of %s not found on server %s after "
|
|
"resizing back to original flavor size, instead found %s" %
|
|
(page_size, server['id'], size_found)
|
|
)
|
|
|
|
self.delete_server(server['id'])
|
|
|
|
@testtools.skipUnless(
|
|
len(CONF.whitebox_hardware.configured_hugepage_sizes) > 1,
|
|
'Need at least 2 configured hugepage sizes to execute test')
|
|
def test_hugepage_resize_size_to_size(self):
|
|
"""Resize a guest with a specified hugepage size to another size
|
|
|
|
Create two flavors based on the two provided hugepage sizes. The
|
|
flavors created use explicit sizes Create a
|
|
server using the first flavor, resize the guest to the second flavor,
|
|
and resize back to the original spec
|
|
"""
|
|
start_size, target_size = \
|
|
CONF.whitebox_hardware.configured_hugepage_sizes
|
|
|
|
flavor_a = self.create_flavor(
|
|
ram=str(CONF.whitebox.hugepage_guest_ram_size),
|
|
extra_specs={'hw:mem_page_size': str(start_size)})
|
|
|
|
server = self.create_test_server(flavor=flavor_a['id'],
|
|
wait_until='ACTIVE')
|
|
|
|
size_found = self._get_xml_hugepage_size(server['id'])
|
|
self.assertTrue(
|
|
start_size == size_found,
|
|
"Expected pagesize of %s not found on server %s instead "
|
|
"found %s" % (start_size, server['id'], size_found)
|
|
)
|
|
|
|
flavor_b = self.create_flavor(
|
|
ram=str(CONF.whitebox.hugepage_guest_ram_size),
|
|
extra_specs={'hw:mem_page_size': str(target_size)})
|
|
|
|
# Resize to the target size and confirm memory backing element is
|
|
# present and has the correct size
|
|
self.resize_server(server['id'], flavor_b['id'])
|
|
size_found = self._get_xml_hugepage_size(server['id'])
|
|
self.assertTrue(
|
|
target_size == size_found,
|
|
"Expected pagesize of %s not found on server %s after resize "
|
|
"instead found %s" % (target_size, server['id'], size_found)
|
|
)
|
|
|
|
# Resize back to its original size and confirm memory backing
|
|
# element is present and has the correct size
|
|
self.resize_server(server['id'], flavor_a['id'])
|
|
size_found = self._get_xml_hugepage_size(server['id'])
|
|
|
|
self.assertTrue(
|
|
start_size == size_found,
|
|
"Expected pagesize of %s not found on server %s after resizing "
|
|
"back to original flavor size, instead found %s" %
|
|
(start_size, server['id'], size_found)
|
|
)
|