]>
git.ipfire.org Git - pakfire.git/blob - python/pakfire/cgroup.py
9 log
= logging
.getLogger("pakfire.cgroups")
11 CGROUP_MOUNTPOINT
= "/sys/fs/cgroup/systemd"
14 def __init__(self
, name
):
15 assert supported(), "cgroups are not supported by this kernel"
18 self
.path
= os
.path
.join(CGROUP_MOUNTPOINT
, name
)
19 self
.path
= os
.path
.abspath(self
.path
)
24 # Initialize the cgroup.
27 log
.debug("cgroup '%s' has been successfully initialized." % self
.name
)
30 return "<%s %s>" % (self
.__class
__.__name
__, self
.name
)
32 def __cmp__(self
, other
):
33 return cmp(self
.path
, other
.path
)
36 def find_by_pid(cls
, pid
):
38 Returns the cgroup of the process with the given PID.
40 If no cgroup can be found, None is returned.
45 for d
, subdirs
, files
in os
.walk(CGROUP_MOUNTPOINT
):
46 if not "tasks" in files
:
50 if pid
in cgroup
.tasks
:
56 Returns true, if this hosts supports cgroups.
58 return os
.path
.ismount(CGROUP_MOUNTPOINT
)
62 Creates the filesystem structure for
65 if os
.path
.exists(self
.path
):
68 log
.debug("cgroup '%s' has been created." % self
.name
)
69 os
.makedirs(self
.path
)
71 def create_child_cgroup(self
, name
):
73 Create a child cgroup with name relative to the
76 return self
.__class
__(os
.path
.join(self
.name
, name
))
80 Attaches this task to the cgroup.
89 All running tasks will be migrated to the parent cgroup.
91 # Don't delete the root cgroup.
95 # Move all tasks to the parent.
96 self
.migrate(self
.parent
)
98 # Just make sure the statement above worked.
99 assert self
.is_empty(recursive
=True), "cgroup must be empty to be destroyed"
100 assert not self
.processes
102 # Remove the file tree.
106 # Ignore "Device or resource busy".
112 def _read(self
, file):
114 Reads the contect of file in the cgroup directory.
116 file = os
.path
.join(self
.path
, file)
118 with
open(file) as f
:
121 def _read_pids(self
, file):
123 Reads file and interprets the lines as a sorted list.
125 _pids
= self
._read
(file)
129 for pid
in _pids
.splitlines():
142 def _write(self
, file, what
):
144 Writes what to file in the cgroup directory.
146 file = os
.path
.join(self
.path
, file)
155 return self
.parent
.root
161 # Cannot go above CGROUP_MOUNTPOINT.
162 if self
.path
== CGROUP_MOUNTPOINT
:
165 if self
._parent
is None:
166 parent_name
= os
.path
.dirname(self
.name
)
167 self
._parent
= CGroup(parent_name
)
175 for name
in os
.listdir(self
.path
):
176 path
= os
.path
.join(self
.path
, name
)
177 if not os
.path
.isdir(path
):
180 name
= os
.path
.join(self
.name
, name
)
183 subgroups
.append(group
)
187 def is_empty(self
, recursive
=False):
189 Returns True if the cgroup is empty.
191 Otherwise returns False.
197 for subgroup
in self
.subgroups
:
198 if subgroup
.is_empty(recursive
=recursive
):
208 Returns a list of pids of all tasks
209 in this process group.
211 return self
._read
_pids
("tasks")
216 Returns a list of pids of all processes
217 that are currently running within the cgroup.
219 return self
._read
_pids
("cgroup.procs")
221 def attach_task(self
, pid
):
223 Attaches the task with the given PID to
226 self
._write
("tasks", pid
)
228 def migrate_task(self
, other
, pid
):
230 Migrates a single task to another cgroup.
232 other
.attach_task(pid
)
234 def migrate(self
, other
):
235 if self
.is_empty(recursive
=True):
238 log
.info("Migrating all tasks from '%s' to '%s'." \
239 % (self
.name
, other
.name
))
242 # Migrate all tasks to the new cgroup.
243 for task
in self
.tasks
:
244 self
.migrate_task(other
, task
)
246 # Also do that for all subgroups.
247 for subgroup
in self
.subgroups
:
248 subgroup
.migrate(other
)
253 def kill(self
, sig
=signal
.SIGTERM
, recursive
=True):
254 killed_processes
= []
259 for proc
in self
.processes
:
264 # Skip all processes that have already been killed.
265 if proc
in killed_processes
:
268 # If we haven't killed the process yet, we kill it.
269 log
.debug("Sending signal %s to process %s..." % (sig
, proc
))
274 # Skip "No such process" error
280 # Save all killed processes to a list.
281 killed_processes
.append(proc
)
284 # If no processes are left to be killed, we end the loop.
287 # Nothing more to do if not in recursive mode.
291 # Kill all processes in subgroups as well.
292 for subgroup
in self
.subgroups
:
293 subgroup
.kill(sig
=sig
, recursive
=recursive
)
295 def kill_and_wait(self
):
296 # Safely kill all processes in the cgroup.
297 # This first sends SIGTERM and then checks 8 times
298 # after 200ms whether the group is empty. If not,
299 # everything what's still in there gets SIGKILL
300 # and it is five more times checked if everything
312 # If no signal is given and there are no processes
313 # left, our job is done and we can exit.
314 if not self
.processes
:
318 # Send sig to all processes in the cgroup.
319 log
.debug("Sending signal %s to all processes in '%s'." % (sig
, self
.name
))
320 self
.kill(sig
=sig
, recursive
=True)
325 return self
.is_empty()
328 # Alias for simple access to check if this host supports cgroups.
329 supported
= CGroup
.supported
331 # Alias for simple access to find the cgroup of a certain process.
332 find_by_pid
= CGroup
.find_by_pid