balloon_check.py 9.2 KB
Newer Older
1 2
import re, logging, random, time
from autotest.client.shared import error
3
from virttest import qemu_monitor, utils_test, utils_misc
4 5


6
@error.context_aware
7 8
def run_balloon_check(test, params, env):
    """
9
    Check Memory ballooning, use M when compare memory in this script:
10 11 12 13 14 15 16 17
    1) Boot a guest with balloon enabled/disabled.
    2) Check whether monitor command 'info balloon' works
    3) Reduce memory to random size between Free memory to max memory size
    4) Run optional test after evicting memory (Optional)
    5) Reset memory value to original memory assigned on qemu (Optional)
    6) Run optional test after enlarging memory (Optional)
    7) Check memory after sub test
    8) Check whether the memory is set up correctly
18

19
    @param test: QEMU test object
20 21 22
    @param params: Dictionary with the test parameters
    @param env: Dictionary with test environment.
    """
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
    def error_report(step, expect_value, monitor_value, guest_value,
                     guest_changed=None, ori_value=None):
        """
        Generate the error report

        @param step: the step of the error happen
        @param expect_value: memory size assign to the vm
        @param monitor_value: memory size report from monitor, this value can
                              be None
        @param guest_value: memory size report from guest, this value can be
                            None
        @param ori_value: memory size in qemu command line
        """
        logging.error("Memory size mismatch %s:\n" % step)
        if guest_changed is not None:
            error_msg = "Wanted to be changed: %s\n" % (ori_value
                                                       - expect_value)
            if monitor_value:
                error_msg += "Changed in monitor: %s\n" % (ori_value
                                                            - monitor_value)
            error_msg += "Changed in guest: %s\n" % guest_changed
        else:
            error_msg = "Assigner to VM: %s\n" % expect_value
            if monitor_value:
                error_msg += "Reported by monitor: %s\n" % monitor_value
            if guest_value:
                error_msg += "Reported by guest OS: %s\n" % guest_value
        logging.error(error_msg)


53 54 55 56 57 58 59 60 61
    def check_ballooned_memory():
        """
        Verify the actual memory reported by monitor command info balloon. If
        the operation failed, increase the failure counter.

        @return: Number of failures occurred during operation.
        """
        try:
            output = vm.monitor.info("balloon")
62
            ballooned_mem = int(re.findall("\d+", str(output))[0])
63
            if vm.monitor.protocol == "qmp":
64
                ballooned_mem *= 1024 ** -2
65
        except qemu_monitor.MonitorError, e:
66
            logging.error(e)
67 68 69 70 71 72 73 74 75 76 77 78 79 80
            return 0
        return ballooned_mem


    def get_memory_status():
        """
        Get Memory status inside guest. As memory balloon shows different
        results in Windows and Linux guests. So use different method for
        them.

        @return: Number of failures occurred during operation and the memory
                 size.
        """
        try:
81
            if params["os_type"] == "windows":
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
            # In Windows guest we get the free memory for memory compare
                memory = session.cmd_output(free_mem_cmd)
                memory = int(re.findall("\d+", memory)[0])
                memory *= 1024 ** -1
            else:
                memory = vm.get_current_memory_size()
        except Exception, e:
            logging.error(e)
            return 0
        return memory


    def memory_check(step, ballooned_mem, ori_mmem, ori_gmem, ratio):
        """
        Check memory status according expect values
97

98 99 100 101 102 103 104 105 106 107 108
        @param step: the check point string
        @param ballooned_mem: ballooned memory in current step
        @param ori_mmem: original memory size get from monitor
        @param ori_gmem: original memory size get from guest
        @param ratio: the ratio that can accept when check results
        """
        error.context("Check memory status %s" % step, logging.info)
        mmem = check_ballooned_memory()
        gmem = get_memory_status()
        if (abs(mmem - ori_mmem) != ballooned_mem
           or (abs(gmem - ori_gmem) < ratio * ballooned_mem)):
109
            if params["os_type"] == "windows":
110 111 112 113 114 115 116 117
                error_report(step, ori_mmem - ballooned_mem, mmem, None,
                             abs(gmem - ori_gmem), ori_mmem)
            else:
                error_report(step, ori_mmem - ballooned_mem, mmem, gmem)
            raise error.TestFail("Balloon test failed %s" % step)


    def balloon_memory(new_mem):
118 119 120 121 122 123
        """
        Baloon memory to new_mem and verifies on both qemu monitor and
        guest OS if change worked.

        @param new_mem: New desired memory.
        """
124 125
        error.context("Change VM memory to %s" % new_mem, logging.info)
        compare_mem = new_mem
126
        if params["monitor_type"] == "qmp":
