github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/cmd/juju/storage/storage.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // The storage command provides a storage management interface, 5 // for manipulating and inspecting storage entities (volumes, 6 // filesystems, charm storage). 7 package storage 8 9 import ( 10 "time" 11 12 "github.com/juju/cmd" 13 "github.com/juju/errors" 14 "github.com/juju/loggo" 15 "github.com/juju/names" 16 17 "github.com/juju/juju/api/storage" 18 "github.com/juju/juju/apiserver/params" 19 "github.com/juju/juju/cmd/envcmd" 20 "github.com/juju/juju/cmd/juju/common" 21 ) 22 23 var logger = loggo.GetLogger("juju.cmd.juju.storage") 24 25 const storageCmdDoc = ` 26 "juju storage" is used to manage storage instances in 27 the Juju environment. 28 ` 29 30 const storageCmdPurpose = "manage storage instances" 31 32 // NewSuperCommand creates the storage supercommand and 33 // registers the subcommands that it supports. 34 func NewSuperCommand() cmd.Command { 35 storagecmd := cmd.NewSuperCommand( 36 cmd.SuperCommandParams{ 37 Name: "storage", 38 Doc: storageCmdDoc, 39 UsagePrefix: "juju", 40 Purpose: storageCmdPurpose, 41 }) 42 storagecmd.Register(envcmd.Wrap(&ShowCommand{})) 43 storagecmd.Register(envcmd.Wrap(&ListCommand{})) 44 storagecmd.Register(envcmd.Wrap(&AddCommand{})) 45 storagecmd.Register(NewPoolSuperCommand()) 46 storagecmd.Register(NewVolumeSuperCommand()) 47 storagecmd.Register(NewFilesystemSuperCommand()) 48 return storagecmd 49 } 50 51 // StorageCommandBase is a helper base structure that has a method to get the 52 // storage managing client. 53 type StorageCommandBase struct { 54 envcmd.EnvCommandBase 55 } 56 57 // NewStorageAPI returns a storage api for the root api endpoint 58 // that the environment command returns. 59 func (c *StorageCommandBase) NewStorageAPI() (*storage.Client, error) { 60 root, err := c.NewAPIRoot() 61 if err != nil { 62 return nil, err 63 } 64 return storage.NewClient(root), nil 65 } 66 67 // StorageInfo defines the serialization behaviour of the storage information. 68 type StorageInfo struct { 69 Kind string `yaml:"kind" json:"kind"` 70 Status EntityStatus `yaml:"status" json:"status"` 71 Persistent bool `yaml:"persistent" json:"persistent"` 72 Attachments *StorageAttachments `yaml:"attachments" json:"attachments"` 73 } 74 75 // StorageAttachments contains details about all attachments to a storage 76 // instance. 77 type StorageAttachments struct { 78 // Units is a mapping from unit ID to unit storage attachment details. 79 Units map[string]UnitStorageAttachment `yaml:"units" json:"units"` 80 } 81 82 // UnitStorageAttachment contains details of a unit storage attachment. 83 type UnitStorageAttachment struct { 84 // MachineId is the ID of the machine that the unit is assigned to. 85 // 86 // This is omitempty to cater for legacy results, where the machine 87 // information is not available. 88 MachineId string `yaml:"machine,omitempty" json:"machine,omitempty"` 89 90 // Location is the location of the storage attachment. 91 Location string `yaml:"location,omitempty" json:"location,omitempty"` 92 93 // TODO(axw) per-unit status when we have it in state. 94 } 95 96 // formatStorageDetails takes a set of StorageDetail and 97 // creates a mapping from storage ID to storage details. 98 func formatStorageDetails(storages []params.StorageDetails) (map[string]StorageInfo, error) { 99 if len(storages) == 0 { 100 return nil, nil 101 } 102 output := make(map[string]StorageInfo) 103 for _, details := range storages { 104 storageTag, storageInfo, err := createStorageInfo(details) 105 if err != nil { 106 return nil, errors.Trace(err) 107 } 108 output[storageTag.Id()] = storageInfo 109 } 110 return output, nil 111 } 112 113 func createStorageInfo(details params.StorageDetails) (names.StorageTag, StorageInfo, error) { 114 storageTag, err := names.ParseStorageTag(details.StorageTag) 115 if err != nil { 116 return names.StorageTag{}, StorageInfo{}, errors.Trace(err) 117 } 118 119 info := StorageInfo{ 120 Kind: details.Kind.String(), 121 Status: EntityStatus{ 122 details.Status.Status, 123 details.Status.Info, 124 // TODO(axw) we should support formatting as ISO time 125 common.FormatTime(details.Status.Since, false), 126 }, 127 Persistent: details.Persistent, 128 } 129 130 if len(details.Attachments) > 0 { 131 unitStorageAttachments := make(map[string]UnitStorageAttachment) 132 for unitTagString, attachmentDetails := range details.Attachments { 133 unitTag, err := names.ParseUnitTag(unitTagString) 134 if err != nil { 135 return names.StorageTag{}, StorageInfo{}, errors.Trace(err) 136 } 137 var machineId string 138 if attachmentDetails.MachineTag != "" { 139 machineTag, err := names.ParseMachineTag(attachmentDetails.MachineTag) 140 if err != nil { 141 return names.StorageTag{}, StorageInfo{}, errors.Trace(err) 142 } 143 machineId = machineTag.Id() 144 } 145 unitStorageAttachments[unitTag.Id()] = UnitStorageAttachment{ 146 machineId, 147 attachmentDetails.Location, 148 } 149 } 150 info.Attachments = &StorageAttachments{unitStorageAttachments} 151 } 152 153 return storageTag, info, nil 154 } 155 156 func storageDetailsFromLegacy(legacy params.LegacyStorageDetails) params.StorageDetails { 157 nowUTC := time.Now().UTC() 158 details := params.StorageDetails{ 159 legacy.StorageTag, 160 legacy.OwnerTag, 161 legacy.Kind, 162 params.EntityStatus{ 163 Status: params.Status(legacy.Status), 164 Since: &nowUTC, 165 }, 166 legacy.Persistent, 167 nil, 168 } 169 if legacy.UnitTag != "" { 170 details.Attachments = map[string]params.StorageAttachmentDetails{ 171 legacy.UnitTag: params.StorageAttachmentDetails{ 172 legacy.StorageTag, 173 legacy.UnitTag, 174 "", // machine is unknown in legacy 175 legacy.Location, 176 }, 177 } 178 } 179 return details 180 }