qemu_guest_agent.py 37.8 KB
Newer Older
L
Lucas Meneghel Rodrigues 已提交
1 2 3
import logging
import time
import os
4
import re
5 6 7

import aexpect

8
from autotest.client.shared import error
9
from autotest.client import utils
10 11
from avocado.utils import path as avo_path
from avocado.utils import process
12
from avocado.core import exceptions
13

14
from virttest import error_context
15
from virttest import guest_agent
16
from virttest import utils_misc
17
from virttest import env_process
18
from virttest import data_dir
19 20


21
class BaseVirtTest(object):
L
Lucas Meneghel Rodrigues 已提交
22

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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
    def __init__(self, test, params, env):
        self.test = test
        self.params = params
        self.env = env

    def initialize(self, test, params, env):
        if test:
            self.test = test
        if params:
            self.params = params
        if env:
            self.env = env

    def setup(self, test, params, env):
        if test:
            self.test = test
        if params:
            self.params = params
        if env:
            self.env = env

    def run_once(self, test, params, env):
        if test:
            self.test = test
        if params:
            self.params = params
        if env:
            self.env = env

    def before_run_once(self, test, params, env):
        pass

    def after_run_once(self, test, params, env):
        pass

    def cleanup(self, test, params, env):
        pass

    def execute(self, test, params, env):
        self.initialize(test, params, env)
        self.setup(test, params, env)
        try:
            self.before_run_once(test, params, env)
            self.run_once(test, params, env)
            self.after_run_once(test, params, env)
        finally:
            self.cleanup(test, params, env)


class QemuGuestAgentTest(BaseVirtTest):

    def __init__(self, test, params, env):
        BaseVirtTest.__init__(self, test, params, env)

        self._open_session_list = []
        self.gagent = None
        self.vm = None

    def _get_session(self, params, vm):
        if not vm:
            vm = self.vm
        vm.verify_alive()
        timeout = int(params.get("login_timeout", 360))
        session = vm.wait_for_login(timeout=timeout)
        return session

    def _cleanup_open_session(self):
        try:
            for s in self._open_session_list:
                if s:
                    s.close()
        except Exception:
            pass

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
    @error_context.context_aware
    def _check_ga_pkg(self, session, cmd_check_pkg):
        '''
        Check if the package is installed
        :param session: use for sending cmd
        :param cmd_check_pkg: cmd to check if ga pkg is installed
        '''
        error_context.context("Check whether qemu-ga is installed.", logging.info)
        s, o = session.cmd_status_output(cmd_check_pkg)
        return s == 0

    @error_context.context_aware
    def _check_ga_service(self, session, cmd_check_status):
        '''
        Check if the service is started.
        :param session: use for sending cmd
        :param cmd_check_status: cmd to check if ga service is started
        '''
        error_context.context("Check whether qemu-ga service is started.", logging.info)
        s, o = session.cmd_status_output(cmd_check_status)
        return s == 0

    @error_context.context_aware
120
    def gagent_install(self, session, vm, *args):
121 122 123
        if args and isinstance(args, tuple):
            gagent_install_cmd = args[0]
        else:
124
            self.test.error("Missing config 'gagent_install_cmd'")
125 126

        if not gagent_install_cmd:
127
            self.test.error("Gagent_install_cmd's value is empty.")
128

129 130
        error_context.context("Try to install 'qemu-guest-agent' package.",
                              logging.info)
131 132
        s, o = session.cmd_status_output(gagent_install_cmd)
        if s:
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
            self.test.fail("Could not install qemu-guest-agent package"
                           " in VM '%s', detail: '%s'" % (vm.name, o))

    @error_context.context_aware
    def gagent_uninstall(self, session, vm, *args):
        """
        uninstall qemu-ga pkg in guest.
        :param session: use for sending cmd
        :param vm: guest object.
        :param args: Qemu-ga pkg uninstall cmd.
        """
        if args and isinstance(args, tuple):
            gagent_uninstall_cmd = args[0]
        else:
            self.test.error("Missing config 'gagent_uninstall_cmd'")

        if not gagent_uninstall_cmd:
            self.test.error("Gagent_uninstall_cmd's value is empty.")

        error_context.context("Try to uninstall 'qemu-guest-agent' package.",
                              logging.info)
        s, o = session.cmd_status_output(gagent_uninstall_cmd)
        if s:
            self.test.fail("Could not uninstall qemu-guest-agent package "
                           "in VM '%s', detail: '%s'" % (vm.name, o))
158

