Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename Disk stuff -> Drive and teach diskwipe example more tricks #163

Merged
Merged
7 changes: 4 additions & 3 deletions actions/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ type VirtualDiskManager interface {
VirtualDisks(ctx context.Context) ([]*utils.MvcliDevice, error)
}

// DiskWiper defines an interface to override disk data
type DiskWiper interface {
WipeDisk(ctx context.Context, log *logrus.Logger, logicalName string) error
// DriveWiper defines an interface to override disk data
type DriveWiper interface {
// WipeDrive wipes away all data from the drive, wipe is always verified to have succeeded
WipeDrive(context.Context, *logrus.Logger, *common.Drive) error
}
12 changes: 6 additions & 6 deletions actions/storage_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,28 +78,28 @@ func (s *StorageControllerAction) GetControllerUtility(vendorName, modelName str
}

// GetWipeUtility returns the wipe utility based on the disk wipping features
func (s *StorageControllerAction) GetWipeUtility(logicalName string) (DiskWiper, error) {
s.Logger.Tracef("%s | Detecting wipe utility", logicalName)
func (s *StorageControllerAction) GetWipeUtility(drive *common.Drive) (DriveWiper, error) {
s.Logger.Tracef("%s | Detecting wipe utility", drive.LogicalName)
// TODO: use disk wipping features to return the best wipe utility, currently only one available

return utils.NewFillZeroCmd(s.trace), nil
}

func (s *StorageControllerAction) WipeDisk(ctx context.Context, log *logrus.Logger, logicalName string) error {
util, err := s.GetWipeUtility(logicalName)
func (s *StorageControllerAction) WipeDrive(ctx context.Context, log *logrus.Logger, drive *common.Drive) error {
util, err := s.GetWipeUtility(drive)
if err != nil {
return err
}

// Watermark disk
// Before wiping the disk, we apply watermarks to later verify successful deletion
check, err := utils.ApplyWatermarks(logicalName)
check, err := utils.ApplyWatermarks(drive)
if err != nil {
return err
}

// Wipe the disk
err = util.WipeDisk(ctx, log, logicalName)
err = util.WipeDrive(ctx, log, drive)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions examples/diskwipe/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
diskwipe
91 changes: 83 additions & 8 deletions examples/diskwipe/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,101 @@ package main

import (
"context"
"flag"
"strings"
"time"

"github.com/bmc-toolbox/common"
"github.com/metal-toolbox/ironlib"
"github.com/metal-toolbox/ironlib/actions"
"github.com/metal-toolbox/ironlib/utils"
"github.com/sirupsen/logrus"
)

// This example invokes ironlib and wipes the disk /dev/sdZZZ with a timeout of 1 day
var (
defaultTimeout = time.Minute

logicalName = flag.String("drive", "/dev/someN", "disk to wipe by filling with zeros")
timeout = flag.String("timeout", defaultTimeout.String(), "time to wait for command to complete")
verbose = flag.Bool("verbose", false, "show command runs and output")
)

func main() {
flag.Parse()

logger := logrus.New()
logger.Formatter = new(logrus.JSONFormatter)
logger.SetLevel(logrus.TraceLevel)
logger.Formatter = new(logrus.TextFormatter)
if *verbose {
logger.SetLevel(logrus.TraceLevel)
}
l := logger.WithField("drive", *logicalName)

sca := actions.NewStorageControllerAction(logger)
ctx, cancel := context.WithTimeout(context.Background(), 86400*time.Second)
timeout, err := time.ParseDuration(*timeout)
if err != nil {
l.WithError(err).Fatal("failed to parse timeout duration")
}

ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

err := sca.WipeDisk(ctx, logger, "/dev/sdZZZ")
collector, err := ironlib.New(logger)
if err != nil {
l.WithError(err).Fatal("exiting")
}

inventory, err := collector.GetInventory(ctx, actions.WithDynamicCollection())
if err != nil {
l.WithError(err).Fatal("exiting")
}

var drive *common.Drive
for _, d := range inventory.Drives {
if d.LogicalName == *logicalName {
drive = d
break
}
}
if drive == nil {
l.Fatal("unable to find disk")
}

// Pick the most appropriate wipe based on the disk type and features supported
var wiper actions.DriveWiper
switch drive.Protocol {
case "nvme":
wiper = utils.NewNvmeCmd(*verbose)
case "sata":
// Lets see if drive supports TRIM, if so we'll use blkdiscard
for _, cap := range drive.Capabilities {
if strings.HasPrefix(cap.Description, "Data Set Management TRIM supported") {
if cap.Enabled {
wiper = utils.NewBlkdiscardCmd(*verbose)
}
break
}
}

// drive does not support TRIM so we fall back to filling it up with zero
if wiper == nil {
wiper = utils.NewFillZeroCmd(*verbose)

// If the user supplied a non-default timeout then we'll honor it, otherwise we just go with a huge timeout.
// If this were *real* code and not an example some work could be done to guesstimate a timeout based on disk size.
if timeout == defaultTimeout {
l.WithField("timeout", timeout.String()).Info("increasing timeout")
timeout = 24 * time.Hour
ctx, cancel = context.WithTimeout(context.WithoutCancel(ctx), timeout)
defer cancel()
}
}
}

if wiper == nil {
l.Fatal("failed find appropriate wiper drive")
}

err = wiper.WipeDrive(ctx, logger, drive)
if err != nil {
logger.Fatal(err)
l.Fatal("failed to wipe drive")
}
logger.Println("Wiped successfully!")
}
1 change: 1 addition & 0 deletions examples/nvme-ns-wipe/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nvme-ns-wipe
16 changes: 9 additions & 7 deletions examples/blkdiscard/main.go → examples/nvme-ns-wipe/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,32 @@ import (
)

var (
device = flag.String("device", "/dev/sdZZZ", "disk to wipe using blkdiscard")
timeout = flag.String("timeout", (2 * time.Minute).String(), "time to wait for command to complete")
logicalName = flag.String("drive", "/dev/nvmeX", "nvme disk to wipe")
timeout = flag.String("timeout", (1 * time.Minute).String(), "time to wait for command to complete")
verbose = flag.Bool("verbose", false, "show command runs and output")
)

// This example invokes ironlib and runs blkdiscard on the disk /dev/sdZZZ
func main() {
flag.Parse()

logger := logrus.New()
logger.Formatter = new(logrus.TextFormatter)
logger.SetLevel(logrus.TraceLevel)
if *verbose {
logger.SetLevel(logrus.TraceLevel)
}

timeout, err := time.ParseDuration(*timeout)
if err != nil {
logger.WithError(err).Fatal("failed to parse timeout duration")
}

blkdiscard := utils.NewBlkdiscardCmd()
nvme := utils.NewNvmeCmd(*verbose)

ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

logger.Info("running blkdiscard on ", *device)
err = blkdiscard.Discard(ctx, *device)
logger.Info("resetting namespaces")
err = nvme.ResetNS(ctx, *logicalName)
if err != nil {
logger.WithError(err).Fatal("exiting")
}
Expand Down
1 change: 0 additions & 1 deletion examples/nvme-wipe/.gitignore

This file was deleted.

46 changes: 0 additions & 46 deletions examples/nvme-wipe/main.go

This file was deleted.

28 changes: 23 additions & 5 deletions utils/blkdiscard.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"cmp"
"context"
"os"

"github.com/bmc-toolbox/common"
"github.com/sirupsen/logrus"
)

const (
Expand All @@ -15,26 +18,41 @@ type Blkdiscard struct {
}

// Return a new blkdiscard executor
func NewBlkdiscardCmd() *Blkdiscard {
func NewBlkdiscardCmd(trace bool) *Blkdiscard {
// lookup env var for util
utility := cmp.Or(os.Getenv(EnvBlkdiscardUtility), "blkdiscard")

e := NewExecutor(utility)
e.SetEnv([]string{"LC_ALL=C.UTF-8"})

if !trace {
e.SetQuiet()
}

return &Blkdiscard{Executor: e}
}

// Discard runs blkdiscard on the given device (--force is always used)
func (b *Blkdiscard) Discard(ctx context.Context, device string) error {
b.Executor.SetArgs("--force", device)
func (b *Blkdiscard) Discard(ctx context.Context, drive *common.Drive) error {
b.Executor.SetArgs("--force", drive.LogicalName)

_, err := b.Executor.Exec(ctx)
verify, err := ApplyWatermarks(drive)
if err != nil {
return err
}

return nil
_, err = b.Executor.Exec(ctx)
if err != nil {
return err
}

return verify()
}

// WipeDrive implements DriveWipe by calling Discard
func (b *Blkdiscard) WipeDrive(ctx context.Context, logger *logrus.Logger, drive *common.Drive) error {
logger.WithField("drive", drive.LogicalName).WithField("method", "blkdiscard").Info("wiping")
return b.Discard(ctx, drive)
}

// NewFakeBlkdiscard returns a mock implementation of the Blkdiscard interface for use in tests.
Expand Down
18 changes: 17 additions & 1 deletion utils/blkdiscard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,28 @@ package utils

import (
"context"
"os"
"testing"

"github.com/bmc-toolbox/common"
"github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
)

func Test_blkdiscard(t *testing.T) {
err := NewFakeBlkdiscard().Discard(context.TODO(), "/dev/sdZZZ")
logger, hook := test.NewNullLogger()
defer hook.Reset()

d := t.TempDir()
tmpfile, err := os.CreateTemp(d, "fake-block-device")
assert.NoError(t, err)

err = os.WriteFile(tmpfile.Name(), make([]byte, 8192), 0o600)
assert.NoError(t, err)

drive := &common.Drive{Common: common.Common{LogicalName: tmpfile.Name()}}
err = NewFakeBlkdiscard().WipeDrive(context.TODO(), logger, drive)

// fake-block-device isn't a blockdevice that supports TRIM so we expect an error
assert.Error(t, err)
}
17 changes: 12 additions & 5 deletions utils/fill_zero.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"time"

"github.com/bmc-toolbox/common"
"github.com/sirupsen/logrus"
)

Expand All @@ -23,12 +24,17 @@ func NewFillZeroCmd(trace bool) *FillZero {
return &z
}

func (z *FillZero) WipeDisk(ctx context.Context, l *logrus.Logger, logicalName string) error {
log := l.WithField("device", logicalName)
log.Info("starting zero-fill")
func (z *FillZero) WipeDrive(ctx context.Context, logger *logrus.Logger, drive *common.Drive) error {
log := logger.WithField("drive", drive.LogicalName).WithField("method", "zero-fill")
log.Info("wiping")

verify, err := ApplyWatermarks(drive)
if err != nil {
return err
}

// Write open
file, err := os.OpenFile(logicalName, os.O_WRONLY, 0)
file, err := os.OpenFile(drive.LogicalName, os.O_WRONLY, 0)
if err != nil {
return err
}
Expand Down Expand Up @@ -78,7 +84,8 @@ func (z *FillZero) WipeDisk(ctx context.Context, l *logrus.Logger, logicalName s
if err != nil {
return err
}
return nil

return verify()
}

func printProgress(log *logrus.Entry, totalBytesWritten, partitionSize int64, start time.Time, bytesSinceLastPrint int64) {
Expand Down
Loading
Loading