def test_reclose(self):
        cg_path = create_random_cg(self.parent_cg_path)

        cg = Cgroup(cg_path)
        cg.open()
        cg.close()
        self.assertRaises(AssertionError, cg.close)
    def test_trigger_restart(self):
        cg_path = create_random_cg(self.parent_cg_path)

        q = queue.Queue()
        cg = Cgroup(cg_path)
        cg.open()

        cg.wakeup(q, None)
        self.assertHasNoMessages(q)

        set_memlimit(cg_path)
        cg.wakeup(q, None)
        self.assertHasNoMessages(q)

        trigger_oom(cg_path)

        # The test program should fill 128 MB rather fast; give it 10s
        for _ in range(100):
            cg.wakeup(q, None)
            try:
                msg = q.get_nowait()
            except queue.Empty:
                time.sleep(0.1)
                continue
            self.assertIsInstance(msg, RestartRequestedMessage)
            self.assertEqual(cg, msg.cg)
            break
        else:
            raise Exception("Queue never received a message!")

        cg.close()
    def test_trigger_restart(self):
        cg_path = create_random_cg(self.parent_cg_path)

        q = queue.Queue()
        cg = Cgroup(cg_path)
        cg.open()

        cg.wakeup(q)
        self.assertRaises(queue.Empty, q.get_nowait)

        enable_memlimit_and_trigger_oom(cg_path)

        # The test program should fill 128 MB rather fast; give it 10s
        for _ in range(100):
            cg.wakeup(q)
            try:
                msg = q.get_nowait()
            except queue.Empty:
                time.sleep(0.1)
                continue
            self.assertIsInstance(msg, RestartRequestedMessage)
            self.assertEqual(cg, msg.cg)
            break
        else:
            raise Exception("Queue never received a message!")

        cg.close()
    def test_reclose(self):
        cg_path = create_random_cg(self.parent_cg_path)

        cg = Cgroup(cg_path)
        cg.open()
        cg.close()
        self.assertRaises(AssertionError, cg.close)
def enable_memlimit_and_trigger_oom(path):
    cg = Cgroup(path)
    cg.open()

    # Set a memory limit then disable the OOM killer via a wakeup
    cg.set_memory_limit_in_bytes(1024 * 1024 * 128)  # 128 MB
    cg.wakeup(queue.Queue())
    cg.close()

    test_program = 'l = []\nwhile True:\n  l.append(object())'

    subprocess.Popen(["sudo", "cgexec", "-g", _descriptor_from_cg_path(path),
                      "python", "-c", test_program])
    def test_stale_cgroup(self):
        cg_path = create_random_cg(self.parent_cg_path)

        cg = Cgroup(cg_path)
        cg.open()

        delete_cg(cg_path)

        q = queue.Queue()
        cg.wakeup(q, None)
        self.assertRaises(EnvironmentError, cg.wakeup, q, None, raise_for_stale=True)

        cg.close()
    def test_stale_cgroup(self):
        cg_path = create_random_cg(self.parent_cg_path)

        cg = Cgroup(cg_path)
        cg.open()

        delete_cg(cg_path)

        q = queue.Queue()
        cg.wakeup(q)
        self.assertRaises(EnvironmentError, cg.wakeup, q, raise_for_stale=True)

        cg.close()
    def test_disable_oom_killer(self):
        cg_path = create_random_cg(self.parent_cg_path)

        cg = Cgroup(cg_path)
        cg.open()
        cg.wakeup(queue.Queue())
        self.assertEqual("0", cg.oom_control_status()["oom_kill_disable"])

        # The OOM Killer should be disabled if there is a task limit
        cg.set_memory_limit_in_bytes(1024)
        cg.wakeup(queue.Queue())
        self.assertEqual("1", cg.oom_control_status()["oom_kill_disable"])

        cg.close()
    def test_disable_oom_killer(self):
        cg_path = create_random_cg(self.parent_cg_path)

        cg = Cgroup(cg_path)
        cg.open()
        cg.wakeup(queue.Queue(), None)
        self.assertEqual("0", cg.oom_control_status()["oom_kill_disable"])

        # The OOM Killer should be disabled if there is a task limit
        cg.set_memory_limit_in_bytes(1024)
        cg.wakeup(queue.Queue(), None)
        self.assertEqual("1", cg.oom_control_status()["oom_kill_disable"])

        cg.close()
def enable_memlimit_and_trigger_oom(path):
    cg = Cgroup(path)
    cg.open()

    # Set a memory limit then disable the OOM killer via a wakeup
    cg.set_memory_limit_in_bytes(1024 * 1024 * 128)  # 128 MB
    cg.wakeup(queue.Queue())
    cg.close()

    test_program = 'l = []\nwhile True:\n  l.append(object())'

    subprocess.Popen([
        "sudo", "cgexec", "-g",
        descriptor_from_cg_path(path), "python", "-c", test_program
    ])
    def test_wakeup_on_sync(self):
        cg_path = create_random_cg(self.parent_cg_path)

        cg = Cgroup(cg_path)
        cg.open()
        cg.set_memory_limit_in_bytes(1024)
        self.assertEqual("0", cg.oom_control_status()["oom_kill_disable"])

        index = CgroupIndex(self.parent_cg_path, queue.Queue())
        index.open()
        index.sync()
        index.close()

        self.assertEqual("1", cg.oom_control_status()["oom_kill_disable"])
        cg.close()
