package topology import ( "fmt" "github.com/chrislusf/seaweedfs/weed/storage/types" "github.com/chrislusf/seaweedfs/weed/util" "sync" "github.com/chrislusf/seaweedfs/weed/pb/master_pb" "github.com/chrislusf/seaweedfs/weed/storage/erasure_coding" "github.com/chrislusf/seaweedfs/weed/storage/needle" "github.com/chrislusf/seaweedfs/weed/storage" ) type Disk struct { NodeImpl volumes map[needle.VolumeId]storage.VolumeInfo ecShards map[needle.VolumeId]*erasure_coding.EcVolumeInfo ecShardsLock sync.RWMutex } func NewDisk(diskType string) *Disk { s := &Disk{} s.id = NodeId(diskType) s.nodeType = "Disk" s.diskUsages = newDiskUsages() s.volumes = make(map[needle.VolumeId]storage.VolumeInfo, 2) s.ecShards = make(map[needle.VolumeId]*erasure_coding.EcVolumeInfo, 2) s.NodeImpl.value = s return s } type DiskUsages struct { sync.RWMutex usages map[types.DiskType]*DiskUsageCounts } func newDiskUsages() *DiskUsages { return &DiskUsages{ usages: make(map[types.DiskType]*DiskUsageCounts), } } func (d *DiskUsages) negative() *DiskUsages { d.RLock() defer d.RUnlock() t := newDiskUsages() for diskType, b := range d.usages { a := t.getOrCreateDisk(diskType) a.volumeCount = -b.volumeCount a.remoteVolumeCount = -b.remoteVolumeCount a.activeVolumeCount = -b.activeVolumeCount a.ecShardCount = -b.ecShardCount a.maxVolumeCount = -b.maxVolumeCount } return t } func (d *DiskUsages) ToMap() interface{} { d.RLock() defer d.RUnlock() ret := make(map[string]interface{}) for diskType, diskUsage := range d.usages { ret[diskType.String()] = diskUsage.ToMap() } return ret } func (d *DiskUsages) ToDiskInfo() map[string]*master_pb.DiskInfo { ret := make(map[string]*master_pb.DiskInfo) for diskType, diskUsageCounts := range d.usages { m := &master_pb.DiskInfo{ VolumeCount: uint64(diskUsageCounts.volumeCount), MaxVolumeCount: uint64(diskUsageCounts.maxVolumeCount), FreeVolumeCount: uint64(diskUsageCounts.maxVolumeCount - diskUsageCounts.volumeCount), ActiveVolumeCount: uint64(diskUsageCounts.activeVolumeCount), RemoteVolumeCount: uint64(diskUsageCounts.remoteVolumeCount), } ret[string(diskType)] = m } return ret } func (d *DiskUsages) FreeSpace() (freeSpace int64) { d.RLock() defer d.RUnlock() for _, diskUsage := range d.usages { freeSpace += diskUsage.FreeSpace() } return } func (d *DiskUsages) GetMaxVolumeCount() (maxVolumeCount int64) { d.RLock() defer d.RUnlock() for _, diskUsage := range d.usages { maxVolumeCount += diskUsage.maxVolumeCount } return } type DiskUsageCounts struct { volumeCount int64 remoteVolumeCount int64 activeVolumeCount int64 ecShardCount int64 maxVolumeCount int64 } func (a *DiskUsageCounts) addDiskUsageCounts(b *DiskUsageCounts) { a.volumeCount += b.volumeCount a.remoteVolumeCount += b.remoteVolumeCount a.activeVolumeCount += b.activeVolumeCount a.ecShardCount += b.ecShardCount a.maxVolumeCount += b.maxVolumeCount } func (a *DiskUsageCounts) FreeSpace() int64 { freeVolumeSlotCount := a.maxVolumeCount + a.remoteVolumeCount - a.volumeCount if a.ecShardCount > 0 { freeVolumeSlotCount = freeVolumeSlotCount - a.ecShardCount/erasure_coding.DataShardsCount - 1 } return freeVolumeSlotCount } func (a *DiskUsageCounts) minus(b *DiskUsageCounts) *DiskUsageCounts { return &DiskUsageCounts{ volumeCount: a.volumeCount - b.volumeCount, remoteVolumeCount: a.remoteVolumeCount - b.remoteVolumeCount, activeVolumeCount: a.activeVolumeCount - b.activeVolumeCount, ecShardCount: a.ecShardCount - b.ecShardCount, maxVolumeCount: a.maxVolumeCount - b.maxVolumeCount, } } func (diskUsage *DiskUsageCounts) ToMap() interface{} { ret := make(map[string]interface{}) ret["Volumes"] = diskUsage.volumeCount ret["EcShards"] = diskUsage.ecShardCount ret["Max"] = diskUsage.maxVolumeCount ret["Free"] = diskUsage.FreeSpace() return ret } func (du *DiskUsages) getOrCreateDisk(diskType types.DiskType) *DiskUsageCounts { du.Lock() defer du.Unlock() t, found := du.usages[diskType] if found { return t } t = &DiskUsageCounts{} du.usages[diskType] = t return t } func (d *Disk) String() string { d.RLock() defer d.RUnlock() return fmt.Sprintf("Disk:%s, volumes:%v, ecShards:%v", d.NodeImpl.String(), d.volumes, d.ecShards) } func (d *Disk) AddOrUpdateVolume(v storage.VolumeInfo) (isNew, isChangedRO bool) { d.Lock() defer d.Unlock() return d.doAddOrUpdateVolume(v) } func (d *Disk) doAddOrUpdateVolume(v storage.VolumeInfo) (isNew, isChangedRO bool) { deltaDiskUsages := newDiskUsages() deltaDiskUsage := deltaDiskUsages.getOrCreateDisk(types.ToDiskType(v.DiskType)) if oldV, ok := d.volumes[v.Id]; !ok { d.volumes[v.Id] = v deltaDiskUsage.volumeCount = 1 if v.IsRemote() { deltaDiskUsage.remoteVolumeCount = 1 } if !v.ReadOnly { deltaDiskUsage.activeVolumeCount = 1 } d.UpAdjustMaxVolumeId(v.Id) d.UpAdjustDiskUsageDelta(deltaDiskUsages) isNew = true } else { if oldV.IsRemote() != v.IsRemote() { if v.IsRemote() { deltaDiskUsage.remoteVolumeCount = 1 } if oldV.IsRemote() { deltaDiskUsage.remoteVolumeCount = -1 } d.UpAdjustDiskUsageDelta(deltaDiskUsages) } isChangedRO = d.volumes[v.Id].ReadOnly != v.ReadOnly d.volumes[v.Id] = v } return } func (d *Disk) GetVolumes() (ret []storage.VolumeInfo) { d.RLock() for _, v := range d.volumes { ret = append(ret, v) } d.RUnlock() return ret } func (d *Disk) GetVolumesById(id needle.VolumeId) (storage.VolumeInfo, error) { d.RLock() defer d.RUnlock() vInfo, ok := d.volumes[id] if ok { return vInfo, nil } else { return storage.VolumeInfo{}, fmt.Errorf("volumeInfo not found") } } func (d *Disk) GetDataCenter() *DataCenter { dn := d.Parent() rack := dn.Parent() dcNode := rack.Parent() dcValue := dcNode.GetValue() return dcValue.(*DataCenter) } func (d *Disk) GetRack() *Rack { return d.Parent().Parent().(*NodeImpl).value.(*Rack) } func (d *Disk) GetTopology() *Topology { p := d.Parent() for p.Parent() != nil { p = p.Parent() } t := p.(*Topology) return t } func (d *Disk) ToMap() interface{} { ret := make(map[string]interface{}) diskUsage := d.diskUsages.getOrCreateDisk(types.ToDiskType(string(d.Id()))) ret["Volumes"] = diskUsage.volumeCount ret["VolumeIds"] = d.GetVolumeIds() ret["EcShards"] = diskUsage.ecShardCount ret["Max"] = diskUsage.maxVolumeCount ret["Free"] = d.FreeSpace() return ret } func (d *Disk) FreeSpace() int64 { t := d.diskUsages.getOrCreateDisk(types.ToDiskType(string(d.Id()))) return t.FreeSpace() } func (d *Disk) ToDiskInfo() *master_pb.DiskInfo { diskUsage := d.diskUsages.getOrCreateDisk(types.ToDiskType(string(d.Id()))) m := &master_pb.DiskInfo{ Type: string(d.Id()), VolumeCount: uint64(diskUsage.volumeCount), MaxVolumeCount: uint64(diskUsage.maxVolumeCount), FreeVolumeCount: uint64(diskUsage.maxVolumeCount - diskUsage.volumeCount), ActiveVolumeCount: uint64(diskUsage.activeVolumeCount), RemoteVolumeCount: uint64(diskUsage.remoteVolumeCount), } for _, v := range d.GetVolumes() { m.VolumeInfos = append(m.VolumeInfos, v.ToVolumeInformationMessage()) } for _, ecv := range d.GetEcShards() { m.EcShardInfos = append(m.EcShardInfos, ecv.ToVolumeEcShardInformationMessage()) } return m } // GetVolumeIds returns the human readable volume ids limited to count of max 100. func (d *Disk) GetVolumeIds() string { d.RLock() defer d.RUnlock() ids := make([]int, 0, len(d.volumes)) for k := range d.volumes { ids = append(ids, int(k)) } return util.HumanReadableIntsMax(100, ids...) }