github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/addresser/addresser.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package addresser 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 10 "github.com/juju/juju/apiserver/common" 11 "github.com/juju/juju/apiserver/params" 12 "github.com/juju/juju/environs" 13 "github.com/juju/juju/instance" 14 "github.com/juju/juju/network" 15 "github.com/juju/juju/state" 16 "github.com/juju/juju/state/watcher" 17 ) 18 19 func init() { 20 common.RegisterStandardFacade("Addresser", 2, NewAddresserAPI) 21 } 22 23 var logger = loggo.GetLogger("juju.apiserver.addresser") 24 25 // AddresserAPI provides access to the Addresser API facade. 26 type AddresserAPI struct { 27 st StateInterface 28 resources *common.Resources 29 authorizer common.Authorizer 30 } 31 32 // NewAddresserAPI creates a new server-side Addresser API facade. 33 func NewAddresserAPI( 34 st *state.State, 35 resources *common.Resources, 36 authorizer common.Authorizer, 37 ) (*AddresserAPI, error) { 38 isModelManager := authorizer.AuthModelManager() 39 if !isModelManager { 40 // Addresser must run as model manager. 41 return nil, common.ErrPerm 42 } 43 sti := getState(st) 44 return &AddresserAPI{ 45 st: sti, 46 resources: resources, 47 authorizer: authorizer, 48 }, nil 49 } 50 51 // getNetworkingEnviron checks if the environment implements NetworkingEnviron 52 // and also if it supports IP address allocation. 53 func (api *AddresserAPI) getNetworkingEnviron() (environs.NetworkingEnviron, bool, error) { 54 config, err := api.st.ModelConfig() 55 if err != nil { 56 return nil, false, errors.Annotate(err, "getting model config") 57 } 58 env, err := environs.New(config) 59 if err != nil { 60 return nil, false, errors.Annotate(err, "validating model config") 61 } 62 netEnv, ok := environs.SupportsNetworking(env) 63 if !ok { 64 return nil, false, nil 65 } 66 ok, err = netEnv.SupportsAddressAllocation(network.AnySubnet) 67 if err != nil && !errors.IsNotSupported(err) { 68 return nil, false, errors.Annotate(err, "checking allocation support") 69 } 70 return netEnv, ok, nil 71 } 72 73 // CanDeallocateAddresses checks if the current environment can 74 // deallocate IP addresses. 75 func (api *AddresserAPI) CanDeallocateAddresses() params.BoolResult { 76 result := params.BoolResult{} 77 _, ok, err := api.getNetworkingEnviron() 78 if err != nil { 79 result.Error = common.ServerError(err) 80 return result 81 } 82 result.Result = ok 83 return result 84 } 85 86 // CleanupIPAddresses releases and removes the dead IP addresses. 87 func (api *AddresserAPI) CleanupIPAddresses() params.ErrorResult { 88 result := params.ErrorResult{} 89 netEnv, ok, err := api.getNetworkingEnviron() 90 if err != nil { 91 result.Error = common.ServerError(err) 92 return result 93 } 94 if !ok { 95 result.Error = common.ServerError(errors.NotSupportedf("IP address deallocation")) 96 return result 97 } 98 // Retrieve dead addresses, release and remove them. 99 logger.Debugf("retrieving dead IP addresses") 100 ipAddresses, err := api.st.DeadIPAddresses() 101 if err != nil { 102 err = errors.Annotate(err, "getting dead addresses") 103 result.Error = common.ServerError(err) 104 return result 105 } 106 canRetry := false 107 for _, ipAddress := range ipAddresses { 108 ipAddressValue := ipAddress.Value() 109 logger.Debugf("releasing dead IP address %q", ipAddressValue) 110 err := api.releaseIPAddress(netEnv, ipAddress) 111 if err != nil { 112 logger.Warningf("cannot release IP address %q: %v (will retry)", ipAddressValue, err) 113 canRetry = true 114 continue 115 } 116 logger.Debugf("removing released IP address %q", ipAddressValue) 117 err = ipAddress.Remove() 118 if errors.IsNotFound(err) { 119 continue 120 } 121 if err != nil { 122 logger.Warningf("failed to remove released IP address %q: %v (will retry)", ipAddressValue, err) 123 canRetry = true 124 continue 125 } 126 } 127 if canRetry { 128 result.Error = common.ServerError(common.ErrTryAgain) 129 } 130 return result 131 } 132 133 // netEnvReleaseAddress is used for testability. 134 var netEnvReleaseAddress = func(env environs.NetworkingEnviron, 135 instId instance.Id, subnetId network.Id, addr network.Address, macAddress, hostname string) error { 136 return env.ReleaseAddress(instId, subnetId, addr, macAddress, hostname) 137 } 138 139 // releaseIPAddress releases one IP address. 140 func (api *AddresserAPI) releaseIPAddress(netEnv environs.NetworkingEnviron, ipAddress StateIPAddress) (err error) { 141 defer errors.DeferredAnnotatef(&err, "failed to release IP address %q", ipAddress.Value()) 142 logger.Tracef("attempting to release dead IP address %q", ipAddress.Value()) 143 // Final check if IP address is really dead. 144 if ipAddress.Life() != state.Dead { 145 return errors.New("IP address not dead") 146 } 147 // Now release the IP address. 148 subnetId := network.Id(ipAddress.SubnetId()) 149 err = netEnvReleaseAddress(netEnv, ipAddress.InstanceId(), subnetId, ipAddress.Address(), ipAddress.MACAddress(), "") 150 if err != nil { 151 return errors.Trace(err) 152 } 153 return nil 154 } 155 156 // WatchIPAddresses observes changes to the IP addresses. 157 func (api *AddresserAPI) WatchIPAddresses() (params.EntitiesWatchResult, error) { 158 watch := &ipAddressesWatcher{api.st.WatchIPAddresses(), api.st} 159 160 if changes, ok := <-watch.Changes(); ok { 161 mappedChanges, err := watch.MapChanges(changes) 162 if err != nil { 163 return params.EntitiesWatchResult{}, errors.Trace(err) 164 } 165 return params.EntitiesWatchResult{ 166 EntitiesWatcherId: api.resources.Register(watch), 167 Changes: mappedChanges, 168 }, nil 169 } 170 return params.EntitiesWatchResult{}, watcher.EnsureErr(watch) 171 }