Esempio n. 1
0
    def test_mount_cgroups_should_handle_errors_when_mounting_an_individual_controller(
            self):
        original_run_get_output = shellutil.run_get_output

        def mock_run_get_output(cmd, *args, **kwargs):
            if cmd.startswith('mount '):
                if 'memory' in cmd:
                    raise Exception(
                        'A test exception mounting the memory controller')
                return 0, None
            return original_run_get_output(cmd, *args, **kwargs)

        with patch(
                "azurelinuxagent.common.osutil.default.shellutil.run_get_output",
                side_effect=mock_run_get_output) as patch_run_get_output:
            with patch("azurelinuxagent.common.cgroupconfigurator.logger.warn"
                       ) as mock_logger_warn:
                FileSystemCgroupsApi.mount_cgroups()

                # the cgroup filesystem and the cpu controller should still have been mounted
                mount_commands = MountCgroupsTestCase._get_mount_commands(
                    patch_run_get_output)

                self.assertRegex(mount_commands, ';mount.* cgroup_root ',
                                 'The cgroups file system was not mounted')
                self.assertRegex(mount_commands, ';mount.* cpu,cpuacct ',
                                 'The cpu controller was not mounted')

                # A warning should have been logged for the memory controller
                args, kwargs = mock_logger_warn.call_args
                self.assertIn(
                    'A test exception mounting the memory controller', args)
Esempio n. 2
0
    def test_mount_cgroups_should_raise_when_the_cgroups_filesystem_fails_to_mount(
            self):
        original_run_get_output = shellutil.run_get_output

        def mock_run_get_output(cmd, *args, **kwargs):
            if cmd.startswith('mount '):
                if 'cgroup_root' in cmd:
                    raise Exception(
                        'A test exception mounting the cgroups file system')
                return 0, None
            return original_run_get_output(cmd, *args, **kwargs)

        with patch(
                "azurelinuxagent.common.osutil.default.shellutil.run_get_output",
                side_effect=mock_run_get_output) as patch_run_get_output:
            with self.assertRaises(Exception) as context_manager:
                FileSystemCgroupsApi.mount_cgroups()

            self.assertRegex(
                str(context_manager.exception),
                'A test exception mounting the cgroups file system')

            mount_commands = MountCgroupsTestCase._get_mount_commands(
                patch_run_get_output)
            self.assertNotIn(
                'memory', mount_commands,
                'The memory controller should not have been mounted')
            self.assertNotIn(
                'cpu', mount_commands,
                'The cpu controller should not have been mounted')
Esempio n. 3
0
    def test_mount_cgroups_should_mount_the_cpu_and_memory_controllers(self):
        # the mount command requires root privileges; make it a no op and check only for file existence
        original_run_get_output = shellutil.run_get_output

        def mock_run_get_output(cmd, *args, **kwargs):
            if cmd.startswith('mount '):
                return 0, None
            return original_run_get_output(cmd, *args, **kwargs)

        with patch(
                "azurelinuxagent.common.osutil.default.shellutil.run_get_output",
                side_effect=mock_run_get_output) as patch_run_get_output:
            FileSystemCgroupsApi.mount_cgroups()

            # the directories for the controllers should have been created
            for controller in ['cpu', 'memory', 'cpuacct', 'cpu,cpuacct']:
                directory = os.path.join(self.cgroups_file_system_root,
                                         controller)
                self.assertTrue(
                    os.path.exists(directory),
                    "A directory for controller {0} was not created".format(
                        controller))

            # the cgroup filesystem and the cpu and memory controllers should have been mounted
            mount_commands = MountCgroupsTestCase._get_mount_commands(
                patch_run_get_output)

            self.assertRegex(mount_commands, ';mount.* cgroup_root ',
                             'The cgroups file system was not mounted')
            self.assertRegex(mount_commands, ';mount.* cpu,cpuacct ',
                             'The cpu controller was not mounted')
            self.assertRegex(mount_commands, ';mount.* memory ',
                             'The memory controller was not mounted')
