Repository: threefoldtech/zos
Pull request #1352 (2021-08-11 09:09:20)

Zos3/storage refactor


Patch:
diff --git a/cmds/identityd/main.go b/cmds/identityd/main.go
index 7e3cff28..9f6f56c3 100644
--- a/cmds/identityd/main.go
+++ b/cmds/identityd/main.go
@@ -14,7 +14,6 @@ import (
 	"github.com/pkg/errors"
 	"github.com/threefoldtech/zos/pkg/app"
 	"github.com/threefoldtech/zos/pkg/stubs"
-	"github.com/threefoldtech/zos/pkg/substrate"
 	"github.com/threefoldtech/zos/pkg/upgrade"
 
 	"github.com/cenkalti/backoff/v3"
@@ -108,16 +107,7 @@ func main() {
 		}
 		stub := stubs.NewIdentityManagerStub(client)
 		nodeID := stub.NodeID(ctx)
-		fmt.Println("NodeID:", nodeID)
-
-		sk := stub.PrivateKey(ctx)
-		identity, err := substrate.Identity(sk)
-		if err != nil {
-			fmt.Fprintln(os.Stderr, "failed to get node identity:", err)
-			os.Exit(1)
-		}
-
-		fmt.Println("Address:", identity.Address)
+		fmt.Println(nodeID)
 		os.Exit(0)
 	}
 
