plugins/ipam: round robin ip allocation for host-local ipam

This changes the ip allocation logic to round robin. Before this, host-local IPAM searched for available IPs from start of subnet. Hence it tends to allocate IPs that had been used recently. This is not ideal since it may cause collisions.
This commit is contained in:
Minhan Xia
2016-06-02 11:37:05 -07:00
committed by Stefan Junker
parent 5c3c171642
commit 2445a960a9
8 changed files with 296 additions and 4 deletions

View File

@ -15,12 +15,15 @@
package disk
import (
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
)
const lastIPFile = "last_reserved_ip"
var defaultDataDir = "/var/lib/cni/networks"
type Store struct {
@ -59,9 +62,25 @@ func (s *Store) Reserve(id string, ip net.IP) (bool, error) {
os.Remove(f.Name())
return false, err
}
// store the reserved ip in lastIPFile
ipfile := filepath.Join(s.dataDir, lastIPFile)
err = ioutil.WriteFile(ipfile, []byte(ip.String()), 0644)
if err != nil {
return false, err
}
return true, nil
}
// LastReservedIP returns the last reserved IP if exists
func (s *Store) LastReservedIP() (net.IP, error) {
ipfile := filepath.Join(s.dataDir, lastIPFile)
data, err := ioutil.ReadFile(ipfile)
if err != nil {
return nil, fmt.Errorf("Failed to retrieve last reserved ip: %v", err)
}
return net.ParseIP(string(data)), nil
}
func (s *Store) Release(ip net.IP) error {
return os.Remove(filepath.Join(s.dataDir, ip.String()))
}

View File

@ -21,6 +21,7 @@ type Store interface {
Unlock() error
Close() error
Reserve(id string, ip net.IP) (bool, error)
LastReservedIP() (net.IP, error)
Release(ip net.IP) error
ReleaseByID(id string) error
}

View File

@ -0,0 +1,72 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package testing
import (
"net"
)
type FakeStore struct {
ipMap map[string]string
lastReservedIP net.IP
}
func NewFakeStore(ipmap map[string]string, lastIP net.IP) *FakeStore {
return &FakeStore{ipmap, lastIP}
}
func (s *FakeStore) Lock() error {
return nil
}
func (s *FakeStore) Unlock() error {
return nil
}
func (s *FakeStore) Close() error {
return nil
}
func (s *FakeStore) Reserve(id string, ip net.IP) (bool, error) {
key := ip.String()
if _, ok := s.ipMap[key]; !ok {
s.ipMap[key] = id
s.lastReservedIP = ip
return true, nil
}
return false, nil
}
func (s *FakeStore) LastReservedIP() (net.IP, error) {
return s.lastReservedIP, nil
}
func (s *FakeStore) Release(ip net.IP) error {
delete(s.ipMap, ip.String())
return nil
}
func (s *FakeStore) ReleaseByID(id string) error {
toDelete := []string{}
for k, v := range s.ipMap {
if v == id {
toDelete = append(toDelete, k)
}
}
for _, ip := range toDelete {
delete(s.ipMap, ip)
}
return nil
}