예제 #12
0
    def test_wakeup_on_sync(self):
        cg_path = create_random_cg(self.parent_cg_path)

        cg = Cgroup(cg_path)
        cg.open()
        cg.set_memory_limit_in_bytes(1024)
        self.assertEqual("0", cg.oom_control_status()["oom_kill_disable"])

        index = CgroupIndex(self.parent_cg_path, queue.Queue())
        index.open()
        index.sync()
        index.close()

        self.assertEqual("1", cg.oom_control_status()["oom_kill_disable"])
        cg.close()
예제 #13
0
    def sync(self):
        logger.debug("syncing cgroups")

        # Sync all monitors with disk, and deregister stale ones. It's
        # important to actually *wakeup* monitors here, so as to ensure we
        # don't race with Docker when it creates a cgroup (which could result
        # in us not seeing the memory limit and therefore not disabling the OOM
        # killer).
        for cg in list(self._path_hash.values()):
            try:
                cg.wakeup(self.job_queue, None, raise_for_stale=True)
            except EnvironmentError:
                self.deregister(cg)
                cg.close()

        for entry in os.listdir(self.root_cg_path):
            path = os.path.join(self.root_cg_path, entry)

            # Is this a CG or just a regular file?
            if not os.path.isdir(path):
                continue

            # We're already tracking this CG. It *might* have changed between
            # our check and now, but in that case we'll catch it at the next
            # sync.
            if path in self._path_hash:
                continue

            # This a new CG, Register and wake it up immediately after in case
            # there already is some handling to do (typically: disabling the
            # OOM killer).
            cg = Cgroup(path)

            try:
                cg.open()
            except EnvironmentError as e:
                # CG exited before we had a chance to register it. That's OK.
                logger.warning("%s: error opening new cg: %s", cg.name(), e)
            else:
                self.register(cg)
                cg.wakeup(self.job_queue, None)
예제 #14
0
class CgroupTestUnit(unittest.TestCase, QueueAssertionHelper):
    def setUp(self):
        self.mock_cg = tempfile.mkdtemp()

        self.write_oom_control()
        with open(self.cg_path("memory.pressure_level"), "w") as f:
            f.write('')

        self.monitor = Cgroup(self.mock_cg)
        self.queue = queue.Queue()

    def tearDown(self):
        shutil.rmtree(self.mock_cg)

    # Helpers

    def write_oom_control(self, oom_kill_disable="0", under_oom="0"):
        control = ["oom_kill_disable {0}".format(oom_kill_disable),
                   "under_oom {0}".format(under_oom)]

        with open(self.cg_path("memory.oom_control"), "w") as f:
            f.write("\n".join(control))
            f.write("\n")

    def write_memory_usage(self, memory_usage=12345):
        with open(self.cg_path("memory.usage_in_bytes"), "w") as f:
            f.write(str(memory_usage))
            f.write("\n")

    def write_memory_limit(self, memory_limit=9223372036854771712):
        with open(self.cg_path("memory.limit_in_bytes"), "w") as f:
            f.write(str(memory_limit))
            f.write("\n")

    def cg_path(self, path):
        return os.path.join(self.mock_cg, path)

    # Tests

    def test_open(self):
        self.write_oom_control()
        self.monitor.open()

        expected = "{0} {1}\n{2} {3} {4}\n".format(
                self.monitor.event_oom.fileno(),
                self.monitor.oom_control.fileno(),
                self.monitor.event_pressure.fileno(),
                self.monitor.memory_pressure.fileno(),
                "critical"
        )

        self.monitor.close()

        with open(self.cg_path("cgroup.event_control")) as f:
            self.assertEqual(expected, f.read())

    def test_wakeup_with_limits_and_disable_oom_killer(self):
        self.write_oom_control()
        self.write_memory_limit(1024)

        self.monitor.open()
        self.monitor.wakeup(self.queue, None)
        self.monitor.close()

        with open(self.cg_path("memory.oom_control")) as f:
            self.assertEqual("1\n", f.read())

    def test_wakeup_with_oom_killer_disabled_is_noop(self):
        self.write_oom_control(oom_kill_disable="1")
        self.write_memory_limit(1024)

        self.monitor.open()
        self.monitor.wakeup(self.queue, None)
        self.monitor.close()

        # File shoud not have been touched
        with open(self.cg_path("memory.oom_control")) as f:
            self.assertEqual("oom_kill_disable 1\n", f.readline())

    def test_wakeup_without_limits_is_noop(self):
        self.write_oom_control(oom_kill_disable="0")
        self.write_memory_limit()

        self.monitor.open()
        self.monitor.wakeup(self.queue, None)
        self.monitor.close()

        # File shoud not have been touched
        with open(self.cg_path("memory.oom_control")) as f:
            self.assertEqual("oom_kill_disable 0\n", f.readline())

    def test_wakeup_with_stale_group_does_not_raise(self):
        self.write_oom_control(oom_kill_disable="0")

        self.monitor.open()

        os.close(self.monitor.oom_control.fileno())
        self.monitor.wakeup(self.queue, None)
        self.assertRaises(EnvironmentError, self.monitor.wakeup, None,
                          self.queue, raise_for_stale=True)

        # Close the other FD manually. We still need to attempt closing the
        # wrapper to avoid a resource warning.
        os.close(self.monitor.event_oom.fileno())
        try:
            self.monitor.oom_control.close()
        except EnvironmentError:
            pass

    def test_wakeup_under_oom_requests_restart(self):
        self.monitor.open()

        self.write_oom_control(oom_kill_disable="1", under_oom="1")
        self.monitor.wakeup(self.queue, None)

        self.assertHasMessageForCg(
            self.queue,
            RestartRequestedMessage,
            self.mock_cg
        )

    def test_wakeup_memory_pressure_notifies(self):
        self.monitor.open()

        self.write_memory_usage()
        self.write_memory_limit()

        self.monitor.wakeup(
            self.queue,
            self.monitor.event_pressure.fileno()
        )

        self.assertHasMessageForCg(
            self.queue,
            MemoryPressureMessage,
            self.mock_cg
        )

    def test_pressure_wakeup_with_stale_group_does_not_raise(self):
        self.monitor.open()

        self.monitor.wakeup(
            self.queue,
            self.monitor.event_pressure.fileno()
        )

        self.assertHasMessageForCg(
            self.queue,
            MemoryPressureMessage,
            self.mock_cg
        )
    def test_open_close(self):
        cg_path = create_random_cg(self.parent_cg_path)

        cg = Cgroup(cg_path)
        cg.open()
        cg.close()
