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