Esempio n. 4
0
    def test_mount_cgroups_should_not_mount_cgroup_controllers_when_they_already_exist(
            self):
        os.mkdir(self.cgroups_file_system_root)
        os.mkdir(os.path.join(self.cgroups_file_system_root, 'cpu,cpuacct'))
        os.mkdir(os.path.join(self.cgroups_file_system_root, 'memory'))

        original_run_get_output = shellutil.run_get_output

        def mock_run_get_output(cmd, *args, **kwargs):
            if cmd.startswith('mount '):
                return 0, None
            return original_run_get_output(cmd, *args, **kwargs)

        with patch(
                "azurelinuxagent.common.osutil.default.shellutil.run_get_output",
                side_effect=mock_run_get_output) as patch_run_get_output:
            FileSystemCgroupsApi.mount_cgroups()

            mount_commands = MountCgroupsTestCase._get_mount_commands(
                patch_run_get_output)

            self.assertNotIn(
                'cgroup_root', mount_commands,
                'The cgroups file system should not have been mounted')
            self.assertNotIn(
                'cpu,cpuacct', mount_commands,
                'The cpu controller should not have been mounted')
            self.assertNotIn(
                'memory', mount_commands,
                'The memory controller should not have been mounted')
Esempio n. 5
0
    def test_start_extension_command_should_add_the_child_process_to_the_extension_cgroup(
            self):
        api = FileSystemCgroupsApi()
        api.create_extension_cgroups_root()

        process, extension_cgroups = api.start_extension_command(
            extension_name="Microsoft.Compute.TestExtension-1.2.3",
            command="date",
            shell=False,
            cwd=self.tmp_dir,
            env={},
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE)

        process.wait()

        for cgroup in extension_cgroups:
            cgroups_procs_path = os.path.join(cgroup.path, "cgroup.procs")
            with open(cgroups_procs_path, "r") as f:
                contents = f.read()
            pid = int(contents)

            self.assertEquals(
                pid, process.pid,
                "The PID of the command was not added to {0}. Expected: {1}, got: {2}"
                .format(cgroups_procs_path, process.pid, pid))
Esempio n. 6
0
    def test_create_extension_cgroups_should_create_cgroups_on_all_controllers(self):
        api = FileSystemCgroupsApi()
        api.create_extension_cgroups_root()
        extension_cgroups = api.create_extension_cgroups("Microsoft.Compute.TestExtension-1.2.3")

        def assert_cgroup_created(controller):
            cgroup_path = os.path.join(self.cgroups_file_system_root, controller, "walinuxagent.extensions",
                                       "Microsoft.Compute.TestExtension_1.2.3")

            self.assertTrue(any(cgroups.path == cgroup_path for cgroups in extension_cgroups))
            self.assertTrue(os.path.exists(cgroup_path))

        assert_cgroup_created("cpu")
        assert_cgroup_created("memory")
Esempio n. 7
0
    def test_create_extension_cgroups_root_should_create_root_directory_for_extensions(self):
        FileSystemCgroupsApi().create_extension_cgroups_root()

        cpu_cgroup = os.path.join(self.cgroups_file_system_root, "cpu", "walinuxagent.extensions")
        self.assertTrue(os.path.exists(cpu_cgroup))

        memory_cgroup = os.path.join(self.cgroups_file_system_root, "memory", "walinuxagent.extensions")
        self.assertTrue(os.path.exists(memory_cgroup))
