github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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 "github.com/juju/cmd" 8 "github.com/juju/errors" 9 "github.com/juju/names" 10 11 "github.com/juju/juju/apiserver/params" 12 jujucmd "github.com/juju/juju/cmd" 13 "github.com/juju/juju/cmd/envcmd" 14 "github.com/juju/juju/cmd/juju/common" 15 ) 16 17 const volumeCmdDoc = ` 18 "juju storage volume" is used to manage storage volumes in 19 the Juju environment. 20 ` 21 22 const volumeCmdPurpose = "manage storage volumes" 23 24 // NewVolumeSuperCommand creates the storage volume super subcommand and 25 // registers the subcommands that it supports. 26 func NewVolumeSuperCommand() cmd.Command { 27 poolcmd := jujucmd.NewSubSuperCommand(cmd.SuperCommandParams{ 28 Name: "volume", 29 Doc: volumeCmdDoc, 30 UsagePrefix: "juju storage", 31 Purpose: volumeCmdPurpose, 32 }) 33 poolcmd.Register(envcmd.Wrap(&VolumeListCommand{})) 34 return poolcmd 35 } 36 37 // VolumeCommandBase is a helper base structure for volume commands. 38 type VolumeCommandBase struct { 39 StorageCommandBase 40 } 41 42 // VolumeInfo defines the serialization behaviour for storage volume. 43 type VolumeInfo struct { 44 // from params.Volume. This is provider-supplied unique volume id. 45 VolumeId string `yaml:"id" json:"id"` 46 47 // from params.Volume 48 HardwareId string `yaml:"hardwareid" json:"hardwareid"` 49 50 // from params.Volume 51 Size uint64 `yaml:"size" json:"size"` 52 53 // from params.Volume 54 Persistent bool `yaml:"persistent" json:"persistent"` 55 56 // from params.VolumeAttachments 57 DeviceName string `yaml:"device,omitempty" json:"device,omitempty"` 58 59 // from params.VolumeAttachments 60 ReadOnly bool `yaml:"read-only" json:"read-only"` 61 62 // from params.Volume. This is juju volume id. 63 Volume string `yaml:"volume,omitempty" json:"volume,omitempty"` 64 65 // from params.Volume. 66 Status EntityStatus `yaml:"status,omitempty" json:"status,omitempty"` 67 } 68 69 type EntityStatus struct { 70 Current params.Status `json:"current,omitempty" yaml:"current,omitempty"` 71 Message string `json:"message,omitempty" yaml:"message,omitempty"` 72 Since string `json:"since,omitempty" yaml:"since,omitempty"` 73 } 74 75 // convertToVolumeInfo returns map of maps with volume info 76 // keyed first on machine_id and then on volume_id. 77 func convertToVolumeInfo(all []params.VolumeItem) (map[string]map[string]map[string]VolumeInfo, error) { 78 result := map[string]map[string]map[string]VolumeInfo{} 79 for _, one := range all { 80 if err := convertVolumeItem(one, result); err != nil { 81 return nil, errors.Trace(err) 82 } 83 } 84 return result, nil 85 } 86 87 func convertVolumeItem(item params.VolumeItem, all map[string]map[string]map[string]VolumeInfo) error { 88 if len(item.Attachments) != 0 { 89 // add info for volume attachments 90 return convertVolumeAttachments(item, all) 91 } 92 unattached, unit, storage := createInfo(item.Volume) 93 addOneToAll("unattached", unit, storage, unattached, all) 94 return nil 95 } 96 97 var idFromTag = func(s string) (string, error) { 98 tag, err := names.ParseTag(s) 99 if err != nil { 100 return "", errors.Annotatef(err, "invalid tag %v", tag) 101 } 102 return tag.Id(), nil 103 } 104 105 func convertVolumeAttachments(item params.VolumeItem, all map[string]map[string]map[string]VolumeInfo) error { 106 for _, one := range item.Attachments { 107 machine, err := idFromTag(one.MachineTag) 108 if err != nil { 109 return errors.Trace(err) 110 } 111 info, unit, storage := createInfo(item.Volume) 112 info.DeviceName = one.Info.DeviceName 113 info.ReadOnly = one.Info.ReadOnly 114 115 addOneToAll(machine, unit, storage, info, all) 116 } 117 return nil 118 } 119 120 func addOneToAll(machineId, unitId, storageId string, item VolumeInfo, all map[string]map[string]map[string]VolumeInfo) { 121 machineVolumes, ok := all[machineId] 122 if !ok { 123 machineVolumes = map[string]map[string]VolumeInfo{} 124 all[machineId] = machineVolumes 125 } 126 unitVolumes, ok := machineVolumes[unitId] 127 if !ok { 128 unitVolumes = map[string]VolumeInfo{} 129 machineVolumes[unitId] = unitVolumes 130 } 131 unitVolumes[storageId] = item 132 } 133 134 func createInfo(volume params.VolumeInstance) (info VolumeInfo, unit, storage string) { 135 info.VolumeId = volume.VolumeId 136 info.HardwareId = volume.HardwareId 137 info.Size = volume.Size 138 info.Persistent = volume.Persistent 139 info.Status = EntityStatus{ 140 volume.Status.Status, 141 volume.Status.Info, 142 // TODO(axw) we should support formatting as ISO time 143 common.FormatTime(volume.Status.Since, false), 144 } 145 146 if v, err := idFromTag(volume.VolumeTag); err == nil { 147 info.Volume = v 148 } 149 var err error 150 if storage, err = idFromTag(volume.StorageTag); err != nil { 151 storage = "unassigned" 152 } 153 if unit, err = idFromTag(volume.UnitTag); err != nil { 154 unit = "unattached" 155 } 156 return 157 }