github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/cmd/juju/space/list.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package space 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "net" 10 "strings" 11 12 "launchpad.net/gnuflag" 13 14 "github.com/juju/cmd" 15 "github.com/juju/errors" 16 "github.com/juju/juju/apiserver/params" 17 ) 18 19 // ListCommand displays a list of all spaces known to Juju. 20 type ListCommand struct { 21 SpaceCommandBase 22 Short bool 23 out cmd.Output 24 } 25 26 const listCommandDoc = ` 27 Displays all defined spaces. If --short is not given both spaces and 28 their subnets are displayed, otherwise just a list of spaces. The 29 --format argument has the same semantics as in other CLI commands - 30 "yaml" is the default. The --output argument allows the command 31 output to be redirected to a file. ` 32 33 // Info is defined on the cmd.Command interface. 34 func (c *ListCommand) Info() *cmd.Info { 35 return &cmd.Info{ 36 Name: "list", 37 Args: "[--short] [--format yaml|json] [--output <path>]", 38 Purpose: "list spaces known to Juju, including associated subnets", 39 Doc: strings.TrimSpace(listCommandDoc), 40 } 41 } 42 43 // SetFlags is defined on the cmd.Command interface. 44 func (c *ListCommand) SetFlags(f *gnuflag.FlagSet) { 45 c.SpaceCommandBase.SetFlags(f) 46 c.out.AddFlags(f, "yaml", map[string]cmd.Formatter{ 47 "yaml": cmd.FormatYaml, 48 "json": cmd.FormatJson, 49 }) 50 51 f.BoolVar(&c.Short, "short", false, "only display spaces.") 52 } 53 54 // Init is defined on the cmd.Command interface. It checks the 55 // arguments for sanity and sets up the command to run. 56 func (c *ListCommand) Init(args []string) error { 57 // No arguments are accepted, just flags. 58 if err := cmd.CheckEmpty(args); err != nil { 59 return errors.Trace(err) 60 } 61 62 return nil 63 } 64 65 // Run implements Command.Run. 66 func (c *ListCommand) Run(ctx *cmd.Context) error { 67 return c.RunWithAPI(ctx, func(api SpaceAPI, ctx *cmd.Context) error { 68 spaces, err := api.ListSpaces() 69 if err != nil { 70 return errors.Annotate(err, "cannot list spaces") 71 } 72 if len(spaces) == 0 { 73 ctx.Infof("no spaces to display") 74 return c.out.Write(ctx, nil) 75 } 76 77 if c.Short { 78 result := formattedShortList{} 79 for _, space := range spaces { 80 result.Spaces = append(result.Spaces, space.Name) 81 } 82 return c.out.Write(ctx, result) 83 } 84 // Construct the output list for displaying with the chosen 85 // format. 86 result := formattedList{ 87 Spaces: make(map[string]map[string]formattedSubnet), 88 } 89 90 for _, space := range spaces { 91 result.Spaces[space.Name] = make(map[string]formattedSubnet) 92 for _, subnet := range space.Subnets { 93 subResult := formattedSubnet{ 94 Type: typeUnknown, 95 ProviderId: subnet.ProviderId, 96 Zones: subnet.Zones, 97 } 98 // Display correct status according to the life cycle value. 99 // 100 // TODO(dimitern): Do this on the apiserver side, also 101 // do the same for params.Space, so in case of an 102 // error it can be displayed. 103 switch subnet.Life { 104 case params.Alive: 105 subResult.Status = statusInUse 106 case params.Dying, params.Dead: 107 subResult.Status = statusTerminating 108 } 109 110 // Use the CIDR to determine the subnet type. 111 // TODO(dimitern): Do this on the apiserver side. 112 if ip, _, err := net.ParseCIDR(subnet.CIDR); err != nil { 113 // This should never happen as subnets will be 114 // validated before saving in state. 115 msg := fmt.Sprintf("error: invalid subnet CIDR: %s", subnet.CIDR) 116 subResult.Status = msg 117 } else if ip.To4() != nil { 118 subResult.Type = typeIPv4 119 } else if ip.To16() != nil { 120 subResult.Type = typeIPv6 121 } 122 result.Spaces[space.Name][subnet.CIDR] = subResult 123 } 124 } 125 return c.out.Write(ctx, result) 126 }) 127 } 128 129 const ( 130 typeUnknown = "unknown" 131 typeIPv4 = "ipv4" 132 typeIPv6 = "ipv6" 133 134 statusInUse = "in-use" 135 statusTerminating = "terminating" 136 ) 137 138 // TODO(dimitern): Display space attributes along with subnets (state 139 // or error,public,?) 140 141 type formattedList struct { 142 Spaces map[string]map[string]formattedSubnet `json:"spaces" yaml:"spaces"` 143 } 144 145 type formattedShortList struct { 146 Spaces []string `json:"spaces" yaml:"spaces"` 147 } 148 149 // A goyaml bug means we can't declare these types locally to the 150 // GetYAML methods. 151 type formattedListNoMethods formattedList 152 153 // MarshalJSON is defined on json.Marshaller. 154 func (l formattedList) MarshalJSON() ([]byte, error) { 155 return json.Marshal(formattedListNoMethods(l)) 156 } 157 158 // GetYAML is defined on yaml.Getter. 159 func (l formattedList) GetYAML() (tag string, value interface{}) { 160 return "", formattedListNoMethods(l) 161 } 162 163 type formattedSubnet struct { 164 Type string `json:"type" yaml:"type"` 165 ProviderId string `json:"provider-id,omitempty" yaml:"provider-id,omitempty"` 166 Status string `json:"status,omitempty" yaml:"status,omitempty"` 167 Zones []string `json:"zones" yaml:"zones"` 168 } 169 170 // A goyaml bug means we can't declare these types locally to the 171 // GetYAML methods. 172 type formattedSubnetNoMethods formattedSubnet 173 174 // MarshalJSON is defined on json.Marshaller. 175 func (s formattedSubnet) MarshalJSON() ([]byte, error) { 176 return json.Marshal(formattedSubnetNoMethods(s)) 177 } 178 179 // GetYAML is defined on yaml.Getter. 180 func (s formattedSubnet) GetYAML() (tag string, value interface{}) { 181 return "", formattedSubnetNoMethods(s) 182 }