gitlab.com/SkynetLabs/skyd@v1.6.9/cmd/skyc/daemoncmd.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  
     7  	"github.com/spf13/cobra"
     8  
     9  	"gitlab.com/NebulousLabs/errors"
    10  	"gitlab.com/SkynetLabs/skyd/build"
    11  	"go.sia.tech/siad/modules"
    12  )
    13  
    14  var (
    15  	alertsCmd = &cobra.Command{
    16  		Use:   "alerts",
    17  		Short: "view daemon alerts",
    18  		Long:  "view daemon alerts",
    19  		Run:   wrap(alertscmd),
    20  	}
    21  
    22  	stopCmd = &cobra.Command{
    23  		Use:   "stop",
    24  		Short: "Stop the Skynet daemon",
    25  		Long:  "Stop the Skynet daemon.",
    26  		Run:   wrap(stopcmd),
    27  	}
    28  
    29  	globalRatelimitCmd = &cobra.Command{
    30  		Use:   "ratelimit [maxdownloadspeed] [maxuploadspeed]",
    31  		Short: "set the global maxdownloadspeed and maxuploadspeed",
    32  		Long: `Set the global maxdownloadspeed and maxuploadspeed in
    33  Bytes per second: B/s, KB/s, MB/s, GB/s, TB/s
    34  or
    35  Bits per second: Bps, Kbps, Mbps, Gbps, Tbps
    36  Set them to 0 for no limit.`,
    37  		Run: wrap(globalratelimitcmd),
    38  	}
    39  
    40  	profileCmd = &cobra.Command{
    41  		Use:   "profile",
    42  		Short: "Start and stop profiles for the daemon",
    43  		Long:  "Start and stop CPU, memory, and/or trace profiles for the daemon",
    44  		Run:   profilecmd,
    45  	}
    46  
    47  	profileStartCmd = &cobra.Command{
    48  		Use:   "start",
    49  		Short: "Start the profile for the daemon",
    50  		Long: `Start a CPU, memory, and/or trace profile for the daemon by using
    51  the corresponding flag.  Provide a profileDir to save the profiles to.  If no
    52  profileDir is provided the profiles will be saved in the default profile
    53  directory in the siad data directory.`,
    54  		Run: wrap(profilestartcmd),
    55  	}
    56  
    57  	profileStopCmd = &cobra.Command{
    58  		Use:   "stop",
    59  		Short: "Stop profiles for the daemon",
    60  		Long:  "Stop profiles for the daemon",
    61  		Run:   wrap(profilestopcmd),
    62  	}
    63  
    64  	stackCmd = &cobra.Command{
    65  		Use:   "stack",
    66  		Short: "Get current stack trace for the daemon",
    67  		Long:  "Get current stack trace for the daemon",
    68  		Run:   wrap(stackcmd),
    69  	}
    70  
    71  	versionCmd = &cobra.Command{
    72  		Use:   "version",
    73  		Short: "Print version information",
    74  		Long:  "Print version information.",
    75  		Run:   wrap(versioncmd),
    76  	}
    77  )
    78  
    79  // alertscmd prints the alerts from the daemon. This will not print critical
    80  // alerts as critical alerts are printed on every siac command
    81  func alertscmd() {
    82  	al, err := httpClient.DaemonAlertsGet()
    83  	if err != nil {
    84  		fmt.Println("Could not get daemon alerts:", err)
    85  		return
    86  	}
    87  	if len(al.Alerts) == 0 {
    88  		fmt.Println("There are no alerts registered.")
    89  		return
    90  	}
    91  	if len(al.Alerts) == len(al.CriticalAlerts) {
    92  		// Return since critical alerts are already displayed
    93  		return
    94  	}
    95  
    96  	// Print Error alerts
    97  	const maxAlerts = 1000
    98  	remainingAlerts := maxAlerts - len(al.CriticalAlerts)
    99  	if remainingAlerts <= 0 {
   100  		fmt.Println("Only first", maxAlerts, "alerts printed")
   101  		return
   102  	}
   103  	alertsToPrint := remainingAlerts
   104  	if alertsToPrint > len(al.ErrorAlerts) {
   105  		alertsToPrint = len(al.ErrorAlerts)
   106  	}
   107  	printAlerts(al.ErrorAlerts[:alertsToPrint], modules.SeverityError)
   108  
   109  	// Print Warning alerts
   110  	remainingAlerts -= len(al.ErrorAlerts)
   111  	if remainingAlerts <= 0 {
   112  		fmt.Println("Only first", maxAlerts, "alerts printed")
   113  		return
   114  	}
   115  	alertsToPrint = remainingAlerts
   116  	if alertsToPrint > len(al.WarningAlerts) {
   117  		alertsToPrint = len(al.WarningAlerts)
   118  	}
   119  	printAlerts(al.WarningAlerts[:alertsToPrint], modules.SeverityWarning)
   120  
   121  	// Print max alerts message
   122  	if len(al.CriticalAlerts)+len(al.ErrorAlerts)+len(al.WarningAlerts) > maxAlerts {
   123  		fmt.Println("Only first", maxAlerts, "alerts printed")
   124  	}
   125  }
   126  
   127  // profilecmd displays the usage info for the command.
   128  func profilecmd(cmd *cobra.Command, args []string) {
   129  	_ = cmd.UsageFunc()(cmd)
   130  	os.Exit(exitCodeUsage)
   131  }
   132  
   133  // profilestartcmd starts the profile for the daemon.
   134  func profilestartcmd() {
   135  	var profileFlags string
   136  	if daemonCPUProfile {
   137  		profileFlags += "c"
   138  	}
   139  	if daemonMemoryProfile {
   140  		profileFlags += "m"
   141  	}
   142  	if daemonTraceProfile {
   143  		profileFlags += "t"
   144  	}
   145  	if profileFlags == "" {
   146  		die("no profiles submitted")
   147  	}
   148  	err := httpClient.DaemonStartProfilePost(profileFlags, daemonProfileDirectory)
   149  	if err != nil {
   150  		die(err)
   151  	}
   152  	fmt.Println("Profile Started!")
   153  }
   154  
   155  // profilestopcmd stops the profile for the daemon.
   156  func profilestopcmd() {
   157  	err := httpClient.DaemonStopProfilePost()
   158  	if err != nil {
   159  		die(err)
   160  	}
   161  	fmt.Println("Profile Stopped")
   162  }
   163  
   164  // version prints the version of siac and siad.
   165  func versioncmd() {
   166  	fmt.Println("Skynet Client")
   167  	if build.ReleaseTag == "" {
   168  		fmt.Println("\tVersion " + build.NodeVersion)
   169  	} else {
   170  		fmt.Println("\tVersion " + build.NodeVersion + "-" + build.ReleaseTag)
   171  	}
   172  	if build.GitRevision != "" {
   173  		fmt.Println("\tGit Revision " + build.GitRevision)
   174  		fmt.Println("\tBuild Time   " + build.BuildTime)
   175  	}
   176  	dvg, err := httpClient.DaemonVersionGet()
   177  	if err != nil {
   178  		fmt.Println("Could not get daemon version:", err)
   179  		return
   180  	}
   181  	fmt.Println("Skynet daemon")
   182  	fmt.Println("\tVersion " + dvg.Version)
   183  	if build.GitRevision != "" {
   184  		fmt.Println("\tGit Revision " + dvg.GitRevision)
   185  		fmt.Println("\tBuild Time   " + dvg.BuildTime)
   186  	}
   187  }
   188  
   189  // stopcmd is the handler for the command `skyc stop`.
   190  // Stops the daemon.
   191  func stopcmd() {
   192  	err := httpClient.DaemonStopGet()
   193  	if err != nil {
   194  		die("Could not stop daemon:", err)
   195  	}
   196  	fmt.Println("Skynet daemon stopped.")
   197  }
   198  
   199  // stackcmd is the handler for the command `skyc stack` and writes the current
   200  // stack trace to an output file.
   201  func stackcmd() {
   202  	// Get the stack trace
   203  	dsg, err := httpClient.DaemonStackGet()
   204  	if err != nil {
   205  		die("Could not get the stack:", err)
   206  	}
   207  	fmt.Println(dsg.Stack)
   208  	// Create output file
   209  	f, err := os.Create(daemonStackOutputFile)
   210  	if err != nil {
   211  		die("Unable to create output file:", err)
   212  	}
   213  	defer func() {
   214  		if err := f.Close(); err != nil {
   215  			die(err)
   216  		}
   217  	}()
   218  
   219  	// Write stack trace to output file
   220  	_, err = f.Write([]byte(dsg.Stack))
   221  	if err != nil {
   222  		die("Unable to write to output file:", err)
   223  	}
   224  
   225  	fmt.Println("Current stack trace written to:", daemonStackOutputFile)
   226  }
   227  
   228  // globalratelimitcmd is the handler for the command `skyc ratelimit`.
   229  // Sets the global maxuploadspeed and maxdownloadspeed the daemon can use.
   230  func globalratelimitcmd(downloadSpeedStr, uploadSpeedStr string) {
   231  	downloadSpeedInt, err := parseRatelimit(downloadSpeedStr)
   232  	if err != nil {
   233  		die(errors.AddContext(err, "unable to parse download speed"))
   234  	}
   235  	uploadSpeedInt, err := parseRatelimit(uploadSpeedStr)
   236  	if err != nil {
   237  		die(errors.AddContext(err, "unable to parse upload speed"))
   238  	}
   239  	err = httpClient.DaemonGlobalRateLimitPost(downloadSpeedInt, uploadSpeedInt)
   240  	if err != nil {
   241  		die("Could not set global ratelimit speed:", err)
   242  	}
   243  	fmt.Println("Set global maxdownloadspeed to ", downloadSpeedInt, " and maxuploadspeed to ", uploadSpeedInt)
   244  }
   245  
   246  // printAlerts is a helper function to print details of a slice of alerts
   247  // with given severity description to command line
   248  func printAlerts(alerts []modules.Alert, as modules.AlertSeverity) {
   249  	fmt.Printf("\n  There are %v %s alerts\n", len(alerts), as.String())
   250  	for _, a := range alerts {
   251  		fmt.Printf(`
   252  ------------------
   253    Module:   %s
   254    Severity: %s
   255    Message:  %s
   256    Cause:    %s`, a.Module, a.Severity.String(), a.Msg, a.Cause)
   257  	}
   258  	fmt.Printf("\n------------------\n\n")
   259  }