rv_connect.py 14.1 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 16 17 18
from virttest import utils_net
from virttest import utils_spice
from virttest import remote
from virttest import utils_misc
19

L
Lucas Meneghel Rodrigues 已提交
20

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

39
    client_vm.send_key("kp_enter")  # send enter
40

41

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


X
Xu Han 已提交
54
def launch_rv(test, client_vm, guest_vm, params):
55 56 57 58 59
    """
    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 已提交
60 61 62
    :param client_vm - vm object
    :param guest_vm - vm object
    :param params
63 64
    """
    rv_binary = params.get("rv_binary", "remote-viewer")
65
    rv_ld_library_path = params.get("rv_ld_library_path")
66 67 68 69 70 71 72
    display = params.get("display")

    proxy = params.get("spice_proxy", None)
    if proxy:
        try:
            socket.inet_aton(params.get("proxy_ip", None))
        except socket.error:
X
Xu Han 已提交
73
            test.cancel('Parameter proxy_ip not changed from default values')
74

75
    host_ip = utils_net.get_host_ip_address(params)
76
    host_port = None
77
    if guest_vm.get_spice_var("listening_addr") == "ipv6":
78 79 80 81 82 83 84
        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")

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

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

105 106 107 108 109 110 111 112 113 114
    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"

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

    if display == "spice":
119

120 121 122
        ticket = guest_vm.get_spice_var("spice_password")

        if guest_vm.get_spice_var("spice_ssl") == "yes":
123 124

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

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

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

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

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

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

    else:
        raise Exception("Unsupported display value")
190 191

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

196 197 198 199 200 201 202
    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")
X
Xu Han 已提交
203
        if not rv_parameters_from == "file":
204
            cmd += " --spice-smartcard"
205

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

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

216
    if client_vm.params.get("os_type") == "linux":
217
        cmd = "nohup " + cmd + " &> /dev/null &"  # Launch it on background
218 219 220 221
        if rv_ld_library_path:
            cmd = "export LD_LIBRARY_PATH=" + rv_ld_library_path + ";" + cmd

    if rv_parameters_from == "file":
X
Xu Han 已提交
222
        logging.info("Generating file")
223
        utils_spice.gen_rv_file(params, guest_vm, host_subj, cacert)
X
Xu Han 已提交
224
        logging.info("Uploading file to client")
225
        client_vm.copy_files_to("rv_file.vv", "~/rv_file.vv")
226

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

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

    logging.info("Launching %s on the client (virtual)", cmd)
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 279
    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)

280
        utils_spice.wait_timeout(15)  # Wait for conncetion to establish
281 282

    is_rv_connected = True
283

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

305
    if test_type == "negative" and is_rv_connected:
X
Xu Han 已提交
306 307
        test.fail("remote-viewer connection was established when"
                  " it was supposed to be unsuccessful")
308

L
Lucas Meneghel Rodrigues 已提交
309
    # Get spice info
310 311 312 313 314 315 316 317
    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 已提交
318 319
        # Remove brackets from ipv6 host ip
        if (host_ip[1:len(host_ip) - 1] in output):
320 321 322
            logging.info("Reported ipv6 address found in output from"
                         " 'info spice'")
        else:
X
Xu Han 已提交
323 324
            test.fail("ipv6 address not found from qemu monitor"
                      " command: 'info spice'")
325 326 327 328
    else:
        logging.info("Not checking the value of 'info spice'"
                     " from the qemu monitor")

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

334

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

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

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

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

358
    if (client_vm.params.get("os_type") == "windows" and
359
            client_vm.params.get("rv_installer", None)):
360 361 362 363 364
        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():
365 366 367 368 369
            try:
                session = env.get_vm(vm).wait_for_login(timeout=360)
                output = session.cmd('cat /etc/redhat-release')
                logging.info(output)
            except ShellCmdError:
X
Xu Han 已提交
370 371
                test.cancel("Test is only currently supported on "
                            "RHEL and Fedora operating systems")
372 373 374 375
            if "release 6." in output:
                waittime = 15
            else:
                waittime = 60
376 377
            utils_spice.clear_interface(env.get_vm(vm),
                                        int(params.get("login_timeout", "360")))
378 379

        utils_spice.wait_timeout(waittime)
380

X
Xu Han 已提交
381
    launch_rv(test, client_vm, guest_vm, params)
382 383 384

    client_session.close()
    guest_session.close()