rv_connect.py 14.5 KB
Newer Older
1 2 3 4 5 6 7
"""
rv_connect.py - connect with remote-viewer to remote target

Requires: binaries remote-viewer, Xorg, netstat
          Use example kickstart RHEL-6-spice.ks

"""
L
Lucas Meneghel Rodrigues 已提交
8 9
import logging
import socket
10 11 12 13 14

from aexpect import ShellStatusError
from aexpect import ShellCmdError
from aexpect import ShellProcessTerminatedError

15
from autotest.client.shared import error
16

17 18 19 20
from virttest import utils_net
from virttest import utils_spice
from virttest import remote
from virttest import utils_misc
21

L
Lucas Meneghel Rodrigues 已提交
22

23
def str_input(client_vm, ticket):
24 25
    """
    sends spice_password trough vm.send_key()
L
Lucas Meneghel Rodrigues 已提交
26 27
    :param client_session - vm() object
    :param ticket - use params.get("spice_password")
28 29
    """
    logging.info("Passing ticket '%s' to the remote-viewer.", ticket)
L
Lucas Meneghel Rodrigues 已提交
30 31 32 33 34 35
    char_mapping = {":": "shift-semicolon",
                    ",": "comma",
                    ".": "dot",
                    "/": "slash",
                    "?": "shift-slash",
                    "=": "equal"}
36
    for character in ticket:
37 38
        if character in char_mapping:
            character = char_mapping[character]
39 40
        client_vm.send_key(character)

41
    client_vm.send_key("kp_enter")  # send enter
42

43

44 45 46
def print_rv_version(client_session, rv_binary):
    """
    prints remote-viewer and spice-gtk version available inside client_session
L
Lucas Meneghel Rodrigues 已提交
47 48
    :param client_session - vm.wait_for_login()
    :param rv_binary - remote-viewer binary
49 50
    """
    logging.info("remote-viewer version: %s",
L
Lucas Meneghel Rodrigues 已提交
51
                 client_session.cmd(rv_binary + " -V"))
52
    logging.info("spice-gtk version: %s",
L
Lucas Meneghel Rodrigues 已提交
53
                 client_session.cmd(rv_binary + " --spice-gtk-version"))
54 55 56 57 58 59 60 61


def launch_rv(client_vm, guest_vm, params):
    """
    Launches rv_binary with args based on spice configuration
    inside client_session on background.
    remote-viewer will try to connect from vm1 from vm2

L
Lucas Meneghel Rodrigues 已提交
62 63 64
    :param client_vm - vm object
    :param guest_vm - vm object
    :param params
65 66
    """
    rv_binary = params.get("rv_binary", "remote-viewer")
67
    rv_ld_library_path = params.get("rv_ld_library_path")
68 69 70 71 72 73 74 75 76
    display = params.get("display")

    proxy = params.get("spice_proxy", None)
    if proxy:
        try:
            socket.inet_aton(params.get("proxy_ip", None))
        except socket.error:
            raise error.TestNAError('Parameter proxy_ip not changed from default values')

77
    host_ip = utils_net.get_host_ip_address(params)
78
    host_port = None
79
    if guest_vm.get_spice_var("listening_addr") == "ipv6":
80 81 82 83 84 85 86
        host_ip = ("[" + utils_misc.convert_ipv4_to_ipv6(host_ip) +
                   "]")
    host_tls_port = None

    disable_audio = params.get("disable_audio", "no")
    full_screen = params.get("full_screen")

87 88
    check_spice_info = params.get("spice_info")
    ssltype = params.get("ssltype")
89
    test_type = params.get("test_type")
90 91 92 93 94 95 96 97 98

    # cmd var keeps final remote-viewer command line
    # to be executed on client
    cmd = rv_binary
    if client_vm.params.get("os_type") != "windows":
        cmd = cmd + " --display=:0.0"

    # If qemu_ticket is set, set the password
    #  of the VM using the qemu-monitor
99
    ticket = None
100 101 102 103
    ticket_send = params.get("spice_password_send")
    qemu_ticket = params.get("qemu_password")
    if qemu_ticket:
        guest_vm.monitor.cmd("set_password spice %s" % qemu_ticket)
