Skip to content

Commit 3f3feaa

Browse files
Merge pull request containers#10716 from cdoern/podFlags
Podman Pod Create --cpus and --cpuset-cpus flags
2 parents b0a3ac3 + bbd085a commit 3f3feaa

File tree

15 files changed

+326
-29
lines changed

15 files changed

+326
-29
lines changed

cmd/podman/containers/create.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,6 @@ func createInit(c *cobra.Command) error {
222222
}
223223
cliVals.Env = env
224224
}
225-
226225
if c.Flag("cgroups").Changed && cliVals.CGroupsMode == "split" && registry.IsRemote() {
227226
return errors.Errorf("the option --cgroups=%q is not supported in remote mode", cliVals.CGroupsMode)
228227
}
@@ -292,6 +291,8 @@ func createPodIfNecessary(s *specgen.SpecGenerator, netOpts *entities.NetOptions
292291
Net: netOpts,
293292
CreateCommand: os.Args,
294293
Hostname: s.ContainerBasicConfig.Hostname,
294+
Cpus: cliVals.CPUS,
295+
CpusetCpus: cliVals.CPUSetCPUs,
295296
}
296297
// Unset config values we passed to the pod to prevent them being used twice for the container and pod.
297298
s.ContainerBasicConfig.Hostname = ""