Esempio n. 8
0
    def test_cleanup_legacy_cgroups_should_move_daemon_pid_to_new_cgroup_and_remove_legacy_cgroups(
            self):
        # Set up a mock /var/run/waagent.pid file
        daemon_pid = "42"
        daemon_pid_file = os.path.join(self.tmp_dir, "waagent.pid")
        fileutil.write_file(daemon_pid_file, daemon_pid + "\n")

        # Set up old controller cgroups and add the daemon PID to them
        legacy_cpu_cgroup = CGroupsTools.create_legacy_agent_cgroup(
            self.cgroups_file_system_root, "cpu", daemon_pid)
        legacy_memory_cgroup = CGroupsTools.create_legacy_agent_cgroup(
            self.cgroups_file_system_root, "memory", daemon_pid)

        # Set up new controller cgroups and add extension handler's PID to them
        new_cpu_cgroup = CGroupsTools.create_agent_cgroup(
            self.cgroups_file_system_root, "cpu", "999")
        new_memory_cgroup = CGroupsTools.create_agent_cgroup(
            self.cgroups_file_system_root, "memory", "999")

        with patch("azurelinuxagent.common.cgroupapi.add_event"
                   ) as mock_add_event:
            with patch(
                    "azurelinuxagent.common.cgroupapi.get_agent_pid_file_path",
                    return_value=daemon_pid_file):
                FileSystemCgroupsApi().cleanup_legacy_cgroups()

        # The method should have added the daemon PID to the new controllers and deleted the old ones
        new_cpu_contents = fileutil.read_file(
            os.path.join(new_cpu_cgroup, "cgroup.procs"))
        new_memory_contents = fileutil.read_file(
            os.path.join(new_memory_cgroup, "cgroup.procs"))

        self.assertTrue(daemon_pid in new_cpu_contents)
        self.assertTrue(daemon_pid in new_memory_contents)

        self.assertFalse(os.path.exists(legacy_cpu_cgroup))
        self.assertFalse(os.path.exists(legacy_memory_cgroup))

        # Assert the event parameters that were sent out
        self.assertEquals(len(mock_add_event.call_args_list), 2)
        self.assertTrue(
            all(kwargs['op'] == 'CGroupsCleanUp'
                for _, kwargs in mock_add_event.call_args_list))
        self.assertTrue(
            all(kwargs['is_success']
                for _, kwargs in mock_add_event.call_args_list))
        self.assertTrue(
            any(
                re.match(
                    r"Moved daemon's PID from legacy cgroup to /.*/cgroup/cpu/walinuxagent.service",
                    kwargs['message'])
                for _, kwargs in mock_add_event.call_args_list))
        self.assertTrue(
            any(
                re.match(
                    r"Moved daemon's PID from legacy cgroup to /.*/cgroup/memory/walinuxagent.service",
                    kwargs['message'])
                for _, kwargs in mock_add_event.call_args_list))
Esempio n. 9
0
    def test_remove_extension_cgroups_should_log_a_warning_when_the_cgroup_contains_active_tasks(self):
        api = FileSystemCgroupsApi()
        api.create_extension_cgroups_root()
        api.create_extension_cgroups("Microsoft.Compute.TestExtension-1.2.3")

        with patch("azurelinuxagent.common.cgroupapi.logger.warn") as mock_logger_warn:
            with patch("azurelinuxagent.common.cgroupapi.os.rmdir", side_effect=OSError(16, "Device or resource busy")):
                api.remove_extension_cgroups("Microsoft.Compute.TestExtension-1.2.3")

            args, kwargs = mock_logger_warn.call_args
            message = args[0]
            self.assertIn("still has active tasks", message)
Esempio n. 10
0
    def test_remove_extension_cgroups_should_remove_all_cgroups(self):
        api = FileSystemCgroupsApi()
        api.create_extension_cgroups_root()
        extension_cgroups = api.create_extension_cgroups("Microsoft.Compute.TestExtension-1.2.3")

        api.remove_extension_cgroups("Microsoft.Compute.TestExtension-1.2.3")

        for cgroup in extension_cgroups:
            self.assertFalse(os.path.exists(cgroup.path))
Esempio n. 11
0
    def test_mount_cgroups_should_raise_when_all_controllers_fail_to_mount(
            self):
        original_run_get_output = shellutil.run_get_output

        def mock_run_get_output(cmd, *args, **kwargs):
            if cmd.startswith('mount '):
                if 'memory' in cmd or 'cpu,cpuacct' in cmd:
                    raise Exception(
                        'A test exception mounting a cgroup controller')
                return 0, None
            return original_run_get_output(cmd, *args, **kwargs)

        with patch(
                "azurelinuxagent.common.osutil.default.shellutil.run_get_output",
                side_effect=mock_run_get_output):
            with self.assertRaises(Exception) as context_manager:
                FileSystemCgroupsApi.mount_cgroups()

            self.assertRegex(str(context_manager.exception),
                             'A test exception mounting a cgroup controller')
