github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 "gopkg.in/juju/names.v2" 12 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/cmd/juju/common" 15 "github.com/juju/juju/core/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 // Pool is the name of the storage pool that the volume came from. 31 Pool string `yaml:"pool,omitempty" json:"pool,omitempty"` 32 33 // from params.Volume 34 HardwareId string `yaml:"hardware-id,omitempty" json:"hardware-id,omitempty"` 35 36 // from params.Volume 37 WWN string `yaml:"wwn,omitempty" json:"wwn,omitempty"` 38 39 // from params.Volume 40 Size uint64 `yaml:"size" json:"size"` 41 42 // from params.Volume 43 Persistent bool `yaml:"persistent" json:"persistent"` 44 45 // Life is the lifecycle state of the volume. 46 Life string `yaml:"life,omitempty" json:"life,omitempty"` 47 48 // from params.Volume 49 Status EntityStatus `yaml:"status,omitempty" json:"status,omitempty"` 50 } 51 52 type EntityStatus struct { 53 Current status.Status `json:"current,omitempty" yaml:"current,omitempty"` 54 Message string `json:"message,omitempty" yaml:"message,omitempty"` 55 Since string `json:"since,omitempty" yaml:"since,omitempty"` 56 } 57 58 type VolumeAttachments struct { 59 Machines map[string]VolumeAttachment `yaml:"machines,omitempty" json:"machines,omitempty"` 60 Containers map[string]VolumeAttachment `yaml:"containers,omitempty" json:"containers,omitempty"` 61 Units map[string]UnitStorageAttachment `yaml:"units,omitempty" json:"units,omitempty"` 62 } 63 64 type VolumeAttachment struct { 65 DeviceName string `yaml:"device,omitempty" json:"device,omitempty"` 66 DeviceLink string `yaml:"device-link,omitempty" json:"device-link,omitempty"` 67 BusAddress string `yaml:"bus-address,omitempty" json:"bus-address,omitempty"` 68 ReadOnly bool `yaml:"read-only" json:"read-only"` 69 Life string `yaml:"life,omitempty" json:"life,omitempty"` 70 // TODO(axw) add machine volume attachment status when we have it 71 } 72 73 //generateListVolumeOutput returns a map of volume info 74 func generateListVolumeOutput(ctx *cmd.Context, api StorageListAPI, ids []string) (map[string]VolumeInfo, error) { 75 76 results, err := api.ListVolumes(ids) 77 if err != nil { 78 return nil, errors.Trace(err) 79 } 80 // filter out valid output, if any 81 var valid []params.VolumeDetails 82 for _, result := range results { 83 if result.Error == nil { 84 valid = append(valid, result.Result...) 85 continue 86 } 87 // display individual error 88 fmt.Fprintf(ctx.Stderr, "%v\n", result.Error) 89 } 90 if len(valid) == 0 { 91 return nil, nil 92 } 93 return convertToVolumeInfo(valid) 94 } 95 96 // convertToVolumeInfo returns a map of volume IDs to volume info. 97 func convertToVolumeInfo(all []params.VolumeDetails) (map[string]VolumeInfo, error) { 98 result := make(map[string]VolumeInfo) 99 for _, one := range all { 100 volumeTag, info, err := createVolumeInfo(one) 101 if err != nil { 102 return nil, errors.Trace(err) 103 } 104 result[volumeTag.Id()] = info 105 } 106 return result, nil 107 } 108 109 var idFromTag = func(s string) (string, error) { 110 tag, err := names.ParseTag(s) 111 if err != nil { 112 return "", errors.Annotatef(err, "invalid tag %v", tag) 113 } 114 return tag.Id(), nil 115 } 116 117 func createVolumeInfo(details params.VolumeDetails) (names.VolumeTag, VolumeInfo, error) { 118 volumeTag, err := names.ParseVolumeTag(details.VolumeTag) 119 if err != nil { 120 return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err) 121 } 122 123 var info VolumeInfo 124 info.ProviderVolumeId = details.Info.VolumeId 125 info.HardwareId = details.Info.HardwareId 126 info.WWN = details.Info.WWN 127 info.Pool = details.Info.Pool 128 info.Size = details.Info.Size 129 info.Persistent = details.Info.Persistent 130 info.Life = string(details.Life) 131 info.Status = EntityStatus{ 132 details.Status.Status, 133 details.Status.Info, 134 // TODO(axw) we should support formatting as ISO time 135 common.FormatTime(details.Status.Since, false), 136 } 137 138 attachmentsFromDetails := func( 139 in map[string]params.VolumeAttachmentDetails, 140 out map[string]VolumeAttachment, 141 ) error { 142 for tag, attachment := range in { 143 id, err := idFromTag(tag) 144 if err != nil { 145 return errors.Trace(err) 146 } 147 out[id] = VolumeAttachment{ 148 attachment.DeviceName, 149 attachment.DeviceLink, 150 attachment.BusAddress, 151 attachment.ReadOnly, 152 string(attachment.Life), 153 } 154 } 155 return nil 156 } 157 158 if len(details.MachineAttachments) > 0 { 159 machineAttachments := make(map[string]VolumeAttachment) 160 if err := attachmentsFromDetails(details.MachineAttachments, machineAttachments); err != nil { 161 return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err) 162 } 163 info.Attachments = &VolumeAttachments{ 164 Machines: machineAttachments, 165 } 166 } 167 168 if len(details.UnitAttachments) > 0 { 169 unitAttachments := make(map[string]VolumeAttachment) 170 if err := attachmentsFromDetails(details.UnitAttachments, unitAttachments); err != nil { 171 return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err) 172 } 173 if info.Attachments == nil { 174 info.Attachments = &VolumeAttachments{} 175 } 176 info.Attachments.Containers = unitAttachments 177 } 178 179 if details.Storage != nil { 180 storageTag, storageInfo, err := createStorageInfo(*details.Storage) 181 if err != nil { 182 return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err) 183 } 184 info.Storage = storageTag.Id() 185 if storageInfo.Attachments != nil { 186 if info.Attachments == nil { 187 info.Attachments = &VolumeAttachments{} 188 } 189 info.Attachments.Units = storageInfo.Attachments.Units 190 } 191 } 192 193 return volumeTag, info, nil 194 }