Skip to content

Commit

Permalink
Merge pull request #965 from Luap99/network
Browse files Browse the repository at this point in the history
libnetwork/cni: add support for arbitrary ipam plugins
  • Loading branch information
openshift-merge-robot authored Mar 17, 2022
2 parents de09839 + e48d59a commit 4654290
Show file tree
Hide file tree
Showing 13 changed files with 172 additions and 16 deletions.
12 changes: 9 additions & 3 deletions libnetwork/cni/cni_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,21 @@ func findPluginByName(plugins []*libcni.NetworkConfig, name string) bool {
// It returns an array of subnets and an extra bool if dhcp is configured.
func convertIPAMConfToNetwork(network *types.Network, ipam *ipamConfig, confPath string) error {
if ipam.PluginType == types.DHCPIPAMDriver {
network.IPAMOptions["driver"] = types.DHCPIPAMDriver
network.IPAMOptions[types.Driver] = types.DHCPIPAMDriver
return nil
}

if ipam.PluginType != types.HostLocalIPAMDriver {
return errors.Errorf("unsupported ipam plugin %s in %s", ipam.PluginType, confPath)
// This is not an error. While we only support certain ipam drivers, we
// cannot make it fail for unsupported ones. CNI is still able to use them,
// just our translation logic cannot convert this into a Network.
// For the same reason this is not warning, it would just be annoying for
// everyone using a unknown ipam driver.
logrus.Infof("unsupported ipam plugin %q in %s", ipam.PluginType, confPath)
return nil
}

network.IPAMOptions["driver"] = types.HostLocalIPAMDriver
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
for _, r := range ipam.Ranges {
for _, ipam := range r {
s := types.Subnet{}
Expand Down
4 changes: 2 additions & 2 deletions libnetwork/cni/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,12 @@ func createIPMACVLAN(network *types.Network) error {
}
}
if len(network.Subnets) == 0 {
network.IPAMOptions["driver"] = types.DHCPIPAMDriver
network.IPAMOptions[types.Driver] = types.DHCPIPAMDriver
if network.Internal {
return errors.New("internal is not supported with macvlan and dhcp ipam driver")
}
} else {
network.IPAMOptions["driver"] = types.HostLocalIPAMDriver
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
}
return nil
}
41 changes: 36 additions & 5 deletions libnetwork/cni/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ var _ = Describe("Config", func() {
}
logBuffer = bytes.Buffer{}
logrus.SetOutput(&logBuffer)
logrus.SetLevel(logrus.InfoLevel)
})

JustBeforeEach(func() {
Expand All @@ -45,6 +46,7 @@ var _ = Describe("Config", func() {
})

AfterEach(func() {
logrus.SetLevel(logrus.InfoLevel)
os.RemoveAll(cniConfDir)
})

Expand Down Expand Up @@ -1071,6 +1073,8 @@ var _ = Describe("Config", func() {

Context("network load valid existing ones", func() {

numberOfConfigFiles := 0

BeforeEach(func() {
dir := "testfiles/valid"
files, err := ioutil.ReadDir(dir)
Expand All @@ -1088,26 +1092,41 @@ var _ = Describe("Config", func() {
Fail("Failed to copy test files")
}
}
numberOfConfigFiles = len(files)
})

It("load networks from disk", func() {
logrus.SetLevel(logrus.WarnLevel)
nets, err := libpodNet.NetworkList()
Expect(err).To(BeNil())
Expect(nets).To(HaveLen(9))
Expect(nets).To(HaveLen(numberOfConfigFiles))
// test the we do not show logrus warnings/errors
logString := logBuffer.String()
Expect(logString).To(BeEmpty())
})

It("load networks from disk with log level debug", func() {
logrus.SetLevel(logrus.DebugLevel)
nets, err := libpodNet.NetworkList()
Expect(err).To(BeNil())
Expect(nets).To(HaveLen(numberOfConfigFiles))
// check for the unsupported ipam plugin message
logString := logBuffer.String()
Expect(logString).ToNot(BeEmpty())
Expect(logString).To(ContainSubstring("unsupported ipam plugin \\\"\\\" in %s", cniConfDir+"/ipam-none.conflist"))
Expect(logString).To(ContainSubstring("unsupported ipam plugin \\\"\\\" in %s", cniConfDir+"/ipam-empty.conflist"))
Expect(logString).To(ContainSubstring("unsupported ipam plugin \\\"static\\\" in %s", cniConfDir+"/ipam-static.conflist"))
})

It("change network struct fields should not affect network struct in the backend", func() {
nets, err := libpodNet.NetworkList()
Expect(err).To(BeNil())
Expect(nets).To(HaveLen(9))
Expect(nets).To(HaveLen(numberOfConfigFiles))

nets[0].Name = "myname"
nets, err = libpodNet.NetworkList()
Expect(err).To(BeNil())
Expect(nets).To(HaveLen(9))
Expect(nets).To(HaveLen(numberOfConfigFiles))
Expect(nets).ToNot(ContainElement(HaveNetworkName("myname")))

network, err := libpodNet.NetworkInspect("bridge")
Expand Down Expand Up @@ -1144,6 +1163,7 @@ var _ = Describe("Config", func() {
Expect(network.Driver).To(Equal("macvlan"))
Expect(network.Subnets).To(HaveLen(0))
// DHCP
Expect(network.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp"))
})

It("internal network", func() {
Expand Down Expand Up @@ -1230,6 +1250,15 @@ var _ = Describe("Config", func() {
))
})

It("ipam static network", func() {
network, err := libpodNet.NetworkInspect("ipam-static")
Expect(err).To(BeNil())
Expect(network.Name).To(Equal("ipam-static"))
Expect(network.ID).To(HaveLen(64))
Expect(network.Driver).To(Equal("bridge"))
Expect(network.Subnets).To(HaveLen(0))
})

It("network list with filters (name)", func() {
filters := map[string][]string{
"name": {"internal", "bridge"},
Expand Down Expand Up @@ -1304,10 +1333,12 @@ var _ = Describe("Config", func() {

networks, err := libpodNet.NetworkList(filterFuncs...)
Expect(err).To(BeNil())
Expect(networks).To(HaveLen(9))
Expect(networks).To(HaveLen(numberOfConfigFiles))
Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"),
HaveNetworkName("mtu"), HaveNetworkName("vlan"), HaveNetworkName("podman"),
HaveNetworkName("label"), HaveNetworkName("macvlan"), HaveNetworkName("macvlan_mtu"), HaveNetworkName("dualstack")))
HaveNetworkName("label"), HaveNetworkName("macvlan"), HaveNetworkName("macvlan_mtu"),
HaveNetworkName("dualstack"), HaveNetworkName("ipam-none"), HaveNetworkName("ipam-empty"),
HaveNetworkName("ipam-static")))
})

It("network list with filters (label)", func() {
Expand Down
2 changes: 2 additions & 0 deletions libnetwork/cni/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ var _ = Describe("run CNI", func() {
if err != nil {
Fail("Failed to create netns")
}
logrus.SetLevel(logrus.WarnLevel)
})

JustBeforeEach(func() {
Expand All @@ -104,6 +105,7 @@ var _ = Describe("run CNI", func() {
})

AfterEach(func() {
logrus.SetLevel(logrus.InfoLevel)
os.RemoveAll(cniConfDir)

netns.UnmountNS(netNSTest)
Expand Down
34 changes: 34 additions & 0 deletions libnetwork/cni/testfiles/valid/ipam-empty.conflist
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"cniVersion": "0.4.0",
"name": "ipam-empty",
"plugins": [
{
"type": "bridge",
"bridge": "cni-podman124",
"isGateway": true,
"ipMasq": true,
"hairpinMode": true,
"ipam": {}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
},
{
"type": "firewall",
"backend": ""
},
{
"type": "tuning"
},
{
"type": "dnsname",
"domainName": "dns.podman",
"capabilities": {
"aliases": true
}
}
]
}
33 changes: 33 additions & 0 deletions libnetwork/cni/testfiles/valid/ipam-none.conflist
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"cniVersion": "0.4.0",
"name": "ipam-none",
"plugins": [
{
"type": "bridge",
"bridge": "cni-podman123",
"isGateway": true,
"ipMasq": true,
"hairpinMode": true
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
},
{
"type": "firewall",
"backend": ""
},
{
"type": "tuning"
},
{
"type": "dnsname",
"domainName": "dns.podman",
"capabilities": {
"aliases": true
}
}
]
}
49 changes: 49 additions & 0 deletions libnetwork/cni/testfiles/valid/ipam-static.conflist
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"cniVersion": "0.4.0",
"name": "ipam-static",
"plugins": [
{
"type": "bridge",
"bridge": "cni-podman125",
"isGateway": true,
"ipMasq": true,
"hairpinMode": true,
"ipam": {
"type": "static",
"routes": [
{
"dst": "0.0.0.0/0"
}
],
"addresses": [
[
{
"subnet": "10.89.0.89/16",
"gateway": "10.89.0.1"
}
]
]
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
},
{
"type": "firewall",
"backend": ""
},
{
"type": "tuning"
},
{
"type": "dnsname",
"domainName": "dns.podman",
"capabilities": {
"aliases": true
}
}
]
}
4 changes: 2 additions & 2 deletions libnetwork/internal/util/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func CreateBridge(n NetUtil, network *types.Network, usedNetworks []*net.IPNet,
}
}

if network.IPAMOptions["driver"] != types.DHCPIPAMDriver {
if network.IPAMOptions[types.Driver] != types.DHCPIPAMDriver {
if len(network.Subnets) == 0 {
freeSubnet, err := GetFreeIPv4NetworkSubnet(usedNetworks, subnetPools)
if err != nil {
Expand Down Expand Up @@ -63,7 +63,7 @@ func CreateBridge(n NetUtil, network *types.Network, usedNetworks []*net.IPNet,
network.Subnets = append(network.Subnets, *freeSubnet)
}
}
network.IPAMOptions["driver"] = types.HostLocalIPAMDriver
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
}
return nil
}
2 changes: 1 addition & 1 deletion libnetwork/internal/util/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func validatePerNetworkOpts(network *types.Network, netOpts *types.PerNetworkOpt
if netOpts.InterfaceName == "" {
return errors.Errorf("interface name on network %s is empty", network.Name)
}
if network.IPAMOptions["driver"] == types.HostLocalIPAMDriver {
if network.IPAMOptions[types.Driver] == types.HostLocalIPAMDriver {
outer:
for _, ip := range netOpts.StaticIPs {
for _, s := range network.Subnets {
Expand Down
2 changes: 1 addition & 1 deletion libnetwork/netavark/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func createMacvlan(network *types.Network) error {
if len(network.Subnets) == 0 {
return errors.Errorf("macvlan driver needs at least one subnet specified, DHCP is not supported with netavark")
}
network.IPAMOptions["driver"] = types.HostLocalIPAMDriver
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver

// validate the given options, we do not need them but just check to make sure they are valid
for key, value := range network.Options {
Expand Down
2 changes: 1 addition & 1 deletion libnetwork/netavark/ipam.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ func (n *netavarkNetwork) deallocIPs(opts *types.NetworkOptions) error {
// it checks the ipam driver and if subnets are set
func requiresIPAMAlloc(network *types.Network) bool {
// only do host allocation when driver is set to HostLocalIPAMDriver or unset
switch network.IPAMOptions["driver"] {
switch network.IPAMOptions[types.Driver] {
case "", types.HostLocalIPAMDriver:
default:
return false
Expand Down
1 change: 1 addition & 0 deletions libnetwork/types/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const (
IPVLANNetworkDriver = "ipvlan"

// IPAM drivers
Driver = "driver"
// HostLocalIPAMDriver store the ip
HostLocalIPAMDriver = "host-local"
// DHCPIPAMDriver get subnet and ip from dhcp server
Expand Down
2 changes: 1 addition & 1 deletion libnetwork/util/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func createFilterFuncs(key string, filterValues []string) (types.FilterFunc, err
return util.StringMatchRegexSlice(net.Name, filterValues)
}, nil

case "driver":
case types.Driver:
// matches network driver
return func(net types.Network) bool {
return util.StringInSlice(net.Driver, filterValues)
Expand Down

0 comments on commit 4654290

Please sign in to comment.