mirror of
https://github.com/containers/podman
synced 2024-10-19 08:44:11 +00:00
Merge pull request #11686 from cdoern/podDeviceOptions
Pod Device-Read-BPS support
This commit is contained in:
commit
81aabc8054
|
@ -164,14 +164,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
|||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(deviceCgroupRuleFlagName, completion.AutocompleteNone)
|
||||
|
||||
deviceReadBpsFlagName := "device-read-bps"
|
||||
createFlags.StringSliceVar(
|
||||
&cf.DeviceReadBPs,
|
||||
deviceReadBpsFlagName, []string{},
|
||||
"Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(deviceReadBpsFlagName, completion.AutocompleteDefault)
|
||||
|
||||
deviceReadIopsFlagName := "device-read-iops"
|
||||
createFlags.StringSliceVar(
|
||||
&cf.DeviceReadIOPs,
|
||||
|
@ -869,6 +861,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
|||
volumeDesciption,
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag)
|
||||
|
||||
deviceFlagName := "device"
|
||||
createFlags.StringSliceVar(
|
||||
&cf.Devices,
|
||||
|
@ -876,4 +869,12 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
|||
"Add a host device to the container",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault)
|
||||
|
||||
deviceReadBpsFlagName := "device-read-bps"
|
||||
createFlags.StringSliceVar(
|
||||
&cf.DeviceReadBPs,
|
||||
deviceReadBpsFlagName, []string{},
|
||||
"Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(deviceReadBpsFlagName, completion.AutocompleteDefault)
|
||||
}
|
||||
|
|
|
@ -101,6 +101,7 @@ func create(cmd *cobra.Command, args []string) error {
|
|||
podIDFD *os.File
|
||||
imageName string
|
||||
rawImageName string
|
||||
podName string
|
||||
)
|
||||
labelFile = infraOptions.LabelFile
|
||||
labels = infraOptions.Label
|
||||
|
@ -158,10 +159,12 @@ func create(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
podName = createOptions.Name
|
||||
err = common.ContainerToPodOptions(&infraOptions, &createOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
createOptions.Name = podName
|
||||
}
|
||||
|
||||
if cmd.Flag("pod-id-file").Changed {
|
||||
|
@ -264,6 +267,17 @@ func create(cmd *cobra.Command, args []string) error {
|
|||
podSpec.ImageVolumes = podSpec.InfraContainerSpec.ImageVolumes
|
||||
podSpec.OverlayVolumes = podSpec.InfraContainerSpec.OverlayVolumes
|
||||
podSpec.Mounts = podSpec.InfraContainerSpec.Mounts
|
||||
|
||||
// Marshall and Unmarshal the spec in order to map similar entities
|
||||
wrapped, err := json.Marshal(podSpec.InfraContainerSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal(wrapped, podSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
podSpec.Name = podName
|
||||
}
|
||||
PodSpec := entities.PodSpec{PodSpecGen: *podSpec}
|
||||
response, err := registry.ContainerEngine().PodCreate(context.Background(), PodSpec)
|
||||
|
|
|
@ -41,7 +41,7 @@ Examples of the List Format:
|
|||
#### **--device**=_host-device_[**:**_container-device_][**:**_permissions_]
|
||||
|
||||
Add a host device to the pod. Optional *permissions* parameter
|
||||
can be used to specify device permissions It is a combination of
|
||||
can be used to specify device permissions. It is a combination of
|
||||
**r** for read, **w** for write, and **m** for **mknod**(2).
|
||||
|
||||
Example: **--device=/dev/sdc:/dev/xvdc:rwm**.
|
||||
|
@ -55,6 +55,10 @@ Podman may load kernel modules required for using the specified
|
|||
device. The devices that Podman will load modules for when necessary are:
|
||||
/dev/fuse.
|
||||
|
||||
#### **--device-read-bps**=*path*
|
||||
|
||||
Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)
|
||||
|
||||
#### **--dns**=*ipaddr*
|
||||
|
||||
Set custom DNS servers in the /etc/resolv.conf file that will be shared between all containers in the pod. A special option, "none" is allowed which disables creation of /etc/resolv.conf for the pod.
|
||||
|
|
|
@ -531,49 +531,25 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
|
|||
hostConfig.BlkioWeightDevice = append(hostConfig.BlkioWeightDevice, weightDev)
|
||||
}
|
||||
|
||||
handleThrottleDevice := func(devs []spec.LinuxThrottleDevice) ([]define.InspectBlkioThrottleDevice, error) {
|
||||
out := []define.InspectBlkioThrottleDevice{}
|
||||
for _, dev := range devs {
|
||||
key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor)
|
||||
if deviceNodes == nil {
|
||||
nodes, err := util.FindDeviceNodes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deviceNodes = nodes
|
||||
}
|
||||
path, ok := deviceNodes[key]
|
||||
if !ok {
|
||||
logrus.Infof("Could not locate throttle device %s in system devices", key)
|
||||
continue
|
||||
}
|
||||
throttleDev := define.InspectBlkioThrottleDevice{}
|
||||
throttleDev.Path = path
|
||||
throttleDev.Rate = dev.Rate
|
||||
out = append(out, throttleDev)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
readBps, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleReadBpsDevice)
|
||||
readBps, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleReadBpsDevice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hostConfig.BlkioDeviceReadBps = readBps
|
||||
|
||||
writeBps, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleWriteBpsDevice)
|
||||
writeBps, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleWriteBpsDevice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hostConfig.BlkioDeviceWriteBps = writeBps
|
||||
|
||||
readIops, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleReadIOPSDevice)
|
||||
readIops, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleReadIOPSDevice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hostConfig.BlkioDeviceReadIOps = readIops
|
||||
|
||||
writeIops, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleWriteIOPSDevice)
|
||||
writeIops, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleWriteIOPSDevice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -894,3 +870,27 @@ func (c *Container) GetDevices(priv bool, ctrSpec spec.Spec, deviceNodes map[str
|
|||
}
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
func blkioDeviceThrottle(deviceNodes map[string]string, devs []spec.LinuxThrottleDevice) ([]define.InspectBlkioThrottleDevice, error) {
|
||||
out := []define.InspectBlkioThrottleDevice{}
|
||||
for _, dev := range devs {
|
||||
key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor)
|
||||
if deviceNodes == nil {
|
||||
nodes, err := util.FindDeviceNodes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deviceNodes = nodes
|
||||
}
|
||||
path, ok := deviceNodes[key]
|
||||
if !ok {
|
||||
logrus.Infof("Could not locate throttle device %s in system devices", key)
|
||||
continue
|
||||
}
|
||||
throttleDev := define.InspectBlkioThrottleDevice{}
|
||||
throttleDev.Path = path
|
||||
throttleDev.Rate = dev.Rate
|
||||
out = append(out, throttleDev)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
|
|
@ -61,6 +61,8 @@ type InspectPodData struct {
|
|||
Mounts []InspectMount `json:"mounts,omitempty"`
|
||||
// Devices contains the specified host devices
|
||||
Devices []InspectDevice `json:"devices,omitempty"`
|
||||
// BlkioDeviceReadBps contains the Read/Access limit for the pod's devices
|
||||
BlkioDeviceReadBps []InspectBlkioThrottleDevice `json:"device_read_bps,omitempty"`
|
||||
}
|
||||
|
||||
// InspectPodInfraConfig contains the configuration of the pod's infra
|
||||
|
|
|
@ -584,6 +584,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
|
|||
var infraConfig *define.InspectPodInfraConfig
|
||||
var inspectMounts []define.InspectMount
|
||||
var devices []define.InspectDevice
|
||||
var deviceLimits []define.InspectBlkioThrottleDevice
|
||||
if p.state.InfraContainerID != "" {
|
||||
infra, err := p.runtime.GetContainer(p.state.InfraContainerID)
|
||||
if err != nil {
|
||||
|
@ -604,12 +605,18 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nodes map[string]string
|
||||
devices, err = infra.GetDevices(false, *infra.config.Spec, nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spec := infra.config.Spec
|
||||
if spec.Linux != nil && spec.Linux.Resources != nil && spec.Linux.Resources.BlockIO != nil {
|
||||
deviceLimits, err = blkioDeviceThrottle(nodes, spec.Linux.Resources.BlockIO.ThrottleReadBpsDevice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(infra.config.ContainerNetworkConfig.DNSServer) > 0 {
|
||||
infraConfig.DNSServer = make([]string, 0, len(infra.config.ContainerNetworkConfig.DNSServer))
|
||||
|
@ -638,28 +645,29 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
|
|||
}
|
||||
|
||||
inspectData := define.InspectPodData{
|
||||
ID: p.ID(),
|
||||
Name: p.Name(),
|
||||
Namespace: p.Namespace(),
|
||||
Created: p.CreatedTime(),
|
||||
CreateCommand: p.config.CreateCommand,
|
||||
State: podState,
|
||||
Hostname: p.config.Hostname,
|
||||
Labels: p.Labels(),
|
||||
CreateCgroup: p.config.UsePodCgroup,
|
||||
CgroupParent: p.CgroupParent(),
|
||||
CgroupPath: p.state.CgroupPath,
|
||||
CreateInfra: infraConfig != nil,
|
||||
InfraContainerID: p.state.InfraContainerID,
|
||||
InfraConfig: infraConfig,
|
||||
SharedNamespaces: sharesNS,
|
||||
NumContainers: uint(len(containers)),
|
||||
Containers: ctrs,
|
||||
CPUSetCPUs: p.ResourceLim().CPU.Cpus,
|
||||
CPUPeriod: p.CPUPeriod(),
|
||||
CPUQuota: p.CPUQuota(),
|
||||
Mounts: inspectMounts,
|
||||
Devices: devices,
|
||||
ID: p.ID(),
|
||||
Name: p.Name(),
|
||||
Namespace: p.Namespace(),
|
||||
Created: p.CreatedTime(),
|
||||
CreateCommand: p.config.CreateCommand,
|
||||
State: podState,
|
||||
Hostname: p.config.Hostname,
|
||||
Labels: p.Labels(),
|
||||
CreateCgroup: p.config.UsePodCgroup,
|
||||
CgroupParent: p.CgroupParent(),
|
||||
CgroupPath: p.state.CgroupPath,
|
||||
CreateInfra: infraConfig != nil,
|
||||
InfraContainerID: p.state.InfraContainerID,
|
||||
InfraConfig: infraConfig,
|
||||
SharedNamespaces: sharesNS,
|
||||
NumContainers: uint(len(containers)),
|
||||
Containers: ctrs,
|
||||
CPUSetCPUs: p.ResourceLim().CPU.Cpus,
|
||||
CPUPeriod: p.CPUPeriod(),
|
||||
CPUQuota: p.CPUQuota(),
|
||||
Mounts: inspectMounts,
|
||||
Devices: devices,
|
||||
BlkioDeviceReadBps: deviceLimits,
|
||||
}
|
||||
|
||||
return &inspectData, nil
|
||||
|
|
|
@ -119,6 +119,7 @@ type PodCreateOptions struct {
|
|||
CGroupParent string `json:"cgroup_parent,omitempty"`
|
||||
CreateCommand []string `json:"create_command,omitempty"`
|
||||
Devices []string `json:"devices,omitempty"`
|
||||
DeviceReadBPs []string `json:"device_read_bps,omitempty"`
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
Infra bool `json:"infra,omitempty"`
|
||||
InfraImage string `json:"infra_image,omitempty"`
|
||||
|
@ -167,7 +168,7 @@ type ContainerCreateOptions struct {
|
|||
CPUSetMems string
|
||||
Devices []string `json:"devices,omitempty"`
|
||||
DeviceCGroupRule []string
|
||||
DeviceReadBPs []string
|
||||
DeviceReadBPs []string `json:"device_read_bps,omitempty"`
|
||||
DeviceReadIOPs []string
|
||||
DeviceWriteBPs []string
|
||||
DeviceWriteIOPs []string
|
||||
|
@ -200,7 +201,7 @@ type ContainerCreateOptions struct {
|
|||
MemoryReservation string
|
||||
MemorySwap string
|
||||
MemorySwappiness int64
|
||||
Name string `json:"container_name,omitempty"`
|
||||
Name string `json:"container_name"`
|
||||
NoHealthCheck bool
|
||||
OOMKillDisable bool
|
||||
OOMScoreAdj int
|
||||
|
|
|
@ -191,9 +191,6 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
|
|||
if len(s.User) == 0 && inspectData != nil {
|
||||
s.User = inspectData.Config.User
|
||||
}
|
||||
if err := finishThrottleDevices(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Unless already set via the CLI, check if we need to disable process
|
||||
// labels or set the defaults.
|
||||
if len(s.SelinuxOpts) == 0 {
|
||||
|
@ -251,10 +248,10 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
|
|||
return warnings, nil
|
||||
}
|
||||
|
||||
// finishThrottleDevices takes the temporary representation of the throttle
|
||||
// FinishThrottleDevices takes the temporary representation of the throttle
|
||||
// devices in the specgen and looks up the major and major minors. it then
|
||||
// sets the throttle devices proper in the specgen
|
||||
func finishThrottleDevices(s *specgen.SpecGenerator) error {
|
||||
func FinishThrottleDevices(s *specgen.SpecGenerator) error {
|
||||
if bps := s.ThrottleReadBpsDevice; len(bps) > 0 {
|
||||
for k, v := range bps {
|
||||
statT := unix.Stat_t{}
|
||||
|
@ -263,6 +260,9 @@ func finishThrottleDevices(s *specgen.SpecGenerator) error {
|
|||
}
|
||||
v.Major = (int64(unix.Major(uint64(statT.Rdev))))
|
||||
v.Minor = (int64(unix.Minor(uint64(statT.Rdev))))
|
||||
if s.ResourceLimits.BlockIO == nil {
|
||||
s.ResourceLimits.BlockIO = new(spec.LinuxBlockIO)
|
||||
}
|
||||
s.ResourceLimits.BlockIO.ThrottleReadBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleReadBpsDevice, v)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package generate
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -52,6 +53,24 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
|
|||
if infraConfig != nil && len(infraConfig.Spec.Linux.Devices) > 0 {
|
||||
s.DevicesFrom = append(s.DevicesFrom, infraConfig.ID)
|
||||
}
|
||||
if infraConfig != nil && infraConfig.Spec.Linux.Resources != nil && infraConfig.Spec.Linux.Resources.BlockIO != nil && len(infraConfig.Spec.Linux.Resources.BlockIO.ThrottleReadBpsDevice) > 0 {
|
||||
tempDev := make(map[string]spec.LinuxThrottleDevice)
|
||||
for _, val := range infraConfig.Spec.Linux.Resources.BlockIO.ThrottleReadBpsDevice {
|
||||
nodes, err := util.FindDeviceNodes()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
key := fmt.Sprintf("%d:%d", val.Major, val.Minor)
|
||||
tempDev[nodes[key]] = spec.LinuxThrottleDevice{Rate: uint64(val.Rate)}
|
||||
}
|
||||
for i, dev := range s.ThrottleReadBpsDevice {
|
||||
tempDev[i] = dev
|
||||
}
|
||||
s.ThrottleReadBpsDevice = tempDev
|
||||
}
|
||||
if err := FinishThrottleDevices(s); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
// Set defaults for unset namespaces
|
||||
if s.PidNS.IsDefault() {
|
||||
defaultNS, err := GetDefaultNamespaceMode("pid", rtc, pod)
|
||||
|
|
|
@ -201,6 +201,8 @@ type PodResourceConfig struct {
|
|||
CPUPeriod uint64 `json:"cpu_period,omitempty"`
|
||||
// CPU quota of the cpuset, determined by --cpus
|
||||
CPUQuota int64 `json:"cpu_quota,omitempty"`
|
||||
// ThrottleReadBpsDevice contains the rate at which the devices in the pod can be read from/accessed
|
||||
ThrottleReadBpsDevice map[string]spec.LinuxThrottleDevice `json:"throttleReadBpsDevice,omitempty"`
|
||||
}
|
||||
|
||||
// NewPodSpecGenerator creates a new pod spec
|
||||
|
|
|
@ -903,4 +903,25 @@ ENTRYPOINT ["sleep","99999"]
|
|||
|
||||
})
|
||||
|
||||
It("podman pod create --device-read-bps", func() {
|
||||
SkipIfRootless("Cannot create devices in /dev in rootless mode")
|
||||
SkipIfRootlessCgroupsV1("Setting device-read-bps not supported on cgroupv1 for rootless users")
|
||||
|
||||
podName := "testPod"
|
||||
session := podmanTest.Podman([]string{"pod", "create", "--device-read-bps", "/dev/zero:1mb", "--name", podName})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
|
||||
if CGROUPSV2 {
|
||||
session = podmanTest.Podman([]string{"run", "--rm", "--pod", podName, ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/io.max"})
|
||||
} else {
|
||||
session = podmanTest.Podman([]string{"run", "--rm", "--pod", podName, ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.read_bps_device"})
|
||||
}
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
if !CGROUPSV2 {
|
||||
Expect(session.OutputToString()).To(ContainSubstring("1048576"))
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue