rv_connect.py 14.4 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
from virttest.aexpect import ShellStatusError
11
from virttest.aexpect import ShellCmdError
12
from virttest.aexpect import ShellProcessTerminatedError
13
from virttest import utils_net, utils_spice, remote, utils_misc
14
from autotest.client.shared import error
15

L
Lucas Meneghel Rodrigues 已提交
16

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

35
    client_vm.send_key("kp_enter")  # send enter
36

37

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


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 已提交
56 57 58
    :param client_vm - vm object
    :param guest_vm - vm object
    :param params
59 60
    """
    rv_binary = params.get("rv_binary", "remote-viewer")
61
    rv_ld_library_path = params.get("rv_ld_library_path")
62 63 64 65 66 67 68 69 70
    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')

71
    host_ip = utils_net.get_host_ip_address(params)
72
    host_port = None
73
    if guest_vm.get_spice_var("listening_addr") == "ipv6":
74 75 76 77 78 79 80
        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")

81 82
    check_spice_info = params.get("spice_info")
    ssltype = params.get("ssltype")
83
    test_type = params.get("test_type")
84 85 86 87 88 89 90 91 92

    # 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
93
    ticket = None
94 95 96 97
    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)
98
        logging.info("Sending to qemu monitor: set_password spice %s"
99
                     % qemu_ticket)
100

101 102 103 104 105 106 107 108 109 110
    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"

111
    client_session = client_vm.wait_for_login(
L
Lucas Meneghel Rodrigues 已提交
112
        timeout=int(params.get("login_timeout", 360)))
113 114

    if display == "spice":
115

116 117 118
        ticket = guest_vm.get_spice_var("spice_password")

        if guest_vm.get_spice_var("spice_ssl") == "yes":
119 120

            # client needs cacert file
121
            cacert = "%s/%s" % (guest_vm.get_spice_var("spice_x509_prefix"),
L
Lucas Meneghel Rodrigues 已提交
122
                                guest_vm.get_spice_var("spice_x509_cacert_file"))
123 124 125 126 127 128 129 130 131 132 133 134
            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 已提交
135 136 137
            # cacert subj is in format for create certificate(with '/' delimiter)
            # remote-viewer needs ',' delimiter. And also is needed to remove
            # first character (it's '/')
138
            host_subj = guest_vm.get_spice_var("spice_x509_server_subj")
139
            host_subj = host_subj.replace('/', ',')[1:]
140 141 142 143 144 145 146 147 148 149 150
            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,
151 152
                                                                  host_tls_port,
                                                                  host_port)
153 154
            else:
                spice_url = " spice://%s?tls-port=%s\&port=%s" % (host_ip,
155 156
                                                                  host_tls_port,
                                                                  host_port)
157

158
            if rv_parameters_from == "menu":
159
                line = spice_url
160 161
            elif rv_parameters_from == "file":
                pass
162 163
            else:
                cmd += spice_url
164

165 166
            if not rv_parameters_from == "file":
                cmd += " --spice-ca-file=%s" % cacert
167

168 169
            if (params.get("spice_client_host_subject") == "yes" and not
                    rv_parameters_from == "file"):
170 171 172 173
                cmd += " --spice-host-subject=\"%s\"" % host_subj

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

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

    else:
        raise Exception("Unsupported display value")
188 189

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

194 195 196 197 198 199 200
    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")
201 202
        if not rv_parameters_from == file:
            cmd += " --spice-smartcard"
203

L
Lucas Meneghel Rodrigues 已提交
204
        if certdb is not None:
205 206 207 208
            logging.debug("Remote Viewer set to use the following certificate"
                          " database: " + certdb)
            cmd += " --spice-smartcard-db " + certdb

L
Lucas Meneghel Rodrigues 已提交
209
        if gencerts is not None:
210 211 212
            logging.debug("Remote Viewer set to use the following certs: " +
                          gencerts)
            cmd += " --spice-smartcard-certificates " + gencerts
213

214
    if client_vm.params.get("os_type") == "linux":
215
        cmd = "nohup " + cmd + " &> /dev/null &"  # Launch it on background
216 217 218 219 220 221 222 223
        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")
224

225
    # Launching the actual set of commands
226
    try:
227 228 229 230 231
        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)

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

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

240 241 242 243 244 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
    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
279 280

    is_rv_connected = True
281

282
    try:
283 284 285 286 287
        utils_spice.verify_established(client_vm, host_ip,
                                       host_port, rv_binary,
                                       host_tls_port,
                                       params.get("spice_secure_channels",
                                                  None))
288 289 290
    except utils_spice.RVConnectError:
        if test_type == "negative":
            logging.info("remote-viewer connection failed as expected")
291
            if ssltype in ("invalid_implicit_hs", "invalid_explicit_hs"):
L
Lucas Meneghel Rodrigues 已提交
292
                # Check the qemu process output to verify what is expected
293 294 295 296 297 298 299
                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
300
        else:
301
            raise error.TestFail("remote-viewer connection failed")
302

303 304 305 306
    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 已提交
307
    # Get spice info
308 309 310 311 312 313 314 315
    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 已提交
316 317
        # Remove brackets from ipv6 host ip
        if (host_ip[1:len(host_ip) - 1] in output):
318 319 320 321
            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 已提交
322
                                 " command: 'info spice'")
323 324 325 326
    else:
        logging.info("Not checking the value of 'info spice'"
                     " from the qemu monitor")

L
Lucas Meneghel Rodrigues 已提交
327
    # prevent from kill remote-viewer after test finish
328 329 330
    if client_vm.params.get("os_type") == "linux":
        cmd = "disown -ar"
    client_session.cmd_output(cmd)
331

332

333
def run(test, params, env):
334 335 336 337 338 339 340
    """
    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 已提交
341 342
    :param test: QEMU test object.  :param params: Dictionary with the test parameters.
    :param env: Dictionary with test environment.
343 344 345
    """

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

347 348
    guest_vm.verify_alive()
    guest_session = guest_vm.wait_for_login(
L
Lucas Meneghel Rodrigues 已提交
349
        timeout=int(params.get("login_timeout", 360)))
350 351 352 353

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

356
    if (client_vm.params.get("os_type") == "windows" and
357
            client_vm.params.get("rv_installer", None)):
358 359 360 361 362
        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():
363 364 365 366 367 368 369 370 371 372 373
            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
374 375
            utils_spice.clear_interface(env.get_vm(vm),
                                        int(params.get("login_timeout", "360")))
376 377

        utils_spice.wait_timeout(waittime)
378

379 380 381 382
    launch_rv(client_vm, guest_vm, params)

    client_session.close()
    guest_session.close()