cmd/podman/pods/create.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ import (
55
"fmt"
66
"io/ioutil"
77
"os"
8+
"runtime"
9+
"sort"
10+
"strconv"
811
"strings"
912

1013
"github.com/containers/common/pkg/completion"
14+
"github.com/containers/common/pkg/sysinfo"
1115
"github.com/containers/podman/v3/cmd/podman/common"
1216
"github.com/containers/podman/v3/cmd/podman/parse"
1317
"github.com/containers/podman/v3/cmd/podman/registry"
@@ -16,6 +20,7 @@ import (
1620
"github.com/containers/podman/v3/pkg/errorhandling"
1721
"github.com/containers/podman/v3/pkg/specgen"
1822
"github.com/containers/podman/v3/pkg/util"
23+
"github.com/docker/docker/pkg/parsers"
1924
"github.com/pkg/errors"
2025
"github.com/sirupsen/logrus"
2126
"github.com/spf13/cobra"
@@ -55,6 +60,14 @@ func init() {
5560

5661
common.DefineNetFlags(createCommand)
5762

63+
cpusetflagName := "cpuset-cpus"
64+
flags.StringVar(&createOptions.CpusetCpus, cpusetflagName, "", "CPUs in which to allow execution")
65+
_ = createCommand.RegisterFlagCompletionFunc(cpusetflagName, completion.AutocompleteDefault)
66+
67+
cpusflagName := "cpus"
68+
flags.Float64Var(&createOptions.Cpus, cpusflagName, 0.000, "set amount of CPUs for the pod")
69+
_ = createCommand.RegisterFlagCompletionFunc(cpusflagName, completion.AutocompleteDefault)
70+
5871
cgroupParentflagName := "cgroup-parent"
5972
flags.StringVar(&createOptions.CGroupParent, cgroupParentflagName, "", "Set parent cgroup for the pod")
6073
_ = createCommand.RegisterFlagCompletionFunc(cgroupParentflagName, completion.AutocompleteDefault)
@@ -185,6 +198,46 @@ func create(cmd *cobra.Command, args []string) error {
185198
}
186199
}
187200

201+
numCPU := sysinfo.NumCPU()
202+
if numCPU == 0 {
203+
numCPU = runtime.NumCPU()
204+
}
205+
if createOptions.Cpus > float64(numCPU) {
206+
createOptions.Cpus = float64(numCPU)
207+
}
208+
copy := createOptions.CpusetCpus
209+
cpuSet := createOptions.Cpus
210+
if cpuSet == 0 {
211+
cpuSet = float64(sysinfo.NumCPU())
212+
}
213+
ret, err := parsers.ParseUintList(copy)
214+
copy = ""
215+
if err != nil {
216+
errors.Wrapf(err, "could not parse list")
217+
}
218+
var vals []int
219+
for ind, val := range ret {
220+
if val {
221+
vals = append(vals, ind)
222+
}
223+
}
224+
sort.Ints(vals)
225+
for ind, core := range vals {
226+
if core > int(cpuSet) {
227+
if copy == "" {
228+
copy = "0-" + strconv.Itoa(int(cpuSet))
229+
createOptions.CpusetCpus = copy
230+
break
231+
} else {
232+
createOptions.CpusetCpus = copy
233+
break
234+
}
235+
} else if ind != 0 {
236+
copy += "," + strconv.Itoa(core)
237+
} else {
238+
copy = "" + strconv.Itoa(core)
239+
}
240+
}
188241
response, err := registry.ContainerEngine().PodCreate(context.Background(), createOptions)
189242
if err != nil {
190243
return err

docs/source/markdown/podman-pod-create.1.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@ Add a host to the /etc/hosts file shared between all containers in the pod.
2323

2424
Path to cgroups under which the cgroup for the pod will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist.
2525

26+
#### **--cpus**=*amount*
27+
28+
Set the total number of CPUs delegated to the pod. Default is 0.000 which indicates that there is no limit on computation power.
29+
30+
#### **--cpuset-cpus**=*amount*
31+
32+
Limit the CPUs to support execution. First CPU is numbered 0. Unlike --cpus this is of type string and parsed as a list of numbers
33+
34+
Format is 0-3,0,1
35+
36+
Examples of the List Format:
37+
38+
0-4,9 # bits 0, 1, 2, 3, 4, and 9 set
39+
0-2,7,12-14 # bits 0, 1, 2, 7, 12, 13, and 14 set
40+
41+
2642
#### **--dns**=*ipaddr*
2743

2844
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.

libpod/container_validate.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,5 @@ func (c *Container) validate() error {
131131
if c.config.User == "" && (c.config.Spec.Process.User.UID != 0 || c.config.Spec.Process.User.GID != 0) {
132132
return errors.Wrapf(define.ErrInvalidArg, "please set User explicitly via WithUser() instead of in OCI spec directly")
133133
}
134-
135134
return nil
136135
}

libpod/define/pod_inspect.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ type InspectPodData struct {
5151
// Containers gives a brief summary of all containers in the pod and
5252
// their current status.
5353
Containers []InspectPodContainerInfo `json:"Containers,omitempty"`
54+
// CPUPeriod contains the CPU period of the pod
55+
CPUPeriod uint64 `json:"cpu_period,omitempty"`
56+
// CPUQuota contains the CPU quota of the pod
57+
CPUQuota int64 `json:"cpu_quota,omitempty"`
58+
// CPUSetCPUs contains linux specific CPU data for the pod
59+
CPUSetCPUs string `json:"cpuset_cpus,omitempty"`
5460
}
5561

5662
// InspectPodInfraConfig contains the configuration of the pod's infra
@@ -91,6 +97,12 @@ type InspectPodInfraConfig struct {
9197
Networks []string
9298
// NetworkOptions are additional options for each network
9399
NetworkOptions map[string][]string
100+
// CPUPeriod contains the CPU period of the pod
101+
CPUPeriod uint64 `json:"cpu_period,omitempty"`
102+
// CPUQuota contains the CPU quota of the pod
103+
CPUQuota int64 `json:"cpu_quota,omitempty"`
104+
// CPUSetCPUs contains linux specific CPU data for the container
105+
CPUSetCPUs string `json:"cpuset_cpus,omitempty"`
94106
}
95107

96108
// InspectPodContainerInfo contains information on a container in a pod.

libpod/options.go

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/containers/storage"
2121
"github.com/containers/storage/pkg/idtools"
2222
"github.com/cri-o/ocicni/pkg/ocicni"
23+
"github.com/opencontainers/runtime-spec/specs-go"
2324
"github.com/opencontainers/runtime-tools/generate"
2425
"github.com/pkg/errors"
2526
"github.com/sirupsen/logrus"
@@ -559,7 +560,6 @@ func WithMaxLogSize(limit int64) CtrCreateOption {
559560
if ctr.valid {
560561
return define.ErrRuntimeFinalized
561562
}
562-
563563
ctr.config.LogSize = limit
564564

565565
return nil
@@ -867,7 +867,6 @@ func WithMountNSFrom(nsCtr *Container) CtrCreateOption {
867867
if err := checkDependencyContainer(nsCtr, ctr); err != nil {
868868
return err
869869
}
870-
871870
ctr.config.MountNsCtr = nsCtr.ID()
872871

873872
return nil
@@ -2359,3 +2358,42 @@ func WithVolatile() CtrCreateOption {
23592358
return nil
23602359
}
23612360
}
2361+
2362+
// WithPodCPUPAQ takes the given cpu period and quota and inserts them in the proper place.
2363+
func WithPodCPUPAQ(period uint64, quota int64) PodCreateOption {
2364+
return func(pod *Pod) error {
2365+
if pod.valid {
2366+
return define.ErrPodFinalized
2367+
}
2368+
if pod.CPUPeriod() != 0 && pod.CPUQuota() != 0 {
2369+
pod.config.InfraContainer.ResourceLimits.CPU = &specs.LinuxCPU{
2370+
Period: &period,
2371+
Quota: &quota,
2372+
}
2373+
} else {
2374+
pod.config.InfraContainer.ResourceLimits = &specs.LinuxResources{}
2375+
pod.config.InfraContainer.ResourceLimits.CPU = &specs.LinuxCPU{
2376+
Period: &period,
2377+
Quota: &quota,
2378+
}
2379+
}
2380+
return nil
2381+
}
2382+
}
2383+
2384+
// WithPodCPUSetCPUS computes and sets the Cpus linux resource string which determines the amount of cores, from those available, we are allowed to execute on
2385+
func WithPodCPUSetCPUs(inp string) PodCreateOption {
2386+
return func(pod *Pod) error {
2387+
if pod.valid {
2388+
return define.ErrPodFinalized
2389+
}
2390+
if pod.ResourceLim().CPU.Period != nil {
2391+
pod.config.InfraContainer.ResourceLimits.CPU.Cpus = inp
2392+
} else {
2393+
pod.config.InfraContainer.ResourceLimits = &specs.LinuxResources{}
2394+
pod.config.InfraContainer.ResourceLimits.CPU = &specs.LinuxCPU{}
2395+
pod.config.InfraContainer.ResourceLimits.CPU.Cpus = inp
2396+
}
2397+
return nil
2398+
}
2399+
}

libpod/pod.go

Lines changed: 85 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package libpod
22

33
import (
4+
"context"
45
"net"
56
"time"
67

78
"github.com/containers/podman/v3/libpod/define"
89
"github.com/containers/podman/v3/libpod/lock"
910
"github.com/cri-o/ocicni/pkg/ocicni"
11+
"github.com/opencontainers/runtime-spec/specs-go"
1012
"github.com/pkg/errors"
1113
)
1214

@@ -91,25 +93,26 @@ type podState struct {
9193
// Generally speaking, aside from those two exceptions, these options will set
9294
// the equivalent field in the container's configuration.
9395
type InfraContainerConfig struct {
94-
ConmonPidFile string `json:"conmonPidFile"`
95-
HasInfraContainer bool `json:"makeInfraContainer"`
96-
NoNetwork bool `json:"noNetwork,omitempty"`
97-
HostNetwork bool `json:"infraHostNetwork,omitempty"`
98-
PortBindings []ocicni.PortMapping `json:"infraPortBindings"`
99-
StaticIP net.IP `json:"staticIP,omitempty"`
100-
StaticMAC net.HardwareAddr `json:"staticMAC,omitempty"`
101-
UseImageResolvConf bool `json:"useImageResolvConf,omitempty"`
102-
DNSServer []string `json:"dnsServer,omitempty"`
103-
DNSSearch []string `json:"dnsSearch,omitempty"`
104-
DNSOption []string `json:"dnsOption,omitempty"`
105-
UseImageHosts bool `json:"useImageHosts,omitempty"`
106-
HostAdd []string `json:"hostsAdd,omitempty"`
107-
Networks []string `json:"networks,omitempty"`
108-
ExitCommand []string `json:"exitCommand,omitempty"`
109-
InfraImage string `json:"infraImage,omitempty"`
110-
InfraCommand []string `json:"infraCommand,omitempty"`
111-
Slirp4netns bool `json:"slirp4netns,omitempty"`
112-
NetworkOptions map[string][]string `json:"network_options,omitempty"`
96+
ConmonPidFile string `json:"conmonPidFile"`
97+
HasInfraContainer bool `json:"makeInfraContainer"`
98+
NoNetwork bool `json:"noNetwork,omitempty"`
99+
HostNetwork bool `json:"infraHostNetwork,omitempty"`
100+
PortBindings []ocicni.PortMapping `json:"infraPortBindings"`
101+
StaticIP net.IP `json:"staticIP,omitempty"`
102+
StaticMAC net.HardwareAddr `json:"staticMAC,omitempty"`
103+
UseImageResolvConf bool `json:"useImageResolvConf,omitempty"`
104+
DNSServer []string `json:"dnsServer,omitempty"`
105+
DNSSearch []string `json:"dnsSearch,omitempty"`
106+
DNSOption []string `json:"dnsOption,omitempty"`
107+
UseImageHosts bool `json:"useImageHosts,omitempty"`
108+
HostAdd []string `json:"hostsAdd,omitempty"`
109+
Networks []string `json:"networks,omitempty"`
110+
ExitCommand []string `json:"exitCommand,omitempty"`
111+
InfraImage string `json:"infraImage,omitempty"`
112+
InfraCommand []string `json:"infraCommand,omitempty"`
113+
Slirp4netns bool `json:"slirp4netns,omitempty"`
114+
NetworkOptions map[string][]string `json:"network_options,omitempty"`
115+
ResourceLimits *specs.LinuxResources `json:"resource_limits,omitempty"`
113116
}
114117

115118
// ID retrieves the pod's ID
@@ -128,6 +131,45 @@ func (p *Pod) Namespace() string {
128131
return p.config.Namespace
129132
}
130133

134+
// ResourceLim returns the cpuset resource limits for the pod
135+
func (p *Pod) ResourceLim() *specs.LinuxResources {
136+
resCopy := &specs.LinuxResources{}
137+
if err := JSONDeepCopy(p.config.InfraContainer.ResourceLimits, resCopy); err != nil {
138+
return nil
139+
}
140+
if resCopy != nil && resCopy.CPU != nil {
141+
return resCopy
142+
}
143+
empty := &specs.LinuxResources{
144+
CPU: &specs.LinuxCPU{},
145+
}
146+
return empty
147+
}
148+
149+
// CPUPeriod returns the pod CPU period
150+
func (p *Pod) CPUPeriod() uint64 {
151+
resCopy := &specs.LinuxResources{}
152+
if err := JSONDeepCopy(p.config.InfraContainer.ResourceLimits, resCopy); err != nil {
153+
return 0
154+
}
155+
if resCopy != nil && resCopy.CPU != nil && resCopy.CPU.Period != nil {
156+
return *resCopy.CPU.Period
157+
}
158+
return 0
159+
}
160+
161+
// CPUQuota returns the pod CPU quota
162+
func (p *Pod) CPUQuota() int64 {
163+
resCopy := &specs.LinuxResources{}
164+
if err := JSONDeepCopy(p.config.InfraContainer.ResourceLimits, resCopy); err != nil {
165+
return 0
166+
}
167+
if resCopy != nil && resCopy.CPU != nil && resCopy.CPU.Quota != nil {
168+
return *resCopy.CPU.Quota
169+
}
170+
return 0
171+
}
172+
131173
// Labels returns the pod's labels
132174
func (p *Pod) Labels() map[string]string {
133175
labels := make(map[string]string)
@@ -208,7 +250,31 @@ func (p *Pod) CgroupPath() (string, error) {
208250
if err := p.updatePod(); err != nil {
209251
return "", err
210252
}
253+
if p.state.CgroupPath != "" {
254+
return p.state.CgroupPath, nil
255+
}
256+
if !p.HasInfraContainer() {
257+
return "", errors.Wrap(define.ErrNoSuchCtr, "pod has no infra container")
258+
}
259+
260+
id := p.state.InfraContainerID
211261

262+
if id != "" {
263+
ctr, err := p.runtime.state.Container(id)
264+
if err != nil {
265+
return "", errors.Wrapf(err, "could not get infra")
266+
}
267+
if ctr != nil {
268+
ctr.Start(context.Background(), false)
269+
cgroupPath, err := ctr.CGroupPath()
270+
if err != nil {
271+
return "", errors.Wrapf(err, "could not get container cgroup")
272+
}
273+
p.state.CgroupPath = cgroupPath
274+
p.save()
275+
return cgroupPath, nil
276+
}
277+
}
212278
return p.state.CgroupPath, nil
213279
}
214280

libpod/pod_api.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,9 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
538538
infraConfig.StaticMAC = p.config.InfraContainer.StaticMAC.String()
539539
infraConfig.NoManageResolvConf = p.config.InfraContainer.UseImageResolvConf
540540
infraConfig.NoManageHosts = p.config.InfraContainer.UseImageHosts
541+
infraConfig.CPUPeriod = p.CPUPeriod()
542+
infraConfig.CPUQuota = p.CPUQuota()
543+
infraConfig.CPUSetCPUs = p.ResourceLim().CPU.Cpus
541544

542545
if len(p.config.InfraContainer.DNSServer) > 0 {
543546
infraConfig.DNSServer = make([]string, 0, len(p.config.InfraContainer.DNSServer))
@@ -581,6 +584,9 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
581584
SharedNamespaces: sharesNS,
582585
NumContainers: uint(len(containers)),
583586
Containers: ctrs,
587+
CPUSetCPUs: p.ResourceLim().CPU.Cpus,
588+
CPUPeriod: p.CPUPeriod(),
589+
CPUQuota: p.CPUQuota(),
584590
}
585591

586592
return &inspectData, nil

0 commit comments

Comments
 (0)