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  }