1
2
3
4
19
20 package stats
21
22 import (
23 "fmt"
24 "time"
25
26 "github.com/Microsoft/hcsshim"
27 cadvisorapiv2 "github.com/google/cadvisor/info/v2"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/types"
30 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
31 "k8s.io/klog/v2"
32 statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
33 )
34
35
36 type windowsNetworkStatsProvider interface {
37 HNSListEndpointRequest() ([]hcsshim.HNSEndpoint, error)
38 GetHNSEndpointStats(endpointName string) (*hcsshim.HNSEndpointStats, error)
39 }
40
41
42 type networkStats struct{}
43
44 func (s networkStats) HNSListEndpointRequest() ([]hcsshim.HNSEndpoint, error) {
45 return hcsshim.HNSListEndpointRequest()
46 }
47
48 func (s networkStats) GetHNSEndpointStats(endpointName string) (*hcsshim.HNSEndpointStats, error) {
49 return hcsshim.GetHNSEndpointStats(endpointName)
50 }
51
52
53 func (p *criStatsProvider) listContainerNetworkStats() (map[string]*statsapi.NetworkStats, error) {
54 networkStatsProvider := newNetworkStatsProvider(p)
55
56 endpoints, err := networkStatsProvider.HNSListEndpointRequest()
57 if err != nil {
58 klog.ErrorS(err, "Failed to fetch current HNS endpoints")
59 return nil, err
60 }
61
62 networkStats := make(map[string]*statsapi.NetworkStats)
63 for _, endpoint := range endpoints {
64 endpointStats, err := networkStatsProvider.GetHNSEndpointStats(endpoint.Id)
65 if err != nil {
66 klog.V(2).InfoS("Failed to fetch statistics for endpoint, continue to get stats for other endpoints", "endpointId", endpoint.Id, "containers", endpoint.SharedContainers)
67 continue
68 }
69
70
71 for _, cId := range endpoint.SharedContainers {
72 networkStat, found := networkStats[cId]
73 if found && networkStat.Name != endpoint.Name {
74 iStat := hcsStatToInterfaceStat(endpointStats, endpoint.Name)
75 networkStat.Interfaces = append(networkStat.Interfaces, iStat)
76 continue
77 }
78 networkStats[cId] = hcsStatsToNetworkStats(p.clock.Now(), endpointStats, endpoint.Name)
79 }
80 }
81
82 return networkStats, nil
83 }
84
85 func (p *criStatsProvider) addCRIPodContainerStats(criSandboxStat *runtimeapi.PodSandboxStats,
86 ps *statsapi.PodStats, fsIDtoInfo map[runtimeapi.FilesystemIdentifier]*cadvisorapiv2.FsInfo,
87 containerMap map[string]*runtimeapi.Container,
88 podSandbox *runtimeapi.PodSandbox,
89 rootFsInfo *cadvisorapiv2.FsInfo,
90 updateCPUNanoCoreUsage bool) error {
91 for _, criContainerStat := range criSandboxStat.GetWindows().GetContainers() {
92 container, found := containerMap[criContainerStat.Attributes.Id]
93 if !found {
94 continue
95 }
96
97 cs, err := p.makeWinContainerStats(criContainerStat, container, rootFsInfo, fsIDtoInfo, podSandbox.GetMetadata())
98 if err != nil {
99 return fmt.Errorf("make container stats: %w", err)
100
101 }
102 ps.Containers = append(ps.Containers, *cs)
103 }
104
105 return nil
106 }
107
108 func (p *criStatsProvider) makeWinContainerStats(
109 stats *runtimeapi.WindowsContainerStats,
110 container *runtimeapi.Container,
111 rootFsInfo *cadvisorapiv2.FsInfo,
112 fsIDtoInfo map[runtimeapi.FilesystemIdentifier]*cadvisorapiv2.FsInfo,
113 meta *runtimeapi.PodSandboxMetadata) (*statsapi.ContainerStats, error) {
114 result := &statsapi.ContainerStats{
115 Name: stats.Attributes.Metadata.Name,
116
117 StartTime: metav1.NewTime(time.Unix(0, container.CreatedAt)),
118 CPU: &statsapi.CPUStats{},
119 Memory: &statsapi.MemoryStats{},
120 Rootfs: &statsapi.FsStats{},
121
122 }
123 if stats.Cpu != nil {
124 result.CPU.Time = metav1.NewTime(time.Unix(0, stats.Cpu.Timestamp))
125 if stats.Cpu.UsageCoreNanoSeconds != nil {
126 result.CPU.UsageCoreNanoSeconds = &stats.Cpu.UsageCoreNanoSeconds.Value
127 }
128 if stats.Cpu.UsageNanoCores != nil {
129 result.CPU.UsageNanoCores = &stats.Cpu.UsageNanoCores.Value
130 }
131 } else {
132 result.CPU.Time = metav1.NewTime(time.Unix(0, time.Now().UnixNano()))
133 result.CPU.UsageCoreNanoSeconds = uint64Ptr(0)
134 result.CPU.UsageNanoCores = uint64Ptr(0)
135 }
136 if stats.Memory != nil {
137 result.Memory.Time = metav1.NewTime(time.Unix(0, stats.Memory.Timestamp))
138 if stats.Memory.WorkingSetBytes != nil {
139 result.Memory.WorkingSetBytes = &stats.Memory.WorkingSetBytes.Value
140 }
141 if stats.Memory.AvailableBytes != nil {
142 result.Memory.AvailableBytes = &stats.Memory.AvailableBytes.Value
143 }
144 if stats.Memory.PageFaults != nil {
145 result.Memory.PageFaults = &stats.Memory.PageFaults.Value
146 }
147 } else {
148 result.Memory.Time = metav1.NewTime(time.Unix(0, time.Now().UnixNano()))
149 result.Memory.WorkingSetBytes = uint64Ptr(0)
150 result.Memory.AvailableBytes = uint64Ptr(0)
151 result.Memory.PageFaults = uint64Ptr(0)
152 }
153 if stats.WritableLayer != nil {
154 result.Rootfs.Time = metav1.NewTime(time.Unix(0, stats.WritableLayer.Timestamp))
155 if stats.WritableLayer.UsedBytes != nil {
156 result.Rootfs.UsedBytes = &stats.WritableLayer.UsedBytes.Value
157 }
158 }
159 var err error
160 fsID := stats.GetWritableLayer().GetFsId()
161 if fsID != nil {
162 imageFsInfo, found := fsIDtoInfo[*fsID]
163 if !found {
164 imageFsInfo, err = p.getFsInfo(fsID)
165 if err != nil {
166 return nil, fmt.Errorf("get filesystem info: %w", err)
167 }
168 fsIDtoInfo[*fsID] = imageFsInfo
169 }
170 if imageFsInfo != nil {
171
172
173
174
175 result.Rootfs.AvailableBytes = &imageFsInfo.Available
176 result.Rootfs.CapacityBytes = &imageFsInfo.Capacity
177 }
178 }
179
180
181
182 result.Logs, err = p.hostStatsProvider.getPodContainerLogStats(meta.GetNamespace(), meta.GetName(), types.UID(meta.GetUid()), container.GetMetadata().GetName(), rootFsInfo)
183 if err != nil {
184 klog.ErrorS(err, "Unable to fetch container log stats", "containerName", container.GetMetadata().GetName())
185 }
186 return result, nil
187 }
188
189
190 func hcsStatsToNetworkStats(timestamp time.Time, hcsStats *hcsshim.HNSEndpointStats, endpointName string) *statsapi.NetworkStats {
191 result := &statsapi.NetworkStats{
192 Time: metav1.NewTime(timestamp),
193 Interfaces: make([]statsapi.InterfaceStats, 0),
194 }
195
196 iStat := hcsStatToInterfaceStat(hcsStats, endpointName)
197
198
199 result.Interfaces = append(result.Interfaces, iStat)
200 result.InterfaceStats = iStat
201
202 return result
203 }
204
205 func hcsStatToInterfaceStat(hcsStats *hcsshim.HNSEndpointStats, endpointName string) statsapi.InterfaceStats {
206 iStat := statsapi.InterfaceStats{
207 Name: endpointName,
208 RxBytes: &hcsStats.BytesReceived,
209 TxBytes: &hcsStats.BytesSent,
210 }
211 return iStat
212 }
213
214 func addCRIPodCPUStats(ps *statsapi.PodStats, criPodStat *runtimeapi.PodSandboxStats) {
215 if criPodStat == nil || criPodStat.Windows == nil || criPodStat.Windows.Cpu == nil {
216 return
217 }
218 criCPU := criPodStat.Windows.Cpu
219 ps.CPU = &statsapi.CPUStats{
220 Time: metav1.NewTime(time.Unix(0, criCPU.Timestamp)),
221 UsageNanoCores: valueOfUInt64Value(criCPU.UsageNanoCores),
222 UsageCoreNanoSeconds: valueOfUInt64Value(criCPU.UsageCoreNanoSeconds),
223 }
224 }
225
226 func addCRIPodMemoryStats(ps *statsapi.PodStats, criPodStat *runtimeapi.PodSandboxStats) {
227 if criPodStat == nil || criPodStat.Windows == nil || criPodStat.Windows.Memory == nil {
228 return
229 }
230 criMemory := criPodStat.Windows.Memory
231 ps.Memory = &statsapi.MemoryStats{
232 Time: metav1.NewTime(time.Unix(0, criMemory.Timestamp)),
233 AvailableBytes: valueOfUInt64Value(criMemory.AvailableBytes),
234 WorkingSetBytes: valueOfUInt64Value(criMemory.WorkingSetBytes),
235 PageFaults: valueOfUInt64Value(criMemory.PageFaults),
236 }
237 }
238
239 func addCRIPodProcessStats(ps *statsapi.PodStats, criPodStat *runtimeapi.PodSandboxStats) {
240 if criPodStat == nil || criPodStat.Windows == nil || criPodStat.Windows.Process == nil {
241 return
242 }
243 ps.ProcessStats = &statsapi.ProcessStats{
244 ProcessCount: valueOfUInt64Value(criPodStat.Windows.Process.ProcessCount),
245 }
246 }
247
248 func addCRIPodNetworkStats(ps *statsapi.PodStats, criPodStat *runtimeapi.PodSandboxStats) {
249 if criPodStat == nil || criPodStat.Windows == nil || criPodStat.Windows.Network == nil {
250 return
251 }
252 criNetwork := criPodStat.Windows.Network
253 iStats := statsapi.NetworkStats{
254 Time: metav1.NewTime(time.Unix(0, criNetwork.Timestamp)),
255 InterfaceStats: criInterfaceToWinSummary(criNetwork.DefaultInterface),
256 Interfaces: make([]statsapi.InterfaceStats, 0, len(criNetwork.Interfaces)),
257 }
258 for _, iface := range criNetwork.Interfaces {
259 iStats.Interfaces = append(iStats.Interfaces, criInterfaceToWinSummary(iface))
260 }
261 ps.Network = &iStats
262 }
263
264 func criInterfaceToWinSummary(criIface *runtimeapi.WindowsNetworkInterfaceUsage) statsapi.InterfaceStats {
265 return statsapi.InterfaceStats{
266 Name: criIface.Name,
267 RxBytes: valueOfUInt64Value(criIface.RxBytes),
268 TxBytes: valueOfUInt64Value(criIface.TxBytes),
269 }
270 }
271
272
273
274 func newNetworkStatsProvider(p *criStatsProvider) windowsNetworkStatsProvider {
275 var statsProvider windowsNetworkStatsProvider
276 if p.windowsNetworkStatsProvider == nil {
277 statsProvider = networkStats{}
278 } else {
279 statsProvider = p.windowsNetworkStatsProvider.(windowsNetworkStatsProvider)
280 }
281 return statsProvider
282 }
283
View as plain text