Load nf_conntrack at boot time

Sysctl rules fined in charm config fails to be
applied in ovn enviroments because the rules are
applied before nf_conntrack is loaded.

By adding nf_conntrack to the /etc/modules file
it guarantees that it will be loaded before the
rules are applied.

Closes-Bug: #1922778
Change-Id: I51dae65cdc06e35230160bcaedda99710a72617d
This commit is contained in:
Rodrigo Barbieri 2024-04-15 13:45:45 -03:00
parent 31df8c6bf2
commit 8078971333
2 changed files with 108 additions and 0 deletions

View File

@ -24,9 +24,12 @@ import os
import subprocess import subprocess
import grp import grp
import shutil import shutil
import yaml
import charmhelpers.core.unitdata as unitdata import charmhelpers.core.unitdata as unitdata
from charmhelpers.core.kernel import modprobe
from charmhelpers.core.hookenv import ( from charmhelpers.core.hookenv import (
Hooks, Hooks,
config, config,
@ -250,6 +253,7 @@ def config_changed():
send_remote_restart = True send_remote_restart = True
sysctl_settings = config('sysctl') sysctl_settings = config('sysctl')
ensure_nf_conntrack_module_loaded(sysctl_settings)
if sysctl_settings and not is_container(): if sysctl_settings and not is_container():
create_sysctl( create_sysctl(
sysctl_settings, sysctl_settings,
@ -330,6 +334,27 @@ def config_changed():
check_and_start_iscsid() check_and_start_iscsid()
def ensure_nf_conntrack_module_loaded(sysctl_str):
"""Loads and writes nf_conntrack to /etc/modules if present in sysctls."""
# abort if empty or container
if not sysctl_str or is_container():
return
try:
sysctl_dict = yaml.safe_load(sysctl_str)
except yaml.YAMLError:
log("Error parsing YAML sysctl_dict: {}".format(sysctl_str),
level=ERROR)
return
if any("nf_conntrack" in key for key in sysctl_dict.keys()):
try:
modprobe("nf_conntrack")
except Exception as e:
log("Failed to load or persist nf_conntrack"
" kernel module: {}".format(e), level=WARNING)
def update_all_configs(): def update_all_configs():
CONFIGS.write_all() CONFIGS.write_all()

View File

@ -453,6 +453,89 @@ class NovaComputeRelationsTests(CharmTestCase):
user='nova' user='nova'
) )
@patch.object(hooks, 'is_container')
@patch('yaml.safe_load')
def test_ensure_nf_conntrack_module_loaded_empty(
self, safe_load, is_container):
is_container.return_value = False
hooks.ensure_nf_conntrack_module_loaded("")
safe_load.assert_not_called()
@patch.object(hooks, 'is_container')
@patch('yaml.safe_load')
def test_ensure_nf_conntrack_module_loaded_container(
self, safe_load, is_container):
is_container.return_value = True
hooks.ensure_nf_conntrack_module_loaded("foo")
safe_load.assert_not_called()
@patch.object(hooks, 'modprobe')
@patch.object(hooks, 'is_container')
@patch('yaml.safe_load')
def test_ensure_nf_conntrack_module_loaded_failed_yaml(
self, safe_load, is_container, modprobe):
is_container.return_value = False
hooks.ensure_nf_conntrack_module_loaded("foo")
safe_load.assert_called_once_with("foo")
modprobe.assert_not_called()
@patch.object(hooks, 'log')
@patch.object(hooks, 'modprobe')
@patch.object(hooks, 'is_container')
def test_ensure_nf_conntrack_module_loaded_failed_mobprobe(
self, is_container, modprobe, log):
is_container.return_value = False
modprobe.side_effect = Exception("FOO")
data = ("{ net.ipv4.neigh.default.gc_thresh1 : 128,"
"net.ipv4.neigh.default.gc_thresh2 : 28672,"
"net.ipv4.neigh.default.gc_thresh3 : 32768,"
"net.ipv6.neigh.default.gc_thresh1 : 128,"
"net.ipv6.neigh.default.gc_thresh2 : 28672,"
"net.ipv6.neigh.default.gc_thresh3 : 32768,"
"net.nf_conntrack_max : 1000000,"
"net.netfilter.nf_conntrack_buckets : 204800,"
"net.netfilter.nf_conntrack_max : 1000000 }")
hooks.ensure_nf_conntrack_module_loaded(data)
log.assert_called_once_with(
"Failed to load or persist nf_conntrack kernel module: FOO",
level="WARNING")
modprobe.assert_called_once_with("nf_conntrack")
@patch.object(hooks, 'log')
@patch.object(hooks, 'modprobe')
@patch.object(hooks, 'is_container')
def test_ensure_nf_conntrack_module_loaded(
self, is_container, modprobe, log):
is_container.return_value = False
data = ("{ net.ipv4.neigh.default.gc_thresh1 : 128,"
"net.ipv4.neigh.default.gc_thresh2 : 28672,"
"net.ipv4.neigh.default.gc_thresh3 : 32768,"
"net.ipv6.neigh.default.gc_thresh1 : 128,"
"net.ipv6.neigh.default.gc_thresh2 : 28672,"
"net.ipv6.neigh.default.gc_thresh3 : 32768,"
"net.nf_conntrack_max : 1000000,"
"net.netfilter.nf_conntrack_buckets : 204800,"
"net.netfilter.nf_conntrack_max : 1000000 }")
hooks.ensure_nf_conntrack_module_loaded(data)
log.assert_not_called()
modprobe.assert_called_once_with("nf_conntrack")
@patch.object(hooks, 'log')
@patch.object(hooks, 'modprobe')
@patch.object(hooks, 'is_container')
def test_ensure_nf_conntrack_module_loaded_no_sysctl(
self, is_container, modprobe, log):
is_container.return_value = False
data = ("{ net.ipv4.neigh.default.gc_thresh1 : 128,"
"net.ipv4.neigh.default.gc_thresh2 : 28672,"
"net.ipv4.neigh.default.gc_thresh3 : 32768,"
"net.ipv6.neigh.default.gc_thresh1 : 128,"
"net.ipv6.neigh.default.gc_thresh2 : 28672,"
"net.ipv6.neigh.default.gc_thresh3 : 32768 }")
hooks.ensure_nf_conntrack_module_loaded(data)
modprobe.assert_not_called()
log.assert_not_called()
def test_amqp_joined(self): def test_amqp_joined(self):
hooks.amqp_joined() hooks.amqp_joined()
self.relation_set.assert_called_with( self.relation_set.assert_called_with(