104
        logging.info("Sending to qemu monitor: set_password spice %s"
105
                     % qemu_ticket)
106

107 108 109 110 111 112 113 114 115 116
    gencerts = params.get("gencerts")
    certdb = params.get("certdb")
    smartcard = params.get("smartcard")
    host_subj = None
    cacert = None

    rv_parameters_from = params.get("rv_parameters_from", "cmd")
    if rv_parameters_from == 'file':
        cmd += " ~/rv_file.vv"

117
    client_session = client_vm.wait_for_login(
L
Lucas Meneghel Rodrigues 已提交
118
        timeout=int(params.get("login_timeout", 360)))
119 120

    if display == "spice":
121

122 123 124
        ticket = guest_vm.get_spice_var("spice_password")

        if guest_vm.get_spice_var("spice_ssl") == "yes":
125 126

            # client needs cacert file
127
            cacert = "%s/%s" % (guest_vm.get_spice_var("spice_x509_prefix"),
L
Lucas Meneghel Rodrigues 已提交
128
                                guest_vm.get_spice_var("spice_x509_cacert_file"))
129 130 131 132 133 134 135 136 137 138 139 140
            client_session.cmd("rm -rf %s && mkdir -p %s" % (
                               guest_vm.get_spice_var("spice_x509_prefix"),
                               guest_vm.get_spice_var("spice_x509_prefix")))
            remote.copy_files_to(client_vm.get_address(), 'scp',
                                 params.get("username"),
                                 params.get("password"),
                                 params.get("shell_port"),
                                 cacert, cacert)

            host_tls_port = guest_vm.get_spice_var("spice_tls_port")
            host_port = guest_vm.get_spice_var("spice_port")

L
Lucas Meneghel Rodrigues 已提交
141 142 143
            # cacert subj is in format for create certificate(with '/' delimiter)
            # remote-viewer needs ',' delimiter. And also is needed to remove
            # first character (it's '/')
144
            host_subj = guest_vm.get_spice_var("spice_x509_server_subj")
145
            host_subj = host_subj.replace('/', ',')[1:]
146 147 148 149 150 151 152 153 154 155 156
            if ssltype == "invalid_explicit_hs":
                host_subj = "Invalid Explicit HS"
            else:
                host_subj += host_ip

            # If it's invalid implicit, a remote-viewer connection
            # will be attempted with the hostname, since ssl certs were
            # generated with the ip address
            hostname = socket.gethostname()
            if ssltype == "invalid_implicit_hs":
                spice_url = " spice://%s?tls-port=%s\&port=%s" % (hostname,
157 158
                                                                  host_tls_port,
                                                                  host_port)
159 160
            else:
                spice_url = " spice://%s?tls-port=%s\&port=%s" % (host_ip,
161 162
                                                                  host_tls_port,
                                                                  host_port)
163

164
            if rv_parameters_from == "menu":
165
                line = spice_url
166 167
            elif rv_parameters_from == "file":
                pass
168 169
            else:
                cmd += spice_url
170

171 172
            if not rv_parameters_from == "file":
                cmd += " --spice-ca-file=%s" % cacert
173

174 175
            if (params.get("spice_client_host_subject") == "yes" and not
                    rv_parameters_from == "file"):
176 177 178 179
                cmd += " --spice-host-subject=\"%s\"" % host_subj

        else:
            host_port = guest_vm.get_spice_var("spice_port")
180
            if rv_parameters_from == "menu":
L
Lucas Meneghel Rodrigues 已提交
181 182
                # line to be sent through monitor once r-v is started
                # without spice url
183
                line = "spice://%s?port=%s" % (host_ip, host_port)
184 185
            elif rv_parameters_from == "file":
                pass
186 187
            else:
                cmd += " spice://%s?port=%s" % (host_ip, host_port)
188 189 190 191 192 193

    elif display == "vnc":
        raise NotImplementedError("remote-viewer vnc")

    else:
        raise Exception("Unsupported display value")
