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