127 128 129
            new_mem = new_mem * 1024 * 1024
        # This should be replaced by proper monitor method call
        vm.monitor.send_args_cmd("balloon value=%s" % new_mem)
130 131 132 133 134 135 136 137 138
        balloon_timeout = float(params.get("balloon_timeout", 100))
        s = utils_misc.wait_for((lambda: compare_mem
                                         == check_ballooned_memory()),
                                balloon_timeout)
        if s is None:
            raise error.TestFail("Failed to balloon memory to expect"
                                 " value during %ss" % balloon_timeout)


139
    free_mem_cmd = params["free_mem_cmd"]
140
    ratio = float(params.get("ratio", 0.5))
141
    vm = env.get_vm(params["main_vm"])
142
    error.context("Boot a guest", logging.info)
143 144 145 146 147
    vm.verify_alive()
    timeout = int(params.get("login_timeout", 360))
    session = vm.wait_for_login(timeout=timeout)

    # Upper limit that we can raise the memory
148
    vm_assigned_mem = int(params["mem"])
149

150
    error.context("Check the memory in guest", logging.info)
151
    boot_mem = vm.get_memory_size()
152 153

    error.context("Check whether monitor command 'info balloon' works")
154 155 156 157 158 159 160
    monitor_boot_mem = check_ballooned_memory()
    if boot_mem != vm_assigned_mem or monitor_boot_mem != vm_assigned_mem:
        error_report("check memory before test", vm_assigned_mem,
                     monitor_boot_mem, boot_mem)
        raise error.TestError("Memory status is in currect after guest boot"
                              " up, abort the test")
    if monitor_boot_mem:
161
        logging.info("Current VM memory according to ballooner: %s",
162 163 164
                      monitor_boot_mem)

    guest_boot_mem = get_memory_status()
165

166 167
    error.context("Reduce memory to random size between Free memory"
                  "to max memory size", logging.info)
168
    s, o = session.cmd_status_output(free_mem_cmd)
169 170 171
    if s != 0:
        raise error.TestError("Can not get guest memory information")

172
    vm_mem_free = int(re.findall('\d+', o)[0]) / 1024
173

174 175
    new_mem = int(random.uniform(vm_assigned_mem - vm_mem_free,
                                 vm_assigned_mem))
176 177 178 179
    balloon_memory(new_mem)
    ballooned_mem = vm_assigned_mem - new_mem
    memory_check("after evict memory", ballooned_mem,
                 monitor_boot_mem, guest_boot_mem, ratio)
180

181 182
    if (params.get("run_evict_sub_test", "no") == "yes"
        and 'sub_balloon_test_evict' in params):
183
        error.context("Run optional test after evicting memory", logging.info)
184 185 186 187 188
        balloon_test = params['sub_balloon_test_evict']
        utils_test.run_virt_sub_test(test, params, env, sub_type=balloon_test)
        if balloon_test == "shutdown" :
            logging.info("Guest shutdown normally after balloon")
            return
189 190
        if params.get("session_need_update", "no") == "yes":
            session = vm.wait_for_login(timeout=timeout)
191 192 193 194
        if params.get("qemu_quit_after_sub_case", "no") == "yes":
            ballooned_mem = 0
        memory_check("after subtest when evicting memory", ballooned_mem,
                     monitor_boot_mem, guest_boot_mem, ratio)
195

196 197
    error.context("Enlarge memory to random size between current memory to"
                  " max memory size", logging.info)
198 199
    # This will ensure we won't trigger guest OOM killer while running
    # multiple iterations.
200 201 202 203 204 205
    expect_mem = int(random.uniform(new_mem, vm_assigned_mem))

    balloon_memory(expect_mem)
    ballooned_mem = vm_assigned_mem - expect_mem
    memory_check("after enlarge memory", ballooned_mem,
                 monitor_boot_mem, guest_boot_mem, ratio)
206

207 208
    if (params.get("run_enlarge_sub_test", "no") == "yes"
        and 'sub_balloon_test_enlarge' in params):
209 210
        error.context("Run optional test after enlarging memory",
                      logging.info)
211 212 213 214 215
        balloon_test = params['sub_balloon_test_enlarge']
        utils_test.run_virt_sub_test(test, params, env, sub_type=balloon_test)
        if balloon_test == "shutdown" :
            logging.info("Guest shutdown normally after balloon")
            return
216 217
        if params.get("session_need_update", "no") == "yes":
            session = vm.wait_for_login(timeout=timeout)
218 219 220 221
        if params.get("qemu_quit_after_sub_case", "no") == "yes":
            ballooned_mem = 0
        memory_check("after subtest when enlarging memory", ballooned_mem,
                    monitor_boot_mem , guest_boot_mem, ratio)
222
    session.close()