diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 8265972aef..c5a9809577 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -19,6 +19,7 @@ - Defaults for the `--cgroup-config` option for `podman create` and `podman run` can now be set in `containers.conf`. - Podman now supports auto updates for containers running inside a pod ([#17181](https://github.com/containers/podman/issues/17181)). - Podman can now use a SQLite database as a backend for increased stability. The default remains the old database, BoltDB. The database to use is selected through the `database_backend` field in `containers.conf`. +- Netavark plugin support is added, the netavark network backend now allows users to create custom network drivers. `podman network create -d ` can be used to create a network config for your plugin and then podman will use it like any other config and takes care of setup/teardown on container start/stop. This requires at least netavark version 1.6. ### Changes - Remote builds using the `podman build` command no longer allows `.containerignore` or `.dockerignore` files to be symlinks outside the build context. diff --git a/cmd/podman/networks/create.go b/cmd/podman/networks/create.go index 9842734889..626c2d32e1 100644 --- a/cmd/podman/networks/create.go +++ b/cmd/podman/networks/create.go @@ -77,6 +77,10 @@ func networkCreateFlags(cmd *cobra.Command) { flags.StringArrayVar(&networkCreateOptions.Subnets, subnetFlagName, nil, "subnets in CIDR format") _ = cmd.RegisterFlagCompletionFunc(subnetFlagName, completion.AutocompleteNone) + interfaceFlagName := "interface-name" + flags.StringVar(&networkCreateOptions.InterfaceName, interfaceFlagName, "", "interface name which is used by the driver") + _ = cmd.RegisterFlagCompletionFunc(interfaceFlagName, completion.AutocompleteNone) + flags.BoolVar(&networkCreateOptions.DisableDNS, "disable-dns", false, "disable dns plugin") flags.BoolVar(&networkCreateOptions.IgnoreIfExists, "ignore", false, "Don't fail if network already exists") @@ -118,6 +122,7 @@ func networkCreate(cmd *cobra.Command, args []string) error { DNSEnabled: !networkCreateOptions.DisableDNS, NetworkDNSServers: networkCreateOptions.NetworkDNSServers, Internal: networkCreateOptions.Internal, + NetworkInterface: networkCreateOptions.InterfaceName, } if cmd.Flags().Changed(ipamDriverFlagName) { diff --git a/docs/source/markdown/podman-network-create.1.md b/docs/source/markdown/podman-network-create.1.md index ea856f135a..8b5da028a9 100644 --- a/docs/source/markdown/podman-network-create.1.md +++ b/docs/source/markdown/podman-network-create.1.md @@ -8,8 +8,8 @@ podman\-network-create - Create a Podman network ## DESCRIPTION Create a network configuration for use with Podman. By default, Podman creates a bridge connection. -A *Macvlan* connection can be created with the *-d macvlan* option. A parent device for macvlan can -be designated with the *-o parent=``* option. +A *Macvlan* connection can be created with the *-d macvlan* option. A parent device for macvlan or +ipvlan can be designated with the *-o parent=``* or *--network-interface=``* option. If no options are provided, Podman will assign a free subnet and name for the network. @@ -22,38 +22,50 @@ release because it is used as a special network mode in **podman run/create --ne #### **--disable-dns** Disables the DNS plugin for this network which if enabled, can perform container to container name -resolution. +resolution. It is only supported with the `bridge` driver, for other drivers it will be always disabled. #### **--dns**=*ip* Set network-scoped DNS resolver/nameserver for containers in this network. If not set, the host servers from `/etc/resolv.conf` will be used. It can be overwritten on the container level with the `podman run/create --dns` option. This option can be specified multiple times to set more than one IP. -#### **--driver**, **-d** +#### **--driver**, **-d**=*driver* Driver to manage the network. Currently `bridge`, `macvlan` and `ipvlan` are supported. Defaults to `bridge`. As rootless the `macvlan` and `ipvlan` driver have no access to the host network interfaces because rootless networking requires a separate network namespace. -Special considerations for the *netavark* backend: +The netavark backend allows the use of so called *netavark plugins*, see the +[plugin-API.md](https://github.com/containers/netavark/blob/main/plugin-API.md) +documentation in netavark. The binary must be placed in a specified directory +so podman can discover it, this list is set in `netavark_plugin_dirs` in +**[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)** +under the `[network]` section. -- The `macvlan` driver requires the `--subnet` option, DHCP is currently not supported. -- The `ipvlan` driver is not currently supported. +The name of the plugin can then be used as driver to create a network for your plugin. +The list of all supported drivers and plugins can be seen with `podman info --format {{.Plugins.Network}}`. -#### **--gateway** +#### **--gateway**=*ip* Define a gateway for the subnet. To provide a gateway address, a *subnet* option is required. Can be specified multiple times. The argument order of the **--subnet**, **--gateway** and **--ip-range** options must match. #### **--ignore** + Ignore the create request if a network with the same name already exists instead of failing. Note, trying to create a network with an existing name and different parameters, will not change the configuration of the existing one +#### **--interface-name**=*name* + +This option maps the the *network_interface* option in the network config, see **podman network inspect**. +Depending on the driver this can have different effects, for `bridge` it will be the bridge interface name. +For `macvlan` and `ipvlan` this will be the parent device on the host. It is the same as `--opt parent=...`. + #### **--internal** Restrict external access of this network. Note when using this option, the dnsname plugin will be automatically disabled. -#### **--ip-range** +#### **--ip-range**=*range* Allocate container IP from a range. The range must be a complete subnet and in CIDR notation. The *ip-range* option must be used with a *subnet* option. Can be specified multiple times. @@ -76,7 +88,7 @@ View the driver in the **podman network inspect** output under the `ipam_options Enable IPv6 (Dual Stack) networking. If not subnets are given it will allocate an ipv4 and an ipv6 subnet. -#### **--label** +#### **--label**=*label* Set metadata for a network (e.g., --label mykey=value). @@ -103,7 +115,7 @@ The `macvlan` and `ipvlan` driver support the following options: - Supported values for `macvlan` are `bridge`, `private`, `vepa`, `passthru`. Defaults to `bridge`. - Supported values for `ipvlan` are `l2`, `l3`, `l3s`. Defaults to `l2`. -#### **--subnet** +#### **--subnet**=*subnet* The subnet in CIDR notation. Can be specified multiple times to allocate more than one subnet for this network. The argument order of the **--subnet**, **--gateway** and **--ip-range** options must match. @@ -154,7 +166,7 @@ newnet ``` ## SEE ALSO -**[podman(1)](podman.1.md)**, **[podman-network(1)](podman-network.1.md)**, **[podman-network-inspect(1)](podman-network-inspect.1.md)**, **[podman-network-ls(1)](podman-network-ls.1.md)** +**[podman(1)](podman.1.md)**, **[podman-network(1)](podman-network.1.md)**, **[podman-network-inspect(1)](podman-network-inspect.1.md)**, **[podman-network-ls(1)](podman-network-ls.1.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)** ## HISTORY August 2021, Updated with the new network format by Paul Holzinger diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go index 0ac3d5bfa7..0b419d33b0 100644 --- a/pkg/domain/entities/network.go +++ b/pkg/domain/entities/network.go @@ -55,6 +55,8 @@ type NetworkCreateOptions struct { Options map[string]string // IgnoreIfExists if true, do not fail if the network already exists IgnoreIfExists bool + // InterfaceName sets the NetworkInterface in the network config + InterfaceName string } // NetworkUpdateOptions describes options to update a network diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go index 90e85b9456..de54a40d51 100644 --- a/test/e2e/network_create_test.go +++ b/test/e2e/network_create_test.go @@ -481,4 +481,31 @@ var _ = Describe("Podman network create", func() { Expect(nc).To(Exit(0)) Expect(nc.OutputToString()).To(Equal(name)) }) + + It("podman network create --interface-name", func() { + netName := "bridge-" + stringid.GenerateRandomID() + bridgeName := "mybridge" + stringid.GenerateRandomID()[:4] + nc := podmanTest.Podman([]string{"network", "create", "--interface-name", bridgeName, netName}) + nc.WaitWithDefaultTimeout() + defer podmanTest.removeNetwork(netName) + Expect(nc).To(Exit(0)) + Expect(nc.OutputToString()).To(Equal(netName)) + + session := podmanTest.Podman([]string{"network", "inspect", "--format", "{{.NetworkInterface}}", netName}) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + Expect(session.OutputToString()).To(Equal(bridgeName)) + + session = podmanTest.Podman([]string{"run", "-d", "--network", netName, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + + // can only check this as root + if !isRootless() { + // make sure cni/netavark created bridge with expected name + bridge, err := net.InterfaceByName(bridgeName) + Expect(err).ToNot(HaveOccurred()) + Expect(bridge.Name).To(Equal(bridgeName)) + } + }) }) diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go index ce789f8afa..4671fd3ccc 100644 --- a/test/e2e/network_test.go +++ b/test/e2e/network_test.go @@ -477,59 +477,7 @@ var _ = Describe("Podman network", func() { Expect(lines[1]).To(Equal(netName2)) }) - It("podman CNI network with multiple aliases", func() { - SkipIfNetavark(podmanTest) - var worked bool - netName := createNetworkName("aliasTest") - session := podmanTest.Podman([]string{"network", "create", netName}) - session.WaitWithDefaultTimeout() - defer podmanTest.removeNetwork(netName) - Expect(session).Should(Exit(0)) - - interval := 250 * time.Millisecond - for i := 0; i < 6; i++ { - n := podmanTest.Podman([]string{"network", "exists", netName}) - n.WaitWithDefaultTimeout() - worked = n.ExitCode() == 0 - if worked { - break - } - time.Sleep(interval) - interval *= 2 - } - - top := podmanTest.Podman([]string{"run", "-dt", "--name=web", "--network=" + netName, "--network-alias=web1", "--network-alias=web2", NGINX_IMAGE}) - top.WaitWithDefaultTimeout() - Expect(top).Should(Exit(0)) - interval = 250 * time.Millisecond - // Wait for the nginx service to be running - for i := 0; i < 6; i++ { - // Test curl against the container's name - c1 := podmanTest.Podman([]string{"run", "--dns-search", "dns.podman", "--network=" + netName, NGINX_IMAGE, "curl", "web"}) - c1.WaitWithDefaultTimeout() - worked = c1.ExitCode() == 0 - if worked { - break - } - time.Sleep(interval) - interval *= 2 - } - Expect(worked).To(BeTrue()) - - // Nginx is now running so no need to do a loop - // Test against the first alias - c2 := podmanTest.Podman([]string{"run", "--dns-search", "dns.podman", "--network=" + netName, NGINX_IMAGE, "curl", "web1"}) - c2.WaitWithDefaultTimeout() - Expect(c2).Should(Exit(0)) - - // Test against the second alias - c3 := podmanTest.Podman([]string{"run", "--dns-search", "dns.podman", "--network=" + netName, NGINX_IMAGE, "curl", "web2"}) - c3.WaitWithDefaultTimeout() - Expect(c3).Should(Exit(0)) - }) - - It("podman Netavark network with multiple aliases", func() { - SkipIfCNI(podmanTest) + It("podman network with multiple aliases", func() { var worked bool netName := createNetworkName("aliasTest") session := podmanTest.Podman([]string{"network", "create", netName}) @@ -620,34 +568,37 @@ var _ = Describe("Podman network", func() { Expect(nc).Should(Exit(0)) }) - It("podman network create/remove macvlan as driver (-d) with device name", func() { - // Netavark currently does not do dhcp so the this test fails - SkipIfNetavark(podmanTest) - net := "macvlan" + stringid.GenerateRandomID() - nc := podmanTest.Podman([]string{"network", "create", "-d", "macvlan", "-o", "parent=lo", net}) - nc.WaitWithDefaultTimeout() - defer podmanTest.removeNetwork(net) - Expect(nc).Should(Exit(0)) - - inspect := podmanTest.Podman([]string{"network", "inspect", net}) - inspect.WaitWithDefaultTimeout() - Expect(inspect).Should(Exit(0)) - - var results []types.Network - err := json.Unmarshal([]byte(inspect.OutputToString()), &results) - Expect(err).ToNot(HaveOccurred()) - Expect(results).To(HaveLen(1)) - result := results[0] - - Expect(result).To(HaveField("Driver", "macvlan")) - Expect(result).To(HaveField("NetworkInterface", "lo")) - Expect(result.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) - Expect(result.Subnets).To(HaveLen(0)) - - nc = podmanTest.Podman([]string{"network", "rm", net}) - nc.WaitWithDefaultTimeout() - Expect(nc).Should(Exit(0)) - }) + for _, opt := range []string{"-o=parent=lo", "--interface-name=lo"} { + opt := opt + It(fmt.Sprintf("podman network create/remove macvlan as driver (-d) with %s", opt), func() { + // Netavark currently does not do dhcp so the this test fails + SkipIfNetavark(podmanTest) + net := "macvlan" + stringid.GenerateRandomID() + nc := podmanTest.Podman([]string{"network", "create", "-d", "macvlan", opt, net}) + nc.WaitWithDefaultTimeout() + defer podmanTest.removeNetwork(net) + Expect(nc).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"network", "inspect", net}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + + var results []types.Network + err := json.Unmarshal([]byte(inspect.OutputToString()), &results) + Expect(err).ToNot(HaveOccurred()) + Expect(results).To(HaveLen(1)) + result := results[0] + + Expect(result).To(HaveField("Driver", "macvlan")) + Expect(result).To(HaveField("NetworkInterface", "lo")) + Expect(result.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) + Expect(result.Subnets).To(HaveLen(0)) + + nc = podmanTest.Podman([]string{"network", "rm", net}) + nc.WaitWithDefaultTimeout() + Expect(nc).Should(Exit(0)) + }) + } It("podman network create/remove ipvlan as driver (-d) with device name", func() { // Netavark currently does not support ipvlan diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 263864e21e..a9fcbd008f 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -961,8 +961,7 @@ EXPOSE 2004-2005/tcp`, ALPINE) Expect(run.OutputToString()).To(ContainSubstring(ipAddr)) }) - It("podman CNI network works across user ns", func() { - SkipIfNetavark(podmanTest) + It("podman network works across user ns", func() { netName := stringid.GenerateRandomID() create := podmanTest.Podman([]string{"network", "create", netName}) create.WaitWithDefaultTimeout() @@ -1085,8 +1084,7 @@ EXPOSE 2004-2005/tcp`, ALPINE) pingTest("--net=private") }) - It("podman run check dnsname plugin with CNI", func() { - SkipIfNetavark(podmanTest) + It("podman run check dns", func() { pod := "testpod" session := podmanTest.Podman([]string{"pod", "create", "--name", pod}) session.WaitWithDefaultTimeout() @@ -1157,8 +1155,7 @@ EXPOSE 2004-2005/tcp`, ALPINE) Expect(session).Should(Exit(0)) }) - It("podman run check dnsname adds dns search domain with CNI", func() { - SkipIfNetavark(podmanTest) + It("podman network adds dns search domain with dns", func() { net := createNetworkName("dnsname") session := podmanTest.Podman([]string{"network", "create", net}) session.WaitWithDefaultTimeout()