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 }