159 160 161 162 163 164 165 166 167 168
    @error_context.context_aware
    def gagent_start(self, session, vm, *args):
        """
        Start qemu-guest-agent in guest,if start a running service,for rhel
        guest return code is zero,for windows guest,return code is not zero.
        :param session: use for sending cmd
        :param env: Dictionary with test environment.
        :param args: Start cmd.
        """

169 170 171 172 173 174 175 176
        if args and isinstance(args, tuple):
            gagent_start_cmd = args[0]
        else:
            raise error.TestError("Missing config 'gagent_start_cmd'")

        if not gagent_start_cmd:
            return

177 178
        error_context.context("Try to start 'qemu-guest-agent'.", logging.info)
        s, o = session.cmd_status_output(gagent_start_cmd)
179
        if s and "already been started" not in o:
180
            raise error.TestFail("Could not start qemu-guest-agent in VM"
L
Lucas Meneghel Rodrigues 已提交
181
                                 " '%s', detail: '%s'" % (vm.name, o))
182

183
    @error_context.context_aware
184 185 186 187
    def gagent_create(self, params, vm, *args):
        if self.gagent:
            return self.gagent

188
        error_context.context("Create a QemuAgent object.", logging.info)
189 190 191 192 193
        if not (args and isinstance(args, tuple) and len(args) == 2):
            raise error.TestError("Got invalid arguments for guest agent")

        gagent_serial_type = args[0]
        gagent_name = args[1]
194 195 196 197 198 199 200 201

        if gagent_serial_type == guest_agent.QemuAgent.SERIAL_TYPE_VIRTIO:
            filename = vm.get_virtio_port_filename(gagent_name)
        elif gagent_serial_type == guest_agent.QemuAgent.SERIAL_TYPE_ISA:
            filename = vm.get_serial_console_filename(gagent_name)
        else:
            raise guest_agent.VAgentNotSupportedError("Not supported serial"
                                                      " type")
202
        gagent = guest_agent.QemuAgent(vm, gagent_name, gagent_serial_type,
203
                                       filename, get_supported_cmds=True)
204 205 206 207
        self.gagent = gagent

        return self.gagent

208
    @error_context.context_aware
209
    def gagent_verify(self, params, vm):
210
        error_context.context("Check if guest agent work.", logging.info)
211 212 213 214 215 216 217 218

        if not self.gagent:
            raise error.TestError("Could not find guest agent object"
                                  "for VM '%s'" % vm.name)

        self.gagent.verify_responsive()
        logging.info(self.gagent.cmd("guest-info"))

219
    @error_context.context_aware
220 221
    def setup(self, test, params, env):
        BaseVirtTest.setup(self, test, params, env)
222 223
        start_vm = params["start_vm"]
        if (not self.vm) and (start_vm == "yes"):
224 225 226
            vm = self.env.get_vm(params["main_vm"])
            vm.verify_alive()
            self.vm = vm
227 228 229 230 231 232
            session = self._get_session(params, self.vm)

            if self._check_ga_pkg(session, params.get("gagent_pkg_check_cmd")):
                logging.info("qemu-ga is already installed.")
            else:
                logging.info("qemu-ga is not installed.")
233
                self.gagent_install(session, self.vm, *[params.get("gagent_install_cmd")])
234 235 236 237 238

            if self._check_ga_service(session, params.get("gagent_status_cmd")):
                logging.info("qemu-ga service is already running.")
            else:
                logging.info("qemu-ga service is not running.")
239
                self.gagent_start(session, self.vm, *[params.get("gagent_start_cmd")])
240 241 242

            session.close()
            args = [params.get("gagent_serial_type"), params.get("gagent_name")]
243
            self.gagent_create(params, self.vm, *args)
244 245 246

    def run_once(self, test, params, env):
        BaseVirtTest.run_once(self, test, params, env)
247 248
        start_vm = params["start_vm"]
        if (not self.vm) and (start_vm == "yes"):
249 250 251
            vm = self.env.get_vm(params["main_vm"])
            vm.verify_alive()
            self.vm = vm
252
            self.gagent_verify(self.params, self.vm)
253 254 255 256 257

    def cleanup(self, test, params, env):
        self._cleanup_open_session()