예제 #16
0
class CgroupTestUnit(unittest.TestCase):
    def setUp(self):
        self.mock_cg = tempfile.mkdtemp()
        self.monitor = Cgroup(self.mock_cg)
        self.queue = queue.Queue()

    def tearDown(self):
        shutil.rmtree(self.mock_cg)

    # Helpers

    def write_oom_control(self, oom_kill_disable="0", under_oom="0"):
        control = [
            "oom_kill_disable {0}".format(oom_kill_disable),
            "under_oom {0}".format(under_oom)
        ]

        with open(self.cg_path("memory.oom_control"), "w") as f:
            f.write("\n".join(control))
            f.write("\n")

    def write_memory_limit(self, memory_limit=9223372036854771712):
        with open(self.cg_path("memory.limit_in_bytes"), "w") as f:
            f.write(str(memory_limit))
            f.write("\n")

    def cg_path(self, path):
        return os.path.join(self.mock_cg, path)

    # Tests

    def test_open(self):
        self.write_oom_control()
        self.monitor.open()
        evt_fileno = self.monitor.event_fileno()
        oom_control_fileno = self.monitor.oom_control.fileno()
        self.monitor.close()

        with open(self.cg_path("cgroup.event_control")) as f:
            e = "{0} {1}\n".format(evt_fileno, oom_control_fileno)
            self.assertEqual(e, f.read())

    def test_wakeup_disable_oom_killer(self):
        self.write_oom_control()
        self.write_memory_limit(1024)

        self.monitor.open()
        self.monitor.wakeup(self.queue)
        self.monitor.close()

        with open(self.cg_path("memory.oom_control")) as f:
            self.assertEqual("1\n", f.read())

    def test_wakeup_oom_killer_is_disabled(self):
        self.write_oom_control(oom_kill_disable="1")
        self.write_memory_limit(1024)

        self.monitor.open()
        self.monitor.wakeup(self.queue)
        self.monitor.close()

        # File shoud not have been touched
        with open(self.cg_path("memory.oom_control")) as f:
            self.assertEqual("oom_kill_disable 1\n", f.readline())

    def test_wakeup_no_memory_limit(self):
        self.write_oom_control(oom_kill_disable="0")
        self.write_memory_limit()

        self.monitor.open()
        self.monitor.wakeup(self.queue)
        self.monitor.close()

        # File shoud not have been touched
        with open(self.cg_path("memory.oom_control")) as f:
            self.assertEqual("oom_kill_disable 0\n", f.readline())

    def test_wakeup_stale(self):
        self.write_oom_control(oom_kill_disable="0")

        self.monitor.open()

        os.close(self.monitor.oom_control.fileno())
        self.monitor.wakeup(self.queue)
        self.assertRaises(EnvironmentError,
                          self.monitor.wakeup,
                          self.queue,
                          raise_for_stale=True)

        # Close the other FD manually. We still need to attempt closing the
        # wrapper to avoid a resource warning.
        os.close(self.monitor.event_fileno())
        try:
            self.monitor.oom_control.close()
        except EnvironmentError:
            pass
    def test_open_close(self):
        cg_path = create_random_cg(self.parent_cg_path)

        cg = Cgroup(cg_path)
        cg.open()
        cg.close()