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 }