4
4
# Copyright 2015-Present Datadog, Inc
5
5
6
6
import errno
7
+ import os
7
8
import re
8
9
9
10
@@ -13,31 +14,54 @@ class UnresolvableContainerID(Exception):
13
14
"""
14
15
15
16
16
- class ContainerID (object ):
17
+ class Cgroup (object ):
17
18
"""
18
- A reader class that retrieves the current container ID parsed from a the cgroup file.
19
+ A reader class that retrieves either:
20
+ - The current container ID parsed from the cgroup file
21
+ - The cgroup controller inode.
19
22
20
23
Returns:
21
- object: ContainerID
24
+ object: Cgroup
22
25
23
26
Raises:
24
27
`NotImplementedError`: No proc filesystem is found (non-Linux systems)
25
28
`UnresolvableContainerID`: Unable to read the container ID
26
29
"""
27
30
28
31
CGROUP_PATH = "/proc/self/cgroup"
32
+ CGROUP_MOUNT_PATH = "/sys/fs/cgroup" # cgroup mount path.
33
+ CGROUP_NS_PATH = "/proc/self/ns/cgroup" # path to the cgroup namespace file.
34
+ CGROUPV1_BASE_CONTROLLER = "memory" # controller used to identify the container-id in cgroup v1 (memory).
35
+ CGROUPV2_BASE_CONTROLLER = "" # controller used to identify the container-id in cgroup v2.
36
+ HOST_CGROUP_NAMESPACE_INODE = 0xEFFFFFFB # inode of the host cgroup namespace.
37
+
29
38
UUID_SOURCE = r"[0-9a-f]{8}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{12}"
30
39
CONTAINER_SOURCE = r"[0-9a-f]{64}"
31
40
TASK_SOURCE = r"[0-9a-f]{32}-\d+"
32
41
LINE_RE = re .compile (r"^(\d+):([^:]*):(.+)$" )
33
42
CONTAINER_RE = re .compile (r"(?:.+)?({0}|{1}|{2})(?:\.scope)?$" .format (UUID_SOURCE , CONTAINER_SOURCE , TASK_SOURCE ))
34
43
35
44
def __init__ (self ):
36
- self .container_id = self ._read_container_id (self .CGROUP_PATH )
45
+ if self ._is_host_cgroup_namespace ():
46
+ self .container_id = self ._read_cgroup_path ()
47
+ return
48
+ self .container_id = self ._get_cgroup_from_inode ()
49
+
50
+ def _is_host_cgroup_namespace (self ):
51
+ """Check if the current process is in a host cgroup namespace."""
52
+ try :
53
+ return (
54
+ os .stat (self .CGROUP_NS_PATH ).st_ino == self .HOST_CGROUP_NAMESPACE_INODE
55
+ if os .path .exists (self .CGROUP_NS_PATH )
56
+ else False
57
+ )
58
+ except Exception :
59
+ return False
37
60
38
- def _read_container_id (self , fpath ):
61
+ def _read_cgroup_path (self ):
62
+ """Read the container ID from the cgroup file."""
39
63
try :
40
- with open (fpath , mode = "r" ) as fp :
64
+ with open (self . CGROUP_PATH , mode = "r" ) as fp :
41
65
for line in fp :
42
66
line = line .strip ()
43
67
match = self .LINE_RE .match (line )
@@ -55,3 +79,33 @@ def _read_container_id(self, fpath):
55
79
except Exception as e :
56
80
raise UnresolvableContainerID ("Unable to read the container ID: " + str (e ))
57
81
return None
82
+
83
+ def _get_cgroup_from_inode (self ):
84
+ """Read the container ID from the cgroup inode."""
85
+ # Parse /proc/self/cgroup and get a map of controller to its associated cgroup node path.
86
+ cgroup_controllers_paths = {}
87
+ with open (self .CGROUP_PATH , mode = "r" ) as fp :
88
+ for line in fp :
89
+ tokens = line .strip ().split (":" )
90
+ if len (tokens ) != 3 :
91
+ continue
92
+ if tokens [1 ] == self .CGROUPV1_BASE_CONTROLLER or tokens [1 ] == self .CGROUPV2_BASE_CONTROLLER :
93
+ cgroup_controllers_paths [tokens [1 ]] = tokens [2 ]
94
+
95
+ # Retrieve the cgroup inode from "/sys/fs/cgroup + controller + cgroupNodePath"
96
+ for controller in [
97
+ self .CGROUPV1_BASE_CONTROLLER ,
98
+ self .CGROUPV2_BASE_CONTROLLER ,
99
+ ]:
100
+ if controller in cgroup_controllers_paths :
101
+ inode_path = os .path .join (
102
+ self .CGROUP_MOUNT_PATH ,
103
+ controller ,
104
+ cgroup_controllers_paths [controller ] if cgroup_controllers_paths [controller ] != "/" else "" ,
105
+ )
106
+ inode = os .stat (inode_path ).st_ino
107
+ # 0 is not a valid inode. 1 is a bad block inode and 2 is the root of a filesystem.
108
+ if inode > 2 :
109
+ return "in-{0}" .format (inode )
110
+
111
+ return None
0 commit comments