github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/storage/volume.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 "fmt" 8 9 "github.com/juju/cmd" 10 "github.com/juju/errors" 11 "github.com/juju/names" 12 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/cmd/juju/common" 15 "github.com/juju/juju/status" 16 ) 17 18 // VolumeInfo defines the serialization behaviour for storage volume. 19 type VolumeInfo struct { 20 // from params.Volume. This is provider-supplied unique volume id. 21 ProviderVolumeId string `yaml:"provider-id,omitempty" json:"provider-id,omitempty"` 22 23 // Storage is the ID of the storage instance that the volume is 24 // assigned to, if any. 25 Storage string `yaml:"storage,omitempty" json:"storage,omitempty"` 26 27 // Attachments is the set of entities attached to the volume. 28 Attachments *VolumeAttachments `yaml:"attachments,omitempty" json:"attachments,omitempty"` 29 30 // from params.Volume 31 HardwareId string `yaml:"hardware-id,omitempty" json:"hardware-id,omitempty"` 32 33 // from params.Volume 34 Size uint64 `yaml:"size" json:"size"` 35 36 // from params.Volume 37 Persistent bool `yaml:"persistent" json:"persistent"` 38 39 // from params.Volume 40 Status EntityStatus `yaml:"status,omitempty" json:"status,omitempty"` 41 } 42 43 type EntityStatus struct { 44 Current status.Status `json:"current,omitempty" yaml:"current,omitempty"` 45 Message string `json:"message,omitempty" yaml:"message,omitempty"` 46 Since string `json:"since,omitempty" yaml:"since,omitempty"` 47 } 48 49 type VolumeAttachments struct { 50 Machines map[string]MachineVolumeAttachment `yaml:"machines,omitempty" json:"machines,omitempty"` 51 Units map[string]UnitStorageAttachment `yaml:"units,omitempty" json:"units,omitempty"` 52 } 53 54 type MachineVolumeAttachment struct { 55 DeviceName string `yaml:"device,omitempty" json:"device,omitempty"` 56 DeviceLink string `yaml:"device-link,omitempty" json:"device-link,omitempty"` 57 BusAddress string `yaml:"bus-address,omitempty" json:"bus-address,omitempty"` 58 ReadOnly bool `yaml:"read-only" json:"read-only"` 59 // TODO(axw) add machine volume attachment status when we have it 60 } 61 62 //generateListVolumeOutput returns a map of volume info 63 func (c *listCommand) generateListVolumeOutput(ctx *cmd.Context, api StorageListAPI) (output interface{}, err error) { 64 65 results, err := api.ListVolumes(c.ids) 66 if err != nil { 67 return nil, err 68 } 69 // filter out valid output, if any 70 var valid []params.VolumeDetails 71 for _, result := range results { 72 if result.Error == nil { 73 valid = append(valid, result.Result...) 74 continue 75 } 76 // display individual error 77 fmt.Fprintf(ctx.Stderr, "%v\n", result.Error) 78 } 79 if len(valid) == 0 { 80 return nil, nil 81 } 82 info, err := convertToVolumeInfo(valid) 83 if err != nil { 84 return nil, err 85 } 86 switch c.out.Name() { 87 case "yaml", "json": 88 output = map[string]map[string]VolumeInfo{"volumes": info} 89 default: 90 output = info 91 } 92 return output, nil 93 } 94 95 // convertToVolumeInfo returns a map of volume IDs to volume info. 96 func convertToVolumeInfo(all []params.VolumeDetails) (map[string]VolumeInfo, error) { 97 result := make(map[string]VolumeInfo) 98 for _, one := range all { 99 volumeTag, info, err := createVolumeInfo(one) 100 if err != nil { 101 return nil, errors.Trace(err) 102 } 103 result[volumeTag.Id()] = info 104 } 105 return result, nil 106 } 107 108 var idFromTag = func(s string) (string, error) { 109 tag, err := names.ParseTag(s) 110 if err != nil { 111 return "", errors.Annotatef(err, "invalid tag %v", tag) 112 } 113 return tag.Id(), nil 114 } 115 116 func createVolumeInfo(details params.VolumeDetails) (names.VolumeTag, VolumeInfo, error) { 117 volumeTag, err := names.ParseVolumeTag(details.VolumeTag) 118 if err != nil { 119 return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err) 120 } 121 122 var info VolumeInfo 123 info.ProviderVolumeId = details.Info.VolumeId 124 info.HardwareId = details.Info.HardwareId 125 info.Size = details.Info.Size 126 info.Persistent = details.Info.Persistent 127 info.Status = EntityStatus{ 128 details.Status.Status, 129 details.Status.Info, 130 // TODO(axw) we should support formatting as ISO time 131 common.FormatTime(details.Status.Since, false), 132 } 133 134 if len(details.MachineAttachments) > 0 { 135 machineAttachments := make(map[string]MachineVolumeAttachment) 136 for machineTag, attachment := range details.MachineAttachments { 137 machineId, err := idFromTag(machineTag) 138 if err != nil { 139 return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err) 140 } 141 machineAttachments[machineId] = MachineVolumeAttachment{ 142 attachment.DeviceName, 143 attachment.DeviceLink, 144 attachment.BusAddress, 145 attachment.ReadOnly, 146 } 147 } 148 info.Attachments = &VolumeAttachments{ 149 Machines: machineAttachments, 150 } 151 } 152 153 if details.Storage != nil { 154 storageTag, storageInfo, err := createStorageInfo(*details.Storage) 155 if err != nil { 156 return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err) 157 } 158 info.Storage = storageTag.Id() 159 if storageInfo.Attachments != nil { 160 info.Attachments.Units = storageInfo.Attachments.Units 161 } 162 } 163 164 return volumeTag, info, nil 165 }