258 259 260 261 262 263 264 265 266 267
class QemuGuestAgentBasicCheck(QemuGuestAgentTest):

    def __init__(self, test, params, env):
        QemuGuestAgentTest.__init__(self, test, params, env)

        self.exception_list = []

    def gagent_check_install(self, test, params, env):
        pass

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
    @error_context.context_aware
    def gagent_check_install_uninstall(self, test, params, env):
        """
        Repeat install/uninstall qemu-ga package in guest

        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environment.
        """
        repeats = int(params.get("repeat_times", 1))
        logging.info("Repeat install/uninstall qemu-ga pkg for %s times" % repeats)

        if not self.vm:
            self.vm = self.env.get_vm(params["main_vm"])
            self.vm.verify_alive()

        session = self._get_session(params, self.vm)
        for i in xrange(repeats):
            error_context.context("Install/uninstall qemu-ga pkg for the %sth time" % (i + 1),
                                  logging.info)
            if self._check_ga_pkg(session, params.get("gagent_pkg_check_cmd")):
                self.gagent_uninstall(session, self.vm, *[params.get("gagent_uninstall_cmd")])
                self.gagent_install(session, self.vm, *[params.get("gagent_install_cmd")])
            else:
                self.gagent_install(session, self.vm, *[params.get("gagent_install_cmd")])
                self.gagent_uninstall(session, self.vm, *[params.get("gagent_uninstall_cmd")])
        session.close()

296
    @error_context.context_aware
297 298 299 300 301 302 303
    def gagent_check_sync(self, test, params, env):
        """
        Execute "guest-sync" command to guest agent

        Test steps:
        1) Send "guest-sync" command in the host side.

L
Lucas Meneghel Rodrigues 已提交
304 305 306
        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environmen.
307
        """
308
        error_context.context("Check guest agent command 'guest-sync'", logging.info)
309 310
        self.gagent.sync()

311
    @error_context.context_aware
312
    def __gagent_check_shutdown(self, shutdown_mode):
313 314
        error_context.context("Check guest agent command 'guest-shutdown'"
                              ", shutdown mode '%s'" % shutdown_mode, logging.info)
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
        if not self.env or not self.params:
            raise error.TestError("You should run 'setup' method before test")

        if not (self.vm and self.vm.is_alive()):
            vm = self.env.get_vm(self.params["main_vm"])
            vm.verify_alive()
            self.vm = vm
        self.gagent.shutdown(shutdown_mode)

    def __gagent_check_serial_output(self, pattern):
        start_time = time.time()
        while (time.time() - start_time) < self.vm.REBOOT_TIMEOUT:
            if pattern in self.vm.serial_console.get_output():
                return True
        return False

    def gagent_check_powerdown(self, test, params, env):
        """
        Shutdown guest with guest agent command "guest-shutdown"

L
Lucas Meneghel Rodrigues 已提交
335 336 337
        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environmen.
338 339 340 341 342
        """
        self.__gagent_check_shutdown(self.gagent.SHUTDOWN_MODE_POWERDOWN)
        if not utils_misc.wait_for(self.vm.is_dead, self.vm.REBOOT_TIMEOUT):
            raise error.TestFail("Could not shutdown VM via guest agent'")

343
    @error_context.context_aware
344 345 346 347
    def gagent_check_reboot(self, test, params, env):
        """
        Reboot guest with guest agent command "guest-shutdown"

L
Lucas Meneghel Rodrigues 已提交
348 349 350
        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environmen.
351 352
        """
        self.__gagent_check_shutdown(self.gagent.SHUTDOWN_MODE_REBOOT)
353
        pattern = params["gagent_guest_reboot_pattern"]
354
        error_context.context("Verify serial output has '%s'" % pattern)
355 356 357
        rebooted = self.__gagent_check_serial_output(pattern)
        if not rebooted:
            raise error.TestFail("Could not reboot VM via guest agent")
358
        error_context.context("Try to re-login to guest after reboot")
359 360 361 362 363 364 365
        try:
            session = self._get_session(self.params, None)
            session.close()
        except Exception, detail:
            raise error.TestFail("Could not login to guest,"
                                 " detail: '%s'" % detail)

366
    @error_context.context_aware
367 368 369 370
    def gagent_check_halt(self, test, params, env):
        """
        Halt guest with guest agent command "guest-shutdown"

L
Lucas Meneghel Rodrigues 已提交
371 372 373
        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environmen.
374 375
        """
        self.__gagent_check_shutdown(self.gagent.SHUTDOWN_MODE_HALT)
376
        pattern = params["gagent_guest_shutdown_pattern"]
377
        error_context.context("Verify serial output has '%s'" % pattern)
378 379 380 381 382 383
        halted = self.__gagent_check_serial_output(pattern)
        if not halted:
            raise error.TestFail("Could not halt VM via guest agent")
        # Since VM is halted, force shutdown it.
        try:
            self.vm.destroy(gracefully=False)
384 385 386
        except Exception, detail:
            logging.warn("Got an exception when force destroying guest:"
                         " '%s'", detail)
387

388
    @error_context.context_aware
