diff --git a/common/e2e_agent/client.go b/common/e2e_agent/client.go index b6e159b..8dc70d4 100644 --- a/common/e2e_agent/client.go +++ b/common/e2e_agent/client.go @@ -77,7 +77,7 @@ type Lvm struct { Pv string `json:"pv"` // Physical volume Vg string `json:"vg"` // Volume group ThinPoolAutoExtendThreshold int `json:"thinPoolAutoExtendThreshold"` // thin pool auto extend threshold - ThinPoolAutoExtendPercent int `json:"ThinPoolAutoExtendPercent"` // thin pool auto extend percent + ThinPoolAutoExtendPercent int `json:"thinPoolAutoExtendPercent"` // thin pool auto extend percent } type LoopDevice struct { @@ -1095,3 +1095,27 @@ func RemoveHostPathDisk(serverAddr string, diskPath string, mountPoint string) e logf.Log.Info("RemoveHostPathDisk succeeded", "output", out) return err } + +// LvmLvChangeMonitor monitor lvm lv +func LvmLvChangeMonitor(serverAddr string, vgName string) (string, error) { + data := Lvm{ + Vg: vgName, + } + logf.Log.Info("Executing lvchange", "addr", serverAddr, "data", data) + url := "http://" + getAgentAddress(serverAddr) + "/lvmlvchangemonitor" + encodedresult, err := sendRequestGetResponse("POST", url, data, false) + if err != nil { + logf.Log.Info("sendRequestGetResponse", "encodedresult", encodedresult, "error", err.Error()) + return encodedresult, err + } + out, e2eagenterrcode, err := UnwrapResult(encodedresult) + if err != nil { + logf.Log.Info("unwrap failed", "encodedresult", encodedresult, "error", err.Error()) + return encodedresult, err + } + if e2eagenterrcode != ErrNone { + return out, fmt.Errorf("failed to monitor lvm lv, errcode %d", e2eagenterrcode) + } + logf.Log.Info("LvmLvChangeMonitor succeeded", "output", out) + return out, err +} diff --git a/common/lvm/util.go b/common/lvm/util.go index 374b9d3..d2324d1 100644 --- a/common/lvm/util.go +++ b/common/lvm/util.go @@ -168,3 +168,58 @@ func SetupLvmNodes(vgName string, size int64) (LvmNodesDevicePvVgConfig, error) err = lvmNodeConfig.ConfigureLvmNodesWithDeviceAndVg() return lvmNodeConfig, err } + +// EnableLvmThinPoolAutoExpansion enable auto extending of the Thin Pool (Configure Over-Provisioning protection) +func EnableLvmThinPoolAutoExpansion(vgName string, thinPoolAutoExtendThreshold, thinPoolAutoExtendPercent int) error { + + workerNodes, err := ListLvmNode(common.NSOpenEBS()) + if err != nil { + return fmt.Errorf("failed to list lvm worker nodes, error: %v", err) + } + if len(workerNodes) == 0 { + return fmt.Errorf("lvm worker nodes not found") + } + /* + 1. Since we automatically create thin pool as part of first thin volume provisioning, + we need to enable the monitoring using lvchange command on the all thin pools across + the nodes to use the auto extend threshold feature. + monitor lvm lv thin pool + lvchange --monitor y lvmvg/lvmvg_thinpool + + 2. Editing the settings in the /etc/lvm/lvm.conf can allow auto growth of the thin pool when required. + By default, the threshold is 100% which means that the pool will not grow. + If we set this to, 75%, the Thin Pool will autoextend when the pool is 75% full. + It will increase by the default percentage of 20% if the value is not changed. + We can see these settings using the command grep against the file. + $ grep -E ‘^\s*thin_pool_auto’ /etc/lvm/lvm.conf + thin_pool_autoextend_threshold = 100 + thin_pool_autoextend_percent = 20 + */ + + for _, node := range workerNodes { + nodeIp, err := k8stest.GetNodeIPAddress(node) + if err != nil { + return fmt.Errorf("failed to get node %s IP, error: %v", node, err) + } + out, err := e2e_agent.LvmLvChangeMonitor(*nodeIp, vgName) + if err != nil { + return fmt.Errorf("failed to set up lv monitor for vg %s on node %s,output: %s error: %v", + vgName, node, out, err) + } + + out, err = e2e_agent.LvmThinPoolAutoExtendThreshold(*nodeIp, thinPoolAutoExtendThreshold) + if err != nil { + return fmt.Errorf("failed to set up thin_pool_autoextend_threshold value %d on node %s,output: %s error: %v", + thinPoolAutoExtendThreshold, node, out, err) + } + + out, err = e2e_agent.LvmThinPoolAutoExtendPercent(*nodeIp, thinPoolAutoExtendPercent) + if err != nil { + return fmt.Errorf("failed to set up thin_pool_autoextend_percent value %d on node %s,output: %s error: %v", + thinPoolAutoExtendPercent, node, out, err) + } + + } + + return nil +} diff --git a/tools/e2e-agent/Dockerfile b/tools/e2e-agent/Dockerfile index 513690d..2048c4b 100644 --- a/tools/e2e-agent/Dockerfile +++ b/tools/e2e-agent/Dockerfile @@ -11,7 +11,7 @@ COPY ./ / # It ensures that devices are configured as soon as they are plugged in and discovered. # It propagates information about a processed device. RUN apt-get update; apt-get install net-tools iptables wget parted udev nvme-cli build-essential gettext gettext-base \ - libinih-dev uuid-dev liburcu-dev libblkid-dev btrfs-progs lvm2 zfsutils-linux -y; + libinih-dev uuid-dev liburcu-dev libblkid-dev btrfs-progs -y; RUN wget https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz; \ tar -C /usr/local/ -xzf go${GO_VERSION}.linux-amd64.tar.gz; \ rm -rf go${GO_VERSION}.linux-amd64.tar.gz; \ diff --git a/tools/e2e-agent/build.sh b/tools/e2e-agent/build.sh index c39f82e..e3edacf 100755 --- a/tools/e2e-agent/build.sh +++ b/tools/e2e-agent/build.sh @@ -9,7 +9,7 @@ # as long as we do not make breaking changes. set -e IMAGE="openebs/e2e-agent" -TAG="v3.0.3" +TAG="v3.0.4" registry="" tag_as_latest="" diff --git a/tools/e2e-agent/e2e-agent.yaml b/tools/e2e-agent/e2e-agent.yaml index 8e715a0..7f62dab 100644 --- a/tools/e2e-agent/e2e-agent.yaml +++ b/tools/e2e-agent/e2e-agent.yaml @@ -17,6 +17,15 @@ data: else chroot /host zfs "$@" fi + LVM: | + #!/bin/sh + if [ -x /host/sbin/lvm ]; then + chroot /host /sbin/lvm "$@" + elif [ -x /host/usr/sbin/lvm ]; then + chroot /host /usr/sbin/lvm "$@" + else + chroot /host lvm "$@" + fi --- kind: DaemonSet @@ -50,7 +59,7 @@ spec: securityContext: privileged: true allowPrivilegeEscalation: true - image: openebs/e2e-agent:v3.0.3 + image: openebs/e2e-agent:v3.0.4 imagePullPolicy: Always volumeMounts: - name: host-root @@ -63,6 +72,9 @@ spec: - mountPath: /sbin/zfs name: chroot-zfs subPath: ZFS + - mountPath: /sbin/lvm + name: chroot-lvm + subPath: LVM volumes: - name: host-root hostPath: @@ -80,6 +92,10 @@ spec: configMap: defaultMode: 0555 name: test-vars + - name: chroot-lvm + configMap: + defaultMode: 0555 + name: test-vars --- diff --git a/tools/e2e-agent/lvm.go b/tools/e2e-agent/lvm.go index 7480bee..4fe1962 100644 --- a/tools/e2e-agent/lvm.go +++ b/tools/e2e-agent/lvm.go @@ -12,7 +12,7 @@ type Lvm struct { Pv string `json:"pv"` // Physical volume Vg string `json:"vg"` // Volume group ThinPoolAutoExtendThreshold int `json:"thinPoolAutoExtendThreshold"` // thin pool auto extend threshold - ThinPoolAutoExtendPercent int `json:"ThinPoolAutoExtendPercent"` // thin pool auto extend percent + ThinPoolAutoExtendPercent int `json:"thinPoolAutoExtendPercent"` // thin pool auto extend percent } // LvmVersion check lvm version installed on node @@ -36,7 +36,7 @@ func LvmListVg(w http.ResponseWriter, r *http.Request) { var msg string klog.Info("List lvm vgs") - lvmListCommand := "vgs --reportformat json" + lvmListCommand := "lvm vgs --reportformat json" output, err := bashLocal(lvmListCommand) if err != nil { msg = fmt.Sprintf("cannot list lvm vgs Error %s", err.Error()) @@ -52,7 +52,7 @@ func LvmListPv(w http.ResponseWriter, r *http.Request) { var msg string klog.Info("List lvm pvs") - lvmListCommand := "pvs --reportformat json" + lvmListCommand := "lvm pvs --reportformat json" output, err := bashLocal(lvmListCommand) if err != nil { msg = fmt.Sprintf("cannot list lvm pvs Error %s", err.Error()) @@ -82,7 +82,7 @@ func LvmCreatePv(w http.ResponseWriter, r *http.Request) { } klog.Info("creates lvm pv, data: %v", lvm) - lvmPvCreateCommand := fmt.Sprintf("pvcreate %s", lvm.Pv) + lvmPvCreateCommand := fmt.Sprintf("lvm pvcreate %s", lvm.Pv) output, err := bashLocal(lvmPvCreateCommand) if err != nil { msg = fmt.Sprintf("cannot create lvm pv Error %s", err.Error()) @@ -118,7 +118,7 @@ func LvmCreateVg(w http.ResponseWriter, r *http.Request) { } klog.Info("creates lvm vg, data: %v", lvm) - lvmVgCreateCommand := fmt.Sprintf("vgcreate %s %s", lvm.Vg, lvm.Pv) + lvmVgCreateCommand := fmt.Sprintf("lvm vgcreate %s %s", lvm.Vg, lvm.Pv) output, err := bashLocal(lvmVgCreateCommand) if err != nil { msg = fmt.Sprintf("cannot create lvm vg Error %s", err.Error()) @@ -148,7 +148,7 @@ func LvmRemovePv(w http.ResponseWriter, r *http.Request) { } klog.Info("remove lvm pv, data: %v", lvm) - lvmPvRemoveCommand := fmt.Sprintf("pvremove %s", lvm.Pv) + lvmPvRemoveCommand := fmt.Sprintf("lvm pvremove %s", lvm.Pv) output, err := bashLocal(lvmPvRemoveCommand) if err != nil { msg = fmt.Sprintf("cannot remove lvm pv Error %s", err.Error()) @@ -178,7 +178,7 @@ func LvmRemoveVg(w http.ResponseWriter, r *http.Request) { } klog.Info("remove lvm vg, data: %v", lvm) - lvmVgRemoveCommand := fmt.Sprintf("vgremove %s", lvm.Vg) + lvmVgRemoveCommand := fmt.Sprintf("lvm vgremove %s", lvm.Vg) output, err := bashLocal(lvmVgRemoveCommand) if err != nil { msg = fmt.Sprintf("cannot create lvm vg Error %s", err.Error()) @@ -208,7 +208,7 @@ func LvmThinPoolAutoExtendThreshold(w http.ResponseWriter, r *http.Request) { } klog.Info("update lvm.conf thin pool auto extend threshold value, data: %v", lvm) - lvmThinPoolAutoExtentThresholdCommand := fmt.Sprintf("sed -i '/^[^#]*thin_pool_autoextend_threshold/ s/= .*/= %d/' /etc/lvm/lvm.conf", + lvmThinPoolAutoExtentThresholdCommand := fmt.Sprintf("sed -i '/^[^#]*thin_pool_autoextend_threshold/ s/= .*/= %d/' /host/etc/lvm/lvm.conf", lvm.ThinPoolAutoExtendThreshold) output, err := bashLocal(lvmThinPoolAutoExtentThresholdCommand) if err != nil { @@ -231,17 +231,17 @@ func LvmThinPoolAutoExtendPercent(w http.ResponseWriter, r *http.Request) { WrapResult(msg, ErrJsonDecode, w) return } - if lvm.ThinPoolAutoExtendThreshold <= 0 { - msg = "no thin pool auto extent percent passed" + if lvm.ThinPoolAutoExtendPercent <= 0 { + msg = "no thin pool auto extend percent passed" klog.Error(msg) WrapResult(msg, UnprocessableEntityErrorCode, w) return } klog.Info("update lvm.conf thin pool auto extend threshold value, data: %v", lvm) - lvmThinPoolAutoExtentPercentCommand := fmt.Sprintf("sed -i '/^[^#]*thin_pool_autoextend_percent/ s/= .*/= %d/' /etc/lvm/lvm.conf", + lvmThinPoolAutoExtendPercentCommand := fmt.Sprintf("sed -i '/^[^#]*thin_pool_autoextend_percent/ s/= .*/= %d/' /host/etc/lvm/lvm.conf", lvm.ThinPoolAutoExtendPercent) - output, err := bashLocal(lvmThinPoolAutoExtentPercentCommand) + output, err := bashLocal(lvmThinPoolAutoExtendPercentCommand) if err != nil { msg = fmt.Sprintf("update lvm.conf thin pool auto extend percent value, Error %s", err.Error()) klog.Error(msg) @@ -250,3 +250,34 @@ func LvmThinPoolAutoExtendPercent(w http.ResponseWriter, r *http.Request) { } WrapResult(output, ErrNone, w) } + +// LvmLvChangeMonitor monitor lvm lv +func LvmLvChangeMonitor(w http.ResponseWriter, r *http.Request) { + var msg string + var lvm Lvm + d := json.NewDecoder(r.Body) + if err := d.Decode(&lvm); err != nil { + msg = fmt.Sprintf("failed to read JSON encoded data, Error: %s", err.Error()) + klog.Error(msg) + WrapResult(msg, ErrJsonDecode, w) + return + } + + if lvm.Vg == "" { + msg = "no vg name passed" + klog.Error(msg) + WrapResult(msg, UnprocessableEntityErrorCode, w) + return + } + klog.Info("monitor lvm lv, data: %v", lvm) + + lvmLvMonitorCommand := fmt.Sprintf("lvchange --monitor y %s/%s_thinpool", lvm.Vg, lvm.Vg) + output, err := bashLocal(lvmLvMonitorCommand) + if err != nil { + msg = fmt.Sprintf("cannot monitor lvm lv, Error %s", err.Error()) + klog.Error(msg) + WrapResult(msg, ErrExecFailed, w) + return + } + WrapResult(output, ErrNone, w) +} diff --git a/tools/e2e-agent/server.go b/tools/e2e-agent/server.go index d9200f8..3f314a0 100644 --- a/tools/e2e-agent/server.go +++ b/tools/e2e-agent/server.go @@ -163,6 +163,7 @@ func handleRequests() { router.HandleFunc("/lvmremovevg", LvmRemoveVg).Methods("POST") router.HandleFunc("/lvmthinpoolautoextendthreshold", LvmThinPoolAutoExtendThreshold).Methods("POST") router.HandleFunc("/lvmthinpoolautoextendpercent", LvmThinPoolAutoExtendPercent).Methods("POST") + router.HandleFunc("/lvmlvchangemonitor", LvmLvChangeMonitor).Methods("POST") //loop device router.HandleFunc("/createloopdevice", CreateLoopDevice).Methods("POST") router.HandleFunc("/deleteloopdevice", DeleteLoopDevice).Methods("POST")