194 195

    # Check to see if the test is using the full screen option.
196
    if full_screen == "yes" and not rv_parameters_from == "file":
197 198 199
        logging.info("Remote Viewer Set to use Full Screen")
        cmd += " --full-screen"

200 201 202 203 204 205 206
    if disable_audio == "yes":
        logging.info("Remote Viewer Set to disable audio")
        cmd += " --spice-disable-audio"

    # Check to see if the test is using a smartcard.
    if smartcard == "yes":
        logging.info("remote viewer Set to use a smartcard")
207 208
        if not rv_parameters_from == file:
            cmd += " --spice-smartcard"
209

L
Lucas Meneghel Rodrigues 已提交
210
        if certdb is not None:
211 212 213 214
            logging.debug("Remote Viewer set to use the following certificate"
                          " database: " + certdb)
            cmd += " --spice-smartcard-db " + certdb

L
Lucas Meneghel Rodrigues 已提交
215
        if gencerts is not None:
216 217 218
            logging.debug("Remote Viewer set to use the following certs: " +
                          gencerts)
            cmd += " --spice-smartcard-certificates " + gencerts
219

220
    if client_vm.params.get("os_type") == "linux":
221
        cmd = "nohup " + cmd + " &> /dev/null &"  # Launch it on background
222 223 224 225 226 227 228 229
        if rv_ld_library_path:
            cmd = "export LD_LIBRARY_PATH=" + rv_ld_library_path + ";" + cmd

    if rv_parameters_from == "file":
        print "Generating file"
        utils_spice.gen_rv_file(params, guest_vm, host_subj, cacert)
        print "Uploading file to client"
        client_vm.copy_files_to("rv_file.vv", "~/rv_file.vv")
230

231
    # Launching the actual set of commands
232
    try:
233 234 235 236 237
        if rv_ld_library_path:
            print_rv_version(client_session, "LD_LIBRARY_PATH=/usr/local/lib " + rv_binary)
        else:
            print_rv_version(client_session, rv_binary)

238
    except (ShellStatusError, ShellProcessTerminatedError):
239 240
        # Sometimes It fails with Status error, ingore it and continue.
        # It's not that important to have printed versions in the log.
241 242
        logging.debug("Ignoring a Status Exception that occurs from calling "
                      "print versions of remote-viewer or spice-gtk")
243 244

    logging.info("Launching %s on the client (virtual)", cmd)
245

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
    if proxy:
        if "http" in proxy:
            split = proxy.split('//')[1].split(':')
        else:
            split = proxy.split(':')
        host_ip = split[0]
        if len(split) > 1:
            host_port = split[1]
        else:
            host_port = "3128"
        if rv_parameters_from != "file":
            client_session.cmd("export SPICE_PROXY=%s" % proxy)

    if not params.get("rv_verify") == "only":
        try:
            client_session.cmd(cmd)
        except ShellStatusError:
            logging.debug("Ignoring a status exception, will check connection"
                          "of remote-viewer later")

        # Send command line through monitor since url was not provided
        if rv_parameters_from == "menu":
            utils_spice.wait_timeout(1)
            str_input(client_vm, line)

        # client waits for user entry (authentication) if spice_password is set
        # use qemu monitor password if set, else, if set, try normal password.
        if qemu_ticket:
            # Wait for remote-viewer to launch
            utils_spice.wait_timeout(5)
            str_input(client_vm, qemu_ticket)
        elif ticket:
            if ticket_send:
                ticket = ticket_send

            utils_spice.wait_timeout(5)  # Wait for remote-viewer to launch
            str_input(client_vm, ticket)

        utils_spice.wait_timeout(5)  # Wait for conncetion to establish
285 286

    is_rv_connected = True
287

288
    try:
289 290 291 292 293
        utils_spice.verify_established(client_vm, host_ip,
                                       host_port, rv_binary,
                                       host_tls_port,
                                       params.get("spice_secure_channels",
                                                  None))
294 295 296
    except utils_spice.RVConnectError:
        if test_type == "negative":
            logging.info("remote-viewer connection failed as expected")