389 390 391 392 393 394 395 396
    def gagent_check_sync_delimited(self, test, params, env):
        """
        Execute "guest-sync-delimited" command to guest agent

        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environment.
        """
397 398
        error_context.context("Check guest agent command 'guest-sync-delimited'",
                              logging.info)
399 400
        self.gagent.sync("guest-sync-delimited")

401
    @error_context.context_aware
402 403 404 405 406 407
    def _gagent_verify_password(self, vm, new_password):
        """
        check if the password  works well for the specific user
        """
        vm.wait_for_login(password=new_password)

408
    @error_context.context_aware
409 410 411
    def gagent_check_set_user_password(self, test, params, env):
        """
        Execute "guest-set-user-password" command to guest agent
412 413 414
        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environment.
415 416 417
        """
        old_password = params.get("password", "")
        new_password = params.get("new_password", "123456")
418
        ga_username = params.get("ga_username", "root")
419
        crypted = params.get("crypted", "") == "yes"
420
        error_context.context("Change guest's password.")
421
        try:
422 423
            self.gagent.set_user_password(new_password, crypted, ga_username)
            error_context.context("Check if the guest could be login by new password",
424
                                  logging.info)
425 426 427
            self._gagent_verify_password(self.vm, new_password)

        except guest_agent.VAgentCmdError:
428
            test.fail("Failed to set the new password for guest")
429 430

        finally:
431 432
            error_context.context("Reset back the password of guest", logging.info)
            self.gagent.set_user_password(old_password, username=ga_username)
433

434
    @error_context.context_aware
435 436 437
    def gagent_check_get_vcpus(self, test, params, env):
        """
        Execute "guest-set-vcpus" command to guest agent
438 439 440
        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environment.
441
        """
442
        self.gagent.get_vcpus()
443

444
    @error_context.context_aware
445 446 447
    def gagent_check_set_vcpus(self, test, params, env):
        """
        Execute "guest-set-vcpus" command to guest agent
448 449 450
        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environment.
451
        """
452
        error_context.context("get the cpu number of the testing guest")
453 454
        vcpus_info = self.gagent.get_vcpus()
        vcpus_num = len(vcpus_info)
455
        error_context.context("the vcpu number:%d" % vcpus_num, logging.info)
456 457 458 459 460 461 462 463 464 465 466 467
        if vcpus_num < 2:
            raise error.TestNAError("the vpus number of guest should be more"
                                    " than 1")
        vcpus_info[vcpus_num - 1]["online"] = False
        del vcpus_info[vcpus_num - 1]["can-offline"]
        action = {'vcpus': [vcpus_info[vcpus_num - 1]]}
        self.gagent.set_vcpus(action)
        # Check if the result is as expected
        vcpus_info = self.gagent.get_vcpus()
        if vcpus_info[vcpus_num - 1]["online"] is not False:
            raise error.TestFail("the vcpu status is not changed as expected")

468
    @error_context.context_aware
469 470 471 472 473 474 475 476 477 478
    def gagent_check_get_time(self, test, params, env):
        """
        Execute "guest-get-time" command to guest agent
        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environment.
        """
        timeout = float(params.get("login_timeout", 240))
        session = self.vm.wait_for_login(timeout=timeout)
        get_guest_time_cmd = params["get_guest_time_cmd"]
479
        error_context.context("get the time of the guest", logging.info)
480
        nanoseconds_time = self.gagent.get_time()
481 482
        error_context.context("the time get by guest-get-time is '%d' "
                              % nanoseconds_time, logging.info)
483 484 485
        guest_time = session.cmd_output(get_guest_time_cmd)
        if not guest_time:
            raise error.TestError("can't get the guest time for contrast")
486 487
        error_context.context("the time get inside guest by shell cmd is '%d' "
                              % int(guest_time), logging.info)
488 489 490 491 492
        delta = abs(int(guest_time) - nanoseconds_time / 1000000000)
        if delta > 3:
            raise error.TestFail("the time get by guest agent is not the same "
                                 "with that by time check cmd inside guest")

493
    @error_context.context_aware
494 495 496 497 498 499 500 501 502 503
    def gagent_check_set_time(self, test, params, env):
        """
        Execute "guest-set-time" command to guest agent
        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environment.
        """
        timeout = float(params.get("login_timeout", 240))
        session = self.vm.wait_for_login(timeout=timeout)
        get_guest_time_cmd = params["get_guest_time_cmd"]
504
        error_context.context("get the time of the guest", logging.info)
505 506 507
        guest_time_before = session.cmd_output(get_guest_time_cmd)
        if not guest_time_before:
            raise error.TestError("can't get the guest time for contrast")
