github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/juju/subnet/list.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package subnet
     5  
     6  import (
     7  	"net"
     8  	"strings"
     9  
    10  	"github.com/juju/gnuflag"
    11  
    12  	"github.com/juju/cmd"
    13  	"github.com/juju/errors"
    14  	"github.com/juju/juju/apiserver/params"
    15  	"gopkg.in/juju/names.v2"
    16  
    17  	"github.com/juju/juju/cmd/modelcmd"
    18  	"github.com/juju/juju/cmd/output"
    19  )
    20  
    21  // NewListCommand returns a cammin used to list all subnets
    22  // known to Juju.
    23  func NewListCommand() cmd.Command {
    24  	return modelcmd.Wrap(&listCommand{})
    25  }
    26  
    27  // listCommand displays a list of all subnets known to Juju
    28  type listCommand struct {
    29  	SubnetCommandBase
    30  
    31  	SpaceName string
    32  	ZoneName  string
    33  
    34  	spaceTag *names.SpaceTag
    35  
    36  	Out cmd.Output
    37  }
    38  
    39  const listCommandDoc = `
    40  Displays a list of all subnets known to Juju. Results can be filtered
    41  using the optional --space and/or --zone arguments to only display
    42  subnets associated with a given network space and/or availability zone.
    43  
    44  Like with other Juju commands, the output and its format can be changed
    45  using the --format and --output (or -o) optional arguments. Supported
    46  output formats include "yaml" (default) and "json". To redirect the
    47  output to a file, use --output.
    48  `
    49  
    50  // Info is defined on the cmd.Command interface.
    51  func (c *listCommand) Info() *cmd.Info {
    52  	return &cmd.Info{
    53  		Name:    "subnets",
    54  		Args:    "[--space <name>] [--zone <name>] [--format yaml|json] [--output <path>]",
    55  		Purpose: "List subnets known to Juju.",
    56  		Doc:     strings.TrimSpace(listCommandDoc),
    57  		Aliases: []string{"list-subnets"},
    58  	}
    59  }
    60  
    61  // SetFlags is defined on the cmd.Command interface.
    62  func (c *listCommand) SetFlags(f *gnuflag.FlagSet) {
    63  	c.SubnetCommandBase.SetFlags(f)
    64  	c.Out.AddFlags(f, "yaml", output.DefaultFormatters)
    65  
    66  	f.StringVar(&c.SpaceName, "space", "", "Filter results by space name")
    67  	f.StringVar(&c.ZoneName, "zone", "", "Filter results by zone name")
    68  }
    69  
    70  // Init is defined on the cmd.Command interface. It checks the
    71  // arguments for sanity and sets up the command to run.
    72  func (c *listCommand) Init(args []string) error {
    73  	// No arguments are accepted, just flags.
    74  	err := cmd.CheckEmpty(args)
    75  	if err != nil {
    76  		return err
    77  	}
    78  
    79  	// Validate space name, if given and store as tag.
    80  	c.spaceTag = nil
    81  	if c.SpaceName != "" {
    82  		tag, err := c.ValidateSpace(c.SpaceName)
    83  		if err != nil {
    84  			c.SpaceName = ""
    85  			return err
    86  		}
    87  		c.spaceTag = &tag
    88  	}
    89  	return nil
    90  }
    91  
    92  // Run implements Command.Run.
    93  func (c *listCommand) Run(ctx *cmd.Context) error {
    94  	return c.RunWithAPI(ctx, func(api SubnetAPI, ctx *cmd.Context) error {
    95  		// Validate space and/or zone, if given to display a nicer error
    96  		// message.
    97  		// Get the list of subnets, filtering them as requested.
    98  		subnets, err := api.ListSubnets(c.spaceTag, c.ZoneName)
    99  		if err != nil {
   100  			return errors.Annotate(err, "cannot list subnets")
   101  		}
   102  
   103  		// Display a nicer message in case no subnets were found.
   104  		if len(subnets) == 0 {
   105  			if c.SpaceName != "" || c.ZoneName != "" {
   106  				ctx.Infof("No subnets found matching requested criteria.")
   107  			} else {
   108  				ctx.Infof("No subnets to display.")
   109  			}
   110  			return nil
   111  		}
   112  
   113  		// Construct the output list for displaying with the chosen
   114  		// format.
   115  		result := formattedList{
   116  			Subnets: make(map[string]formattedSubnet),
   117  		}
   118  		for _, sub := range subnets {
   119  			subResult := formattedSubnet{
   120  				ProviderId: sub.ProviderId,
   121  				Zones:      sub.Zones,
   122  			}
   123  
   124  			// Use the CIDR to determine the subnet type.
   125  			if ip, _, err := net.ParseCIDR(sub.CIDR); err != nil {
   126  				return errors.Errorf("subnet %q has invalid CIDR", sub.CIDR)
   127  			} else if ip.To4() != nil {
   128  				subResult.Type = typeIPv4
   129  			} else if ip.To16() != nil {
   130  				subResult.Type = typeIPv6
   131  			}
   132  			// Space must be valid, but verify anyway.
   133  			spaceTag, err := names.ParseSpaceTag(sub.SpaceTag)
   134  			if err != nil {
   135  				return errors.Annotatef(err, "subnet %q has invalid space", sub.CIDR)
   136  			}
   137  			subResult.Space = spaceTag.Id()
   138  
   139  			// Display correct status according to the life cycle value.
   140  			switch sub.Life {
   141  			case params.Alive:
   142  				subResult.Status = statusInUse
   143  			case params.Dying, params.Dead:
   144  				subResult.Status = statusTerminating
   145  			}
   146  
   147  			result.Subnets[sub.CIDR] = subResult
   148  		}
   149  
   150  		return c.Out.Write(ctx, result)
   151  	})
   152  }
   153  
   154  const (
   155  	typeIPv4 = "ipv4"
   156  	typeIPv6 = "ipv6"
   157  
   158  	statusInUse       = "in-use"
   159  	statusTerminating = "terminating"
   160  )
   161  
   162  type formattedList struct {
   163  	Subnets map[string]formattedSubnet `json:"subnets" yaml:"subnets"`
   164  }
   165  
   166  type formattedSubnet struct {
   167  	Type       string   `json:"type" yaml:"type"`
   168  	ProviderId string   `json:"provider-id,omitempty" yaml:"provider-id,omitempty"`
   169  	Status     string   `json:"status,omitempty" yaml:"status,omitempty"`
   170  	Space      string   `json:"space" yaml:"space"`
   171  	Zones      []string `json:"zones" yaml:"zones"`
   172  }