summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Holzinger <pholzing@redhat.com>2022-01-27 15:13:55 +0100
committerPaul Holzinger <pholzing@redhat.com>2022-01-27 16:38:39 +0100
commit6961d912061c81b7a444e7c00163ae240e318c42 (patch)
tree2949cf26bf6a516da58475064e17c932cf273eb6
parent5659b0734c628f3f42fd976b6ce91372be3019ae (diff)
downloadpodman-6961d912061c81b7a444e7c00163ae240e318c42.tar.gz
podman-6961d912061c81b7a444e7c00163ae240e318c42.tar.bz2
podman-6961d912061c81b7a444e7c00163ae240e318c42.zip
network create: allow multiple subnets
podman network create --subnet, --gateway and --ip-range can now be specified multiple times to join the network to more than one subnet. This is very useful if you want to use a dual stack network and assign a fixed ipv4 and ipv6 subnet. The order of the options is important here, the first --gateway/--ip-range will be assigned to the first subnet and so on. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
-rw-r--r--cmd/podman/networks/create.go64
-rw-r--r--docs/source/markdown/podman-network-create.1.md18
-rw-r--r--pkg/domain/entities/network.go6
-rw-r--r--test/e2e/network_create_test.go78
4 files changed, 141 insertions, 25 deletions
diff --git a/cmd/podman/networks/create.go b/cmd/podman/networks/create.go
index 9f6470858..3dd393c46 100644
--- a/cmd/podman/networks/create.go
+++ b/cmd/podman/networks/create.go
@@ -47,13 +47,13 @@ func networkCreateFlags(cmd *cobra.Command) {
_ = cmd.RegisterFlagCompletionFunc(optFlagName, completion.AutocompleteNone)
gatewayFlagName := "gateway"
- flags.IPVar(&networkCreateOptions.Gateway, gatewayFlagName, nil, "IPv4 or IPv6 gateway for the subnet")
+ flags.IPSliceVar(&networkCreateOptions.Gateways, gatewayFlagName, nil, "IPv4 or IPv6 gateway for the subnet")
_ = cmd.RegisterFlagCompletionFunc(gatewayFlagName, completion.AutocompleteNone)
flags.BoolVar(&networkCreateOptions.Internal, "internal", false, "restrict external access from this network")
ipRangeFlagName := "ip-range"
- flags.IPNetVar(&networkCreateOptions.Range, ipRangeFlagName, net.IPNet{}, "allocate container IP from range")
+ flags.StringArrayVar(&networkCreateOptions.Ranges, ipRangeFlagName, nil, "allocate container IP from range")
_ = cmd.RegisterFlagCompletionFunc(ipRangeFlagName, completion.AutocompleteNone)
// TODO consider removing this for 4.0
@@ -72,7 +72,7 @@ func networkCreateFlags(cmd *cobra.Command) {
flags.BoolVar(&networkCreateOptions.IPv6, "ipv6", false, "enable IPv6 networking")
subnetFlagName := "subnet"
- flags.IPNetVar(&networkCreateOptions.Subnet, subnetFlagName, net.IPNet{}, "subnet in CIDR format")
+ flags.StringArrayVar(&networkCreateOptions.Subnets, subnetFlagName, nil, "subnets in CIDR format")
_ = cmd.RegisterFlagCompletionFunc(subnetFlagName, completion.AutocompleteNone)
flags.BoolVar(&networkCreateOptions.DisableDNS, "disable-dns", false, "disable dns plugin")
@@ -125,27 +125,35 @@ func networkCreate(cmd *cobra.Command, args []string) error {
}
}
- if networkCreateOptions.Subnet.IP != nil {
- s := types.Subnet{
- Subnet: types.IPNet{IPNet: networkCreateOptions.Subnet},
- Gateway: networkCreateOptions.Gateway,
+ if len(networkCreateOptions.Subnets) > 0 {
+ if len(networkCreateOptions.Gateways) > len(networkCreateOptions.Subnets) {
+ return errors.New("cannot set more gateways than subnets")
}
- if networkCreateOptions.Range.IP != nil {
- startIP, err := util.FirstIPInSubnet(&networkCreateOptions.Range)
+ if len(networkCreateOptions.Ranges) > len(networkCreateOptions.Subnets) {
+ return errors.New("cannot set more ranges than subnets")
+ }
+
+ for i := range networkCreateOptions.Subnets {
+ subnet, err := types.ParseCIDR(networkCreateOptions.Subnets[i])
if err != nil {
- return errors.Wrap(err, "failed to get first ip in range")
+ return err
}
- lastIP, err := util.LastIPInSubnet(&networkCreateOptions.Range)
- if err != nil {
- return errors.Wrap(err, "failed to get last ip in range")
+ s := types.Subnet{
+ Subnet: subnet,
+ }
+ if len(networkCreateOptions.Ranges) > i {
+ leaseRange, err := parseRange(networkCreateOptions.Ranges[i])
+ if err != nil {
+ return err
+ }
+ s.LeaseRange = leaseRange
}
- s.LeaseRange = &types.LeaseRange{
- StartIP: startIP,
- EndIP: lastIP,
+ if len(networkCreateOptions.Gateways) > i {
+ s.Gateway = networkCreateOptions.Gateways[i]
}
+ network.Subnets = append(network.Subnets, s)
}
- network.Subnets = append(network.Subnets, s)
- } else if networkCreateOptions.Range.IP != nil || networkCreateOptions.Gateway != nil {
+ } else if len(networkCreateOptions.Ranges) > 0 || len(networkCreateOptions.Gateways) > 0 {
return errors.New("cannot set gateway or range without subnet")
}
@@ -156,3 +164,23 @@ func networkCreate(cmd *cobra.Command, args []string) error {
fmt.Println(response.Name)
return nil
}
+
+func parseRange(iprange string) (*types.LeaseRange, error) {
+ _, subnet, err := net.ParseCIDR(iprange)
+ if err != nil {
+ return nil, err
+ }
+
+ startIP, err := util.FirstIPInSubnet(subnet)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to get first ip in range")
+ }
+ lastIP, err := util.LastIPInSubnet(subnet)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to get last ip in range")
+ }
+ return &types.LeaseRange{
+ StartIP: startIP,
+ EndIP: lastIP,
+ }, nil
+}
diff --git a/docs/source/markdown/podman-network-create.1.md b/docs/source/markdown/podman-network-create.1.md
index 8a9b2f3cf..5be0c2595 100644
--- a/docs/source/markdown/podman-network-create.1.md
+++ b/docs/source/markdown/podman-network-create.1.md
@@ -46,7 +46,8 @@ The `macvlan` and `ipvlan` driver support the following options:
#### **--gateway**
Define a gateway for the subnet. If you want to provide a gateway address, you must also provide a
-*subnet* option.
+*subnet* option. Can be specified multiple times.
+The argument order of the **--subnet**, **--gateway** and **--ip-range** options must match.
#### **--internal**
@@ -56,7 +57,8 @@ automatically disabled.
#### **--ip-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.
+must be used with a *subnet* option. Can be specified multiple times.
+The argument order of the **--subnet**, **--gateway** and **--ip-range** options must match.
#### **--label**
@@ -64,11 +66,13 @@ Set metadata for a network (e.g., --label mykey=value).
#### **--subnet**
-The subnet in CIDR notation.
+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.
+This is useful to set a static ipv4 and ipv6 subnet.
#### **--ipv6**
-Enable IPv6 (Dual Stack) networking.
+Enable IPv6 (Dual Stack) networking. If not subnets are given it will allocate a ipv4 and ipv6 subnet.
## EXAMPLE
@@ -102,6 +106,12 @@ $ podman network create --subnet 192.168.55.0/24 --ip-range 192.168.55.128/25
cni-podman5
```
+Create a network with a static ipv4 and ipv6 subnet and set a gateway.
+```
+$ podman network create --subnet 192.168.55.0/24 --gateway 192.168.55.3 --subnet fd52:2a5a:747e:3acd::/64 --gateway fd52:2a5a:747e:3acd::10
+podman4
+```
+
Create a Macvlan based network using the host interface eth0. Macvlan networks can only be used as root.
```
# podman network create -d macvlan -o parent=eth0 newnet
diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go
index 79edc3227..a057640b3 100644
--- a/pkg/domain/entities/network.go
+++ b/pkg/domain/entities/network.go
@@ -43,12 +43,12 @@ type NetworkRmReport struct {
type NetworkCreateOptions struct {
DisableDNS bool
Driver string
- Gateway net.IP
+ Gateways []net.IP
Internal bool
Labels map[string]string
MacVLAN string
- Range net.IPNet
- Subnet net.IPNet
+ Ranges []string
+ Subnets []string
IPv6 bool
// Mapping of driver options and values.
Options map[string]string
diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go
index ade78a308..4a8a24ad7 100644
--- a/test/e2e/network_create_test.go
+++ b/test/e2e/network_create_test.go
@@ -356,4 +356,82 @@ var _ = Describe("Podman network create", func() {
}
})
+ It("podman network create with multiple subnets", func() {
+ name := "subnets-" + stringid.GenerateNonCryptoID()
+ subnet1 := "10.10.0.0/24"
+ subnet2 := "10.10.1.0/24"
+ nc := podmanTest.Podman([]string{"network", "create", "--subnet", subnet1, "--subnet", subnet2, name})
+ nc.WaitWithDefaultTimeout()
+ defer podmanTest.removeCNINetwork(name)
+ Expect(nc).To(Exit(0))
+ Expect(nc.OutputToString()).To(Equal(name))
+
+ inspect := podmanTest.Podman([]string{"network", "inspect", name})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect).To(Exit(0))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`"subnet": "` + subnet1))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`"subnet": "` + subnet2))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`"ipv6_enabled": false`))
+ })
+
+ It("podman network create with multiple subnets dual stack", func() {
+ name := "subnets-" + stringid.GenerateNonCryptoID()
+ subnet1 := "10.10.2.0/24"
+ subnet2 := "fd52:2a5a:747e:3acd::/64"
+ nc := podmanTest.Podman([]string{"network", "create", "--subnet", subnet1, "--subnet", subnet2, name})
+ nc.WaitWithDefaultTimeout()
+ defer podmanTest.removeCNINetwork(name)
+ Expect(nc).To(Exit(0))
+ Expect(nc.OutputToString()).To(Equal(name))
+
+ inspect := podmanTest.Podman([]string{"network", "inspect", name})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect).To(Exit(0))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`"subnet": "` + subnet1))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`"subnet": "` + subnet2))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`"ipv6_enabled": true`))
+ })
+
+ It("podman network create with multiple subnets dual stack with gateway and range", func() {
+ name := "subnets-" + stringid.GenerateNonCryptoID()
+ subnet1 := "10.10.3.0/24"
+ gw1 := "10.10.3.10"
+ range1 := "10.10.3.0/26"
+ subnet2 := "fd52:2a5a:747e:3acd::/64"
+ gw2 := "fd52:2a5a:747e:3acd::10"
+ nc := podmanTest.Podman([]string{"network", "create", "--subnet", subnet1, "--gateway", gw1, "--ip-range", range1, "--subnet", subnet2, "--gateway", gw2, name})
+ nc.WaitWithDefaultTimeout()
+ defer podmanTest.removeCNINetwork(name)
+ Expect(nc).To(Exit(0))
+ Expect(nc.OutputToString()).To(Equal(name))
+
+ inspect := podmanTest.Podman([]string{"network", "inspect", name})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect).To(Exit(0))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`"subnet": "` + subnet1))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`"gateway": "` + gw1))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`"start_ip": "10.10.3.1",`))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`"end_ip": "10.10.3.63"`))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`"subnet": "` + subnet2))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`"gateway": "` + gw2))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`"ipv6_enabled": true`))
+ })
+
+ It("podman network create invalid options with multiple subnets", func() {
+ name := "subnets-" + stringid.GenerateNonCryptoID()
+ subnet1 := "10.10.3.0/24"
+ gw1 := "10.10.3.10"
+ gw2 := "fd52:2a5a:747e:3acd::10"
+ nc := podmanTest.Podman([]string{"network", "create", "--subnet", subnet1, "--gateway", gw1, "--gateway", gw2, name})
+ nc.WaitWithDefaultTimeout()
+ Expect(nc).To(Exit(125))
+ Expect(nc.ErrorToString()).To(Equal("Error: cannot set more gateways than subnets"))
+
+ range1 := "10.10.3.0/26"
+ range2 := "10.10.3.0/28"
+ nc = podmanTest.Podman([]string{"network", "create", "--subnet", subnet1, "--ip-range", range1, "--ip-range", range2, name})
+ nc.WaitWithDefaultTimeout()
+ Expect(nc).To(Exit(125))
+ Expect(nc.ErrorToString()).To(Equal("Error: cannot set more ranges than subnets"))
+ })
})