508 509
        error_context.context("the time before being moved back into past  is '%d' "
                              % int(guest_time_before), logging.info)
510 511 512 513
        # Need to move the guest time one week into the past
        target_time = (int(guest_time_before) - 604800) * 1000000000
        self.gagent.set_time(target_time)
        guest_time_after = session.cmd_output(get_guest_time_cmd)
514 515
        error_context.context("the time after being moved back into past  is '%d' "
                              % int(guest_time_after), logging.info)
516 517 518 519 520 521 522 523 524
        delta = abs(int(guest_time_after) - target_time / 1000000000)
        if delta > 3:
            raise error.TestFail("the time set for guest is not the same "
                                 "with target")
        # Set the system time from the hwclock
        if params["os_type"] != "windows":
            move_time_cmd = params["move_time_cmd"]
            session.cmd("hwclock -w")
            guest_hwclock_after_set = session.cmd_output("date +%s")
525 526
            error_context.context("hwclock is '%d' " % int(guest_hwclock_after_set),
                                  logging.info)
527 528
            session.cmd(move_time_cmd)
            time_after_move = session.cmd_output("date +%s")
529 530
            error_context.context("the time after move back is '%d' "
                                  % int(time_after_move), logging.info)
531 532
            self.gagent.set_time()
            guest_time_after_reset = session.cmd_output(get_guest_time_cmd)
533 534
            error_context.context("the time after being reset is '%d' "
                                  % int(guest_time_after_reset), logging.info)
535
            guest_hwclock = session.cmd_output("date +%s")
536 537
            error_context.context("hwclock for compare is '%d' " % int(guest_hwclock),
                                  logging.info)
538 539 540 541 542
            delta = abs(int(guest_time_after_reset) - int(guest_hwclock))
            if delta > 3:
                raise error.TestFail("The guest time can't be set from hwclock"
                                     " on host")

543
    @error_context.context_aware
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
    def _get_mem_used(self, session, cmd):
        """
        get memory usage of the process

        :param session: use for sending cmd
        :param cmd: get details of the process
        """

        output = session.cmd_output(cmd)
        logging.info("The process details: %s" % output)
        try:
            memory_usage = int(output.split(" ")[-2].replace(",", ""))
            return memory_usage
        except Exception:
            raise exceptions.TestError("Get invalid memory usage by "
                                       "cmd '%s' (%s)" % (cmd, output))

561
    @error_context.context_aware
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
    def gagent_check_memory_leak(self, test, params, env):
        """
        repeat execute "guest-info" command to guest agent, check memory
        usage of the qemu-ga

        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environment.
        """

        timeout = float(params.get("login_timeout", 240))
        test_command = params.get("test_command", "guest-info")
        memory_usage_cmd = params.get("memory_usage_cmd",
                                      "tasklist | findstr /I qemu-ga.exe")
        session = self.vm.wait_for_login(timeout=timeout)
577 578
        error_context.context("get the memory usage of qemu-ga before run '%s'" %
                              test_command, logging.info)
579 580 581 582
        memory_usage_before = self._get_mem_used(session, memory_usage_cmd)
        session.close()
        repeats = int(params.get("repeats", 1))
        for i in range(repeats):
583 584
            error_context.context("execute '%s' %s times" % (test_command, i + 1),
                                  logging.info)
585 586 587
            return_msg = self.gagent.guest_info()
            logging.info(str(return_msg))
        self.vm.verify_alive()
588 589
        error_context.context("get the memory usage of qemu-ga after run '%s'" %
                              test_command, logging.info)
590 591 592 593 594 595 596 597 598 599
        session = self.vm.wait_for_login(timeout=timeout)
        memory_usage_after = self._get_mem_used(session, memory_usage_cmd)
        session.close()
        # less than 500K is acceptable.
        if memory_usage_after - memory_usage_before > 500:
            raise error.TestFail("The memory usages are different, "
                                 "before run command is %skb and after"
                                 " run command is %skb" % (memory_usage_before,
                                                           memory_usage_after))

