func ringCmd(r ring.Ring, b *ring.Builder, filename string) error { if b == nil { return fmt.Errorf("only valid for builder files") } r = b.Ring() if err := persist(nil, b, filename); err != nil { return err } return persist(r, nil, strings.TrimSuffix(filename, ".builder")+".ring") }
func removeCmd(r ring.Ring, b *ring.Builder, args []string) error { if r != nil { return fmt.Errorf("cannot remove a node from a ring; use with a builder instead") } if len(args) != 1 || !strings.HasPrefix(args[0], "id=") { return fmt.Errorf("must specify node to remove with id=<value>") } id, err := strconv.ParseUint(args[0][3:], 16, 64) if err != nil { return fmt.Errorf("invalid id %#v", args[0][3:]) } b.RemoveNode(id) return nil }
func tierCmd(r ring.Ring, b *ring.Builder, args []string) error { var tiers [][]string if r == nil { tiers = b.Tiers() } else { tiers = r.Tiers() } report := [][]string{ []string{"Tier", "Existing"}, []string{"Level", "Values"}, } reportOpts := brimtext.NewDefaultAlignOptions() reportOpts.Alignments = []brimtext.Alignment{brimtext.Right, brimtext.Left} fmted := false OUT: for _, values := range tiers { for _, value := range values { if strings.Contains(value, " ") { fmted = true break OUT } } } for level, values := range tiers { sort.Strings(values) var pvalue string if fmted { for _, value := range values { pvalue += fmt.Sprintf(" %#v", value) } pvalue = strings.Trim(pvalue, " ") } else { pvalue = strings.Join(values, " ") } report = append(report, []string{ strconv.Itoa(level), strings.Trim(pvalue, " "), }) } fmt.Print(brimtext.Align(report, reportOpts)) return nil }
func pretendElapsedCmd(r ring.Ring, b *ring.Builder, args []string) error { if b == nil { return fmt.Errorf("only valid for builder files") } if len(args) != 1 { return fmt.Errorf("syntax: <minutes>") } m, err := strconv.Atoi(args[0]) if err != nil { return fmt.Errorf("could not parse %#v: %s", args[0], err.Error()) } if m < 0 { return fmt.Errorf("cannot pretend to go backwards in time") } if m > math.MaxUint16 { return fmt.Errorf("cannot pretend to elapse more than %d minutes", math.MaxUint16) } b.PretendElapsed(uint16(m)) return nil }
func mainCmd(r ring.Ring, b *ring.Builder) error { if r != nil { // TODO: // Version Info (the value as well as the time translation) // Number of tier levels // Replica count // Indication of how risky the assignments are: // Replicas not in distinct tiers, nodes s := r.Stats() report := [][]string{ []string{brimtext.ThousandsSep(int64(s.PartitionCount), ","), "Partitions"}, []string{brimtext.ThousandsSep(int64(s.PartitionBitCount), ","), "Partition Bits"}, []string{brimtext.ThousandsSep(int64(s.NodeCount), ","), "Nodes"}, []string{brimtext.ThousandsSep(int64(s.InactiveNodeCount), ","), "Inactive Nodes"}, []string{brimtext.ThousandsSepU(s.TotalCapacity, ","), "Total Node Capacity"}, []string{fmt.Sprintf("%.02f%%", s.MaxUnderNodePercentage), fmt.Sprintf("Worst Underweight Node (ID %016x)", s.MaxUnderNodeID)}, []string{fmt.Sprintf("%.02f%%", s.MaxOverNodePercentage), fmt.Sprintf("Worst Overweight Node (ID %016x)", s.MaxOverNodeID)}, } reportOpts := brimtext.NewDefaultAlignOptions() reportOpts.Alignments = []brimtext.Alignment{brimtext.Right, brimtext.Left} fmt.Print(brimtext.Align(report, reportOpts)) } if b != nil { // TODO: // Inactive node count // Total capacity report := [][]string{ []string{brimtext.ThousandsSep(int64(len(b.Nodes())), ","), "Nodes"}, []string{brimtext.ThousandsSep(int64(b.ReplicaCount()), ","), "Replicas"}, []string{brimtext.ThousandsSep(int64(b.PointsAllowed()), ","), "Points Allowed"}, []string{brimtext.ThousandsSep(int64(b.MaxPartitionBitCount()), ","), "Max Partition Bits"}, []string{brimtext.ThousandsSep(int64(b.MoveWait()), ","), "Move Wait"}, } reportOpts := brimtext.NewDefaultAlignOptions() reportOpts.Alignments = []brimtext.Alignment{brimtext.Right, brimtext.Left} fmt.Print(brimtext.Align(report, reportOpts)) return nil } return nil }
func persist(r ring.Ring, b *ring.Builder, filename string) error { dir, name := path.Split(filename) if dir == "" { dir = "." } f, err := ioutil.TempFile(dir, name+".") if err != nil { return err } tmp := f.Name() if r != nil { err = r.Persist(f) } else { err = b.Persist(f) } if err != nil { f.Close() return err } if err = f.Close(); err != nil { return err } return os.Rename(path.Join(dir, tmp), filename) }
func addOrSetCmd(r ring.Ring, b *ring.Builder, args []string, n ring.BuilderNode) error { if r != nil { return fmt.Errorf("cannot add a node to ring; use with a builder instead") } active := true capacity := uint32(1) var tiers []string var addresses []string meta := "" for _, arg := range args { sarg := strings.SplitN(arg, "=", 2) if len(sarg) != 2 { return fmt.Errorf(`invalid expression %#v; needs "="`, arg) } if sarg[0] == "" { return fmt.Errorf(`invalid expression %#v; nothing was left of "="`, arg) } if sarg[1] == "" { return fmt.Errorf(`invalid expression %#v; nothing was right of "="`, arg) } switch sarg[0] { case "active": switch sarg[1] { case "true": active = true case "false": active = false default: return fmt.Errorf(`invalid expression %#v; use "true" or "false" for the value of active`, arg) } if n != nil { n.SetActive(active) } case "capacity": c, err := strconv.Atoi(sarg[1]) if err != nil { return fmt.Errorf("invalid expression %#v; %s", arg, err) } if c < 0 { return fmt.Errorf("invalid expression %#v; min is 0", arg) } if c > math.MaxUint32 { return fmt.Errorf("invalid expression %#v; max is %d", arg, math.MaxUint32) } capacity = uint32(c) if n != nil { n.SetCapacity(capacity) } case "meta": meta = sarg[1] if n != nil { n.SetMeta(meta) } default: if strings.HasPrefix(sarg[0], "tier") { level, err := strconv.Atoi(sarg[0][4:]) if err != nil { return fmt.Errorf("invalid expression %#v; %#v doesn't specify a number", arg, sarg[0][4:]) } if level < 0 { return fmt.Errorf("invalid expression %#v; minimum level is 0", arg) } if len(tiers) <= level { t := make([]string, level+1) copy(t, tiers) tiers = t } tiers[level] = sarg[1] if n != nil { n.SetTier(level, tiers[level]) } } else if strings.HasPrefix(sarg[0], "address") { index, err := strconv.Atoi(sarg[0][7:]) if err != nil { return fmt.Errorf("invalid expression %#v; %#v doesn't specify a number", arg, sarg[0][4:]) } if index < 0 { return fmt.Errorf("invalid expression %#v; minimum index is 0", arg) } if len(addresses) <= index { a := make([]string, index+1) copy(a, addresses) addresses = a } addresses[index] = sarg[1] if n != nil { n.SetAddress(index, addresses[index]) } } } } if n == nil { n = b.AddNode(active, capacity, tiers, addresses, meta) report := [][]string{ []string{"ID:", fmt.Sprintf("%016x", n.ID())}, []string{"Active:", fmt.Sprintf("%v", n.Active())}, []string{"Capacity:", fmt.Sprintf("%d", n.Capacity())}, []string{"Tiers:", strings.Join(n.Tiers(), "\n")}, []string{"Addresses:", strings.Join(n.Addresses(), "\n")}, []string{"Meta:", n.Meta()}, } fmt.Print(brimtext.Align(report, nil)) } return nil }
func nodeCmd(r ring.Ring, b *ring.Builder, args []string, full bool) (changed bool, err error) { var nodes ring.NodeSlice if r != nil { nodes = r.Nodes() } else { bnodes := b.Nodes() nodes = make(ring.NodeSlice, len(bnodes)) for i := len(nodes) - 1; i >= 0; i-- { nodes[i] = bnodes[i] } } filterArgs := make([]string, 0) setArgs := make([]string, 0) setFound := false for _, arg := range args { if arg == "set" { setFound = true continue } if setFound { setArgs = append(setArgs, arg) } else { filterArgs = append(filterArgs, arg) } } if len(setArgs) > 0 && b == nil { err = fmt.Errorf("set is only valid for builder files") return } if nodes, err = nodes.Filter(filterArgs); err != nil { return } if len(nodes) > 0 && len(setArgs) > 0 { for _, n := range nodes { if err = addOrSetCmd(r, b, setArgs, n.(ring.BuilderNode)); err != nil { return } changed = true } } if full || len(nodes) == 1 { first := true for _, n := range nodes { if first { first = false } else { fmt.Println() } report := [][]string{ []string{"ID:", fmt.Sprintf("%016x", n.ID())}, []string{"Active:", fmt.Sprintf("%v", n.Active())}, []string{"Capacity:", fmt.Sprintf("%d", n.Capacity())}, []string{"Tiers:", strings.Join(n.Tiers(), "\n")}, []string{"Addresses:", strings.Join(n.Addresses(), "\n")}, []string{"Meta:", n.Meta()}, } fmt.Print(brimtext.Align(report, nil)) } return } header := []string{ "ID", "Active", "Capacity", "Address", "Meta", } report := [][]string{header} reportAlign := brimtext.NewDefaultAlignOptions() reportAlign.Alignments = []brimtext.Alignment{ brimtext.Left, brimtext.Right, brimtext.Right, brimtext.Right, brimtext.Left, } reportLine := func(n ring.Node) []string { return []string{ fmt.Sprintf("%016x", n.ID()), fmt.Sprintf("%v", n.Active()), fmt.Sprintf("%d", n.Capacity()), n.Address(0), n.Meta(), } } for _, n := range nodes { if n.Active() { report = append(report, reportLine(n)) } } for _, n := range nodes { if !n.Active() { report = append(report, reportLine(n)) } } fmt.Print(brimtext.Align(report, reportAlign)) return }