github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/storage/list.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 "io" 9 10 "github.com/juju/cmd" 11 "github.com/juju/errors" 12 "github.com/juju/gnuflag" 13 14 "github.com/juju/juju/apiserver/params" 15 jujucmd "github.com/juju/juju/cmd" 16 "github.com/juju/juju/cmd/modelcmd" 17 ) 18 19 // NewListCommand returns a command for listing storage instances. 20 func NewListCommand() cmd.Command { 21 cmd := &listCommand{} 22 cmd.newAPIFunc = func() (StorageListAPI, error) { 23 return cmd.NewStorageAPI() 24 } 25 return modelcmd.Wrap(cmd) 26 } 27 28 const listCommandDoc = ` 29 List information about storage. 30 ` 31 32 // listCommand returns storage instances. 33 type listCommand struct { 34 StorageCommandBase 35 out cmd.Output 36 ids []string 37 filesystem bool 38 volume bool 39 newAPIFunc func() (StorageListAPI, error) 40 } 41 42 // Info implements Command.Info. 43 func (c *listCommand) Info() *cmd.Info { 44 return jujucmd.Info(&cmd.Info{ 45 Name: "storage", 46 Args: "<filesystem|volume> ...", 47 Purpose: "Lists storage details.", 48 Doc: listCommandDoc, 49 Aliases: []string{"list-storage"}, 50 }) 51 } 52 53 // SetFlags implements Command.SetFlags. 54 func (c *listCommand) SetFlags(f *gnuflag.FlagSet) { 55 c.StorageCommandBase.SetFlags(f) 56 c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{ 57 "yaml": cmd.FormatYaml, 58 "json": cmd.FormatJson, 59 "tabular": formatListTabularOne, 60 }) 61 // TODO(axw) deprecate these flags, and introduce separate commands 62 // for listing just filesystems or volumes. 63 f.BoolVar(&c.filesystem, "filesystem", false, "List filesystem storage") 64 f.BoolVar(&c.volume, "volume", false, "List volume storage") 65 } 66 67 // Init implements Command.Init. 68 func (c *listCommand) Init(args []string) (err error) { 69 if c.filesystem && c.volume { 70 return errors.New("--filesystem and --volume can not be used together") 71 } 72 if len(args) > 0 && !c.filesystem && !c.volume { 73 return errors.New("specifying IDs only supported with --filesystem and --volume options") 74 } 75 c.ids = args 76 return nil 77 } 78 79 // Run implements Command.Run. 80 func (c *listCommand) Run(ctx *cmd.Context) (err error) { 81 api, err := c.newAPIFunc() 82 if err != nil { 83 return err 84 } 85 defer api.Close() 86 87 params := GetCombinedStorageInfoParams{ 88 Context: ctx, APIClient: api, Ids: c.ids, 89 } 90 switch { 91 case c.filesystem: 92 params.WantFilesystems = true 93 case c.volume: 94 params.WantVolumes = true 95 default: 96 params.WantStorage = true 97 params.WantVolumes = true 98 params.WantFilesystems = true 99 } 100 101 combined, err := GetCombinedStorageInfo(params) 102 if err != nil { 103 return err 104 } 105 if combined.empty() { 106 if c.out.Name() == "tabular" { 107 ctx.Infof("No storage to display.") 108 } 109 return nil 110 } 111 return c.out.Write(ctx, *combined) 112 } 113 114 // GetCombinedStorageInfoParams holds parameters for the GetCombinedStorageInfo call. 115 type GetCombinedStorageInfoParams struct { 116 Context *cmd.Context 117 APIClient StorageListAPI 118 Ids []string 119 WantStorage, WantVolumes, WantFilesystems bool 120 } 121 122 // GetCombinedStorageInfo returns a list of StorageInstances, Filesystems and Volumes for juju cmdline display purposes 123 func GetCombinedStorageInfo(p GetCombinedStorageInfoParams) (*CombinedStorage, error) { 124 combined := &CombinedStorage{} 125 if p.WantFilesystems { 126 filesystems, err := generateListFilesystemsOutput(p.Context, p.APIClient, p.Ids) 127 if err != nil { 128 return nil, errors.Trace(err) 129 } 130 combined.Filesystems = filesystems 131 } 132 if p.WantVolumes { 133 volumes, err := generateListVolumeOutput(p.Context, p.APIClient, p.Ids) 134 if err != nil { 135 return nil, errors.Trace(err) 136 } 137 combined.Volumes = volumes 138 } 139 if p.WantStorage { 140 storageInstances, err := generateListStorageOutput(p.Context, p.APIClient) 141 if err != nil { 142 return nil, errors.Trace(err) 143 } 144 combined.StorageInstances = storageInstances 145 } 146 return combined, nil 147 } 148 149 // StorageListAPI defines the API methods that the storage commands use. 150 type StorageListAPI interface { 151 Close() error 152 ListStorageDetails() ([]params.StorageDetails, error) 153 ListFilesystems(machines []string) ([]params.FilesystemDetailsListResult, error) 154 ListVolumes(machines []string) ([]params.VolumeDetailsListResult, error) 155 } 156 157 // generateListStorageOutput returns a map of storage details 158 func generateListStorageOutput(ctx *cmd.Context, api StorageListAPI) (map[string]StorageInfo, error) { 159 results, err := api.ListStorageDetails() 160 if err != nil { 161 return nil, errors.Trace(err) 162 } 163 if len(results) == 0 { 164 return nil, nil 165 } 166 return formatStorageDetails(results) 167 } 168 169 // CombinedStorage holds a list of StorageInstances, Filesystems and Volumes for juju cmdline display purposes. 170 type CombinedStorage struct { 171 StorageInstances map[string]StorageInfo `yaml:"storage,omitempty" json:"storage,omitempty"` 172 Filesystems map[string]FilesystemInfo `yaml:"filesystems,omitempty" json:"filesystems,omitempty"` 173 Volumes map[string]VolumeInfo `yaml:"volumes,omitempty" json:"volumes,omitempty"` 174 } 175 176 func (c *CombinedStorage) empty() bool { 177 return len(c.StorageInstances) == 0 && len(c.Filesystems) == 0 && len(c.Volumes) == 0 178 } 179 180 // formatListTabularOne writes a tabular summary of storage instances or filesystems or volumes. 181 func formatListTabularOne(writer io.Writer, value interface{}) error { 182 return formatListTabular(writer, value, false) 183 } 184 185 func formatListTabular(writer io.Writer, value interface{}, all bool) error { 186 combined := value.(CombinedStorage) 187 var newline bool 188 if len(combined.StorageInstances) > 0 { 189 // If we're listing storage in tabular format, we combine all 190 // of the information into a list of "storage". 191 if err := formatStorageInstancesListTabular(writer, combined); err != nil { 192 return errors.Trace(err) 193 } 194 if !all { 195 return nil 196 } 197 newline = true 198 } 199 if len(combined.Filesystems) > 0 { 200 if newline { 201 fmt.Fprintln(writer) 202 } 203 if err := formatFilesystemListTabular(writer, combined.Filesystems); err != nil { 204 return err 205 } 206 if !all { 207 return nil 208 } 209 newline = true 210 } 211 if len(combined.Volumes) > 0 { 212 if newline { 213 fmt.Fprintln(writer) 214 } 215 if err := formatVolumeListTabular(writer, combined.Volumes); err != nil { 216 return err 217 } 218 } 219 return nil 220 } 221 222 // FormatListTabularAll writes a tabular summary of storage instances, filesystems and volumes. 223 func FormatListTabularAll(writer io.Writer, value interface{}) error { 224 return formatListTabular(writer, value, true) 225 }