github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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 if errors.IsNotSupported(err) { 71 ctx.Infof("cannot list spaces: %v", err) 72 } 73 return errors.Annotate(err, "cannot list spaces") 74 } 75 if len(spaces) == 0 { 76 ctx.Infof("no spaces to display") 77 return c.out.Write(ctx, nil) 78 } 79 80 if c.Short { 81 result := formattedShortList{} 82 for _, space := range spaces { 83 result.Spaces = append(result.Spaces, space.Name) 84 } 85 return c.out.Write(ctx, result) 86 } 87 // Construct the output list for displaying with the chosen 88 // format. 89 result := formattedList{ 90 Spaces: make(map[string]map[string]formattedSubnet), 91 } 92 93 for _, space := range spaces { 94 result.Spaces[space.Name] = make(map[string]formattedSubnet) 95 for _, subnet := range space.Subnets { 96 subResult := formattedSubnet{ 97 Type: typeUnknown, 98 ProviderId: subnet.ProviderId, 99 Zones: subnet.Zones, 100 } 101 // Display correct status according to the life cycle value. 102 // 103 // TODO(dimitern): Do this on the apiserver side, also 104 // do the same for params.Space, so in case of an 105 // error it can be displayed. 106 switch subnet.Life { 107 case params.Alive: 108 subResult.Status = statusInUse 109 case params.Dying, params.Dead: 110 subResult.Status = statusTerminating 111 } 112 113 // Use the CIDR to determine the subnet type. 114 // TODO(dimitern): Do this on the apiserver side. 115 if ip, _, err := net.ParseCIDR(subnet.CIDR); err != nil { 116 // This should never happen as subnets will be 117 // validated before saving in state. 118 msg := fmt.Sprintf("error: invalid subnet CIDR: %s", subnet.CIDR) 119 subResult.Status = msg 120 } else if ip.To4() != nil { 121 subResult.Type = typeIPv4 122 } else if ip.To16() != nil { 123 subResult.Type = typeIPv6 124 } 125 result.Spaces[space.Name][subnet.CIDR] = subResult 126 } 127 } 128 return c.out.Write(ctx, result) 129 }) 130 } 131 132 const ( 133 typeUnknown = "unknown" 134 typeIPv4 = "ipv4" 135 typeIPv6 = "ipv6" 136 137 statusInUse = "in-use" 138 statusTerminating = "terminating" 139 ) 140 141 // TODO(dimitern): Display space attributes along with subnets (state 142 // or error,public,?) 143 144 type formattedList struct { 145 Spaces map[string]map[string]formattedSubnet `json:"spaces" yaml:"spaces"` 146 } 147 148 type formattedShortList struct { 149 Spaces []string `json:"spaces" yaml:"spaces"` 150 } 151 152 // A goyaml bug means we can't declare these types locally to the 153 // GetYAML methods. 154 type formattedListNoMethods formattedList 155 156 // MarshalJSON is defined on json.Marshaller. 157 func (l formattedList) MarshalJSON() ([]byte, error) { 158 return json.Marshal(formattedListNoMethods(l)) 159 } 160 161 // GetYAML is defined on yaml.Getter. 162 func (l formattedList) GetYAML() (tag string, value interface{}) { 163 return "", formattedListNoMethods(l) 164 } 165 166 type formattedSubnet struct { 167 Type string `json:"type" yaml:"type"` 168 ProviderId string `json:"provider-id,omitempty" yaml:"provider-id,omitempty"` 169 Status string `json:"status,omitempty" yaml:"status,omitempty"` 170 Zones []string `json:"zones" yaml:"zones"` 171 } 172 173 // A goyaml bug means we can't declare these types locally to the 174 // GetYAML methods. 175 type formattedSubnetNoMethods formattedSubnet 176 177 // MarshalJSON is defined on json.Marshaller. 178 func (s formattedSubnet) MarshalJSON() ([]byte, error) { 179 return json.Marshal(formattedSubnetNoMethods(s)) 180 } 181 182 // GetYAML is defined on yaml.Getter. 183 func (s formattedSubnet) GetYAML() (tag string, value interface{}) { 184 return "", formattedSubnetNoMethods(s) 185 }