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