600
    @error_context.context_aware
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
    def gagent_check_fstrim(self, test, params, env):
        """
        Execute "guest-fstrim" command to guest agent
        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environment.

        """
        def get_host_scsi_disk():
            """
            Get latest scsi disk which enulated by scsi_debug module
            Return the device name and the id in host
            """
            scsi_disk_info = process.system_output(
                avo_path.find_command('lsscsi'), shell=True).splitlines()
            scsi_debug = [_ for _ in scsi_disk_info if 'scsi_debug' in _][-1]
            scsi_debug = scsi_debug.split()
            host_id = scsi_debug[0][1:-1]
            device_name = scsi_debug[-1]
            return (host_id, device_name)

        def get_guest_discard_disk(session):
            """
            Get disk without partitions in guest.
            """
            list_disk_cmd = "ls /dev/[sh]d*|sed 's/[0-9]//p'|uniq -u"
            disk = session.cmd_output(list_disk_cmd).splitlines()[0]
            return disk

        def get_provisioning_mode(device, host_id):
            """
            Get disk provisioning mode, value usually is 'writesame_16',
            depends on params for scsi_debug module.
            """
            device_name = os.path.basename(device)
            path = "/sys/block/%s/device/scsi_disk" % device_name
            path += "/%s/provisioning_mode" % host_id
            return utils.read_one_line(path).strip()

        def get_allocation_bitmap():
            """
            get block allocation bitmap
            """
            path = "/sys/bus/pseudo/drivers/scsi_debug/map"
            try:
                return utils.read_one_line(path).strip()
            except IOError:
                logging.warn("could not get bitmap info, path '%s' is "
                             "not exist", path)
            return ""

        for vm in env.get_all_vms():
            if vm:
                vm.destroy()
                env.unregister_vm(vm.name)
        host_id, disk_name = get_host_scsi_disk()
        provisioning_mode = get_provisioning_mode(disk_name, host_id)
        logging.info("Current provisioning_mode = '%s'", provisioning_mode)
        bitmap = get_allocation_bitmap()
        if bitmap:
            logging.debug("block allocation bitmap: %s" % bitmap)
            raise error.TestError("block allocation bitmap"
                                  " not empty before test.")
        vm_name = params["main_vm"]
        test_image = "scsi_debug"
        params["start_vm"] = "yes"
        params["image_name_%s" % test_image] = disk_name
        params["image_format_%s" % test_image] = "raw"
        params["image_raw_device_%s" % test_image] = "yes"
        params["force_create_image_%s" % test_image] = "no"
        params["drive_format_%s" % test_image] = "scsi-block"
        params["drv_extra_params_%s" % test_image] = "discard=on"
        params["images"] = " ".join([params["images"], test_image])

675
        error_context.context("boot guest with disk '%s'" % disk_name, logging.info)
676 677
        env_process.preprocess_vm(test, params, env, vm_name)

678
        self.setup(test, params, env)
679 680 681 682
        timeout = float(params.get("login_timeout", 240))
        session = self.vm.wait_for_login(timeout=timeout)
        device_name = get_guest_discard_disk(session)

683
        error_context.context("format disk '%s' in guest" % device_name, logging.info)
684 685 686 687
        format_disk_cmd = params["format_disk_cmd"]
        format_disk_cmd = format_disk_cmd.replace("DISK", device_name)
        session.cmd(format_disk_cmd)

688 689
        error_context.context("mount disk with discard options '%s'" % device_name,
                              logging.info)
690 691 692 693
        mount_disk_cmd = params["mount_disk_cmd"]
        mount_disk_cmd = mount_disk_cmd.replace("DISK", device_name)
        session.cmd(mount_disk_cmd)

694
        error_context.context("write the disk with dd command", logging.info)
695 696 697
        write_disk_cmd = params["write_disk_cmd"]
        session.cmd(write_disk_cmd)

698
        error_context.context("Delete the file created before on disk", logging.info)
699 700 701 702 703 704 705
        delete_file_cmd = params["delete_file_cmd"]
        session.cmd(delete_file_cmd)

        # check the bitmap before trim
        bitmap_before_trim = get_allocation_bitmap()
        if not re.match(r"\d+-\d+", bitmap_before_trim):
            raise error.TestFail("didn't get the bitmap of the target disk")
706 707
        error_context.context("the bitmap_before_trim is %s" % bitmap_before_trim,
                              logging.info)
708
        total_block_before_trim = abs(sum([eval(i) for i in
L
Lucas Meneghel Rodrigues 已提交
709
                                           bitmap_before_trim.split(',')]))
710 711
        error_context.context("the total_block_before_trim is %d"
                              % total_block_before_trim, logging.info)
712

713
        error_context.context("execute the guest-fstrim cmd", logging.info)
714 715 716 717 718 719
        self.gagent.fstrim()

        # check the bitmap after trim
        bitmap_after_trim = get_allocation_bitmap()
        if not re.match(r"\d+-\d+", bitmap_after_trim):
            raise error.TestFail("didn't get the bitmap of the target disk")
720 721
        error_context.context("the bitmap_after_trim is %s" % bitmap_after_trim,
                              logging.info)
