github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/storage/volumelistformatters.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storage 5 6 import ( 7 "bytes" 8 "fmt" 9 "sort" 10 "strings" 11 "text/tabwriter" 12 13 "github.com/dustin/go-humanize" 14 "github.com/juju/errors" 15 ) 16 17 // formatVolumeListTabular returns a tabular summary of volume instances. 18 func formatVolumeListTabular(value interface{}) ([]byte, error) { 19 infos, ok := value.(map[string]VolumeInfo) 20 if !ok { 21 return nil, errors.Errorf("expected value of type %T, got %T", infos, value) 22 } 23 return formatVolumeListTabularTyped(infos), nil 24 } 25 26 func formatVolumeListTabularTyped(infos map[string]VolumeInfo) []byte { 27 var out bytes.Buffer 28 const ( 29 // To format things into columns. 30 minwidth = 0 31 tabwidth = 1 32 padding = 2 33 padchar = ' ' 34 flags = 0 35 ) 36 tw := tabwriter.NewWriter(&out, minwidth, tabwidth, padding, padchar, flags) 37 38 print := func(values ...string) { 39 fmt.Fprintln(tw, strings.Join(values, "\t")) 40 } 41 print("MACHINE", "UNIT", "STORAGE", "ID", "PROVIDER-ID", "DEVICE", "SIZE", "STATE", "MESSAGE") 42 43 volumeAttachmentInfos := make(volumeAttachmentInfos, 0, len(infos)) 44 for volumeId, info := range infos { 45 volumeAttachmentInfo := volumeAttachmentInfo{ 46 VolumeId: volumeId, 47 VolumeInfo: info, 48 } 49 if info.Attachments == nil { 50 volumeAttachmentInfos = append(volumeAttachmentInfos, volumeAttachmentInfo) 51 continue 52 } 53 // Each unit attachment must have a corresponding volume 54 // attachment. Enumerate each of the volume attachments, 55 // and locate the corresponding unit attachment if any. 56 // Each volume attachment has at most one corresponding 57 // unit attachment. 58 for machineId, machineInfo := range info.Attachments.Machines { 59 volumeAttachmentInfo := volumeAttachmentInfo 60 volumeAttachmentInfo.MachineId = machineId 61 volumeAttachmentInfo.MachineVolumeAttachment = machineInfo 62 for unitId, unitInfo := range info.Attachments.Units { 63 if unitInfo.MachineId == machineId { 64 volumeAttachmentInfo.UnitId = unitId 65 volumeAttachmentInfo.UnitStorageAttachment = unitInfo 66 break 67 } 68 } 69 volumeAttachmentInfos = append(volumeAttachmentInfos, volumeAttachmentInfo) 70 } 71 } 72 sort.Sort(volumeAttachmentInfos) 73 74 for _, info := range volumeAttachmentInfos { 75 var size string 76 if info.Size > 0 { 77 size = humanize.IBytes(info.Size * humanize.MiByte) 78 } 79 print( 80 info.MachineId, info.UnitId, info.Storage, 81 info.VolumeId, info.ProviderVolumeId, 82 info.DeviceName, size, 83 string(info.Status.Current), info.Status.Message, 84 ) 85 } 86 87 tw.Flush() 88 return out.Bytes() 89 } 90 91 type volumeAttachmentInfo struct { 92 VolumeId string 93 VolumeInfo 94 95 MachineId string 96 MachineVolumeAttachment 97 98 UnitId string 99 UnitStorageAttachment 100 } 101 102 type volumeAttachmentInfos []volumeAttachmentInfo 103 104 func (v volumeAttachmentInfos) Len() int { 105 return len(v) 106 } 107 108 func (v volumeAttachmentInfos) Swap(i, j int) { 109 v[i], v[j] = v[j], v[i] 110 } 111 112 func (v volumeAttachmentInfos) Less(i, j int) bool { 113 switch compareStrings(v[i].MachineId, v[j].MachineId) { 114 case -1: 115 return true 116 case 1: 117 return false 118 } 119 120 switch compareSlashSeparated(v[i].UnitId, v[j].UnitId) { 121 case -1: 122 return true 123 case 1: 124 return false 125 } 126 127 switch compareSlashSeparated(v[i].Storage, v[j].Storage) { 128 case -1: 129 return true 130 case 1: 131 return false 132 } 133 134 return v[i].VolumeId < v[j].VolumeId 135 }