diff --git a/internal/controllers/vgmanager/controller.go b/internal/controllers/vgmanager/controller.go index d9fdc621c..999b07bc0 100644 --- a/internal/controllers/vgmanager/controller.go +++ b/internal/controllers/vgmanager/controller.go @@ -482,8 +482,12 @@ func (r *Reconciler) validateLVs(ctx context.Context, volumeGroup *lvmv1alpha1.L } if lvAttr.State != StateActive { - return fmt.Errorf("found inactive logical volume, maybe external repairs are necessary/already happening or there is another"+ - "entity conflicting with vg-manager, cannot proceed until volume is activated again: lv_attr: %s", lvAttr) + //If inactive, try activating it + err := r.LVM.ActivateLV(lv.Name, volumeGroup.Name) + if err != nil { + return fmt.Errorf("could not activate the inactive logical volume, maybe external repairs are necessary/already happening or there is another"+ + "entity conflicting with vg-manager, cannot proceed until volume is activated again: lv_attr: %s", lvAttr) + } } metadataPercentage, err := strconv.ParseFloat(lv.MetadataPercent, 32) if err != nil { diff --git a/internal/controllers/vgmanager/controller_test.go b/internal/controllers/vgmanager/controller_test.go index fc1271069..47c94d337 100644 --- a/internal/controllers/vgmanager/controller_test.go +++ b/internal/controllers/vgmanager/controller_test.go @@ -243,7 +243,7 @@ func testMockedBlockDeviceOnHost(ctx context.Context) { thinPool = lvm.LogicalVolume{ Name: vg.Spec.ThinPoolConfig.Name, VgName: vg.GetName(), - LvAttr: "twi-a-tz--", + LvAttr: "twi---tz--", LvSize: "1.0G", MetadataPercent: "10.0", } @@ -256,6 +256,7 @@ func testMockedBlockDeviceOnHost(ctx context.Context) { Lv: []lvm.LogicalVolume{thinPool}, }}}, nil).Once() instances.LVM.EXPECT().ListVGs().Return([]lvm.VolumeGroup{createdVG}, nil).Twice() + instances.LVM.EXPECT().ActivateLV(thinPool.Name, vg.GetName()).Return(nil).Once() }) By("triggering the next reconciliation after the creation of the thin pool", func() { @@ -284,7 +285,7 @@ func testMockedBlockDeviceOnHost(ctx context.Context) { }) var oldReadyGeneration int64 - By("verifiyng the VGStatus is now ready", func() { + By("verifying the VGStatus is now ready", func() { checkDistributedEvent(corev1.EventTypeNormal, "all the available devices are attached to the volume group") Expect(instances.client.Get(ctx, client.ObjectKeyFromObject(nodeStatus), nodeStatus)).To(Succeed()) Expect(nodeStatus.Spec.LVMVGStatus).ToNot(BeEmpty()) @@ -326,6 +327,7 @@ func testMockedBlockDeviceOnHost(ctx context.Context) { instances.LVM.EXPECT().ListLVs(vg.GetName()).Return(&lvm.LVReport{Report: []lvm.LVReportItem{{ Lv: []lvm.LogicalVolume{thinPool}, }}}, nil).Once() + instances.LVM.EXPECT().ActivateLV(thinPool.Name, createdVG.Name).Return(nil).Once() }) By("triggering the verification reconcile that should confirm the ready state", func() { diff --git a/internal/controllers/vgmanager/lvm/lvm.go b/internal/controllers/vgmanager/lvm/lvm.go index 90f404942..52e5041a5 100644 --- a/internal/controllers/vgmanager/lvm/lvm.go +++ b/internal/controllers/vgmanager/lvm/lvm.go @@ -102,6 +102,7 @@ type LVM interface { LVExists(lvName, vgName string) (bool, error) CreateLV(lvName, vgName string, sizePercent int) error ExtendLV(lvName, vgName string, sizePercent int) error + ActivateLV(lvName, vgName string) error DeleteLV(lvName, vgName string) error } @@ -452,6 +453,19 @@ func (hlvm *HostLVM) ExtendLV(lvName, vgName string, sizePercent int) error { return nil } +// ActivateLV activates the logical volume +func (hlvm *HostLVM) ActivateLV(lvName, vgName string) error { + lv := fmt.Sprintf("%s/%s", vgName, lvName) + + // deactivate logical volume + _, err := hlvm.ExecuteCommandWithOutputAsHost(lvChangeCmd, "-ay", lv) + if err != nil { + return fmt.Errorf("failed to activate thin pool %q in volume group %q. %w", lvName, vgName, err) + } + + return nil +} + func (hlvm *HostLVM) execute(v interface{}, args ...string) error { output, err := hlvm.ExecuteCommandWithOutputAsHost(lvmCmd, args...) if err != nil { diff --git a/internal/controllers/vgmanager/lvm/mocks/mock_lvm.go b/internal/controllers/vgmanager/lvm/mocks/mock_lvm.go index bc6189802..875c6a99d 100644 --- a/internal/controllers/vgmanager/lvm/mocks/mock_lvm.go +++ b/internal/controllers/vgmanager/lvm/mocks/mock_lvm.go @@ -20,6 +20,49 @@ func (_m *MockLVM) EXPECT() *MockLVM_Expecter { return &MockLVM_Expecter{mock: &_m.Mock} } +// ActivateLV provides a mock function with given fields: lvName, vgName +func (_m *MockLVM) ActivateLV(lvName string, vgName string) error { + ret := _m.Called(lvName, vgName) + + var r0 error + if rf, ok := ret.Get(0).(func(string, string) error); ok { + r0 = rf(lvName, vgName) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockLVM_ActivateLV_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ActivateLV' +type MockLVM_ActivateLV_Call struct { + *mock.Call +} + +// ActivateLV is a helper method to define mock.On call +// - lvName string +// - vgName string +func (_e *MockLVM_Expecter) ActivateLV(lvName interface{}, vgName interface{}) *MockLVM_ActivateLV_Call { + return &MockLVM_ActivateLV_Call{Call: _e.mock.On("ActivateLV", lvName, vgName)} +} + +func (_c *MockLVM_ActivateLV_Call) Run(run func(lvName string, vgName string)) *MockLVM_ActivateLV_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string)) + }) + return _c +} + +func (_c *MockLVM_ActivateLV_Call) Return(_a0 error) *MockLVM_ActivateLV_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockLVM_ActivateLV_Call) RunAndReturn(run func(string, string) error) *MockLVM_ActivateLV_Call { + _c.Call.Return(run) + return _c +} + // AddTagToVG provides a mock function with given fields: vgName func (_m *MockLVM) AddTagToVG(vgName string) error { ret := _m.Called(vgName)