summaryrefslogtreecommitdiff
path: root/libpod/network/netavark/ipam_test.go
diff options
context:
space:
mode:
authorPaul Holzinger <pholzing@redhat.com>2021-10-20 15:55:22 +0200
committerPaul Holzinger <pholzing@redhat.com>2021-11-11 16:25:19 +0100
commit4febe557692aeec8ca9d9b9cdc732772ba7d5876 (patch)
tree0d8bd9c4d3809ca099315fb2437ece00690260be /libpod/network/netavark/ipam_test.go
parenteaae29462880aa0fb17e8d448cc79519e070e64f (diff)
downloadpodman-4febe557692aeec8ca9d9b9cdc732772ba7d5876.tar.gz
podman-4febe557692aeec8ca9d9b9cdc732772ba7d5876.tar.bz2
podman-4febe557692aeec8ca9d9b9cdc732772ba7d5876.zip
netavark IPAM assignment
Add a new boltdb to handle IPAM assignment. The db structure is the following: Each network has their own bucket with the network name as bucket key. Inside the network bucket there is an ID bucket which maps the container ID (key) to a json array of ip addresses (value). The network bucket also has a bucket for each subnet, the subnet is used as key. Inside the subnet bucket an ip is used as key and the container ID as value. The db should be stored on a tmpfs to ensure we always have a clean state after a reboot. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
Diffstat (limited to 'libpod/network/netavark/ipam_test.go')
-rw-r--r--libpod/network/netavark/ipam_test.go433
1 files changed, 433 insertions, 0 deletions
diff --git a/libpod/network/netavark/ipam_test.go b/libpod/network/netavark/ipam_test.go
new file mode 100644
index 000000000..4b3947501
--- /dev/null
+++ b/libpod/network/netavark/ipam_test.go
@@ -0,0 +1,433 @@
+package netavark
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "os"
+ "path/filepath"
+
+ "github.com/containers/podman/v3/libpod/network/types"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/sirupsen/logrus"
+)
+
+var _ = Describe("IPAM", func() {
+ var (
+ networkInterface *netavarkNetwork
+ networkConfDir string
+ logBuffer bytes.Buffer
+ )
+
+ BeforeEach(func() {
+ var err error
+ networkConfDir, err = ioutil.TempDir("", "podman_netavark_test")
+ if err != nil {
+ Fail("Failed to create tmpdir")
+
+ }
+ logBuffer = bytes.Buffer{}
+ logrus.SetOutput(&logBuffer)
+ })
+
+ JustBeforeEach(func() {
+ libpodNet, err := NewNetworkInterface(InitConfig{
+ NetworkConfigDir: networkConfDir,
+ IPAMDBPath: filepath.Join(networkConfDir, "ipam.db"),
+ LockFile: filepath.Join(networkConfDir, "netavark.lock"),
+ })
+ if err != nil {
+ Fail("Failed to create NewCNINetworkInterface")
+ }
+
+ networkInterface = libpodNet.(*netavarkNetwork)
+ // run network list to force a network load
+ networkInterface.NetworkList()
+ })
+
+ AfterEach(func() {
+ os.RemoveAll(networkConfDir)
+ })
+
+ It("simple ipam alloc", func() {
+ netName := types.DefaultNetworkName
+ for i := 2; i < 100; i++ {
+ opts := &types.NetworkOptions{
+ ContainerID: "someContainerID",
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+
+ err := networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.88.0.%d", i)).To4()))
+ }
+ })
+
+ It("ipam try to alloc same ip", func() {
+ netName := types.DefaultNetworkName
+ opts := &types.NetworkOptions{
+ ContainerID: "someContainerID",
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+
+ err := networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP("10.88.0.2").To4()))
+
+ opts = &types.NetworkOptions{
+ ContainerID: "otherID",
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {StaticIPs: []net.IP{net.ParseIP("10.88.0.2")}},
+ },
+ }
+ err = networkInterface.allocIPs(opts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("IPAM error: requested ip address 10.88.0.2 is already allocated to container ID someContainerID"))
+ })
+
+ It("ipam try to alloc more ips as in range", func() {
+ s, _ := types.ParseCIDR("10.0.0.1/24")
+ network, err := networkInterface.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {
+ Subnet: s,
+ LeaseRange: &types.LeaseRange{
+ StartIP: net.ParseIP("10.0.0.10"),
+ EndIP: net.ParseIP("10.0.0.20"),
+ },
+ },
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName := network.Name
+
+ for i := 10; i < 21; i++ {
+ opts := &types.NetworkOptions{
+ ContainerID: fmt.Sprintf("someContainerID-%d", i),
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+
+ err = networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.0.0.%d", i)).To4()))
+ }
+
+ opts := &types.NetworkOptions{
+ ContainerID: "someContainerID-22",
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+
+ // now this should fail because all free ips are already assigned
+ err = networkInterface.allocIPs(opts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("IPAM error: failed to find free IP in range: 10.0.0.10 - 10.0.0.20"))
+ })
+
+ It("ipam basic setup", func() {
+ netName := types.DefaultNetworkName
+ opts := &types.NetworkOptions{
+ ContainerID: "someContainerID",
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+
+ expectedIP := net.ParseIP("10.88.0.2").To4()
+
+ err := networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(expectedIP))
+
+ // remove static ips from opts
+ netOpts := opts.Networks[netName]
+ netOpts.StaticIPs = nil
+ opts.Networks[netName] = netOpts
+
+ err = networkInterface.getAssignedIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(expectedIP))
+
+ err = networkInterface.allocIPs(opts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("IPAM error: requested ip address 10.88.0.2 is already allocated to container ID someContainerID"))
+
+ // dealloc the ip
+ err = networkInterface.deallocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(expectedIP))
+ })
+
+ It("ipam dual stack", func() {
+ s1, _ := types.ParseCIDR("10.0.0.0/26")
+ s2, _ := types.ParseCIDR("fd80::/24")
+ network, err := networkInterface.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {
+ Subnet: s1,
+ },
+ {
+ Subnet: s2,
+ },
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName := network.Name
+
+ opts := &types.NetworkOptions{
+ ContainerID: "someContainerID",
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+
+ err = networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(2))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4()))
+ Expect(opts.Networks[netName].StaticIPs[1]).To(Equal(net.ParseIP("fd80::2")))
+
+ // remove static ips from opts
+ netOpts := opts.Networks[netName]
+ netOpts.StaticIPs = nil
+ opts.Networks[netName] = netOpts
+
+ err = networkInterface.getAssignedIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(2))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4()))
+ Expect(opts.Networks[netName].StaticIPs[1]).To(Equal(net.ParseIP("fd80::2")))
+
+ err = networkInterface.deallocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+
+ // try to alloc the same again
+ err = networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(2))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4()))
+ Expect(opts.Networks[netName].StaticIPs[1]).To(Equal(net.ParseIP("fd80::2")))
+ })
+
+ It("ipam with two networks", func() {
+ s, _ := types.ParseCIDR("10.0.0.0/24")
+ network, err := networkInterface.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {
+ Subnet: s,
+ },
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName1 := network.Name
+
+ s, _ = types.ParseCIDR("10.0.1.0/24")
+ network, err = networkInterface.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {
+ Subnet: s,
+ },
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName2 := network.Name
+
+ opts := &types.NetworkOptions{
+ ContainerID: "someContainerID",
+ Networks: map[string]types.PerNetworkOptions{
+ netName1: {},
+ netName2: {},
+ },
+ }
+
+ err = networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName1))
+ Expect(opts.Networks[netName1].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName1].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4()))
+ Expect(opts.Networks).To(HaveKey(netName2))
+ Expect(opts.Networks[netName2].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName2].StaticIPs[0]).To(Equal(net.ParseIP("10.0.1.2").To4()))
+
+ // remove static ips from opts
+ netOpts := opts.Networks[netName1]
+ netOpts.StaticIPs = nil
+ opts.Networks[netName1] = netOpts
+ netOpts = opts.Networks[netName2]
+ netOpts.StaticIPs = nil
+ opts.Networks[netName2] = netOpts
+
+ err = networkInterface.getAssignedIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName1))
+ Expect(opts.Networks[netName1].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName1].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4()))
+ Expect(opts.Networks).To(HaveKey(netName2))
+ Expect(opts.Networks[netName2].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName2].StaticIPs[0]).To(Equal(net.ParseIP("10.0.1.2").To4()))
+
+ err = networkInterface.deallocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+
+ // try to alloc the same again
+ err = networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName1))
+ Expect(opts.Networks[netName1].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName1].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4()))
+ Expect(opts.Networks).To(HaveKey(netName2))
+ Expect(opts.Networks[netName2].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName2].StaticIPs[0]).To(Equal(net.ParseIP("10.0.1.2").To4()))
+ })
+
+ It("ipam alloc more ips as in subnet", func() {
+ s, _ := types.ParseCIDR("10.0.0.0/26")
+ network, err := networkInterface.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {
+ Subnet: s,
+ },
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName := network.Name
+
+ for i := 2; i < 64; i++ {
+ opts := &types.NetworkOptions{
+ ContainerID: fmt.Sprintf("id-%d", i),
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+ err = networkInterface.allocIPs(opts)
+ if i < 63 {
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.0.0.%d", i)).To4()))
+ } else {
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("IPAM error: failed to find free IP in range: 10.0.0.1 - 10.0.0.62"))
+ }
+ }
+ })
+
+ It("ipam alloc -> dealloc -> alloc", func() {
+ s, _ := types.ParseCIDR("10.0.0.0/27")
+ network, err := networkInterface.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {
+ Subnet: s,
+ },
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName := network.Name
+
+ for i := 2; i < 10; i++ {
+ opts := types.NetworkOptions{
+ ContainerID: fmt.Sprintf("id-%d", i),
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+ err = networkInterface.allocIPs(&opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.0.0.%d", i)).To4()))
+
+ err = networkInterface.deallocIPs(&opts)
+ Expect(err).ToNot(HaveOccurred())
+ }
+
+ for i := 0; i < 30; i++ {
+ opts := types.NetworkOptions{
+ ContainerID: fmt.Sprintf("id-%d", i),
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+ err = networkInterface.allocIPs(&opts)
+ if i < 29 {
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ // The (i+8)%29+2 part looks cryptic but it is actually simple, we already have 8 ips allocated above
+ // so we expect the 8 available ip. We have 29 assignable ip addresses in this subnet because "i"+8 can
+ // be greater than 30 we have to modulo by 29 to go back to the beginning. Also the first free ip is
+ // network address + 2, so we have to add 2 to the result
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.0.0.%d", (i+8)%29+2)).To4()))
+ } else {
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("IPAM error: failed to find free IP in range: 10.0.0.1 - 10.0.0.30"))
+ }
+ }
+ })
+
+ It("ipam with dhcp driver should not set ips", func() {
+ network, err := networkInterface.NetworkCreate(types.Network{
+ IPAMOptions: map[string]string{
+ "driver": types.DHCPIPAMDriver,
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName := network.Name
+
+ opts := &types.NetworkOptions{
+ ContainerID: "someContainerID",
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+
+ err = networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(0))
+
+ err = networkInterface.getAssignedIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(0))
+
+ // dealloc the ip
+ err = networkInterface.deallocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ })
+
+})