Esempio n. 12
0
    def test_start_extension_command_should_add_the_child_process_to_the_extension_cgroup(
            self, _):
        api = FileSystemCgroupsApi()
        api.create_extension_cgroups_root()

        with tempfile.TemporaryFile(dir=self.tmp_dir, mode="w+b") as stdout:
            with tempfile.TemporaryFile(dir=self.tmp_dir,
                                        mode="w+b") as stderr:
                extension_cgroups, process_output = api.start_extension_command(
                    extension_name="Microsoft.Compute.TestExtension-1.2.3",
                    command="echo $$",
                    timeout=300,
                    shell=True,
                    cwd=self.tmp_dir,
                    env={},
                    stdout=stdout,
                    stderr=stderr)

        # The expected format of the process output is [stdout]\n{PID}\n\n\n[stderr]\n"
        pattern = re.compile(r"\[stdout\]\n(\d+)\n\n\n\[stderr\]\n")
        m = pattern.match(process_output)

        try:
            pid_from_output = int(m.group(1))
        except Exception as e:
            self.fail(
                "No PID could be extracted from the process output! Error: {0}"
                .format(ustr(e)))

        for cgroup in extension_cgroups:
            cgroups_procs_path = os.path.join(cgroup.path, "cgroup.procs")
            with open(cgroups_procs_path, "r") as f:
                contents = f.read()
            pid_from_cgroup = int(contents)

            self.assertEquals(
                pid_from_output, pid_from_cgroup,
                "The PID from the process output ({0}) does not match the PID found in the"
                "process cgroup {1} ({2})".format(pid_from_output,
                                                  cgroups_procs_path,
                                                  pid_from_cgroup))
Esempio n. 13
0
    def test_mount_cgroups_should_not_create_symbolic_links_when_the_cpu_controller_fails_to_mount(
            self):
        original_run_get_output = shellutil.run_get_output

        def mock_run_get_output(cmd, *args, **kwargs):
            if cmd.startswith('mount '):
                if 'cpu,cpuacct' in cmd:
                    raise Exception(
                        'A test exception mounting the cpu controller')
                return 0, None
            return original_run_get_output(cmd, *args, **kwargs)

        with patch(
                "azurelinuxagent.common.osutil.default.shellutil.run_get_output",
                side_effect=mock_run_get_output):
            with patch("azurelinuxagent.common.osutil.default.os.symlink"
                       ) as patch_symlink:
                FileSystemCgroupsApi.mount_cgroups()

                self.assertEquals(
                    patch_symlink.call_count, 0,
                    'A symbolic link should not have been created')
Esempio n. 14
0
    def test_create_agent_cgroups_should_create_cgroups_on_all_controllers(self):
        agent_cgroups = FileSystemCgroupsApi().create_agent_cgroups()

        def assert_cgroup_created(controller):
            cgroup_path = os.path.join(self.cgroups_file_system_root, controller, "walinuxagent.service")
            self.assertTrue(any(cgroups.path == cgroup_path for cgroups in agent_cgroups))
            self.assertTrue(os.path.exists(cgroup_path))
            cgroup_task = int(fileutil.read_file(os.path.join(cgroup_path, "cgroup.procs")))
            current_process = os.getpid()
            self.assertEqual(cgroup_task, current_process)

        assert_cgroup_created("cpu")
        assert_cgroup_created("memory")
Esempio n. 15
0
    def test_get_extension_cgroups_should_return_all_cgroups(self):
        api = FileSystemCgroupsApi()
        api.create_extension_cgroups_root()
        created = api.create_extension_cgroups("Microsoft.Compute.TestExtension-1.2.3")

        retrieved = api.get_extension_cgroups("Microsoft.Compute.TestExtension-1.2.3")

        self.assertEqual(len(retrieved), len(created))

        for cgroup in created:
            self.assertTrue(any(retrieved_cgroup.path == cgroup.path for retrieved_cgroup in retrieved))