Skip to content

Commit

Permalink
machine_os: Helpers for enabling hugepages on unit
Browse files Browse the repository at this point in the history
Add helpers to update grub configuration and reboot units to
enable hugepages.  This is necessary to reserve the hugepages
before userspace applications fragment the system memory with
their memory allocations.

The reboot helper has value on its own for other types of tests,
for example for on-metal SR-IOV testing.

(cherry picked from commit c6ddb0c)
  • Loading branch information
fnordahl committed Jul 21, 2023
1 parent c553434 commit 39d1ce2
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 0 deletions.
100 changes: 100 additions & 0 deletions unit_tests/utilities/test_zaza_utilities_machine_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,103 @@ def test_disable_vfio_unsafe_noiommu_mode(self):
model_name='aModel')
self._set_vfio_unsafe_noiommu_mode.assert_called_once_with(
unit, False, model_name='aModel')

def test_get_hv_application(self):
self.patch_object(machine_os_utils.zaza.charm_lifecycle.utils,
'get_config_options')
self.get_config_options.return_value = {
machine_os_utils.HV_APPLICATION_KEY: 'someApp'
}
self.assertEquals(machine_os_utils.get_hv_application(), 'someApp')

def test_reboot_hvs(self):
# No hv_application
self.patch_object(machine_os_utils, 'get_hv_application')
self.patch_object(machine_os_utils.zaza.model, 'get_units')
machine_os_utils.reboot_hvs()
self.assertFalse(self.get_units.called)

# No arguments provided to function
self.get_hv_application.return_value = 'someApp'
unit = mock.MagicMock()
unit.name = 'someApp/0'
self.get_units.return_value = [unit]
self.patch_object(machine_os_utils.zaza.utilities.generic, 'reboot')
self.patch_object(machine_os_utils.zaza.model,
'block_until_unit_wl_status')
self.patch_object(machine_os_utils.zaza.charm_lifecycle.utils,
'get_charm_config')
self.get_charm_config.return_value = {
'target_deploy_status': {'someDeployStatus': None}}
self.patch_object(machine_os_utils.zaza.model,
'wait_for_application_states')
machine_os_utils.reboot_hvs()
self.get_units.assert_called_once_with('someApp')
self.reboot.assert_called_once_with('someApp/0')
self.wait_for_application_states.assert_called_once_with(
states={'someDeployStatus': None})

# Units provided as argument
self.get_units.reset_mock()
self.reboot.reset_mock()
self.wait_for_application_states.reset_mock()
machine_os_utils.reboot_hvs(units=[unit])
self.assertFalse(self.get_units.called)
self.reboot.assert_called_once_with('someApp/0')
self.wait_for_application_states.assert_called_once_with(
states={'someDeployStatus': None})

def test_enable_hugepages(self):
self.patch_object(machine_os_utils.zaza.utilities.juju, 'remote_run')
self.remote_run.return_value = '41\n'
self.patch_object(machine_os_utils, 'reboot_hvs')
unit = mock.MagicMock()
unit.name = 'someApp/0'
with self.assertRaises(AssertionError):
machine_os_utils.enable_hugepages(unit, 42, model_name='aModel')

self.remote_run.reset_mock()
self.reboot_hvs.reset_mock()
self.remote_run.return_value = '42\n'
machine_os_utils.enable_hugepages(unit, 42, model_name='aModel')
self.reboot_hvs.assert_called_once_with([unit])
self.remote_run.assert_has_calls([
mock.call(
'someApp/0',
'echo \'GRUB_CMDLINE_LINUX_DEFAULT='
'"default_hugepagesz=1G hugepagesz=1G hugepages=42"\''
' > /etc/default/grub.d/99-zaza-hugepages.cfg && update-grub',
model_name='aModel', fatal=True),
mock.call(
'someApp/0',
'cat /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages'
'',
model_name='aModel',
fatal=True)
])

def test_disable_hugepages(self):
self.patch_object(machine_os_utils.zaza.utilities.juju, 'remote_run')
self.remote_run.return_value = '41\n'
self.patch_object(machine_os_utils, 'reboot_hvs')
unit = mock.MagicMock()
unit.name = 'someApp/0'
with self.assertRaises(AssertionError):
machine_os_utils.disable_hugepages(unit, model_name='aModel')

