func main() {
	var err error
	flag.Parse()
	if *replicationEnd == 0 {
		*replicationEnd = *replication
	}
	nPeers := *nodes + *delta
	if *delta <= 0 {
		nPeers = *nodes
	}
	peers = make([]*models.PeerInfo, nPeers)
	for i := 0; i < nPeers; i++ {
		u, _ := metadata.MakeOrGetUUID("")
		peers[i] = &models.PeerInfo{
			UUID:        u,
			TotalBlocks: 100 * 1024 * 1024 * 1024, // 100giga-blocks for testing
		}
	}
	blockSize, err = humanize.ParseBytes(*blockSizeStr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error parsing block-size: %s\n", err)
		os.Exit(1)
	}
	totalData, err = humanize.ParseBytes(*totalDataStr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error parsing total-data: %s\n", err)
		os.Exit(1)
	}
	nblocks := totalData / blockSize
	var blocks []torus.BlockRef
	inode := torus.INodeID(1)
	part := float64(*partition) / 100.0
	for len(blocks) < int(nblocks) {
		perFile := rand.Intn(1000) + 1
		f := rand.NormFloat64()
		var out []torus.BlockRef
		if f < part {
			out, inode = generateRewrittenFile(torus.VolumeID(1), inode, perFile)
		} else {
			out, inode = generateLinearFile(torus.VolumeID(1), inode, perFile)
		}
		blocks = append(blocks, out...)
	}
	r1, r2 := createRings()
	fmt.Printf("Unique blocks: %d\n", len(blocks))
	cluster := assignData(blocks, r1)
	fmt.Println("@START *****")
	cluster.printBalance()
	newc, rebalance := cluster.Rebalance(r1, r2)
	fmt.Println("@END *****")
	newc.printBalance()
	fmt.Println("Changes:")
	rebalance.printStats()
}
func (b *blockEtcd) CreateBlockVolume(volume *models.Volume) error {
	new, err := b.AtomicModifyKey([]byte(etcd.MkKey("meta", "volumeminter")), etcd.BytesAddOne)
	volume.Id = new.(uint64)
	if err != nil {
		return err
	}
	vbytes, err := volume.Marshal()
	if err != nil {
		return err
	}
	inodeBytes := torus.NewINodeRef(torus.VolumeID(volume.Id), 1).ToBytes()

	do := b.Etcd.Client.Txn(b.getContext()).If(
		etcdv3.Compare(etcdv3.Version(etcd.MkKey("volumes", volume.Name)), "=", 0),
	).Then(
		etcdv3.OpPut(etcd.MkKey("volumes", volume.Name), string(etcd.Uint64ToBytes(volume.Id))),
		etcdv3.OpPut(etcd.MkKey("volumeid", etcd.Uint64ToHex(volume.Id)), string(vbytes)),
		etcdv3.OpPut(etcd.MkKey("volumemeta", etcd.Uint64ToHex(volume.Id), "inode"), string(etcd.Uint64ToBytes(1))),
		etcdv3.OpPut(etcd.MkKey("volumemeta", etcd.Uint64ToHex(volume.Id), "blockinode"), string(inodeBytes)),
	)
	resp, err := do.Commit()
	if err != nil {
		return err
	}
	if !resp.Succeeded {
		return torus.ErrExists
	}
	return nil
}
func (s *BlockVolume) getOrCreateBlockINode(ref torus.INodeRef) (*models.INode, error) {
	if ref.Volume() != torus.VolumeID(s.volume.Id) {
		panic("ids managed by metadata didn't match, how is that possible?")
	}
	if ref.INode != 1 {
		return s.srv.INodes.GetINode(s.getContext(), ref)
	}
	globals, err := s.mds.GlobalMetadata()
	if err != nil {

	}
	bs, err := blockset.CreateBlocksetFromSpec(globals.DefaultBlockSpec, nil)
	if err != nil {
		return nil, err
	}
	nBlocks := (s.volume.MaxBytes / globals.BlockSize)
	if s.volume.MaxBytes%globals.BlockSize != 0 {
		nBlocks++
	}
	err = bs.Truncate(int(nBlocks), globals.BlockSize)
	if err != nil {
		return nil, err
	}
	inode := models.NewEmptyINode()
	inode.INode = 1
	inode.Volume = s.volume.Id
	inode.Filesize = s.volume.MaxBytes
	inode.Blocks, err = torus.MarshalBlocksetToProto(bs)
	return inode, err
}
func (c *etcdCtx) NewVolumeID() (torus.VolumeID, error) {
	c.etcd.mut.Lock()
	defer c.etcd.mut.Unlock()
	k := []byte(MkKey("meta", "volumeminter"))
	newID, err := c.AtomicModifyKey(k, BytesAddOne)
	if err != nil {
		return 0, err
	}
	return torus.VolumeID(newID.(uint64)), nil
}
func DeleteBlockVolume(mds torus.MetadataService, volume string) error {
	vol, err := mds.GetVolume(volume)
	if err != nil {
		return err
	}
	bmds, err := createBlockMetadata(mds, vol.Name, torus.VolumeID(vol.Id))
	if err != nil {
		return err
	}
	return bmds.DeleteVolume()
}
func (b *blockvolGC) PrepVolume(vol *models.Volume) error {
	if vol.Type != VolumeType {
		return nil
	}
	mds, err := createBlockMetadata(b.srv.MDS, vol.Name, torus.VolumeID(vol.Id))
	if err != nil {
		return err
	}
	curRef, err := mds.GetINode()
	if err != nil {
		return err
	}
	b.highwaters[curRef.Volume()] = 0
	if curRef.INode <= 1 {
		return nil
	}

	snaps, err := mds.GetSnapshots()
	if err != nil {
		return err
	}

	curINodes := make([]torus.INodeRef, 0, len(snaps)+1)
	curINodes = append(curINodes, curRef)
	for _, x := range snaps {
		curINodes = append(curINodes, torus.INodeRefFromBytes(x.INodeRef))
	}

	for _, x := range curINodes {
		inode, err := b.inodes.GetINode(b.getContext(), x)
		if err != nil {
			return err
		}
		set, err := blockset.UnmarshalFromProto(inode.Blocks, nil)
		if err != nil {
			return err
		}
		refs := set.GetAllBlockRefs()
		for _, ref := range refs {
			if ref.IsZero() {
				continue
			}
			if ref.INode > b.highwaters[ref.Volume()] {
				b.highwaters[ref.Volume()] = ref.INode
			}
			b.set[ref] = true
		}
	}
	b.curINodes = append(b.curINodes, curINodes...)
	return nil
}
func (b *blockTempMetadata) CreateBlockVolume(volume *models.Volume) error {
	b.LockData()
	defer b.UnlockData()
	_, ok := b.GetData(fmt.Sprint(volume.Id))
	if ok {
		return torus.ErrExists
	}
	b.CreateVolume(volume)
	b.SetData(fmt.Sprint(volume.Id), &blockTempVolumeData{
		locked: "",
		id:     torus.NewINodeRef(torus.VolumeID(volume.Id), 1),
	})
	return nil
}
func OpenBlockVolume(s *torus.Server, volume string) (*BlockVolume, error) {
	vol, err := s.MDS.GetVolume(volume)
	if err != nil {
		return nil, err
	}
	mds, err := createBlockMetadata(s.MDS, vol.Name, torus.VolumeID(vol.Id))
	if err != nil {
		return nil, err
	}
	return &BlockVolume{
		srv:    s,
		mds:    mds,
		volume: vol,
	}, nil
}
func (c *etcdCtx) GetVolumes() ([]*models.Volume, torus.VolumeID, error) {
	promOps.WithLabelValues("get-volumes").Inc()
	txn := c.etcd.Client.Txn(c.getContext()).Then(
		etcdv3.OpGet(MkKey("meta", "volumeminter")),
		etcdv3.OpGet(MkKey("volumeid"), etcdv3.WithPrefix()),
	)
	resp, err := txn.Commit()
	if err != nil {
		return nil, 0, err
	}
	highwater := BytesToUint64(resp.Responses[0].GetResponseRange().Kvs[0].Value)
	list := resp.Responses[1].GetResponseRange().Kvs
	var out []*models.Volume
	for _, x := range list {
		v := &models.Volume{}
		err := v.Unmarshal(x.Value)
		if err != nil {
			return nil, 0, err
		}
		out = append(out, v)
	}
	return out, torus.VolumeID(highwater), nil
}
func (t *Client) CreateVolume(volume *models.Volume) error {
	t.srv.volIndex[volume.Name] = volume
	t.srv.inode[torus.VolumeID(volume.Id)] = 1
	return nil
}