multinode/storage: disk space of concrete node and total disk space implemented
Change-Id: I50a96d3e9a77615e79b826a592c0a07c247e0520
This commit is contained in:
parent
1a974c327d
commit
d426d4f00d
@ -244,6 +244,65 @@ func (storage *Storage) TotalUsageSatellite(w http.ResponseWriter, r *http.Reque
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TotalDiskSpace returns all info about all storagenodes disk space usage.
|
||||||
|
func (storage *Storage) TotalDiskSpace(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
var err error
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
|
||||||
|
totalDiskSpace, err := storage.service.TotalDiskSpace(ctx)
|
||||||
|
if err != nil {
|
||||||
|
storage.log.Error("could not get total disk space", zap.Error(err))
|
||||||
|
storage.serveError(w, http.StatusInternalServerError, ErrStorage.Wrap(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = json.NewEncoder(w).Encode(totalDiskSpace); err != nil {
|
||||||
|
storage.log.Error("failed to write json response", zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiskSpace returns all info about concrete storagenode disk space usage.
|
||||||
|
func (storage *Storage) DiskSpace(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
var err error
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
segments := mux.Vars(r)
|
||||||
|
|
||||||
|
nodeIDparam, ok := segments["nodeID"]
|
||||||
|
if !ok {
|
||||||
|
storage.serveError(w, http.StatusBadRequest, ErrStorage.New("node id is missing"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nodeID, err := storj.NodeIDFromString(nodeIDparam)
|
||||||
|
if err != nil {
|
||||||
|
storage.serveError(w, http.StatusBadRequest, ErrStorage.Wrap(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
diskSpace, err := storage.service.DiskSpace(ctx, nodeID)
|
||||||
|
if err != nil {
|
||||||
|
if nodes.ErrNoNode.Has(err) {
|
||||||
|
storage.serveError(w, http.StatusNotFound, ErrStorage.Wrap(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.log.Error("could not get disk space", zap.Error(err))
|
||||||
|
storage.serveError(w, http.StatusInternalServerError, ErrStorage.Wrap(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = json.NewEncoder(w).Encode(diskSpace); err != nil {
|
||||||
|
storage.log.Error("failed to write json response", zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// serveError set http statuses and send json error.
|
// serveError set http statuses and send json error.
|
||||||
func (storage *Storage) serveError(w http.ResponseWriter, status int, err error) {
|
func (storage *Storage) serveError(w http.ResponseWriter, status int, err error) {
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
|
@ -122,6 +122,8 @@ func NewServer(log *zap.Logger, listener net.Listener, config Config, services S
|
|||||||
storageRouter.HandleFunc("/usage/{nodeID}", storageController.Usage).Methods(http.MethodGet)
|
storageRouter.HandleFunc("/usage/{nodeID}", storageController.Usage).Methods(http.MethodGet)
|
||||||
storageRouter.HandleFunc("/satellites/{satelliteID}/usage", storageController.TotalUsageSatellite).Methods(http.MethodGet)
|
storageRouter.HandleFunc("/satellites/{satelliteID}/usage", storageController.TotalUsageSatellite).Methods(http.MethodGet)
|
||||||
storageRouter.HandleFunc("/satellites/{satelliteID}/usage/{nodeID}", storageController.UsageSatellite).Methods(http.MethodGet)
|
storageRouter.HandleFunc("/satellites/{satelliteID}/usage/{nodeID}", storageController.UsageSatellite).Methods(http.MethodGet)
|
||||||
|
storageRouter.HandleFunc("/disk-space", storageController.TotalDiskSpace).Methods(http.MethodGet)
|
||||||
|
storageRouter.HandleFunc("/disk-space/{nodeID}", storageController.DiskSpace).Methods(http.MethodGet)
|
||||||
|
|
||||||
if server.config.StaticDir != "" {
|
if server.config.StaticDir != "" {
|
||||||
router.PathPrefix("/static/").Handler(http.StripPrefix("/static", fs))
|
router.PathPrefix("/static/").Handler(http.StripPrefix("/static", fs))
|
||||||
|
@ -125,6 +125,73 @@ func (service *Service) TotalUsageSatellite(ctx context.Context, satelliteID sto
|
|||||||
return cache.Sorted(), nil
|
return cache.Sorted(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TotalDiskSpace returns all info about all storagenodes disk space usage.
|
||||||
|
func (service *Service) TotalDiskSpace(ctx context.Context) (totalDiskSpace DiskSpace, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
nodes, err := service.nodes.List(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return DiskSpace{}, Error.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, node := range nodes {
|
||||||
|
diskSpace, err := service.dialDiskSpace(ctx, node)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: how should we handle offline node?
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
totalDiskSpace.Add(diskSpace)
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalDiskSpace, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiskSpace returns all info about concrete storagenode disk space usage.
|
||||||
|
func (service *Service) DiskSpace(ctx context.Context, nodeID storj.NodeID) (_ DiskSpace, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
node, err := service.nodes.Get(ctx, nodeID)
|
||||||
|
if err != nil {
|
||||||
|
return DiskSpace{}, Error.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return service.dialDiskSpace(ctx, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// dialDiskSpace dials node and retrieves all info about concrete storagenode disk space usage.
|
||||||
|
func (service *Service) dialDiskSpace(ctx context.Context, node nodes.Node) (diskSpace DiskSpace, err error) {
|
||||||
|
conn, err := service.dialer.DialNodeURL(ctx, storj.NodeURL{
|
||||||
|
ID: node.ID,
|
||||||
|
Address: node.PublicAddress,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return DiskSpace{}, Error.Wrap(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err = errs.Combine(err, conn.Close())
|
||||||
|
}()
|
||||||
|
|
||||||
|
storageClient := multinodepb.NewDRPCStorageClient(conn)
|
||||||
|
|
||||||
|
diskSpaceResponse, err := storageClient.DiskSpace(ctx, &multinodepb.DiskSpaceRequest{
|
||||||
|
Header: &multinodepb.RequestHeader{
|
||||||
|
ApiKey: node.APISecret,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return DiskSpace{}, Error.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return DiskSpace{
|
||||||
|
Allocated: diskSpaceResponse.Allocated,
|
||||||
|
Used: diskSpaceResponse.UsedPieces,
|
||||||
|
Trash: diskSpaceResponse.UsedTrash,
|
||||||
|
Free: diskSpaceResponse.Free,
|
||||||
|
Available: diskSpaceResponse.Available,
|
||||||
|
Overused: diskSpaceResponse.Overused,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// dialUsage dials node and retrieves it's storage usage for provided interval.
|
// dialUsage dials node and retrieves it's storage usage for provided interval.
|
||||||
func (service *Service) dialUsage(ctx context.Context, node nodes.Node, from, to time.Time) (_ []UsageStamp, err error) {
|
func (service *Service) dialUsage(ctx context.Context, node nodes.Node, from, to time.Time) (_ []UsageStamp, err error) {
|
||||||
defer mon.Task()(&ctx)(&err)
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
@ -53,3 +53,23 @@ func (cache *UsageStampDailyCache) Sorted() []UsageStamp {
|
|||||||
|
|
||||||
return usage
|
return usage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DiskSpace stores all info about storagenode disk space usage.
|
||||||
|
type DiskSpace struct {
|
||||||
|
Allocated int64 `json:"allocated"`
|
||||||
|
Used int64 `json:"usedPieces"`
|
||||||
|
Trash int64 `json:"usedTrash"`
|
||||||
|
Free int64 `json:"free"`
|
||||||
|
Available int64 `json:"available"`
|
||||||
|
Overused int64 `json:"overused"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add combines disk space with another one.
|
||||||
|
func (diskSpace *DiskSpace) Add(space DiskSpace) {
|
||||||
|
diskSpace.Allocated += space.Allocated
|
||||||
|
diskSpace.Used += space.Used
|
||||||
|
diskSpace.Trash += space.Trash
|
||||||
|
diskSpace.Free += space.Free
|
||||||
|
diskSpace.Available += space.Available
|
||||||
|
diskSpace.Overused += space.Overused
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"storj.io/storj/multinode/storage"
|
"storj.io/storj/multinode/storage"
|
||||||
@ -150,3 +151,79 @@ func TestUsageStampDailyCache(t *testing.T) {
|
|||||||
stamps := cache.Sorted()
|
stamps := cache.Sorted()
|
||||||
require.Equal(t, expected, stamps)
|
require.Equal(t, expected, stamps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDiskSpaceAdd(t *testing.T) {
|
||||||
|
totalDiskSpace := storage.DiskSpace{
|
||||||
|
Allocated: 0,
|
||||||
|
Used: 0,
|
||||||
|
Trash: 0,
|
||||||
|
Free: 0,
|
||||||
|
Available: 0,
|
||||||
|
Overused: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
testData := []struct {
|
||||||
|
diskSpace storage.DiskSpace
|
||||||
|
expected storage.DiskSpace
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
diskSpace: storage.DiskSpace{
|
||||||
|
Allocated: 1,
|
||||||
|
Used: 1,
|
||||||
|
Trash: 1,
|
||||||
|
Free: 1,
|
||||||
|
Available: 1,
|
||||||
|
Overused: 1,
|
||||||
|
},
|
||||||
|
expected: storage.DiskSpace{
|
||||||
|
Allocated: 1,
|
||||||
|
Used: 1,
|
||||||
|
Trash: 1,
|
||||||
|
Free: 1,
|
||||||
|
Available: 1,
|
||||||
|
Overused: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
diskSpace: storage.DiskSpace{
|
||||||
|
Allocated: 2,
|
||||||
|
Used: 2,
|
||||||
|
Trash: 2,
|
||||||
|
Free: 2,
|
||||||
|
Available: 2,
|
||||||
|
Overused: 2,
|
||||||
|
},
|
||||||
|
expected: storage.DiskSpace{
|
||||||
|
Allocated: 3,
|
||||||
|
Used: 3,
|
||||||
|
Trash: 3,
|
||||||
|
Free: 3,
|
||||||
|
Available: 3,
|
||||||
|
Overused: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
diskSpace: storage.DiskSpace{
|
||||||
|
Allocated: 3,
|
||||||
|
Used: 3,
|
||||||
|
Trash: 3,
|
||||||
|
Free: 3,
|
||||||
|
Available: 3,
|
||||||
|
Overused: 3,
|
||||||
|
},
|
||||||
|
expected: storage.DiskSpace{
|
||||||
|
Allocated: 6,
|
||||||
|
Used: 6,
|
||||||
|
Trash: 6,
|
||||||
|
Free: 6,
|
||||||
|
Available: 6,
|
||||||
|
Overused: 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testData {
|
||||||
|
totalDiskSpace.Add(test.diskSpace)
|
||||||
|
assert.Equal(t, totalDiskSpace, test.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user