github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/juju/subnet/add.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  	"strings"
     8  
     9  	"github.com/juju/cmd"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/juju/network"
    12  	"gopkg.in/juju/names.v2"
    13  
    14  	"github.com/juju/juju/apiserver/params"
    15  	"github.com/juju/juju/cmd/juju/common"
    16  	"github.com/juju/juju/cmd/modelcmd"
    17  )
    18  
    19  // NewAddCommand returns a command used to add an existing subnet to Juju.
    20  func NewAddCommand() cmd.Command {
    21  	return modelcmd.Wrap(&addCommand{})
    22  }
    23  
    24  // addCommand calls the API to add an existing subnet to Juju.
    25  type addCommand struct {
    26  	SubnetCommandBase
    27  
    28  	CIDR       names.SubnetTag
    29  	RawCIDR    string // before normalizing (e.g. 10.10.0.0/8 to 10.0.0.0/8)
    30  	ProviderId string
    31  	Space      names.SpaceTag
    32  	Zones      []string
    33  }
    34  
    35  const addCommandDoc = `
    36  Adds an existing subnet to Juju, making it available for use. Unlike
    37  "juju create-subnet", this command does not create a new subnet, so it
    38  is supported on a wider variety of clouds (where SDN features are not
    39  available, e.g. MAAS). The subnet will be associated with the given
    40  existing Juju network space.
    41  
    42  Subnets can be referenced by either their CIDR or ProviderId (if the
    43  provider supports it). If CIDR is used an multiple subnets have the
    44  same CIDR, an error will be returned, including the list of possible
    45  provider IDs uniquely identifying each subnet.
    46  
    47  Any availablility zones associated with the added subnet are automatically
    48  discovered using the cloud API (if supported). If this is not possible,
    49  since any subnet needs to be part of at least one zone, specifying
    50  zone(s) is required.
    51  `
    52  
    53  // Info is defined on the cmd.Command interface.
    54  func (c *addCommand) Info() *cmd.Info {
    55  	return &cmd.Info{
    56  		Name:    "add-subnet",
    57  		Args:    "<CIDR>|<provider-id> <space> [<zone1> <zone2> ...]",
    58  		Purpose: "Add an existing subnet to Juju.",
    59  		Doc:     strings.TrimSpace(addCommandDoc),
    60  	}
    61  }
    62  
    63  // Init is defined on the cmd.Command interface. It checks the
    64  // arguments for sanity and sets up the command to run.
    65  func (c *addCommand) Init(args []string) (err error) {
    66  	defer errors.DeferredAnnotatef(&err, "invalid arguments specified")
    67  
    68  	// Ensure we have 2 or more arguments.
    69  	switch len(args) {
    70  	case 0:
    71  		return errNoCIDROrID
    72  	case 1:
    73  		return errNoSpace
    74  	}
    75  
    76  	// Try to validate first argument as a CIDR first.
    77  	c.RawCIDR = args[0]
    78  	c.CIDR, err = c.ValidateCIDR(args[0], false)
    79  	if err != nil {
    80  		// If it's not a CIDR it could be a ProviderId, so ignore the
    81  		// error.
    82  		c.ProviderId = args[0]
    83  		c.RawCIDR = ""
    84  	}
    85  
    86  	// Validate the space name.
    87  	c.Space, err = c.ValidateSpace(args[1])
    88  	if err != nil {
    89  		return err
    90  	}
    91  
    92  	// Add any given zones.
    93  	for _, zone := range args[2:] {
    94  		c.Zones = append(c.Zones, zone)
    95  	}
    96  	return nil
    97  }
    98  
    99  // Run implements Command.Run.
   100  func (c *addCommand) Run(ctx *cmd.Context) error {
   101  	return c.RunWithAPI(ctx, func(api SubnetAPI, ctx *cmd.Context) error {
   102  		if c.CIDR.Id() != "" && c.RawCIDR != c.CIDR.Id() {
   103  			ctx.Infof(
   104  				"WARNING: using CIDR %q instead of the incorrectly specified %q.",
   105  				c.CIDR.Id(), c.RawCIDR,
   106  			)
   107  		}
   108  
   109  		// Add the existing subnet.
   110  		err := api.AddSubnet(c.CIDR, network.Id(c.ProviderId), c.Space, c.Zones)
   111  		// TODO(dimitern): Change this once the API returns a concrete error.
   112  		if err != nil && strings.Contains(err.Error(), "multiple subnets with") {
   113  			// Special case: multiple subnets with the same CIDR exist
   114  			ctx.Infof("ERROR: %v.", err)
   115  			return nil
   116  		} else if err != nil {
   117  			if params.IsCodeUnauthorized(err) {
   118  				common.PermissionsMessage(ctx.Stderr, "add a subnet")
   119  			}
   120  			return errors.Annotatef(err, "cannot add subnet")
   121  		}
   122  
   123  		if c.ProviderId != "" {
   124  			ctx.Infof("added subnet with ProviderId %q in space %q", c.ProviderId, c.Space.Id())
   125  		} else {
   126  			ctx.Infof("added subnet with CIDR %q in space %q", c.CIDR.Id(), c.Space.Id())
   127  		}
   128  		return nil
   129  	})
   130  }