github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/apiserver/spaces/spaces.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package spaces 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "github.com/juju/names" 10 11 "github.com/juju/juju/apiserver/common" 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/environs" 14 "github.com/juju/juju/environs/config" 15 "github.com/juju/juju/state" 16 ) 17 18 var logger = loggo.GetLogger("juju.apiserver.spaces") 19 20 func init() { 21 common.RegisterStandardFacade("Spaces", 1, NewAPI) 22 } 23 24 // API defines the methods the Spaces API facade implements. 25 type API interface { 26 CreateSpaces(params.CreateSpacesParams) (params.ErrorResults, error) 27 ListSpaces() (params.ListSpacesResults, error) 28 } 29 30 // Backing defines the state methods this facede needs, so they can be 31 // mocked for testing. 32 type Backing interface { 33 // EnvironConfig returns the configuration of the environment. 34 EnvironConfig() (*config.Config, error) 35 36 // AddSpace creates a space. 37 AddSpace(name string, subnetIds []string, public bool) error 38 39 // AllSpaces returns all known Juju network spaces. 40 AllSpaces() ([]common.BackingSpace, error) 41 } 42 43 // spacesAPI implements the API interface. 44 type spacesAPI struct { 45 backing Backing 46 resources *common.Resources 47 authorizer common.Authorizer 48 } 49 50 // NewAPI creates a new Space API server-side facade with a 51 // state.State backing. 52 func NewAPI(st *state.State, res *common.Resources, auth common.Authorizer) (API, error) { 53 return newAPIWithBacking(&stateShim{st: st}, res, auth) 54 } 55 56 // newAPIWithBacking creates a new server-side Spaces API facade with 57 // the given Backing. 58 func newAPIWithBacking(backing Backing, resources *common.Resources, authorizer common.Authorizer) (API, error) { 59 // Only clients can access the Spaces facade. 60 if !authorizer.AuthClient() { 61 return nil, common.ErrPerm 62 } 63 return &spacesAPI{ 64 backing: backing, 65 resources: resources, 66 authorizer: authorizer, 67 }, nil 68 } 69 70 // CreateSpaces creates a new Juju network space, associating the 71 // specified subnets with it (optional; can be empty). 72 func (api *spacesAPI) CreateSpaces(args params.CreateSpacesParams) (results params.ErrorResults, err error) { 73 err = api.supportsSpaces() 74 if err != nil { 75 return results, common.ServerError(errors.Trace(err)) 76 } 77 78 results.Results = make([]params.ErrorResult, len(args.Spaces)) 79 80 for i, space := range args.Spaces { 81 err := api.createOneSpace(space) 82 if err == nil { 83 continue 84 } 85 results.Results[i].Error = common.ServerError(errors.Trace(err)) 86 } 87 88 return results, nil 89 } 90 91 func (api *spacesAPI) createOneSpace(args params.CreateSpaceParams) error { 92 // Validate the args, assemble information for api.backing.AddSpaces 93 var subnets []string 94 95 spaceTag, err := names.ParseSpaceTag(args.SpaceTag) 96 if err != nil { 97 return errors.Trace(err) 98 } 99 100 for _, tag := range args.SubnetTags { 101 subnetTag, err := names.ParseSubnetTag(tag) 102 if err != nil { 103 return errors.Trace(err) 104 } 105 subnets = append(subnets, subnetTag.Id()) 106 } 107 108 // Add the validated space 109 err = api.backing.AddSpace(spaceTag.Id(), subnets, args.Public) 110 if err != nil { 111 return errors.Trace(err) 112 } 113 return nil 114 } 115 116 func backingSubnetToParamsSubnet(subnet common.BackingSubnet) params.Subnet { 117 cidr := subnet.CIDR() 118 vlantag := subnet.VLANTag() 119 providerid := subnet.ProviderId() 120 zones := subnet.AvailabilityZones() 121 status := subnet.Status() 122 var spaceTag names.SpaceTag 123 if subnet.SpaceName() != "" { 124 spaceTag = names.NewSpaceTag(subnet.SpaceName()) 125 } 126 127 return params.Subnet{ 128 CIDR: cidr, 129 VLANTag: vlantag, 130 ProviderId: providerid, 131 Zones: zones, 132 Status: status, 133 SpaceTag: spaceTag.String(), 134 Life: subnet.Life(), 135 } 136 } 137 138 // ListSpaces lists all the available spaces and their associated subnets. 139 func (api *spacesAPI) ListSpaces() (results params.ListSpacesResults, err error) { 140 err = api.supportsSpaces() 141 if err != nil { 142 return results, common.ServerError(errors.Trace(err)) 143 } 144 145 spaces, err := api.backing.AllSpaces() 146 if err != nil { 147 return results, errors.Trace(err) 148 } 149 150 results.Results = make([]params.Space, len(spaces)) 151 for i, space := range spaces { 152 result := params.Space{} 153 result.Name = space.Name() 154 155 subnets, err := space.Subnets() 156 if err != nil { 157 err = errors.Annotatef(err, "fetching subnets") 158 result.Error = common.ServerError(err) 159 results.Results[i] = result 160 continue 161 } 162 163 result.Subnets = make([]params.Subnet, len(subnets)) 164 for i, subnet := range subnets { 165 result.Subnets[i] = backingSubnetToParamsSubnet(subnet) 166 } 167 results.Results[i] = result 168 } 169 return results, nil 170 } 171 172 // supportsSpaces checks if the environment implements NetworkingEnviron 173 // and also if it supports spaces. 174 func (api *spacesAPI) supportsSpaces() error { 175 config, err := api.backing.EnvironConfig() 176 if err != nil { 177 return errors.Annotate(err, "getting environment config") 178 } 179 env, err := environs.New(config) 180 if err != nil { 181 return errors.Annotate(err, "validating environment config") 182 } 183 netEnv, ok := environs.SupportsNetworking(env) 184 if !ok { 185 return errors.NotSupportedf("networking") 186 } 187 ok, err = netEnv.SupportsSpaces() 188 if err != nil { 189 logger.Warningf("environment does not support spaces: %v", err) 190 } 191 return err 192 }