Skip to content

Commit

Permalink
added bench tests for lvm_logical_volume_watcher.go and some fixes
Browse files Browse the repository at this point in the history
Signed-off-by: Viktor Kramarenko <[email protected]>
  • Loading branch information
ViktorKram committed Mar 21, 2024
1 parent ca224e0 commit 4821779
Show file tree
Hide file tree
Showing 3 changed files with 297 additions and 22 deletions.
41 changes: 20 additions & 21 deletions images/agent/pkg/controller/lvm_logical_volume_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,23 @@ import (
"context"
"errors"
"fmt"
"github.com/google/go-cmp/cmp"
"k8s.io/client-go/util/workqueue"
"reflect"

"sds-node-configurator/api/v1alpha1"
"sds-node-configurator/config"
"sds-node-configurator/internal"
"sds-node-configurator/pkg/logger"
"sds-node-configurator/pkg/monitoring"
"sds-node-configurator/pkg/utils"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"time"

"github.com/google/go-cmp/cmp"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/client-go/util/workqueue"
"k8s.io/utils/strings/slices"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
Expand Down Expand Up @@ -232,7 +231,6 @@ func reconcileLLVDeleteFunc(
lvName := llv.Spec.ActualLVNameOnTheNode

err := deleteLVIfExists(log, vgName, lvName)

if err != nil {
log.Error(err, fmt.Sprintf("[reconcileLLVDeleteFunc] unable to delete the LV %s in VG %s", lvName, vgName))
err = updateLVMLogicalVolumePhase(ctx, cl, log, metrics, llv, failedStatusPhase, fmt.Sprintf("Unable to delete the the LV %s in VG %s, err: %s", lvName, vgName, err.Error()))
Expand Down Expand Up @@ -347,8 +345,8 @@ func reconcileLLVUpdateFunc(
case Thick:
freeSpace, err := getFreeVGSpace(lvg)
if err != nil {
log.Error(err, fmt.Sprintf("[reconcileLLVUpdateFunc] unable to count free space in VG, name: %s", vgName))
err = updateLVMLogicalVolumePhase(ctx, cl, log, metrics, llv, failedStatusPhase, fmt.Sprintf("Unable to count free VG space, VG name %s, err: %s", vgName, err.Error()))
log.Error(err, fmt.Sprintf("[reconcileLLVUpdateFunc] unable to lvCount free space in VG, name: %s", vgName))
err = updateLVMLogicalVolumePhase(ctx, cl, log, metrics, llv, failedStatusPhase, fmt.Sprintf("Unable to lvCount free VG space, VG name %s, err: %s", vgName, err.Error()))
if err != nil {
log.Error(err, fmt.Sprintf("[reconcileLLVUpdateFunc] unable to update the LVMLogicalVolume %s", llv.Name))
}
Expand All @@ -370,8 +368,8 @@ func reconcileLLVUpdateFunc(
case Thin:
// freeSpace, err := getFreeLVSpace(log, llv.Spec.Thin.PoolName)
// if err != nil {
// log.Error(err, fmt.Sprintf("[reconcileLLVUpdateFunc] unable to count free space in Thin-pool, name: %s", llv.Spec.Thin.PoolName))
// err = updateLVMLogicalVolumePhase(ctx, cl, log, metrics, llv, failedStatusPhase, fmt.Sprintf("Unable to count free Thin-pool space, err: %s", err.Error()))
// log.Error(err, fmt.Sprintf("[reconcileLLVUpdateFunc] unable to lvCount free space in Thin-pool, name: %s", llv.Spec.Thin.PoolName))
// err = updateLVMLogicalVolumePhase(ctx, cl, log, metrics, llv, failedStatusPhase, fmt.Sprintf("Unable to lvCount free Thin-pool space, err: %s", err.Error()))
// if err != nil {
// log.Error(err, fmt.Sprintf("[reconcileLLVUpdateFunc] unable to update the LVMLogicalVolume %s", llv.Name))
// }
Expand Down Expand Up @@ -500,18 +498,12 @@ func reconcileLLVCreateFunc(

vgName := lvg.Spec.ActualVGNameOnTheNode
lvName := llv.Spec.ActualLVNameOnTheNode
added, err := addLLVFinalizerIfNotExist(ctx, cl, log, metrics, llv)
if err != nil {
log.Error(err, fmt.Sprintf("[reconcileLLVCreateFunc] unable to update the LVMLogicalVolume %s", llv.Name))
return
}
log.Debug(fmt.Sprintf("[reconcileLLVCreateFunc] a finalizer to the LVMLogicalVolume %s was added: %t", llv.Name, added))

switch llv.Spec.Type {
case Thick:
freeSpace, err := getFreeVGSpace(lvg)
if err != nil {
log.Error(err, fmt.Sprintf("[reconcileLLVCreateFunc] unable to count free space in VG, name: %s", vgName))
log.Error(err, fmt.Sprintf("[reconcileLLVCreateFunc] unable to lvCount free space in VG, name: %s", vgName))
err = updateLVMLogicalVolumePhase(ctx, cl, log, metrics, llv, failedStatusPhase, fmt.Sprintf("Unable to get free VG space, err: %s", err.Error()))
if err != nil {
log.Error(err, fmt.Sprintf("[reconcileLLVCreateFunc] unable to updateLVMLogicalVolumePhase for LVMLogicalVolume %s", llv.Name))
Expand Down Expand Up @@ -544,7 +536,7 @@ func reconcileLLVCreateFunc(
case Thin:
// freeSpace, err := getFreeLVSpace(log, llv.Spec.Thin.PoolName)
// if err != nil {
// log.Error(err, fmt.Sprintf("[reconcileLLVCreateFunc] unable to count free space in LV, name: %s", llv.Spec.Thin.PoolName))
// log.Error(err, fmt.Sprintf("[reconcileLLVCreateFunc] unable to lvCount free space in LV, name: %s", llv.Spec.Thin.PoolName))
// err = updateLVMLogicalVolumePhase(ctx, cl, log, metrics, llv, failedStatusPhase, fmt.Sprintf("Unable to get free LV space, err: %s", err.Error()))
// if err != nil {
// log.Error(err, fmt.Sprintf("[reconcileLLVCreateFunc] unable to update the LVMLogicalVolume, name: %s", llv.Name))
Expand Down Expand Up @@ -578,6 +570,12 @@ func reconcileLLVCreateFunc(
}

log.Info(fmt.Sprintf("[reconcileLLVCreateFunc] successfully created LV %s in VG %s for LVMLogicalVolume resource with name: %s", lvName, vgName, llv.Name))
added, err := addLLVFinalizerIfNotExist(ctx, cl, log, metrics, llv)
if err != nil {
log.Error(err, fmt.Sprintf("[reconcileLLVCreateFunc] unable to update the LVMLogicalVolume %s", llv.Name))
return
}
log.Debug(fmt.Sprintf("[reconcileLLVCreateFunc] a finalizer to the LVMLogicalVolume %s was added: %t", llv.Name, added))

actualSize, err := getLVActualSize(log, vgName, lvName)
if err != nil {
Expand Down Expand Up @@ -765,16 +763,17 @@ func FindLV(log logger.Logger, vgName, lvName string) (*internal.LVData, error)
log.Debug(fmt.Sprintf("[FindLV] runs cmd: %s", cmd))
if err != nil {
log.Error(err, "[shouldReconcileByCreateFunc] unable to GetAllLVs")
return &internal.LVData{}, err
return nil, err
}

log.Debug(fmt.Sprintf("[FindLV] Try to find LV: %s in VG: %s", lvName, vgName))
log.Debug(fmt.Sprintf("[FindLV] Try to find a LV: %s in VG: %s", lvName, vgName))
for _, lv := range lvs {
log.Trace(fmt.Sprintf("[FindLV] processing LV: %s, VG: %s", lv.LVName, lv.VGName))
if lv.VGName == vgName && lv.LVName == lvName {
return &lv, nil
}
}

return &internal.LVData{}, nil
log.Debug(fmt.Sprintf("[FindLV] LV %s was not found", lvName))
return nil, nil
}
276 changes: 276 additions & 0 deletions images/agent/pkg/controller/lvm_logical_volume_watcher_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
package controller

import (
"context"
"fmt"
v1 "k8s.io/api/core/v1"
sv1 "k8s.io/api/storage/v1"
extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
apiruntime "k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"os"
"sds-node-configurator/api/v1alpha1"
"sds-node-configurator/internal"
"sds-node-configurator/pkg/kubutils"
"sigs.k8s.io/controller-runtime/pkg/client"
"testing"
)

var (
lvgName = "hdd-lvg-on-node-0"
poolName = "hhd-thin"
lvCount = 600
size, err = resource.ParseQuantity("1Gi")

resizeOn = false

ctx = context.Background()
cl client.Client

resourcesSchemeFuncs = []func(*apiruntime.Scheme) error{
v1alpha1.AddToScheme,
clientgoscheme.AddToScheme,
extv1.AddToScheme,
v1.AddToScheme,
sv1.AddToScheme,
}
)

func BenchmarkRunThickLLVCreationSingleThread(b *testing.B) {
b.Logf("starts the test")
llvNames := make(map[string]bool, lvCount)

b.StartTimer()
for i := 0; i < lvCount; i++ {
llv := configureTestThickLLV(fmt.Sprintf("test-llv-%d", i), lvgName)
err := cl.Create(ctx, llv)
if err != nil {
b.Logf("unable to create test LLV %s, err: %s", llv.Name, err.Error())
}
llvNames[llv.Name] = false
}
lvCreatedTime := b.Elapsed().Seconds()

succeeded := 0
for succeeded < len(llvNames) {
llvs, err := getAllLLV(ctx, cl)
if err != nil {
b.Error(err)
continue
}

for llvName, created := range llvNames {
if llv, found := llvs[llvName]; found {
if llv.Status != nil {
b.Logf("LV %s status %s", llvName, llv.Status.Phase)
}
if err != nil {
b.Logf("can't check LLV %s llv", llvName)
continue
}

if llv.Status != nil &&
llv.Status.Phase == createdStatusPhase &&
!created {
succeeded++
llvNames[llvName] = true

if resizeOn {
add, err := resource.ParseQuantity("1G")
if err != nil {
b.Logf(err.Error())
continue
}

llv.Spec.Size.Add(add)
err = cl.Update(ctx, &llv)
if err != nil {
b.Logf(err.Error())
continue
}

b.Logf("resize for LV %s succeeded", llvName)
}
}
}

}
}
b.Logf("[TIME] LLV resources were configured for %f", lvCreatedTime)

b.Logf("All LLV were created for %f. Ends the test", b.Elapsed().Seconds())
}

func BenchmarkRunThinLLVCreationSingleThread(b *testing.B) {
b.Logf("starts thin test")
llvNames := make(map[string]bool, lvCount)

b.StartTimer()
for i := 0; i < lvCount; i++ {
llv := configureTestThinLLV(fmt.Sprintf("test-llv-%d", i), lvgName, poolName)
err := cl.Create(ctx, llv)
if err != nil {
b.Logf("unable to create test LLV %s, err: %s", llv.Name, err.Error())
continue
}
llvNames[llv.Name] = false
}
createdTime := b.Elapsed().Seconds()

succeeded := 0
for succeeded < len(llvNames) {
llvs, err := getAllLLV(ctx, cl)
if err != nil {
b.Error(err)
continue
}

for llvName, visited := range llvNames {
if llv, found := llvs[llvName]; found {
if llv.Status != nil {
b.Logf("LV %s status %s", llvName, llv.Status.Phase)
}
if err != nil {
b.Logf("can't check LLV %s llv", llvName)
continue
}

if llv.Status != nil &&
llv.Status.Phase == createdStatusPhase &&
!visited {
succeeded++
llvNames[llvName] = true

if resizeOn {
add, err := resource.ParseQuantity("1G")
if err != nil {
b.Logf(err.Error())
continue
}

llv.Spec.Size.Add(add)
err = cl.Update(ctx, &llv)
if err != nil {
b.Logf(err.Error())
continue
}

b.Logf("resize for LV %s succeeded", llvName)
}
}
}

}
}
b.Logf("All LLV were configured for %f. Ends the test", createdTime)
b.Logf("All LLV were created in %f. Ends the test", b.Elapsed().Seconds())
}

//func BenchmarkDeleteTestLLV(b *testing.B) {
// b.Logf("delete test llv")
// for name := range llvNames {
// llv := &v1alpha1.LVMLogicalVolume{
// TypeMeta: metav1.TypeMeta{
// Kind: v1alpha1.LVMLogicalVolumeKind,
// APIVersion: v1alpha1.TypeMediaAPIVersion,
// },
// ObjectMeta: metav1.ObjectMeta{
// Name: name,
// },
// }
// err := cl.Delete(ctx, llv)
// if err != nil {
// b.Logf(err.Error())
// }
// }
// b.Logf("ends the deletion")
//}

func getAllLLV(ctx context.Context, cl client.Client) (map[string]v1alpha1.LVMLogicalVolume, error) {
list := &v1alpha1.LVMLogicalVolumeList{}
err := cl.List(ctx, list)
if err != nil {
return nil, err
}

res := make(map[string]v1alpha1.LVMLogicalVolume, len(list.Items))
for _, lv := range list.Items {
res[lv.Name] = lv
}

return res, nil
}

func configureTestThickLLV(name, lvgName string) *v1alpha1.LVMLogicalVolume {
return &v1alpha1.LVMLogicalVolume{
TypeMeta: metav1.TypeMeta{
Kind: v1alpha1.LVMLogicalVolumeKind,
APIVersion: v1alpha1.TypeMediaAPIVersion,
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Finalizers: []string{internal.SdsNodeConfiguratorFinalizer},
},
Spec: v1alpha1.LVMLogicalVolumeSpec{
ActualLVNameOnTheNode: name,
Type: Thick,
Size: size,
LvmVolumeGroupName: lvgName,
},
}
}

func configureTestThinLLV(name, lvgName, poolName string) *v1alpha1.LVMLogicalVolume {
return &v1alpha1.LVMLogicalVolume{
TypeMeta: metav1.TypeMeta{
Kind: v1alpha1.LVMLogicalVolumeKind,
APIVersion: v1alpha1.TypeMediaAPIVersion,
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Finalizers: []string{internal.SdsNodeConfiguratorFinalizer},
},
Spec: v1alpha1.LVMLogicalVolumeSpec{
ActualLVNameOnTheNode: name,
Type: Thin,
Size: size,
LvmVolumeGroupName: lvgName,
Thin: &v1alpha1.ThinLogicalVolumeSpec{PoolName: poolName},
},
}
}

func init() {
//flag.StringVar(&lvgName, "lvgName", "", "LVMVolumeGroup name used to create LV")
//flag.StringVar(&poolName, "poolName", "", "Thin pool name used to create LV")
//flag.IntVar(&lvCount, "lvCount", 0, "Test LV lvCount")
//flag.Parse()

config, err := kubutils.KubernetesDefaultConfigCreate()
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}

scheme := runtime.NewScheme()
for _, f := range resourcesSchemeFuncs {
err := f(scheme)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
}

options := client.Options{
Scheme: scheme,
}

cl, err = client.New(config, options)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
Loading

0 comments on commit 4821779

Please sign in to comment.