722
        total_block_after_trim = abs(sum([eval(i) for i in
L
Lucas Meneghel Rodrigues 已提交
723
                                          bitmap_after_trim.split(',')]))
724 725
        error_context.context("the total_block_after_trim is %d"
                              % total_block_after_trim, logging.info)
726 727 728 729 730 731 732

        if total_block_after_trim > total_block_before_trim:
            raise error.TestFail("the bitmap_after_trim is lager, the command"
                                 " guest-fstrim may not work")
        if self.vm:
            self.vm.destroy()

733
    @error_context.context_aware
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
    def gagent_check_get_interfaces(self, test, params, env):
        """
        Execute "guest-network-get-interfaces" command to guest agent
        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environment.
        """
        def find_interface_by_name(interface_list, target_interface):
            """
            find the specific network interface in the interface list return
            by guest agent. return True if find successfully
            """
            for interface in interface_list:
                if "target_interface" == interface["name"]:
                    return True
            return False
        session = self._get_session(params, None)

        # check if the cmd "guest-network-get-interfaces" work
        ret = self.gagent.get_network_interface()
        if not find_interface_by_name(ret, "lo"):
            error.TestFail("didn't find 'lo' interface in the return value")

757
        error_context.context("set down the interface: lo", logging.info)
758 759 760 761 762
        down_interface_cmd = "ip link set lo down"
        session.cmd(down_interface_cmd)

        interfaces_pre_add = self.gagent.get_network_interface()

763
        error_context.context("add the new device bridge in guest", logging.info)
764 765 766 767 768 769 770 771 772 773 774 775
        add_brige_cmd = "ip link add link lo name lo_brige type bridge"
        session.cmd(add_brige_cmd)

        interfaces_after_add = self.gagent.get_network_interface()

        bridge_list = [_ for _ in interfaces_after_add if _ not in
                       interfaces_pre_add]
        if (len(bridge_list) != 1) or \
           ("lo_brige" != bridge_list[0]["name"]):
            error.TestFail("the interface list info after interface was down"
                           " was not as expected")

776
    @error_context.context_aware
777 778 779 780
    def _action_before_fsfreeze(self, *args):
        session = self._get_session(self.params, None)
        self._open_session_list.append(session)

781
    @error_context.context_aware
782
    def _action_after_fsfreeze(self, *args):
783
        error_context.context("Verfiy FS is frozen in guest.")
784 785 786 787 788 789 790 791
        if not isinstance(args, tuple):
            return

        if not self._open_session_list:
            raise error.TestError("Could not find any opened session")
        # Use the last opened session to send cmd.
        session = self._open_session_list[-1]
        try:
792
            session.cmd(self.params["gagent_fs_test_cmd"])
793 794 795 796 797 798
        except aexpect.ShellTimeoutError:
            logging.debug("FS freeze successfully.")
        else:
            raise error.TestFail("FS freeze failed, guest still can"
                                 " write file")

799
    @error_context.context_aware
800 801 802
    def _action_before_fsthaw(self, *args):
        pass

803
    @error_context.context_aware
804 805 806
    def _action_after_fsthaw(self, *args):
        pass

807
    @error_context.context_aware
808 809 810 811 812 813 814 815 816 817
    def gagent_check_fsfreeze(self, test, params, env):
        """
        Test guest agent commands "guest-fsfreeze-freeze/status/thaw"

        Test steps:
        1) Check the FS is thawed.
        2) Freeze the FS.
        3) Check the FS is frozen from both guest agent side and guest os side.
        4) Thaw the FS.

L
Lucas Meneghel Rodrigues 已提交
818 819 820
        :param test: kvm test object
        :param params: Dictionary with the test parameters
        :param env: Dictionary with test environmen.
821 822
        """
        error.base_context("Check guest agent command 'guest-fsfreeze-freeze'",
L
Lucas Meneghel Rodrigues 已提交
823
                           logging.info)
824
        error_context.context("Verify FS is thawed and freeze the FS.")
825 826 827 828 829 830 831 832 833 834 835 836 837 838 839

        try:
            expect_status = self.gagent.FSFREEZE_STATUS_THAWED
            self.gagent.verify_fsfreeze_status(expect_status)
        except guest_agent.VAgentFreezeStatusError:
            # Thaw guest FS if the fs status is incorrect.
            self.gagent.fsthaw(check_status=False)

        self._action_before_fsfreeze(test, params, env)
        self.gagent.fsfreeze()
        try:
            self._action_after_fsfreeze(test, params, env)

            # Next, thaw guest fs.
            self._action_before_fsthaw(test, params, env)
