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