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  }