def test_clean_tasksless_resctrl_groups(rmdir_mock): with patch('wca.resctrl.open', create_open_mock({ '/sys/fs/resctrl/mon_groups/c1/tasks': '', # empty '/sys/fs/resctrl/mon_groups/c2/tasks': '1234', '/sys/fs/resctrl/empty/mon_groups/c3/tasks': '', '/sys/fs/resctrl/half_empty/mon_groups/c5/tasks': '1234', '/sys/fs/resctrl/half_empty/mon_groups/c6/tasks': '', })): mon_groups_relation = {'': ['c1', 'c2'], 'empty': ['c3'], 'half_empty': ['c5', 'c6'], } clean_taskless_groups(mon_groups_relation) rmdir_mock.assert_has_calls([ call('/sys/fs/resctrl/mon_groups/c1'), call('/sys/fs/resctrl/empty'), call('/sys/fs/resctrl/half_empty/mon_groups/c6') ])
def sync_containers_state(self, tasks: List[Task]) -> Dict[Task, ContainerInterface]: """Syncs state of ContainerManager with a system by removing orphaned containers, and creating containers for newly arrived tasks, and synchronizing containers' state. Function is responsible for cleaning and initializing stateful subsystems such as: - perf counters: opens file descriptors for counters, - resctrl (ResGroups): creates and manages directories in resctrl filesystem and scarce "closid" hardware identifiers Can throw OutOfClosidsException. """ # Find difference between discovered tasks and already watched containers. new_tasks, containers_to_cleanup = _find_new_and_dead_tasks( tasks, list(self.containers.values())) if containers_to_cleanup: log.debug('sync_containers_state: cleaning up %d containers', len(containers_to_cleanup)) log.log(logger.TRACE, 'sync_containers_state: containers_to_cleanup=%r', containers_to_cleanup) # Clean up and remove orphaned containers. for container_to_cleanup in containers_to_cleanup: container_to_cleanup.cleanup() # Recreate self.containers. # mutated state e.g. labels for Kubernetes containers = {} for task in tasks: for known_task, container in self.containers.items(): if task.cgroup_path == known_task.cgroup_path: containers[task] = container continue self.containers = containers if new_tasks: log.debug('Found %d new tasks', len(new_tasks)) log.log(logger.TRACE, 'sync_containers_state: new_tasks=%r', new_tasks) # Prepare state of currently assigned resgroups # and remove some orphaned resgroups. container_name_to_ctrl_group = {} if self._rdt_information: assert self._rdt_information.is_monitoring_enabled(), \ "rdt_enabled requires RDT monitoring for keeping groups relation." mon_groups_relation = resctrl.read_mon_groups_relation() log.log(TRACE, 'mon_groups_relation (before cleanup): %s', pprint.pformat(mon_groups_relation)) resctrl.clean_taskless_groups(mon_groups_relation) mon_groups_relation = resctrl.read_mon_groups_relation() log.log(TRACE, 'mon_groups_relation (after cleanup): %s', pprint.pformat(mon_groups_relation)) # Calculate inverse relation of container_name # to res_group name based on mon_groups_relations. for ctrl_group, container_names in mon_groups_relation.items(): for container_name in container_names: container_name_to_ctrl_group[container_name] = ctrl_group log.log(TRACE, 'container_name_to_ctrl_group: %s', pprint.pformat(container_name_to_ctrl_group)) # Create new containers and store them. for new_task in new_tasks: self.containers[new_task] = self._create_container(new_task) # Sync "state" of individual containers. # Note: only pids are synchronized, not allocations. for container in self.containers.values(): if self._rdt_information: if container.get_name() in container_name_to_ctrl_group: resgroup_name = container_name_to_ctrl_group[container.get_name()] container.set_resgroup(ResGroup(name=resgroup_name)) else: # Every newly detected container is first assigned to the root group. container.set_resgroup(ResGroup(name='')) container.sync() log.log(logger.TRACE, 'sync_containers_state: containers=%r', self.containers) return self.containers