1/*
2Copyright 2017 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package stats
18
19import (
20	"fmt"
21
22	cadvisorapiv1 "github.com/google/cadvisor/info/v1"
23	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24	"k8s.io/apimachinery/pkg/types"
25	internalapi "k8s.io/cri-api/pkg/apis"
26	statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
27	"k8s.io/kubernetes/pkg/kubelet/cadvisor"
28	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
29	kubepod "k8s.io/kubernetes/pkg/kubelet/pod"
30	"k8s.io/kubernetes/pkg/kubelet/server/stats"
31	"k8s.io/kubernetes/pkg/kubelet/stats/pidlimit"
32	"k8s.io/kubernetes/pkg/kubelet/status"
33)
34
35// NewCRIStatsProvider returns a Provider that provides the node stats
36// from cAdvisor and the container stats from CRI.
37func NewCRIStatsProvider(
38	cadvisor cadvisor.Interface,
39	resourceAnalyzer stats.ResourceAnalyzer,
40	podManager kubepod.Manager,
41	runtimeCache kubecontainer.RuntimeCache,
42	runtimeService internalapi.RuntimeService,
43	imageService internalapi.ImageManagerService,
44	hostStatsProvider HostStatsProvider,
45	disableAcceleratorUsageMetrics bool,
46) *Provider {
47	return newStatsProvider(cadvisor, podManager, runtimeCache, newCRIStatsProvider(cadvisor, resourceAnalyzer,
48		runtimeService, imageService, hostStatsProvider, disableAcceleratorUsageMetrics))
49}
50
51// NewCadvisorStatsProvider returns a containerStatsProvider that provides both
52// the node and the container stats from cAdvisor.
53func NewCadvisorStatsProvider(
54	cadvisor cadvisor.Interface,
55	resourceAnalyzer stats.ResourceAnalyzer,
56	podManager kubepod.Manager,
57	runtimeCache kubecontainer.RuntimeCache,
58	imageService kubecontainer.ImageService,
59	statusProvider status.PodStatusProvider,
60	hostStatsProvider HostStatsProvider,
61) *Provider {
62	return newStatsProvider(cadvisor, podManager, runtimeCache, newCadvisorStatsProvider(cadvisor, resourceAnalyzer, imageService, statusProvider, hostStatsProvider))
63}
64
65// newStatsProvider returns a new Provider that provides node stats from
66// cAdvisor and the container stats using the containerStatsProvider.
67func newStatsProvider(
68	cadvisor cadvisor.Interface,
69	podManager kubepod.Manager,
70	runtimeCache kubecontainer.RuntimeCache,
71	containerStatsProvider containerStatsProvider,
72) *Provider {
73	return &Provider{
74		cadvisor:               cadvisor,
75		podManager:             podManager,
76		runtimeCache:           runtimeCache,
77		containerStatsProvider: containerStatsProvider,
78	}
79}
80
81// Provider provides the stats of the node and the pod-managed containers.
82type Provider struct {
83	cadvisor     cadvisor.Interface
84	podManager   kubepod.Manager
85	runtimeCache kubecontainer.RuntimeCache
86	containerStatsProvider
87	rlimitStatsProvider
88}
89
90// containerStatsProvider is an interface that provides the stats of the
91// containers managed by pods.
92type containerStatsProvider interface {
93	ListPodStats() ([]statsapi.PodStats, error)
94	ListPodStatsAndUpdateCPUNanoCoreUsage() ([]statsapi.PodStats, error)
95	ListPodCPUAndMemoryStats() ([]statsapi.PodStats, error)
96	ImageFsStats() (*statsapi.FsStats, error)
97	ImageFsDevice() (string, error)
98}
99
100type rlimitStatsProvider interface {
101	RlimitStats() (*statsapi.RlimitStats, error)
102}
103
104// RlimitStats returns base information about process count
105func (p *Provider) RlimitStats() (*statsapi.RlimitStats, error) {
106	return pidlimit.Stats()
107}
108
109// GetCgroupStats returns the stats of the cgroup with the cgroupName. Note that
110// this function doesn't generate filesystem stats.
111func (p *Provider) GetCgroupStats(cgroupName string, updateStats bool) (*statsapi.ContainerStats, *statsapi.NetworkStats, error) {
112	info, err := getCgroupInfo(p.cadvisor, cgroupName, updateStats)
113	if err != nil {
114		return nil, nil, fmt.Errorf("failed to get cgroup stats for %q: %v", cgroupName, err)
115	}
116	// Rootfs and imagefs doesn't make sense for raw cgroup.
117	s := cadvisorInfoToContainerStats(cgroupName, info, nil, nil)
118	n := cadvisorInfoToNetworkStats(info)
119	return s, n, nil
120}
121
122// GetCgroupCPUAndMemoryStats returns the CPU and memory stats of the cgroup with the cgroupName. Note that
123// this function doesn't generate filesystem stats.
124func (p *Provider) GetCgroupCPUAndMemoryStats(cgroupName string, updateStats bool) (*statsapi.ContainerStats, error) {
125	info, err := getCgroupInfo(p.cadvisor, cgroupName, updateStats)
126	if err != nil {
127		return nil, fmt.Errorf("failed to get cgroup stats for %q: %v", cgroupName, err)
128	}
129	// Rootfs and imagefs doesn't make sense for raw cgroup.
130	s := cadvisorInfoToContainerCPUAndMemoryStats(cgroupName, info)
131	return s, nil
132}
133
134// RootFsStats returns the stats of the node root filesystem.
135func (p *Provider) RootFsStats() (*statsapi.FsStats, error) {
136	rootFsInfo, err := p.cadvisor.RootFsInfo()
137	if err != nil {
138		return nil, fmt.Errorf("failed to get rootFs info: %v", err)
139	}
140
141	var nodeFsInodesUsed *uint64
142	if rootFsInfo.Inodes != nil && rootFsInfo.InodesFree != nil {
143		nodeFsIU := *rootFsInfo.Inodes - *rootFsInfo.InodesFree
144		nodeFsInodesUsed = &nodeFsIU
145	}
146
147	// Get the root container stats's timestamp, which will be used as the
148	// imageFs stats timestamp.  Don't force a stats update, as we only want the timestamp.
149	rootStats, err := getCgroupStats(p.cadvisor, "/", false)
150	if err != nil {
151		return nil, fmt.Errorf("failed to get root container stats: %v", err)
152	}
153
154	return &statsapi.FsStats{
155		Time:           metav1.NewTime(rootStats.Timestamp),
156		AvailableBytes: &rootFsInfo.Available,
157		CapacityBytes:  &rootFsInfo.Capacity,
158		UsedBytes:      &rootFsInfo.Usage,
159		InodesFree:     rootFsInfo.InodesFree,
160		Inodes:         rootFsInfo.Inodes,
161		InodesUsed:     nodeFsInodesUsed,
162	}, nil
163}
164
165// GetContainerInfo returns stats (from cAdvisor) for a container.
166func (p *Provider) GetContainerInfo(podFullName string, podUID types.UID, containerName string, req *cadvisorapiv1.ContainerInfoRequest) (*cadvisorapiv1.ContainerInfo, error) {
167	// Resolve and type convert back again.
168	// We need the static pod UID but the kubecontainer API works with types.UID.
169	podUID = types.UID(p.podManager.TranslatePodUID(podUID))
170
171	pods, err := p.runtimeCache.GetPods()
172	if err != nil {
173		return nil, err
174	}
175	pod := kubecontainer.Pods(pods).FindPod(podFullName, podUID)
176	container := pod.FindContainerByName(containerName)
177	if container == nil {
178		return nil, kubecontainer.ErrContainerNotFound
179	}
180
181	ci, err := p.cadvisor.DockerContainer(container.ID.ID, req)
182	if err != nil {
183		return nil, err
184	}
185	return &ci, nil
186}
187
188// GetRawContainerInfo returns the stats (from cadvisor) for a non-Kubernetes
189// container.
190func (p *Provider) GetRawContainerInfo(containerName string, req *cadvisorapiv1.ContainerInfoRequest, subcontainers bool) (map[string]*cadvisorapiv1.ContainerInfo, error) {
191	if subcontainers {
192		return p.cadvisor.SubcontainerInfo(containerName, req)
193	}
194	containerInfo, err := p.cadvisor.ContainerInfo(containerName, req)
195	if err != nil {
196		return nil, err
197	}
198	return map[string]*cadvisorapiv1.ContainerInfo{
199		containerInfo.Name: containerInfo,
200	}, nil
201}
202
203// HasDedicatedImageFs returns true if a dedicated image filesystem exists for storing images.
204func (p *Provider) HasDedicatedImageFs() (bool, error) {
205	device, err := p.containerStatsProvider.ImageFsDevice()
206	if err != nil {
207		return false, err
208	}
209	rootFsInfo, err := p.cadvisor.RootFsInfo()
210	if err != nil {
211		return false, err
212	}
213	return device != rootFsInfo.Device, nil
214}
215