ipvs: ensure selected scheduler kernel modules are loaded

Signed-off-by: Christopher M. Luciano <cmluciano@us.ibm.com>
This commit is contained in:
Christopher M. Luciano 2020-07-13 15:20:59 -04:00
parent 240a72b5c0
commit e2a0eddaf0
No known key found for this signature in database
GPG Key ID: 5148DBB31F2843F1
5 changed files with 71 additions and 4 deletions

View File

@ -122,7 +122,7 @@ func newProxyServer(
iptInterface = utiliptables.New(execer, protocol) iptInterface = utiliptables.New(execer, protocol)
kernelHandler = ipvs.NewLinuxKernelHandler() kernelHandler = ipvs.NewLinuxKernelHandler()
ipsetInterface = utilipset.New(execer) ipsetInterface = utilipset.New(execer)
canUseIPVS, err := ipvs.CanUseIPVSProxier(kernelHandler, ipsetInterface) canUseIPVS, err := ipvs.CanUseIPVSProxier(kernelHandler, ipsetInterface, config.IPVS.Scheduler)
if string(config.Mode) == proxyModeIPVS && err != nil { if string(config.Mode) == proxyModeIPVS && err != nil {
klog.Errorf("Can't use the IPVS proxier: %v", err) klog.Errorf("Can't use the IPVS proxier: %v", err)
} }

View File

@ -79,6 +79,7 @@ func Test_getProxyMode(t *testing.T) {
kernelCompat bool kernelCompat bool
ipsetError error ipsetError error
expected string expected string
scheduler string
}{ }{
{ // flag says userspace { // flag says userspace
flag: "userspace", flag: "userspace",
@ -142,6 +143,14 @@ func Test_getProxyMode(t *testing.T) {
kernelCompat: true, kernelCompat: true,
expected: proxyModeIPTables, expected: proxyModeIPTables,
}, },
{ // flag says ipvs, ipset version ok, kernel modules installed for sed scheduler
flag: "ipvs",
kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack", "ip_vs_sed"},
kernelVersion: "4.19",
ipsetVersion: ipvs.MinIPSetCheckVersion,
expected: proxyModeIPVS,
scheduler: "sed",
},
} }
for i, c := range cases { for i, c := range cases {
kcompater := &fakeKernelCompatTester{c.kernelCompat} kcompater := &fakeKernelCompatTester{c.kernelCompat}
@ -150,7 +159,11 @@ func Test_getProxyMode(t *testing.T) {
modules: c.kmods, modules: c.kmods,
kernelVersion: c.kernelVersion, kernelVersion: c.kernelVersion,
} }
canUseIPVS, _ := ipvs.CanUseIPVSProxier(khandler, ipsetver) scheduler := cases[i].scheduler
if scheduler == "" {
scheduler = "rr"
}
canUseIPVS, _ := ipvs.CanUseIPVSProxier(khandler, ipsetver, scheduler)
r := getProxyMode(c.flag, canUseIPVS, kcompater) r := getProxyMode(c.flag, canUseIPVS, kcompater)
if r != c.expected { if r != c.expected {
t.Errorf("Case[%d] Expected %q, got %q", i, c.expected, r) t.Errorf("Case[%d] Expected %q, got %q", i, c.expected, r)

View File

@ -693,7 +693,7 @@ func (handle *LinuxKernelHandler) GetKernelVersion() (string, error) {
// This is determined by checking if all the required kernel modules can be loaded. It may // This is determined by checking if all the required kernel modules can be loaded. It may
// return an error if it fails to get the kernel modules information without error, in which // return an error if it fails to get the kernel modules information without error, in which
// case it will also return false. // case it will also return false.
func CanUseIPVSProxier(handle KernelHandler, ipsetver IPSetVersioner) (bool, error) { func CanUseIPVSProxier(handle KernelHandler, ipsetver IPSetVersioner, scheduler string) (bool, error) {
mods, err := handle.GetModules() mods, err := handle.GetModules()
if err != nil { if err != nil {
return false, fmt.Errorf("error getting installed ipvs required kernel modules: %v", err) return false, fmt.Errorf("error getting installed ipvs required kernel modules: %v", err)
@ -711,6 +711,7 @@ func CanUseIPVSProxier(handle KernelHandler, ipsetver IPSetVersioner) (bool, err
} }
mods = utilipvs.GetRequiredIPVSModules(kernelVersion) mods = utilipvs.GetRequiredIPVSModules(kernelVersion)
wantModules := sets.NewString() wantModules := sets.NewString()
mods = append(mods, utilipvs.GetRequiredSchedulerModules(scheduler))
wantModules.Insert(mods...) wantModules.Insert(mods...)
modules := wantModules.Difference(loadModules).UnsortedList() modules := wantModules.Difference(loadModules).UnsortedList()

View File

@ -259,6 +259,7 @@ func TestCleanupLeftovers(t *testing.T) {
func TestCanUseIPVSProxier(t *testing.T) { func TestCanUseIPVSProxier(t *testing.T) {
testCases := []struct { testCases := []struct {
mods []string mods []string
scheduler string
kernelVersion string kernelVersion string
kernelErr error kernelErr error
ipsetVersion string ipsetVersion string
@ -268,6 +269,7 @@ func TestCanUseIPVSProxier(t *testing.T) {
// case 0, kernel error // case 0, kernel error
{ {
mods: []string{"foo", "bar", "baz"}, mods: []string{"foo", "bar", "baz"},
scheduler: "",
kernelVersion: "4.19", kernelVersion: "4.19",
kernelErr: fmt.Errorf("oops"), kernelErr: fmt.Errorf("oops"),
ipsetVersion: "0.0", ipsetVersion: "0.0",
@ -276,6 +278,7 @@ func TestCanUseIPVSProxier(t *testing.T) {
// case 1, ipset error // case 1, ipset error
{ {
mods: []string{"foo", "bar", "baz"}, mods: []string{"foo", "bar", "baz"},
scheduler: "",
kernelVersion: "4.19", kernelVersion: "4.19",
ipsetVersion: MinIPSetCheckVersion, ipsetVersion: MinIPSetCheckVersion,
ipsetErr: fmt.Errorf("oops"), ipsetErr: fmt.Errorf("oops"),
@ -284,6 +287,7 @@ func TestCanUseIPVSProxier(t *testing.T) {
// case 2, missing required kernel modules and ipset version too low // case 2, missing required kernel modules and ipset version too low
{ {
mods: []string{"foo", "bar", "baz"}, mods: []string{"foo", "bar", "baz"},
scheduler: "rr",
kernelVersion: "4.19", kernelVersion: "4.19",
ipsetVersion: "1.1", ipsetVersion: "1.1",
ok: false, ok: false,
@ -291,6 +295,7 @@ func TestCanUseIPVSProxier(t *testing.T) {
// case 3, missing required ip_vs_* kernel modules // case 3, missing required ip_vs_* kernel modules
{ {
mods: []string{"ip_vs", "a", "bc", "def"}, mods: []string{"ip_vs", "a", "bc", "def"},
scheduler: "sed",
kernelVersion: "4.19", kernelVersion: "4.19",
ipsetVersion: MinIPSetCheckVersion, ipsetVersion: MinIPSetCheckVersion,
ok: false, ok: false,
@ -298,6 +303,7 @@ func TestCanUseIPVSProxier(t *testing.T) {
// case 4, ipset version too low // case 4, ipset version too low
{ {
mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"}, mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"},
scheduler: "rr",
kernelVersion: "4.19", kernelVersion: "4.19",
ipsetVersion: "4.3.0", ipsetVersion: "4.3.0",
ok: false, ok: false,
@ -305,6 +311,7 @@ func TestCanUseIPVSProxier(t *testing.T) {
// case 5, ok for linux kernel 4.19 // case 5, ok for linux kernel 4.19
{ {
mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"}, mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"},
scheduler: "rr",
kernelVersion: "4.19", kernelVersion: "4.19",
ipsetVersion: MinIPSetCheckVersion, ipsetVersion: MinIPSetCheckVersion,
ok: true, ok: true,
@ -312,6 +319,7 @@ func TestCanUseIPVSProxier(t *testing.T) {
// case 6, ok for linux kernel 4.18 // case 6, ok for linux kernel 4.18
{ {
mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"}, mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"},
scheduler: "rr",
kernelVersion: "4.18", kernelVersion: "4.18",
ipsetVersion: MinIPSetCheckVersion, ipsetVersion: MinIPSetCheckVersion,
ok: true, ok: true,
@ -319,16 +327,33 @@ func TestCanUseIPVSProxier(t *testing.T) {
// case 7. ok when module list has extra modules // case 7. ok when module list has extra modules
{ {
mods: []string{"foo", "ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack", "bar"}, mods: []string{"foo", "ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack", "bar"},
scheduler: "rr",
kernelVersion: "4.19", kernelVersion: "4.19",
ipsetVersion: "6.19", ipsetVersion: "6.19",
ok: true, ok: true,
}, },
// case 8, not ok for sed based IPVS scheduling
{
mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"},
scheduler: "sed",
kernelVersion: "4.19",
ipsetVersion: MinIPSetCheckVersion,
ok: false,
},
// case 9, ok for dh based IPVS scheduling
{
mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack", "ip_vs_dh"},
scheduler: "dh",
kernelVersion: "4.19",
ipsetVersion: MinIPSetCheckVersion,
ok: true,
},
} }
for i := range testCases { for i := range testCases {
handle := &fakeKernelHandler{modules: testCases[i].mods, kernelVersion: testCases[i].kernelVersion} handle := &fakeKernelHandler{modules: testCases[i].mods, kernelVersion: testCases[i].kernelVersion}
versioner := &fakeIPSetVersioner{version: testCases[i].ipsetVersion, err: testCases[i].ipsetErr} versioner := &fakeIPSetVersioner{version: testCases[i].ipsetVersion, err: testCases[i].ipsetErr}
ok, err := CanUseIPVSProxier(handle, versioner) ok, err := CanUseIPVSProxier(handle, versioner, testCases[i].scheduler)
if ok != testCases[i].ok { if ok != testCases[i].ok {
t.Errorf("Case [%d], expect %v, got %v: err: %v", i, testCases[i].ok, ok, err) t.Errorf("Case [%d], expect %v, got %v: err: %v", i, testCases[i].ok, ok, err)
} }

View File

@ -83,6 +83,14 @@ const (
KernelModuleIPVSWRR string = "ip_vs_wrr" KernelModuleIPVSWRR string = "ip_vs_wrr"
// KernelModuleIPVSSH is the kernel module "ip_vs_sh" // KernelModuleIPVSSH is the kernel module "ip_vs_sh"
KernelModuleIPVSSH string = "ip_vs_sh" KernelModuleIPVSSH string = "ip_vs_sh"
// KernelModuleIPVSLC is the kernel module "ip_vs_lc"
KernelModuleIPVSLC string = "ip_vs_lc"
// KernelModuleIPVSNQ is the kernel module "ip_vs_nq"
KernelModuleIPVSNQ string = "ip_vs_nq"
// KernelModuleIPVSDH is the kernel module "ip_vs_dh"
KernelModuleIPVSDH string = "ip_vs_dh"
// KernelModuleIPVSSED is the kernel module "ip_vs_sed"
KernelModuleIPVSSED string = "ip_vs_sed"
// KernelModuleNfConntrackIPV4 is the module "nf_conntrack_ipv4" // KernelModuleNfConntrackIPV4 is the module "nf_conntrack_ipv4"
KernelModuleNfConntrackIPV4 string = "nf_conntrack_ipv4" KernelModuleNfConntrackIPV4 string = "nf_conntrack_ipv4"
// KernelModuleNfConntrack is the kernel module "nf_conntrack" // KernelModuleNfConntrack is the kernel module "nf_conntrack"
@ -139,3 +147,23 @@ func GetRequiredIPVSModules(kernelVersion *version.Version) []string {
func IsRsGracefulTerminationNeeded(proto string) bool { func IsRsGracefulTerminationNeeded(proto string) bool {
return !strings.EqualFold(proto, "UDP") && !strings.EqualFold(proto, "SCTP") return !strings.EqualFold(proto, "UDP") && !strings.EqualFold(proto, "SCTP")
} }
// GetRequiredSchedulerModules returns the required ipvs scheduler module for configured scheduler
func GetRequiredSchedulerModules(scheduler string) string {
switch s := scheduler; s {
case "rr":
return KernelModuleIPVSRR
case "lc":
return KernelModuleIPVSLC
case "nq":
return KernelModuleIPVSNQ
case "dh":
return KernelModuleIPVSDH
case "sh":
return KernelModuleIPVSSH
case "sed":
return KernelModuleIPVSSED
default:
return KernelModuleIPVSRR
}
}