github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/siac/hostcmd.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"os"
     7  	"text/tabwriter"
     8  
     9  	"github.com/NebulousLabs/Sia/api"
    10  	"github.com/NebulousLabs/Sia/modules"
    11  	"github.com/NebulousLabs/Sia/types"
    12  
    13  	"github.com/spf13/cobra"
    14  )
    15  
    16  var (
    17  	hostCmd = &cobra.Command{
    18  		Use:   "host",
    19  		Short: "Perform host actions",
    20  		Long:  "View or modify host settings.",
    21  		Run:   wrap(hostcmd),
    22  	}
    23  
    24  	hostConfigCmd = &cobra.Command{
    25  		Use:   "config [setting] [value]",
    26  		Short: "Modify host settings",
    27  		Long: `Modify host settings.
    28  Available settings:
    29  
    30  Parameter                        Unit
    31  
    32  acceptingcontracts               boolean
    33  collateral                       currency/TB
    34  collateralbudget                 currency
    35  maxcollateral                    currency
    36  maxdownloadbatchsize             int
    37  maxduration                      int
    38  maxrevisebatchsize               int
    39  minimumcontractprice             currency
    40  minimumdownloadbandwidthprice    currency/TB
    41  minimumstorageprice              currency/TB/month
    42  minimumuploadbandwidthprice      currency/TB
    43  netaddress                       string
    44  windowsize                       int
    45  
    46  Currency units can be specified, e.g. 10SC; run 'siac help wallet' for details.
    47  
    48  For a description of each parameter, see doc/API.md.
    49  
    50  To configure the host to accept new contracts, set acceptingcontracts to true:
    51  	siac host config acceptingcontracts true
    52  `,
    53  		Run: wrap(hostconfigcmd),
    54  	}
    55  
    56  	hostAnnounceCmd = &cobra.Command{
    57  		Use:   "announce",
    58  		Short: "Announce yourself as a host",
    59  		Long: `Announce yourself as a host on the network.
    60  Announcing will also configure the host to start accepting contracts.
    61  You can revert this by running:
    62  	siac host config acceptingcontracts false
    63  You may also supply a specific address to be announced, e.g.:
    64  	siac host announce my-host-domain.com:9001
    65  Doing so will override the standard connectivity checks.`,
    66  		Run: hostannouncecmd,
    67  	}
    68  
    69  	hostFolderCmd = &cobra.Command{
    70  		Use:   "folder",
    71  		Short: "Add, remove, or resize a storage folder",
    72  		Long:  "Add, remove, or resize a storage folder.",
    73  	}
    74  
    75  	hostFolderAddCmd = &cobra.Command{
    76  		Use:   "add [path] [size]",
    77  		Short: "Add a storage folder to the host",
    78  		Long:  "Add a storage folder to the host, specifying how much data it should store",
    79  		Run:   wrap(hostfolderaddcmd),
    80  	}
    81  
    82  	hostFolderRemoveCmd = &cobra.Command{
    83  		Use:   "remove [path]",
    84  		Short: "Remove a storage folder from the host",
    85  		Long: `Remove a storage folder from the host. Note that this does not delete any
    86  data; it will instead be distributed across the remaining storage folders.`,
    87  
    88  		Run: wrap(hostfolderremovecmd),
    89  	}
    90  
    91  	hostFolderResizeCmd = &cobra.Command{
    92  		Use:   "resize [path] [size]",
    93  		Short: "Resize a storage folder",
    94  		Long: `Change how much data a storage folder should store. If the new size is less
    95  than what the folder is currently storing, data will be distributed across the
    96  other storage folders.`,
    97  		Run: wrap(hostfolderresizecmd),
    98  	}
    99  
   100  	hostSectorCmd = &cobra.Command{
   101  		Use:   "sector",
   102  		Short: "Add or delete a sector (add not supported)",
   103  		Long: `Add or delete a sector. Adding is not currently supported. Note that
   104  deleting a sector may impact host revenue.`,
   105  	}
   106  
   107  	hostSectorDeleteCmd = &cobra.Command{
   108  		Use:   "delete [root]",
   109  		Short: "Delete a sector",
   110  		Long: `Delete a sector, identified by its Merkle root. Note that deleting a
   111  sector may impact host revenue.`,
   112  		Run: wrap(hostsectordeletecmd),
   113  	}
   114  )
   115  
   116  // hostcmd is the handler for the command `siac host`.
   117  // Prints info about the host and its storage folders.
   118  func hostcmd() {
   119  	hg := new(api.HostGET)
   120  	err := getAPI("/host", &hg)
   121  	if err != nil {
   122  		die("Could not fetch host settings:", err)
   123  	}
   124  	sg := new(api.StorageGET)
   125  	err = getAPI("/storage", &sg)
   126  	if err != nil {
   127  		die("Could not fetch storage info:", err)
   128  	}
   129  
   130  	es := hg.ExternalSettings
   131  	fm := hg.FinancialMetrics
   132  	is := hg.InternalSettings
   133  	nm := hg.NetworkMetrics
   134  
   135  	// calculate total storage available and remaining
   136  	var totalstorage, storageremaining uint64
   137  	for _, folder := range sg.StorageFolderMetadata {
   138  		totalstorage += folder.Capacity
   139  		storageremaining += folder.CapacityRemaining
   140  	}
   141  
   142  	// convert accepting bool
   143  	accept := yesNo(is.AcceptingContracts)
   144  	// convert price from bytes/block to TB/Month
   145  	price := currencyUnits(is.MinimumStoragePrice.Mul(modules.BlockBytesPerMonthTerabyte))
   146  	// calculate total revenue
   147  	totalRevenue := fm.ContractCompensation.
   148  		Add(fm.StorageRevenue).
   149  		Add(fm.DownloadBandwidthRevenue).
   150  		Add(fm.UploadBandwidthRevenue)
   151  	totalPotentialRevenue := fm.PotentialContractCompensation.
   152  		Add(fm.PotentialStorageRevenue).
   153  		Add(fm.PotentialDownloadBandwidthRevenue).
   154  		Add(fm.PotentialUploadBandwidthRevenue)
   155  	fmt.Printf(`Host info:
   156  	Storage:      %v (%v used)
   157  	Price:        %v / TB / Month
   158  	Max Duration: %v Blocks
   159  
   160  	Accepting Contracts: %v
   161  	Anticipated Revenue: %v
   162  	Locked Collateral:   %v
   163  	Risked Collateral:   %v
   164  	Revenue:             %v
   165  	Lost Revenue:        %v
   166  	Lost Collateral:     %v
   167  `, filesizeUnits(int64(totalstorage)), filesizeUnits(int64(totalstorage-storageremaining)),
   168  		price, is.MaxDuration, accept, currencyUnits(totalPotentialRevenue),
   169  		currencyUnits(fm.LockedStorageCollateral), currencyUnits(fm.RiskedStorageCollateral),
   170  		currencyUnits(totalRevenue), currencyUnits(fm.LostRevenue),
   171  		currencyUnits(fm.LostStorageCollateral))
   172  
   173  	// display more info if verbose flag is set
   174  	if hostVerbose {
   175  		// describe net address
   176  		netaddr := es.NetAddress
   177  		if is.NetAddress == "" {
   178  			netaddr += " (automatically determined)"
   179  		} else {
   180  			netaddr += " (manually specified)"
   181  		}
   182  		fmt.Printf(`
   183  	Net Address: %v
   184  
   185  RPC Stats:
   186  	Error Calls:        %v
   187  	Unrecognized Calls: %v
   188  	Download Calls:     %v
   189  	Renew Calls:        %v
   190  	Revise Calls:       %v
   191  	Settings Calls:     %v
   192  	FormContract Calls: %v
   193  `, netaddr, nm.ErrorCalls, nm.UnrecognizedCalls, nm.DownloadCalls,
   194  			nm.RenewCalls, nm.ReviseCalls, nm.SettingsCalls, nm.FormContractCalls)
   195  	}
   196  
   197  	fmt.Println("\nStorage Folders:")
   198  
   199  	// display storage folder info
   200  	if len(sg.StorageFolderMetadata) == 0 {
   201  		fmt.Println("No storage folders configured")
   202  		return
   203  	}
   204  	w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
   205  	fmt.Fprintf(w, "\tUsed\tCapacity\t%% Used\tPath\n")
   206  	for _, folder := range sg.StorageFolderMetadata {
   207  		curSize := int64(folder.Capacity - folder.CapacityRemaining)
   208  		pctUsed := 100 * (float64(curSize) / float64(folder.Capacity))
   209  		fmt.Fprintf(w, "\t%s\t%s\t%.2f\t%s\n", filesizeUnits(curSize), filesizeUnits(int64(folder.Capacity)), pctUsed, folder.Path)
   210  	}
   211  	w.Flush()
   212  }
   213  
   214  // hostconfigcmd is the handler for the command `siac host config [setting] [value]`.
   215  // Modifies host settings.
   216  func hostconfigcmd(param, value string) {
   217  	switch param {
   218  	// currency (convert to hastings)
   219  	case "collateralbudget", "maxcollateral", "minimumcontractprice":
   220  		hastings, err := parseCurrency(value)
   221  		if err != nil {
   222  			die("Could not parse "+param+":", err)
   223  		}
   224  		value = hastings
   225  
   226  	// currency/TB (convert to hastings/byte)
   227  	case "collateral", "minimumdownloadbandwidthprice", "minimumuploadbandwidthprice":
   228  		hastings, err := parseCurrency(value)
   229  		if err != nil {
   230  			die("Could not parse "+param+":", err)
   231  		}
   232  		i, _ := new(big.Int).SetString(hastings, 10)
   233  		c := types.NewCurrency(i).Div(modules.BytesPerTerabyte)
   234  		value = c.String()
   235  
   236  	// currency/TB/month (convert to hastings/byte/block)
   237  	case "minimumstorageprice":
   238  		hastings, err := parseCurrency(value)
   239  		if err != nil {
   240  			die("Could not parse "+param+":", err)
   241  		}
   242  		i, _ := new(big.Int).SetString(hastings, 10)
   243  		c := types.NewCurrency(i).Div(modules.BlockBytesPerMonthTerabyte)
   244  		value = c.String()
   245  
   246  	// other valid settings
   247  	case "acceptingcontracts", "maxdownloadbatchsize", "maxduration",
   248  		"maxrevisebatchsize", "netaddress", "windowsize":
   249  
   250  	// invalid settings
   251  	default:
   252  		die("\"" + param + "\" is not a host setting")
   253  	}
   254  	err := post("/host", param+"="+value)
   255  	if err != nil {
   256  		die("Could not update host settings:", err)
   257  	}
   258  	fmt.Println("Host settings updated.")
   259  }
   260  
   261  // hostannouncecmd is the handler for the command `siac host announce`.
   262  // Announces yourself as a host to the network. Optionally takes an address to
   263  // announce as.
   264  func hostannouncecmd(cmd *cobra.Command, args []string) {
   265  	var err error
   266  	switch len(args) {
   267  	case 0:
   268  		err = post("/host/announce", "")
   269  	case 1:
   270  		err = post("/host/announce", "netaddress="+args[0])
   271  	default:
   272  		cmd.Usage()
   273  		os.Exit(exitCodeUsage)
   274  	}
   275  	if err != nil {
   276  		die("Could not announce host:", err)
   277  	}
   278  	fmt.Println("Host announcement submitted to network.")
   279  
   280  	// start accepting contracts
   281  	err = post("/host", "acceptingcontracts=true")
   282  	if err != nil {
   283  		die("Could not configure host to accept contracts:", err)
   284  	}
   285  	fmt.Println(`
   286  The host has also been configured to accept contracts.
   287  To revert this, run:
   288  	siac host config acceptingcontracts false
   289  `)
   290  }
   291  
   292  // hostfolderaddcmd adds a folder to the host.
   293  func hostfolderaddcmd(path, size string) {
   294  	size, err := parseFilesize(size)
   295  	if err != nil {
   296  		die("Could not parse size:", err)
   297  	}
   298  	err = post("/storage/folders/add", fmt.Sprintf("path=%s&size=%s", abs(path), size))
   299  	if err != nil {
   300  		die("Could not add folder:", err)
   301  	}
   302  	fmt.Println("Added folder", path)
   303  }
   304  
   305  // hostfolderremovecmd removes a folder from the host.
   306  func hostfolderremovecmd(path string) {
   307  	err := post("/storage/folders/remove", "path="+abs(path))
   308  	if err != nil {
   309  		die("Could not remove folder:", err)
   310  	}
   311  	fmt.Println("Removed folder", path)
   312  }
   313  
   314  // hostfolderresizecmd resizes a folder in the host.
   315  func hostfolderresizecmd(path, newsize string) {
   316  	newsize, err := parseFilesize(newsize)
   317  	if err != nil {
   318  		die("Could not parse size:", err)
   319  	}
   320  	err = post("/storage/folders/resize", fmt.Sprintf("path=%s&newsize=%s", abs(path), newsize))
   321  	if err != nil {
   322  		die("Could not resize folder:", err)
   323  	}
   324  	fmt.Printf("Resized folder %v to %v\n", path, newsize)
   325  }
   326  
   327  // hostsectordeletecmd deletes a sector from the host.
   328  func hostsectordeletecmd(root string) {
   329  	err := post("/storage/sectors/delete/"+root, "")
   330  	if err != nil {
   331  		die("Could not delete sector:", err)
   332  	}
   333  	fmt.Println("Deleted sector", root)
   334  }