feat(osctl): add df command (#569)

Signed-off-by: Andrew Rynhard <andrew@andrewrynhard.com>
This commit is contained in:
Andrew Rynhard 2019-04-26 08:24:31 -07:00 committed by GitHub
parent 50c51ac717
commit a8fa1f5cd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 148 additions and 0 deletions

42
cmd/osctl/cmd/df.go Normal file
View File

@ -0,0 +1,42 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// nolint: dupl,golint
package cmd
import (
"github.com/spf13/cobra"
"github.com/talos-systems/talos/cmd/osctl/pkg/client"
"github.com/talos-systems/talos/cmd/osctl/pkg/helpers"
"github.com/talos-systems/talos/internal/pkg/constants"
)
// dfCmd represents the df command.
var dfCmd = &cobra.Command{
Use: "df",
Short: "List disk usage",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
creds, err := client.NewDefaultClientCredentials(talosconfig)
if err != nil {
helpers.Fatalf("error getting client credentials: %s", err)
}
if target != "" {
creds.Target = target
}
c, err := client.NewClient(constants.OsdPort, creds)
if err != nil {
helpers.Fatalf("error constructing client: %s", err)
}
if err := c.DF(); err != nil {
helpers.Fatalf("error getting df: %s", err)
}
},
}
func init() {
dfCmd.Flags().StringVarP(&target, "target", "t", "", "target the specificed node")
rootCmd.AddCommand(dfCmd)
}

View File

@ -13,6 +13,7 @@ import (
"encoding/gob"
"fmt"
"io"
"math"
"os"
"text/tabwriter"
@ -271,3 +272,28 @@ func (c *Client) Top() (pl []proc.ProcessList, err error) {
err = dec.Decode(&pl)
return
}
// DF implements the proto.OSDClient interface.
func (c *Client) DF() (err error) {
ctx := context.Background()
reply, err := c.client.DF(ctx, &empty.Empty{})
if err != nil {
fmt.Printf("one or more error encountered: %+v", err)
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "FILESYSTEM\tSIZE(GB)\tUSED(GB)\tAVAILABLE(GB)\tPERCENT USED\tMOUNTED ON")
for _, r := range reply.Stats {
percentAvailable := 100.0 - 100.0*(float64(r.Available)/float64(r.Size))
if math.IsNaN(percentAvailable) {
continue
}
fmt.Fprintf(w, "%s\t%.02f\t%.02f\t%.02f\t%.02f%%\t%s\n", r.Filesystem, float64(r.Size)*1e-9, float64(r.Size-r.Available)*1e-9, float64(r.Available)*1e-9, percentAvailable, r.MountedOn)
}
if err := w.Flush(); err != nil {
return err
}
return nil
}

View File

@ -5,12 +5,14 @@
package reg
import (
"bufio"
"bytes"
"context"
"encoding/gob"
"io/ioutil"
"log"
"os"
"strings"
"github.com/containerd/cgroups"
"github.com/containerd/containerd"
@ -19,6 +21,7 @@ import (
"github.com/containerd/containerd/oci"
"github.com/containerd/typeurl"
"github.com/golang/protobuf/ptypes/empty"
"github.com/hashicorp/go-multierror"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/talos-systems/talos/internal/app/init/pkg/system/events"
@ -399,3 +402,68 @@ func (r *Registrator) Top(ctx context.Context, in *empty.Empty) (reply *proto.To
reply = &proto.TopReply{ProcessList: p}
return
}
// DF implements the proto.OSDServer interface.
func (r *Registrator) DF(ctx context.Context, in *empty.Empty) (reply *proto.DFReply, err error) {
file, err := os.Open("/proc/mounts")
if err != nil {
return nil, err
}
// nolint: errcheck
defer file.Close()
var (
stat unix.Statfs_t
multiErr *multierror.Error
)
stats := []*proto.DFStat{}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fields := strings.Fields(scanner.Text())
if len(fields) < 2 {
continue
}
filesystem := fields[0]
mountpoint := fields[1]
f, err := os.Stat(mountpoint)
if err != nil {
multiErr = multierror.Append(multiErr, err)
continue
}
if mode := f.Mode(); !mode.IsDir() {
continue
}
if err := unix.Statfs(mountpoint, &stat); err != nil {
multiErr = multierror.Append(multiErr, err)
continue
}
totalSize := uint64(stat.Bsize) * stat.Blocks
totalAvail := uint64(stat.Bsize) * stat.Bavail
stat := &proto.DFStat{
Filesystem: filesystem,
Size: totalSize,
Available: totalAvail,
MountedOn: mountpoint,
}
stats = append(stats, stat)
}
if err := scanner.Err(); err != nil {
multiErr = multierror.Append(multiErr, err)
}
reply = &proto.DFReply{
Stats: stats,
}
return reply, multiErr.ErrorOrNil()
}

View File

@ -17,6 +17,7 @@ service OSD {
rpc Routes(google.protobuf.Empty) returns (RoutesReply) {}
rpc Stats(StatsRequest) returns (StatsReply) {}
rpc Top(google.protobuf.Empty) returns (TopReply) {}
rpc DF(google.protobuf.Empty) returns (DFReply) {}
rpc Version(google.protobuf.Empty) returns (Data) {}
}
@ -89,3 +90,14 @@ message TopRequest {}
message TopReply { ProcessList process_list = 1; }
message ProcessList { bytes bytes = 1; }
// The response message containing the requested df stats.
message DFReply { repeated DFStat stats = 1; }
// The response message containing the requested processes.
message DFStat {
string filesystem = 1;
uint64 size = 2;
uint64 available = 3;
string mounted_on = 4;
}