New 'statistics' SDL CLI command
[ric-plt/sdlgo.git] / internal / cli / statistics.go
diff --git a/internal/cli/statistics.go b/internal/cli/statistics.go
new file mode 100644 (file)
index 0000000..05608e2
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+   Copyright (c) 2021 AT&T Intellectual Property.
+   Copyright (c) 2018-2021 Nokia.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+/*
+ * This source code is part of the near-RT RIC (RAN Intelligent Controller)
+ * platform project (RICP).
+ */
+
+package cli
+
+import (
+       "fmt"
+       "gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/sdlgoredis"
+       "github.com/spf13/cobra"
+       "os"
+       "reflect"
+       "text/tabwriter"
+)
+
+func init() {
+       rootCmd.AddCommand(newStatisticsCmd(newDatabase))
+}
+
+var (
+       statsLong = `Display SDL resource usage statistics`
+
+       statsExample = `  # Show statistics per node
+  sdlcli statistics`
+)
+
+func newStatisticsCmd(dbCreateCb DbCreateCb) *cobra.Command {
+       return &cobra.Command{
+               Use:     "statistics",
+               Short:   "Display statistics.",
+               Long:    statsLong,
+               Example: statsExample,
+               Args:    cobra.ExactArgs(0),
+               RunE: func(cmd *cobra.Command, args []string) error {
+                       statistics, err := runStats(dbCreateCb)
+                       if err != nil {
+                               fmt.Fprintf(os.Stderr, "%s", buf.String())
+                               return err
+                       }
+                       printStatistics(cmd, statistics)
+                       return nil
+               },
+       }
+}
+
+func runStats(dbCreateCb DbCreateCb) ([]*sdlgoredis.DbStatistics, error) {
+       var statistics []*sdlgoredis.DbStatistics
+       for _, dbInst := range dbCreateCb().Instances {
+               dbStatistics, err := dbInst.Statistics()
+               if err != nil {
+                       return nil, err
+               }
+               statistics = append(statistics, dbStatistics)
+       }
+       return statistics, nil
+}
+
+func writeClientsInfo(w *tabwriter.Writer, clients sdlgoredis.ClientsInfoFields) {
+       fmt.Fprintf(w, "\t\t\tConnectedClients:%d\n", clients.ConnectedClients)
+       fmt.Fprintf(w, "\t\t\tClientRecentMaxInputBuffer:%d\n", clients.ClientRecentMaxInputBuffer)
+       fmt.Fprintf(w, "\t\t\tClientRecentMaxOutputBuffer:%d\n", clients.ClientRecentMaxOutputBuffer)
+}
+
+func writeMemoryInfo(w *tabwriter.Writer, memory sdlgoredis.MeroryInfoFields) {
+       fmt.Fprintf(w, "\t\t\tUsedMemory:%d\n", memory.UsedMemory)
+       fmt.Fprintf(w, "\t\t\tUsedMemoryHuman:%s\n", memory.UsedMemoryHuman)
+       fmt.Fprintf(w, "\t\t\tUsedMemoryRss:%d\n", memory.UsedMemoryRss)
+       fmt.Fprintf(w, "\t\t\tUsedMemoryRssHuman:%s\n", memory.UsedMemoryRssHuman)
+       fmt.Fprintf(w, "\t\t\tUsedMemoryPeak:%d\n", memory.UsedMemoryPeak)
+       fmt.Fprintf(w, "\t\t\tUsedMemoryPeakHuman:%s\n", memory.UsedMemoryPeakHuman)
+       fmt.Fprintf(w, "\t\t\tUsedMemoryPeakPerc:%s\n", memory.UsedMemoryPeakPerc)
+       fmt.Fprintf(w, "\t\t\tMemFragmentationRatio:%.2f\n", memory.MemFragmentationRatio)
+       fmt.Fprintf(w, "\t\t\tMemFragmentationBytes:%d\n", memory.MemFragmentationBytes)
+}
+
+func writeStatsInfo(w *tabwriter.Writer, stats sdlgoredis.StatsInfoFields) {
+       fmt.Fprintf(w, "\t\t\tTotalConnectionsReceived:%d\n", stats.TotalConnectionsReceived)
+       fmt.Fprintf(w, "\t\t\tTotalCommandsProcessed:%d\n", stats.TotalCommandsProcessed)
+       fmt.Fprintf(w, "\t\t\tSyncFull:%d\n", stats.SyncFull)
+       fmt.Fprintf(w, "\t\t\tSyncPartialOk:%d\n", stats.SyncPartialOk)
+       fmt.Fprintf(w, "\t\t\tSyncPartialErr:%d\n", stats.SyncPartialErr)
+       fmt.Fprintf(w, "\t\t\tPubsubChannels:%d\n", stats.PubsubChannels)
+}
+
+func writeCpuInfo(w *tabwriter.Writer, cpu sdlgoredis.CpuInfoFields) {
+       fmt.Fprintf(w, "\t\t\tUsedCpuSys:%v\n", cpu.UsedCpuSys)
+       fmt.Fprintf(w, "\t\t\tUsedCpuUser:%v\n", cpu.UsedCpuUser)
+}
+
+func fillCommandstatsInfo(w *tabwriter.Writer, i interface{}, cmdstat string) {
+       stype := reflect.ValueOf(i).Elem()
+       callsField := stype.FieldByName("Calls").Interface()
+       usecField := stype.FieldByName("Usec").Interface()
+       usecPerCallField := stype.FieldByName("UsecPerCall").Interface()
+
+       if callsField.(uint32) > 0 {
+               fmt.Fprintf(w, "\t\t\t%s:calls=%d usec:%d usecPerCall:%.2f\n",
+                       cmdstat, callsField, usecField, usecPerCallField)
+       }
+}
+
+func writeCommandstatsInfo(w *tabwriter.Writer, commandstats sdlgoredis.CommandstatsInfoFields) {
+       fillCommandstatsInfo(w, &commandstats.CmdstatReplconf, "CmdstatReplconf")
+       fillCommandstatsInfo(w, &commandstats.CmdstatKeys, "CmdstatKeys")
+       fillCommandstatsInfo(w, &commandstats.CmdstatRole, "CmdstatRole")
+       fillCommandstatsInfo(w, &commandstats.CmdstatPsync, "CmdstatPsync")
+       fillCommandstatsInfo(w, &commandstats.CmdstatMset, "CmdstatMset")
+       fillCommandstatsInfo(w, &commandstats.CmdstatPublish, "CmdstatPublish")
+       fillCommandstatsInfo(w, &commandstats.CmdstatInfo, "CmdstatInfo")
+       fillCommandstatsInfo(w, &commandstats.CmdstatPing, "CmdstatPing")
+       fillCommandstatsInfo(w, &commandstats.CmdstatClient, "CmdstatClient")
+       fillCommandstatsInfo(w, &commandstats.CmdstatCommand, "CmdstatCommand")
+       fillCommandstatsInfo(w, &commandstats.CmdstatSubscribe, "CmdstatSubscribe")
+       fillCommandstatsInfo(w, &commandstats.CmdstatMonitor, "CmdstatMonitor")
+       fillCommandstatsInfo(w, &commandstats.CmdstatConfig, "CmdstatConfig")
+       fillCommandstatsInfo(w, &commandstats.CmdstatSlaveof, "CmdstatSlaveof")
+}
+
+func writeKeyspaceInfo(w *tabwriter.Writer, keyspace sdlgoredis.KeyspaceInfoFields) {
+       fmt.Fprintf(w, "\t\t\tDb0:keys=%d\n", keyspace.Db.Keys)
+}
+
+func printStatistics(cmd *cobra.Command, statistics []*sdlgoredis.DbStatistics) {
+       w := tabwriter.NewWriter(cmd.OutOrStdout(), 6, 4, 2, ' ', 0)
+       fmt.Fprintln(w, "CLUSTER\tROLE\tADDRESS\tSTATISTICS")
+
+       for i, s := range statistics {
+               for _, stats := range s.Stats {
+                       fmt.Fprintf(w, "%d", i)
+                       if stats.Info.Fields.PrimaryRole {
+                               fmt.Fprintf(w, "\tPrimary")
+                       } else {
+                               fmt.Fprintf(w, "\tReplica")
+                       }
+                       fmt.Fprintf(w, "\t%s:%s", stats.IPAddr, stats.Port)
+                       fmt.Fprintf(w, "\tUptimeInDays:%d\n", stats.Info.Fields.Server.UptimeInDays)
+                       writeClientsInfo(w, stats.Info.Fields.Clients)
+                       writeMemoryInfo(w, stats.Info.Fields.Memory)
+                       writeStatsInfo(w, stats.Info.Fields.Stats)
+                       writeCpuInfo(w, stats.Info.Fields.Cpu)
+                       writeCommandstatsInfo(w, stats.Info.Fields.Commandstats)
+                       writeKeyspaceInfo(w, stats.Info.Fields.Keyspace)
+               }
+       }
+       w.Flush()
+}