github.com/cloudfoundry-attic/garden-linux@v0.333.2-candidate/network/subnets/subnets.go (about)

     1  // The subnets package provides a subnet pool from which networks may be dynamically acquired or
     2  // statically reserved.
     3  package subnets
     4  
     5  import (
     6  	"fmt"
     7  	"math"
     8  	"net"
     9  	"sync"
    10  
    11  	"github.com/cloudfoundry-incubator/garden-linux/linux_backend"
    12  	"github.com/pivotal-golang/lager"
    13  )
    14  
    15  // Subnets provides a means of allocating subnets and associated IP addresses.
    16  type Subnets interface {
    17  	// Allocates an IP address and associates it with a subnet. The subnet is selected by the given SubnetSelector.
    18  	// The IP address is selected by the given IPSelector.
    19  	// Returns a subnet, an IP address, and a boolean which is true if and only if this is the
    20  	// first IP address to be associated with this subnet.
    21  	// If either selector fails, an error is returned.
    22  	Acquire(SubnetSelector, IPSelector, lager.Logger) (*linux_backend.Network, error)
    23  
    24  	// Releases an IP address associated with an allocated subnet. If the subnet has no other IP
    25  	// addresses associated with it, it is deallocated.
    26  	// Returns a boolean which is true if and only if the subnet was deallocated.
    27  	// Returns an error if the given combination is not already in the pool.
    28  	Release(*linux_backend.Network, lager.Logger) error
    29  
    30  	// Remove an IP address so it appears to be associated with the given subnet.
    31  	Remove(*linux_backend.Network, lager.Logger) error
    32  
    33  	// Returns the number of /30 subnets which can be Acquired by a DynamicSubnetSelector.
    34  	Capacity() int
    35  }
    36  
    37  type pool struct {
    38  	allocated    map[string][]net.IP // net.IPNet.String +> seq net.IP
    39  	dynamicRange *net.IPNet
    40  	mu           sync.Mutex
    41  }
    42  
    43  //go:generate counterfeiter . SubnetSelector
    44  
    45  // SubnetSelector is a strategy for selecting a subnet.
    46  type SubnetSelector interface {
    47  	// Returns a subnet based on a dynamic range and some existing statically-allocated
    48  	// subnets. If no suitable subnet can be found, returns an error.
    49  	SelectSubnet(dynamic *net.IPNet, existing []*net.IPNet) (*net.IPNet, error)
    50  }
    51  
    52  //go:generate counterfeiter . IPSelector
    53  
    54  // IPSelector is a strategy for selecting an IP address in a subnet.
    55  type IPSelector interface {
    56  	// Returns an IP address in the given subnet which is not one of the given existing
    57  	// IP addresses. If no such IP address can be found, returns an error.
    58  	SelectIP(subnet *net.IPNet, existing []net.IP) (net.IP, error)
    59  }
    60  
    61  // New creates a Subnets implementation from a dynamic allocation range.
    62  // All dynamic allocations come from the range, static allocations are prohibited
    63  // from the dynamic range.
    64  func NewSubnets(ipNet *net.IPNet) (Subnets, error) {
    65  	return &pool{dynamicRange: ipNet, allocated: make(map[string][]net.IP)}, nil
    66  }
    67  
    68  // Acquire uses the given subnet and IP selectors to request a subnet, container IP address combination
    69  // from the pool.
    70  func (p *pool) Acquire(sn SubnetSelector, i IPSelector, logger lager.Logger) (network *linux_backend.Network, err error) {
    71  	p.mu.Lock()
    72  	defer p.mu.Unlock()
    73  
    74  	logger = logger.Session("acquire")
    75  
    76  	network = &linux_backend.Network{}
    77  
    78  	allocatedSubnets := subnets(p.allocated)
    79  	logger.Info("subnet-selecting", lager.Data{"allocated-subnets": subnetsStr(allocatedSubnets)})
    80  	if network.Subnet, err = sn.SelectSubnet(p.dynamicRange, allocatedSubnets); err != nil {
    81  		logger.Error("subnet-selecting-failed", err)
    82  		return nil, err
    83  	}
    84  	logger.Info("subnet-selected", lager.Data{"subnet": network.Subnet.String()})
    85  
    86  	ips := p.allocated[network.Subnet.String()]
    87  	logger.Info("ip-selecting", lager.Data{"allocated-ips": ipsStr(ips)})
    88  	allocatedIPs := append(ips, NetworkIP(network.Subnet), GatewayIP(network.Subnet), BroadcastIP(network.Subnet))
    89  	if network.IP, err = i.SelectIP(network.Subnet, allocatedIPs); err != nil {
    90  		logger.Error("ip-selecting-failed", err)
    91  		return nil, err
    92  	}
    93  	logger.Info("ip-selected", lager.Data{"ip": network.IP.String()})
    94  
    95  	p.allocated[network.Subnet.String()] = append(ips, network.IP)
    96  	logger.Info("new-allocated", lager.Data{"allocated-ips": ipsStr(p.allocated[network.Subnet.String()])})
    97  
    98  	return network, nil
    99  }
   100  
   101  // Remove re-allocates a given subnet and ip address combination in the pool. It returns
   102  // an error if the combination is already allocated.
   103  func (p *pool) Remove(network *linux_backend.Network, logger lager.Logger) error {
   104  	p.mu.Lock()
   105  	defer p.mu.Unlock()
   106  
   107  	logger = logger.Session("release")
   108  
   109  	if network.IP == nil {
   110  		return ErrIpCannotBeNil
   111  	}
   112  
   113  	for _, existing := range p.allocated[network.Subnet.String()] {
   114  		if existing.Equal(network.IP) {
   115  			return ErrOverlapsExistingSubnet
   116  		}
   117  	}
   118  
   119  	p.allocated[network.Subnet.String()] = append(p.allocated[network.Subnet.String()], network.IP)
   120  	logger.Info("new-allocated", lager.Data{"allocated-ips": ipsStr(p.allocated[network.Subnet.String()]), "subnet": network.Subnet.String()})
   121  
   122  	return nil
   123  }
   124  
   125  func (p *pool) Release(network *linux_backend.Network, logger lager.Logger) error {
   126  	p.mu.Lock()
   127  	defer p.mu.Unlock()
   128  
   129  	logger = logger.Session("release")
   130  
   131  	subnetString := network.Subnet.String()
   132  	ips := p.allocated[subnetString]
   133  
   134  	if i, found := indexOf(ips, network.IP); found {
   135  		if reducedIps, empty := removeIPAtIndex(ips, i); empty {
   136  			delete(p.allocated, subnetString)
   137  			logger.Info("changing-allocated-subnets", lager.Data{"allocated-subnets": subnetsStr(subnets(p.allocated))})
   138  		} else {
   139  			p.allocated[subnetString] = reducedIps
   140  			logger.Info("changing-allocated-ips", lager.Data{"allocated-ips": ipsStr(reducedIps)})
   141  		}
   142  
   143  		return nil
   144  	}
   145  
   146  	return ErrReleasedUnallocatedSubnet
   147  }
   148  
   149  // Capacity returns the number of /30 subnets that can be allocated
   150  // from the pool's dynamic allocation range.
   151  func (m *pool) Capacity() int {
   152  	masked, total := m.dynamicRange.Mask.Size()
   153  	return int(math.Pow(2, float64(total-masked)) / 4)
   154  }
   155  
   156  // Returns the gateway IP of a given subnet, which is always the maximum valid IP
   157  func GatewayIP(subnet *net.IPNet) net.IP {
   158  	return next(subnet.IP)
   159  }
   160  
   161  // Returns the network IP of a subnet.
   162  func NetworkIP(subnet *net.IPNet) net.IP {
   163  	return subnet.IP
   164  }
   165  
   166  // Returns the broadcast IP of a subnet.
   167  func BroadcastIP(subnet *net.IPNet) net.IP {
   168  	return max(subnet)
   169  }
   170  
   171  // returns the keys in the given map whose values are non-empty slices
   172  func subnets(m map[string][]net.IP) (result []*net.IPNet) {
   173  	for k, v := range m {
   174  		if len(v) > 0 {
   175  			_, ipn, err := net.ParseCIDR(k)
   176  			if err != nil {
   177  				panic(fmt.Sprintf("failed to parse a CIDR in the subnet pool: %s", err))
   178  			}
   179  
   180  			result = append(result, ipn)
   181  		}
   182  	}
   183  
   184  	return result
   185  }
   186  
   187  func subnetsStr(subnets []*net.IPNet) []string {
   188  	var retVal []string
   189  
   190  	for _, subnet := range subnets {
   191  		retVal = append(retVal, subnet.String())
   192  	}
   193  
   194  	return retVal
   195  }
   196  
   197  func ipsStr(ips []net.IP) []string {
   198  	var retVal []string
   199  
   200  	for _, ip := range ips {
   201  		retVal = append(retVal, ip.String())
   202  	}
   203  
   204  	return retVal
   205  }
   206  
   207  func indexOf(a []net.IP, w net.IP) (int, bool) {
   208  	for i, v := range a {
   209  		if v.Equal(w) {
   210  			return i, true
   211  		}
   212  	}
   213  
   214  	return -1, false
   215  }
   216  
   217  // removeAtIndex removes from a slice at the given index,
   218  // and returns the new slice and boolean, true iff the new slice is empty.
   219  func removeIPAtIndex(ips []net.IP, i int) ([]net.IP, bool) {
   220  	l := len(ips)
   221  	ips[i] = ips[l-1]
   222  	ips = ips[:l-1]
   223  	return ips, l == 1
   224  }