From 4a00814f82860dc138c42ee9624b3edd97d3b222 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Fri, 22 Sep 2023 11:14:34 -0400 Subject: [PATCH] cephadm: move sysctl specific functions to sysctl.py Signed-off-by: John Mulligan --- src/cephadm/cephadm.py | 103 +------------------------- src/cephadm/cephadmlib/sysctl.py | 116 ++++++++++++++++++++++++++++++ src/cephadm/tests/test_cephadm.py | 6 +- 3 files changed, 122 insertions(+), 103 deletions(-) create mode 100644 src/cephadm/cephadmlib/sysctl.py diff --git a/src/cephadm/cephadm.py b/src/cephadm/cephadm.py index 7ddad159b6b07..929a0dd7c65c3 100755 --- a/src/cephadm/cephadm.py +++ b/src/cephadm/cephadm.py @@ -165,10 +165,11 @@ from cephadmlib.host_facts import HostFacts, list_networks from cephadmlib.ssh import authorize_ssh_key, check_ssh_connectivity from cephadmlib.daemon_form import ( DaemonForm, - SysctlDaemonForm, create as daemon_form_create, register as register_daemon_form, ) +from cephadmlib.sysctl import install_sysctl, migrate_sysctl_dir + FuncT = TypeVar('FuncT', bound=Callable) @@ -3213,106 +3214,6 @@ def update_firewalld(ctx, daemon_type): firewall.apply_rules() -def install_sysctl(ctx: CephadmContext, fsid: str, daemon: DaemonForm) -> None: - """ - Set up sysctl settings - """ - def _write(conf: Path, lines: List[str]) -> None: - lines = [ - '# created by cephadm', - '', - *lines, - '', - ] - with write_new(conf, owner=None, perms=None) as f: - f.write('\n'.join(lines)) - - if not isinstance(daemon, SysctlDaemonForm): - return - - daemon_type = daemon.identity.daemon_type - conf = Path(ctx.sysctl_dir).joinpath(f'90-ceph-{fsid}-{daemon_type}.conf') - - lines = daemon.get_sysctl_settings() - lines = filter_sysctl_settings(ctx, lines) - - # apply the sysctl settings - if lines: - Path(ctx.sysctl_dir).mkdir(mode=0o755, exist_ok=True) - _write(conf, lines) - call_throws(ctx, ['sysctl', '--system']) - - -def sysctl_get(ctx: CephadmContext, variable: str) -> Union[str, None]: - """ - Read a sysctl setting by executing 'sysctl -b {variable}' - """ - out, err, code = call(ctx, ['sysctl', '-b', variable]) - return out or None - - -def filter_sysctl_settings(ctx: CephadmContext, lines: List[str]) -> List[str]: - """ - Given a list of sysctl settings, examine the system's current configuration - and return those which are not currently set as described. - """ - def test_setting(desired_line: str) -> bool: - # Remove any comments - comment_start = desired_line.find('#') - if comment_start != -1: - desired_line = desired_line[:comment_start] - desired_line = desired_line.strip() - if not desired_line or desired_line.isspace(): - return False - setting, desired_value = map(lambda s: s.strip(), desired_line.split('=')) - if not setting or not desired_value: - return False - actual_value = sysctl_get(ctx, setting) - return desired_value != actual_value - return list(filter(test_setting, lines)) - - -def migrate_sysctl_dir(ctx: CephadmContext, fsid: str) -> None: - """ - Cephadm once used '/usr/lib/sysctl.d' for storing sysctl configuration. - This moves it to '/etc/sysctl.d'. - """ - deprecated_location: str = '/usr/lib/sysctl.d' - deprecated_confs: List[str] = glob(f'{deprecated_location}/90-ceph-{fsid}-*.conf') - if not deprecated_confs: - return - - file_count: int = len(deprecated_confs) - logger.info(f'Found sysctl {file_count} files in deprecated location {deprecated_location}. Starting Migration.') - for conf in deprecated_confs: - try: - shutil.move(conf, ctx.sysctl_dir) - file_count -= 1 - except shutil.Error as err: - if str(err).endswith('already exists'): - logger.warning(f'Destination file already exists. Deleting {conf}.') - try: - os.unlink(conf) - file_count -= 1 - except OSError as del_err: - logger.warning(f'Could not remove {conf}: {del_err}.') - else: - logger.warning(f'Could not move {conf} from {deprecated_location} to {ctx.sysctl_dir}: {err}') - - # Log successful migration - if file_count == 0: - logger.info(f'Successfully migrated sysctl config to {ctx.sysctl_dir}.') - return - - # Log partially successful / unsuccessful migration - files_processed: int = len(deprecated_confs) - if file_count < files_processed: - status: str = f'partially successful (failed {file_count}/{files_processed})' - elif file_count == files_processed: - status = 'unsuccessful' - logger.warning(f'Migration of sysctl configuration {status}. You may want to perform a migration manually.') - - def install_base_units(ctx, fsid): # type: (CephadmContext, str) -> None """ diff --git a/src/cephadm/cephadmlib/sysctl.py b/src/cephadm/cephadmlib/sysctl.py new file mode 100644 index 0000000000000..66a8b0c5ff3e1 --- /dev/null +++ b/src/cephadm/cephadmlib/sysctl.py @@ -0,0 +1,116 @@ +# sysctl.py - functions for working with linux sysctl properties + +import logging +import os +import shutil + +from glob import glob +from pathlib import Path +from typing import List, Union + +from .call_wrappers import call, call_throws +from .context import CephadmContext +from .daemon_form import DaemonForm, SysctlDaemonForm +from .file_utils import write_new + +logger = logging.getLogger() + + +def install_sysctl(ctx: CephadmContext, fsid: str, daemon: DaemonForm) -> None: + """ + Set up sysctl settings + """ + def _write(conf: Path, lines: List[str]) -> None: + lines = [ + '# created by cephadm', + '', + *lines, + '', + ] + with write_new(conf, owner=None, perms=None) as f: + f.write('\n'.join(lines)) + + if not isinstance(daemon, SysctlDaemonForm): + return + + daemon_type = daemon.identity.daemon_type + conf = Path(ctx.sysctl_dir).joinpath(f'90-ceph-{fsid}-{daemon_type}.conf') + + lines = daemon.get_sysctl_settings() + lines = filter_sysctl_settings(ctx, lines) + + # apply the sysctl settings + if lines: + Path(ctx.sysctl_dir).mkdir(mode=0o755, exist_ok=True) + _write(conf, lines) + call_throws(ctx, ['sysctl', '--system']) + + +def sysctl_get(ctx: CephadmContext, variable: str) -> Union[str, None]: + """ + Read a sysctl setting by executing 'sysctl -b {variable}' + """ + out, err, code = call(ctx, ['sysctl', '-b', variable]) + return out or None + + +def filter_sysctl_settings(ctx: CephadmContext, lines: List[str]) -> List[str]: + """ + Given a list of sysctl settings, examine the system's current configuration + and return those which are not currently set as described. + """ + def test_setting(desired_line: str) -> bool: + # Remove any comments + comment_start = desired_line.find('#') + if comment_start != -1: + desired_line = desired_line[:comment_start] + desired_line = desired_line.strip() + if not desired_line or desired_line.isspace(): + return False + setting, desired_value = map(lambda s: s.strip(), desired_line.split('=')) + if not setting or not desired_value: + return False + actual_value = sysctl_get(ctx, setting) + return desired_value != actual_value + return list(filter(test_setting, lines)) + + +def migrate_sysctl_dir(ctx: CephadmContext, fsid: str) -> None: + """ + Cephadm once used '/usr/lib/sysctl.d' for storing sysctl configuration. + This moves it to '/etc/sysctl.d'. + """ + deprecated_location: str = '/usr/lib/sysctl.d' + deprecated_confs: List[str] = glob(f'{deprecated_location}/90-ceph-{fsid}-*.conf') + if not deprecated_confs: + return + + file_count: int = len(deprecated_confs) + logger.info(f'Found sysctl {file_count} files in deprecated location {deprecated_location}. Starting Migration.') + for conf in deprecated_confs: + try: + shutil.move(conf, ctx.sysctl_dir) + file_count -= 1 + except shutil.Error as err: + if str(err).endswith('already exists'): + logger.warning(f'Destination file already exists. Deleting {conf}.') + try: + os.unlink(conf) + file_count -= 1 + except OSError as del_err: + logger.warning(f'Could not remove {conf}: {del_err}.') + else: + logger.warning(f'Could not move {conf} from {deprecated_location} to {ctx.sysctl_dir}: {err}') + + # Log successful migration + if file_count == 0: + logger.info(f'Successfully migrated sysctl config to {ctx.sysctl_dir}.') + return + + # Log partially successful / unsuccessful migration + files_processed: int = len(deprecated_confs) + if file_count < files_processed: + status: str = f'partially successful (failed {file_count}/{files_processed})' + elif file_count == files_processed: + status = 'unsuccessful' + logger.warning(f'Migration of sysctl configuration {status}. You may want to perform a migration manually.') diff --git a/src/cephadm/tests/test_cephadm.py b/src/cephadm/tests/test_cephadm.py index c5366ba4f1716..fd75cf828a3cb 100644 --- a/src/cephadm/tests/test_cephadm.py +++ b/src/cephadm/tests/test_cephadm.py @@ -2506,8 +2506,10 @@ cluster_network=3.3.3.0/24, 4.4.4.0/24 assert _str_to_networks(cluster_network) == ['3.3.3.0/24', '4.4.4.0/24'] class TestSysctl: - @mock.patch('cephadm.sysctl_get') + @mock.patch('cephadmlib.sysctl.sysctl_get') def test_filter_sysctl_settings(self, _sysctl_get): + from cephadmlib.sysctl import filter_sysctl_settings + ctx = _cephadm.CephadmContext() input = [ # comment-only lines should be ignored @@ -2531,7 +2533,7 @@ class TestSysctl: "65530", "something else", ] - result = _cephadm.filter_sysctl_settings(ctx, input) + result = filter_sysctl_settings(ctx, input) assert len(_sysctl_get.call_args_list) == 6 assert _sysctl_get.call_args_list[0].args[1] == "something" assert _sysctl_get.call_args_list[1].args[1] == "fs.aio-max-nr" -- 2.39.5