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 }