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  }