self.remote_run.reset_mock()
self.reboot_hvs.reset_mock()
self.remote_run.return_value = '0\n'
machine_os_utils.disable_hugepages(unit, model_name='aModel')
self.reboot_hvs.assert_called_once_with([unit])
self.remote_run.assert_has_calls([
mock.call(
'someApp/0',
'rm /etc/default/grub.d/99-zaza-hugepages.cfg && update-grub',
model_name='aModel', fatal=True),
mock.call(
'someApp/0',
'cat /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages'
'',
model_name='aModel', fatal=True)
])
98 changes: 98 additions & 0 deletions zaza/utilities/machine_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@
# limitations under the License.
"""Tools for directly augmenting deployed machine operating system."""

import logging

import zaza.charm_lifecycle.utils
import zaza.model
import zaza.utilities.generic
import zaza.utilities.juju
import zaza.utilities.machine_os


def install_modules_extra(unit_name, model_name=None):
Expand Down Expand Up @@ -163,3 +168,96 @@ def disable_vfio_unsafe_noiommu_mode(unit, model_name=None):
:raises: AssertionError, zaza.model.CommandRunFailed
"""
_set_vfio_unsafe_noiommu_mode(unit, False, model_name=model_name)


HV_APPLICATION_KEY = 'hypervisor_application'


def get_hv_application():
"""Get hypervisor application from test definition configure options.
:returns: Name of hypervisor application, if configured.
:rtype: Optional[str]
"""
config_options = zaza.charm_lifecycle.utils.get_config_options()
return config_options.get(HV_APPLICATION_KEY)


def reboot_hvs(units=None):
"""Reboot hypervisors.
:param units: List of units we should operate on.
Default is to reboot all units of the configured hypervisor
application.
:type units: Optional[List[juju.unit.Unit]]
"""
hv_application = get_hv_application()
if not hv_application:
logging.warning(
'Not rebooting any units, no application name '
'configured (configure_options: ["{}"])'
.format(HV_APPLICATION_KEY))
return
units = units or zaza.model.get_units(hv_application)
for unit in units:
zaza.utilities.generic.reboot(unit.name)
zaza.model.block_until_unit_wl_status(unit.name, "unknown")
target_deploy_status = zaza.charm_lifecycle.utils.get_charm_config().get(
'target_deploy_status', {})
zaza.model.wait_for_application_states(
states=target_deploy_status)


def enable_hugepages(unit, nr_hugepages, model_name=None):
"""Enable boot time allocation of hugepages.
:param unit: Unit to operate on
:type unit: juju.unit.Unit
:param nr_hugepages: Number of 1G hugepages to request
:type nr_hugepages: int
:param model_name: Name of model to query
:type model_name: Optional[str]
:raises: AssertionError, zaza.model.CommandRunFailed
"""
cmd = (
'echo \'GRUB_CMDLINE_LINUX_DEFAULT='
'"default_hugepagesz=1G hugepagesz=1G hugepages={}"\' '
'> /etc/default/grub.d/99-zaza-hugepages.cfg && '
'update-grub'
.format(nr_hugepages))
zaza.utilities.juju.remote_run(
unit.name, cmd, model_name=model_name, fatal=True)

reboot_hvs([unit])

cmd = 'cat /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages'
result = zaza.utilities.juju.remote_run(
unit.name, cmd, model_name=model_name, fatal=True).rstrip()
assert int(result) == nr_hugepages, (
'Unable to acquire the requested number of hugepages ({} != {})'
.format(result, nr_hugepages))


def disable_hugepages(unit, model_name=None):
"""Disable boot time allocation of hugepages.
:param unit: Unit to operate on
:type unit: juju.unit.Unit
:param model_name: Name of model to query
:type model_name: Optional[str]
:raises: AssertionError, zaza.model.CommandRunFailed
"""
cmd = (
'rm /etc/default/grub.d/99-zaza-hugepages.cfg && '
'update-grub')
result = zaza.utilities.juju.remote_run(
unit.name, cmd, model_name=model_name, fatal=True)

reboot_hvs([unit])

cmd = 'cat /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages'
result = zaza.utilities.juju.remote_run(
unit.name, cmd, model_name=model_name, fatal=True).rstrip()
assert int(result) == 0, (
'Unable to disable hugepages ({} != 0)'
.format(result))

0 comments on commit 39d1ce2

Please sign in to comment.