diff --git a/cmds/modules/capacityd/register.go b/cmds/modules/capacityd/register.go
index 4b71e950..f0f96ad4 100644
--- a/cmds/modules/capacityd/register.go
+++ b/cmds/modules/capacityd/register.go
@@ -112,8 +112,11 @@ func registerNode(
 	log.Info().Msg("registering node on blockchain")
 
 	sk := ed25519.PrivateKey(mgr.PrivateKey(ctx))
-
-	if _, err := sub.EnsureAccount(sk); err != nil {
+	id, err := substrate.IdentityFromSecureKey(sk)
+	if err != nil {
+		return 0, err
+	}
+	if _, err := sub.EnsureAccount(&id); err != nil {
 		return 0, errors.Wrap(err, "failed to ensure account")
 	}
 
@@ -155,12 +158,12 @@ func registerNode(
 		node.Location = location
 
 		log.Debug().Msg("node data have changing, issuing an update node")
-		_, err = sub.UpdateNode(sk, *node)
+		_, err = sub.UpdateNode(&id, *node)
 		return uint32(node.TwinID), err
 	}
 
 	// create node
-	_, err = sub.CreateNode(sk, substrate.Node{
+	_, err = sub.CreateNode(&id, substrate.Node{
 		FarmID:       types.U32(env.FarmerID),
 		TwinID:       types.U32(twinID),
 		Resources:    resources,
@@ -178,13 +181,13 @@ func registerNode(
 }
 
 func ensureTwin(sub *substrate.Substrate, sk ed25519.PrivateKey, ip net.IP) (uint32, error) {
-	identity, err := substrate.Identity(sk)
+	identity, err := substrate.IdentityFromSecureKey(sk)
 	if err != nil {
 		return 0, err
 	}
 	twinID, err := sub.GetTwinByPubKey(identity.PublicKey)
 	if errors.Is(err, substrate.ErrNotFound) {
-		return sub.CreateTwin(sk, ip)
+		return sub.CreateTwin(&identity, ip)
 	} else if err != nil {
 		return 0, errors.Wrap(err, "failed to list twins")
 	}
@@ -199,5 +202,5 @@ func ensureTwin(sub *substrate.Substrate, sk ed25519.PrivateKey, ip net.IP) (uin
 	}
 
 	// update twin to new ip
-	return sub.UpdateTwin(sk, ip)
+	return sub.UpdateTwin(&identity, ip)
 }
diff --git a/cmds/modules/provisiond/main.go b/cmds/modules/provisiond/main.go
index f8d7274d..169492a1 100644
--- a/cmds/modules/provisiond/main.go
+++ b/cmds/modules/provisiond/main.go
@@ -3,7 +3,6 @@ package provisiond
 import (
 	"context"
 	"crypto/ed25519"
-	"fmt"
 	"os"
 	"path/filepath"
 	"time"
@@ -209,7 +208,7 @@ func action(cli *cli.Context) error {
 		return errors.Wrap(err, "failed to create substrate admins database")
 	}
 
-	kp, err := substrate.Identity(sk)
+	kp, err := substrate.IdentityFromSecureKey(sk)
 	if err != nil {
 		return errors.Wrap(err, "failed to get substrate keypair from secure key")
 	}
@@ -260,7 +259,7 @@ func action(cli *cli.Context) error {
 		Msg("starting provision module")
 
 	// call the runtime upgrade before running engine
-	provisioners.RuntimeUpgrade(ctx)
+	provisioners.InitializeZDB(ctx)
 
 	// spawn the engine
 	go func() {
@@ -312,22 +311,12 @@ func action(cli *cli.Context) error {
 func getNodeReserved(cl zbus.Client, available gridtypes.Capacity) (counter primitives.Counters, err error) {
 	// fill in reserved storage
 	storage := stubs.NewStorageModuleStub(cl)
-	fs, err := storage.GetCacheFS(context.TODO())
+	fs, err := storage.Cache(context.TODO())
 	if err != nil {
 		return counter, err
 	}
 
-	var v *primitives.AtomicUnit
-	switch fs.DiskType {
-	case zos.HDDDevice:
-		v = &counter.HRU
-	case zos.SSDDevice:
-		v = &counter.SRU
-	default:
-		return counter, fmt.Errorf("unknown cache disk type '%s'", fs.DiskType)
-	}
-
-	v.Increment(fs.Usage.Size)
+	counter.SRU.Increment(fs.Usage.Size)
 
 	// we reserve 10% of memory to ZOS itself, with a min of 2G
 	counter.MRU.Increment(
diff --git a/cmds/modules/provisiond/reporter.go b/cmds/modules/provisiond/reporter.go
index 0c34a1c3..88416bc2 100644
--- a/cmds/modules/provisiond/reporter.go
+++ b/cmds/modules/provisiond/reporter.go
@@ -73,7 +73,7 @@ type Report struct {
 type Reporter struct {
 	cl        zbus.Client
 	nodeID    uint32
-	sk        ed25519.PrivateKey
+	identity  substrate.Identity
 	engine    provision.Engine
 	queue     *dque.DQue
 	substrate *substrate.Substrate
@@ -109,14 +109,15 @@ func NewReporter(engine provision.Engine, nodeID uint32, cl zbus.Client, root st
 		return nil, errors.Wrap(err, "failed to setup report persisted queue")
 	}
 
-	identity := stubs.NewIdentityManagerStub(cl)
-	sk := ed25519.PrivateKey(identity.PrivateKey(context.TODO()))
+	idMgr := stubs.NewIdentityManagerStub(cl)
+	sk := ed25519.PrivateKey(idMgr.PrivateKey(context.TODO()))
+	id, err := substrate.IdentityFromSecureKey(sk)
 
 	return &Reporter{
 		cl:        cl,
 		engine:    engine,
 		nodeID:    nodeID,
-		sk:        sk,
+		identity:  id,
 		queue:     queue,
 		substrate: sub,
 	}, nil
@@ -137,7 +138,7 @@ func (r *Reporter) pushOne() ([]Consumption, error) {
 	for _, cmp := range report.Consumption {
 		consumptions = append(consumptions, cmp.Consumption)
 	}
-	if err := r.substrate.Report(r.sk, consumptions); err != nil {
+	if err := r.substrate.Report(&r.identity, consumptions); err != nil {
 		return nil, errors.Wrap(err, "failed to publish consumption report")
 	}
 
diff --git a/cmds/modules/storaged/main.go b/cmds/modules/storaged/main.go
index 18f7eb17..12511b58 100644
--- a/cmds/modules/storaged/main.go
+++ b/cmds/modules/storaged/main.go
@@ -10,7 +10,6 @@ import (
 	"github.com/urfave/cli/v2"
 
 	"github.com/threefoldtech/zbus"
-	"github.com/threefoldtech/zos/pkg/app"
 	"github.com/threefoldtech/zos/pkg/storage"
 	"github.com/threefoldtech/zos/pkg/utils"
 )
@@ -63,13 +62,6 @@ func action(cli *cli.Context) error {
 
 	server.Register(zbus.ObjectID{Name: "storage", Version: "0.0.1"}, storageModule)
 
-	vdiskModule, err := storage.NewVDiskModule(storageModule)
-	if err != nil {
-		log.Error().Err(err).Bool("limited-cache", app.CheckFlag(app.LimitedCache)).Msg("failed to initialize virtual disk module")
-	} else {
-		server.Register(zbus.ObjectID{Name: "vdisk", Version: "0.0.1"}, vdiskModule)
-	}
-
 	log.Info().
 		Str("broker", msgBrokerCon).
 		Uint("worker nr", workerNr).
diff --git a/go.sum b/go.sum
index 97f3a096..1a564b04 100644
--- a/go.sum
+++ b/go.sum
@@ -324,6 +324,7 @@ github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW
 github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
 github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
 github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
+github.com/dave/jennifer v1.3.0 h1:p3tl41zjjCZTNBytMwrUuiAnherNUZktlhPTKoF/sEk=
 github.com/dave/jennifer v1.3.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
 github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go
index 1f2edf83..6ecf1171 100644
--- a/pkg/cache/cache.go
+++ b/pkg/cache/cache.go
@@ -32,5 +32,5 @@ func VolatileDir(name string, size uint64) (string, error) {
 	if err := os.Mkdir(name, 0700); err != nil {
 		return name, err
 	}
-	return name, syscall.Mount("", name, "tmpfs", 0, fmt.Sprintf("size=%d", size))
+	return name, syscall.Mount("none", name, "tmpfs", 0, fmt.Sprintf("size=%d", size))
 }
diff --git a/pkg/container.go b/pkg/container.go
index 79ac304a..c1fc905c 100644
--- a/pkg/container.go
+++ b/pkg/container.go
@@ -4,6 +4,8 @@ package pkg
 //go:generate zbusc -module container -version 0.0.1 -name container -package stubs github.com/threefoldtech/zos/pkg+ContainerModule stubs/container_stub.go
 
 import (
+	"time"
+
 	"github.com/threefoldtech/zos/pkg/container/logger"
 	"github.com/threefoldtech/zos/pkg/gridtypes"
 )
@@ -63,6 +65,8 @@ type Container struct {
 	Stats []Stats
 	// Elevated privileges (to use fuse inside)
 	Elevated bool
+	// CreatedAt time
+	CreatedAt time.Time
 }
 
 // ContainerModule defines rpc interface to containerd
diff --git a/pkg/container/container.go b/pkg/container/container.go
index bb4663d4..d3e81f14 100644
--- a/pkg/container/container.go
+++ b/pkg/container/container.go
@@ -352,12 +352,16 @@ func (c *Module) Inspect(ns string, id pkg.ContainerID) (result pkg.Container, e
 	if err != nil {
 		return result, err
 	}
-
 	spec, err := container.Spec(ctx)
 	if err != nil {
 		return result, err
 	}
+	info, err := container.Info(ctx)
+	if err != nil {
+		return pkg.Container{}, err
+	}
 
+	result.CreatedAt = info.CreatedAt
 	result.RootFS = spec.Root.Path
 	result.Name = container.ID()
 
diff --git a/pkg/flist/cleanup.go b/pkg/flist/cleanup.go
index a186e971..6d9c2246 100644
--- a/pkg/flist/cleanup.go
+++ b/pkg/flist/cleanup.go
@@ -80,7 +80,7 @@ func (f *flistModule) cleanupMount(ctx context.Context, name string) error {
 		log.Warn().Err(err).Str("path", path).Msg("fail to unmount flist")
 	}
 
-	fs, err := f.storage.Path(ctx, name)
+	fs, err := f.storage.VolumeLookup(ctx, name)
 	if err != nil {
 		log.Warn().Err(err).Str("subvolume", name).Msg("subvolume does not exist")
 		return nil
diff --git a/pkg/flist/flist.go b/pkg/flist/flist.go
index 19d4eb62..0e5d2de8 100644
--- a/pkg/flist/flist.go
+++ b/pkg/flist/flist.go
@@ -23,7 +23,6 @@ import (
 	"github.com/threefoldtech/zos/pkg"
 	"github.com/threefoldtech/zos/pkg/environment"
 	"github.com/threefoldtech/zos/pkg/gridtypes"
-	"github.com/threefoldtech/zos/pkg/gridtypes/zos"
 	"github.com/threefoldtech/zos/pkg/stubs"
 )
 
@@ -64,17 +63,17 @@ type volumeAllocator interface {
 	// more space is available in such a pool, `ErrNotEnoughSpace` is returned.
 	// It is up to the caller to handle such a situation and decide if he wants
 	// to try again on a different devicetype
-	CreateFilesystem(ctx context.Context, name string, size gridtypes.Unit, poolType pkg.DeviceType) (pkg.Filesystem, error)
+	VolumeCreate(ctx context.Context, name string, size gridtypes.Unit) (pkg.Volume, error)
 
 	// ReleaseFilesystem signals that the named filesystem is no longer needed.
 	// The filesystem will be unmounted and subsequently removed.
 	// All data contained in the filesystem will be lost, and the
 	// space which has been reserved for this filesystem will be reclaimed.
-	ReleaseFilesystem(ctx context.Context, name string) error
+	VolumeDelete(ctx context.Context, name string) error
 
 	// Path return the filesystem named name
 	// if no filesystem with this name exists, an error is returned
-	Path(ctx context.Context, name string) (pkg.Filesystem, error)
+	VolumeLookup(ctx context.Context, name string) (pkg.Volume, error)
 }
 
 type flistModule struct {
@@ -301,7 +300,7 @@ func (f *flistModule) mountOverlay(ctx context.Context, name, ro string, size gr
 	}
 
 	// check if the filesystem doesn't already exists
-	backend, err := f.storage.Path(ctx, name)
+	backend, err := f.storage.VolumeLookup(ctx, name)
 	newAllocation := false
 	if err != nil {
 		log.Info().Msgf("create new subvolume %s", name)
@@ -311,7 +310,7 @@ func (f *flistModule) mountOverlay(ctx context.Context, name, ro string, size gr
 			return fmt.Errorf("invalid mount option, missing disk type")
 		}
 		newAllocation = true
-		backend, err = f.storage.CreateFilesystem(ctx, name, size, zos.SSDDevice)
+		backend, err = f.storage.VolumeCreate(ctx, name, size)
 		if err != nil {
 			return errors.Wrap(err, "failed to create read-write subvolume for 0-fs")
 		}
@@ -322,7 +321,7 @@ func (f *flistModule) mountOverlay(ctx context.Context, name, ro string, size gr
 		// we need to deallocate the filesystem
 
 		if newAllocation && err != nil {
-			f.storage.ReleaseFilesystem(ctx, name)
+			f.storage.VolumeDelete(ctx, name)
 		}
 	}()
 
@@ -527,7 +526,7 @@ func (f *flistModule) Unmount(name string) error {
 	// - delete the volume, this should be done only for RW (TODO)
 	// mounts, but for now it's still safe to try to remove the subvolume anyway
 	// this will work only for rw mounts.
-	if err := f.storage.ReleaseFilesystem(context.Background(), name); err != nil {
+	if err := f.storage.VolumeDelete(context.Background(), name); err != nil {
 		log.Error().Err(err).Msg("fail to clean up subvolume")
 	}
 
diff --git a/pkg/gridtypes/zos/zdb.go b/pkg/gridtypes/zos/zdb.go
index 43b8f656..f83b21d6 100644
--- a/pkg/gridtypes/zos/zdb.go
+++ b/pkg/gridtypes/zos/zdb.go
@@ -34,7 +34,6 @@ type ZDB struct {
 	Size     gridtypes.Unit `json:"size"`
 	Mode     ZDBMode        `json:"mode"`
 	Password string         `json:"password"`
-	DiskType DeviceType     `json:"disk_type"`
 	Public   bool           `json:"public"`
 }
 
@@ -48,9 +47,6 @@ func (z ZDB) Valid(getter gridtypes.WorkloadGetter) error {
 		return fmt.Errorf("invalid mode")
 	}
 
-	if err := z.DiskType.Valid(); err != nil {
-		return err
-	}
 	return nil
 }
 
@@ -66,9 +62,7 @@ func (z ZDB) Challenge(b io.Writer) error {
 	if _, err := fmt.Fprintf(b, "%s", z.Password); err != nil {
 		return err
 	}
-	if _, err := fmt.Fprintf(b, "%s", z.DiskType.String()); err != nil {
-		return err
-	}
+
 	if _, err := fmt.Fprintf(b, "%t", z.Public); err != nil {
 		return err
 	}
@@ -78,15 +72,7 @@ func (z ZDB) Challenge(b io.Writer) error {
 
 // Capacity implements WorkloadData
 func (z ZDB) Capacity() (cap gridtypes.Capacity, err error) {
-	switch z.DiskType {
-	case HDDDevice:
-		cap.HRU = z.Size
-	case SSDDevice:
-		cap.SRU = z.Size
-	default:
-		return cap, fmt.Errorf("invalid volume type '%s'", z.DiskType.String())
-	}
-
+	cap.HRU = z.Size
 	return
 }
 
diff --git a/pkg/identity/identityd.go b/pkg/identity/identityd.go
index a4d300c5..64409c84 100644
--- a/pkg/identity/identityd.go
+++ b/pkg/identity/identityd.go
@@ -3,6 +3,7 @@ package identity
 import (
 	"os"
 
+	"github.com/rs/zerolog/log"
 	"github.com/threefoldtech/zos/pkg/crypto"
 
 	"github.com/pkg/errors"
@@ -29,6 +30,9 @@ func NewManager(path string) (pkg.IdentityManager, error) {
 			return nil, errors.Wrap(err, "failed to persist key seed")
 		}
 	} else if err != nil {
+		if err := os.Remove(path); err != nil {
+			log.Error().Err(err).Msg("failed to delete corrupt seed file")
+		}
 		return nil, errors.Wrap(err, "failed to load seed")
 	} else {
 		pair, err = FromSeed(seed)
diff --git a/pkg/network/networker.go b/pkg/network/networker.go
index 01008769..1317113f 100644
--- a/pkg/network/networker.go
+++ b/pkg/network/networker.go
@@ -565,7 +565,7 @@ func (n networker) Addrs(iface string, netns string) ([]net.IP, error) {
 			return errors.Wrapf(err, "failed to list addresses of interfaces %s", iface)
 		}
 		ips = make([]net.IP, 0, len(addrs))
-		for i, addr := range addrs {
+		for _, addr := range addrs {
 			ip := addr.IP
 			if ip6 := ip.To16(); ip6 != nil {
 				// ipv6
@@ -575,8 +575,9 @@ func (n networker) Addrs(iface string, netns string) ([]net.IP, error) {
 				}
 			}
 
-			ips[i] = addr.IP
+			ips = append(ips, addr.IP)
 		}
+
 		return nil
 	}
 
@@ -749,7 +750,7 @@ func (n *networker) SetPublicConfig(cfg pkg.PublicConfig) error {
 		return errors.Wrap(err, "failed to connect to substrate")
 	}
 	sk := n.identity.PrivateKey(context.Background())
-	identity, err := substrate.Identity(sk)
+	identity, err := substrate.IdentityFromSecureKey(sk)
 	if err != nil {
 		return err
 	}
@@ -789,7 +790,7 @@ func (n *networker) SetPublicConfig(cfg pkg.PublicConfig) error {
 	}
 	// update the node
 	node.PublicConfig = subCfg
-	_, err = sub.UpdateNode(sk, *node)
+	_, err = sub.UpdateNode(&identity, *node)
 	return err
 }
 
diff --git a/pkg/primitives/provisioner.go b/pkg/primitives/provisioner.go
index f6e49c9d..e4570d8f 100644
--- a/pkg/primitives/provisioner.go
+++ b/pkg/primitives/provisioner.go
@@ -1,8 +1,6 @@
 package primitives
 
 import (
-	"context"
-
 	"github.com/threefoldtech/zbus"
 	"github.com/threefoldtech/zos/pkg/gridtypes"
 	"github.com/threefoldtech/zos/pkg/gridtypes/zos"
@@ -25,20 +23,16 @@ func NewPrimitivesProvisioner(zbus zbus.Client) *Primitives {
 	}
 
 	provisioners := map[gridtypes.WorkloadType]provision.DeployFunction{
-		// zos.ContainerType:      p.containerProvision,
-		zos.ZMountType:  p.zMountProvision,
-		zos.NetworkType: p.networkProvision,
-		zos.ZDBType:     p.zdbProvision,
-		// zos.KubernetesType:     p.kubernetesProvision,
+		zos.ZMountType:   p.zMountProvision,
+		zos.NetworkType:  p.networkProvision,
+		zos.ZDBType:      p.zdbProvision,
 		zos.ZMachineType: p.virtualMachineProvision,
 		zos.PublicIPType: p.publicIPProvision,
 	}
 	decommissioners := map[gridtypes.WorkloadType]provision.RemoveFunction{
-		// zos.ContainerType:      p.containerDecommission,
-		zos.ZMountType:  p.zMountDecommission,
-		zos.NetworkType: p.networkDecommission,
-		zos.ZDBType:     p.zdbDecommission,
-		// zos.KubernetesType:     p.kubernetesDecomission,
+		zos.ZMountType:   p.zMountDecommission,
+		zos.NetworkType:  p.networkDecommission,
+		zos.ZDBType:      p.zdbDecommission,
 		zos.ZMachineType: p.vmDecomission,
 		zos.PublicIPType: p.publicIPDecomission,
 	}
@@ -52,8 +46,3 @@ func NewPrimitivesProvisioner(zbus zbus.Client) *Primitives {
 
 	return p
 }
-
-// RuntimeUpgrade runs upgrade needed when provision daemon starts
-func (p *Primitives) RuntimeUpgrade(ctx context.Context) {
-	p.upgradeRunningZdb(ctx)
-}
diff --git a/pkg/primitives/vm.go b/pkg/primitives/vm.go
index 709998e8..087958d0 100644
--- a/pkg/primitives/vm.go
+++ b/pkg/primitives/vm.go
@@ -43,7 +43,7 @@ func (p *Primitives) virtualMachineProvision(ctx context.Context, wl *gridtypes.
 }
 
 func (p *Primitives) mountsToDisks(ctx context.Context, deployment gridtypes.Deployment, disks []zos.MachineMount, format bool) ([]pkg.VMDisk, error) {
-	storage := stubs.NewVDiskModuleStub(p.zbus)
+	storage := stubs.NewStorageModuleStub(p.zbus)
 
 	var results []pkg.VMDisk
 	for _, disk := range disks {
@@ -58,13 +58,13 @@ func (p *Primitives) mountsToDisks(ctx context.Context, deployment gridtypes.Dep
 			return nil, fmt.Errorf("invalid disk '%s' state", disk.Name)
 		}
 
-		info, err := storage.Inspect(ctx, wl.ID.String())
+		info, err := storage.DiskLookup(ctx, wl.ID.String())
 		if err != nil {
 			return nil, errors.Wrapf(err, "failed to inspect disk '%s'", disk.Name)
 		}
 
 		if format {
-			if err := storage.EnsureFilesystem(ctx, wl.ID.String()); err != nil {
+			if err := storage.DiskFormat(ctx, wl.ID.String()); err != nil {
 				return nil, errors.Wrap(err, "failed to prepare mount")
 			}
 		}
@@ -76,7 +76,7 @@ func (p *Primitives) mountsToDisks(ctx context.Context, deployment gridtypes.Dep
 }
 func (p *Primitives) virtualMachineProvisionImpl(ctx context.Context, wl *gridtypes.WorkloadWithID) (result zos.ZMachineResult, err error) {
 	var (
-		storage = stubs.NewVDiskModuleStub(p.zbus)
+		storage = stubs.NewStorageModuleStub(p.zbus)
 		network = stubs.NewNetworkerStub(p.zbus)
 		flist   = stubs.NewFlisterStub(p.zbus)
 		vm      = stubs.NewVMModuleStub(p.zbus)
@@ -237,14 +237,14 @@ func (p *Primitives) virtualMachineProvisionImpl(ctx context.Context, wl *gridty
 			return result, fmt.Errorf("boot disk was not deployed correctly")
 		}
 		var info pkg.VDisk
-		info, err = storage.Inspect(ctx, disk.ID.String())
+		info, err = storage.DiskLookup(ctx, disk.ID.String())
 		if err != nil {
 			return result, errors.Wrap(err, "disk does not exist")
 		}
 
 		//TODO: this should not happen if disk image was written before !!
 		// fs detection must be done here
-		if err = storage.WriteImage(ctx, disk.ID.String(), imageInfo.ImagePath); err != nil {
+		if err = storage.DiskWrite(ctx, disk.ID.String(), imageInfo.ImagePath); err != nil {
 			return result, errors.Wrap(err, "failed to write image to disk")
 		}
 
diff --git a/pkg/primitives/zdb.go b/pkg/primitives/zdb.go
index 3d62679a..d347b1ba 100644
--- a/pkg/primitives/zdb.go
+++ b/pkg/primitives/zdb.go
@@ -25,88 +25,191 @@ import (
 const (
 	// https://hub.grid.tf/api/flist/tf-autobuilder/threefoldtech-0-db-development.flist/light
 	// To get the latest symlink pointer
-	zdbFlistURL    = "https://hub.grid.tf/tf-autobuilder/threefoldtech-0-db-development-b5155357d5.flist"
-	zdbContainerNS = "zdb"
-	zdbPort        = 9900
+	zdbFlistURL         = "https://hub.grid.tf/tf-autobuilder/threefoldtech-0-db-release-development-c81c68391d.flist"
+	zdbContainerNS      = "zdb"
+	zdbContainerDataMnt = "/zdb"
+	zdbPort             = 9900
 )
 
 // ZDB types
 type ZDB = zos.ZDB
 
+type tZDBContainer pkg.Container
+
+func (z *tZDBContainer) DataMount() (string, error) {
+	for _, mnt := range z.Mounts {
+		if mnt.Target == zdbContainerDataMnt {
+			return mnt.Source, nil
+		}
+	}
+
+	return "", fmt.Errorf("container '%s' does not have a valid data mount", z.Name)
+}
+
 func (p *Primitives) zdbProvision(ctx context.Context, wl *gridtypes.WorkloadWithID) (interface{}, error) {
 	return p.zdbProvisionImpl(ctx, wl)
 }
 
-func (p *Primitives) zdbProvisionImpl(ctx context.Context, wl *gridtypes.WorkloadWithID) (zos.ZDBResult, error) {
+func (p *Primitives) zdbListContainers(ctx context.Context) (map[pkg.ContainerID]tZDBContainer, error) {
 	var (
-		storage = stubs.NewZDBAllocaterStub(p.zbus)
+		contmod = stubs.NewContainerModuleStub(p.zbus)
+	)
+
+	containerIDs, err := contmod.List(ctx, zdbContainerNS)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to list running containers")
+	}
+
+	// for each container we try to find a free space to jam in this new zdb namespace
+	// request
+	m := make(map[pkg.ContainerID]tZDBContainer)
 
-		nsID   = wl.ID.String()
-		config ZDB
+	for _, containerID := range containerIDs {
+		container, err := contmod.Inspect(ctx, zdbContainerNS, containerID)
+		if err != nil {
+			log.Error().Err(err).Str("container-id", string(containerID)).Msg("failed to inspect zdb container")
+			continue
+		}
+		cont := tZDBContainer(container)
+
+		if _, err = cont.DataMount(); err != nil {
+			log.Error().Err(err).Msg("failed to get data directory of zdb container")
+			continue
+		}
+		m[containerID] = cont
+	}
+
+	return m, nil
+}
+
+func (p *Primitives) zdbProvisionImpl(ctx context.Context, wl *gridtypes.WorkloadWithID) (zos.ZDBResult, error) {
+	var (
+		//contmod = stubs.NewContainerModuleStub(p.zbus)
+		storage = stubs.NewStorageModuleStub(p.zbus)
+		nsID    = wl.ID.String()
+		config  ZDB
 	)
 	if err := json.Unmarshal(wl.Data, &config); err != nil {
 		return zos.ZDBResult{}, errors.Wrap(err, "failed to decode reservation schema")
 	}
 
-	// if we reached here, we need to create the 0-db namespace
-	log.Debug().Msg("allocating storage for namespace")
-	allocation, err := storage.Allocate(ctx, nsID, config.DiskType, config.Size, config.Mode)
+	ipsToString := func(ips []net.IP) []string {
+		result := make([]string, 0, len(ips))
+		for _, ip := range ips {
+			result = append(result, ip.String())
+		}
+
+		return result
+	}
+	// for each container we try to find a free space to jam in this new zdb namespace
+	// request
+	containers, err := p.zdbListContainers(ctx)
 	if err != nil {
-		return zos.ZDBResult{}, errors.Wrap(err, "failed to allocate storage")
+		return zos.ZDBResult{}, errors.Wrap(err, "failed to list container data volumes")
 	}
 
-	containerID := pkg.ContainerID(allocation.VolumeID)
+	var candidates []tZDBContainer
+	// check if namespace already exist
+	for id, container := range containers {
+		dataPath, _ := container.DataMount() // the error should not happen
 
-	cont, err := p.ensureZdbContainer(ctx, allocation, config.Mode)
-	if err != nil {
-		return zos.ZDBResult{}, errors.Wrapf(err, "failed to ensure zdb containe running")
+		index := zdb.NewIndex(dataPath)
+		nss, err := index.Namespaces()
+		if err != nil {
+			// skip or error
+			log.Error().Err(err).Str("container-id", string(id)).Msg("couldn't list namespaces")
+			continue
+		}
+
+		for _, ns := range nss {
+			if ns.Name != nsID {
+				continue
+			}
+
+			containerIPs, err := p.waitZDBIPs(ctx, container.Network.Namespace, container.CreatedAt)
+			if err != nil {
+				return zos.ZDBResult{}, errors.Wrap(err, "failed to find IP address on zdb0 interface")
+			}
+
+			return zos.ZDBResult{
+				Namespace: nsID,
+				IPs:       ipsToString(containerIPs),
+				Port:      zdbPort,
+			}, nil
+		}
+
+		// we did not find the namespace, but is this container
+		// a possible candidate to hold the new namespace?
+		reserved, err := index.Reserved()
+		if err != nil {
+			return zos.ZDBResult{}, errors.Wrap(err, "failed to check total reserved size")
+		}
+
+		device, err := storage.DeviceLookup(ctx, container.Name)
+		if err != nil {
+			log.Error().Err(err).Str("container", string(id)).Msg("failed to inspect zdb device")
+			continue
+		}
+
+		if reserved+uint64(config.Size) <= uint64(device.Usage.Size) {
+			candidates = append(candidates, container)
+		}
+	}
+
+	var cont tZDBContainer
+	if len(candidates) > 0 {
+		cont = candidates[0]
+	} else {
+		// allocate new disk
+		device, err := storage.DeviceAllocate(ctx, config.Size)
+		if err != nil {
+			return zos.ZDBResult{}, errors.Wrap(err, "couldn't allocate device to satisfy namespace size")
+		}
+		cont, err = p.ensureZdbContainer(ctx, device)
+		if err != nil {
+			return zos.ZDBResult{}, errors.Wrap(err, "failed to start zdb container")
+		}
 	}
 
-	containerIPs, err := p.waitZDBIPs(ctx, cont.Network.Namespace)
+	containerIPs, err := p.waitZDBIPs(ctx, cont.Network.Namespace, cont.CreatedAt)
 	if err != nil {
 		return zos.ZDBResult{}, errors.Wrap(err, "failed to find IP address on zdb0 interface")
 	}
-	log.Warn().Msgf("ip for zdb containers %s", containerIPs)
 
+	log.Warn().Msgf("ip for zdb containers %s", containerIPs)
 	// this call will actually configure the namespace in zdb and set the password
-	if err := p.createZDBNamespace(containerID, nsID, config); err != nil {
+	if err := p.createZDBNamespace(pkg.ContainerID(cont.Name), nsID, config); err != nil {
 		return zos.ZDBResult{}, errors.Wrap(err, "failed to create zdb namespace")
 	}
 
 	return zos.ZDBResult{
 		Namespace: nsID,
-		IPs: func() []string {
-			ips := make([]string, len(containerIPs))
-			for i, ip := range containerIPs {
-				ips[i] = ip.String()
-			}
-			return ips
-		}(),
-		Port: zdbPort,
+		IPs:       ipsToString(containerIPs),
+		Port:      zdbPort,
 	}, nil
 }
 
-func (p *Primitives) ensureZdbContainer(ctx context.Context, allocation pkg.Allocation, mode pkg.ZDBMode) (pkg.Container, error) {
+func (p *Primitives) ensureZdbContainer(ctx context.Context, device pkg.Device) (tZDBContainer, error) {
 	var container = stubs.NewContainerModuleStub(p.zbus)
 
-	name := pkg.ContainerID(allocation.VolumeID)
+	name := pkg.ContainerID(device.ID)
 
 	cont, err := container.Inspect(ctx, zdbContainerNS, name)
 	if err != nil && strings.Contains(err.Error(), "not found") {
 		// container not found, create one
-		if err := p.createZdbContainer(ctx, allocation, mode); err != nil {
-			return cont, err
+		if err := p.createZdbContainer(ctx, device); err != nil {
+			return tZDBContainer(cont), err
 		}
 		cont, err = container.Inspect(ctx, zdbContainerNS, name)
 		if err != nil {
-			return pkg.Container{}, err
+			return tZDBContainer{}, err
 		}
 	} else if err != nil {
 		// other error
-		return pkg.Container{}, err
+		return tZDBContainer{}, err
 	}
 
-	return cont, nil
+	return tZDBContainer(cont), nil
 
 }
 
@@ -115,34 +218,29 @@ func (p *Primitives) zdbRootFS(ctx context.Context) (string, error) {
 	var err error
 	var rootFS string
 
-	for _, typ := range []zos.DeviceType{zos.HDDDevice, zos.SSDDevice} {
-		rootFS, err = flist.Mount(ctx, zdbFlistURL, "", pkg.MountOptions{
-			Limit:    10 * gridtypes.Megabyte,
-			ReadOnly: false,
-		})
-
-		if err != nil {
-			log.Error().Err(err).Msgf("failed to allocate rootfs for zdb container (type: '%s'): %s", typ, err)
-		}
-
-		if err == nil {
-			break
-		}
+	hash, err := flist.FlistHash(ctx, zdbFlistURL)
+	if err != nil {
+		return "", errors.Wrap(err, "failed to get flist hash")
 	}
 
+	rootFS, err = flist.Mount(ctx, hash, zdbFlistURL, pkg.MountOptions{
+		Limit:    10 * gridtypes.Megabyte,
+		ReadOnly: false,
+	})
+
 	if err != nil {
-		return "", errors.Wrap(err, "failed to allocate rootfs for zdb container")
+		return "", errors.Wrap(err, "failed to mount zdb flist")
 	}
 
 	return rootFS, nil
 }
 
-func (p *Primitives) createZdbContainer(ctx context.Context, allocation pkg.Allocation, mode pkg.ZDBMode) error {
+func (p *Primitives) createZdbContainer(ctx context.Context, device pkg.Device) error {
 	var (
-		name       = pkg.ContainerID(allocation.VolumeID)
+		name       = pkg.ContainerID(device.ID)
 		cont       = stubs.NewContainerModuleStub(p.zbus)
 		flist      = stubs.NewFlisterStub(p.zbus)
-		volumePath = allocation.VolumePath
+		volumePath = device.Path
 		network    = stubs.NewNetworkerStub(p.zbus)
 
 		slog = log.With().Str("containerID", string(name)).Logger()
@@ -166,7 +264,7 @@ func (p *Primitives) createZdbContainer(ctx context.Context, allocation pkg.Allo
 	}
 
 	// create the network namespace and macvlan for the 0-db container
-	netNsName, err := network.ZDBPrepare(ctx, allocation.VolumeID)
+	netNsName, err := network.ZDBPrepare(ctx, device.ID)
 	if err != nil {
 		if err := flist.Unmount(ctx, string(name)); err != nil {
 			slog.Error().Err(err).Str("path", rootFS).Msgf("failed to unmount")
@@ -192,7 +290,7 @@ func (p *Primitives) createZdbContainer(ctx context.Context, allocation pkg.Allo
 		return err
 	}
 
-	cmd := fmt.Sprintf("/bin/zdb --data /data --index /data --mode %s  --listen :: --port %d --socket /socket/zdb.sock --dualnet", string(mode), zdbPort)
+	cmd := fmt.Sprintf("/bin/zdb --protect --admin '%s' --data /zdb/data --index /zdb/index  --listen :: --port %d --socket /socket/zdb.sock --dualnet", device.ID, zdbPort)
 
 	err = p.zdbRun(ctx, string(name), rootFS, cmd, netNsName, volumePath, socketDir)
 	if err != nil {
@@ -220,31 +318,34 @@ func (p *Primitives) createZdbContainer(ctx context.Context, allocation pkg.Allo
 func (p *Primitives) zdbRun(ctx context.Context, name string, rootfs string, cmd string, netns string, volumepath string, socketdir string) error {
 	var cont = stubs.NewContainerModuleStub(p.zbus)
 
+	conf := pkg.Container{
+		Name:        name,
+		RootFS:      rootfs,
+		Entrypoint:  cmd,
+		Interactive: false,
+		Network:     pkg.NetworkInfo{Namespace: netns},
+		Mounts: []pkg.MountInfo{
+			{
+				Source: volumepath,
+				Target: zdbContainerDataMnt,
+			},
+			{
+				Source: socketdir,
+				Target: "/socket",
+			},
+		},
+	}
+
 	_, err := cont.Run(
 		ctx,
 		zdbContainerNS,
-		pkg.Container{
-			Name:        name,
-			RootFS:      rootfs,
-			Entrypoint:  cmd,
-			Interactive: false,
-			Network:     pkg.NetworkInfo{Namespace: netns},
-			Mounts: []pkg.MountInfo{
-				{
-					Source: volumepath,
-					Target: "/data",
-				},
-				{
-					Source: socketdir,
-					Target: "/socket",
-				},
-			},
-		})
+		conf,
+	)
 
 	return err
 }
 
-func (p *Primitives) waitZDBIPs(ctx context.Context, namespace string) ([]net.IP, error) {
+func (p *Primitives) waitZDBIPs(ctx context.Context, namespace string, created time.Time) ([]net.IP, error) {
 	// TODO: this method need to be abstracted, since it's now depends on the knewledge
 	// of the networking daemon internal (interfaces names)
 	// may be at least just get all ips from all interfaces inside the namespace
@@ -254,11 +355,11 @@ func (p *Primitives) waitZDBIPs(ctx context.Context, namespace string) ([]net.IP
 		containerIPs []net.IP
 	)
 
+	log.Debug().Time("created-at", created).Str("namespace", namespace).Msg("checking zdb container ips")
 	getIP := func() error {
-
 		ips, err := network.Addrs(ctx, nwmod.ZDBPubIface, namespace)
 		if err != nil {
-			log.Debug().Err(err).Msg("not ip public found, waiting")
+			log.Debug().Err(err).Msg("no public ip found, waiting")
 			return err
 		}
 		yggIps, err := network.Addrs(ctx, nwmod.ZDBYggIface, namespace)
@@ -288,7 +389,9 @@ func (p *Primitives) waitZDBIPs(ctx context.Context, namespace string) ([]net.IP
 		}
 
 		log.Warn().Msgf("public %v ygg: %v", public, ygg)
-		if public && ygg {
+		if public && ygg || time.Since(created) > 2*time.Minute {
+			// if we have all ips detected or if the container is older than 2 minutes
+			// so it's safe we assume ips are final
 			return nil
 		}
 		return fmt.Errorf("waiting for more addresses")
@@ -322,6 +425,10 @@ func (p *Primitives) createZDBNamespace(containerID pkg.ContainerID, nsID string
 		if err := zdbCl.CreateNamespace(nsID); err != nil {
 			return errors.Wrapf(err, "failed to create namespace in 0-db: %s", containerID)
 		}
+
+		if err := zdbCl.NamespaceSetMode(nsID, string(config.Mode)); err != nil {
+			return errors.Wrap(err, "failed to set namespace mode")
+		}
 	}
 
 	if config.Password != "" {
@@ -342,91 +449,68 @@ func (p *Primitives) createZDBNamespace(containerID pkg.ContainerID, nsID string
 }
 
 func (p *Primitives) zdbDecommission(ctx context.Context, wl *gridtypes.WorkloadWithID) error {
-	var (
-		storage       = stubs.NewZDBAllocaterStub(p.zbus)
-		storageClient = stubs.NewStorageModuleStub(p.zbus)
-
-		config zos.ZDB
-		nsID   = wl.ID.String()
-	)
-
-	if err := json.Unmarshal(wl.Data, &config); err != nil {
-		return errors.Wrap(err, "failed to decode reservation schema")
-	}
-
-	allocation, err := storage.Find(ctx, wl.ID.String())
-	if err != nil && strings.Contains(err.Error(), "not found") {
-		return nil
-	} else if err != nil {
-		return err
-	}
-
-	_, err = p.ensureZdbContainer(ctx, allocation, config.Mode)
+	containers, err := p.zdbListContainers(ctx)
 	if err != nil {
-		return errors.Wrap(err, "failed to find namespace zdb container")
+		return errors.Wrap(err, "failed to list running zdbs")
 	}
 
-	containerID := pkg.ContainerID(allocation.VolumeID)
-
-	zdbCl := zdbConnection(containerID)
-	defer zdbCl.Close()
-	if err := zdbCl.Connect(); err != nil {
-		return errors.Wrapf(err, "failed to connect to 0-db: %s", containerID)
-	}
-
-	if err := zdbCl.DeleteNamespace(nsID); err != nil {
-		return errors.Wrapf(err, "failed to delete namespace in 0-db: %s", containerID)
-	}
-
-	ns, err := zdbCl.Namespaces()
-	if err != nil {
-		return errors.Wrap(err, "failed to retrieve zdb namespaces")
-	}
-
-	log.Info().Msgf("zdb has %d namespaces left", len(ns))
+	for id, container := range containers {
+		con := zdbConnection(id)
+		if con.Connect(); err == nil {
+			if ok, _ := con.Exist(wl.ID.String()); ok {
+				if err := con.DeleteNamespace(wl.ID.String()); err != nil {
+					return errors.Wrap(err, "failed to delete namespace")
+				}
+			}
 
-	// If there are no more namespaces left except for the default namespace, we can delete this subvolume
-	if len(ns) == 1 && ns[0] == "default" {
-		log.Info().Msg("decommissioning zdb container because there are no more namespaces left")
-		err = p.deleteZdbContainer(ctx, containerID)
+			continue
+		}
+		// if we failed to connect, may be check the data directory if the namespace exists
+		data, err := container.DataMount()
 		if err != nil {
-			return errors.Wrap(err, "failed to decommission zdb container")
+			log.Error().Err(err).Str("container-id", string(id)).Msg("failed to get container data directory")
+			return err
+		}
+
+		idx := zdb.NewIndex(data)
+		if !idx.Exists(wl.ID.String()) {
+			continue
 		}
 
-		log.Info().Msgf("deleting subvolumes of reservation: %s", allocation.VolumeID)
-		// we also need to delete the flist volume
-		return storageClient.ReleaseFilesystem(ctx, allocation.VolumeID)
+		return idx.Delete(wl.ID.String())
 	}
 
 	return nil
 }
 
-func (p *Primitives) deleteZdbContainer(ctx context.Context, containerID pkg.ContainerID) error {
-	container := stubs.NewContainerModuleStub(p.zbus)
-	flist := stubs.NewFlisterStub(p.zbus)
+// func (p *Primitives) deleteZdbContainer(ctx context.Context, containerID pkg.ContainerID) error {
+// 	// TODO: if a zdb container is not serving any namespaces, should we delete it?
 
-	info, err := container.Inspect(ctx, "zdb", containerID)
-	if err != nil && strings.Contains(err.Error(), "not found") {
-		return nil
-	} else if err != nil {
-		return errors.Wrapf(err, "failed to inspect container '%s'", containerID)
-	}
+// 	container := stubs.NewContainerModuleStub(p.zbus)
+// 	flist := stubs.NewFlisterStub(p.zbus)
 
-	if err := container.Delete(ctx, "zdb", containerID); err != nil {
-		return errors.Wrapf(err, "failed to delete container %s", containerID)
-	}
+// 	info, err := container.Inspect(ctx, "zdb", containerID)
+// 	if err != nil && strings.Contains(err.Error(), "not found") {
+// 		return nil
+// 	} else if err != nil {
+// 		return errors.Wrapf(err, "failed to inspect container '%s'", containerID)
+// 	}
 
-	network := stubs.NewNetworkerStub(p.zbus)
-	if err := network.ZDBDestroy(ctx, info.Network.Namespace); err != nil {
-		return errors.Wrapf(err, "failed to destroy zdb network namespace")
-	}
+// 	if err := container.Delete(ctx, "zdb", containerID); err != nil {
+// 		return errors.Wrapf(err, "failed to delete container %s", containerID)
+// 	}
 
-	if err := flist.Unmount(ctx, string(containerID)); err != nil {
-		return errors.Wrapf(err, "failed to unmount flist at %s", info.RootFS)
-	}
+// 	network := stubs.NewNetworkerStub(p.zbus)
+// 	if err := network.ZDBDestroy(ctx, info.Network.Namespace); err != nil {
+// 		return errors.Wrapf(err, "failed to destroy zdb network namespace")
+// 	}
 
-	return nil
-}
+// 	if err := flist.Unmount(ctx, string(containerID)); err != nil {
+// 		return errors.Wrapf(err, "failed to unmount flist at %s", info.RootFS)
+// 	}
+
+// 	return nil
+// }
 
 func socketDir(containerID pkg.ContainerID) string {
 	return fmt.Sprintf("/var/run/zdb_%s", containerID)
@@ -439,7 +523,7 @@ func socketFile(containerID pkg.ContainerID) string {
 // we declare this method as a variable so we can
 // mock it in testing.
 var zdbConnection = func(id pkg.ContainerID) zdb.Client {
-	socket := fmt.Sprintf("unix://%s", socketFile(id))
+	socket := fmt.Sprintf("unix://%s@%s", string(id), socketFile(id))
 	return zdb.New(socket)
 }
 
@@ -465,20 +549,13 @@ func isYgg(ip net.IP) bool {
 	return yggNet.Contains(ip)
 }
 
-func (p *Primitives) upgradeRunningZdb(ctx context.Context) error {
-	log.Info().Msg("checking for any outdated zdb running")
-
-	flistmod := stubs.NewFlisterStub(p.zbus)
-	contmod := stubs.NewContainerModuleStub(p.zbus)
-
-	// Listing running zdb containers
-	log.Debug().Msg("fetching zdb containers list")
-	containers, err := contmod.List(ctx, zdbContainerNS)
-	if err != nil {
-		log.Error().Err(err).Msg("could not load containers list")
-		return err
-	}
-
+// InitializeZDB makes sure all required zdbs are running
+func (p *Primitives) InitializeZDB(ctx context.Context) error {
+	var (
+		storage  = stubs.NewStorageModuleStub(p.zbus)
+		contmod  = stubs.NewContainerModuleStub(p.zbus)
+		flistmod = stubs.NewFlisterStub(p.zbus)
+	)
 	// fetching extected hash
 	log.Debug().Msg("fetching flist hash")
 	expected, err := flistmod.FlistHash(ctx, zdbFlistURL)
@@ -487,88 +564,110 @@ func (p *Primitives) upgradeRunningZdb(ctx context.Context) error {
 		return err
 	}
 
-	log.Debug().Str("hash", expected).Msg("expecting hash")
-
-	// Checking if containers are running latest zdb version
-	for _, c := range containers {
-		if c == "" {
-			continue
-		}
+	devices, err := storage.Devices(ctx)
+	if err != nil {
+		return errors.Wrap(err, "failed to list allocated zdb devices")
+	}
 
-		log.Debug().Str("id", string(c)).Msg("inspecting container")
+	poolNames := make(map[string]pkg.Device)
+	for _, device := range devices {
+		poolNames[device.ID] = device
+	}
 
-		continfo, err := contmod.Inspect(ctx, zdbContainerNS, c)
+	containers, err := contmod.List(ctx, zdbContainerNS)
+	if err != nil {
+		return errors.Wrap(err, "failed to list running zdb container")
+	}
 
-		if err != nil {
-			log.Error().Err(err).Msg("could not inspect container")
-			continue
+	for _, container := range containers {
+		if err := p.upgradeRuntime(ctx, expected, container); err != nil {
+			log.Error().Err(err).Msg("failed to upgrade running zdb container")
 		}
 
-		hash, err := flistmod.HashFromRootPath(ctx, continfo.RootFS)
-		if err != nil {
-			log.Error().Err(err).Msg("could not find container running flist hash")
-			continue
+		delete(poolNames, string(container))
+	}
+
+	// do we still have allocated pools that does not have associated zdbs.
+	for _, device := range poolNames {
+		if _, err := p.ensureZdbContainer(ctx, device); err != nil {
+			log.Error().Err(err).Str("pool", device.ID).Msg("failed to create zdb container associated with pool")
 		}
+	}
+	return nil
+}
 
-		log.Debug().Str("hash", hash).Msg("running container hash")
+func (p *Primitives) upgradeRuntime(ctx context.Context, expected string, container pkg.ContainerID) error {
+	var (
+		flistmod = stubs.NewFlisterStub(p.zbus)
+		contmod  = stubs.NewContainerModuleStub(p.zbus)
+	)
+	continfo, err := contmod.Inspect(ctx, zdbContainerNS, container)
 
-		if expected != hash {
-			log.Info().Str("id", string(c)).Msg("restarting container, update found")
+	if err != nil {
+		return err
+	}
 
-			// extracting required informations
-			volumeid := continfo.Name // VolumeID is the Container Name
-			volumepath := ""          // VolumePath is /data mount on the container
-			socketdir := ""           // SocketDir is /socket on the container
-			zdbcmd := continfo.Entrypoint
-			netns := continfo.Network.Namespace
+	hash, err := flistmod.HashFromRootPath(ctx, continfo.RootFS)
+	if err != nil {
+		return errors.Wrap(err, "could not find container running flist hash")
+	}
 
-			log.Info().Str("id", volumeid).Str("path", volumepath).Msg("rebuild zdb container")
+	log.Debug().Str("hash", hash).Msg("running container hash")
+	if hash == expected {
+		return nil
+	}
 
-			for _, mnt := range continfo.Mounts {
-				if mnt.Target == "/data" {
-					volumepath = mnt.Source
-				}
+	log.Info().Str("id", string(container)).Msg("restarting container, update found")
 
-				if mnt.Target == "/socket" {
-					socketdir = mnt.Source
-				}
-			}
+	// extracting required informations
+	volumeid := continfo.Name // VolumeID is the Container Name
+	volumepath := ""          // VolumePath is /data mount on the container
+	socketdir := ""           // SocketDir is /socket on the container
+	zdbcmd := continfo.Entrypoint
+	netns := continfo.Network.Namespace
 
-			if volumepath == "" {
-				log.Error().Msg("could not grab container /data mountpoint")
-				continue
-			}
+	log.Info().Str("id", volumeid).Str("path", volumepath).Msg("rebuild zdb container")
 
-			// stopping running zdb
-			err := contmod.Delete(ctx, zdbContainerNS, c)
-			if err != nil {
-				log.Error().Err(err).Msg("could not stop running zdb container")
-				continue
-			}
+	for _, mnt := range continfo.Mounts {
+		if mnt.Target == zdbContainerDataMnt {
+			volumepath = mnt.Source
+		}
 
-			// cleanup old containers rootfs
-			if err = flistmod.Unmount(ctx, volumeid); err != nil {
-				log.Error().Err(err).Str("path", continfo.RootFS).Msgf("failed to unmount old zdb container")
-			}
+		if mnt.Target == "/socket" {
+			socketdir = mnt.Source
+		}
+	}
 
-			// restarting zdb
+	if volumepath == "" {
+		return fmt.Errorf("could not grab container /data mountpoint")
+	}
 
-			// mount the new flist
-			rootfs, err := p.zdbRootFS(ctx)
-			if err != nil {
-				log.Error().Err(err).Msg("could not initialize zdb rootfs")
-				continue
-			}
+	// stopping running zdb
 
-			// respawn the container
-			err = p.zdbRun(ctx, volumeid, rootfs, zdbcmd, netns, volumepath, socketdir)
-			if err != nil {
-				log.Error().Err(err).Msg("could not restart zdb container")
+	if err := contmod.Delete(ctx, zdbContainerNS, container); err != nil {
+		return errors.Wrap(err, "could not stop running zdb container")
+	}
 
-				if err = flistmod.Unmount(ctx, volumeid); err != nil {
-					log.Error().Err(err).Str("path", rootfs).Msgf("failed to unmount zdb container")
-				}
-			}
+	// cleanup old containers rootfs
+	if err = flistmod.Unmount(ctx, volumeid); err != nil {
+		log.Error().Err(err).Str("path", continfo.RootFS).Msgf("failed to unmount old zdb container")
+	}
+
+	// restarting zdb
+
+	// mount the new flist
+	rootfs, err := p.zdbRootFS(ctx)
+	if err != nil {
+		return errors.Wrap(err, "could not initialize zdb rootfs")
+	}
+
+	// respawn the container
+	err = p.zdbRun(ctx, volumeid, rootfs, zdbcmd, netns, volumepath, socketdir)
+	if err != nil {
+		log.Error().Err(err).Msg("could not restart zdb container")
+
+		if err = flistmod.Unmount(ctx, volumeid); err != nil {
+			log.Error().Err(err).Str("path", rootfs).Msgf("failed to unmount zdb container")
 		}
 	}
 
diff --git a/pkg/primitives/zmount.go b/pkg/primitives/zmount.go
index 9d7e31c9..5c828262 100644
--- a/pkg/primitives/zmount.go
+++ b/pkg/primitives/zmount.go
@@ -22,12 +22,12 @@ func (p *Primitives) volumeProvisionImpl(ctx context.Context, wl *gridtypes.Work
 	}
 
 	vol.ID = wl.ID.String()
-	vdisk := stubs.NewVDiskModuleStub(p.zbus)
-	if vdisk.Exists(ctx, vol.ID) {
+	vdisk := stubs.NewStorageModuleStub(p.zbus)
+	if vdisk.DiskExists(ctx, vol.ID) {
 		return vol, nil
 	}
 
-	_, err = vdisk.Allocate(ctx, vol.ID, config.Size)
+	_, err = vdisk.DiskCreate(ctx, vol.ID, config.Size)
 
 	return vol, err
 }
@@ -38,6 +38,6 @@ func (p *Primitives) zMountProvision(ctx context.Context, wl *gridtypes.Workload
 }
 
 func (p *Primitives) zMountDecommission(ctx context.Context, wl *gridtypes.WorkloadWithID) error {
-	vdisk := stubs.NewVDiskModuleStub(p.zbus)
-	return vdisk.Deallocate(ctx, wl.ID.String())
+	vdisk := stubs.NewStorageModuleStub(p.zbus)
+	return vdisk.DiskDelete(ctx, wl.ID.String())
 }
diff --git a/pkg/provision/engine.go b/pkg/provision/engine.go
index 3393afec..7d0ae26a 100644
--- a/pkg/provision/engine.go
+++ b/pkg/provision/engine.go
@@ -198,11 +198,19 @@ func GetDeployment(ctx context.Context) gridtypes.Deployment {
 	return *dl
 }
 
+func withDeployment(ctx context.Context, dl *gridtypes.Deployment) context.Context {
+	return context.WithValue(ctx, deploymentKey{}, dl)
+}
+
 // GetContract of deployment. panics if engine has no substrate set.
 func GetContract(ctx context.Context) *substrate.Contract {
 	return ctx.Value(contractKey{}).(*substrate.Contract)
 }
 
+func withContract(ctx context.Context, contract *substrate.Contract) context.Context {
+	return context.WithValue(ctx, contractKey{}, contract)
+}
+
 // GetSubstrate if engine has substrate set, panics otherwise
 func GetSubstrate(ctx context.Context) *substrate.Substrate {
 	return ctx.Value(substrateKey{}).(*substrate.Substrate)
@@ -338,7 +346,7 @@ func (e *NativeEngine) Run(root context.Context) error {
 		}
 
 		job := obj.(*engineJob)
-		ctx := context.WithValue(root, deploymentKey{}, &job.Target)
+		ctx := withDeployment(root, &job.Target)
 
 		// contract validation
 		// this should ONLY be done on provosion and update operation
@@ -417,7 +425,7 @@ func (e *NativeEngine) contract(ctx context.Context, dl *gridtypes.Deployment) (
 		return nil, errors.Wrap(err, "failed to get deployment contract")
 	}
 
-	ctx = context.WithValue(ctx, contractKey{}, contract)
+	ctx = withContract(ctx, contract)
 
 	if uint32(contract.Node) != e.nodeID {
 		return nil, fmt.Errorf("invalid node address in contract")
@@ -459,7 +467,7 @@ func (e *NativeEngine) boot(root context.Context) error {
 			// unfortunately we have to inject this value here
 			// since the boot runs outside the engine queue.
 
-			ctx := context.WithValue(root, deploymentKey{}, dl)
+			ctx := withDeployment(root, &dl)
 			if e.installDeployment(ctx, &dl) {
 				if err := e.storage.Set(dl); err != nil {
 					log.Error().Err(err).Msg("failed to set workload result")
@@ -660,7 +668,7 @@ func (e *NativeEngine) DecommissionCached(id string, reason string) error {
 
 	//to bad we have to repeat this here
 	ctx := context.WithValue(context.Background(), engineKey{}, e)
-	ctx = context.WithValue(ctx, deploymentKey{}, dl)
+	ctx = withDeployment(ctx, &dl)
 
 	ctx, cancel := context.WithTimeout(ctx, 3*time.Minute)
 	defer cancel()
diff --git a/pkg/rmb/rmb.go b/pkg/rmb/rmb.go
index c6ae2d17..f3a73dd5 100644
--- a/pkg/rmb/rmb.go
+++ b/pkg/rmb/rmb.go
@@ -66,7 +66,7 @@ func (m *messageBusSubrouter) call(ctx context.Context, route string, payload []
 	if ok {
 		defer func() {
 			if rec := recover(); rec != nil {
-				err = fmt.Errorf("handler panicked with: %s", err)
+				err = fmt.Errorf("handler panicked with: %s", rec)
 			}
 		}()
 
diff --git a/pkg/storage.go b/pkg/storage.go
index 80aa4863..23a5d58c 100644
--- a/pkg/storage.go
+++ b/pkg/storage.go
@@ -11,21 +11,8 @@ import (
 
 //go:generate mkdir -p stubs
 //go:generate zbusc -module storage -version 0.0.1 -name storage -package stubs github.com/threefoldtech/zos/pkg+StorageModule stubs/storage_stub.go
-//go:generate zbusc -module storage -version 0.0.1 -name vdisk -package stubs github.com/threefoldtech/zos/pkg+VDiskModule stubs/vdisk_stub.go
 
 // RaidProfile type
-type RaidProfile string
-
-const (
-	// Single profile
-	Single RaidProfile = "single"
-	// Raid0 profile
-	Raid0 RaidProfile = "raid0"
-	// Raid1 profile
-	Raid1 RaidProfile = "raid1"
-	// Raid10 profile
-	Raid10 RaidProfile = "raid10"
-)
 
 // ErrNotEnoughSpace indicates that there is not enough space in a pool
 // of the requested type to create the filesystem
@@ -70,100 +57,99 @@ type (
 	}
 )
 
-// Validate make sure profile is correct
-func (p RaidProfile) Validate() error {
-	if _, ok := raidProfiles[p]; !ok {
-		return fmt.Errorf("not supported raid profile '%s'", p)
-	}
-
-	return nil
-}
-
-var (
-	raidProfiles = map[RaidProfile]struct{}{
-		Single: {}, Raid1: {}, Raid10: {},
-	}
-	// DefaultPolicy value
-	DefaultPolicy = StoragePolicy{
-		Raid: Single,
-	}
-
-	// NullPolicy does not create pools
-	NullPolicy = StoragePolicy{}
-)
-
-// StoragePolicy describes the pool creation policy
-type StoragePolicy struct {
-	// Raid profile for this policy
-	Raid RaidProfile
-	// Number of disks to use in a single pool
-	// note that, the disks count must be valid for
-	// the chosen raid profile.
-	Disks uint8
-
-	// Only create this amount of storage pools. Default to 0 -> unlimited.
-	// The spared disks can later be used in automatic repair if a physical
-	// disk got corrupt or bad.
-	// Note that if it's set to 0 (unlimited), some disks might be spared anyway
-	// in case the number of disks required in the policy doesn't add up to pools
-	// for example, a pool of 2s on a machine with 5 disks.
-	MaxPools uint8
-}
-
 // Usage struct
 type Usage struct {
 	Size gridtypes.Unit
 	Used gridtypes.Unit
 }
 
-// Filesystem represents a storage space that can be used as a filesystem
-type Filesystem struct {
-	// Filesystem ID
-	ID int
-	// Path of the Filesystem
-	Path string
-	// Usage reports the current usage of the Filesystem
+// Volume struct is a btrfs subvolume
+type Volume struct {
+	Name  string
+	Path  string
 	Usage Usage
-	// Name of the Filesystem
-	Name string
-	// FsType of the Filesystem
-	FsType string
-	// DiskType of the Filesystem
-	DiskType DeviceType
 }
 
-// VolumeAllocater is the zbus interface of the storage module responsible
-// for volume allocation
-type VolumeAllocater interface {
-	// CreateFilesystem creates a filesystem with a given size. The filesystem
-	// is mounted, and the path to the mountpoint is returned. The filesystem
-	// is only attempted to be created in a pool of the given type. If no
-	// more space is available in such a pool, `ErrNotEnoughSpace` is returned.
-	// It is up to the caller to handle such a situation and decide if he wants
-	// to try again on a different devicetype
-	CreateFilesystem(name string, size gridtypes.Unit, poolType DeviceType) (Filesystem, error)
-
-	// UpdateFilesystem changes a filesystem size to given value.
-	UpdateFilesystem(name string, size gridtypes.Unit) (Filesystem, error)
-	// ReleaseFilesystem signals that the named filesystem is no longer needed.
-	// The filesystem will be unmounted and subsequently removed.
-	// All data contained in the filesystem will be lost, and the
-	// space which has been reserved for this filesystem will be reclaimed.
-	ReleaseFilesystem(name string) error
-
-	// ListFilesystems return all the filesystem managed by storeaged present on the nodes
-	// this can be an expensive call on server with a lot of disk, don't use it in a
-	// intensive loop
-	// Special filesystem like internal cache and vdisk are not return by this function
-	// to access them use the GetCacheFS or GetVdiskFS
-	ListFilesystems() ([]Filesystem, error)
-
-	// Path return the filesystem named name
-	// if no filesystem with this name exists, an error is returned
-	Path(name string) (Filesystem, error)
-
-	// GetCacheFS return the special filesystem used by 0-OS to store internal state and flist cache
-	GetCacheFS() (Filesystem, error)
+// Device struct is a full hdd
+type Device struct {
+	Path  string
+	ID    string
+	Usage Usage
+}
+
+// StorageModule is the storage subsystem interface
+// this should allow you to work with the following types of storage medium
+// - full disks (device) (these are used by zdb)
+// - subvolumes these are used as a read-write layers for 0-fs mounts
+// - vdisks are used by zmachines
+// this works as following:
+// a storage module maintains a list of ALL disks on the system
+// separated in 2 sets of pools (SSDs, and HDDs)
+// ssd pools can only be used for
+// - subvolumes
+// - vdisks
+// hdd pools are only used by zdb as one disk
+type StorageModule interface {
+	// Cache method return information about zos cache volume
+	Cache() (Volume, error)
+
+	// Total gives the total amount of storage available for a device type
+	Total(kind DeviceType) (uint64, error)
+	// BrokenPools lists the broken storage pools that have been detected
+	BrokenPools() []BrokenPool
+	// BrokenDevices lists the broken devices that have been detected
+	BrokenDevices() []BrokenDevice
+	//Monitor returns stats stream about pools
+	Monitor(ctx context.Context) <-chan PoolsStats
+
+	// Volume management
+
+	// VolumeCreate creates a new volume
+	VolumeCreate(name string, size gridtypes.Unit) (Volume, error)
+
+	// VolumeUpdate updates the size of an existing volume
+	VolumeUpdate(name string, size gridtypes.Unit) error
+
+	// VolumeLookup return volume information for given name
+	VolumeLookup(name string) (Volume, error)
+
+	// VolumeDelete deletes a volume by name
+	VolumeDelete(name string) error
+
+	// VolumeList list all volumes
+	VolumeList() ([]Volume, error)
+
+	// Virtual disk management
+
+	// DiskCreate creates a virtual disk given name and size
+	DiskCreate(name string, size gridtypes.Unit) (VDisk, error)
+
+	// DiskWrite writes the given raw image to disk
+	DiskWrite(name string, image string) error
+
+	// DiskFormat makes sure disk has filesystem, if it already formatted nothing happens
+	DiskFormat(name string) error
+
+	// DiskLookup looks up vdisk by name
+	DiskLookup(name string) (VDisk, error)
+
+	// DiskExists checks if disk exists
+	DiskExists(name string) bool
+
+	// DiskDelete deletes a disk
+	DiskDelete(name string) error
+
+	DiskList() ([]VDisk, error)
+	// Device management
+
+	//Devices list all "allocated" devices
+	Devices() ([]Device, error)
+
+	// DeviceAllocate allocates a new device (formats and give a new ID)
+	DeviceAllocate(min gridtypes.Unit) (Device, error)
+
+	// DeviceLookup inspects a previously allocated device
+	DeviceLookup(name string) (Device, error)
 }
 
 // VDisk info returned by a call to inspect
@@ -178,41 +164,3 @@ type VDisk struct {
 func (d *VDisk) Name() string {
 	return filepath.Base(d.Path)
 }
-
-// VDiskModule interface
-type VDiskModule interface {
-	// AllocateDisk with given id and size and an optional source disk, return path to virtual disk
-	Allocate(id string, size gridtypes.Unit) (string, error)
-
-	// Writes an image image to disk with id
-	WriteImage(id string, image string) error
-
-	// EnsureFilesystem ensures disk has a valid filesystem
-	// this method is idempotent
-	EnsureFilesystem(id string) error
-
-	// DeallocateVDisk removes a virtual disk
-	Deallocate(id string) error
-	// Exists checks if disk with that ID already allocated
-	Exists(id string) bool
-	// Inspect return info about the disk
-	Inspect(id string) (VDisk, error)
-	// List lists all the available vdisks
-	List() ([]VDisk, error)
-}
-
-// StorageModule defines the api for storage
-type StorageModule interface {
-	VolumeAllocater
-	ZDBAllocater
-
-	// Total gives the total amount of storage available for a device type
-	Total(kind DeviceType) (uint64, error)
-	// BrokenPools lists the broken storage pools that have been detected
-	BrokenPools() []BrokenPool
-	// BrokenDevices lists the broken devices that have been detected
-	BrokenDevices() []BrokenDevice
-
-	//Monitor returns stats stream about pools
-	Monitor(ctx context.Context) <-chan PoolsStats
-}
diff --git a/pkg/storage/device.go b/pkg/storage/device.go
new file mode 100644
index 00000000..54ea91e1
--- /dev/null
+++ b/pkg/storage/device.go
@@ -0,0 +1,146 @@
+package storage
+
+import (
+	"fmt"
+
+	"github.com/pkg/errors"
+	"github.com/rs/zerolog/log"
+	"github.com/threefoldtech/zos/pkg"
+	"github.com/threefoldtech/zos/pkg/gridtypes"
+)
+
+const (
+	zdbVolume = "zdb"
+)
+
+//Devices list all "allocated" devices
+func (m *Module) Devices() ([]pkg.Device, error) {
+	var devices []pkg.Device
+	for _, hdd := range m.hdds {
+		if _, err := hdd.Mounted(); err != nil {
+			continue
+		}
+
+		volumes, err := hdd.Volumes()
+		if err != nil {
+			log.Error().Err(err).Str("pool", hdd.Name()).Msg("failed to get pool volumes")
+			continue
+		}
+		usage, err := hdd.Usage()
+		if err != nil {
+			return nil, err
+		}
+		for _, vol := range volumes {
+			if vol.Name() != zdbVolume {
+				continue
+			}
+
+			devices = append(devices, pkg.Device{
+				ID:   hdd.Name(),
+				Path: vol.Path(),
+				Usage: pkg.Usage{
+					Size: gridtypes.Unit(usage.Size),
+					Used: gridtypes.Unit(usage.Used),
+				},
+			})
+			break
+		}
+	}
+
+	return devices, nil
+}
+
+// DeviceLookup looks up device by name
+func (m *Module) DeviceLookup(name string) (pkg.Device, error) {
+	for _, hdd := range m.hdds {
+		if hdd.Name() != name {
+			continue
+		}
+
+		if _, err := hdd.Mounted(); err != nil {
+			return pkg.Device{}, errors.Wrap(err, "device is not allocated")
+		}
+
+		volumes, err := hdd.Volumes()
+		if err != nil {
+			return pkg.Device{}, errors.Wrap(err, "failed to get pool volumes")
+		}
+
+		usage, err := hdd.Usage()
+		if err != nil {
+			return pkg.Device{}, err
+		}
+		for _, vol := range volumes {
+			if vol.Name() != zdbVolume {
+				continue
+			}
+
+			return pkg.Device{
+				ID:   hdd.Name(),
+				Path: vol.Path(),
+				Usage: pkg.Usage{
+					Size: gridtypes.Unit(usage.Size),
+					Used: gridtypes.Unit(usage.Used),
+				},
+			}, nil
+		}
+
+		return pkg.Device{}, errors.Wrap(err, "device is not allocated (no zdb volume found)")
+	}
+
+	return pkg.Device{}, fmt.Errorf("device not found")
+}
+
+// DeviceAllocate allocates a new free device, allocation is done
+// by creation a zdb subvolume
+func (m *Module) DeviceAllocate(min gridtypes.Unit) (pkg.Device, error) {
+	for _, hdd := range m.hdds {
+		if _, err := hdd.Mounted(); err == nil {
+			// mounted pool. skip
+			continue
+		}
+
+		if hdd.Device().Size() < uint64(min) {
+			continue
+		}
+
+		if _, err := hdd.Mount(); err != nil {
+			log.Error().Err(err).Str("pool", hdd.Name()).Msg("failed to mount pool")
+			continue
+		}
+
+		volumes, err := hdd.Volumes()
+		if err != nil {
+			log.Error().Err(err).Str("pool", hdd.Name()).Msg("failed to get pool volumes")
+			continue
+		}
+
+		if len(volumes) != 0 {
+			log.Info().Str("pool", hdd.Name()).Msg("pool is already used")
+			continue
+		}
+
+		volume, err := hdd.AddVolume(zdbVolume)
+		if err != nil {
+			log.Error().Err(err).Msg("failed to create zdb volume")
+			continue
+		}
+
+		usage, err := hdd.Usage()
+		if err != nil {
+			return pkg.Device{}, err
+		}
+
+		return pkg.Device{
+			ID:   hdd.Name(),
+			Path: volume.Path(),
+			Usage: pkg.Usage{
+				Size: gridtypes.Unit(usage.Size),
+				Used: gridtypes.Unit(usage.Used),
+			},
+		}, nil
+
+	}
+
+	return pkg.Device{}, fmt.Errorf("no more free devices found")
+}
diff --git a/pkg/storage/disk.go b/pkg/storage/disk.go
index fcc9c94d..97b1bf65 100644
--- a/pkg/storage/disk.go
+++ b/pkg/storage/disk.go
@@ -12,6 +12,7 @@ import (
 
 	"github.com/g0rbe/go-chattr"
 	"github.com/pkg/errors"
+	log "github.com/rs/zerolog/log"
 	"github.com/threefoldtech/zos/pkg"
 	"github.com/threefoldtech/zos/pkg/gridtypes"
 )
@@ -21,23 +22,69 @@ const (
 	vdiskVolumeName = "vdisks"
 )
 
-type vdiskModule struct {
-	module *Module
+// VDiskPools return a list of all vdisk pools
+func (s *Module) diskPools() ([]string, error) {
+	var paths []string
+	for _, pool := range s.ssds {
+		if _, err := pool.Mounted(); err != nil {
+			continue
+		}
+
+		volumes, err := pool.Volumes()
+		if err != nil {
+			return nil, errors.Wrapf(err, "failed to list pool '%s' volumes", pool.Path())
+		}
+
+		for _, volume := range volumes {
+			if volume.Name() == vdiskVolumeName {
+				paths = append(paths, volume.Path())
+			}
+		}
+	}
+
+	return paths, nil
 }
 
-// NewVDiskModule creates a new disk allocator
-func NewVDiskModule(module *Module) (pkg.VDiskModule, error) {
-	return &vdiskModule{module: module}, nil
+// VDiskFindCandidate find a suitbale location for creating a vdisk of the given size
+func (s *Module) diskFindCandidate(size gridtypes.Unit) (path string, err error) {
+	candidates, err := s.findCandidates(size)
+	if err != nil {
+		return path, err
+	}
+	// does anyone have a vdisk subvol
+	for _, candidate := range candidates {
+		volumes, err := candidate.Pool.Volumes()
+		if err != nil {
+			log.Error().Str("pool", candidate.Pool.Path()).Err(err).Msg("failed to list pool volumes")
+			continue
+		}
+		for _, volume := range volumes {
+			if volume.Name() != vdiskVolumeName {
+				continue
+			}
+
+			return volume.Path(), nil
+		}
+	}
+	// none has a vdiks subvolume, we need to
+	// create one.
+	candidate := candidates[0]
+	volume, err := candidate.Pool.AddVolume(vdiskVolumeName)
+	if err != nil {
+		return path, errors.Wrap(err, "failed to create vdisk pool")
+	}
+
+	return volume.Path(), nil
 }
 
-func (d *vdiskModule) findDisk(id string) (string, error) {
-	pools, err := d.module.VDiskPools()
+func (s *Module) findDisk(id string) (string, error) {
+	pools, err := s.diskPools()
 	if err != nil {
 		return "", errors.Wrapf(err, "failed to find disk with id '%s'", id)
 	}
 
 	for _, pool := range pools {
-		path, err := d.safePath(pool, id)
+		path, err := s.safePath(pool, id)
 		if err != nil {
 			return "", err
 		}
@@ -51,22 +98,22 @@ func (d *vdiskModule) findDisk(id string) (string, error) {
 	return "", os.ErrNotExist
 }
 
-// EnsureFilesystem ensures that the virtual disk has a valid filesystem
+// DiskFormat ensures that the virtual disk has a valid filesystem
 // currently the fs is btrfs
-func (d *vdiskModule) EnsureFilesystem(id string) error {
-	path, err := d.findDisk(id)
+func (s *Module) DiskFormat(name string) error {
+	path, err := s.findDisk(name)
 	if err != nil {
-		return errors.Wrapf(err, "couldn't find disk with id: %s", id)
+		return errors.Wrapf(err, "couldn't find disk with id: %s", name)
 	}
 
-	return d.ensureFS(path)
+	return s.ensureFS(path)
 }
 
-//
-func (d *vdiskModule) WriteImage(id string, image string) error {
-	path, err := d.findDisk(id)
+// DiskWrite writes image to disk
+func (s *Module) DiskWrite(name string, image string) error {
+	path, err := s.findDisk(name)
 	if err != nil {
-		return errors.Wrapf(err, "couldn't find disk with id: %s", id)
+		return errors.Wrapf(err, "couldn't find disk with id: %s", name)
 	}
 	source, err := os.Open(image)
 	if err != nil {
@@ -96,24 +143,24 @@ func (d *vdiskModule) WriteImage(id string, image string) error {
 		return errors.Wrap(err, "failed to write disk image")
 	}
 
-	return d.expandFs(path)
+	return s.expandFs(path)
 }
 
-// AllocateDisk with given size and an optional source disk, return path to virtual disk (size in MB)
-func (d *vdiskModule) Allocate(id string, size gridtypes.Unit) (string, error) {
-	path, err := d.findDisk(id)
+// DiskCreate with given size, return path to virtual disk (size in MB)
+func (s *Module) DiskCreate(name string, size gridtypes.Unit) (disk pkg.VDisk, err error) {
+	path, err := s.findDisk(name)
 	if err == nil {
-		return path, errors.Wrapf(os.ErrExist, "disk with id '%s' already exists", id)
+		return disk, errors.Wrapf(os.ErrExist, "disk with id '%s' already exists", name)
 	}
 
-	base, err := d.module.VDiskFindCandidate(size)
+	base, err := s.diskFindCandidate(size)
 	if err != nil {
-		return "", errors.Wrapf(err, "failed to find a candidate to host vdisk of size '%d'", size)
+		return disk, errors.Wrapf(err, "failed to find a candidate to host vdisk of size '%d'", size)
 	}
 
-	path, err = d.safePath(base, id)
+	path, err = s.safePath(base, name)
 	if err != nil {
-		return "", err
+		return disk, err
 	}
 
 	defer func() {
@@ -126,19 +173,19 @@ func (d *vdiskModule) Allocate(id string, size gridtypes.Unit) (string, error) {
 	var file *os.File
 	file, err = os.Create(path)
 	if err != nil {
-		return "", err
+		return disk, err
 	}
 
 	defer file.Close()
 	if err = chattr.SetAttr(file, chattr.FS_NOCOW_FL); err != nil {
-		return "", err
+		return disk, err
 	}
 
 	err = syscall.Fallocate(int(file.Fd()), 0, 0, int64(size))
-	return path, err
+	return pkg.VDisk{Path: path, Size: int64(size)}, err
 }
 
-func (d *vdiskModule) ensureFS(disk string) error {
+func (s *Module) ensureFS(disk string) error {
 	output, err := exec.Command("mkfs.btrfs", disk).CombinedOutput()
 	if err == nil {
 		return nil
@@ -157,7 +204,7 @@ func (d *vdiskModule) ensureFS(disk string) error {
 	return errors.Wrapf(err, "unknown btrfs error '%s'", string(output))
 }
 
-func (d *vdiskModule) expandFs(disk string) error {
+func (s *Module) expandFs(disk string) error {
 	dname, err := ioutil.TempDir("", "btrfs-resize")
 	if err != nil {
 		return errors.Wrap(err, "couldn't create a temp dir to mount the btrfs fs to resize it")
@@ -179,7 +226,7 @@ func (d *vdiskModule) expandFs(disk string) error {
 	return nil
 }
 
-func (d *vdiskModule) safePath(base, id string) (string, error) {
+func (s *Module) safePath(base, id string) (string, error) {
 	path := filepath.Join(base, id)
 	// this to avoid passing an `injection` id like '../name'
 	// and end up deleting a file on the system. so only delete
@@ -192,9 +239,9 @@ func (d *vdiskModule) safePath(base, id string) (string, error) {
 	return path, nil
 }
 
-// DeallocateVDisk removes a virtual disk
-func (d *vdiskModule) Deallocate(id string) error {
-	path, err := d.findDisk(id)
+// DiskDelete removes a virtual disk
+func (s *Module) DiskDelete(name string) error {
+	path, err := s.findDisk(name)
 	if os.IsNotExist(err) {
 		return nil
 	} else if err != nil {
@@ -208,16 +255,16 @@ func (d *vdiskModule) Deallocate(id string) error {
 	return nil
 }
 
-// DeallocateVDisk removes a virtual disk
-func (d *vdiskModule) Exists(id string) bool {
-	_, err := d.findDisk(id)
+// DiskExists checks if a disk exists
+func (s *Module) DiskExists(id string) bool {
+	_, err := s.findDisk(id)
 
 	return err == nil
 }
 
-// Inspect return info about the disk
-func (d *vdiskModule) Inspect(id string) (disk pkg.VDisk, err error) {
-	path, err := d.findDisk(id)
+// DiskLookup return info about the disk
+func (s *Module) DiskLookup(id string) (disk pkg.VDisk, err error) {
+	path, err := s.findDisk(id)
 
 	if err != nil {
 		return disk, err
@@ -233,8 +280,9 @@ func (d *vdiskModule) Inspect(id string) (disk pkg.VDisk, err error) {
 	return
 }
 
-func (d *vdiskModule) List() ([]pkg.VDisk, error) {
-	pools, err := d.module.VDiskPools()
+// DiskList list all created disks
+func (s *Module) DiskList() ([]pkg.VDisk, error) {
+	pools, err := s.diskPools()
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/storage/filesystem/btrfs.go b/pkg/storage/filesystem/btrfs.go
index b284e4a4..2152b3e3 100644
--- a/pkg/storage/filesystem/btrfs.go
+++ b/pkg/storage/filesystem/btrfs.go
@@ -6,19 +6,16 @@ import (
 	"os"
 	"os/exec"
 	"path/filepath"
-	"strings"
 	"syscall"
 
+	"github.com/google/uuid"
 	"github.com/pkg/errors"
 	"github.com/rs/zerolog/log"
 
 	"github.com/threefoldtech/zos/pkg"
-	"github.com/threefoldtech/zos/pkg/storage/zdbpool"
 )
 
 var (
-	_ Filesystem = (*btrfs)(nil)
-
 	// ErrDeviceAlreadyMounted indicates that a mounted device is attempted
 	// to be mounted again (without MS_BIND flag).
 	ErrDeviceAlreadyMounted = fmt.Errorf("device is already mounted")
@@ -27,166 +24,88 @@ var (
 	ErrDeviceNotMounted = fmt.Errorf("device is not mounted")
 )
 
-var (
-	// divisors for the total usable size of a filesystem
-	// an efficiency multiplier would probably make slightly more sense,
-	// but this way we don't have to cast uints to floats later
-	raidSizeDivisor = map[pkg.RaidProfile]uint64{
-		pkg.Single: 1,
-		pkg.Raid1:  2,
-		pkg.Raid10: 2,
-	}
-)
-
-// btrfs is the filesystem implementation for btrfs
-type btrfs struct {
-	devices DeviceManager
-	utils   BtrfsUtil
+type btrfsPool struct {
+	device Device
+	utils  BtrfsUtil
+	name   string
 }
 
-func newBtrfs(manager DeviceManager, exec executer) *btrfs {
-	return &btrfs{devices: manager, utils: newUtils(exec)}
+// NewBtrfsPool creates a btrfs pool associated with device.
+// if device does not have a filesystem one is created
+func NewBtrfsPool(device Device) (Pool, error) {
+	return newBtrfsPool(device, executerFunc(run))
 }
 
-// NewBtrfs creates a new filesystem that implements btrfs
-func NewBtrfs(manager DeviceManager) Filesystem {
-	return newBtrfs(manager, executerFunc(run))
-}
+func newBtrfsPool(device Device, exe executer) (Pool, error) {
+	pool := &btrfsPool{
+		device: device,
+		utils:  newUtils(exe),
+	}
 
-func (b *btrfs) Create(ctx context.Context, name string, policy pkg.RaidProfile, devices ...*Device) (Pool, error) {
-	return b.create(ctx, name, policy, false, devices)
+	return pool, pool.prepare()
 }
 
-func (b *btrfs) CreateForce(ctx context.Context, name string, policy pkg.RaidProfile, devices ...*Device) (Pool, error) {
-	return b.create(ctx, name, policy, true, devices)
+func (p *btrfsPool) ID() int {
+	return 0
 }
 
-func (b *btrfs) create(ctx context.Context, name string, policy pkg.RaidProfile, force bool, devices []*Device) (Pool, error) {
-	name = strings.TrimSpace(name)
-	if len(name) == 0 {
-		return nil, fmt.Errorf("invalid name")
-	}
+func (p *btrfsPool) Device() Device {
+	return p.device
+}
 
-	block, err := b.devices.ByLabel(ctx, name)
+func (p *btrfsPool) prepare() error {
+	info, err := p.device.Info()
 	if err != nil {
-		return nil, err
+		return errors.Wrapf(err, "failed to get device '%s' info", p.device.Path())
 	}
 
-	if len(block) != 0 {
-		return nil, fmt.Errorf("unique name is required")
+	p.name = info.Label
+	if info.Used() {
+		// device already have filesystem
+		return nil
 	}
 
-	paths := []string{}
-	for _, device := range devices {
-		if device.Used() {
-			return nil, fmt.Errorf("device '%v' is already used", device.Path)
-		}
+	ctx := context.Background()
 
-		paths = append(paths, device.Path)
+	// otherwise format
+	if err := p.format(ctx); err != nil {
+		return err
 	}
+	// make sure kernel knows about this
+	return Partprobe(ctx)
+}
+
+func (p *btrfsPool) format(ctx context.Context) error {
+	name := uuid.New().String()
+	p.name = name
 
 	args := []string{
 		"-L", name,
-		"-d", string(policy),
-		"-m", string(policy),
+		p.device.Path(),
 	}
 
-	if force {
-		args = append(args, "-f")
-	}
-
-	args = append(args, paths...)
-	if _, err := b.utils.run(ctx, "mkfs.btrfs", args...); err != nil {
-		return nil, err
-	}
-
-	// update cached devices
-	for _, dev := range devices {
-		dev.Label = name
-		dev.Filesystem = BtrfsFSType
-	}
-
-	return newBtrfsPool(name, devices, &b.utils), nil
-}
-
-func (b *btrfs) List(ctx context.Context, filter Filter) ([]Pool, error) {
-	if filter == nil {
-		filter = All
-	}
-	var pools []Pool
-	available, err := b.utils.List(ctx, "", false)
-	if err != nil {
-		return nil, err
-	}
-
-	for _, fs := range available {
-		if len(fs.Label) == 0 {
-			// we only assume labeled devices are managed
-			continue
-		}
-
-		devices, err := b.devices.ByLabel(ctx, fs.Label)
-		if err != nil {
-			return nil, err
-		}
-
-		pool := newBtrfsPool(fs.Label, devices, &b.utils)
-
-		if !filter(pool) {
-			continue
-		}
-
-		if len(devices) == 0 {
-			// since this should not be able to happen consider it an error
-			return nil, fmt.Errorf("pool %v has no corresponding devices on the system", fs.Label)
-		}
-
-		pools = append(pools, pool)
+	if _, err := p.utils.run(ctx, "mkfs.btrfs", args...); err != nil {
+		return errors.Wrapf(err, "failed to format device '%s'", p.device.Path())
 	}
 
-	return pools, nil
-}
-
-type btrfsPool struct {
-	name    string
-	devices []*Device
-	utils   *BtrfsUtil
-}
-
-func newBtrfsPool(name string, devices []*Device, utils *BtrfsUtil) *btrfsPool {
-	return &btrfsPool{
-		name:    name,
-		devices: devices,
-		utils:   utils,
-	}
+	return nil
 }
 
 // Mounted checks if the pool is mounted
 // It doesn't check the default mount location of the pool
 // but instead check if any of the pool devices is mounted
 // under any location
-func (p *btrfsPool) Mounted() (string, bool) {
-	ctx := context.Background()
-	list, _ := p.utils.List(ctx, p.Name(), true)
-	if len(list) != 1 {
-		return "", false
+func (p *btrfsPool) Mounted() (string, error) {
+	info, err := p.device.Info()
+	if err != nil {
+		return "", err
 	}
 
-	return p.mounted(&list[0])
-}
-
-func (p *btrfsPool) mounted(fs *Btrfs) (string, bool) {
-	for _, device := range fs.Devices {
-		if target, ok := GetMountTarget(device.Path); ok {
-			return target, true
-		}
+	if len(info.Mountpoint) != 0 {
+		return info.Mountpoint, nil
 	}
 
-	return "", false
-}
-
-func (p *btrfsPool) ID() int {
-	return 0
+	return "", ErrDeviceNotMounted
 }
 
 func (p *btrfsPool) Name() string {
@@ -209,50 +128,21 @@ func (p *btrfsPool) FsType() string {
 
 // Mount mounts the pool in it's default mount location under /mnt/name
 func (p *btrfsPool) Mount() (string, error) {
-	ctx := context.Background()
-	list, _ := p.utils.List(ctx, p.name, false)
-	if len(list) != 1 {
-		return "", fmt.Errorf("unknown pool '%s'", p.name)
-	}
-
-	fs := list[0]
-
-	if len(fs.Devices) == 0 {
-		return "", nil
-	}
-
-	if mnt, mounted := p.mounted(&fs); mounted {
+	mnt, err := p.Mounted()
+	if err == nil {
 		return mnt, nil
+	} else if !errors.Is(err, ErrDeviceNotMounted) {
+		return "", errors.Wrap(err, "failed to check device mount status")
 	}
 
-	mnt := p.Path()
-	if err := os.MkdirAll(mnt, 0755); err != nil {
-		return "", err
-	}
-
-	if err := syscall.Mount(fs.Devices[0].Path, mnt, "btrfs", 0, ""); err != nil {
-		return "", err
-	}
-
-	if err := p.utils.QGroupEnable(ctx, mnt); err != nil {
-		return "", fmt.Errorf("failed to enable qgroup: %w", err)
-	}
-
-	return mnt, p.maintenance()
-}
-
-// MountWithoutScan mounts the pool in it's default mount location under /mnt/name
-// This wont trigger a btrfs filesystem scan and leaves unused disks in standby mode
-// We mount the pool based on the path and the device it has saved
-func (p *btrfsPool) MountWithoutScan() (string, error) {
+	// device is not mounted
 	ctx := context.Background()
-
-	mnt := p.Path()
+	mnt = p.Path()
 	if err := os.MkdirAll(mnt, 0755); err != nil {
 		return "", err
 	}
 
-	if err := syscall.Mount(p.devices[0].Path, mnt, "btrfs", 0, ""); err != nil {
+	if err := syscall.Mount(p.device.Path(), mnt, "btrfs", 0, ""); err != nil {
 		return "", err
 	}
 
@@ -264,73 +154,20 @@ func (p *btrfsPool) MountWithoutScan() (string, error) {
 }
 
 func (p *btrfsPool) UnMount() error {
-	mnt, ok := p.Mounted()
-	if !ok {
+	mnt, err := p.Mounted()
+	if errors.Is(err, ErrDeviceNotMounted) {
 		return nil
-	}
-
-	return syscall.Unmount(mnt, syscall.MNT_DETACH)
-}
-
-func (p *btrfsPool) addDevice(device *Device, root string) error {
-	ctx := context.Background()
-
-	if err := p.utils.DeviceAdd(ctx, device.Path, root); err != nil {
-		return err
-	}
-
-	// update cached device
-	device.Label = p.name
-	device.Filesystem = BtrfsFSType
-
-	p.devices = append(p.devices, device)
-
-	return nil
-}
-
-func (p *btrfsPool) AddDevice(device *Device) error {
-	mnt, ok := p.Mounted()
-	if !ok {
-		return ErrDeviceNotMounted
-	}
-
-	return p.addDevice(device, mnt)
-}
-
-func (p *btrfsPool) removeDevice(device *Device, root string) error {
-	ctx := context.Background()
-
-	if err := p.utils.DeviceRemove(ctx, device.Path, root); err != nil {
+	} else if err != nil {
 		return err
 	}
 
-	for idx, d := range p.devices {
-		if d.Path == device.Path {
-			// remove device from list
-			p.devices = append(p.devices[:idx], p.devices[idx+1:]...)
-		}
-	}
-
-	// update cached device
-	device.Filesystem = ""
-	device.Label = ""
-
-	return nil
-}
-
-func (p *btrfsPool) RemoveDevice(device *Device) error {
-	mnt, ok := p.Mounted()
-	if !ok {
-		return ErrDeviceNotMounted
-	}
-
-	return p.removeDevice(device, mnt)
+	return syscall.Unmount(mnt, syscall.MNT_DETACH)
 }
 
 func (p *btrfsPool) Volumes() ([]Volume, error) {
-	mnt, ok := p.Mounted()
-	if !ok {
-		return nil, ErrDeviceNotMounted
+	mnt, err := p.Mounted()
+	if err != nil {
+		return nil, err
 	}
 
 	var volumes []Volume
@@ -366,9 +203,9 @@ func (p *btrfsPool) addVolume(root string) (Volume, error) {
 }
 
 func (p *btrfsPool) AddVolume(name string) (Volume, error) {
-	mnt, ok := p.Mounted()
-	if !ok {
-		return nil, ErrDeviceNotMounted
+	mnt, err := p.Mounted()
+	if err != nil {
+		return nil, err
 	}
 
 	root := filepath.Join(mnt, name)
@@ -396,9 +233,9 @@ func (p *btrfsPool) removeVolume(root string) error {
 }
 
 func (p *btrfsPool) RemoveVolume(name string) error {
-	mnt, ok := p.Mounted()
-	if !ok {
-		return ErrDeviceNotMounted
+	mnt, err := p.Mounted()
+	if err != nil {
+		return err
 	}
 
 	root := filepath.Join(mnt, name)
@@ -407,9 +244,9 @@ func (p *btrfsPool) RemoveVolume(name string) error {
 
 // Size return the pool size
 func (p *btrfsPool) Usage() (usage Usage, err error) {
-	mnt, ok := p.Mounted()
-	if !ok {
-		return usage, ErrDeviceNotMounted
+	mnt, err := p.Mounted()
+	if err != nil {
+		return usage, err
 	}
 
 	du, err := p.utils.GetDiskUsage(context.Background(), mnt)
@@ -417,32 +254,18 @@ func (p *btrfsPool) Usage() (usage Usage, err error) {
 		return Usage{}, err
 	}
 
-	fsi, err := p.utils.List(context.Background(), p.name, true)
+	info, err := p.device.Info()
 	if err != nil {
 		return Usage{}, err
 	}
 
-	if len(fsi) == 0 {
-		return Usage{}, fmt.Errorf("could not find total size of pool %v", p.name)
-	}
-
-	var totalSize uint64
-	for _, dev := range fsi[0].Devices {
-		log.Debug().Int64("size", dev.Size).Str("device", dev.Path).Msg("pool usage")
-		totalSize += uint64(dev.Size)
-	}
-
-	return Usage{Size: totalSize / raidSizeDivisor[du.Data.Profile], Used: uint64(fsi[0].Used)}, nil
+	return Usage{Size: info.Size, Used: du.Data.Used}, nil
 }
 
 // Type of the physical storage used for this pool
 func (p *btrfsPool) Type() pkg.DeviceType {
 	// We only create heterogenous pools for now
-	return p.devices[0].DiskType
-}
-
-func (p *btrfsPool) Devices() []*Device {
-	return p.devices
+	return p.device.Type()
 }
 
 // Reserved is reserved size of the devices in bytes
@@ -501,40 +324,27 @@ func (p *btrfsPool) maintenance() error {
 }
 
 func (p *btrfsPool) Shutdown() error {
-	for _, device := range p.Devices() {
-		log.Info().Msgf("Shutting down disk %s ...", device.Path)
-		cmd := exec.Command("hdparm", "-y", device.Path)
-
-		err := cmd.Run()
-		if err != nil {
-			log.Error().Err(err).Msgf("Error shutting down device %s", device.Path)
-			return err
-		}
-
-		device.ShutdownCount.Add(1)
-		log.Info().Msgf("Disk %s is shutdown", device.Path)
+	cmd := exec.Command("hdparm", "-y", p.device.Path())
+	if err := cmd.Run(); err != nil {
+		return errors.Wrapf(err, "failed to shutdown device '%s'", p.device.Path())
 	}
+
 	return nil
 }
 
 type btrfsVolume struct {
 	id    int
 	path  string
-	utils *BtrfsUtil
+	utils BtrfsUtil
 }
 
-func newBtrfsVolume(ID int, path string, utils *BtrfsUtil) Volume {
-	dir := filepath.Base(path)
+func newBtrfsVolume(ID int, path string, utils BtrfsUtil) Volume {
 	vol := btrfsVolume{
 		id:    ID,
 		path:  path,
 		utils: utils,
 	}
 
-	if strings.HasPrefix(dir, "zdb") {
-		return &zdbBtrfsVolume{vol}
-	}
-
 	return &vol
 }
 
@@ -597,43 +407,3 @@ func (v *btrfsVolume) Limit(size uint64) error {
 
 	return v.utils.QGroupLimit(ctx, size, v.Path())
 }
-
-type zdbBtrfsVolume struct {
-	btrfsVolume
-}
-
-func (v *zdbBtrfsVolume) Usage() (usage Usage, err error) {
-	ctx := context.Background()
-	info, err := v.utils.SubvolumeInfo(ctx, v.Path())
-	if err != nil {
-		return usage, err
-	}
-
-	groups, err := v.utils.QGroupList(ctx, v.Path())
-	if err != nil {
-		return usage, err
-	}
-
-	group, ok := groups[fmt.Sprintf("0/%d", info.ID)]
-	if !ok {
-		// no qgroup associated with the subvolume id! means no limit, but we also
-		// cannot read the usage.
-		return
-	}
-
-	zdb := zdbpool.New(v.Path())
-	size, err := zdb.Reserved()
-	if err != nil {
-		return usage, errors.Wrapf(err, "failed to calculate namespaces size")
-	}
-	// otherwise, we return the size as maxrefer and usage as the rfer of the
-	// associated group
-	// todo: size should be the size of the pool, if maxrfer is 0
-	return Usage{Used: group.Rfer, Size: size}, nil
-}
-
-// IsZDBVolume checks if this is a zdb subvolume
-func IsZDBVolume(v Volume) bool {
-	_, ok := v.(*zdbBtrfsVolume)
-	return ok
-}
diff --git a/pkg/storage/filesystem/btrfs_utils.go b/pkg/storage/filesystem/btrfs_utils.go
index f0c9758b..af1fb400 100644
--- a/pkg/storage/filesystem/btrfs_utils.go
+++ b/pkg/storage/filesystem/btrfs_utils.go
@@ -6,8 +6,6 @@ import (
 	"regexp"
 	"strconv"
 	"strings"
-
-	"github.com/threefoldtech/zos/pkg"
 )
 
 var (
@@ -53,9 +51,8 @@ type BtrfsQGroup struct {
 
 // DiskUsage is parsed information from a btrfs fi df line
 type DiskUsage struct {
-	Profile pkg.RaidProfile `json:"profile"`
-	Total   uint64          `json:"total"`
-	Used    uint64          `json:"used"`
+	Total uint64 `json:"total"`
+	Used  uint64 `json:"used"`
 }
 
 // BtrfsDiskUsage is parsed information form btrfs fi df
@@ -369,7 +366,6 @@ func parseFilesystemDF(output string) (usage BtrfsDiskUsage, err error) {
 		default:
 			continue
 		}
-		datainfo.Profile = pkg.RaidProfile(strings.ToLower(line[2]))
 		datainfo.Total, err = strconv.ParseUint(line[3], 10, 64)
 		if err != nil {
 			return
diff --git a/pkg/storage/filesystem/device.go b/pkg/storage/filesystem/device.go
index 785d2a95..945042e9 100644
--- a/pkg/storage/filesystem/device.go
+++ b/pkg/storage/filesystem/device.go
@@ -7,34 +7,40 @@ import (
 	"strings"
 	"time"
 
-	"expvar"
-
 	"github.com/pkg/errors"
 	log "github.com/rs/zerolog/log"
 	"github.com/threefoldtech/zos/pkg/gridtypes/zos"
 )
 
+// Device interface
+type Device interface {
+	// Path returns path to the device like /dev/sda
+	Path() string
+	// Name returns name of the device like sda
+	Name() string
+	// Size device size
+	Size() uint64
+	// Type returns detected device type (hdd, ssd)
+	Type() zos.DeviceType
+	// Info is current device information, this should not be cached because
+	// it might change over time
+	Info() (DeviceInfo, error)
+	// ReadTime detected read time of the device
+	ReadTime() uint64
+}
+
 // DeviceManager is able to list all/specific devices on a system
 type DeviceManager interface {
 	// Device returns the device at the specified path
-	Device(ctx context.Context, device string) (*Device, error)
+	Device(ctx context.Context, device string) (Device, error)
 	// Devices finds all devices on a system
-	Devices(ctx context.Context) (DeviceCache, error)
+	Devices(ctx context.Context) (Devices, error)
 	// ByLabel finds all devices with the specified label
-	ByLabel(ctx context.Context, label string) ([]*Device, error)
-	// Raw returns the devices as represented in the kernel
-	// without using the internal cache, nor flattened
-	Raw(ctx context.Context) (DeviceCache, error)
-	// Reset returns a "clean" instace of the device manager
-	// The implementation must take care to clean any caching
-	// or other in-memory states and starting fresh. It's called
-	// by some routines to make sure a listing of devices will
-	// always return the real state of the system.
-	Reset() DeviceManager
+	ByLabel(ctx context.Context, label string) (Devices, error)
 }
 
-// DeviceCache represents a list of cached in memory devices
-type DeviceCache []Device
+// Devices represents a list of cached in memory devices
+type Devices []Device
 
 // FSType type of filesystem on device
 type FSType string
@@ -44,27 +50,93 @@ const (
 	BtrfsFSType FSType = "btrfs"
 )
 
-// Device represents a physical device
-type Device struct {
-	Type       string         `json:"type"`
-	Path       string         `json:"name"`
-	Label      string         `json:"label"`
-	Filesystem FSType         `json:"fstype"`
-	Children   []Device       `json:"children"`
+// DeviceInfo contains information about the device
+type DeviceInfo struct {
+	Path       string `json:"path"`
+	Label      string `json:"label"`
+	Size       uint64 `json:"size"`
+	Mountpoint string `json:"mountpoint"`
+	Filesystem FSType `json:"fstype"`
+}
+
+// func (i *DeviceInfo) ID() (id string, err error) {
+// 	// check if it's a pool using the label format.
+// 	// if it has a filesystem then it should have the label in correct format
+// 	if _, err = fmt.Sscanf(i.Label, PoolLabelPrefix+"%s", &id); err != nil {
+// 		return id, ErrInvalidLabel
+// 	}
+
+// 	return
+// }
+
+// func (i *DeviceInfo) IsPool() bool {
+// 	id, err := i.ID()
+// 	if err != nil {
+// 		return false
+// 	}
+
+// 	return len(id) != 0
+// }
+
+// Used assumes that the device is used if it has custom label or fstype or children
+func (i *DeviceInfo) Used() bool {
+	return len(i.Label) != 0 || len(i.Filesystem) != 0
+}
+
+type deviceImpl struct {
+	minDevice
+	mgr *lsblkDeviceManager
+}
+
+func (d *deviceImpl) Info() (DeviceInfo, error) {
+	var devices struct {
+		BlockDevices []DeviceInfo `json:"blockdevices"`
+	}
+
+	if err := d.mgr.lsblk(context.Background(), &devices, d.IPath); err != nil {
+		return DeviceInfo{}, err
+	}
+	if len(devices.BlockDevices) != 1 {
+		return DeviceInfo{}, fmt.Errorf("device not found")
+	}
+
+	return devices.BlockDevices[0], nil
+}
+
+func (d *deviceImpl) Type() zos.DeviceType {
+	return d.DiskType
+}
+
+func (d *deviceImpl) ReadTime() uint64 {
+	return d.RTime
+}
+
+type minDevice struct {
+	IPath      string         `json:"path"`
+	IName      string         `json:"name"`
+	ISize      uint64         `json:"size"`
 	DiskType   zos.DeviceType `json:"-"`
-	ReadTime   uint64         `json:"-"`
+	RTime      uint64         `json:"-"`
 	Subsystems string         `json:"subsystems"`
-	//HasPartions is different from children, because once the
-	//devices are flattend in the device, cache, the children list is
-	//zeroed (since all devices are flat), then has partions is set to
-	//make sure the device is not altered.
-	HasPartions   bool `json:"-"`
-	ShutdownCount *expvar.Int
 }
 
-// Used assumes that the device is used if it has custom label or fstype or children
-func (d *Device) Used() bool {
-	return len(d.Label) != 0 || len(d.Filesystem) != 0 || len(d.Children) > 0 || d.HasPartions
+func (m minDevice) toDevice(mgr *lsblkDeviceManager) Device {
+	return &deviceImpl{
+		minDevice: m,
+		mgr:       mgr,
+	}
+}
+
+func (m *minDevice) Path() string {
+	return m.IPath
+}
+
+func (m *minDevice) Size() uint64 {
+	return m.ISize
+}
+
+func (m *minDevice) Name() string {
+	return m.IName
 }
 
 // lsblkDeviceManager uses the lsblk utility to scann the disk for devices, and
@@ -74,7 +146,7 @@ func (d *Device) Used() bool {
 // method is called.
 type lsblkDeviceManager struct {
 	executer
-	cache DeviceCache
+	cache []minDevice
 }
 
 // DefaultDeviceManager returns a default device manager implementation
@@ -90,31 +162,36 @@ func defaultDeviceManager(ctx context.Context, exec executer) DeviceManager {
 	return m
 }
 
-func (l *lsblkDeviceManager) Reset() DeviceManager {
-	return &lsblkDeviceManager{executer: l.executer}
-}
-
 // Devices gets available block devices
-func (l *lsblkDeviceManager) Devices(ctx context.Context) (DeviceCache, error) {
+func (l *lsblkDeviceManager) Devices(ctx context.Context) (Devices, error) {
 	devices, err := l.scan(ctx)
 	if err != nil {
 		return nil, err
 	}
 
-	return devices, nil
+	result := make(Devices, 0, len(devices))
+	for _, dev := range devices {
+		result = append(result, dev.toDevice(l))
+	}
+
+	return result, nil
 }
 
-func (l *lsblkDeviceManager) ByLabel(ctx context.Context, label string) ([]*Device, error) {
-	devices, err := l.scan(ctx)
+func (l *lsblkDeviceManager) ByLabel(ctx context.Context, label string) (Devices, error) {
+	devices, err := l.Devices(ctx)
 	if err != nil {
 		return nil, err
 	}
 
-	var filtered []*Device
+	var filtered Devices
 
-	for idx := range devices {
-		device := &devices[idx]
-		if device.Label == label {
+	for _, device := range devices {
+		info, err := device.Info()
+		if err != nil {
+			return nil, err
+		}
+
+		if info.Label == label {
 			filtered = append(filtered, device)
 		}
 	}
@@ -122,15 +199,15 @@ func (l *lsblkDeviceManager) ByLabel(ctx context.Context, label string) ([]*Devi
 	return filtered, nil
 }
 
-func (l *lsblkDeviceManager) Device(ctx context.Context, path string) (device *Device, err error) {
+func (l *lsblkDeviceManager) Device(ctx context.Context, path string) (device Device, err error) {
 	devices, err := l.scan(ctx)
 	if err != nil {
 		return nil, err
 	}
 
-	for idx := range devices {
-		if devices[idx].Path == path {
-			return &devices[idx], nil
+	for _, dev := range devices {
+		if dev.IPath == path {
+			return dev.toDevice(l), nil
 		}
 	}
 
@@ -138,24 +215,48 @@ func (l *lsblkDeviceManager) Device(ctx context.Context, path string) (device *D
 
 }
 
-func (l *lsblkDeviceManager) Raw(ctx context.Context) (DeviceCache, error) {
-	bytes, err := l.run(ctx, "lsblk", "--json", "--output-all", "--bytes", "--exclude", "1,2,11", "--path")
-	if err != nil {
-		return nil, err
+func (l *lsblkDeviceManager) lsblk(ctx context.Context, output interface{}, device ...string) error {
+	args := []string{
+		"--json",
+		"--output-all",
+		"--bytes",
+		"--exclude",
+		"1,2,11",
+		"--path",
 	}
 
-	var devices struct {
-		BlockDevices []Device `json:"blockdevices"`
+	if len(device) == 1 {
+		args = append(args, device[0])
+	} else if len(device) > 1 {
+		return fmt.Errorf("only one device is supported")
+	}
+
+	bytes, err := l.run(ctx, "lsblk", args...)
+	if err != nil {
+		return err
 	}
 
 	// skipping unmarshal when lsblk response is empty
 	if len(bytes) == 0 {
 		log.Warn().Msg("no disks found on the system")
-		return DeviceCache(devices.BlockDevices), nil
+		return nil
 	}
 
 	// parsing lsblk response
-	if err := json.Unmarshal(bytes, &devices); err != nil {
+	if err := json.Unmarshal(bytes, output); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (l *lsblkDeviceManager) raw(ctx context.Context) ([]minDevice, error) {
+
+	var devices struct {
+		BlockDevices []minDevice `json:"blockdevices"`
+	}
+
+	if err := l.lsblk(ctx, &devices); err != nil {
 		return nil, err
 	}
 
@@ -166,16 +267,16 @@ func (l *lsblkDeviceManager) Raw(ctx context.Context) (DeviceCache, error) {
 		}
 	}
 
-	return DeviceCache(filtered), nil
+	return filtered, nil
 }
 
 // scan the system for disks using the `lsblk` command
-func (l *lsblkDeviceManager) scan(ctx context.Context) (DeviceCache, error) {
+func (l *lsblkDeviceManager) scan(ctx context.Context) ([]minDevice, error) {
 	if l.cache != nil {
 		return l.cache, nil
 	}
 
-	devs, err := l.Raw(ctx)
+	devs, err := l.raw(ctx)
 	if err != nil {
 		errors.Wrap(err, "failed to scan devices")
 	}
@@ -184,7 +285,7 @@ func (l *lsblkDeviceManager) scan(ctx context.Context) (DeviceCache, error) {
 		return nil, err
 	}
 
-	l.cache = l.flattenDevices(devs)
+	l.cache = devs
 	return l.cache, nil
 }
 
@@ -208,28 +309,13 @@ func (l *lsblkDeviceManager) seektime(ctx context.Context, path string) (string,
 	return seekTime.Typ, seekTime.Time, err
 }
 
-func (l *lsblkDeviceManager) flattenDevices(devices DeviceCache) DeviceCache {
-	var list DeviceCache
-	for _, device := range devices {
-		children := device.Children
-		device.Children = nil
-		if len(children) > 0 {
-			device.HasPartions = true
-		}
-		list = append(list, device)
-		list = append(list, l.flattenDevices(children)...)
-	}
-
-	return list
-}
-
-func (l *lsblkDeviceManager) setDeviceTypes(devices []Device) error {
+func (l *lsblkDeviceManager) setDeviceTypes(devices []minDevice) error {
 	for idx := range devices {
 		d := &devices[idx]
 		ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
 		defer cancel()
 
-		typ, rt, err := l.seektime(ctx, d.Path)
+		typ, rt, err := l.seektime(ctx, d.IPath)
 		if err != nil {
 			// don't include errored devices in the result
 			log.Error().Msgf("Failed to get disk read time: %v", err)
@@ -244,14 +330,10 @@ func (l *lsblkDeviceManager) setDeviceTypes(devices []Device) error {
 
 // setDeviceType recursively sets a device type and read time on a device and
 // all of its children
-func (l *lsblkDeviceManager) setDeviceType(device *Device, typ zos.DeviceType, readTime uint64) {
+func (l *lsblkDeviceManager) setDeviceType(device *minDevice, typ zos.DeviceType, readTime uint64) {
 	device.DiskType = typ
-	device.ReadTime = readTime
+	device.RTime = readTime
 
-	for idx := range device.Children {
-		dev := &device.Children[idx]
-		l.setDeviceType(dev, typ, readTime)
-	}
 }
 
 func (l *lsblkDeviceManager) deviceTypeFromString(typ string) zos.DeviceType {
@@ -267,8 +349,8 @@ func (l *lsblkDeviceManager) deviceTypeFromString(typ string) zos.DeviceType {
 }
 
 // ByReadTime implements sort.Interface for []Device based on the ReadTime field
-type ByReadTime DeviceCache
+type ByReadTime Devices
 
 func (a ByReadTime) Len() int           { return len(a) }
 func (a ByReadTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a ByReadTime) Less(i, j int) bool { return a[i].ReadTime < a[j].ReadTime }
+func (a ByReadTime) Less(i, j int) bool { return a[i].ReadTime() < a[j].ReadTime() }
diff --git a/pkg/storage/filesystem/filesystem.go b/pkg/storage/filesystem/filesystem.go
index 2cc71b2d..289199d5 100644
--- a/pkg/storage/filesystem/filesystem.go
+++ b/pkg/storage/filesystem/filesystem.go
@@ -34,18 +34,13 @@ type Pool interface {
 	Volume
 	// Mounted returns whether the pool is mounted or not. If it is mounted,
 	// the mountpoint is returned
-	Mounted() (string, bool)
+	Mounted() (string, error)
 	// Mount the pool, the mountpoint is returned
 	Mount() (string, error)
-	// MountWithoutScan the pool, the mountpoint is returned.
-	// Does not scan for btrfs
-	MountWithoutScan() (string, error)
 	// UnMount the pool
 	UnMount() error
 	//AddDevice to the pool
-	AddDevice(device *Device) error
 	// RemoveDevice from the pool
-	RemoveDevice(device *Device) error
 	// Type of the physical storage in this pool
 	Type() pkg.DeviceType
 	// Reserved is reserved size of the devices in bytes
@@ -56,11 +51,10 @@ type Pool interface {
 	AddVolume(name string) (Volume, error)
 	// RemoveVolume removes a subvolume with the given name
 	RemoveVolume(name string) error
-	// Devices list attached devices
-	Devices() []*Device
-
 	// Shutdown spins down the device where the pool is mounted
 	Shutdown() error
+	// Device return device associated with pool
+	Device() Device
 }
 
 // Filter closure for Filesystem list
@@ -71,22 +65,6 @@ func All(Pool) bool {
 	return true
 }
 
-// Filesystem defines a filesystem interface
-type Filesystem interface {
-	// Create a new filesystem.
-	//
-	// name: name of the filesystem
-	// devices: list of devices to use in the filesystem
-	// profile: Raid profile of the filesystem
-	Create(ctx context.Context, name string, profile pkg.RaidProfile, devices ...*Device) (Pool, error)
-
-	// CreateForce creates a new filesystem with force
-	// It will delete existing data and partition tables
-	CreateForce(ctx context.Context, name string, profile pkg.RaidProfile, devices ...*Device) (Pool, error)
-	// List all existing filesystems on the node
-	List(ctx context.Context, filter Filter) ([]Pool, error)
-}
-
 // Partprobe runs partprobe
 func Partprobe(ctx context.Context) error {
 	if _, err := run(ctx, "partprobe"); err != nil {
diff --git a/pkg/storage/filesystem/migrate.go b/pkg/storage/filesystem/migrate.go
deleted file mode 100644
index 5409c972..00000000
--- a/pkg/storage/filesystem/migrate.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package filesystem
-
-import (
-	"context"
-	"strings"
-	"syscall"
-
-	"github.com/pkg/errors"
-	"github.com/rs/zerolog/log"
-)
-
-const (
-	zosV1PoolPrefix = "sp_"
-)
-
-func wipe(ctx context.Context, device *Device, exe executer) error {
-	_, err := exe.run(ctx, "wipefs", "-a", "-f", device.Path)
-	return err
-}
-
-// Migrate is a simple migration routine that makes sure we have
-// fresh disks to use from zos v1, in case you moving
-// away from older version
-// After migration you should alway use the returned Device manager, or create
-// a new one.
-func Migrate(ctx context.Context, m DeviceManager) (DeviceManager, error) {
-	return migrate(ctx, m, executerFunc(run))
-}
-
-func migrate(ctx context.Context, m DeviceManager, exe executer) (DeviceManager, error) {
-	devices, err := m.Raw(ctx)
-	if err != nil {
-		return nil, errors.Wrap(err, "migration failed, couldn't list disks")
-	}
-
-	var destroyMe []Device
-loop:
-	for _, device := range devices {
-		if strings.HasPrefix(device.Label, zosV1PoolPrefix) {
-			destroyMe = append(destroyMe, device)
-			continue
-		}
-
-		for _, child := range device.Children {
-			if strings.HasPrefix(child.Label, zosV1PoolPrefix) {
-				destroyMe = append(destroyMe, device)
-				continue loop
-			}
-		}
-	}
-
-	for _, device := range destroyMe {
-		log.Warn().Str("device", device.Path).Msg("wiping device")
-		if err := wipe(ctx, &device, exe); err != nil {
-			log.Error().Err(err).Str("device", device.Path).Msg("failed to wipe disk")
-		}
-	}
-
-	if len(destroyMe) > 0 {
-		syscall.Sync()
-		if out, err := exe.run(ctx, "partprobe"); err != nil {
-			log.Error().Err(err).Str("out", string(out)).Msg("failed to partprobe")
-		}
-	}
-
-	return m.Reset(), nil
-}
diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go
index 06c61522..a8e8d88c 100644
--- a/pkg/storage/storage.go
+++ b/pkg/storage/storage.go
@@ -2,7 +2,6 @@ package storage
 
 import (
 	"context"
-	"expvar"
 	"fmt"
 	"os"
 	"os/exec"
@@ -12,7 +11,6 @@ import (
 	"syscall"
 	"time"
 
-	"github.com/google/uuid"
 	"github.com/pkg/errors"
 	log "github.com/rs/zerolog/log"
 	"github.com/shirou/gopsutil/disk"
@@ -34,21 +32,21 @@ const (
 )
 
 var (
-	diskBase = map[pkg.RaidProfile]int{
-		pkg.Single: 1,
-		pkg.Raid1:  2,
-		pkg.Raid10: 4,
-	}
+	_ pkg.StorageModule = (*Module)(nil)
 )
 
 // Module implements functionality for pkg.StorageModule
 type Module struct {
-	pools         []filesystem.Pool
+	devices filesystem.DeviceManager
+
+	ssds []filesystem.Pool
+	hdds []filesystem.Pool
+
 	brokenPools   []pkg.BrokenPool
-	devices       filesystem.DeviceManager
 	brokenDevices []pkg.BrokenDevice
-	totalSSD      uint64
-	totalHDD      uint64
+
+	totalSSD uint64
+	totalHDD uint64
 
 	mu sync.RWMutex
 }
@@ -60,24 +58,14 @@ func New() (*Module, error) {
 
 	m := filesystem.DefaultDeviceManager(ctx)
 
-	m, err := filesystem.Migrate(context.Background(), m)
-	if err != nil {
-		return nil, err
-	}
-
 	s := &Module{
-		pools:         []filesystem.Pool{},
-		brokenPools:   []pkg.BrokenPool{},
+		ssds:          []filesystem.Pool{},
 		devices:       m,
 		brokenDevices: []pkg.BrokenDevice{},
 	}
 
 	// go for a simple linear setup right now
-	err = s.initialize(pkg.StoragePolicy{
-		Raid:     pkg.Single,
-		Disks:    1,
-		MaxPools: 0,
-	})
+	err := s.initialize()
 
 	if err == nil {
 		log.Info().Msgf("Finished initializing storage module")
@@ -116,17 +104,15 @@ func (s *Module) BrokenDevices() []pkg.BrokenDevice {
 }
 
 func (s *Module) dump() {
-	log.Debug().Int("volumes", len(s.pools)).Msg("dumping volumes")
+	log.Debug().Int("volumes", len(s.ssds)).Msg("dumping volumes")
 
-	for _, pool := range s.pools {
-		path, mounted := pool.Mounted()
-		if mounted {
+	for _, pool := range s.ssds {
+		path, err := pool.Mounted()
+		if err == nil {
 			log.Debug().Msgf("pool %s is mounted at: %s", pool.Name(), path)
 		}
-		devices := pool.Devices()
-		for _, device := range devices {
-			log.Debug().Str("path", device.Path).Str("label", device.Label).Str("type", string(device.DiskType)).Send()
-		}
+		device := pool.Device()
+		log.Debug().Str("path", device.Path()).Str("label", pool.Name()).Str("type", string(device.Type())).Send()
 	}
 
 }
@@ -138,156 +124,99 @@ What Initialize will do is the following:
  - Scan free devices, apply the policy.
  - If new pools were created, the pool is going to be mounted automatically
 **/
-func (s *Module) initialize(policy pkg.StoragePolicy) error {
+func (s *Module) initialize() error {
 	// lock for the entire initialization method, so other code which relies
 	// on this observes this as an atomic operation
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	log.Info().Msgf("Initializing storage module")
 
+	vm := true
+	hyperVisor, err := capacity.NewResourceOracle(nil).GetHypervisor()
+	if err == nil {
+		// Disable disk shutdown when running in a VM
+		vm = len(hyperVisor) > 0
+	}
 	// Make sure we finish in 1 minute
 	ctx, cancel := context.WithTimeout(context.Background(), time.Minute*1)
 	defer cancel()
 
-	fs := filesystem.NewBtrfs(s.devices)
-
-	// remount all existing pools
-	log.Info().Msgf("Remounting existing volumes")
-	log.Debug().Msgf("Searching for existing volumes")
-	existingPools, err := fs.List(ctx, filesystem.All)
+	devices, err := s.devices.Devices(ctx)
 	if err != nil {
 		return err
 	}
 
-	for _, pool := range existingPools {
-		_, err := pool.Mount()
+	for _, device := range devices {
+		pool, err := filesystem.NewBtrfsPool(device)
 		if err != nil {
-			s.brokenPools = append(s.brokenPools, pkg.BrokenPool{Label: pool.Name(), Err: err})
-			log.Warn().Msgf("Failed to mount pool %v", pool.Name())
+			log.Error().Err(err).Str("device", device.Path()).Msg("failed to create pool on device")
+			s.brokenDevices = append(s.brokenDevices, pkg.BrokenDevice{Path: device.Path(), Err: err})
 			continue
 		}
-		s.pools = append(s.pools, pool)
-	}
-
-	// list disks
-	log.Info().Msgf("Finding free disks")
-	disks, err := s.devices.Devices(ctx)
-	if err != nil {
-		return err
-	}
-
-	freeDisks := filesystem.DeviceCache{}
 
-	for idx := range disks {
-		if !disks[idx].Used() {
-			log.Debug().Msgf("Found free device %s", disks[idx].Path)
-			freeDisks = append(freeDisks, disks[idx])
+		_, err = pool.Mount()
+		if err != nil {
+			s.brokenPools = append(s.brokenPools, pkg.BrokenPool{Label: pool.Name(), Err: err})
+			continue
 		}
-	}
-
-	// sort by read time so faster disks are first
-	sort.Sort(filesystem.ByReadTime(freeDisks))
-
-	// dumping current s.volumes list
-	s.dump()
-
-	log.Info().Msgf("Creating new volumes using policy: %s", policy.Raid)
-
-	// sanity check for disk amount
-	diskBase, exists := diskBase[policy.Raid]
-	if !exists {
-		return fmt.Errorf("unrecognized storage policy %s", policy.Raid)
-	}
-	if int(policy.Disks)%diskBase != 0 {
-		return fmt.Errorf("invalid amount of disks (%d) for volume for configuration %v", policy.Disks, policy.Raid)
-	}
-
-	// create new pools if applicable
-	// for now create as much pools as we can, need to think more about this
-	newPools := []filesystem.Pool{}
-
-	// also make sure pools are homogenous, only 1 type of device per pool
-	ssds := filesystem.DeviceCache{}
-	hdds := filesystem.DeviceCache{}
-
-	for idx := range freeDisks {
-		if freeDisks[idx].DiskType == zos.SSDDevice {
-			ssds = append(ssds, freeDisks[idx])
-		} else {
-			hdds = append(hdds, freeDisks[idx])
+		usage, err := pool.Usage()
+		if err != nil {
+			log.Error().Err(err).Str("pool", pool.Name()).Str("device", device.Path()).Msg("failed to get usage of pool")
 		}
-	}
 
-	createdPools := 0
-	fdisks := []filesystem.DeviceCache{ssds, hdds}
-	for idx := range fdisks {
-		possiblePools := len(fdisks[idx]) / int(policy.Disks)
-		// only create up to the specified amount of pools
-		if policy.MaxPools != 0 && int(policy.MaxPools) < possiblePools-createdPools {
-			possiblePools = int(policy.MaxPools)
+		typ := device.Type()
+		if vm {
+			// force ssd device for vms
+			typ = zos.SSDDevice
 		}
-		log.Debug().Msgf("Creating %d new volumes", possiblePools)
-
-		for i := 0; i < possiblePools; i++ {
-			log.Debug().Msgf("Creating new volume: %d", i)
-			poolDevices := []*filesystem.Device{}
-
-			for j := 0; j < int(policy.Disks); j++ {
-				log.Debug().Msgf("Grabbing device %d: %s for new volume", i*int(policy.Disks)+j, fdisks[idx][i*int(policy.Disks)+j].Path)
-				poolDevices = append(poolDevices, &fdisks[idx][i*int(policy.Disks)+j])
-			}
-
-			pool, err := fs.CreateForce(ctx, uuid.New().String(), policy.Raid, poolDevices...)
-			if err != nil {
-				log.Info().Err(err).Msg("create filesystem")
-
-				// Failure to create a filesystem -> disk is dead. It is possible
-				// that multiple devices are used to create a single pool, and only
-				// one devices is actuall broken. We should probably expand on
-				// this once we start to use storagepools spanning multiple disks.
-				for _, dev := range poolDevices {
-					s.brokenDevices = append(s.brokenDevices, pkg.BrokenDevice{Path: dev.Path, Err: err})
-				}
-				continue
-			}
 
-			newPools = append(newPools, pool)
-			createdPools++
+		// TODO: this is a debugging code and should not be committed
+		if device.Path() == "/dev/vdd" || device.Path() == "/dev/vde" {
+			typ = zos.HDDDevice
 		}
-	}
-
-	// make sure new pools are added to the list
-	for _, pool := range newPools {
-		_, err := pool.Mount()
-		if err != nil {
-			return err
+		switch typ {
+		case zos.SSDDevice:
+			s.totalSSD += usage.Size
+			s.ssds = append(s.ssds, pool)
+		case zos.HDDDevice:
+			s.totalHDD += usage.Size
+			s.hdds = append(s.hdds, pool)
 		}
-		s.pools = append(s.pools, pool)
 	}
 
-	// add expvar variables
-	s.totalSSD = 0
-	s.totalHDD = 0
-	for _, pool := range s.pools {
-		for _, device := range pool.Devices() {
-			device.ShutdownCount = expvar.NewInt(device.Path)
-		}
-
-		// calculate size for pool
-		usage, err := pool.Usage()
+	// clean up hdd disks to make sure only zdb subvolumes exists
+	// this code makes sure HDDs only have volumes for zdb. Or none
+	for _, hdd := range s.hdds {
+		// we know that all pools are mounted already so it's okay
+		// to access them direction
+		volumes, err := hdd.Volumes()
 		if err != nil {
-			log.Error().Msgf("Failed to get current volume usage: %v", err)
+			log.Error().Err(err).Str("pool", hdd.Name()).Msg("failed to list pool volumes")
 			continue
 		}
 
-		switch pool.Type() {
-		case zos.HDDDevice:
-			s.totalHDD += usage.Size
-		case zos.SSDDevice:
-			s.totalSSD += usage.Size
+		for _, vol := range volumes {
+			if vol.Name() != zdbVolume {
+				if err := hdd.RemoveVolume(vol.Name()); err != nil {
+					log.Error().Err(err).
+						Str("volume", vol.Name()).
+						Str("pool", hdd.Name()).
+						Msg("failed to delete non zdb volume from harddisk")
+				}
+			}
 		}
 	}
 
+	log.Info().
+		Int("ssd-pools", len(s.ssds)).
+		Int("hdd-pools", len(s.hdds)).
+		Int("broken-pools", len(s.brokenPools)).
+		Int("borken-devices", len(s.brokenDevices)).
+		Msg("pool creations completed")
+
+	s.dump()
+
+	// just in case
 	if err := filesystem.Partprobe(ctx); err != nil {
 		return err
 	}
@@ -297,102 +226,98 @@ func (s *Module) initialize(policy pkg.StoragePolicy) error {
 		return err
 	}
 
-	hyperVisor, err := capacity.NewResourceOracle(nil).GetHypervisor()
-	if err == nil {
-		// Disable disk shutdown when running in a VM
-		if len(hyperVisor) > 0 {
-			return nil
-		}
-	}
-
-	if err := s.shutdownUnusedPools(); err != nil {
+	if err := s.shutdownUnusedPools(vm); err != nil {
 		log.Error().Err(err).Msg("Error shutting down unused pools")
 	}
 
-	s.periodicallyCheckDiskShutdown()
+	s.periodicallyCheckDiskShutdown(vm)
 
 	return nil
 }
 
-func (s *Module) shutdownUnusedPools() error {
-	for _, pool := range s.pools {
-		if _, mounted := pool.Mounted(); mounted {
-			volumes, err := pool.Volumes()
-			if err != nil {
-				log.Error().Err(err).Msgf("Failed to retrieve subvolumes on pool %s", pool.Name())
-				return err
-			}
-			log.Debug().Msgf("Pool %s has: %d subvolumes", pool.Name(), len(volumes))
+func (s *Module) shutdownUnusedPools(vm bool) error {
+	log.Debug().Msg("shutting down unused disks")
+	for _, sets := range [][]filesystem.Pool{s.ssds, s.hdds} {
+		for _, pool := range sets {
+			if _, err := pool.Mounted(); err == nil {
+				volumes, err := pool.Volumes()
+				if err != nil {
+					log.Error().Err(err).Msgf("Failed to retrieve subvolumes on pool %s", pool.Name())
+					return err
+				}
+				log.Debug().Msgf("Pool %s has: %d subvolumes", pool.Name(), len(volumes))
 
-			if len(volumes) > 0 {
-				continue
+				if len(volumes) > 0 {
+					continue
+				}
+				err = pool.UnMount()
+				if err != nil {
+					log.Error().Err(err).Msgf("Failed to unmount volume %s", pool.Name())
+					return err
+				}
 			}
-			err = pool.UnMount()
-			if err != nil {
-				log.Error().Err(err).Msgf("Failed to unmount volume %s", pool.Name())
-				return err
+
+			if !vm {
+				// only shutdown on physical machine
+				if err := pool.Shutdown(); err != nil {
+					log.Error().Err(err).Msgf("Error shutting down pool %s", pool.Name())
+				}
 			}
 		}
-		if err := pool.Shutdown(); err != nil {
-			log.Error().Err(err).Msgf("Error shutting down pool %s", pool.Name())
-		}
 	}
+
 	return nil
 }
 
-// UpdateFilesystem updates filesystem size
-func (s *Module) UpdateFilesystem(name string, size gridtypes.Unit) (pkg.Filesystem, error) {
-	_, volume, fs, err := s.path(name)
+// VolumeUpdate updates filesystem size
+func (s *Module) VolumeUpdate(name string, size gridtypes.Unit) error {
+	_, volume, _, err := s.path(name)
 	if err != nil {
-		return pkg.Filesystem{}, err
+		return err
 	}
 
 	if err := volume.Limit(uint64(size)); err != nil {
-		return fs, err
+		return err
 	}
 
-	fs.Usage.Size = size
-	return fs, nil
+	return nil
 }
 
-// CreateFilesystem with the given size in a storage pool.
-func (s *Module) CreateFilesystem(name string, size gridtypes.Unit, poolType pkg.DeviceType) (pkg.Filesystem, error) {
+// VolumeCreate with the given size in a storage pool.
+func (s *Module) VolumeCreate(name string, size gridtypes.Unit) (pkg.Volume, error) {
 	log.Info().Msgf("Creating new volume with size %d", size)
 	if strings.HasPrefix(name, "zdb") {
-		return pkg.Filesystem{}, fmt.Errorf("invalid volume name. zdb prefix is reserved")
+		return pkg.Volume{}, fmt.Errorf("invalid volume name. zdb prefix is reserved")
 	}
 
-	fs, err := s.createSubvolWithQuota(size, name, poolType)
+	fs, err := s.createSubvolWithQuota(size, name)
 	if err != nil {
-		return pkg.Filesystem{}, err
+		return pkg.Volume{}, err
 	}
 
 	usage, err := fs.Usage()
 	if err != nil {
-		return pkg.Filesystem{}, err
+		return pkg.Volume{}, err
 	}
 
-	return pkg.Filesystem{
-		ID:     fs.ID(),
-		FsType: fs.FsType(),
-		Name:   fs.Name(),
-		Path:   fs.Path(),
+	return pkg.Volume{
+		Name: fs.Name(),
+		Path: fs.Path(),
 		Usage: pkg.Usage{
 			Size: gridtypes.Unit(usage.Size),
 			Used: gridtypes.Unit(usage.Used),
 		},
-		DiskType: poolType,
 	}, nil
 }
 
-// ReleaseFilesystem with the given name, this will unmount and then delete
+// VolumeDelete with the given name, this will unmount and then delete
 // the filesystem. After this call, the caller must not perform any more actions
 // on this filesystem
-func (s *Module) ReleaseFilesystem(name string) error {
+func (s *Module) VolumeDelete(name string) error {
 	log.Info().Msgf("Deleting volume %v", name)
 
-	for _, pool := range s.pools {
-		if _, mounted := pool.Mounted(); !mounted {
+	for _, pool := range s.ssds {
+		if _, err := pool.Mounted(); err != nil {
 			continue
 		}
 
@@ -432,12 +357,12 @@ func (s *Module) ReleaseFilesystem(name string) error {
 	return nil
 }
 
-// ListFilesystems return all the filesystem managed by storeaged present on the nodes
-func (s *Module) ListFilesystems() ([]pkg.Filesystem, error) {
-	fss := make([]pkg.Filesystem, 0, 10)
+// VolumeList return all the filesystem managed by storeaged present on the nodes
+func (s *Module) VolumeList() ([]pkg.Volume, error) {
+	fss := make([]pkg.Volume, 0, 10)
 
-	for _, pool := range s.pools {
-		if _, mounted := pool.Mounted(); !mounted {
+	for _, pool := range s.ssds {
+		if _, err := pool.Mounted(); err != nil {
 			continue
 		}
 
@@ -458,16 +383,13 @@ func (s *Module) ListFilesystems() ([]pkg.Filesystem, error) {
 				return nil, err
 			}
 
-			fss = append(fss, pkg.Filesystem{
-				ID:     v.ID(),
-				FsType: v.FsType(),
-				Name:   v.Name(),
-				Path:   v.Path(),
+			fss = append(fss, pkg.Volume{
+				Name: v.Name(),
+				Path: v.Path(),
 				Usage: pkg.Usage{
 					Size: gridtypes.Unit(usage.Size),
 					Used: gridtypes.Unit(usage.Used),
 				},
-				DiskType: pool.Type(),
 			})
 		}
 	}
@@ -475,111 +397,49 @@ func (s *Module) ListFilesystems() ([]pkg.Filesystem, error) {
 	return fss, nil
 }
 
-// Path return the path of the mountpoint of the named filesystem
+// VolumeLookup return the path of the mountpoint of the named filesystem
 // if no volume with name exists, an empty path and an error is returned
-func (s *Module) Path(name string) (pkg.Filesystem, error) {
+func (s *Module) VolumeLookup(name string) (pkg.Volume, error) {
 	_, _, fs, err := s.path(name)
 	return fs, err
 }
 
 // Path return the path of the mountpoint of the named filesystem
 // if no volume with name exists, an empty path and an error is returned
-func (s *Module) path(name string) (filesystem.Pool, filesystem.Volume, pkg.Filesystem, error) {
-	for _, pool := range s.pools {
-		if _, mounted := pool.Mounted(); !mounted {
+func (s *Module) path(name string) (filesystem.Pool, filesystem.Volume, pkg.Volume, error) {
+	for _, pool := range s.ssds {
+		if _, err := pool.Mounted(); err != nil {
 			continue
 		}
 		filesystems, err := pool.Volumes()
 		if err != nil {
-			return nil, nil, pkg.Filesystem{}, err
+			return nil, nil, pkg.Volume{}, err
 		}
 		for _, fs := range filesystems {
 			if fs.Name() == name {
 				usage, err := fs.Usage()
 				if err != nil {
-					return nil, nil, pkg.Filesystem{}, err
+					return nil, nil, pkg.Volume{}, err
 				}
 
-				return pool, fs, pkg.Filesystem{
-					ID:     fs.ID(),
-					FsType: fs.FsType(),
-					Name:   fs.Name(),
-					Path:   fs.Path(),
+				return pool, fs, pkg.Volume{
+					Name: fs.Name(),
+					Path: fs.Path(),
 					Usage: pkg.Usage{
 						Size: gridtypes.Unit(usage.Size),
 						Used: gridtypes.Unit(usage.Used),
 					},
-					DiskType: pool.Type(),
 				}, nil
 			}
 		}
 	}
 
-	return nil, nil, pkg.Filesystem{}, errors.Wrapf(os.ErrNotExist, "subvolume '%s' not found", name)
-}
-
-// VDiskFindCandidate find a suitbale location for creating a vdisk of the given size
-func (s *Module) VDiskFindCandidate(size gridtypes.Unit) (path string, err error) {
-	candidates, err := s.findCandidates(size, zos.SSDDevice)
-	if err != nil {
-		return path, err
-	}
-	// does anyone have a vdisk subvol
-	for _, candidate := range candidates {
-		volumes, err := candidate.Pool.Volumes()
-		if err != nil {
-			log.Error().Str("pool", candidate.Pool.Path()).Err(err).Msg("failed to list pool volumes")
-			continue
-		}
-		for _, volume := range volumes {
-			if volume.Name() != vdiskVolumeName {
-				continue
-			}
-
-			return volume.Path(), nil
-		}
-	}
-	// none has a vdiks subvolume, we need to
-	// create one.
-	candidate := candidates[0]
-	volume, err := candidate.Pool.AddVolume(vdiskVolumeName)
-	if err != nil {
-		return path, errors.Wrap(err, "failed to create vdisk pool")
-	}
-
-	return volume.Path(), nil
+	return nil, nil, pkg.Volume{}, errors.Wrapf(os.ErrNotExist, "subvolume '%s' not found", name)
 }
 
-// VDiskPools return a list of all vdisk pools
-func (s *Module) VDiskPools() ([]string, error) {
-	var paths []string
-	for _, pool := range s.pools {
-		if pool.Type() != zos.SSDDevice {
-			continue
-		}
-
-		if _, mounted := pool.Mounted(); !mounted {
-			continue
-		}
-
-		volumes, err := pool.Volumes()
-		if err != nil {
-			return nil, errors.Wrapf(err, "failed to list pool '%s' volumes", pool.Path())
-		}
-
-		for _, volume := range volumes {
-			if volume.Name() == vdiskVolumeName {
-				paths = append(paths, volume.Path())
-			}
-		}
-	}
-
-	return paths, nil
-}
-
-// GetCacheFS return the special filesystem used by 0-OS to store internal state and flist cache
-func (s *Module) GetCacheFS() (pkg.Filesystem, error) {
-	return s.Path(cacheLabel)
+// Cache return the special filesystem used by 0-OS to store internal state and flist cache
+func (s *Module) Cache() (pkg.Volume, error) {
+	return s.VolumeLookup(cacheLabel)
 }
 
 // ensureCache creates a "cache" subvolume and mounts it in /var
@@ -591,10 +451,11 @@ func (s *Module) ensureCache() error {
 	var cacheFs filesystem.Volume
 
 	// check if cache volume available
-	for _, pool := range s.pools {
-		if _, mounted := pool.Mounted(); !mounted {
+	for _, pool := range s.ssds {
+		if _, err := pool.Mounted(); err != nil {
 			continue
 		}
+
 		filesystems, err := pool.Volumes()
 		if err != nil {
 			return err
@@ -615,7 +476,7 @@ func (s *Module) ensureCache() error {
 		log.Debug().Msgf("No cache found, try to create new cache")
 
 		log.Debug().Msgf("Trying to create new cache on SSD")
-		fs, err := s.createSubvolWithQuota(cacheSize, cacheLabel, zos.SSDDevice)
+		fs, err := s.createSubvolWithQuota(cacheSize, cacheLabel)
 
 		if err != nil {
 			log.Warn().Err(err).Msg("failed to create new cache on SSD")
@@ -624,17 +485,6 @@ func (s *Module) ensureCache() error {
 		}
 	}
 
-	if cacheFs == nil {
-		log.Debug().Msgf("Trying to create new cache on HDD")
-		fs, err := s.createSubvolWithQuota(cacheSize, cacheLabel, zos.HDDDevice)
-
-		if err != nil {
-			log.Warn().Err(err).Msg("failed to create new cache on HDD")
-		} else {
-			cacheFs = fs
-		}
-	}
-
 	if cacheFs == nil {
 		log.Warn().Msg("failed to create persisted cache disk. Running on limited cache")
 
@@ -664,8 +514,8 @@ func (s *Module) ensureCache() error {
 // createSubvolWithQuota creates a subvolume with the given name and limits it to the given size
 // if the requested disk type does not have a storage pool with enough free size available, an error is returned
 // this methods does set a quota limit equal to size on the created volume
-func (s *Module) createSubvolWithQuota(size gridtypes.Unit, name string, poolType pkg.DeviceType) (filesystem.Volume, error) {
-	volume, err := s.createSubvol(size, name, poolType)
+func (s *Module) createSubvolWithQuota(size gridtypes.Unit, name string) (filesystem.Volume, error) {
+	volume, err := s.createSubvol(size, name)
 	if err != nil {
 		return nil, err
 	}
@@ -681,15 +531,11 @@ func (s *Module) createSubvolWithQuota(size gridtypes.Unit, name string, poolTyp
 // createSubvol creates a subvolume with the given name
 // if the requested disk type does not have a storage pool with enough free size available, an error is returned
 // this method does not set any quota on the subvolume, for this uses createSubvolWithQuota
-func (s *Module) createSubvol(size gridtypes.Unit, name string, poolType pkg.DeviceType) (filesystem.Volume, error) {
+func (s *Module) createSubvol(size gridtypes.Unit, name string) (filesystem.Volume, error) {
 	var err error
 
-	if poolType != zos.HDDDevice && poolType != zos.SSDDevice {
-		return nil, pkg.ErrInvalidDeviceType{DeviceType: poolType}
-	}
-
 	// Look for candidates in mounted pools first
-	candidates, err := s.findCandidates(size, poolType)
+	candidates, err := s.findCandidates(size)
 	if err != nil {
 		log.Error().Err(err).Msgf("failed to search candidates on mounted pools")
 		return nil, err
@@ -714,10 +560,10 @@ type candidate struct {
 	Available uint64
 }
 
-func (s *Module) findCandidates(size gridtypes.Unit, poolType pkg.DeviceType) ([]candidate, error) {
+func (s *Module) findCandidates(size gridtypes.Unit) ([]candidate, error) {
 
 	// Look for candidates in mounted pools first
-	candidates, err := s.checkForCandidates(size, poolType, true)
+	candidates, err := s.checkForCandidates(size, true)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to search candidate on mounted pools")
 	}
@@ -727,7 +573,7 @@ func (s *Module) findCandidates(size gridtypes.Unit, poolType pkg.DeviceType) ([
 	// If no candidates or found in mounted pools, we check the unmounted pools and get the first one that fits
 	if len(candidates) == 0 {
 		log.Debug().Msg("Checking unmounted pools")
-		candidates, err = s.checkForCandidates(size, poolType, false)
+		candidates, err = s.checkForCandidates(size, false)
 		if err != nil {
 			return nil, errors.Wrap(err, "failed to search candidates on unmounted pools")
 		}
@@ -736,30 +582,27 @@ func (s *Module) findCandidates(size gridtypes.Unit, poolType pkg.DeviceType) ([
 	}
 
 	if len(candidates) == 0 {
-		return nil, pkg.ErrNotEnoughSpace{DeviceType: poolType}
+		return nil, pkg.ErrNotEnoughSpace{DeviceType: zos.SSDDevice}
 	}
 
 	return candidates, nil
 }
 
-func (s *Module) checkForCandidates(size gridtypes.Unit, poolType pkg.DeviceType, mounted bool) ([]candidate, error) {
+func (s *Module) checkForCandidates(size gridtypes.Unit, mounted bool) ([]candidate, error) {
 	var candidates []candidate
-	for _, pool := range s.pools {
-		_, poolIsMounted := pool.Mounted()
+	for _, pool := range s.ssds {
+		_, err := pool.Mounted()
+		poolIsMounted := err == nil
 		if mounted != poolIsMounted {
 			continue
 		}
 
-		// ignore pools which don't have the right device type
-		if pool.Type() != poolType {
-			continue
-		}
 		log.Debug().Msgf("checking pool %s for space", pool.Name())
 
 		if !poolIsMounted && !mounted {
 			log.Debug().Msgf("Mounting pool %s...", pool.Name())
 			// if the pool is not mounted, and we are looking for not mounted pools, mount it first
-			_, err := pool.MountWithoutScan()
+			_, err := pool.Mount()
 			if err != nil {
 				log.Error().Err(err).Msgf("failed to mount pool %s", pool.Name())
 				return nil, err
@@ -792,20 +635,19 @@ func (s *Module) checkForCandidates(size gridtypes.Unit, poolType pkg.DeviceType
 				err = pool.UnMount()
 				if err != nil {
 					log.Error().Err(err).Msgf("failed to unmount pool %s", pool.Name())
-					return nil, err
 				}
 				err = pool.Shutdown()
 				if err != nil {
 					log.Error().Err(err).Msgf("failed to shutdown pool %s", pool.Name())
-					return nil, err
 				}
 			}
+
 			continue
 		}
 
 		candidates = append(candidates, candidate{
 			Pool:      pool,
-			Available: usage.Size,
+			Available: usage.Size - (reserved + uint64(size)),
 		})
 
 		// if we are looking for not mounted pools, break here
@@ -836,8 +678,8 @@ func (s *Module) Monitor(ctx context.Context) <-chan pkg.PoolsStats {
 			case <-time.After(5 * time.Second):
 			}
 
-			for _, pool := range s.pools {
-				if _, mounted := pool.Mounted(); !mounted {
+			for _, pool := range s.ssds {
+				if _, err := pool.Mounted(); err != nil {
 					continue
 				}
 				devices, err := s.devices.ByLabel(ctx, pool.Name())
@@ -848,7 +690,7 @@ func (s *Module) Monitor(ctx context.Context) <-chan pkg.PoolsStats {
 
 				var deviceNames []string
 				for _, device := range devices {
-					deviceNames = append(deviceNames, device.Path)
+					deviceNames = append(deviceNames, device.Path())
 				}
 
 				usage, err := disk.UsageWithContext(ctx, pool.Path())
@@ -882,39 +724,44 @@ func (s *Module) Monitor(ctx context.Context) <-chan pkg.PoolsStats {
 	return ch
 }
 
-func (s *Module) periodicallyCheckDiskShutdown() {
+func (s *Module) periodicallyCheckDiskShutdown(vm bool) {
 	ticker := time.NewTicker(5 * time.Minute)
 
 	go func() {
 		for {
 			<-ticker.C
 			log.Info().Msg("Checking pools for disks that should be shutdown...")
-			s.shutdownDisks()
+			s.shutdownDisks(vm)
 		}
 	}()
 }
 
 // shutdownDisks will check the disks power status.
 // If a disk is on and it is not mounted then it is not supposed to be on, turn it off
-func (s *Module) shutdownDisks() {
-	for _, pool := range s.pools {
-		for _, device := range pool.Devices() {
-			log.Debug().Msgf("checking device: %s", device.Path)
-			on, err := checkDiskPowerStatus(device.Path)
+func (s *Module) shutdownDisks(vm bool) {
+	for _, set := range [][]filesystem.Pool{s.ssds, s.hdds} {
+		for _, pool := range set {
+			device := pool.Device()
+			log.Debug().Msgf("checking device: %s", device.Path())
+			on, err := checkDiskPowerStatus(device.Path())
 			if err != nil {
 				log.Err(err).Msgf("error occurred while checking disk power status")
 				continue
 			}
 
-			_, mounted := pool.Mounted()
-			if mounted || !on {
+			_, err = pool.Mounted()
+			if err == nil || !on {
+				continue
+			}
+
+			if vm {
 				continue
 			}
 
-			log.Debug().Msgf("shutting down device %s because it is not mounted and the device is on", device.Path)
+			log.Debug().Msgf("shutting down device %s because it is not mounted and the device is on", device.Path())
 			err = pool.Shutdown()
 			if err != nil {
-				log.Err(err).Msgf("failed to shutdown device %s", device.Path)
+				log.Err(err).Msgf("failed to shutdown device %s", device.Path())
 				continue
 			}
 		}
diff --git a/pkg/storage/zdb.go b/pkg/storage/zdb.go
deleted file mode 100644
index b6f889bd..00000000
--- a/pkg/storage/zdb.go
+++ /dev/null
@@ -1,247 +0,0 @@
-package storage
-
-import (
-	"fmt"
-	"sort"
-
-	"github.com/rs/zerolog/log"
-
-	"github.com/google/uuid"
-
-	"github.com/pkg/errors"
-
-	"github.com/threefoldtech/zos/pkg"
-	"github.com/threefoldtech/zos/pkg/gridtypes"
-	"github.com/threefoldtech/zos/pkg/gridtypes/zos"
-	"github.com/threefoldtech/zos/pkg/storage/filesystem"
-	"github.com/threefoldtech/zos/pkg/storage/zdbpool"
-)
-
-// Find finds a zdb namespace allocation
-func (s *Module) Find(nsID string) (allocation pkg.Allocation, err error) {
-	for _, pool := range s.pools {
-		if _, mounted := pool.Mounted(); !mounted {
-			continue
-		}
-
-		volumes, err := pool.Volumes()
-		if err != nil {
-			return allocation, errors.Wrapf(err, "failed to list volume on pool %s", pool.Name())
-		}
-
-		for _, volume := range volumes {
-			// skip all non-zdb volume
-			if !filesystem.IsZDBVolume(volume) {
-				continue
-			}
-
-			zdb := zdbpool.New(volume.Path())
-
-			if !zdb.Exists(nsID) {
-				continue
-			}
-
-			// we found the namespace
-			allocation = pkg.Allocation{
-				VolumeID:   volume.Name(),
-				VolumePath: volume.Path(),
-			}
-
-			return allocation, nil
-		}
-	}
-
-	return pkg.Allocation{}, fmt.Errorf("not found")
-}
-
-// Allocate is responsible to make sure the subvolume used by a 0-db as enough storage capacity
-// of specified size, type and mode
-// it returns the volume ID and its path or an error if it couldn't allocate enough storage
-func (s *Module) Allocate(nsID string, diskType pkg.DeviceType, size gridtypes.Unit, mode pkg.ZDBMode) (allocation pkg.Allocation, err error) {
-	log := log.With().
-		Str("type", string(diskType)).
-		Uint64("size", uint64(size)).
-		Str("mode", string(mode)).
-		Logger()
-
-	if diskType != zos.HDDDevice && diskType != zos.SSDDevice {
-		return allocation, pkg.ErrInvalidDeviceType{DeviceType: diskType}
-	}
-
-	log.Info().Msg("try to allocation space for 0-DB")
-
-	// Initially check if the namespace already exists
-	// if so, return the allocation
-	for _, pool := range s.pools {
-		if _, mounted := pool.Mounted(); !mounted {
-			continue
-		}
-
-		// skip pool with wrong disk type
-		if pool.Type() != diskType {
-			continue
-		}
-
-		volumes, err := pool.Volumes()
-		if err != nil {
-			return allocation, errors.Wrapf(err, "failed to list volume on pool %s", pool.Name())
-		}
-
-		for _, volume := range volumes {
-
-			// skip all non-zdb volume
-			if !filesystem.IsZDBVolume(volume) {
-				continue
-			}
-
-			zdb := zdbpool.New(volume.Path())
-
-			if !zdb.Exists(nsID) {
-				continue
-			}
-
-			// we found the namespace
-			allocation = pkg.Allocation{
-				VolumeID:   volume.Name(),
-				VolumePath: volume.Path(),
-			}
-
-			return allocation, nil
-		}
-	}
-
-	targetMode := zdbpool.IndexModeKeyValue
-	if mode == zos.ZDBModeSeq {
-		targetMode = zdbpool.IndexModeSequential
-	}
-
-	// check for candidates in mounted pools first
-	candidates, err := s.checkForZDBCandidateVolumes(size, diskType, targetMode)
-	if err != nil {
-		log.Error().Err(err).Msgf("failed to search volumes on mounted pools")
-		return allocation, err
-	}
-
-	log.Debug().Msgf("Found %d candidate volumes in mounted pools", len(candidates))
-
-	var volume filesystem.Volume
-	if len(candidates) > 0 {
-		// reverse sort by free space
-		sort.Slice(candidates, func(i, j int) bool {
-			return candidates[i].Free > candidates[j].Free
-		})
-
-		volume = candidates[0]
-	} else {
-		// no candidates, so we have to try to create a new subvolume.
-		// and start a new zdb instance
-		name, err := genZDBPoolName()
-		if err != nil {
-			return allocation, errors.Wrap(err, "failed to generate new sub-volume name")
-		}
-
-		// we create the zdb volume without configuring a quota
-		// the used size will the computed from the 0-db namespaces themselves
-		volume, err = s.createSubvol(size, name, diskType)
-		if err != nil {
-			return allocation, errors.Wrap(err, "failed to create sub-volume")
-		}
-	}
-
-	zdb := zdbpool.New(volume.Path())
-
-	if err := zdb.Create(nsID, "", size); err != nil {
-		return allocation, errors.Wrapf(err, "failed to create namespace directory: '%s/%s'", volume.Path(), nsID)
-	}
-
-	return pkg.Allocation{
-		VolumeID:   volume.Name(),
-		VolumePath: volume.Path(),
-	}, nil
-
-}
-
-type zdbcandidate struct {
-	filesystem.Volume
-	Free uint64
-}
-
-func (s *Module) checkForZDBCandidateVolumes(size gridtypes.Unit, poolType pkg.DeviceType, targetMode zdbpool.IndexMode) ([]zdbcandidate, error) {
-	var candidates []zdbcandidate
-	for _, pool := range s.pools {
-		// ignore pools that are not mounted for now
-		if _, mounted := pool.Mounted(); !mounted {
-			continue
-		}
-
-		// ignore pools which don't have the right device type
-		if pool.Type() != poolType {
-			continue
-		}
-		log.Debug().Msgf("checking pool %s for space", pool.Name())
-
-		usage, err := pool.Usage()
-		if err != nil {
-			log.Error().Err(err).Msgf("failed to read usage of pool %s", pool.Name())
-			return nil, err
-		}
-
-		volumes, err := pool.Volumes()
-		if err != nil {
-			log.Error().Err(err).Msgf("failed to list volume on pool %s", pool.Name())
-			return nil, err
-		}
-
-		for _, volume := range volumes {
-			// skip all non-zdb volume
-			if !filesystem.IsZDBVolume(volume) {
-				continue
-			}
-
-			volumeUsage, err := volume.Usage()
-			if err != nil {
-				log.Error().Err(err).Msgf("failed to list namespaces from volume '%s'", volume.Path())
-				return nil, err
-			}
-
-			if volumeUsage.Size+uint64(size) > usage.Size {
-				// not enough space on this volume
-				continue
-			}
-
-			zdb := zdbpool.New(volume.Path())
-
-			// check if the mode is the same
-			indexMode, err := zdb.IndexMode("default")
-			if err != nil {
-				log.Err(err).Str("namespace", "default").Msg("failed to read index mode")
-				continue
-			}
-
-			if indexMode != targetMode {
-				log.Info().Msg("skip because wrong mode")
-				continue
-			}
-
-			candidates = append(
-				candidates,
-				zdbcandidate{
-					Volume: volume,
-					Free:   usage.Size - (volumeUsage.Size + uint64(size)),
-				})
-		}
-	}
-	return candidates, nil
-}
-
-// ZDBPoolPrefix is the prefix used for the subvolume create for 0-DB containers
-const ZDBPoolPrefix = "zdb"
-
-func genZDBPoolName() (string, error) {
-	id, err := uuid.NewRandom()
-	if err != nil {
-		return "", err
-	}
-	name := ZDBPoolPrefix + id.String()
-	return name, nil
-}
diff --git a/pkg/stubs/storage_stub.go b/pkg/stubs/storage_stub.go
index 708781aa..417f7635 100644
--- a/pkg/stubs/storage_stub.go
+++ b/pkg/stubs/storage_stub.go
@@ -25,25 +25,21 @@ func NewStorageModuleStub(client zbus.Client) *StorageModuleStub {
 	}
 }
 
-func (s *StorageModuleStub) Allocate(ctx context.Context, arg0 string, arg1 zos.DeviceType, arg2 gridtypes.Unit, arg3 zos.ZDBMode) (ret0 pkg.Allocation, ret1 error) {
-	args := []interface{}{arg0, arg1, arg2, arg3}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "Allocate", args...)
+func (s *StorageModuleStub) BrokenDevices(ctx context.Context) (ret0 []pkg.BrokenDevice) {
+	args := []interface{}{}
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "BrokenDevices", args...)
 	if err != nil {
 		panic(err)
 	}
 	if err := result.Unmarshal(0, &ret0); err != nil {
 		panic(err)
 	}
-	ret1 = new(zbus.RemoteError)
-	if err := result.Unmarshal(1, &ret1); err != nil {
-		panic(err)
-	}
 	return
 }
 
-func (s *StorageModuleStub) BrokenDevices(ctx context.Context) (ret0 []pkg.BrokenDevice) {
+func (s *StorageModuleStub) BrokenPools(ctx context.Context) (ret0 []pkg.BrokenPool) {
 	args := []interface{}{}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "BrokenDevices", args...)
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "BrokenPools", args...)
 	if err != nil {
 		panic(err)
 	}
@@ -53,21 +49,25 @@ func (s *StorageModuleStub) BrokenDevices(ctx context.Context) (ret0 []pkg.Broke
 	return
 }
 
-func (s *StorageModuleStub) BrokenPools(ctx context.Context) (ret0 []pkg.BrokenPool) {
+func (s *StorageModuleStub) Cache(ctx context.Context) (ret0 pkg.Volume, ret1 error) {
 	args := []interface{}{}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "BrokenPools", args...)
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "Cache", args...)
 	if err != nil {
 		panic(err)
 	}
 	if err := result.Unmarshal(0, &ret0); err != nil {
 		panic(err)
 	}
+	ret1 = new(zbus.RemoteError)
+	if err := result.Unmarshal(1, &ret1); err != nil {
+		panic(err)
+	}
 	return
 }
 
-func (s *StorageModuleStub) CreateFilesystem(ctx context.Context, arg0 string, arg1 gridtypes.Unit, arg2 zos.DeviceType) (ret0 pkg.Filesystem, ret1 error) {
-	args := []interface{}{arg0, arg1, arg2}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "CreateFilesystem", args...)
+func (s *StorageModuleStub) DeviceAllocate(ctx context.Context, arg0 gridtypes.Unit) (ret0 pkg.Device, ret1 error) {
+	args := []interface{}{arg0}
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "DeviceAllocate", args...)
 	if err != nil {
 		panic(err)
 	}
@@ -81,9 +81,9 @@ func (s *StorageModuleStub) CreateFilesystem(ctx context.Context, arg0 string, a
 	return
 }
 
-func (s *StorageModuleStub) Find(ctx context.Context, arg0 string) (ret0 pkg.Allocation, ret1 error) {
+func (s *StorageModuleStub) DeviceLookup(ctx context.Context, arg0 string) (ret0 pkg.Device, ret1 error) {
 	args := []interface{}{arg0}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "Find", args...)
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "DeviceLookup", args...)
 	if err != nil {
 		panic(err)
 	}
@@ -97,9 +97,9 @@ func (s *StorageModuleStub) Find(ctx context.Context, arg0 string) (ret0 pkg.All
 	return
 }
 
-func (s *StorageModuleStub) GetCacheFS(ctx context.Context) (ret0 pkg.Filesystem, ret1 error) {
+func (s *StorageModuleStub) Devices(ctx context.Context) (ret0 []pkg.Device, ret1 error) {
 	args := []interface{}{}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "GetCacheFS", args...)
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "Devices", args...)
 	if err != nil {
 		panic(err)
 	}
@@ -113,9 +113,63 @@ func (s *StorageModuleStub) GetCacheFS(ctx context.Context) (ret0 pkg.Filesystem
 	return
 }
 
-func (s *StorageModuleStub) ListFilesystems(ctx context.Context) (ret0 []pkg.Filesystem, ret1 error) {
+func (s *StorageModuleStub) DiskCreate(ctx context.Context, arg0 string, arg1 gridtypes.Unit) (ret0 pkg.VDisk, ret1 error) {
+	args := []interface{}{arg0, arg1}
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "DiskCreate", args...)
+	if err != nil {
+		panic(err)
+	}
+	if err := result.Unmarshal(0, &ret0); err != nil {
+		panic(err)
+	}
+	ret1 = new(zbus.RemoteError)
+	if err := result.Unmarshal(1, &ret1); err != nil {
+		panic(err)
+	}
+	return
+}
+
+func (s *StorageModuleStub) DiskDelete(ctx context.Context, arg0 string) (ret0 error) {
+	args := []interface{}{arg0}
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "DiskDelete", args...)
+	if err != nil {
+		panic(err)
+	}
+	ret0 = new(zbus.RemoteError)
+	if err := result.Unmarshal(0, &ret0); err != nil {
+		panic(err)
+	}
+	return
+}
+
+func (s *StorageModuleStub) DiskExists(ctx context.Context, arg0 string) (ret0 bool) {
+	args := []interface{}{arg0}
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "DiskExists", args...)
+	if err != nil {
+		panic(err)
+	}
+	if err := result.Unmarshal(0, &ret0); err != nil {
+		panic(err)
+	}
+	return
+}
+
+func (s *StorageModuleStub) DiskFormat(ctx context.Context, arg0 string) (ret0 error) {
+	args := []interface{}{arg0}
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "DiskFormat", args...)
+	if err != nil {
+		panic(err)
+	}
+	ret0 = new(zbus.RemoteError)
+	if err := result.Unmarshal(0, &ret0); err != nil {
+		panic(err)
+	}
+	return
+}
+
+func (s *StorageModuleStub) DiskList(ctx context.Context) (ret0 []pkg.VDisk, ret1 error) {
 	args := []interface{}{}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "ListFilesystems", args...)
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "DiskList", args...)
 	if err != nil {
 		panic(err)
 	}
@@ -129,6 +183,35 @@ func (s *StorageModuleStub) ListFilesystems(ctx context.Context) (ret0 []pkg.Fil
 	return
 }
 
+func (s *StorageModuleStub) DiskLookup(ctx context.Context, arg0 string) (ret0 pkg.VDisk, ret1 error) {
+	args := []interface{}{arg0}
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "DiskLookup", args...)
+	if err != nil {
+		panic(err)
+	}
+	if err := result.Unmarshal(0, &ret0); err != nil {
+		panic(err)
+	}
+	ret1 = new(zbus.RemoteError)
+	if err := result.Unmarshal(1, &ret1); err != nil {
+		panic(err)
+	}
+	return
+}
+
+func (s *StorageModuleStub) DiskWrite(ctx context.Context, arg0 string, arg1 string) (ret0 error) {
+	args := []interface{}{arg0, arg1}
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "DiskWrite", args...)
+	if err != nil {
+		panic(err)
+	}
+	ret0 = new(zbus.RemoteError)
+	if err := result.Unmarshal(0, &ret0); err != nil {
+		panic(err)
+	}
+	return
+}
+
 func (s *StorageModuleStub) Monitor(ctx context.Context) (<-chan pkg.PoolsStats, error) {
 	ch := make(chan pkg.PoolsStats)
 	recv, err := s.client.Stream(ctx, s.module, s.object, "Monitor")
@@ -148,9 +231,9 @@ func (s *StorageModuleStub) Monitor(ctx context.Context) (<-chan pkg.PoolsStats,
 	return ch, nil
 }
 
-func (s *StorageModuleStub) Path(ctx context.Context, arg0 string) (ret0 pkg.Filesystem, ret1 error) {
+func (s *StorageModuleStub) Total(ctx context.Context, arg0 zos.DeviceType) (ret0 uint64, ret1 error) {
 	args := []interface{}{arg0}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "Path", args...)
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "Total", args...)
 	if err != nil {
 		panic(err)
 	}
@@ -164,9 +247,25 @@ func (s *StorageModuleStub) Path(ctx context.Context, arg0 string) (ret0 pkg.Fil
 	return
 }
 
-func (s *StorageModuleStub) ReleaseFilesystem(ctx context.Context, arg0 string) (ret0 error) {
+func (s *StorageModuleStub) VolumeCreate(ctx context.Context, arg0 string, arg1 gridtypes.Unit) (ret0 pkg.Volume, ret1 error) {
+	args := []interface{}{arg0, arg1}
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "VolumeCreate", args...)
+	if err != nil {
+		panic(err)
+	}
+	if err := result.Unmarshal(0, &ret0); err != nil {
+		panic(err)
+	}
+	ret1 = new(zbus.RemoteError)
+	if err := result.Unmarshal(1, &ret1); err != nil {
+		panic(err)
+	}
+	return
+}
+
+func (s *StorageModuleStub) VolumeDelete(ctx context.Context, arg0 string) (ret0 error) {
 	args := []interface{}{arg0}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "ReleaseFilesystem", args...)
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "VolumeDelete", args...)
 	if err != nil {
 		panic(err)
 	}
@@ -177,9 +276,9 @@ func (s *StorageModuleStub) ReleaseFilesystem(ctx context.Context, arg0 string)
 	return
 }
 
-func (s *StorageModuleStub) Total(ctx context.Context, arg0 zos.DeviceType) (ret0 uint64, ret1 error) {
-	args := []interface{}{arg0}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "Total", args...)
+func (s *StorageModuleStub) VolumeList(ctx context.Context) (ret0 []pkg.Volume, ret1 error) {
+	args := []interface{}{}
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "VolumeList", args...)
 	if err != nil {
 		panic(err)
 	}
@@ -193,9 +292,9 @@ func (s *StorageModuleStub) Total(ctx context.Context, arg0 zos.DeviceType) (ret
 	return
 }
 
-func (s *StorageModuleStub) UpdateFilesystem(ctx context.Context, arg0 string, arg1 gridtypes.Unit) (ret0 pkg.Filesystem, ret1 error) {
-	args := []interface{}{arg0, arg1}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "UpdateFilesystem", args...)
+func (s *StorageModuleStub) VolumeLookup(ctx context.Context, arg0 string) (ret0 pkg.Volume, ret1 error) {
+	args := []interface{}{arg0}
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "VolumeLookup", args...)
 	if err != nil {
 		panic(err)
 	}
@@ -208,3 +307,16 @@ func (s *StorageModuleStub) UpdateFilesystem(ctx context.Context, arg0 string, a
 	}
 	return
 }
+
+func (s *StorageModuleStub) VolumeUpdate(ctx context.Context, arg0 string, arg1 gridtypes.Unit) (ret0 error) {
+	args := []interface{}{arg0, arg1}
+	result, err := s.client.RequestContext(ctx, s.module, s.object, "VolumeUpdate", args...)
+	if err != nil {
+		panic(err)
+	}
+	ret0 = new(zbus.RemoteError)
+	if err := result.Unmarshal(0, &ret0); err != nil {
+		panic(err)
+	}
+	return
+}
diff --git a/pkg/stubs/vdisk_stub.go b/pkg/stubs/vdisk_stub.go
deleted file mode 100644
index b0baff8e..00000000
--- a/pkg/stubs/vdisk_stub.go
+++ /dev/null
@@ -1,124 +0,0 @@
-package stubs
-
-import (
-	"context"
-	zbus "github.com/threefoldtech/zbus"
-	pkg "github.com/threefoldtech/zos/pkg"
-	gridtypes "github.com/threefoldtech/zos/pkg/gridtypes"
-)
-
-type VDiskModuleStub struct {
-	client zbus.Client
-	module string
-	object zbus.ObjectID
-}
-
-func NewVDiskModuleStub(client zbus.Client) *VDiskModuleStub {
-	return &VDiskModuleStub{
-		client: client,
-		module: "storage",
-		object: zbus.ObjectID{
-			Name:    "vdisk",
-			Version: "0.0.1",
-		},
-	}
-}
-
-func (s *VDiskModuleStub) Allocate(ctx context.Context, arg0 string, arg1 gridtypes.Unit) (ret0 string, ret1 error) {
-	args := []interface{}{arg0, arg1}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "Allocate", args...)
-	if err != nil {
-		panic(err)
-	}
-	if err := result.Unmarshal(0, &ret0); err != nil {
-		panic(err)
-	}
-	ret1 = new(zbus.RemoteError)
-	if err := result.Unmarshal(1, &ret1); err != nil {
-		panic(err)
-	}
-	return
-}
-
-func (s *VDiskModuleStub) Deallocate(ctx context.Context, arg0 string) (ret0 error) {
-	args := []interface{}{arg0}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "Deallocate", args...)
-	if err != nil {
-		panic(err)
-	}
-	ret0 = new(zbus.RemoteError)
-	if err := result.Unmarshal(0, &ret0); err != nil {
-		panic(err)
-	}
-	return
-}
-
-func (s *VDiskModuleStub) EnsureFilesystem(ctx context.Context, arg0 string) (ret0 error) {
-	args := []interface{}{arg0}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "EnsureFilesystem", args...)
-	if err != nil {
-		panic(err)
-	}
-	ret0 = new(zbus.RemoteError)
-	if err := result.Unmarshal(0, &ret0); err != nil {
-		panic(err)
-	}
-	return
-}
-
-func (s *VDiskModuleStub) Exists(ctx context.Context, arg0 string) (ret0 bool) {
-	args := []interface{}{arg0}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "Exists", args...)
-	if err != nil {
-		panic(err)
-	}
-	if err := result.Unmarshal(0, &ret0); err != nil {
-		panic(err)
-	}
-	return
-}
-
-func (s *VDiskModuleStub) Inspect(ctx context.Context, arg0 string) (ret0 pkg.VDisk, ret1 error) {
-	args := []interface{}{arg0}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "Inspect", args...)
-	if err != nil {
-		panic(err)
-	}
-	if err := result.Unmarshal(0, &ret0); err != nil {
-		panic(err)
-	}
-	ret1 = new(zbus.RemoteError)
-	if err := result.Unmarshal(1, &ret1); err != nil {
-		panic(err)
-	}
-	return
-}
-
-func (s *VDiskModuleStub) List(ctx context.Context) (ret0 []pkg.VDisk, ret1 error) {
-	args := []interface{}{}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "List", args...)
-	if err != nil {
-		panic(err)
-	}
-	if err := result.Unmarshal(0, &ret0); err != nil {
-		panic(err)
-	}
-	ret1 = new(zbus.RemoteError)
-	if err := result.Unmarshal(1, &ret1); err != nil {
-		panic(err)
-	}
-	return
-}
-
-func (s *VDiskModuleStub) WriteImage(ctx context.Context, arg0 string, arg1 string) (ret0 error) {
-	args := []interface{}{arg0, arg1}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "WriteImage", args...)
-	if err != nil {
-		panic(err)
-	}
-	ret0 = new(zbus.RemoteError)
-	if err := result.Unmarshal(0, &ret0); err != nil {
-		panic(err)
-	}
-	return
-}
diff --git a/pkg/stubs/zdb_stub.go b/pkg/stubs/zdb_stub.go
deleted file mode 100644
index 695bb5d0..00000000
--- a/pkg/stubs/zdb_stub.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package stubs
-
-import (
-	"context"
-	zbus "github.com/threefoldtech/zbus"
-	pkg "github.com/threefoldtech/zos/pkg"
-	gridtypes "github.com/threefoldtech/zos/pkg/gridtypes"
-	zos "github.com/threefoldtech/zos/pkg/gridtypes/zos"
-)
-
-type ZDBAllocaterStub struct {
-	client zbus.Client
-	module string
-	object zbus.ObjectID
-}
-
-func NewZDBAllocaterStub(client zbus.Client) *ZDBAllocaterStub {
-	return &ZDBAllocaterStub{
-		client: client,
-		module: "storage",
-		object: zbus.ObjectID{
-			Name:    "storage",
-			Version: "0.0.1",
-		},
-	}
-}
-
-func (s *ZDBAllocaterStub) Allocate(ctx context.Context, arg0 string, arg1 zos.DeviceType, arg2 gridtypes.Unit, arg3 zos.ZDBMode) (ret0 pkg.Allocation, ret1 error) {
-	args := []interface{}{arg0, arg1, arg2, arg3}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "Allocate", args...)
-	if err != nil {
-		panic(err)
-	}
-	if err := result.Unmarshal(0, &ret0); err != nil {
-		panic(err)
-	}
-	ret1 = new(zbus.RemoteError)
-	if err := result.Unmarshal(1, &ret1); err != nil {
-		panic(err)
-	}
-	return
-}
-
-func (s *ZDBAllocaterStub) Find(ctx context.Context, arg0 string) (ret0 pkg.Allocation, ret1 error) {
-	args := []interface{}{arg0}
-	result, err := s.client.RequestContext(ctx, s.module, s.object, "Find", args...)
-	if err != nil {
-		panic(err)
-	}
-	if err := result.Unmarshal(0, &ret0); err != nil {
-		panic(err)
-	}
-	ret1 = new(zbus.RemoteError)
-	if err := result.Unmarshal(1, &ret1); err != nil {
-		panic(err)
-	}
-	return
-}
diff --git a/pkg/substrate/account.go b/pkg/substrate/account.go
index 6f4f4ae8..684d69ac 100644
--- a/pkg/substrate/account.go
+++ b/pkg/substrate/account.go
@@ -94,7 +94,7 @@ curl --header "Content-Type: application/json" \
   https://api.substrate01.threefold.io/activate
 */
 
-func (s *Substrate) activateAccount(identity signature.KeyringPair) error {
+func (s *Substrate) activateAccount(identity *Identity) error {
 	const activationDefaultURL = "https://explorer.devnet.grid.tf/activation/activate"
 
 	var buf bytes.Buffer
@@ -119,11 +119,7 @@ func (s *Substrate) activateAccount(identity signature.KeyringPair) error {
 
 // EnsureAccount makes sure account is available on blockchain
 // if not, it uses activation service to create one
-func (s *Substrate) EnsureAccount(sk ed25519.PrivateKey) (info types.AccountInfo, err error) {
-	identity, err := Identity(sk)
-	if err != nil {
-		return
-	}
+func (s *Substrate) EnsureAccount(identity *Identity) (info types.AccountInfo, err error) {
 
 	info, err = s.getAccount(identity, s.meta)
 	if errors.Is(err, errAccountNotFound) {
@@ -151,15 +147,44 @@ func (s *Substrate) EnsureAccount(sk ed25519.PrivateKey) (info types.AccountInfo
 
 }
 
-// Identity derive the correct substrate identity from ed25519 key
-func Identity(sk ed25519.PrivateKey) (signature.KeyringPair, error) {
+// Identity is a user identity
+type Identity signature.KeyringPair
+
+// SecureKey returns the ed25519 key from identity
+func (i *Identity) SecureKey() (ed25519.PrivateKey, error) {
+	scheme := subkeyEd25519.Scheme{}
+	kyr, err := subkey.DeriveKeyPair(scheme, i.URI)
+	if err != nil {
+		return nil, err
+	}
+
+	return ed25519.NewKeyFromSeed(kyr.Seed()), nil
+}
+
+// IdentityFromSecureKey derive the correct substrate identity from ed25519 key
+func IdentityFromSecureKey(sk ed25519.PrivateKey) (Identity, error) {
 	str := types.HexEncodeToString(sk.Seed())
-	return keyringPairFromSecret(str, 42)
+	krp, err := keyringPairFromSecret(str, network)
+	if err != nil {
+		return Identity{}, err
+	}
+
+	return Identity(krp), nil
 	// because 42 is the answer to life the universe and everything
 	// no, seriously, don't change it, it has to be 42.
 }
 
-func (s *Substrate) getAccount(identity signature.KeyringPair, meta *types.Metadata) (info types.AccountInfo, err error) {
+//IdentityFromPhrase gets identity from hex seed or mnemonics
+func IdentityFromPhrase(seedOrPhrase string) (Identity, error) {
+	krp, err := keyringPairFromSecret(seedOrPhrase, network)
+	if err != nil {
+		return Identity{}, err
+	}
+
+	return Identity(krp), nil
+}
+
+func (s *Substrate) getAccount(identity *Identity, meta *types.Metadata) (info types.AccountInfo, err error) {
 	key, err := types.CreateStorageKey(meta, "System", "Account", identity.PublicKey, nil)
 	if err != nil {
 		err = errors.Wrap(err, "failed to create storage key")
@@ -179,11 +204,7 @@ func (s *Substrate) getAccount(identity signature.KeyringPair, meta *types.Metad
 }
 
 // GetAccount gets account info with secure key
-func (s *Substrate) GetAccount(sk ed25519.PrivateKey) (info types.AccountInfo, err error) {
-	identity, err := Identity(sk)
-	if err != nil {
-		return
-	}
+func (s *Substrate) GetAccount(identity *Identity) (info types.AccountInfo, err error) {
 	meta, err := s.cl.RPC.State.GetMetadataLatest()
 	if err != nil {
 		return info, err
diff --git a/pkg/substrate/contract.go b/pkg/substrate/contract.go
index 4fa8c6e1..f25641a1 100644
--- a/pkg/substrate/contract.go
+++ b/pkg/substrate/contract.go
@@ -1,7 +1,6 @@
 package substrate
 
 import (
-	"crypto/ed25519"
 	"fmt"
 
 	"github.com/centrifuge/go-substrate-rpc-client/v3/scale"
@@ -64,7 +63,7 @@ type Contract struct {
 }
 
 // CreateContract creates a contract for deployment
-func (s *Substrate) CreateContract(sk ed25519.PrivateKey, node uint32, body []byte, hash string, publicIPs uint32) (uint64, error) {
+func (s *Substrate) CreateContract(identity *Identity, node uint32, body []byte, hash string, publicIPs uint32) (uint64, error) {
 	c, err := types.NewCall(s.meta, "SmartContractModule.create_contract",
 		node, body, hash, publicIPs,
 	)
@@ -73,13 +72,45 @@ func (s *Substrate) CreateContract(sk ed25519.PrivateKey, node uint32, body []by
 		return 0, errors.Wrap(err, "failed to create call")
 	}
 
-	if _, err := s.call(sk, c); err != nil {
+	if _, err := s.call(identity, c); err != nil {
 		return 0, errors.Wrap(err, "failed to create node")
 	}
 
 	return s.GetContractWithHash(node, hash)
 }
 
+// UpdateContract updates existing contract
+func (s *Substrate) UpdateContract(identity *Identity, contract uint64, body []byte, hash string) (uint64, error) {
+	c, err := types.NewCall(s.meta, "SmartContractModule.update_contract",
+		contract, body, hash,
+	)
+
+	if err != nil {
+		return 0, errors.Wrap(err, "failed to create call")
+	}
+
+	if _, err := s.call(identity, c); err != nil {
+		return 0, errors.Wrap(err, "failed to create node")
+	}
+
+	return contract, nil
+}
+
+// CancelContract creates a contract for deployment
+func (s *Substrate) CancelContract(identity *Identity, contract uint64) error {
+	c, err := types.NewCall(s.meta, "SmartContractModule.cancel_contract", contract)
+
+	if err != nil {
+		return errors.Wrap(err, "failed to cancel call")
+	}
+
+	if _, err := s.call(identity, c); err != nil {
+		return errors.Wrap(err, "failed to cancel contract")
+	}
+
+	return nil
+}
+
 // GetContract we should not have calls to create contract, instead only get
 func (s *Substrate) GetContract(id uint64) (*Contract, error) {
 	bytes, err := types.EncodeToBytes(id)
@@ -192,13 +223,13 @@ func (s *Consumption) IsEmpty() bool {
 }
 
 // Report send a capacity report to substrate
-func (s *Substrate) Report(sk ed25519.PrivateKey, consumptions []Consumption) error {
+func (s *Substrate) Report(identity *Identity, consumptions []Consumption) error {
 	c, err := types.NewCall(s.meta, "SmartContractModule.add_reports", consumptions)
 	if err != nil {
 		return errors.Wrap(err, "failed to create call")
 	}
 
-	if _, err := s.call(sk, c); err != nil {
+	if _, err := s.call(identity, c); err != nil {
 		return errors.Wrap(err, "failed to create report")
 	}
 
diff --git a/pkg/substrate/node.go b/pkg/substrate/node.go
index 8b1e08a2..d204c2b7 100644
--- a/pkg/substrate/node.go
+++ b/pkg/substrate/node.go
@@ -1,7 +1,6 @@
 package substrate
 
 import (
-	"crypto/ed25519"
 	"fmt"
 
 	"github.com/centrifuge/go-substrate-rpc-client/v3/scale"
@@ -173,7 +172,7 @@ func (s *Substrate) getNode(key types.StorageKey) (*Node, error) {
 }
 
 // CreateNode creates a node
-func (s *Substrate) CreateNode(sk ed25519.PrivateKey, node Node) (uint32, error) {
+func (s *Substrate) CreateNode(identity *Identity, node Node) (uint32, error) {
 	if node.TwinID == 0 {
 		return 0, fmt.Errorf("twin id is required")
 	}
@@ -187,7 +186,7 @@ func (s *Substrate) CreateNode(sk ed25519.PrivateKey, node Node) (uint32, error)
 		return 0, errors.Wrap(err, "failed to create call")
 	}
 
-	if _, err := s.call(sk, c); err != nil {
+	if _, err := s.call(identity, c); err != nil {
 		return 0, errors.Wrap(err, "failed to create node")
 	}
 
@@ -196,7 +195,7 @@ func (s *Substrate) CreateNode(sk ed25519.PrivateKey, node Node) (uint32, error)
 }
 
 // UpdateNode updates a node
-func (s *Substrate) UpdateNode(sk ed25519.PrivateKey, node Node) (uint32, error) {
+func (s *Substrate) UpdateNode(identity *Identity, node Node) (uint32, error) {
 	if node.TwinID == 0 {
 		return 0, fmt.Errorf("twin id is required")
 	}
@@ -209,7 +208,7 @@ func (s *Substrate) UpdateNode(sk ed25519.PrivateKey, node Node) (uint32, error)
 		return 0, errors.Wrap(err, "failed to create call")
 	}
 
-	if _, err := s.call(sk, c); err != nil {
+	if _, err := s.call(identity, c); err != nil {
 		return 0, errors.Wrap(err, "failed to update node")
 	}
 
diff --git a/pkg/substrate/twin.go b/pkg/substrate/twin.go
index dd0f9090..28c9fd91 100644
--- a/pkg/substrate/twin.go
+++ b/pkg/substrate/twin.go
@@ -1,7 +1,6 @@
 package substrate
 
 import (
-	"crypto/ed25519"
 	"net"
 
 	"github.com/centrifuge/go-substrate-rpc-client/v3/types"
@@ -87,39 +86,29 @@ func (s *Substrate) GetTwin(id uint32) (*Twin, error) {
 }
 
 // CreateTwin creates a twin
-func (s *Substrate) CreateTwin(sk ed25519.PrivateKey, ip net.IP) (uint32, error) {
+func (s *Substrate) CreateTwin(identity *Identity, ip net.IP) (uint32, error) {
 	c, err := types.NewCall(s.meta, "TfgridModule.create_twin", ip.String())
 	if err != nil {
 		return 0, errors.Wrap(err, "failed to create call")
 	}
 
-	if _, err := s.call(sk, c); err != nil {
+	if _, err := s.call(identity, c); err != nil {
 		return 0, errors.Wrap(err, "failed to create twin")
 	}
 
-	identity, err := Identity(sk)
-	if err != nil {
-		return 0, err
-	}
-
 	return s.GetTwinByPubKey(identity.PublicKey)
 }
 
 // UpdateTwin updates a twin
-func (s *Substrate) UpdateTwin(sk ed25519.PrivateKey, ip net.IP) (uint32, error) {
+func (s *Substrate) UpdateTwin(identity *Identity, ip net.IP) (uint32, error) {
 	c, err := types.NewCall(s.meta, "TfgridModule.update_twin", ip.String())
 	if err != nil {
 		return 0, errors.Wrap(err, "failed to create call")
 	}
 
-	if _, err := s.call(sk, c); err != nil {
+	if _, err := s.call(identity, c); err != nil {
 		return 0, errors.Wrap(err, "failed to update twin")
 	}
 
-	identity, err := Identity(sk)
-	if err != nil {
-		return 0, err
-	}
-
 	return s.GetTwinByPubKey(identity.PublicKey)
 }
diff --git a/pkg/substrate/utils.go b/pkg/substrate/utils.go
index ade75ee6..166b11b9 100644
--- a/pkg/substrate/utils.go
+++ b/pkg/substrate/utils.go
@@ -1,10 +1,8 @@
 package substrate
 
 import (
-	"crypto/ed25519"
 	"fmt"
 
-	"github.com/centrifuge/go-substrate-rpc-client/v3/signature"
 	"github.com/centrifuge/go-substrate-rpc-client/v3/types"
 	"github.com/pkg/errors"
 	"github.com/vedhavyas/go-subkey"
@@ -36,7 +34,7 @@ func signBytes(data []byte, privateKeyURI string) ([]byte, error) {
 }
 
 // Sign adds a signature to the extrinsic
-func (s *Substrate) sign(e *types.Extrinsic, signer signature.KeyringPair, o types.SignatureOptions) error {
+func (s *Substrate) sign(e *types.Extrinsic, signer *Identity, o types.SignatureOptions) error {
 	if e.Type() != types.ExtrinsicVersion4 {
 		return fmt.Errorf("unsupported extrinsic version: %v (isSigned: %v, type: %v)", e.Version, e.IsSigned(), e.Type())
 	}
@@ -93,8 +91,7 @@ func (s *Substrate) sign(e *types.Extrinsic, signer signature.KeyringPair, o typ
 	return nil
 }
 
-func (s *Substrate) call(sk ed25519.PrivateKey, call types.Call) (hash types.Hash, err error) {
-
+func (s *Substrate) call(identity *Identity, call types.Call) (hash types.Hash, err error) {
 	// Create the extrinsic
 	ext := types.NewExtrinsic(call)
 
@@ -108,11 +105,6 @@ func (s *Substrate) call(sk ed25519.PrivateKey, call types.Call) (hash types.Has
 		return hash, err
 	}
 
-	identity, err := Identity(sk)
-	if err != nil {
-		return hash, err
-	}
-
 	//node.Address =identity.PublicKey
 	account, err := s.getAccount(identity, s.meta)
 	if err != nil {
diff --git a/pkg/upgrade/hub.go b/pkg/upgrade/hub.go
index 55eaa4ed..5c944dc8 100644
--- a/pkg/upgrade/hub.go
+++ b/pkg/upgrade/hub.go
@@ -108,6 +108,7 @@ func (h *HubClient) List(repo string) ([]FListInfo, error) {
 // path to the extraced meta data directory. the returned path is in format
 // {cache}/{hash}/
 func (h *HubClient) Download(cache, flist string) (string, error) {
+	log.Info().Str("cache", cache).Str("flist", flist).Msg("attempt downloading flist")
 	var info FullFListInfo
 	for {
 		var err error
@@ -136,9 +137,12 @@ func (h *HubClient) Download(cache, flist string) (string, error) {
 	downloaded := filepath.Join(cache, info.Hash)
 	extracted := fmt.Sprintf("%s.d", downloaded)
 
-	if _, err := os.Stat(filepath.Join(extracted, dbFileName)); err == nil {
+	if stat, err := os.Stat(filepath.Join(extracted, dbFileName)); err == nil {
 		// already exists.
-		return extracted, nil
+		if stat.Size() > 0 {
+			log.Info().Str("flist", flist).Msg("already cached")
+			return extracted, nil
+		}
 	}
 
 	u, err := url.Parse(hubBaseURL)
diff --git a/pkg/versioned/data.go b/pkg/versioned/data.go
index 090b6b65..2587c26f 100644
--- a/pkg/versioned/data.go
+++ b/pkg/versioned/data.go
@@ -127,7 +127,9 @@ func WriteFile(filename string, version Version, data []byte, perm os.FileMode)
 	if err != nil {
 		return err
 	}
-	_, err = writer.Write(data)
+	if _, err = writer.Write(data); err != nil {
+		return err
+	}
 
-	return err
+	return file.Sync()
 }
diff --git a/pkg/zdb.go b/pkg/zdb.go
deleted file mode 100644
index 1ba83998..00000000
--- a/pkg/zdb.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package pkg
-
-import (
-	"github.com/threefoldtech/zos/pkg/gridtypes"
-	"github.com/threefoldtech/zos/pkg/gridtypes/zos"
-)
-
-//go:generate mkdir -p stubs
-//go:generate zbusc -module storage -version 0.0.1 -name storage -package stubs github.com/threefoldtech/zos/pkg+ZDBAllocater stubs/zdb_stub.go
-
-// ZDBMode is the enumeration of the modes 0-db can operate in
-type ZDBMode = zos.ZDBMode
-
-// ZDBNamespace is a 0-db namespace
-type ZDBNamespace struct {
-	ID       string
-	DiskType DeviceType
-	Size     uint64
-	Mode     ZDBMode
-	Password string
-	Port     int // Listen port of the 0-db owning the namespace
-}
-
-// Allocation is returned when calling the ZDB allocate. it contains
-// the volume ID and the volume path that has the namespace allocated
-type Allocation struct {
-	VolumeID   string
-	VolumePath string
-}
-
-// ZDBAllocater is the zbus interface of the storage module responsible
-// for 0-db allocation
-type ZDBAllocater interface {
-	// Allocate is responsible to make sure the subvolume used by a 0-db as enough storage capacity
-	// of specified size, type and mode
-	// it returns the volume ID and its path or an error if it couldn't allocate enough storage
-	// Note: if allocation already exists with the namespace name, the current allocation is returned
-	// so no need to call Find before calling allocate
-	Allocate(namespace string, diskType DeviceType, size gridtypes.Unit, mode ZDBMode) (Allocation, error)
-
-	// Find searches the system for the current allocation for the namespace
-	// Return error = "not found" if no allocation exists.
-	Find(namespace string) (allocation Allocation, err error)
-}
diff --git a/pkg/zdb/admin.go b/pkg/zdb/admin.go
index 3f74a1a7..6491bf8c 100644
--- a/pkg/zdb/admin.go
+++ b/pkg/zdb/admin.go
@@ -88,6 +88,20 @@ func (c *clientImpl) NamespaceSetPassword(name, password string) error {
 	return nil
 }
 
+// NamespaceSetMode locks the namespace by a password, use * password to clear it
+func (c *clientImpl) NamespaceSetMode(name, mode string) error {
+	con := c.pool.Get()
+	defer con.Close()
+	ok, err := redis.String(con.Do("NSSET", name, "mode", mode))
+	if err != nil {
+		return err
+	}
+	if ok != "OK" {
+		return fmt.Errorf(ok)
+	}
+	return nil
+}
+
 // NamespaceSetPublic changes the public flag, a public namespace can be read-only if a password is set
 func (c *clientImpl) NamespaceSetPublic(name string, public bool) error {
 	con := c.pool.Get()
diff --git a/pkg/storage/zdbpool/index.go b/pkg/zdb/index.go
similarity index 98%
rename from pkg/storage/zdbpool/index.go
rename to pkg/zdb/index.go
index c0cf1496..18d89fa0 100644
--- a/pkg/storage/zdbpool/index.go
+++ b/pkg/zdb/index.go
@@ -1,4 +1,4 @@
-package zdbpool
+package zdb
 
 import (
 	"encoding/binary"
diff --git a/pkg/storage/zdbpool/ns_header.go b/pkg/zdb/ns_header.go
similarity index 99%
rename from pkg/storage/zdbpool/ns_header.go
rename to pkg/zdb/ns_header.go
index ad1656c2..2fb23a54 100644
--- a/pkg/storage/zdbpool/ns_header.go
+++ b/pkg/zdb/ns_header.go
@@ -1,4 +1,4 @@
-package zdbpool
+package zdb
 
 import (
 	"encoding/binary"
diff --git a/pkg/storage/zdbpool/pool.go b/pkg/zdb/pool.go
similarity index 57%
rename from pkg/storage/zdbpool/pool.go
rename to pkg/zdb/pool.go
index 848fac18..5b47f0dc 100644
--- a/pkg/storage/zdbpool/pool.go
+++ b/pkg/zdb/pool.go
@@ -1,9 +1,11 @@
-package zdbpool
+package zdb
 
 import (
+	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"regexp"
 
 	"github.com/pkg/errors"
 	"github.com/threefoldtech/zos/pkg/gridtypes"
@@ -13,14 +15,19 @@ import (
 // to storge 0-db namespaces
 const Prefix = "zdb"
 
-// ZDBPool represent a part of a disk that is reserved to store 0-db data
-type ZDBPool struct {
+var (
+	nRegex = regexp.MustCompile(`^[\w-]+$`)
+)
+
+// Index represent a part of a disk that is reserved to store 0-db data
+type Index struct {
 	path string
 }
 
-// New creates a ZDBPool with path
-func New(path string) ZDBPool {
-	return ZDBPool{path}
+// NewIndex creates a ZDBPool with path. the path should point
+// where both 'index' and 'data' folders exists.
+func NewIndex(path string) Index {
+	return Index{path}
 }
 
 // NSInfo is a struct containing information about a 0-db namespace
@@ -29,9 +36,29 @@ type NSInfo struct {
 	Size gridtypes.Unit
 }
 
+func (p *Index) index() string {
+	return filepath.Join(p.path, "index")
+}
+
+func (p *Index) data() string {
+	return filepath.Join(p.path, "data")
+}
+
+func (p *Index) valid(n string) error {
+	if len(n) == 0 {
+		return fmt.Errorf("invalid name can't be empty")
+	}
+
+	if !nRegex.MatchString(n) {
+		return fmt.Errorf("name contains invalid characters")
+	}
+
+	return nil
+}
+
 // Reserved return the amount of storage that has been reserved by all the
 // namespace in the pool
-func (p *ZDBPool) Reserved() (uint64, error) {
+func (p *Index) Reserved() (uint64, error) {
 	ns, err := p.Namespaces()
 	if err != nil {
 		return 0, err
@@ -48,8 +75,12 @@ func (p *ZDBPool) Reserved() (uint64, error) {
 // Create a namespace. Note that this create only reserve the name
 // space size (and create namespace descriptor) this must be followed
 // by an actual zdb NSNEW call to create the database files.
-func (p *ZDBPool) Create(name, password string, size gridtypes.Unit) error {
-	dir := filepath.Join(p.path, name)
+func (p *Index) Create(name, password string, size gridtypes.Unit) error {
+	if err := p.valid(name); err != nil {
+		return err
+	}
+
+	dir := filepath.Join(p.index(), name)
 	if err := os.MkdirAll(dir, 0755); err != nil {
 		return errors.Wrapf(err, "namespace '%s' directory creation failed", dir)
 	}
@@ -68,8 +99,12 @@ func (p *ZDBPool) Create(name, password string, size gridtypes.Unit) error {
 }
 
 // Namespace gets a namespace info from pool
-func (p *ZDBPool) Namespace(name string) (info NSInfo, err error) {
-	path := filepath.Join(p.path, name, "zdb-namespace")
+func (p *Index) Namespace(name string) (info NSInfo, err error) {
+	if err := p.valid(name); err != nil {
+		return NSInfo{}, err
+	}
+
+	path := filepath.Join(p.index(), name, "zdb-namespace")
 	f, err := os.Open(path)
 	if err != nil {
 		return info, err
@@ -90,9 +125,8 @@ func (p *ZDBPool) Namespace(name string) (info NSInfo, err error) {
 }
 
 // Namespaces returns a list of NSinfo of all the namespace present in the pool
-func (p *ZDBPool) Namespaces() ([]NSInfo, error) {
-
-	dirs, err := ioutil.ReadDir(p.path)
+func (p *Index) Namespaces() ([]NSInfo, error) {
+	dirs, err := ioutil.ReadDir(p.index())
 	if err != nil {
 		return nil, err
 	}
@@ -123,16 +157,20 @@ func (p *ZDBPool) Namespaces() ([]NSInfo, error) {
 
 // Exists checks if a namespace exists in the pool or not
 // this method is way faster then using Namespaces cause it doesn't have to read any data
-func (p *ZDBPool) Exists(name string) bool {
-	path := filepath.Join(p.path, name, "zdb-namespace")
+func (p *Index) Exists(name string) bool {
+	path := filepath.Join(p.index(), name, "zdb-namespace")
 
 	_, err := os.Stat(path)
 	return err == nil
 }
 
 // IndexMode return the mode of the index of the namespace called name
-func (p *ZDBPool) IndexMode(name string) (mode IndexMode, err error) {
-	path := filepath.Join(p.path, name, "zdb-index-00000")
+func (p *Index) IndexMode(name string) (mode IndexMode, err error) {
+	if err := p.valid(name); err != nil {
+		return IndexMode(0), err
+	}
+
+	path := filepath.Join(p.index(), name, "zdb-index-00000")
 
 	f, err := os.Open(path)
 	if err != nil {
@@ -147,3 +185,20 @@ func (p *ZDBPool) IndexMode(name string) (mode IndexMode, err error) {
 
 	return index.Mode, nil
 }
+
+// Delete both the data and an index of a namespace
+func (p *Index) Delete(name string) error {
+	if err := p.valid(name); err != nil {
+		return err
+	}
+	idx := filepath.Join(p.index(), name)
+	dat := filepath.Join(p.data(), name)
+
+	for _, dir := range []string{idx, dat} {
+		if err := os.RemoveAll(dir); err != nil {
+			return errors.Wrap(err, "failed to delete directory")
+		}
+	}
+
+	return nil
+}
diff --git a/pkg/storage/zdbpool/test_data/pool_layout/default/zdb-data-00000 b/pkg/zdb/test_data/pool_layout/index/default/zdb-data-00000
similarity index 100%
rename from pkg/storage/zdbpool/test_data/pool_layout/default/zdb-data-00000
rename to pkg/zdb/test_data/pool_layout/index/default/zdb-data-00000
diff --git a/pkg/storage/zdbpool/test_data/pool_layout/default/zdb-index-00000 b/pkg/zdb/test_data/pool_layout/index/default/zdb-index-00000
similarity index 100%
rename from pkg/storage/zdbpool/test_data/pool_layout/default/zdb-index-00000
rename to pkg/zdb/test_data/pool_layout/index/default/zdb-index-00000
diff --git a/pkg/storage/zdbpool/test_data/pool_layout/default/zdb-namespace b/pkg/zdb/test_data/pool_layout/index/default/zdb-namespace
similarity index 100%
rename from pkg/storage/zdbpool/test_data/pool_layout/default/zdb-namespace
rename to pkg/zdb/test_data/pool_layout/index/default/zdb-namespace
diff --git a/pkg/storage/zdbpool/test_data/pool_layout/test/zdb-data-00000 b/pkg/zdb/test_data/pool_layout/index/test/zdb-data-00000
similarity index 100%
rename from pkg/storage/zdbpool/test_data/pool_layout/test/zdb-data-00000
rename to pkg/zdb/test_data/pool_layout/index/test/zdb-data-00000
diff --git a/pkg/storage/zdbpool/test_data/pool_layout/test/zdb-index-00000 b/pkg/zdb/test_data/pool_layout/index/test/zdb-index-00000
similarity index 100%
rename from pkg/storage/zdbpool/test_data/pool_layout/test/zdb-index-00000
rename to pkg/zdb/test_data/pool_layout/index/test/zdb-index-00000
diff --git a/pkg/storage/zdbpool/test_data/pool_layout/test/zdb-namespace b/pkg/zdb/test_data/pool_layout/index/test/zdb-namespace
similarity index 100%
rename from pkg/storage/zdbpool/test_data/pool_layout/test/zdb-namespace
rename to pkg/zdb/test_data/pool_layout/index/test/zdb-namespace
diff --git a/pkg/storage/zdbpool/test_data/pool_layout/test2/zdb-data-00000 b/pkg/zdb/test_data/pool_layout/index/test2/zdb-data-00000
similarity index 100%
rename from pkg/storage/zdbpool/test_data/pool_layout/test2/zdb-data-00000
rename to pkg/zdb/test_data/pool_layout/index/test2/zdb-data-00000
diff --git a/pkg/storage/zdbpool/test_data/pool_layout/test2/zdb-index-00000 b/pkg/zdb/test_data/pool_layout/index/test2/zdb-index-00000
similarity index 100%
rename from pkg/storage/zdbpool/test_data/pool_layout/test2/zdb-index-00000
rename to pkg/zdb/test_data/pool_layout/index/test2/zdb-index-00000
diff --git a/pkg/storage/zdbpool/test_data/pool_layout/test2/zdb-namespace b/pkg/zdb/test_data/pool_layout/index/test2/zdb-namespace
similarity index 100%
rename from pkg/storage/zdbpool/test_data/pool_layout/test2/zdb-namespace
rename to pkg/zdb/test_data/pool_layout/index/test2/zdb-namespace
diff --git a/pkg/storage/zdbpool/test_data/zdb-index-00000 b/pkg/zdb/test_data/zdb-index-00000
similarity index 100%
rename from pkg/storage/zdbpool/test_data/zdb-index-00000
rename to pkg/zdb/test_data/zdb-index-00000
diff --git a/pkg/storage/zdbpool/test_data/zdb-namespace b/pkg/zdb/test_data/zdb-namespace
similarity index 100%
rename from pkg/storage/zdbpool/test_data/zdb-namespace
rename to pkg/zdb/test_data/zdb-namespace
diff --git a/pkg/storage/zdbpool/test_data/zdb-namespace.v1 b/pkg/zdb/test_data/zdb-namespace.v1
similarity index 100%
rename from pkg/storage/zdbpool/test_data/zdb-namespace.v1
rename to pkg/zdb/test_data/zdb-namespace.v1
diff --git a/pkg/zdb/zdb.go b/pkg/zdb/zdb.go
index 99a6c903..19ca125b 100644
--- a/pkg/zdb/zdb.go
+++ b/pkg/zdb/zdb.go
@@ -20,6 +20,7 @@ type Client interface {
 	Namespaces() ([]string, error)
 	NamespaceSetSize(name string, size uint64) error
 	NamespaceSetPassword(name, password string) error
+	NamespaceSetMode(name, mode string) error
 	NamespaceSetPublic(name string, public bool) error
 	DBSize() (uint64, error)
 }


Test patch:
diff --git a/pkg/flist/flist_test.go b/pkg/flist/flist_test.go
index 0f7e7152..20106372 100644
--- a/pkg/flist/flist_test.go
+++ b/pkg/flist/flist_test.go
@@ -18,7 +18,6 @@ import (
 	"github.com/stretchr/testify/require"
 	"github.com/threefoldtech/zos/pkg"
 	"github.com/threefoldtech/zos/pkg/gridtypes"
-	"github.com/threefoldtech/zos/pkg/gridtypes/zos"
 )
 
 var (
@@ -34,23 +33,23 @@ type StorageMock struct {
 }
 
 // CreateFilesystem create filesystem mock
-func (s *StorageMock) CreateFilesystem(ctx context.Context, name string, size gridtypes.Unit, poolType pkg.DeviceType) (pkg.Filesystem, error) {
-	args := s.Called(ctx, name, size, poolType)
-	return pkg.Filesystem{
+func (s *StorageMock) VolumeCreate(ctx context.Context, name string, size gridtypes.Unit) (pkg.Volume, error) {
+	args := s.Called(ctx, name, size)
+	return pkg.Volume{
 		Path: args.String(0),
 	}, args.Error(1)
 }
 
 // ReleaseFilesystem releases filesystem mock
-func (s *StorageMock) ReleaseFilesystem(ctx context.Context, name string) error {
+func (s *StorageMock) VolumeDelete(ctx context.Context, name string) error {
 	args := s.Called(ctx, name)
 	return args.Error(0)
 }
 
 // Path implements the pkg.StorageModules interfaces
-func (s *StorageMock) Path(ctx context.Context, name string) (pkg.Filesystem, error) {
+func (s *StorageMock) VolumeLookup(ctx context.Context, name string) (pkg.Volume, error) {
 	args := s.Called(ctx, name)
-	return pkg.Filesystem{
+	return pkg.Volume{
 		Path: args.String(0),
 	}, args.Error(1)
 }
@@ -154,10 +153,10 @@ func TestMountUnmount(t *testing.T) {
 	require.NoError(t, err)
 	defer os.RemoveAll(backend)
 
-	strg.On("Path", mock.Anything, mock.Anything).Return(backend, nil)
+	strg.On("VolumeLookup", mock.Anything, mock.Anything).Return(backend, nil)
 
 	name := "test"
-	strg.On("CreateFilesystem", name, uint64(256*mib), zos.SSDDevice).
+	strg.On("VolumeCreate", name, uint64(256*mib)).
 		Return(backend, nil)
 
 	sys.On("Mount", "overlay", filepath.Join(root, "mountpoint", name), "overlay", uintptr(syscall.MS_NOATIME), mock.Anything).Return(nil)
@@ -167,7 +166,7 @@ func TestMountUnmount(t *testing.T) {
 
 	// Trick flister into thinking that 0-fs has exited
 	os.Remove(cmder.m["pid"])
-	strg.On("ReleaseFilesystem", mock.Anything, filepath.Base(mnt)).Return(nil)
+	strg.On("VolumeDelete", mock.Anything, filepath.Base(mnt)).Return(nil)
 
 	sys.On("Unmount", mnt, 0).Return(nil)
 
@@ -201,7 +200,7 @@ func TestMountUnmountRO(t *testing.T) {
 
 	// Trick flister into thinking that 0-fs has exited
 	os.Remove(cmder.m["pid"])
-	strg.On("ReleaseFilesystem", mock.Anything, filepath.Base(mnt)).Return(nil)
+	strg.On("VolumeDelete", mock.Anything, filepath.Base(mnt)).Return(nil)
 
 	sys.On("Unmount", mnt, 0).Return(nil)
 
@@ -227,9 +226,9 @@ func TestIsolation(t *testing.T) {
 	backend, err := ioutil.TempDir("", "flist_backend")
 	require.NoError(err)
 	defer os.RemoveAll(backend)
-	strg.On("Path", mock.Anything, mock.Anything).Return(backend, nil)
+	strg.On("VolumeLookup", mock.Anything, mock.Anything).Return(backend, nil)
 
-	strg.On("CreateFilesystem", mock.Anything, mock.Anything, mock.Anything, uint64(256*mib), zos.SSDDevice).
+	strg.On("VolumeCreate", mock.Anything, mock.Anything, mock.Anything, uint64(256*mib)).
 		Return(backend, nil)
 
 	name1 := "test1"
diff --git a/pkg/primitives/zdb_test.go b/pkg/primitives/zdb_test.go
index 72d71e18..cb95bea5 100644
--- a/pkg/primitives/zdb_test.go
+++ b/pkg/primitives/zdb_test.go
@@ -1,39 +1,39 @@
 package primitives
 
-import (
-	"net"
-	"testing"
-)
+// import (
+// 	"net"
+// 	"testing"
+// )
 
-func Test_isYgg(t *testing.T) {
-	type args struct {
-		ip net.IP
-	}
-	tests := []struct {
-		name string
-		args args
-		want bool
-	}{
-		{
-			name: "301:8d90:378f:a93:6364:f8c8:1c6b:865d",
-			args: args{
-				ip: net.ParseIP("301:8d90:378f:a93:6364:f8c8:1c6b:865d"),
-			},
-			want: true,
-		},
-		{
-			name: "2a02:2788:864:1314:9eb6:d0ff:fe97:764b",
-			args: args{
-				ip: net.ParseIP("2a02:2788:864:1314:9eb6:d0ff:fe97:764b"),
-			},
-			want: false,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if got := isYgg(tt.args.ip); got != tt.want {
-				t.Errorf("isYgg() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
+// func Test_isYgg(t *testing.T) {
+// 	type args struct {
+// 		ip net.IP
+// 	}
+// 	tests := []struct {
+// 		name string
+// 		args args
+// 		want bool
+// 	}{
+// 		{
+// 			name: "301:8d90:378f:a93:6364:f8c8:1c6b:865d",
+// 			args: args{
+// 				ip: net.ParseIP("301:8d90:378f:a93:6364:f8c8:1c6b:865d"),
+// 			},
+// 			want: true,
+// 		},
+// 		{
+// 			name: "2a02:2788:864:1314:9eb6:d0ff:fe97:764b",
+// 			args: args{
+// 				ip: net.ParseIP("2a02:2788:864:1314:9eb6:d0ff:fe97:764b"),
+// 			},
+// 			want: false,
+// 		},
+// 	}
+// 	for _, tt := range tests {
+// 		t.Run(tt.name, func(t *testing.T) {
+// 			if got := isYgg(tt.args.ip); got != tt.want {
+// 				t.Errorf("isYgg() = %v, want %v", got, tt.want)
+// 			}
+// 		})
+// 	}
+// }
diff --git a/pkg/storage/filesystem/btrfs_ci_test.go b/pkg/storage/filesystem/btrfs_ci_test.go
index 39b3dac7..c861fb9e 100644
--- a/pkg/storage/filesystem/btrfs_ci_test.go
+++ b/pkg/storage/filesystem/btrfs_ci_test.go
@@ -21,13 +21,13 @@ import (
 	"io/ioutil"
 	"os"
 	"path"
+	"path/filepath"
 	"strings"
 	"testing"
 
-	"github.com/threefoldtech/zos/pkg"
-
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+	"github.com/threefoldtech/zos/pkg/gridtypes/zos"
 )
 
 var (
@@ -36,13 +36,23 @@ var (
 
 type TestDevices map[string]string
 
-func (d TestDevices) Loops() DeviceCache {
-	var loops DeviceCache
+func (d TestDevices) Loops() Devices {
+	mgr := lsblkDeviceManager{
+		executer: executerFunc(run),
+	}
 	for _, loop := range d {
-		loops = append(loops, Device{Path: loop})
+		mgr.cache = append(mgr.cache, minDevice{
+			IPath:    loop,
+			IName:    filepath.Base(loop),
+			DiskType: zos.SSDDevice,
+		})
 	}
 
-	return loops
+	devices, err := mgr.Devices(context.Background())
+	if err != nil {
+		panic(err)
+	}
+	return devices
 }
 
 func (d TestDevices) Destroy() {
@@ -99,6 +109,7 @@ func TestMain(m *testing.M) {
 	if err != nil {
 		// we can't create devices. we need to skip
 		// CI test
+		fmt.Println("skipping ci tests")
 		SkipCITests = true
 	} else {
 		devices.Destroy()
@@ -114,8 +125,8 @@ func TestMain(m *testing.M) {
 
 func basePoolTest(t *testing.T, pool Pool) {
 	t.Run("test mounted", func(t *testing.T) {
-		_, mounted := pool.Mounted()
-		assert.False(t, mounted)
+		_, err := pool.Mounted()
+		assert.ErrorIs(t, err, ErrDeviceNotMounted)
 	})
 
 	t.Run("test path", func(t *testing.T) {
@@ -201,98 +212,11 @@ func TestBtrfsSingleCI(t *testing.T) {
 	defer devices.Destroy()
 	loops := devices.Loops()
 
-	fs := NewBtrfs(&TestDeviceManager{loops})
-	pool, err := fs.Create(context.Background(), "test-single", pkg.Single, &loops[0])
-	require.NoError(t, err)
-
-	basePoolTest(t, pool)
-}
-
-func TestBtrfsRaid1CI(t *testing.T) {
-	if SkipCITests {
-		t.Skip("test requires ability to create loop devices")
-	}
-	devices, err := SetupDevices(3)
-	require.NoError(t, err, "failed to initialize devices")
-
-	defer devices.Destroy()
-
-	loops := devices.Loops()
-	fs := NewBtrfs(&TestDeviceManager{loops})
-
-	pool, err := fs.Create(context.Background(), "test-raid1", pkg.Raid1, &loops[0], &loops[1]) //use the first 2 disks
-
-	require.NoError(t, err)
-
-	basePoolTest(t, pool)
-
-	//make sure pool is mounted
-	_, err = pool.Mount()
-	require.NoError(t, err)
-
-	defer pool.UnMount()
-
-	// raid  specific tests
-
-	t.Run("add device", func(t *testing.T) {
-		// add a device to array
-		err = pool.AddDevice(&loops[2])
-		require.NoError(t, err)
-	})
-
-	t.Run("remove device", func(t *testing.T) {
-		// remove device from array
-		err = pool.RemoveDevice(&loops[0])
-		require.NoError(t, err)
-	})
-
-	t.Run("remove second device", func(t *testing.T) {
-		// remove a 2nd device should fail because raid1 should
-		// have at least 2 devices
-		err = pool.RemoveDevice(&loops[1])
-		require.Error(t, err)
-	})
-}
-
-func TestBtrfsListCI(t *testing.T) {
-	if SkipCITests {
-		t.Skip("test requires ability to create loop devices")
-	}
-
-	devices, err := SetupDevices(2)
-	require.NoError(t, err, "failed to initialize devices")
-
-	defer devices.Destroy()
-	loops := devices.Loops()
-	fs := NewBtrfs(&TestDeviceManager{loops})
-
-	names := make(map[string]struct{})
-	for idx := range loops {
-		loop := &loops[idx]
-		name := fmt.Sprintf("test-list-%d", idx)
-		names[name] = struct{}{}
-		_, err := fs.Create(context.Background(), name, pkg.Single, loop)
+	for _, dev := range loops {
+		pool, err := NewBtrfsPool(dev)
 		require.NoError(t, err)
+		basePoolTest(t, pool)
 	}
-	pools, err := fs.List(context.Background(), func(p Pool) bool {
-		return strings.HasPrefix(p.Name(), "test-")
-	})
-
-	require.NoError(t, err)
-
-	for _, pool := range pools {
-		if !strings.HasPrefix(pool.Name(), "test-list") {
-			continue
-		}
-
-		_, exist := names[pool.Name()]
-		require.True(t, exist, "pool %s is not listed", pool)
-
-		delete(names, pool.Name())
-	}
-
-	ok := assert.Len(t, names, 0)
-	assert.True(t, ok, "not all pools were listed")
 }
 
 func TestCLeanUpQgroupsCI(t *testing.T) {
@@ -305,21 +229,8 @@ func TestCLeanUpQgroupsCI(t *testing.T) {
 	defer devices.Destroy()
 
 	loops := devices.Loops()
-	fs := NewBtrfs(&TestDeviceManager{loops})
-
-	names := make(map[string]struct{})
-	for idx := range loops {
-		loop := &loops[idx]
-		name := fmt.Sprintf("test-list-%d", idx)
-		names[name] = struct{}{}
-		_, err := fs.Create(context.Background(), name, pkg.Single, loop)
-		require.NoError(t, err)
-	}
-	pools, err := fs.List(context.Background(), func(p Pool) bool {
-		return strings.HasPrefix(p.Name(), "test-")
-	})
+	pool, err := NewBtrfsPool(loops[0])
 	require.NoError(t, err)
-	pool := pools[0]
 
 	_, err = pool.Mount()
 	require.NoError(t, err)
@@ -337,7 +248,7 @@ func TestCLeanUpQgroupsCI(t *testing.T) {
 
 	qgroups, err := btrfsVol.utils.QGroupList(context.TODO(), pool.Path())
 	require.NoError(t, err)
-	assert.Equal(t, 2, len(qgroups))
+	assert.Equal(t, 1, len(qgroups))
 	t.Logf("qgroups before delete: %v", qgroups)
 
 	_, ok = qgroups[fmt.Sprintf("0/%d", btrfsVol.id)]
@@ -350,5 +261,5 @@ func TestCLeanUpQgroupsCI(t *testing.T) {
 	require.NoError(t, err)
 
 	t.Logf("remaining qgroups: %+v", qgroups)
-	assert.Equal(t, 1, len(qgroups), "qgroups should have been deleted with the subvolume")
+	assert.Equal(t, 0, len(qgroups), "qgroups should have been deleted with the subvolume")
 }
diff --git a/pkg/storage/filesystem/btrfs_test.go b/pkg/storage/filesystem/btrfs_test.go
index 8625827b..5e62f957 100644
--- a/pkg/storage/filesystem/btrfs_test.go
+++ b/pkg/storage/filesystem/btrfs_test.go
@@ -2,99 +2,109 @@ package filesystem
 
 import (
 	"context"
-	"fmt"
 	"testing"
 
-	"github.com/threefoldtech/zos/pkg"
 	"github.com/threefoldtech/zos/pkg/gridtypes/zos"
 
 	"github.com/stretchr/testify/mock"
 	"github.com/stretchr/testify/require"
 )
 
-type TestDeviceManager struct {
-	devices DeviceCache
+type TestDevice struct {
+	path       string
+	name       string
+	size       uint64
+	deviceType zos.DeviceType
+	info       DeviceInfo
+	readTime   uint64
 }
 
-func (m *TestDeviceManager) Reset() DeviceManager {
-	return m
+// Path returns path to the device like /dev/sda
+func (t *TestDevice) Path() string {
+	return t.path
 }
 
-func (m *TestDeviceManager) Device(ctx context.Context, path string) (*Device, error) {
-	for idx := range m.devices {
-		loop := &m.devices[idx]
-		if loop.Path == path {
-			return loop, nil
-		}
-	}
+// Size device size
+func (t *TestDevice) Size() uint64 {
+	return t.size
+}
 
-	return nil, fmt.Errorf("device not found")
+// Name returns name of the device like sda
+func (t *TestDevice) Name() string {
+	return t.name
 }
 
-func (m *TestDeviceManager) ByLabel(ctx context.Context, label string) ([]*Device, error) {
-	var filterred []*Device
-	for idx := range m.devices {
-		device := &m.devices[idx]
-		if device.Label == label {
-			filterred = append(filterred, device)
-		}
-	}
-	return filterred, nil
+// Type returns detected device type (hdd, ssd)
+func (t *TestDevice) Type() zos.DeviceType {
+	return t.deviceType
 }
 
-func (m *TestDeviceManager) Devices(ctx context.Context) (DeviceCache, error) {
-	return m.devices, nil
+// Info is current device information, this should not be cached because
+// it might change over time
+func (t *TestDevice) Info() (DeviceInfo, error) {
+	return t.info, nil
 }
 
-func (m *TestDeviceManager) Raw(ctx context.Context) (DeviceCache, error) {
-	return m.devices, nil
+// ReadTime detected read time of the device
+func (t *TestDevice) ReadTime() uint64 {
+	return t.readTime
 }
 
-func TestBtrfsCreateSingle(t *testing.T) {
+func TestBtrfsCreatePoolExists(t *testing.T) {
 	require := require.New(t)
-	mgr := &TestDeviceManager{
-		devices: DeviceCache{
-			Device{Path: "/tmp/dev1", DiskType: zos.SSDDevice},
+
+	exe := &TestExecuter{}
+	dev := TestDevice{
+		path:       "/tmp/disk",
+		name:       "disk",
+		deviceType: zos.SSDDevice,
+		readTime:   0,
+		info: DeviceInfo{
+			Path:       "/tmp/disk",
+			Label:      "some-label",
+			Mountpoint: "/mnt/some-label",
+			Filesystem: BtrfsFSType,
 		},
 	}
+	pool, err := newBtrfsPool(&dev, exe)
+	require.NoError(err)
+	require.NotNil(pool)
 
-	var exec TestExecuter
-
-	exec.On("run", mock.Anything, "mkfs.btrfs", "-L", "test-single", "-d", "single", "-m", "single", "/tmp/dev1").
-		Return([]byte{}, nil)
-
-	fs := newBtrfs(mgr, &exec)
-	_, err := fs.Create(context.Background(), "test-single", pkg.Single, &mgr.devices[0])
+	mnt, err := pool.Mounted()
 	require.NoError(err)
+	require.Equal("/mnt/some-label", mnt)
 
-	require.Equal("test-single", mgr.devices[0].Label)
-	require.Equal(BtrfsFSType, mgr.devices[0].Filesystem)
+	// if not mounted!
+	dev.info.Mountpoint = ""
+	pool, err = newBtrfsPool(&dev, exe)
+	require.NoError(err)
+	require.NotNil(pool)
 
-	//basePoolTest(t, &exec, pool)
+	_, err = pool.Mounted()
+	require.ErrorIs(err, ErrDeviceNotMounted)
 }
 
-func TestBtrfsCreateRaid1(t *testing.T) {
+func TestBtrfsCreatePoolNotExist(t *testing.T) {
+	// this should actually create a btrfs pool
 	require := require.New(t)
-	mgr := &TestDeviceManager{
-		devices: DeviceCache{
-			Device{Path: "/tmp/dev1", DiskType: zos.SSDDevice},
-			Device{Path: "/tmp/dev2", DiskType: zos.SSDDevice},
+
+	exe := &TestExecuter{}
+	dev := TestDevice{
+		path:       "/tmp/disk",
+		name:       "disk",
+		deviceType: zos.SSDDevice,
+		readTime:   0,
+		info: DeviceInfo{
+			Path: "/tmp/disk",
 		},
 	}
 
-	var exec TestExecuter
-
-	exec.On("run", mock.Anything, "mkfs.btrfs", "-L", "test-raid1",
-		"-d", "raid1", "-m", "raid1",
-		"/tmp/dev1", "/tmp/dev2").Return([]byte{}, nil)
+	// expected formating of the device
+	ctx := context.Background()
+	exe.On("run", ctx, "mkfs.btrfs", "-L", mock.AnythingOfType("string"), dev.path).
+		Return([]byte{}, nil)
 
-	fs := newBtrfs(mgr, &exec)
-	_, err := fs.Create(context.Background(), "test-raid1", pkg.Raid1, &mgr.devices[0], &mgr.devices[1])
+	pool, err := newBtrfsPool(&dev, exe)
 	require.NoError(err)
-
-	require.Equal("test-raid1", mgr.devices[0].Label)
-	require.Equal(BtrfsFSType, mgr.devices[0].Filesystem)
-
-	require.Equal("test-raid1", mgr.devices[1].Label)
-	require.Equal(BtrfsFSType, mgr.devices[1].Filesystem)
+	require.NotNil(pool)
 }
diff --git a/pkg/storage/filesystem/btrfs_utils_test.go b/pkg/storage/filesystem/btrfs_utils_test.go
index b2e19dea..a5bd1fff 100644
--- a/pkg/storage/filesystem/btrfs_utils_test.go
+++ b/pkg/storage/filesystem/btrfs_utils_test.go
@@ -4,8 +4,6 @@ import (
 	"context"
 	"testing"
 
-	"github.com/threefoldtech/zos/pkg"
-
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 	"github.com/stretchr/testify/require"
@@ -159,16 +157,16 @@ GlobalReserve, single: total=16777216, used=0
 
 	assert.Equal(t, BtrfsDiskUsage{
 		Data: DiskUsage{
-			Profile: pkg.Single, Total: 8388608, Used: 65536,
+			Total: 8388608, Used: 65536,
 		},
 		System: DiskUsage{
-			Profile: pkg.Single, Total: 4194304, Used: 16384,
+			Total: 4194304, Used: 16384,
 		},
 		Metadata: DiskUsage{
-			Profile: pkg.Single, Total: 276824064, Used: 163840,
+			Total: 276824064, Used: 163840,
 		},
 		GlobalReserve: DiskUsage{
-			Profile: pkg.Single, Total: 16777216, Used: 0,
+			Total: 16777216, Used: 0,
 		},
 	}, df)
 
diff --git a/pkg/storage/filesystem/device_test.go b/pkg/storage/filesystem/device_test.go
index 83d487f8..49cb9816 100644
--- a/pkg/storage/filesystem/device_test.go
+++ b/pkg/storage/filesystem/device_test.go
@@ -2,7 +2,6 @@ package filesystem
 
 import (
 	"context"
-	"encoding/json"
 	"testing"
 
 	"github.com/stretchr/testify/mock"
@@ -14,24 +13,29 @@ func TestDeviceManagerScan(t *testing.T) {
 	var exec TestExecuter
 	ctx := context.Background()
 
-	var devices struct {
-		BlockDevices []Device `json:"blockdevices"`
-	}
-
-	devices.BlockDevices = []Device{
-		{Type: "block", Path: "/tmp/dev1", Label: "test"},
-		{Type: "block", Path: "/tmp/dev2", Children: []Device{
-			{Path: "/tmp/dev2-1"},
-			{Path: "/tmp/dev2-2"},
-		}},
-	}
-
-	bytes, err := json.Marshal(devices)
-	require.NoError(err)
-
 	// we expect this call to lsblk
 	exec.On("run", ctx, "lsblk", "--json", "--output-all", "--bytes", "--exclude", "1,2,11", "--path").
-		Return(bytes, nil)
+		Return(TestMap{
+			"blockdevices": []TestMap{
+				{"subsystems": "block:scsi:pci", "path": "/tmp/dev1", "name": "dev1"},
+				{"subsystems": "block:scsi:pci", "path": "/tmp/dev2", "name": "dev2"},
+			},
+		}.Bytes(), nil)
+
+	// then other calls per device for extended details
+	exec.On("run", ctx, "lsblk", "--json", "--output-all", "--bytes", "--exclude", "1,2,11", "--path", "/tmp/dev1").
+		Return(TestMap{
+			"blockdevices": []TestMap{
+				{"subsystems": "block:scsi:pci", "path": "/tmp/dev1", "name": "dev1", "label": "test"},
+			},
+		}.Bytes(), nil)
+
+	exec.On("run", ctx, "lsblk", "--json", "--output-all", "--bytes", "--exclude", "1,2,11", "--path", "/tmp/dev2").
+		Return(TestMap{
+			"blockdevices": []TestMap{
+				{"subsystems": "block:scsi:pci", "path": "/tmp/dev2", "name": "dev2", "label": "test2"},
+			},
+		}.Bytes(), nil)
 
 	// then the devices will be tested for types (per device)
 	exec.On("run", mock.Anything, "seektime", "-j", "/tmp/dev1").
@@ -41,21 +45,20 @@ func TestDeviceManagerScan(t *testing.T) {
 		Return([]byte(`{"type": "HDD", "elapsed": 5000}`), nil)
 
 	mgr := defaultDeviceManager(ctx, &exec)
-	require.NoError(err)
 
 	cached, err := mgr.Devices(ctx)
 	require.NoError(err)
 
-	require.Len(cached, 4)
+	require.Len(cached, 2)
 	// make sure all types are set.
 	for _, dev := range cached {
-		require.NotEmpty(dev.DiskType, "device: %s", dev.Path)
+		require.NotEmpty(dev.Type(), "device: %s", dev.Path)
 	}
 
 	filtered, err := mgr.ByLabel(ctx, "test")
 	require.NoError(err)
 	require.Len(filtered, 1)
 
-	require.Equal("/tmp/dev1", cached[0].Path)
+	require.Equal("/tmp/dev1", filtered[0].Path())
 
 }
diff --git a/pkg/storage/filesystem/migrate_test.go b/pkg/storage/filesystem/migrate_test.go
deleted file mode 100644
index 3c09d384..00000000
--- a/pkg/storage/filesystem/migrate_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package filesystem
-
-import (
-	"context"
-	"os"
-	"testing"
-
-	"github.com/stretchr/testify/mock"
-	"github.com/stretchr/testify/require"
-)
-
-func TestMigrate(t *testing.T) {
-	require := require.New(t)
-	var exec TestExecuter
-	mgr := TestDeviceManager{
-		devices: DeviceCache{
-			Device{Path: "/tmp/fakea", Label: "1234"},
-			Device{Path: "/tmp/fakeb", Label: "sp_1234"},
-			Device{Path: "/tmp/fakec", Children: []Device{
-				Device{Path: "/tmp/fakec1", Label: "sp_1234"},
-			}},
-			Device{Path: "/tmp/faked", Children: []Device{
-				Device{Path: "/tmp/faked1", Label: "1234"},
-			}},
-			Device{Path: "/tmp/fakee"},
-		},
-	}
-
-	ctx := context.Background()
-	exec.On("run", ctx, "wipefs", "-a", "-f", mock.Anything).Return([]byte{}, nil).Run(func(args mock.Arguments) {
-		f, err := os.Create(args.String(4))
-		if err != nil {
-			panic(err)
-		}
-		f.Close()
-	})
-	exec.On("run", ctx, "partprobe").Return([]byte{}, nil)
-	_, err := migrate(ctx, &mgr, &exec)
-	require.NoError(err)
-
-	exec.AssertCalled(t, "run", ctx, "partprobe")
-
-	for _, fake := range []string{"/tmp/fakeb", "/tmp/fakec"} {
-		_, err := os.Stat(fake)
-		require.NoError(err)
-		os.Remove(fake)
-	}
-
-	for _, fake := range []string{"/tmp/fakea", "/tmp/faked", "/tmp/fakee"} {
-		_, err := os.Stat(fake)
-		require.True(os.IsNotExist(err))
-	}
-}
diff --git a/pkg/storage/filesystem/utils_test.go b/pkg/storage/filesystem/utils_test.go
index bc04566a..b5e08ad0 100644
--- a/pkg/storage/filesystem/utils_test.go
+++ b/pkg/storage/filesystem/utils_test.go
@@ -3,6 +3,7 @@ package filesystem
 import (
 	"bytes"
 	"context"
+	"encoding/json"
 	"testing"
 
 	"github.com/stretchr/testify/mock"
@@ -23,6 +24,17 @@ func (t *TestExecuter) run(ctx context.Context, name string, args ...string) ([]
 	return result.Get(0).([]byte), result.Error(1)
 }
 
+type TestMap map[string]interface{}
+
+func (m TestMap) Bytes() []byte {
+	bytes, err := json.Marshal(m)
+	if err != nil {
+		panic(err)
+	}
+
+	return bytes
+}
+
 func TestGetMountTarget(t *testing.T) {
 	buf := bytes.NewBufferString(`
 /dev/sda2 /home btrfs rw,relatime,ssd,space_cache,subvolid=258,subvol=/home 0 0
diff --git a/pkg/storage/storage_test.go b/pkg/storage/storage_test.go
index 825e0cb2..ce63322e 100644
--- a/pkg/storage/storage_test.go
+++ b/pkg/storage/storage_test.go
@@ -76,8 +76,8 @@ func (p *testPool) FsType() string {
 	return "test"
 }
 
-func (p *testPool) Mounted() (string, bool) {
-	return p.Path(), true
+func (p *testPool) Mounted() (string, error) {
+	return p.Path(), nil
 }
 
 func (p *testPool) Mount() (string, error) {
@@ -92,14 +92,6 @@ func (p *testPool) UnMount() error {
 	return fmt.Errorf("UnMount not implemented")
 }
 
-func (p *testPool) AddDevice(_ *filesystem.Device) error {
-	return fmt.Errorf("AddDevice not implemented")
-}
-
-func (p *testPool) RemoveDevice(_ *filesystem.Device) error {
-	return fmt.Errorf("RemoveDevice not implemented")
-}
-
 func (p *testPool) Type() zos.DeviceType {
 	return p.ptype
 }
@@ -123,8 +115,8 @@ func (p *testPool) RemoveVolume(name string) error {
 	return args.Error(1)
 }
 
-func (p *testPool) Devices() []*filesystem.Device {
-	return []*filesystem.Device{}
+func (p *testPool) Device() filesystem.Device {
+	return nil
 }
 
 func (p *testPool) Shutdown() error {
@@ -154,19 +146,9 @@ func TestCreateSubvol(t *testing.T) {
 		ptype: zos.SSDDevice,
 	}
 
-	pool3 := &testPool{
-		name:     "pool-3",
-		reserved: 0,
-		usage: filesystem.Usage{
-			Size: 100000,
-			Used: 0,
-		},
-		ptype: zos.HDDDevice,
-	}
-
 	mod := Module{
-		pools: []filesystem.Pool{
-			pool1, pool2, pool3,
+		ssds: []filesystem.Pool{
+			pool1, pool2,
 		},
 	}
 
@@ -174,14 +156,13 @@ func TestCreateSubvol(t *testing.T) {
 		name: "sub",
 	}
 
-	pool1.On("AddVolume", "sub").Return(sub, nil)
+	pool2.On("AddVolume", "sub").Return(sub, nil)
 	sub.On("Limit", uint64(500)).Return(nil)
 
 	pool1.On("Volumes").Return([]filesystem.Volume{}, nil)
 	pool2.On("Volumes").Return([]filesystem.Volume{}, nil)
-	pool3.On("Volumes").Return([]filesystem.Volume{}, nil)
 
-	_, err := mod.createSubvolWithQuota(500, "sub", zos.SSDDevice)
+	_, err := mod.createSubvolWithQuota(500, "sub")
 
 	require.NoError(err)
 }
@@ -220,8 +201,11 @@ func TestCreateSubvolUnlimited(t *testing.T) {
 	}
 
 	mod := Module{
-		pools: []filesystem.Pool{
-			pool1, pool2, pool3,
+		ssds: []filesystem.Pool{
+			pool1, pool2,
+		},
+		hdds: []filesystem.Pool{
+			pool3,
 		},
 	}
 
@@ -229,14 +213,14 @@ func TestCreateSubvolUnlimited(t *testing.T) {
 		name: "sub",
 	}
 
-	pool1.On("AddVolume", "sub").Return(sub, nil)
+	pool2.On("AddVolume", "sub").Return(sub, nil)
 	sub.On("Limit", uint64(0)).Return(nil)
 
 	pool1.On("Volumes").Return([]filesystem.Volume{}, nil)
 	pool2.On("Volumes").Return([]filesystem.Volume{}, nil)
 	pool3.On("Volumes").Return([]filesystem.Volume{}, nil)
 
-	_, err := mod.createSubvolWithQuota(0, "sub", zos.SSDDevice)
+	_, err := mod.createSubvolWithQuota(0, "sub")
 
 	require.NoError(err)
 }
@@ -275,15 +259,18 @@ func TestCreateSubvolNoSpaceLeft(t *testing.T) {
 	}
 
 	mod := Module{
-		pools: []filesystem.Pool{
-			pool1, pool2, pool3,
+		ssds: []filesystem.Pool{
+			pool1, pool2,
+		},
+		hdds: []filesystem.Pool{
+			pool3,
 		},
 	}
 
 	// from the data above the create subvol will prefer pool 2 because it
 	// after adding the subvol, it will still has more space.
 
-	_, err := mod.createSubvolWithQuota(20000, "sub", zos.SSDDevice)
+	_, err := mod.createSubvolWithQuota(20000, "sub")
 
 	require.EqualError(err, "Not enough space left in pools of this type ssd")
 }
@@ -322,7 +309,7 @@ func TestVDiskFindCandidatesHasEnoughSpace(t *testing.T) {
 	}
 
 	mod := Module{
-		pools: []filesystem.Pool{
+		ssds: []filesystem.Pool{
 			pool1, pool2, pool3,
 		},
 	}
@@ -338,65 +325,11 @@ func TestVDiskFindCandidatesHasEnoughSpace(t *testing.T) {
 	pool2.On("Volumes").Return([]filesystem.Volume{}, nil)
 	pool3.On("Volumes").Return([]filesystem.Volume{}, nil)
 
-	_, err := mod.VDiskFindCandidate(500)
+	_, err := mod.diskFindCandidate(500)
 
 	require.NoError(err)
 }
 
-func TestVDiskFindCandidatesWrongType(t *testing.T) {
-	require := require.New(t)
-
-	pool1 := &testPool{
-		name:     "pool-1",
-		reserved: 2000,
-		usage: filesystem.Usage{
-			Size: 10000,
-			Used: 100,
-		},
-		ptype: zos.SSDDevice,
-	}
-
-	pool2 := &testPool{
-		name:     "pool-2",
-		reserved: 1000,
-		usage: filesystem.Usage{
-			Size: 10000,
-			Used: 100,
-		},
-		ptype: zos.SSDDevice,
-	}
-
-	pool3 := &testPool{
-		name:     "pool-3",
-		reserved: 0,
-		usage: filesystem.Usage{
-			Size: 100000,
-			Used: 0,
-		},
-		ptype: zos.HDDDevice,
-	}
-
-	mod := Module{
-		pools: []filesystem.Pool{
-			pool1, pool2, pool3,
-		},
-	}
-
-	sub := &testVolume{
-		name: vdiskVolumeName,
-	}
-
-	pool3.On("AddVolume", vdiskVolumeName).Return(sub, nil)
-
-	pool1.On("Volumes").Return([]filesystem.Volume{sub}, nil)
-	pool2.On("Volumes").Return([]filesystem.Volume{}, nil)
-	pool3.On("Volumes").Return([]filesystem.Volume{}, nil)
-
-	_, err := mod.VDiskFindCandidate(10000)
-	require.EqualError(err, "Not enough space left in pools of this type ssd")
-
-}
-
 func TestVDiskFindCandidatesNoSpace(t *testing.T) {
 	require := require.New(t)
 
@@ -431,7 +364,7 @@ func TestVDiskFindCandidatesNoSpace(t *testing.T) {
 	}
 
 	mod := Module{
-		pools: []filesystem.Pool{
+		ssds: []filesystem.Pool{
 			pool1, pool2, pool3,
 		},
 	}
@@ -446,7 +379,7 @@ func TestVDiskFindCandidatesNoSpace(t *testing.T) {
 	pool2.On("Volumes").Return([]filesystem.Volume{}, nil)
 	pool3.On("Volumes").Return([]filesystem.Volume{}, nil)
 
-	_, err := mod.VDiskFindCandidate(10000)
+	_, err := mod.diskFindCandidate(10000)
 	require.NoError(err)
 
 	if ok := pool3.AssertCalled(t, "AddVolume", vdiskVolumeName); !ok {
diff --git a/pkg/storage/zdbpool/index_test.go b/pkg/zdb/index_test.go
similarity index 95%
rename from pkg/storage/zdbpool/index_test.go
rename to pkg/zdb/index_test.go
index c4ad5ce7..ec431719 100644
--- a/pkg/storage/zdbpool/index_test.go
+++ b/pkg/zdb/index_test.go
@@ -1,4 +1,4 @@
-package zdbpool
+package zdb
 
 import (
 	"os"
diff --git a/pkg/storage/zdbpool/ns_header_test.go b/pkg/zdb/ns_header_test.go
similarity index 98%
rename from pkg/storage/zdbpool/ns_header_test.go
rename to pkg/zdb/ns_header_test.go
index 1c3fcf5e..a3a9b312 100644
--- a/pkg/storage/zdbpool/ns_header_test.go
+++ b/pkg/zdb/ns_header_test.go
@@ -1,4 +1,4 @@
-package zdbpool
+package zdb
 
 import (
 	"bytes"
diff --git a/pkg/storage/zdbpool/pool_test.go b/pkg/zdb/pool_test.go
similarity index 91%
rename from pkg/storage/zdbpool/pool_test.go
rename to pkg/zdb/pool_test.go
index 87863516..632379f3 100644
--- a/pkg/storage/zdbpool/pool_test.go
+++ b/pkg/zdb/pool_test.go
@@ -1,4 +1,4 @@
-package zdbpool
+package zdb
 
 import (
 	"testing"
@@ -9,7 +9,7 @@ import (
 )
 
 func TestNamespaces(t *testing.T) {
-	pool := ZDBPool{
+	pool := Index{
 		path: "./test_data/pool_layout",
 	}
 
@@ -25,7 +25,7 @@ func TestNamespaces(t *testing.T) {
 }
 
 func TestReserved(t *testing.T) {
-	pool := ZDBPool{
+	pool := Index{
 		path: "./test_data/pool_layout",
 	}
 
@@ -35,7 +35,7 @@ func TestReserved(t *testing.T) {
 }
 
 func TestExists(t *testing.T) {
-	pool := ZDBPool{
+	pool := Index{
 		path: "./test_data/pool_layout",
 	}
 
@@ -44,7 +44,7 @@ func TestExists(t *testing.T) {
 }
 
 func TestIndexMode(t *testing.T) {
-	pool := ZDBPool{
+	pool := Index{
 		path: "./test_data/pool_layout",
 	}