github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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 "strings" 10 11 "github.com/juju/cmd" 12 "github.com/juju/errors" 13 "github.com/juju/loggo" 14 "github.com/juju/names" 15 "github.com/juju/utils/featureflag" 16 17 "github.com/juju/juju/api" 18 "github.com/juju/juju/api/subnets" 19 "github.com/juju/juju/apiserver/params" 20 "github.com/juju/juju/cmd/envcmd" 21 "github.com/juju/juju/feature" 22 "github.com/juju/juju/network" 23 ) 24 25 // SubnetAPI defines the necessary API methods needed by the subnet 26 // subcommands. 27 type SubnetAPI interface { 28 io.Closer 29 30 // AddSubnet adds an existing subnet to Juju. 31 AddSubnet(cidr names.SubnetTag, id network.Id, spaceTag names.SpaceTag, zones []string) error 32 33 // ListSubnets returns information about subnets known to Juju, 34 // optionally filtered by space and/or zone (both can be empty). 35 ListSubnets(withSpace *names.SpaceTag, withZone string) ([]params.Subnet, error) 36 37 // AllZones returns all availability zones known to Juju. 38 AllZones() ([]string, error) 39 40 // AllSpaces returns all Juju network spaces. 41 AllSpaces() ([]names.Tag, error) 42 43 // CreateSubnet creates a new Juju subnet. 44 CreateSubnet(subnetCIDR names.SubnetTag, spaceTag names.SpaceTag, zones []string, isPublic bool) error 45 46 // RemoveSubnet marks an existing subnet as no longer used, which 47 // will cause it to get removed at some point after all its 48 // related entites are cleaned up. It will fail if the subnet is 49 // still in use by any machines. 50 RemoveSubnet(subnetCIDR names.SubnetTag) error 51 } 52 53 // mvpAPIShim forwards SubnetAPI methods to the real API facade for 54 // implemented methods only. Tested with a feature test only. 55 type mvpAPIShim struct { 56 SubnetAPI 57 58 apiState api.Connection 59 facade *subnets.API 60 } 61 62 func (m *mvpAPIShim) Close() error { 63 return m.apiState.Close() 64 } 65 66 func (m *mvpAPIShim) AddSubnet(cidr names.SubnetTag, id network.Id, spaceTag names.SpaceTag, zones []string) error { 67 return m.facade.AddSubnet(cidr, id, spaceTag, zones) 68 } 69 70 func (m *mvpAPIShim) ListSubnets(withSpace *names.SpaceTag, withZone string) ([]params.Subnet, error) { 71 return m.facade.ListSubnets(withSpace, withZone) 72 } 73 74 var logger = loggo.GetLogger("juju.cmd.juju.subnet") 75 76 const commandDoc = ` 77 "juju subnet" provides commands to manage Juju subnets. In Juju, a 78 subnet is a logical address range, a subdivision of a network, defined 79 by the subnet's Classless Inter-Domain Routing (CIDR) range, like 80 10.10.0.0/24 or 2001:db8::/32. Alternatively, subnets can be 81 identified uniquely by their provider-specific identifier 82 (ProviderId), if the provider supports that. Subnets have two kinds of 83 supported access: "public" (using shadow addresses) or "private" 84 (using cloud-local addresses, this is the default). For more 85 information about subnets and shadow addresses, please refer to Juju's 86 glossary help topics ("juju help glossary"). ` 87 88 // NewSuperCommand creates the "subnet" supercommand and registers the 89 // subcommands that it supports. 90 func NewSuperCommand() cmd.Command { 91 subnetCmd := cmd.NewSuperCommand(cmd.SuperCommandParams{ 92 Name: "subnet", 93 Doc: strings.TrimSpace(commandDoc), 94 UsagePrefix: "juju", 95 Purpose: "manage subnets", 96 }) 97 subnetCmd.Register(envcmd.Wrap(&AddCommand{})) 98 subnetCmd.Register(envcmd.Wrap(&ListCommand{})) 99 if featureflag.Enabled(feature.PostNetCLIMVP) { 100 // The following commands are not part of the MVP. 101 subnetCmd.Register(envcmd.Wrap(&CreateCommand{})) 102 subnetCmd.Register(envcmd.Wrap(&RemoveCommand{})) 103 } 104 105 return subnetCmd 106 } 107 108 // SubnetCommandBase is the base type embedded into all subnet 109 // subcommands. 110 type SubnetCommandBase struct { 111 envcmd.EnvCommandBase 112 api SubnetAPI 113 } 114 115 // NewAPI returns a SubnetAPI for the root api endpoint that the 116 // environment command returns. 117 func (c *SubnetCommandBase) NewAPI() (SubnetAPI, error) { 118 if c.api != nil { 119 // Already created. 120 return c.api, nil 121 } 122 root, err := c.NewAPIRoot() 123 if err != nil { 124 return nil, errors.Trace(err) 125 } 126 127 // This is tested with a feature test. 128 shim := &mvpAPIShim{ 129 apiState: root, 130 facade: subnets.NewAPI(root), 131 } 132 return shim, nil 133 } 134 135 type RunOnAPI func(api SubnetAPI, ctx *cmd.Context) error 136 137 func (c *SubnetCommandBase) RunWithAPI(ctx *cmd.Context, toRun RunOnAPI) error { 138 api, err := c.NewAPI() 139 if err != nil { 140 return errors.Annotate(err, "cannot connect to the API server") 141 } 142 defer api.Close() 143 return toRun(api, ctx) 144 } 145 146 // Common errors shared between subcommands. 147 var ( 148 errNoCIDR = errors.New("CIDR is required") 149 errNoCIDROrID = errors.New("either CIDR or provider ID is required") 150 errNoSpace = errors.New("space name is required") 151 errNoZones = errors.New("at least one zone is required") 152 ) 153 154 // CheckNumArgs is a helper used to validate the number of arguments 155 // passed to Init(). If the number of arguments is X, errors[X] (if 156 // set) will be returned, otherwise no error occurs. 157 func (s *SubnetCommandBase) CheckNumArgs(args []string, errors []error) error { 158 for num, err := range errors { 159 if len(args) == num { 160 return err 161 } 162 } 163 return nil 164 } 165 166 // ValidateCIDR parses given and returns an error if it's not valid. 167 // If the CIDR is incorrectly specified (e.g. 10.10.10.0/16 instead of 168 // 10.10.0.0/16) and strict is false, the correctly parsed CIDR in the 169 // expected format is returned instead without an error. Otherwise, 170 // when strict is true and given is incorrectly formatted, an error 171 // will be returned. 172 func (s *SubnetCommandBase) ValidateCIDR(given string, strict bool) (names.SubnetTag, error) { 173 _, ipNet, err := net.ParseCIDR(given) 174 if err != nil { 175 logger.Debugf("cannot parse CIDR %q: %v", given, err) 176 return names.SubnetTag{}, errors.Errorf("%q is not a valid CIDR", given) 177 } 178 if strict && given != ipNet.String() { 179 expected := ipNet.String() 180 return names.SubnetTag{}, errors.Errorf("%q is not correctly specified, expected %q", given, expected) 181 } 182 // Already validated, so shouldn't error here. 183 return names.NewSubnetTag(ipNet.String()), nil 184 } 185 186 // ValidateSpace parses given and returns an error if it's not a valid 187 // space name, otherwise returns the parsed tag and no error. 188 func (s *SubnetCommandBase) ValidateSpace(given string) (names.SpaceTag, error) { 189 if !names.IsValidSpace(given) { 190 return names.SpaceTag{}, errors.Errorf("%q is not a valid space name", given) 191 } 192 return names.NewSpaceTag(given), nil 193 }