diff --git a/weed/shell/command_volume_list.go b/weed/shell/command_volume_list.go index a8ddb4c54..2cf6829c2 100644 --- a/weed/shell/command_volume_list.go +++ b/weed/shell/command_volume_list.go @@ -224,12 +224,17 @@ func (c *commandVolumeList) writeDataCenterInfo(writer io.Writer, t *master_pb.D return strings.Compare(a.Id, b.Id) }) dataCenterInfoFound := false + dataCenterHeaderPrinted := false for _, r := range t.RackInfos { if *c.rack != "" && *c.rack != r.Id { continue } s.add(c.writeRackInfo(writer, r, verbosityLevel, func() { + if dataCenterHeaderPrinted { + return + } output(verbosityLevel >= 1, writer, " DataCenter %s%s\n", t.Id, diskInfosToString(t.DiskInfos)) + dataCenterHeaderPrinted = true })) if !dataCenterInfoFound && !s.isEmpty() { dataCenterInfoFound = true @@ -245,13 +250,18 @@ func (c *commandVolumeList) writeRackInfo(writer io.Writer, t *master_pb.RackInf return strings.Compare(a.Id, b.Id) }) rackInfoFound := false + rackHeaderPrinted := false for _, dn := range t.DataNodeInfos { if *c.dataNode != "" && *c.dataNode != dn.Id { continue } s.add(c.writeDataNodeInfo(writer, dn, verbosityLevel, func() { outCenterInfo() + if rackHeaderPrinted { + return + } output(verbosityLevel >= 2, writer, " Rack %s%s\n", t.Id, diskInfosToString(t.DiskInfos)) + rackHeaderPrinted = true })) if !rackInfoFound && !s.isEmpty() { rackInfoFound = true diff --git a/weed/shell/command_volume_list_test.go b/weed/shell/command_volume_list_test.go index 757de239e..be211787d 100644 --- a/weed/shell/command_volume_list_test.go +++ b/weed/shell/command_volume_list_test.go @@ -194,6 +194,34 @@ func TestWriteDataNodeInfo_SplitsCollapsedDisksByPhysicalDiskId(t *testing.T) { } } +func TestWriteTopologyInfo_PrintsParentHeadersOnce(t *testing.T) { + topo := topoFromNodes( + volNode("node1:8081", &master_pb.VolumeInformationMessage{Id: 1, Collection: "c"}), + volNode("node2:8081", &master_pb.VolumeInformationMessage{Id: 2, Collection: "c"}), + ) + topo.DiskInfos = map[string]*master_pb.DiskInfo{"hdd": {Type: "hdd"}} + topo.DataCenterInfos[0].DiskInfos = map[string]*master_pb.DiskInfo{"hdd": {Type: "hdd"}} + topo.DataCenterInfos[0].RackInfos[0].DiskInfos = map[string]*master_pb.DiskInfo{"hdd": {Type: "hdd"}} + + c := &commandVolumeList{} + fs := flag.NewFlagSet("volume.list", flag.ContinueOnError) + c.collectionPattern = fs.String("collection", "", "") + c.dataCenter = fs.String("dataCenter", "", "") + c.rack = fs.String("rack", "", "") + c.dataNode = fs.String("dataNode", "", "") + c.readonly = fs.Bool("readonly", false, "") + c.writable = fs.Bool("writable", false, "") + c.volumeId = fs.Uint64("volumeId", 0, "") + + var dcBuf bytes.Buffer + c.writeTopologyInfo(&dcBuf, topo, 30000, 1) + assert.Equal(t, 1, strings.Count(dcBuf.String(), " DataCenter dc1 hdd(")) + + var rackBuf bytes.Buffer + c.writeTopologyInfo(&rackBuf, topo, 30000, 2) + assert.Equal(t, 1, strings.Count(rackBuf.String(), " Rack rack1 hdd(")) +} + // volNode builds a single-node topology from (volumeId, collection) pairs so the // duplicate-detection tests can describe a cluster compactly. func volNode(nodeId string, volumes ...*master_pb.VolumeInformationMessage) *master_pb.DataNodeInfo {