Merge pull request #967 from Luap99/ipam-none

libnetwork: allow new none ipam driver
This commit is contained in:
OpenShift Merge Robot 2022-03-29 13:39:46 -04:00 committed by GitHub
commit 8c96512c34
12 changed files with 582 additions and 147 deletions

View File

@ -128,21 +128,12 @@ func findPluginByName(plugins []*libcni.NetworkConfig, name string) bool {
// convertIPAMConfToNetwork converts A cni IPAMConfig to libpod network subnets.
// 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 {
switch ipam.PluginType {
case "":
network.IPAMOptions[types.Driver] = types.NoneIPAMDriver
case types.DHCPIPAMDriver:
network.IPAMOptions[types.Driver] = types.DHCPIPAMDriver
return nil
}
if ipam.PluginType != types.HostLocalIPAMDriver {
// 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
}
case types.HostLocalIPAMDriver:
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
for _, r := range ipam.Ranges {
for _, ipam := range r {
@ -199,6 +190,15 @@ func convertIPAMConfToNetwork(network *types.Network, ipam *ipamConfig, confPath
network.Subnets = append(network.Subnets, s)
}
}
default:
// 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)
network.IPAMOptions[types.Driver] = ipam.PluginType
}
return nil
}
@ -225,10 +225,13 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ
var (
routes []ipamRoute
ipamRanges [][]ipamLocalHostRangeConf
ipamConf ipamConfig
ipamConf *ipamConfig
err error
)
if len(network.Subnets) > 0 {
ipamDriver := network.IPAMOptions[types.Driver]
switch ipamDriver {
case types.HostLocalIPAMDriver:
defIpv4Route := false
defIpv6Route := false
for _, subnet := range network.Subnets {
@ -257,48 +260,22 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ
routes = append(routes, route)
}
}
ipamConf = newIPAMHostLocalConf(routes, ipamRanges)
} else {
ipamConf = ipamConfig{PluginType: "dhcp"}
conf := newIPAMHostLocalConf(routes, ipamRanges)
ipamConf = &conf
case types.DHCPIPAMDriver:
ipamConf = &ipamConfig{PluginType: "dhcp"}
case types.NoneIPAMDriver:
// do nothing
default:
return nil, "", errors.Errorf("unsupported ipam driver %q", ipamDriver)
}
vlan := 0
mtu := 0
vlanPluginMode := ""
for k, v := range network.Options {
switch k {
case "mtu":
mtu, err = internalutil.ParseMTU(v)
opts, err := parseOptions(network.Options, network.Driver)
if err != nil {
return nil, "", err
}
case "vlan":
vlan, err = internalutil.ParseVlan(v)
if err != nil {
return nil, "", err
}
case "mode":
switch network.Driver {
case types.MacVLANNetworkDriver:
if !pkgutil.StringInSlice(v, types.ValidMacVLANModes) {
return nil, "", errors.Errorf("unknown macvlan mode %q", v)
}
case types.IPVLANNetworkDriver:
if !pkgutil.StringInSlice(v, types.ValidIPVLANModes) {
return nil, "", errors.Errorf("unknown ipvlan mode %q", v)
}
default:
return nil, "", errors.Errorf("cannot set option \"mode\" with driver %q", network.Driver)
}
vlanPluginMode = v
default:
return nil, "", errors.Errorf("unsupported network option %s", k)
}
}
isGateway := true
ipMasq := true
if network.Internal {
@ -314,7 +291,7 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ
switch network.Driver {
case types.BridgeNetworkDriver:
bridge := newHostLocalBridge(network.NetworkInterface, isGateway, ipMasq, mtu, vlan, &ipamConf)
bridge := newHostLocalBridge(network.NetworkInterface, isGateway, ipMasq, opts.mtu, opts.vlan, ipamConf)
plugins = append(plugins, bridge, newPortMapPlugin(), newFirewallPlugin(), newTuningPlugin())
// if we find the dnsname plugin we add configuration for it
if hasDNSNamePlugin(n.cniPluginDirs) && network.DNSEnabled {
@ -323,10 +300,10 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ
}
case types.MacVLANNetworkDriver:
plugins = append(plugins, newVLANPlugin(types.MacVLANNetworkDriver, network.NetworkInterface, vlanPluginMode, mtu, &ipamConf))
plugins = append(plugins, newVLANPlugin(types.MacVLANNetworkDriver, network.NetworkInterface, opts.vlanPluginMode, opts.mtu, ipamConf))
case types.IPVLANNetworkDriver:
plugins = append(plugins, newVLANPlugin(types.IPVLANNetworkDriver, network.NetworkInterface, vlanPluginMode, mtu, &ipamConf))
plugins = append(plugins, newVLANPlugin(types.IPVLANNetworkDriver, network.NetworkInterface, opts.vlanPluginMode, opts.mtu, ipamConf))
default:
return nil, "", errors.Errorf("driver %q is not supported by cni", network.Driver)
@ -402,3 +379,48 @@ func removeMachinePlugin(conf *libcni.NetworkConfigList) *libcni.NetworkConfigLi
conf.Plugins = plugins
return conf
}
type options struct {
vlan int
mtu int
vlanPluginMode string
}
func parseOptions(networkOptions map[string]string, networkDriver string) (*options, error) {
opt := &options{}
var err error
for k, v := range networkOptions {
switch k {
case "mtu":
opt.mtu, err = internalutil.ParseMTU(v)
if err != nil {
return nil, err
}
case "vlan":
opt.vlan, err = internalutil.ParseVlan(v)
if err != nil {
return nil, err
}
case "mode":
switch networkDriver {
case types.MacVLANNetworkDriver:
if !pkgutil.StringInSlice(v, types.ValidMacVLANModes) {
return nil, errors.Errorf("unknown macvlan mode %q", v)
}
case types.IPVLANNetworkDriver:
if !pkgutil.StringInSlice(v, types.ValidIPVLANModes) {
return nil, errors.Errorf("unknown ipvlan mode %q", v)
}
default:
return nil, errors.Errorf("cannot set option \"mode\" with driver %q", networkDriver)
}
opt.vlanPluginMode = v
default:
return nil, errors.Errorf("unsupported network option %s", k)
}
}
return opt, nil
}

View File

@ -145,12 +145,14 @@ func newHostLocalBridge(name string, isGateWay, ipMasq bool, mtu, vlan int, ipam
MTU: mtu,
HairpinMode: true,
Vlan: vlan,
IPAM: *ipamConf,
}
if ipamConf != nil {
bridge.IPAM = *ipamConf
// if we use host-local set the ips cap to ensure we can set static ips via runtime config
if ipamConf.PluginType == types.HostLocalIPAMDriver {
bridge.Capabilities = caps
}
}
return &bridge
}
@ -259,7 +261,9 @@ func hasDNSNamePlugin(paths []string) bool {
func newVLANPlugin(pluginType, device, mode string, mtu int, ipam *ipamConfig) VLANConfig {
m := VLANConfig{
PluginType: pluginType,
IPAM: *ipam,
}
if ipam != nil {
m.IPAM = *ipam
}
if mtu > 0 {
m.MTU = mtu

View File

@ -53,6 +53,11 @@ func (n *cniNetwork) networkCreate(newNetwork *types.Network, defaultNet bool) (
return nil, err
}
err = validateIPAMDriver(newNetwork)
if err != nil {
return nil, err
}
// Only get the used networks for validation if we do not create the default network.
// The default network should not be validated against used subnets, we have to ensure
// that this network can always be created even when a subnet is already used on the host.
@ -197,13 +202,38 @@ func createIPMACVLAN(network *types.Network) error {
return errors.Errorf("parent interface %s does not exist", network.NetworkInterface)
}
}
switch network.IPAMOptions[types.Driver] {
// set default
case "":
if len(network.Subnets) == 0 {
// if no subnets and no driver choose dhcp
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[types.Driver] = types.HostLocalIPAMDriver
}
case types.HostLocalIPAMDriver:
if len(network.Subnets) == 0 {
return errors.New("host-local ipam driver set but no subnets are given")
}
}
if network.IPAMOptions[types.Driver] == types.DHCPIPAMDriver && network.Internal {
return errors.New("internal is not supported with macvlan and dhcp ipam driver")
}
return nil
}
func validateIPAMDriver(n *types.Network) error {
ipamDriver := n.IPAMOptions[types.Driver]
switch ipamDriver {
case "", types.HostLocalIPAMDriver:
case types.DHCPIPAMDriver, types.NoneIPAMDriver:
if len(n.Subnets) > 0 {
return errors.Errorf("%s ipam driver is set but subnets are given", ipamDriver)
}
default:
return errors.Errorf("unsupported ipam driver %q", ipamDriver)
}
return nil
}

View File

@ -167,6 +167,86 @@ var _ = Describe("Config", func() {
Expect(network1.Internal).To(BeFalse())
})
It("create bridge config with none ipam driver", func() {
network := types.Network{
Driver: "bridge",
IPAMOptions: map[string]string{
"driver": "none",
},
}
network1, err := libpodNet.NetworkCreate(network)
Expect(err).To(BeNil())
Expect(network1.Driver).To(Equal("bridge"))
Expect(network1.IPAMOptions).ToNot(BeEmpty())
Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "none"))
Expect(network1.Subnets).To(HaveLen(0))
// reload configs from disk
libpodNet, err = getNetworkInterface(cniConfDir)
Expect(err).To(BeNil())
network2, err := libpodNet.NetworkInspect(network1.Name)
Expect(err).To(BeNil())
Expect(network2).To(Equal(network1))
})
It("create bridge config with none ipam driver and subnets", func() {
subnet := "10.1.0.0/24"
n, _ := types.ParseCIDR(subnet)
network := types.Network{
Driver: "bridge",
IPAMOptions: map[string]string{
"driver": "none",
},
Subnets: []types.Subnet{
{Subnet: n},
},
}
_, err := libpodNet.NetworkCreate(network)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal("none ipam driver is set but subnets are given"))
})
It("create bridge config with dhcp ipam driver", func() {
network := types.Network{
Driver: "bridge",
IPAMOptions: map[string]string{
"driver": "dhcp",
},
}
network1, err := libpodNet.NetworkCreate(network)
Expect(err).To(BeNil())
Expect(network1.Driver).To(Equal("bridge"))
Expect(network1.IPAMOptions).ToNot(BeEmpty())
Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp"))
Expect(network1.Subnets).To(HaveLen(0))
// reload configs from disk
libpodNet, err = getNetworkInterface(cniConfDir)
Expect(err).To(BeNil())
network2, err := libpodNet.NetworkInspect(network1.Name)
Expect(err).To(BeNil())
Expect(network2).To(Equal(network1))
})
It("create bridge config with none hdcp driver and subnets", func() {
subnet := "10.1.0.0/24"
n, _ := types.ParseCIDR(subnet)
network := types.Network{
Driver: "bridge",
IPAMOptions: map[string]string{
"driver": "dhcp",
},
Subnets: []types.Subnet{
{Subnet: n},
},
}
_, err := libpodNet.NetworkCreate(network)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal("dhcp ipam driver is set but subnets are given"))
})
It("create bridge with same name should fail", func() {
network := types.Network{
Driver: "bridge",
@ -1070,6 +1150,18 @@ var _ = Describe("Config", func() {
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("subnet 10.10.0.0/24 is already used on the host or by another config"))
})
It("create network with invalid ipam driver", func() {
network := types.Network{
Driver: "bridge",
IPAMOptions: map[string]string{
"driver": "blah",
},
}
_, err := libpodNet.NetworkCreate(network)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal("unsupported ipam driver \"blah\""))
})
})
Context("network load valid existing ones", func() {
@ -1114,8 +1206,6 @@ var _ = Describe("Config", func() {
// 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"))
})
@ -1258,6 +1348,27 @@ var _ = Describe("Config", func() {
Expect(network.ID).To(HaveLen(64))
Expect(network.Driver).To(Equal("bridge"))
Expect(network.Subnets).To(HaveLen(0))
Expect(network.IPAMOptions).To(HaveKeyWithValue("driver", "static"))
})
It("ipam none network", func() {
network, err := libpodNet.NetworkInspect("ipam-none")
Expect(err).To(BeNil())
Expect(network.Name).To(Equal("ipam-none"))
Expect(network.ID).To(HaveLen(64))
Expect(network.Driver).To(Equal("bridge"))
Expect(network.Subnets).To(HaveLen(0))
Expect(network.IPAMOptions).To(HaveKeyWithValue("driver", "none"))
})
It("ipam empty network", func() {
network, err := libpodNet.NetworkInspect("ipam-empty")
Expect(err).To(BeNil())
Expect(network.Name).To(Equal("ipam-empty"))
Expect(network.ID).To(HaveLen(64))
Expect(network.Driver).To(Equal("bridge"))
Expect(network.Subnets).To(HaveLen(0))
Expect(network.IPAMOptions).To(HaveKeyWithValue("driver", "none"))
})
It("network list with filters (name)", func() {

View File

@ -125,6 +125,17 @@ func CNIResultToStatus(res cnitypes.Result) (types.StatusBlock, error) {
result.DNSSearchDomains = cniResult.DNS.Search
interfaces := make(map[string]types.NetInterface)
for intIndex, netInterface := range cniResult.Interfaces {
// we are only interested about interfaces in the container namespace
if netInterface.Sandbox == "" {
continue
}
mac, err := net.ParseMAC(netInterface.Mac)
if err != nil {
return result, err
}
subnets := make([]types.NetAddress, 0, len(cniResult.IPs))
for _, ip := range cniResult.IPs {
if ip.Interface == nil {
// we do no expect ips without an interface
@ -133,26 +144,18 @@ func CNIResultToStatus(res cnitypes.Result) (types.StatusBlock, error) {
if len(cniResult.Interfaces) <= *ip.Interface {
return result, errors.Errorf("invalid cni result, interface index %d out of range", *ip.Interface)
}
cniInt := cniResult.Interfaces[*ip.Interface]
netInt, ok := interfaces[cniInt.Name]
if ok {
netInt.Subnets = append(netInt.Subnets, types.NetAddress{
// when we have a ip for this interface add it to the subnets
if *ip.Interface == intIndex {
subnets = append(subnets, types.NetAddress{
IPNet: types.IPNet{IPNet: ip.Address},
Gateway: ip.Gateway,
})
interfaces[cniInt.Name] = netInt
} else {
mac, err := net.ParseMAC(cniInt.Mac)
if err != nil {
return result, err
}
interfaces[cniInt.Name] = types.NetInterface{
}
interfaces[netInterface.Name] = types.NetInterface{
MacAddress: types.HardwareAddr(mac),
Subnets: []types.NetAddress{{
IPNet: types.IPNet{IPNet: ip.Address},
Gateway: ip.Gateway,
}},
}
Subnets: subnets,
}
}
result.Interfaces = interfaces

View File

@ -830,6 +830,91 @@ var _ = Describe("run CNI", func() {
Expect(err).To(BeNil())
})
})
It("setup ipam driver none network", func() {
runTest(func() {
network := types.Network{
IPAMOptions: map[string]string{
types.Driver: types.NoneIPAMDriver,
},
}
network1, err := libpodNet.NetworkCreate(network)
Expect(err).To(BeNil())
intName1 := "eth0"
netName1 := network1.Name
setupOpts := types.SetupOptions{
NetworkOptions: types.NetworkOptions{
ContainerID: stringid.GenerateNonCryptoID(),
Networks: map[string]types.PerNetworkOptions{
netName1: {
InterfaceName: intName1,
},
},
},
}
res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
Expect(err).To(BeNil())
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(netName1))
Expect(res[netName1].Interfaces).To(HaveKey(intName1))
Expect(res[netName1].Interfaces[intName1].Subnets).To(HaveLen(0))
macInt1 := res[netName1].Interfaces[intName1].MacAddress
Expect(macInt1).To(HaveLen(6))
// check in the container namespace if the settings are applied
err = netNSContainer.Do(func(_ ns.NetNS) error {
defer GinkgoRecover()
i, err := net.InterfaceByName(intName1)
Expect(err).To(BeNil())
Expect(i.Name).To(Equal(intName1))
Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt1)))
addrs, err := i.Addrs()
Expect(err).To(BeNil())
// we still have the ipv6 link local address
Expect(addrs).To(HaveLen(1))
addr, ok := addrs[0].(*net.IPNet)
Expect(ok).To(BeTrue(), "cast address to ipnet")
// make sure we are link local
Expect(addr.IP.IsLinkLocalUnicast()).To(BeTrue(), "ip is link local address")
// check loopback adapter
i, err = net.InterfaceByName("lo")
Expect(err).To(BeNil())
Expect(i.Name).To(Equal("lo"))
Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
return nil
})
Expect(err).To(BeNil())
err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
Expect(err).To(BeNil())
logString := logBuffer.String()
Expect(logString).To(BeEmpty())
// check in the container namespace that the interface is removed
err = netNSContainer.Do(func(_ ns.NetNS) error {
defer GinkgoRecover()
_, err := net.InterfaceByName(intName1)
Expect(err).To(HaveOccurred())
// check that only the loopback adapter is left
ints, err := net.Interfaces()
Expect(err).To(BeNil())
Expect(ints).To(HaveLen(1))
Expect(ints[0].Name).To(Equal("lo"))
Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
return nil
})
Expect(err).To(BeNil())
})
})
})
Context("network setup test with networks from disk", func() {

View File

@ -27,7 +27,9 @@ func CreateBridge(n NetUtil, network *types.Network, usedNetworks []*net.IPNet,
}
}
if network.IPAMOptions[types.Driver] != types.DHCPIPAMDriver {
ipamDriver := network.IPAMOptions[types.Driver]
// also do this when the driver is unset
if ipamDriver == "" || ipamDriver == types.HostLocalIPAMDriver {
if len(network.Subnets) == 0 {
freeSubnet, err := GetFreeIPv4NetworkSubnet(usedNetworks, subnetPools)
if err != nil {

View File

@ -67,6 +67,11 @@ func (n *netavarkNetwork) networkCreate(newNetwork *types.Network, defaultNet bo
return nil, err
}
err = validateIPAMDriver(newNetwork)
if err != nil {
return nil, err
}
// Only get the used networks for validation if we do not create the default network.
// The default network should not be validated against used subnets, we have to ensure
// that this network can always be created even when a subnet is already used on the host.
@ -153,10 +158,19 @@ func createMacvlan(network *types.Network) error {
return errors.Errorf("parent interface %s does not exist", network.NetworkInterface)
}
}
// we already validated the drivers before so we just have to set the default here
switch network.IPAMOptions[types.Driver] {
case "":
if len(network.Subnets) == 0 {
return errors.Errorf("macvlan driver needs at least one subnet specified, DHCP is not supported with netavark")
return errors.Errorf("macvlan driver needs at least one subnet specified, DHCP is not yet supported with netavark")
}
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
case types.HostLocalIPAMDriver:
if len(network.Subnets) == 0 {
return errors.Errorf("macvlan driver needs at least one subnet specified, when the host-local ipam driver is set")
}
}
// validate the given options, we do not need them but just check to make sure they are valid
for key, value := range network.Options {
@ -246,3 +260,19 @@ func (n *netavarkNetwork) NetworkInspect(nameOrID string) (types.Network, error)
}
return *network, nil
}
func validateIPAMDriver(n *types.Network) error {
ipamDriver := n.IPAMOptions[types.Driver]
switch ipamDriver {
case "", types.HostLocalIPAMDriver:
case types.NoneIPAMDriver:
if len(n.Subnets) > 0 {
return errors.New("none ipam driver is set but subnets are given")
}
case types.DHCPIPAMDriver:
return errors.New("dhcp ipam driver is not yet supported with netavark")
default:
return errors.Errorf("unsupported ipam driver %q", ipamDriver)
}
return nil
}

View File

@ -794,7 +794,7 @@ var _ = Describe("Config", func() {
network := types.Network{Driver: "macvlan"}
_, err := libpodNet.NetworkCreate(network)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal("macvlan driver needs at least one subnet specified, DHCP is not supported with netavark"))
Expect(err.Error()).To(Equal("macvlan driver needs at least one subnet specified, DHCP is not yet supported with netavark"))
})
It("create macvlan config with internal", func() {
@ -957,6 +957,69 @@ var _ = Describe("Config", func() {
Expect(network1.Options).To(HaveKeyWithValue("mtu", "9000"))
})
It("create bridge config with none ipam driver", func() {
network := types.Network{
Driver: "bridge",
IPAMOptions: map[string]string{
"driver": "none",
},
}
network1, err := libpodNet.NetworkCreate(network)
Expect(err).To(BeNil())
Expect(network1.Driver).To(Equal("bridge"))
Expect(network1.IPAMOptions).ToNot(BeEmpty())
Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "none"))
Expect(network1.Subnets).To(HaveLen(0))
// reload configs from disk
libpodNet, err = getNetworkInterface(networkConfDir)
Expect(err).To(BeNil())
network2, err := libpodNet.NetworkInspect(network1.Name)
Expect(err).To(BeNil())
EqualNetwork(network2, network1)
})
It("create bridge config with none ipam driver and subnets", func() {
subnet := "10.1.0.0/24"
n, _ := types.ParseCIDR(subnet)
network := types.Network{
Driver: "bridge",
IPAMOptions: map[string]string{
"driver": "none",
},
Subnets: []types.Subnet{
{Subnet: n},
},
}
_, err := libpodNet.NetworkCreate(network)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal("none ipam driver is set but subnets are given"))
})
It("create macvlan config with none ipam driver", func() {
network := types.Network{
Driver: "macvlan",
IPAMOptions: map[string]string{
"driver": "none",
},
}
network1, err := libpodNet.NetworkCreate(network)
Expect(err).To(BeNil())
Expect(network1.Driver).To(Equal("macvlan"))
Expect(network1.IPAMOptions).ToNot(BeEmpty())
Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "none"))
Expect(network1.Subnets).To(HaveLen(0))
// reload configs from disk
libpodNet, err = getNetworkInterface(networkConfDir)
Expect(err).To(BeNil())
network2, err := libpodNet.NetworkInspect(network1.Name)
Expect(err).To(BeNil())
EqualNetwork(network2, network1)
})
})
Context("network load valid existing ones", func() {

View File

@ -399,10 +399,10 @@ var _ = Describe("IPAM", func() {
}
})
It("ipam with dhcp driver should not set ips", func() {
It("ipam with none driver should not set ips", func() {
network, err := networkInterface.NetworkCreate(types.Network{
IPAMOptions: map[string]string{
"driver": types.DHCPIPAMDriver,
"driver": types.NoneIPAMDriver,
},
})
Expect(err).ToNot(HaveOccurred())

View File

@ -700,6 +700,89 @@ var _ = Describe("run netavark", func() {
Expect(err.Error()).To(ContainSubstring("interface eth0 already exists on container namespace"))
})
})
It("setup ipam driver none network", func() {
runTest(func() {
network := types.Network{
IPAMOptions: map[string]string{
types.Driver: types.NoneIPAMDriver,
},
}
network1, err := libpodNet.NetworkCreate(network)
Expect(err).To(BeNil())
intName1 := "eth0"
netName1 := network1.Name
setupOpts := types.SetupOptions{
NetworkOptions: types.NetworkOptions{
ContainerID: stringid.GenerateNonCryptoID(),
Networks: map[string]types.PerNetworkOptions{
netName1: {
InterfaceName: intName1,
},
},
},
}
res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
Expect(err).To(BeNil())
Expect(res).To(HaveLen(1))
Expect(res).To(HaveKey(netName1))
Expect(res[netName1].Interfaces).To(HaveKey(intName1))
Expect(res[netName1].Interfaces[intName1].Subnets).To(HaveLen(0))
macInt1 := res[netName1].Interfaces[intName1].MacAddress
Expect(macInt1).To(HaveLen(6))
// check in the container namespace if the settings are applied
err = netNSContainer.Do(func(_ ns.NetNS) error {
defer GinkgoRecover()
i, err := net.InterfaceByName(intName1)
Expect(err).To(BeNil())
Expect(i.Name).To(Equal(intName1))
Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt1)))
addrs, err := i.Addrs()
Expect(err).To(BeNil())
// we still have the ipv6 link local address
Expect(addrs).To(HaveLen(1))
addr, ok := addrs[0].(*net.IPNet)
Expect(ok).To(BeTrue(), "cast address to ipnet")
// make sure we are link local
Expect(addr.IP.IsLinkLocalUnicast()).To(BeTrue(), "ip is link local address")
// check loopback adapter
i, err = net.InterfaceByName("lo")
Expect(err).To(BeNil())
Expect(i.Name).To(Equal("lo"))
Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
return nil
})
Expect(err).To(BeNil())
err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
Expect(err).To(BeNil())
// check in the container namespace that the interface is removed
err = netNSContainer.Do(func(_ ns.NetNS) error {
defer GinkgoRecover()
_, err := net.InterfaceByName(intName1)
Expect(err).To(HaveOccurred())
// check that only the loopback adapter is left
ints, err := net.Interfaces()
Expect(err).To(BeNil())
Expect(ints).To(HaveLen(1))
Expect(ints[0].Name).To(Equal("lo"))
Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
return nil
})
Expect(err).To(BeNil())
})
})
})
func runNetListener(wg *sync.WaitGroup, protocol, ip string, port int, expectedData string) {

View File

@ -12,10 +12,12 @@ const (
// IPAM drivers
Driver = "driver"
// HostLocalIPAMDriver store the ip
// HostLocalIPAMDriver store the ip locally in a db
HostLocalIPAMDriver = "host-local"
// DHCPIPAMDriver get subnet and ip from dhcp server
DHCPIPAMDriver = "dhcp"
// NoneIPAMDriver do not provide ipam management
NoneIPAMDriver = "none"
// DefaultSubnet is the name that will be used for the default CNI network.
DefaultNetworkName = "podman"