Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions internal/controller/discoveredphysicaldisk_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ func mapDriveToStatus(status *metalk8sv1alpha1.DiscoveredPhysicalDiskStatus, res
status.JBOD = &drive.JBOD
status.Status = ptr.To(mapPDStatus(drive.Status))
status.Reason = &drive.Reason
status.DevicePath = &drive.DevicePath
status.PermanentPath = &drive.PermanentPath
}

func mapPDStatus(status physicaldrive.PDStatus) string {
Expand Down
27 changes: 24 additions & 3 deletions internal/controller/discovery_ticker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ var _ = Describe("DiscoveryTicker", func() {
NodeName: "test-node",
Interval: 100 * time.Millisecond,
EventChan: eventChan,
UseCase: usecase.NewDiscoverPhysicalDrives(logr.Discard(), nil, nil, &noopCacheWriter{}, "test-node"),
UseCase: usecase.NewDiscoverPhysicalDrives(
logr.Discard(),
[]service.PhysicalDriveDiscoverer{},
[]service.LogicalVolumeDiscoverer{},
nil,
&noopCacheWriter{},
"test-node",
),
}

done := make(chan error, 1)
Expand All @@ -70,7 +77,14 @@ var _ = Describe("DiscoveryTicker", func() {
NodeName: "test-node",
Interval: 50 * time.Millisecond,
EventChan: eventChan,
UseCase: usecase.NewDiscoverPhysicalDrives(logr.Discard(), nil, nil, &noopCacheWriter{}, "test-node"),
UseCase: usecase.NewDiscoverPhysicalDrives(
logr.Discard(),
[]service.PhysicalDriveDiscoverer{},
[]service.LogicalVolumeDiscoverer{},
nil,
&noopCacheWriter{},
"test-node",
),
}

tickCount := 0
Expand Down Expand Up @@ -110,7 +124,14 @@ var _ = Describe("DiscoveryTicker", func() {
NodeName: "test-node",
Interval: time.Hour, // Long interval; should not matter.
EventChan: eventChan,
UseCase: usecase.NewDiscoverPhysicalDrives(logr.Discard(), nil, nil, &noopCacheWriter{}, "test-node"),
UseCase: usecase.NewDiscoverPhysicalDrives(
logr.Discard(),
[]service.PhysicalDriveDiscoverer{},
[]service.LogicalVolumeDiscoverer{},
nil,
&noopCacheWriter{},
"test-node",
),
}

done := make(chan error, 1)
Expand Down
29 changes: 29 additions & 0 deletions pkg/domain/logicalvolume.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
Copyright 2026.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package domain

import (
"github.com/scality/raidmgmt/pkg/domain/entities/logicalvolume"
)

// DiscoveredLogicalVolume extends the raidmgmt LogicalVolume with RAID
// controller type context, mirroring the pattern used by DiscoveredPhysicalDrive.
type DiscoveredLogicalVolume struct {
ControllerType string
ControllerID int
*logicalvolume.LogicalVolume
}
5 changes: 5 additions & 0 deletions pkg/infrastructure/di/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"disk-management-agent/pkg/infrastructure/discovereddrivecache"
"disk-management-agent/pkg/infrastructure/discoveredphysicaldiskstore"
"disk-management-agent/pkg/infrastructure/logicalvolumediscoverer"
"disk-management-agent/pkg/infrastructure/physicaldrivediscoverer"
"disk-management-agent/pkg/usecase"
)
Expand All @@ -47,6 +48,10 @@ type Container struct {
megaraidStorcliDiscoverer *physicaldrivediscoverer.MegaRAID
smartArrayDiscoverer *physicaldrivediscoverer.SmartArray

megaraidPerccliLVDiscoverer *logicalvolumediscoverer.MegaRAID
megaraidStorcliLVDiscoverer *logicalvolumediscoverer.MegaRAID
smartArrayLVDiscoverer *logicalvolumediscoverer.SmartArray

discoveredPhysicalDiskStore *discoveredphysicaldiskstore.Kubernetes
discoveredDriveCache *discovereddrivecache.InMemory

Expand Down
53 changes: 53 additions & 0 deletions pkg/infrastructure/di/logical_volume_discoverer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
Copyright 2026.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package di

import (
"github.com/scality/raidmgmt/pkg/core"

"disk-management-agent/pkg/infrastructure/logicalvolumediscoverer"
)

func (c *Container) getMegaRAIDPerccliLVDiscoverer() *logicalvolumediscoverer.MegaRAID {
if c.megaraidPerccliLVDiscoverer == nil {
c.megaraidPerccliLVDiscoverer = logicalvolumediscoverer.NewMegaRAID(
core.NewRAIDController(c.getMegaRAIDPerccliRAIDController()),
)
}

return c.megaraidPerccliLVDiscoverer
}

func (c *Container) getMegaRAIDStorcliLVDiscoverer() *logicalvolumediscoverer.MegaRAID {
if c.megaraidStorcliLVDiscoverer == nil {
c.megaraidStorcliLVDiscoverer = logicalvolumediscoverer.NewMegaRAID(
core.NewRAIDController(c.getMegaRAIDStorcliRAIDController()),
)
}

return c.megaraidStorcliLVDiscoverer
}

func (c *Container) getSmartArrayLVDiscoverer() *logicalvolumediscoverer.SmartArray {
if c.smartArrayLVDiscoverer == nil {
c.smartArrayLVDiscoverer = logicalvolumediscoverer.NewSmartArray(
core.NewRAIDController(c.getSmartArrayRAIDController()),
)
}

return c.smartArrayLVDiscoverer
}
11 changes: 9 additions & 2 deletions pkg/infrastructure/di/usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,22 @@ func (c *Container) getDiscoveredDriveCache() *discovereddrivecache.InMemory {
// GetDiscoverPhysicalDrivesUseCase returns the singleton use case instance.
func (c *Container) GetDiscoverPhysicalDrivesUseCase() *usecase.DiscoverPhysicalDrives {
if c.discoverPhysicalDrivesUseCase == nil {
discoverers := []service.PhysicalDriveDiscoverer{
pdDiscoverers := []service.PhysicalDriveDiscoverer{
c.getMegaRAIDPerccliDiscoverer(),
c.getMegaRAIDStorcliDiscoverer(),
c.getSmartArrayDiscoverer(),
}

lvDiscoverers := []service.LogicalVolumeDiscoverer{
c.getMegaRAIDPerccliLVDiscoverer(),
c.getMegaRAIDStorcliLVDiscoverer(),
c.getSmartArrayLVDiscoverer(),
}

c.discoverPhysicalDrivesUseCase = usecase.NewDiscoverPhysicalDrives(
c.logger,
discoverers,
pdDiscoverers,
lvDiscoverers,
c.getDiscoveredPhysicalDiskStore(),
c.getDiscoveredDriveCache(),
c.nodeName,
Expand Down
66 changes: 66 additions & 0 deletions pkg/infrastructure/logicalvolumediscoverer/megaraid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
Copyright 2026.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

//nolint:dupl // This function is similar to the one in the SmartArray implementation.
package logicalvolumediscoverer

import (
"github.com/pkg/errors"
"github.com/scality/raidmgmt/pkg/domain/ports"

"disk-management-agent/pkg/domain"
"disk-management-agent/pkg/service"
)

const megaraidControllerType = "MegaRAID"

// MegaRAID discovers logical volumes behind MegaRAID/PERC controllers
// using storcli or perccli.
type MegaRAID struct {
rc ports.RAIDController
}

var _ service.LogicalVolumeDiscoverer = &MegaRAID{}

func NewMegaRAID(rc ports.RAIDController) *MegaRAID {
return &MegaRAID{rc: rc}
}

func (d *MegaRAID) DiscoverLogicalVolumes() ([]*domain.DiscoveredLogicalVolume, error) {
controllers, err := d.rc.Controllers()
if err != nil {
return nil, errors.Wrap(err, "failed to list MegaRAID controllers")
}

var volumes []*domain.DiscoveredLogicalVolume

for _, ctrl := range controllers {
lvs, err := d.rc.LogicalVolumes(ctrl.Metadata)
if err != nil {
return nil, errors.Wrapf(err, "failed to list logical volumes for MegaRAID controller %d", ctrl.ID)
}

for _, lv := range lvs {
volumes = append(volumes, &domain.DiscoveredLogicalVolume{
ControllerType: megaraidControllerType,
ControllerID: ctrl.ID,
LogicalVolume: lv,
})
}
}

return volumes, nil
}
66 changes: 66 additions & 0 deletions pkg/infrastructure/logicalvolumediscoverer/smartarray.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
Copyright 2026.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

//nolint:dupl // This function is similar to the one in the MegaRAID implementation.
package logicalvolumediscoverer

import (
"github.com/pkg/errors"
"github.com/scality/raidmgmt/pkg/domain/ports"

"disk-management-agent/pkg/domain"
"disk-management-agent/pkg/service"
)

const smartArrayControllerType = "SmartArray"

// SmartArray discovers logical volumes behind HPE Smart Array controllers
// using ssacli.
type SmartArray struct {
rc ports.RAIDController
}

var _ service.LogicalVolumeDiscoverer = &SmartArray{}

func NewSmartArray(rc ports.RAIDController) *SmartArray {
return &SmartArray{rc: rc}
}

func (d *SmartArray) DiscoverLogicalVolumes() ([]*domain.DiscoveredLogicalVolume, error) {
controllers, err := d.rc.Controllers()
if err != nil {
return nil, errors.Wrap(err, "failed to list SmartArray controllers")
}

var volumes []*domain.DiscoveredLogicalVolume

for _, ctrl := range controllers {
lvs, err := d.rc.LogicalVolumes(ctrl.Metadata)
if err != nil {
return nil, errors.Wrapf(err, "failed to list logical volumes for SmartArray controller %d", ctrl.ID)
}

for _, lv := range lvs {
volumes = append(volumes, &domain.DiscoveredLogicalVolume{
ControllerType: smartArrayControllerType,
ControllerID: ctrl.ID,
LogicalVolume: lv,
})
}
}

return volumes, nil
}
26 changes: 26 additions & 0 deletions pkg/service/logical_volume_discoverer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
Copyright 2026.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package service

import "disk-management-agent/pkg/domain"

// LogicalVolumeDiscoverer discovers logical volumes from RAID controllers.
// Each implementation corresponds to a specific RAID controller adapter
// (MegaRAID, SmartArray).
type LogicalVolumeDiscoverer interface {
DiscoverLogicalVolumes() ([]*domain.DiscoveredLogicalVolume, error)
}
Loading
Loading