diff options
-rw-r--r-- | cmd/podman/common/completion.go | 4 | ||||
-rw-r--r-- | cmd/podman/machine/ssh.go | 29 | ||||
-rw-r--r-- | docs/source/markdown/podman-machine-ssh.1.md | 6 | ||||
-rw-r--r-- | docs/source/markdown/podman-network-create.1.md | 17 | ||||
-rw-r--r-- | libpod/network/cni/cni_conversion.go | 41 | ||||
-rw-r--r-- | libpod/network/cni/cni_types.go | 20 | ||||
-rw-r--r-- | libpod/network/cni/config.go | 6 | ||||
-rw-r--r-- | libpod/network/cni/config_test.go | 102 | ||||
-rw-r--r-- | libpod/network/cni/network.go | 2 | ||||
-rw-r--r-- | libpod/network/types/const.go | 2 |
10 files changed, 184 insertions, 45 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 193f09e85..2ea5fa10f 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -1111,7 +1111,7 @@ func AutocompleteManifestFormat(cmd *cobra.Command, args []string, toComplete st // AutocompleteNetworkDriver - Autocomplete network driver option. // -> "bridge", "macvlan" func AutocompleteNetworkDriver(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - drivers := []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver} + drivers := []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver, types.IPVLANNetworkDriver} return drivers, cobra.ShellCompDirectiveNoFileComp } @@ -1257,7 +1257,7 @@ func AutocompleteNetworkFilters(cmd *cobra.Command, args []string, toComplete st "id=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s, completeIDs) }, "label=": nil, "driver=": func(_ string) ([]string, cobra.ShellCompDirective) { - return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver}, cobra.ShellCompDirectiveNoFileComp + return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver, types.IPVLANNetworkDriver}, cobra.ShellCompDirectiveNoFileComp }, "until=": nil, } diff --git a/cmd/podman/machine/ssh.go b/cmd/podman/machine/ssh.go index 84e9e88ab..da0a09338 100644 --- a/cmd/podman/machine/ssh.go +++ b/cmd/podman/machine/ssh.go @@ -5,6 +5,7 @@ package machine import ( "net/url" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/pkg/machine" @@ -15,7 +16,7 @@ import ( var ( sshCmd = &cobra.Command{ - Use: "ssh [NAME] [COMMAND [ARG ...]]", + Use: "ssh [options] [NAME] [COMMAND [ARG ...]]", Short: "SSH into an existing machine", Long: "SSH into a managed virtual machine ", RunE: ssh, @@ -35,6 +36,10 @@ func init() { Command: sshCmd, Parent: machineCmd, }) + flags := sshCmd.Flags() + usernameFlagName := "username" + flags.StringVar(&sshOpts.Username, usernameFlagName, "", "Username to use when ssh-ing into the VM.") + _ = sshCmd.RegisterFlagCompletionFunc(usernameFlagName, completion.AutocompleteNone) } func ssh(cmd *cobra.Command, args []string) error { @@ -48,13 +53,6 @@ func ssh(cmd *cobra.Command, args []string) error { // Set the VM to default vmName := defaultMachineName - // If we're not given a VM name, use the remote username from the connection config - if len(args) == 0 { - sshOpts.Username, err = remoteConnectionUsername() - if err != nil { - return err - } - } // If len is greater than 0, it means we may have been // provided the VM name. If so, we check. The VM name, // if provided, must be in args[0]. @@ -68,10 +66,6 @@ func ssh(cmd *cobra.Command, args []string) error { if validVM { vmName = args[0] } else { - sshOpts.Username, err = remoteConnectionUsername() - if err != nil { - return err - } sshOpts.Args = append(sshOpts.Args, args[0]) } } @@ -83,14 +77,17 @@ func ssh(cmd *cobra.Command, args []string) error { if validVM { sshOpts.Args = args[1:] } else { - sshOpts.Username, err = remoteConnectionUsername() - if err != nil { - return err - } sshOpts.Args = args } } + if !validVM && sshOpts.Username == "" { + sshOpts.Username, err = remoteConnectionUsername() + if err != nil { + return err + } + } + switch vmType { default: vm, err = qemu.LoadVMByName(vmName) diff --git a/docs/source/markdown/podman-machine-ssh.1.md b/docs/source/markdown/podman-machine-ssh.1.md index a5cf69107..c4c732819 100644 --- a/docs/source/markdown/podman-machine-ssh.1.md +++ b/docs/source/markdown/podman-machine-ssh.1.md @@ -4,7 +4,7 @@ podman\-machine\-ssh - SSH into a virtual machine ## SYNOPSIS -**podman machine ssh** [*name*] [*command* [*arg* ...]] +**podman machine ssh** [*options*] [*name*] [*command* [*arg* ...]] ## DESCRIPTION @@ -21,6 +21,10 @@ with the virtual machine is established. Print usage statement. +#### **--username**=*name* + +Username to use when SSH-ing into the VM. + ## EXAMPLES To get an interactive session with the default virtual machine: diff --git a/docs/source/markdown/podman-network-create.1.md b/docs/source/markdown/podman-network-create.1.md index 816dd53ea..d48509581 100644 --- a/docs/source/markdown/podman-network-create.1.md +++ b/docs/source/markdown/podman-network-create.1.md @@ -25,16 +25,23 @@ resolution. #### **--driver**, **-d** -Driver to manage the network. Currently `bridge` and `macvlan` is supported. Defaults to `bridge`. -As rootless the `macvlan` driver has no access to the host network interfaces because rootless networking requires a separate network namespace. +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. #### **--opt**=*option*, **-o** Set driver specific options. -For the `bridge` driver the following options are supported: `mtu` and `vlan`. -The `mtu` option sets the Maximum Transmission Unit (MTU) and takes an integer value. -The `vlan` option assign VLAN tag and enables vlan\_filtering. Defaults to none. +All drivers accept the `mtu` option. The `mtu` option sets the Maximum Transmission Unit (MTU) and takes an integer value. + +Additionally the `bridge` driver supports the following option: +- `vlan`: This option assign VLAN tag and enables vlan\_filtering. Defaults to none. + +The `macvlan` and `ipvlan` driver support the following options: +- `parent`: The host device which should be used for the macvlan interface. Defaults to the default route interface. +- `mode`: This options sets the specified ip/macvlan mode on the interface. + - Supported values for `macvlan` are `bridge`, `private`, `vepa`, `passthru`. Defaults to `bridge`. + - Supported values for `ipvlan` are `l2`, `l3`, `l3s`. Defaults to `l2`. #### **--gateway** diff --git a/libpod/network/cni/cni_conversion.go b/libpod/network/cni/cni_conversion.go index 060794ebe..d69dd7eb3 100644 --- a/libpod/network/cni/cni_conversion.go +++ b/libpod/network/cni/cni_conversion.go @@ -81,20 +81,24 @@ func createNetworkFromCNIConfigList(conf *libcni.NetworkConfigList, confPath str return nil, err } - case types.MacVLANNetworkDriver: - var macvlan macVLANConfig - err := json.Unmarshal(firstPlugin.Bytes, &macvlan) + case types.MacVLANNetworkDriver, types.IPVLANNetworkDriver: + var vlan VLANConfig + err := json.Unmarshal(firstPlugin.Bytes, &vlan) if err != nil { return nil, errors.Wrapf(err, "failed to unmarshal the macvlan plugin config in %s", confPath) } - network.NetworkInterface = macvlan.Master + network.NetworkInterface = vlan.Master // set network options - if macvlan.MTU != 0 { - network.Options["mtu"] = strconv.Itoa(macvlan.MTU) + if vlan.MTU != 0 { + network.Options["mtu"] = strconv.Itoa(vlan.MTU) + } + + if vlan.Mode != "" { + network.Options["mode"] = vlan.Mode } - err = convertIPAMConfToNetwork(&network, macvlan.IPAM, confPath) + err = convertIPAMConfToNetwork(&network, vlan.IPAM, confPath) if err != nil { return nil, err } @@ -207,7 +211,7 @@ func getNetworkArgsFromConfList(args map[string]interface{}, argType string) map return result } } - return nil + return map[string]string{} } // createCNIConfigListFromNetwork will create a cni config file from the given network. @@ -237,6 +241,7 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ vlan := 0 mtu := 0 + vlanPluginMode := "" for k, v := range network.Options { switch k { case "mtu": @@ -251,6 +256,21 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ return nil, "", err } + case "mode": + switch network.Driver { + case types.MacVLANNetworkDriver: + if !pkgutil.StringInSlice(v, []string{"", "bridge", "private", "vepa", "passthru"}) { + return nil, "", errors.Errorf("unknown macvlan mode %q", v) + } + case types.IPVLANNetworkDriver: + if !pkgutil.StringInSlice(v, []string{"", "l2", "l3", "l3s"}) { + 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) } @@ -281,7 +301,10 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ } case types.MacVLANNetworkDriver: - plugins = append(plugins, newMacVLANPlugin(network.NetworkInterface, mtu, ipamConf)) + plugins = append(plugins, newVLANPlugin(types.MacVLANNetworkDriver, network.NetworkInterface, vlanPluginMode, mtu, ipamConf)) + + case types.IPVLANNetworkDriver: + plugins = append(plugins, newVLANPlugin(types.IPVLANNetworkDriver, network.NetworkInterface, vlanPluginMode, mtu, ipamConf)) default: return nil, "", errors.Errorf("driver %q is not supported by cni", network.Driver) diff --git a/libpod/network/cni/cni_types.go b/libpod/network/cni/cni_types.go index 91fd1c27b..fbf917c2d 100644 --- a/libpod/network/cni/cni_types.go +++ b/libpod/network/cni/cni_types.go @@ -50,7 +50,7 @@ type hostLocalBridge struct { PromiscMode bool `json:"promiscMode,omitempty"` Vlan int `json:"vlan,omitempty"` IPAM ipamConfig `json:"ipam"` - Capabilities map[string]bool `json:"capabilities"` + Capabilities map[string]bool `json:"capabilities,omitempty"` } // ipamConfig describes an IPAM configuration @@ -82,13 +82,14 @@ type portMapConfig struct { Capabilities map[string]bool `json:"capabilities"` } -// macVLANConfig describes the macvlan config -type macVLANConfig struct { +// VLANConfig describes the macvlan config +type VLANConfig struct { PluginType string `json:"type"` Master string `json:"master"` IPAM ipamConfig `json:"ipam"` MTU int `json:"mtu,omitempty"` - Capabilities map[string]bool `json:"capabilities"` + Mode string `json:"mode,omitempty"` + Capabilities map[string]bool `json:"capabilities,omitempty"` } // firewallConfig describes the firewall plugin @@ -259,15 +260,18 @@ func hasDNSNamePlugin(paths []string) bool { return false } -// newMacVLANPlugin creates a macvlanconfig with a given device name -func newMacVLANPlugin(device string, mtu int, ipam ipamConfig) macVLANConfig { - m := macVLANConfig{ - PluginType: "macvlan", +// newVLANPlugin creates a macvlanconfig with a given device name +func newVLANPlugin(pluginType, device, mode string, mtu int, ipam ipamConfig) VLANConfig { + m := VLANConfig{ + PluginType: pluginType, IPAM: ipam, } if mtu > 0 { m.MTU = mtu } + if len(mode) > 0 { + m.Mode = mode + } // CNI is supposed to use the default route if a // parent device is not provided if len(device) > 0 { diff --git a/libpod/network/cni/config.go b/libpod/network/cni/config.go index d31cd3002..2a6ad8eb3 100644 --- a/libpod/network/cni/config.go +++ b/libpod/network/cni/config.go @@ -100,8 +100,8 @@ func (n *cniNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (* if err != nil { return nil, err } - case types.MacVLANNetworkDriver: - err = createMacVLAN(&newNetwork) + case types.MacVLANNetworkDriver, types.IPVLANNetworkDriver: + err = createIPMACVLAN(&newNetwork) if err != nil { return nil, err } @@ -214,7 +214,7 @@ func (n *cniNetwork) NetworkInspect(nameOrID string) (types.Network, error) { return *network.libpodNet, nil } -func createMacVLAN(network *types.Network) error { +func createIPMACVLAN(network *types.Network) error { if network.Internal { return errors.New("internal is not supported with macvlan") } diff --git a/libpod/network/cni/config_test.go b/libpod/network/cni/config_test.go index 11ad71870..a0a0ea1af 100644 --- a/libpod/network/cni/config_test.go +++ b/libpod/network/cni/config_test.go @@ -250,6 +250,67 @@ var _ = Describe("Config", func() { grepInFile(path, `"type": "host-local"`) }) + It("create ipvlan config with subnet", func() { + subnet := "10.1.0.0/24" + n, _ := types.ParseCIDR(subnet) + network := types.Network{ + Driver: "ipvlan", + Subnets: []types.Subnet{ + {Subnet: n}, + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + path := filepath.Join(cniConfDir, network1.Name+".conflist") + Expect(path).To(BeARegularFile()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("ipvlan")) + Expect(network1.Labels).To(BeEmpty()) + Expect(network1.Options).To(BeEmpty()) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) + Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.1.0.1")) + Expect(network1.Subnets[0].LeaseRange).To(BeNil()) + Expect(network1.DNSEnabled).To(BeFalse()) + Expect(network1.Internal).To(BeFalse()) + Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "host-local")) + grepInFile(path, `"type": "host-local"`) + }) + + It("create macvlan config with mode", func() { + for _, mode := range []string{"bridge", "private", "vepa", "passthru"} { + network := types.Network{ + Driver: "macvlan", + Options: map[string]string{ + "mode": mode, + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + path := filepath.Join(cniConfDir, network1.Name+".conflist") + Expect(path).To(BeARegularFile()) + Expect(network1.Driver).To(Equal("macvlan")) + Expect(network1.Options).To(HaveKeyWithValue("mode", mode)) + Expect(network1.IPAMOptions).ToNot(BeEmpty()) + Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) + grepInFile(path, `"mode": "`+mode+`"`) + } + }) + + It("create macvlan config with invalid mode", func() { + network := types.Network{ + Driver: "macvlan", + Options: map[string]string{ + "mode": "test", + }, + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring(`unknown macvlan mode "test"`)) + }) + It("create macvlan config with invalid device", func() { network := types.Network{ Driver: "macvlan", @@ -270,6 +331,47 @@ var _ = Describe("Config", func() { Expect(err.Error()).To(ContainSubstring("internal is not supported with macvlan")) }) + It("create ipvlan config with mode", func() { + for _, mode := range []string{"l2", "l3", "l3s"} { + network := types.Network{ + Driver: "ipvlan", + Options: map[string]string{ + "mode": mode, + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + path := filepath.Join(cniConfDir, network1.Name+".conflist") + Expect(path).To(BeARegularFile()) + Expect(network1.Driver).To(Equal("ipvlan")) + Expect(network1.Options).To(HaveKeyWithValue("mode", mode)) + Expect(network1.IPAMOptions).ToNot(BeEmpty()) + Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) + grepInFile(path, `"mode": "`+mode+`"`) + + // reload configs from disk + libpodNet, err = getNetworkInterface(cniConfDir, false) + Expect(err).To(BeNil()) + + network2, err := libpodNet.NetworkInspect(network1.Name) + Expect(err).To(BeNil()) + Expect(network2).To(Equal(network1)) + } + }) + + It("create ipvlan config with invalid mode", func() { + network := types.Network{ + Driver: "ipvlan", + Options: map[string]string{ + "mode": "test", + }, + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring(`unknown ipvlan mode "test"`)) + }) + It("create bridge with subnet", func() { subnet := "10.0.0.0/24" n, _ := types.ParseCIDR(subnet) diff --git a/libpod/network/cni/network.go b/libpod/network/cni/network.go index 46e07f780..d77e63a5d 100644 --- a/libpod/network/cni/network.go +++ b/libpod/network/cni/network.go @@ -109,7 +109,7 @@ func NewCNINetworkInterface(conf InitConfig) (types.ContainerNetwork, error) { // Drivers will return the list of supported network drivers // for this interface. func (n *cniNetwork) Drivers() []string { - return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver} + return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver, types.IPVLANNetworkDriver} } func (n *cniNetwork) loadNetworks() error { diff --git a/libpod/network/types/const.go b/libpod/network/types/const.go index be7ef03cf..916c6e6bf 100644 --- a/libpod/network/types/const.go +++ b/libpod/network/types/const.go @@ -7,6 +7,8 @@ const ( DefaultNetworkDriver = BridgeNetworkDriver // MacVLANNetworkDriver defines the macvlan driver MacVLANNetworkDriver = "macvlan" + // MacVLANNetworkDriver defines the macvlan driver + IPVLANNetworkDriver = "ipvlan" // IPAM drivers // HostLocalIPAMDriver store the ip |