github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/subnet/subnet.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 "io" 8 "net" 9 10 "github.com/juju/cmd" 11 "github.com/juju/errors" 12 "github.com/juju/loggo" 13 "github.com/juju/names" 14 15 "github.com/juju/juju/api" 16 "github.com/juju/juju/api/subnets" 17 "github.com/juju/juju/apiserver/params" 18 "github.com/juju/juju/cmd/modelcmd" 19 "github.com/juju/juju/network" 20 ) 21 22 // SubnetAPI defines the necessary API methods needed by the subnet 23 // subcommands. 24 type SubnetAPI interface { 25 io.Closer 26 27 // AddSubnet adds an existing subnet to Juju. 28 AddSubnet(cidr names.SubnetTag, id network.Id, spaceTag names.SpaceTag, zones []string) error 29 30 // ListSubnets returns information about subnets known to Juju, 31 // optionally filtered by space and/or zone (both can be empty). 32 ListSubnets(withSpace *names.SpaceTag, withZone string) ([]params.Subnet, error) 33 34 // AllZones returns all availability zones known to Juju. 35 AllZones() ([]string, error) 36 37 // AllSpaces returns all Juju network spaces. 38 AllSpaces() ([]names.Tag, error) 39 40 // CreateSubnet creates a new Juju subnet. 41 CreateSubnet(subnetCIDR names.SubnetTag, spaceTag names.SpaceTag, zones []string, isPublic bool) error 42 43 // RemoveSubnet marks an existing subnet as no longer used, which 44 // will cause it to get removed at some point after all its 45 // related entites are cleaned up. It will fail if the subnet is 46 // still in use by any machines. 47 RemoveSubnet(subnetCIDR names.SubnetTag) error 48 } 49 50 // mvpAPIShim forwards SubnetAPI methods to the real API facade for 51 // implemented methods only. Tested with a feature test only. 52 type mvpAPIShim struct { 53 SubnetAPI 54 55 apiState api.Connection 56 facade *subnets.API 57 } 58 59 func (m *mvpAPIShim) Close() error { 60 return m.apiState.Close() 61 } 62 63 func (m *mvpAPIShim) AddSubnet(cidr names.SubnetTag, id network.Id, spaceTag names.SpaceTag, zones []string) error { 64 return m.facade.AddSubnet(cidr, id, spaceTag, zones) 65 } 66 67 func (m *mvpAPIShim) ListSubnets(withSpace *names.SpaceTag, withZone string) ([]params.Subnet, error) { 68 return m.facade.ListSubnets(withSpace, withZone) 69 } 70 71 var logger = loggo.GetLogger("juju.cmd.juju.subnet") 72 73 // SubnetCommandBase is the base type embedded into all subnet 74 // subcommands. 75 type SubnetCommandBase struct { 76 modelcmd.ModelCommandBase 77 api SubnetAPI 78 } 79 80 // NewAPI returns a SubnetAPI for the root api endpoint that the 81 // environment command returns. 82 func (c *SubnetCommandBase) NewAPI() (SubnetAPI, error) { 83 if c.api != nil { 84 // Already created. 85 return c.api, nil 86 } 87 root, err := c.NewAPIRoot() 88 if err != nil { 89 return nil, errors.Trace(err) 90 } 91 92 // This is tested with a feature test. 93 shim := &mvpAPIShim{ 94 apiState: root, 95 facade: subnets.NewAPI(root), 96 } 97 return shim, nil 98 } 99 100 type RunOnAPI func(api SubnetAPI, ctx *cmd.Context) error 101 102 func (c *SubnetCommandBase) RunWithAPI(ctx *cmd.Context, toRun RunOnAPI) error { 103 api, err := c.NewAPI() 104 if err != nil { 105 return errors.Annotate(err, "cannot connect to the API server") 106 } 107 defer api.Close() 108 return toRun(api, ctx) 109 } 110 111 // Common errors shared between subcommands. 112 var ( 113 errNoCIDR = errors.New("CIDR is required") 114 errNoCIDROrID = errors.New("either CIDR or provider ID is required") 115 errNoSpace = errors.New("space name is required") 116 errNoZones = errors.New("at least one zone is required") 117 ) 118 119 // CheckNumArgs is a helper used to validate the number of arguments 120 // passed to Init(). If the number of arguments is X, errors[X] (if 121 // set) will be returned, otherwise no error occurs. 122 func (s *SubnetCommandBase) CheckNumArgs(args []string, errors []error) error { 123 for num, err := range errors { 124 if len(args) == num { 125 return err 126 } 127 } 128 return nil 129 } 130 131 // ValidateCIDR parses given and returns an error if it's not valid. 132 // If the CIDR is incorrectly specified (e.g. 10.10.10.0/16 instead of 133 // 10.10.0.0/16) and strict is false, the correctly parsed CIDR in the 134 // expected format is returned instead without an error. Otherwise, 135 // when strict is true and given is incorrectly formatted, an error 136 // will be returned. 137 func (s *SubnetCommandBase) ValidateCIDR(given string, strict bool) (names.SubnetTag, error) { 138 _, ipNet, err := net.ParseCIDR(given) 139 if err != nil { 140 logger.Debugf("cannot parse CIDR %q: %v", given, err) 141 return names.SubnetTag{}, errors.Errorf("%q is not a valid CIDR", given) 142 } 143 if strict && given != ipNet.String() { 144 expected := ipNet.String() 145 return names.SubnetTag{}, errors.Errorf("%q is not correctly specified, expected %q", given, expected) 146 } 147 // Already validated, so shouldn't error here. 148 return names.NewSubnetTag(ipNet.String()), nil 149 } 150 151 // ValidateSpace parses given and returns an error if it's not a valid 152 // space name, otherwise returns the parsed tag and no error. 153 func (s *SubnetCommandBase) ValidateSpace(given string) (names.SpaceTag, error) { 154 if !names.IsValidSpace(given) { 155 return names.SpaceTag{}, errors.Errorf("%q is not a valid space name", given) 156 } 157 return names.NewSpaceTag(given), nil 158 }