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 }