297
            if ssltype in ("invalid_implicit_hs", "invalid_explicit_hs"):
L
Lucas Meneghel Rodrigues 已提交
298
                # Check the qemu process output to verify what is expected
299 300 301 302 303 304 305
                qemulog = guest_vm.process.get_output()
                if "SSL_accept failed" in qemulog:
                    return
                else:
                    raise error.TestFail("SSL_accept failed not shown in qemu" +
                                         "process as expected.")
            is_rv_connected = False
306
        else:
307
            raise error.TestFail("remote-viewer connection failed")
308

309 310 311 312
    if test_type == "negative" and is_rv_connected:
        raise error.TestFail("remote-viewer connection was established when" +
                             " it was supposed to be unsuccessful")

L
Lucas Meneghel Rodrigues 已提交
313
    # Get spice info
314 315 316 317 318 319 320 321
    output = guest_vm.monitor.cmd("info spice")
    logging.debug("INFO SPICE")
    logging.debug(output)

    # Check to see if ipv6 address is reported back from qemu monitor
    if (check_spice_info == "ipv6"):
        logging.info("Test to check if ipv6 address is reported"
                     " back from the qemu monitor")
L
Lucas Meneghel Rodrigues 已提交
322 323
        # Remove brackets from ipv6 host ip
        if (host_ip[1:len(host_ip) - 1] in output):
324 325 326 327
            logging.info("Reported ipv6 address found in output from"
                         " 'info spice'")
        else:
            raise error.TestFail("ipv6 address not found from qemu monitor"
L
Lucas Meneghel Rodrigues 已提交
328
                                 " command: 'info spice'")
329 330 331 332
    else:
        logging.info("Not checking the value of 'info spice'"
                     " from the qemu monitor")

L
Lucas Meneghel Rodrigues 已提交
333
    # prevent from kill remote-viewer after test finish
334 335 336
    if client_vm.params.get("os_type") == "linux":
        cmd = "disown -ar"
    client_session.cmd_output(cmd)
337

338

339
def run(test, params, env):
340 341 342 343 344 345 346
    """
    Simple test for Remote Desktop connection
    Tests expectes that Remote Desktop client (spice/vnc) will be executed
    from within a second guest so we won't be limited to Linux only clients

    The plan is to support remote-viewer at first place

L
Lucas Meneghel Rodrigues 已提交
347 348
    :param test: QEMU test object.  :param params: Dictionary with the test parameters.
    :param env: Dictionary with test environment.
349 350 351
    """

    guest_vm = env.get_vm(params["guest_vm"])
352

353 354
    guest_vm.verify_alive()
    guest_session = guest_vm.wait_for_login(
L
Lucas Meneghel Rodrigues 已提交
355
        timeout=int(params.get("login_timeout", 360)))
356 357 358 359

    client_vm = env.get_vm(params["client_vm"])
    client_vm.verify_alive()
    client_session = client_vm.wait_for_login(
L
Lucas Meneghel Rodrigues 已提交
360
        timeout=int(params.get("login_timeout", 360)))
361

362
    if (client_vm.params.get("os_type") == "windows" and
363
            client_vm.params.get("rv_installer", None)):
364 365 366 367 368
        utils_spice.install_rv_win(client_vm, params.get("rv_installer"))
        return

    if params.get("clear_interface", "yes") == "yes":
        for vm in params.get("vms").split():
369 370 371 372 373 374 375 376 377 378 379
            try:
                session = env.get_vm(vm).wait_for_login(timeout=360)
                output = session.cmd('cat /etc/redhat-release')
                logging.info(output)
            except ShellCmdError:
                raise error.TestNAError("Test is only currently supported on "
                                        "RHEL and Fedora operating systems")
            if "release 6." in output:
                waittime = 15
            else:
                waittime = 60
380 381
            utils_spice.clear_interface(env.get_vm(vm),
                                        int(params.get("login_timeout", "360")))
382 383

        utils_spice.wait_timeout(waittime)
384

385 386 387 388
    launch_rv(client_vm, guest_vm, params)

    client_session.close()
    guest_session.close()