def unpack(self, container_dir, root_dir, app, app_cgroups, data): root_dir = fs.norm_safe(root_dir) emptydirs, stickydirs, mounts = configure() for plugin in image_fs.plugins(app): _LOGGER.info('Processing plugin: %r', plugin) extra = plugin(self.tm_env).configure(container_dir, app) if extra is not None: emptydirs.update(extra[0]) stickydirs.update(extra[1]) mounts.update(extra[2]) make_fsroot(root_dir, emptydirs, stickydirs, mounts) make_osroot(root_dir, app, data) # FIXME: Lots of things are still reading this file. # Copy updated state manifest as app.json in the # container_dir so it is visible in chrooted env. shutil.copy(os.path.join(container_dir, runtime.STATE_JSON), os.path.join(root_dir, appcfg.APP_JSON)) cgrp = os.path.join(app_cgroups['memory'], 'services') create_environ_dir(container_dir, root_dir, app) create_supervision_tree( self.tm_env, container_dir, root_dir, app, cgroups_path=cgroups.makepath( 'freezer', cgrp ), ) create_overlay(self.tm_env, container_dir, root_dir, app)
def kill_apps_in_cgroup(subsystem, cgrp, delete_cgrp=False): """Kill all apps found in a cgroup""" path = cgroups.makepath(subsystem, cgrp, 'tasks') tasks_files = glob.glob(path) for tasks_file in tasks_files: cgrp_path = os.path.dirname(tasks_file) try: with io.open(tasks_file) as tasks: for pid in tasks: _LOGGER.info('killing process from %r: %s', tasks_file, pid) try: os.kill(int(pid), signal.SIGKILL) except OSError as err: # it is OK to fail to find the PID if err.errno == errno.ESRCH: continue _LOGGER.exception('Unable to kill processes in %r: %s', cgrp_path, err) except IOError as err: # it is OK to fail if the tasks file is already gone if err.errno == errno.ENOENT: _LOGGER.debug('Skipping nonexistent cgroup %r', cgrp_path) continue raise if delete_cgrp: cgrp = cgroups.extractpath(cgrp_path, subsystem) delete(subsystem, cgrp)
def pids_in_cgroup(subsystem, cgrp): """Returns the list of pids in the cgroup.""" path = cgroups.makepath(subsystem, cgrp, 'tasks') with io.open(path) as tasks: return [ int(line.strip()) for line in tasks.readlines() if line ]
def is_oom(): """Checks memory failcount and return oom state of the container.""" # NOTE(boysson): This code runs in the container's namespace so the memory # cgroup is "masked". mem_failcnt = cgroups.makepath('memory', '/', 'memory.failcnt') memsw_failcnt = cgroups.makepath('memory', '/', 'memory.memsw.failcnt') try: with open(mem_failcnt) as f: mem_failcnt = int(f.read().strip()) with open(memsw_failcnt) as f: memsw_failcnt = int(f.read().strip()) return mem_failcnt != 0 or memsw_failcnt != 0 except: # pylint: disable=W0702 _LOGGER.info('Cannot access memory failcnt.', exc_info=True) return False
def apps(): """Returns list of apps in apps cgroup.""" basepath = cgroups.makepath('cpu', 'treadmill/apps') files = os.listdir(basepath) return [ appname for appname in files if os.path.isdir(os.path.join(basepath, appname)) ]
def apps(cgroup_prefix): """Returns list of apps in apps cgroup.""" apps_group = apps_group_name(cgroup_prefix) basepath = cgroups.makepath('cpu', apps_group) files = os.listdir(basepath) return [ appname for appname in files if os.path.isdir(os.path.join(basepath, appname)) ]
def app_cgrp_count(): """Get the number of apps in treadmill/apps""" appcount = 0 basepath = cgroups.makepath('memory', 'treadmill/apps') files = os.listdir(basepath) for appname in files: fullpath = os.path.join(basepath, appname) if os.path.isdir(fullpath): appcount += 1 return appcount
def share_cgroup_info(app, root_dir): """Shares subset of cgroup tree with the container.""" # Bind /cgroup/memory inside chrooted environment to /cgroup/.../memory # of the container. unique_name = appcfg.app_unique_name(app) cgrp = os.path.join('treadmill', 'apps', unique_name) # FIXME: This should be removed and proper cgroups should be # exposed (readonly). This is so that tools that # (correctly) read /proc/self/cgroups can access cgroup # data. shared_subsystems = ['memory'] for subsystem in shared_subsystems: fs.mkdir_safe(os.path.join(root_dir, 'cgroup', subsystem)) fs.mount_bind(root_dir, os.path.join('/cgroup', subsystem), cgroups.makepath(subsystem, cgrp))
def delete(system, group): """ safely delete cgroup path """ fullpath = cgroups.makepath(system, group) for (dirname, _subdirs, _files) in os.walk(fullpath, topdown=False): cgrp = cgroups.extractpath(dirname, system) # free empty memory before the cgroups is destroyed if system == 'memory': memory_force_empty(cgrp) try: cgroups.delete(system, cgrp) except OSError as err: _LOGGER.exception('Unable remove cgroup %s %s, %r', system, cgrp, err) raise err
def share_cgroup_info(root_dir, cgrp): """Shares subset of cgroup tree with the container.""" # Bind /cgroup/memory inside chrooted environment to /cgroup/.../memory # of the container. # FIXME: This should be removed and proper cgroups should be # exposed (readonly). This is so that tools that # (correctly) read /proc/self/cgroups can access cgroup # data. shared_subsystems = ['memory'] for subsystem in shared_subsystems: fs.mkdir_safe(os.path.join(root_dir, 'cgroup', subsystem)) fs_linux.mount_bind(root_dir, os.path.join(os.sep, 'cgroup', subsystem), source=cgroups.makepath(subsystem, cgrp), recursive=True, read_only=False)
def reset_memory_limit_in_bytes(cgroup_prefix): """Recalculate the hard memory limits. If any app uses more than the value we are trying to resize to, it will be expunged. :returns: List of unique application names to expunge from the system. """ total_soft_mem = float(total_soft_memory_limits(cgroup_prefix)) apps_group = apps_group_name(cgroup_prefix) total_hard_mem = cgroups.get_value('memory', apps_group, 'memory.limit_in_bytes') basepath = cgroups.makepath('memory', apps_group) _LOGGER.info('total_soft_mem: %r, total_hard_mem: %r', total_soft_mem, total_hard_mem) expunged = [] for f in os.listdir(basepath): if not os.path.isdir(os.path.join(basepath, f)): continue cgrp = os.path.join(apps_group, f) soft_limit = float( cgroups.get_value('memory', cgrp, 'memory.soft_limit_in_bytes')) percentage_of_allocated = soft_limit / total_soft_mem hard_limit = int(percentage_of_allocated * total_hard_mem) _LOGGER.info('%s: soft_limit %r, pcnt: %r, hard_limit: %r', cgrp, soft_limit, percentage_of_allocated, hard_limit) if hard_limit < soft_limit: hard_limit = int(soft_limit) _LOGGER.debug('Setting cgroup %r hardlimit to %r', cgrp, hard_limit) try: set_memory_hardlimit(cgrp, hard_limit) except TreadmillCgroupError: # Unable to resize group, add it to the expunged groups. expunged.append(f) return expunged
def total_soft_memory_limits(): """Add up soft memory limits.""" total_mem = 0 path = cgroups.makepath('memory', 'treadmill/apps/*', 'memory.soft_limit_in_bytes') mem_files = glob.glob(path) for mem_file in mem_files: try: with io.open(mem_file) as mem: total_mem += int(mem.read().strip()) except IOError as err: # it is ok to fail if the memfile is already gone if err.errno == errno.ENOENT: continue _LOGGER.exception('Unable to read soft-limit %r: %s', mem_file, err) raise return total_mem
def unpack(self, container_dir, root_dir, app): make_fsroot(root_dir) image_fs.configure_plugins(self.tm_env, container_dir, app) # FIXME: Lots of things are still reading this file. # Copy updated state manifest as app.json in the # container_dir so it is visible in chrooted env. shutil.copy(os.path.join(container_dir, runtime.STATE_JSON), os.path.join(root_dir, appcfg.APP_JSON)) cgrp = get_cgroup_path(app) create_environ_dir(container_dir, root_dir, app) create_supervision_tree(self.tm_env, container_dir, root_dir, app, cgroups_path=cgroups.makepath('memory', cgrp)) create_overlay(self.tm_env, container_dir, root_dir, app)
def get_memory_oom_eventfd(cgrp): """Create, register and return a eventfd for a cgroup OOM notification. Args: cgrp ``str``: path to a cgroup root directory. Returns: ``int``: eventfd(2) filedescriptor. """ # 1/ create an eventfd efd = eventfd.eventfd(0, eventfd.EFD_CLOEXEC) # 2/ open memory.oom_control oom_control_file = cgroups.makepath('memory', cgrp, 'memory.oom_control') with io.open(oom_control_file) as oom_control: # 3/ write '<eventfd_fd> <oom_control_fd> to cgroup.event_control cgroups.set_value( 'memory', cgrp, 'cgroup.event_control', '{eventfd_fd} {oom_control_fd}'.format( eventfd_fd=efd, oom_control_fd=oom_control.fileno(), )) return efd
def stat(subsystem, cgrp, pseudofile): """Calls stat the cgrp file""" path = cgroups.makepath(subsystem, cgrp, pseudofile) return os.stat(path)
def reset_cpu_usage(cgrp): """Set the cpu usage to 0""" with io.open(cgroups.makepath('cpuacct', cgrp, 'cpuacct.usage'), 'w') as f: f.write('0')