840
            error_context.context("Thaw the FS.")
841
            self.gagent.fsthaw()
842
        except Exception:
843 844 845 846 847 848 849
            # Thaw fs finally, avoid problem in following cases.
            try:
                self.gagent.fsthaw(check_status=False)
            except Exception, detail:
                # Ignore exception for this thaw action.
                logging.warn("Finally failed to thaw guest fs,"
                             " detail: '%s'", detail)
850 851 852 853
            raise

        # Finally, do something after thaw.
        self._action_after_fsthaw(test, params, env)
854 855 856 857

    def run_once(self, test, params, env):
        QemuGuestAgentTest.run_once(self, test, params, env)

858
        gagent_check_type = self.params["gagent_check_type"]
859 860 861 862 863 864 865 866 867
        chk_type = "gagent_check_%s" % gagent_check_type
        if hasattr(self, chk_type):
            func = getattr(self, chk_type)
            func(test, params, env)
        else:
            raise error.TestError("Could not find matching test, check your"
                                  " config file")


868
class QemuGuestAgentBasicCheckWin(QemuGuestAgentBasicCheck):
L
Lucas Meneghel Rodrigues 已提交
869

870 871 872
    """
    Qemu guest agent test class for windows guest.
    """
873

874 875 876
    @error_context.context_aware
    def setup_gagent_in_host(self, session, params, vm):
        error_context.context("download qemu-ga.msi to host", logging.info)
877
        deps = params["deps"]
878
        gagent_download_cmd = params["gagent_download_cmd"]
879 880 881
        if deps == "yes":
            deps_dir = data_dir.get_deps_dir("windows_ga_install")
            gagent_download_cmd = gagent_download_cmd % deps_dir
882
        utils.run(gagent_download_cmd,
883
                  float(params.get("login_timeout", 360)))
884 885 886 887 888 889
        gagent_host_path = params["gagent_host_path"]
        if not os.path.exists(gagent_host_path):
            raise error.TestFail("qemu-ga install program is not exist, maybe "
                                 "the program is not successfully downloaded ")
        gagent_guest_dir = params["gagent_guest_dir"]
#        gagent_remove_service_cmd = params["gagent_remove_service_cmd"]
890
        s, o = session.cmd_status_output("mkdir %s" % gagent_guest_dir)
891 892 893
        if bool(s) and str(s) != "1":
            raise error.TestError("Could not create qemu-ga directory in "
                                  "VM '%s', detail: '%s'" % (vm.name, o))
894
        error_context.context("Copy qemu guest agent program to guest", logging.info)
895 896
        vm.copy_files_to(gagent_host_path, gagent_guest_dir)

897
    @error_context.context_aware
898 899
    def setup(self, test, params, env):
        BaseVirtTest.setup(self, test, params, env)
900 901
        start_vm = params["start_vm"]
        if (not self.vm) and (start_vm == "yes"):
902 903 904
            vm = self.env.get_vm(params["main_vm"])
            vm.verify_alive()
            self.vm = vm
905 906 907 908 909 910 911
            session = self._get_session(params, self.vm)

            if self._check_ga_pkg(session, params.get("gagent_pkg_check_cmd")):
                logging.info("qemu-ga is already installed.")
            else:
                logging.info("qemu-ga is not installed.")
                self.setup_gagent_in_host(session, params, self.vm)
912
                self.gagent_install(session, self.vm, *[params.get("gagent_install_cmd")])
913 914 915 916 917

            if self._check_ga_service(session, params.get("gagent_status_cmd")):
                logging.info("qemu-ga service is already running.")
            else:
                logging.info("qemu-ga service is not running.")
918
                self.gagent_start(session, self.vm, *[params.get("gagent_start_cmd")])
919

920
            session.close()
921 922
            args = [params.get("gagent_serial_type"), params.get("gagent_name")]
            self.gagent_create(params, vm, *args)
923 924


925
def run(test, params, env):
926 927 928 929
    """
    Test qemu guest agent, this case will:
    1) Start VM with virtio serial port.
    2) Install qemu-guest-agent package in guest.
930
    3) Run some basic test for qemu guest agent.
931

L
Lucas Meneghel Rodrigues 已提交
932 933 934
    :param test: kvm test object
    :param params: Dictionary with the test parameters
    :param env: Dictionary with test environmen.
935
    """
936 937 938 939 940
    if params["os_type"] == "windows":
        gagent_test = QemuGuestAgentBasicCheckWin(test, params, env)
    else:
        gagent_test = QemuGuestAgentBasicCheck(test, params, env)

941
    gagent_test.execute(test, params, env)