github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/cmd"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/gnuflag"
    13  	"gopkg.in/juju/names.v2"
    14  
    15  	"github.com/juju/juju/apiserver/params"
    16  	jujucmd "github.com/juju/juju/cmd"
    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() modelcmd.ModelCommand {
    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 jujucmd.Info(&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 errors.Trace(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  				ProviderNetworkId: sub.ProviderNetworkId,
   122  				Zones:             sub.Zones,
   123  			}
   124  
   125  			// Use the CIDR to determine the subnet type.
   126  			if ip, _, err := net.ParseCIDR(sub.CIDR); err != nil {
   127  				return errors.Errorf("subnet %q has invalid CIDR", sub.CIDR)
   128  			} else if ip.To4() != nil {
   129  				subResult.Type = typeIPv4
   130  			} else if ip.To16() != nil {
   131  				subResult.Type = typeIPv6
   132  			}
   133  			if sub.SpaceTag != "" {
   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  
   142  			// Display correct status according to the life cycle value.
   143  			switch sub.Life {
   144  			case params.Alive:
   145  				subResult.Status = statusInUse
   146  			case params.Dying, params.Dead:
   147  				subResult.Status = statusTerminating
   148  			}
   149  
   150  			result.Subnets[sub.CIDR] = subResult
   151  		}
   152  
   153  		return c.Out.Write(ctx, result)
   154  	}))
   155  }
   156  
   157  const (
   158  	typeIPv4 = "ipv4"
   159  	typeIPv6 = "ipv6"
   160  
   161  	statusInUse       = "in-use"
   162  	statusTerminating = "terminating"
   163  )
   164  
   165  type formattedList struct {
   166  	Subnets map[string]formattedSubnet `json:"subnets" yaml:"subnets"`
   167  }
   168  
   169  type formattedSubnet struct {
   170  	Type              string   `json:"type" yaml:"type"`
   171  	ProviderId        string   `json:"provider-id,omitempty" yaml:"provider-id,omitempty"`
   172  	ProviderNetworkId string   `json:"provider-network-id,omitempty" yaml:"provider-network-id,omitempty"`
   173  	Status            string   `json:"status,omitempty" yaml:"status,omitempty"`
   174  	Space             string   `json:"space" yaml:"space"`
   175  	Zones             []string `json:"zones" yaml:"zones"`
   176  }