mirror of
https://github.com/containers/podman.git
synced 2025-05-20 00:27:03 +08:00
podman: add support for specifying MAC
I basically copied and adapted the statements for setting IP. Closes #1136 Signed-off-by: Jakub Filak <jakub.filak@sap.com>
This commit is contained in:

committed by
Giuseppe Scrivano

parent
455f5b7616
commit
2497b6c77b
@ -475,6 +475,7 @@ type RestoreValues struct {
|
||||
Name string
|
||||
IgnoreRootfs bool
|
||||
IgnoreStaticIP bool
|
||||
IgnoreStaticMAC bool
|
||||
}
|
||||
|
||||
type RmValues struct {
|
||||
|
@ -328,7 +328,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
|
||||
)
|
||||
createFlags.String(
|
||||
"mac-address", "",
|
||||
"Container MAC address (e.g. 92:d0:c6:0a:29:33), not currently supported",
|
||||
"Container MAC address (e.g. 92:d0:c6:0a:29:33)",
|
||||
)
|
||||
createFlags.StringP(
|
||||
"memory", "m", "",
|
||||
|
@ -47,6 +47,7 @@ func init() {
|
||||
flags.StringVarP(&restoreCommand.Name, "name", "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)")
|
||||
flags.BoolVar(&restoreCommand.IgnoreRootfs, "ignore-rootfs", false, "Do not apply root file-system changes when importing from exported checkpoint")
|
||||
flags.BoolVar(&restoreCommand.IgnoreStaticIP, "ignore-static-ip", false, "Ignore IP address set via --static-ip")
|
||||
flags.BoolVar(&restoreCommand.IgnoreStaticMAC, "ignore-static-mac", false, "Ignore MAC address set via --mac-address")
|
||||
|
||||
markFlagHiddenForRemoteClient("latest", flags)
|
||||
}
|
||||
|
@ -336,10 +336,6 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.String("mac-address") != "" {
|
||||
return nil, errors.Errorf("--mac-address option not currently supported")
|
||||
}
|
||||
|
||||
imageID := ""
|
||||
|
||||
inputCommand = c.InputArgs[1:]
|
||||
|
@ -877,6 +877,7 @@ _podman_container_restore() {
|
||||
--tcp-established
|
||||
--ignore-rootfs
|
||||
--ignore-static-ip
|
||||
--ignore-static-mac
|
||||
"
|
||||
case "$prev" in
|
||||
-i|--import)
|
||||
|
@ -76,6 +76,15 @@ a container is restored multiple times from an exported checkpoint with **--name
|
||||
Using **--ignore-static-ip** tells Podman to ignore the IP address if it was configured
|
||||
with **--ip** during container creation.
|
||||
|
||||
**--ignore-static-mac**
|
||||
|
||||
If the container was started with **--mac-address** the restored container also
|
||||
tries to use that MAC address and restore fails if that MAC address is already
|
||||
in use. This can happen, if a container is restored multiple times from an
|
||||
exported checkpoint with **--name, -n**.
|
||||
|
||||
Using **--ignore-static-mac** tells Podman to ignore the MAC address if it was
|
||||
configured with **--mac-address** during container creation.
|
||||
## EXAMPLE
|
||||
|
||||
podman container restore mywebserver
|
||||
|
@ -138,6 +138,10 @@ type Container struct {
|
||||
// being checkpointed. If requestedIP is set it will be used instead
|
||||
// of config.StaticIP.
|
||||
requestedIP net.IP
|
||||
// A restored container should have the same MAC address as before
|
||||
// being checkpointed. If requestedMAC is set it will be used instead
|
||||
// of config.StaticMAC.
|
||||
requestedMAC net.HardwareAddr
|
||||
|
||||
// This is true if a container is restored from a checkpoint.
|
||||
restoreFromCheckpoint bool
|
||||
@ -296,6 +300,10 @@ type ContainerConfig struct {
|
||||
// This cannot be set unless CreateNetNS is set.
|
||||
// If not set, the container will be dynamically assigned an IP by CNI.
|
||||
StaticIP net.IP `json:"staticIP"`
|
||||
// StaticMAC is a static MAC to request for the container.
|
||||
// This cannot be set unless CreateNetNS is set.
|
||||
// If not set, the container will be dynamically assigned a MAC by CNI.
|
||||
StaticMAC net.HardwareAddr `json:"staticMAC"`
|
||||
// PortMappings are the ports forwarded to the container's network
|
||||
// namespace
|
||||
// These are not used unless CreateNetNS is true
|
||||
|
@ -794,6 +794,11 @@ type ContainerCheckpointOptions struct {
|
||||
// important to be able to restore a container multiple
|
||||
// times with '--import --name'.
|
||||
IgnoreStaticIP bool
|
||||
// IgnoreStaticMAC tells the API to ignore the MAC set
|
||||
// during 'podman run' with '--mac-address'. This is especially
|
||||
// important to be able to restore a container multiple
|
||||
// times with '--import --name'.
|
||||
IgnoreStaticMAC bool
|
||||
}
|
||||
|
||||
// Checkpoint checkpoints a container
|
||||
|
@ -794,6 +794,15 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
|
||||
c.config.StaticIP = nil
|
||||
}
|
||||
|
||||
// If a container is restored multiple times from an exported checkpoint with
|
||||
// the help of '--import --name', the restore will fail if during 'podman run'
|
||||
// a static container MAC address was set with '--mac-address'. The user
|
||||
// can tell the restore process to ignore the static MAC with
|
||||
// '--ignore-static-mac'
|
||||
if options.IgnoreStaticMAC {
|
||||
c.config.StaticMAC = nil
|
||||
}
|
||||
|
||||
// Read network configuration from checkpoint
|
||||
// Currently only one interface with one IP is supported.
|
||||
networkStatusFile, err := os.Open(filepath.Join(c.bundlePath(), "network.status"))
|
||||
@ -803,9 +812,9 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
|
||||
// TODO: This implicit restoring with or without IP depending on an
|
||||
// unrelated restore parameter (--name) does not seem like the
|
||||
// best solution.
|
||||
if err == nil && options.Name == "" && !options.IgnoreStaticIP {
|
||||
if err == nil && options.Name == "" && (!options.IgnoreStaticIP || !options.IgnoreStaticMAC) {
|
||||
// The file with the network.status does exist. Let's restore the
|
||||
// container with the same IP address as during checkpointing.
|
||||
// container with the same IP address / MAC address as during checkpointing.
|
||||
defer networkStatusFile.Close()
|
||||
var networkStatus []*cnitypes.Result
|
||||
networkJSON, err := ioutil.ReadAll(networkStatusFile)
|
||||
@ -815,6 +824,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
|
||||
if err := json.Unmarshal(networkJSON, &networkStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
if !options.IgnoreStaticIP {
|
||||
// Take the first IP address
|
||||
var IP net.IP
|
||||
if len(networkStatus) > 0 {
|
||||
@ -827,6 +837,24 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
|
||||
c.requestedIP = IP
|
||||
}
|
||||
}
|
||||
if !options.IgnoreStaticMAC {
|
||||
// Take the first device with a defined sandbox.
|
||||
var MAC net.HardwareAddr
|
||||
for _, n := range networkStatus[0].Interfaces {
|
||||
if n.Sandbox != "" {
|
||||
MAC, err = net.ParseMAC(n.Mac)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to parse MAC %v", n.Mac)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if MAC != nil {
|
||||
// Tell CNI which MAC address we want.
|
||||
c.requestedMAC = MAC
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
@ -1314,7 +1342,7 @@ func (c *Container) copyOwnerAndPerms(source, dest string) error {
|
||||
// Teardown CNI config on refresh
|
||||
func (c *Container) refreshCNI() error {
|
||||
// Let's try and delete any lingering network config...
|
||||
podNetwork := c.runtime.getPodNetwork(c.ID(), c.config.Name, "", c.config.Networks, c.config.PortMappings, c.config.StaticIP)
|
||||
podNetwork := c.runtime.getPodNetwork(c.ID(), c.config.Name, "", c.config.Networks, c.config.PortMappings, c.config.StaticIP, c.config.StaticMAC)
|
||||
return c.runtime.netPlugin.TearDownPod(podNetwork)
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ import (
|
||||
)
|
||||
|
||||
// Get an OCICNI network config
|
||||
func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.PortMapping, staticIP net.IP) ocicni.PodNetwork {
|
||||
func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.PortMapping, staticIP net.IP, staticMAC net.HardwareAddr) ocicni.PodNetwork {
|
||||
defaultNetwork := r.netPlugin.GetDefaultNetworkName()
|
||||
network := ocicni.PodNetwork{
|
||||
Name: name,
|
||||
@ -40,10 +40,17 @@ func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, port
|
||||
},
|
||||
}
|
||||
|
||||
if staticIP != nil {
|
||||
if staticIP != nil || staticMAC != nil {
|
||||
network.Networks = []ocicni.NetAttachment{{Name: defaultNetwork}}
|
||||
var rt ocicni.RuntimeConfig = ocicni.RuntimeConfig{PortMappings: ports}
|
||||
if staticIP != nil {
|
||||
rt.IP = staticIP.String()
|
||||
}
|
||||
if staticMAC != nil {
|
||||
rt.MAC = staticMAC.String()
|
||||
}
|
||||
network.RuntimeConfig = map[string]ocicni.RuntimeConfig{
|
||||
defaultNetwork: {IP: staticIP.String(), PortMappings: ports},
|
||||
defaultNetwork: rt,
|
||||
}
|
||||
} else {
|
||||
network.Networks = make([]ocicni.NetAttachment, len(networks))
|
||||
@ -66,7 +73,16 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
|
||||
requestedIP = ctr.config.StaticIP
|
||||
}
|
||||
|
||||
podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP)
|
||||
var requestedMAC net.HardwareAddr
|
||||
if ctr.requestedMAC != nil {
|
||||
requestedMAC = ctr.requestedMAC
|
||||
// cancel request for a specific MAC in case the container is reused later
|
||||
ctr.requestedMAC = nil
|
||||
} else {
|
||||
requestedMAC = ctr.config.StaticMAC
|
||||
}
|
||||
|
||||
podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP, requestedMAC)
|
||||
|
||||
results, err := r.netPlugin.SetUpPod(podNetwork)
|
||||
if err != nil {
|
||||
@ -447,7 +463,16 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
|
||||
requestedIP = ctr.config.StaticIP
|
||||
}
|
||||
|
||||
podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP)
|
||||
var requestedMAC net.HardwareAddr
|
||||
if ctr.requestedMAC != nil {
|
||||
requestedMAC = ctr.requestedMAC
|
||||
// cancel request for a specific MAC in case the container is reused later
|
||||
ctr.requestedMAC = nil
|
||||
} else {
|
||||
requestedMAC = ctr.config.StaticMAC
|
||||
}
|
||||
|
||||
podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP, requestedMAC)
|
||||
|
||||
if err := r.netPlugin.TearDownPod(podNetwork); err != nil {
|
||||
return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", ctr.ID())
|
||||
|
@ -1052,6 +1052,31 @@ func WithStaticIP(ip net.IP) CtrCreateOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithStaticMAC indicates that the container should request a static MAC from
|
||||
// the CNI plugins.
|
||||
// It cannot be set unless WithNetNS has already been passed.
|
||||
// Further, it cannot be set if additional CNI networks to join have been
|
||||
// specified.
|
||||
func WithStaticMAC(mac net.HardwareAddr) CtrCreateOption {
|
||||
return func(ctr *Container) error {
|
||||
if ctr.valid {
|
||||
return define.ErrCtrFinalized
|
||||
}
|
||||
|
||||
if !ctr.config.CreateNetNS {
|
||||
return errors.Wrapf(define.ErrInvalidArg, "cannot set a static MAC if the container is not creating a network namespace")
|
||||
}
|
||||
|
||||
if len(ctr.config.Networks) != 0 {
|
||||
return errors.Wrapf(define.ErrInvalidArg, "cannot set a static MAC if joining additional CNI networks")
|
||||
}
|
||||
|
||||
ctr.config.StaticMAC = mac
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogDriver sets the log driver for the container
|
||||
func WithLogDriver(driver string) CtrCreateOption {
|
||||
return func(ctr *Container) error {
|
||||
|
@ -553,6 +553,7 @@ func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues)
|
||||
Name: c.Name,
|
||||
IgnoreRootfs: c.IgnoreRootfs,
|
||||
IgnoreStaticIP: c.IgnoreStaticIP,
|
||||
IgnoreStaticMAC: c.IgnoreStaticMAC,
|
||||
}
|
||||
|
||||
filterFuncs = append(filterFuncs, func(c *libpod.Container) bool {
|
||||
|
@ -396,6 +396,14 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
|
||||
options = append(options, libpod.WithStaticIP(ip))
|
||||
}
|
||||
|
||||
if c.MacAddress != "" {
|
||||
mac, err := net.ParseMAC(c.MacAddress)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as MAC address: %v", c.MacAddress, err)
|
||||
}
|
||||
options = append(options, libpod.WithStaticMAC(mac))
|
||||
}
|
||||
|
||||
options = append(options, libpod.WithPrivileged(c.Privileged))
|
||||
|
||||
useImageVolumes := c.ImageVolumeType == TypeBind
|
||||
|
@ -334,6 +334,10 @@ var _ = Describe("Podman checkpoint", func() {
|
||||
IPBefore.WaitWithDefaultTimeout()
|
||||
Expect(IPBefore.ExitCode()).To(Equal(0))
|
||||
|
||||
MACBefore := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.MacAddress}}"})
|
||||
MACBefore.WaitWithDefaultTimeout()
|
||||
Expect(MACBefore.ExitCode()).To(Equal(0))
|
||||
|
||||
result := podmanTest.Podman([]string{"container", "checkpoint", "test_name"})
|
||||
result.WaitWithDefaultTimeout()
|
||||
|
||||
@ -348,9 +352,16 @@ var _ = Describe("Podman checkpoint", func() {
|
||||
IPAfter.WaitWithDefaultTimeout()
|
||||
Expect(IPAfter.ExitCode()).To(Equal(0))
|
||||
|
||||
MACAfter := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.MacAddress}}"})
|
||||
MACAfter.WaitWithDefaultTimeout()
|
||||
Expect(MACAfter.ExitCode()).To(Equal(0))
|
||||
|
||||
// Check that IP address did not change between checkpointing and restoring
|
||||
Expect(IPBefore.OutputToString()).To(Equal(IPAfter.OutputToString()))
|
||||
|
||||
// Check that MAC address did not change between checkpointing and restoring
|
||||
Expect(MACBefore.OutputToString()).To(Equal(MACAfter.OutputToString()))
|
||||
|
||||
Expect(result.ExitCode()).To(Equal(0))
|
||||
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
|
||||
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
|
||||
|
Reference in New Issue
Block a user