summaryrefslogtreecommitdiff
path: root/nova/tests/functional/regressions/test_bug_1595962.py
blob: 94421a81f90db0deb81b8d023c1ab550d9a59804 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# Copyright 2016 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import time
from unittest import mock

import fixtures
import io

from nova import test
from nova.tests import fixtures as nova_fixtures
from nova.tests.fixtures import libvirt as fakelibvirt
from nova.tests.functional import fixtures as func_fixtures
from nova.virt.libvirt import guest as libvirt_guest


class TestSerialConsoleLiveMigrate(test.TestCase):
    REQUIRES_LOCKING = True

    def setUp(self):
        super(TestSerialConsoleLiveMigrate, self).setUp()
        self.useFixture(nova_fixtures.RealPolicyFixture())
        self.useFixture(nova_fixtures.NeutronFixture(self))
        self.useFixture(nova_fixtures.GlanceFixture(self))
        self.useFixture(func_fixtures.PlacementFixture())
        api_fixture = self.useFixture(nova_fixtures.OSAPIFixture(
            api_version='v2.1'))
        # Replace libvirt with fakelibvirt
        self.useFixture(fixtures.MonkeyPatch(
           'nova.virt.libvirt.driver.libvirt',
           fakelibvirt))
        self.useFixture(fixtures.MonkeyPatch(
           'nova.virt.libvirt.host.libvirt',
           fakelibvirt))
        self.useFixture(fixtures.MonkeyPatch(
           'nova.virt.libvirt.guest.libvirt',
           fakelibvirt))
        self.useFixture(nova_fixtures.LibvirtFixture())

        self.admin_api = api_fixture.admin_api
        self.api = api_fixture.api

        self.flags(compute_driver='libvirt.LibvirtDriver')
        self.flags(enabled=True, group="serial_console")
        self.flags(enabled=False, group="vnc")
        self.flags(enabled=False, group="spice")

        self.start_service('conductor')
        self.start_service('scheduler')
        self.compute = self.start_service('compute', host='test_compute1')

        self.useFixture(nova_fixtures.CastAsCallFixture(self))

        self.image_id = self.api.get_images()[0]['id']
        self.flavor_id = self.api.get_flavors()[0]['id']

    @mock.patch.object(fakelibvirt.Domain, 'undefine')
    @mock.patch('nova.virt.libvirt.LibvirtDriver.get_volume_connector')
    @mock.patch('nova.virt.libvirt.guest.Guest.get_job_info')
    @mock.patch.object(fakelibvirt.Domain, 'migrateToURI3')
    @mock.patch('nova.virt.libvirt.host.Host.get_connection')
    @mock.patch('nova.virt.disk.api.get_disk_size', return_value=1024)
    @mock.patch('os.path.getsize', return_value=1024)
    @mock.patch('nova.conductor.tasks.live_migrate.LiveMigrationTask.'
                '_check_destination_is_not_source', return_value=False)
    @mock.patch('nova.virt.libvirt.LibvirtDriver._create_image',
                return_value=(False, False))
    @mock.patch('nova.virt.libvirt.LibvirtDriver._get_local_gb_info',
                return_value={'total': 128,
                              'used': 44,
                              'free': 84})
    @mock.patch('nova.virt.libvirt.driver.libvirt_utils.is_valid_hostname',
                return_value=True)
    @mock.patch('nova.virt.libvirt.driver.libvirt_utils.file_open',
                side_effect=[io.BytesIO(b''), io.BytesIO(b'')])
    def test_serial_console_live_migrate(self, mock_file_open,
                                         mock_valid_hostname,
                                         mock_get_fs_info,
                                         mock_create_image,
                                         mock_conductor_source_check,
                                         mock_path_get_size,
                                         mock_get_disk_size,
                                         mock_host_get_connection,
                                         mock_migrate_to_uri,
                                         mock_get_job_info,
                                         mock_get_volume_connector,
                                         mock_undefine):
        """Regression test for bug #1595962.

        If the graphical consoles VNC and SPICE are disabled, the
        live-migration of an instance will result in an ERROR state.
        VNC and SPICE are usually disabled on IBM z systems platforms
        where graphical consoles are not available. The serial console
        is then enabled and VNC + SPICE are disabled.

        The error will be raised at
            https://opendev.org/openstack/nova/src/commit/
            4f33047d07f5a11b208c344fe206aba01cd8e6fe/
            nova/virt/libvirt/driver.py#L5842-L5852
        """
        mock_get_job_info.return_value = libvirt_guest.JobInfo(
                    type=fakelibvirt.VIR_DOMAIN_JOB_COMPLETED)
        fake_connection = fakelibvirt.Connection('qemu:///system',
                                version=fakelibvirt.FAKE_LIBVIRT_VERSION,
                                hv_version=fakelibvirt.FAKE_QEMU_VERSION)
        mock_host_get_connection.return_value = fake_connection
        # We invoke cleanup on source host first which will call undefine
        # method currently. Since in functional test we make all compute
        # services linked to the same connection, we need to mock the undefine
        # method to avoid triggering 'Domain not found' error in subsequent
        # rpc call post_live_migration_at_destination.
        mock_undefine.return_value = True

        server_attr = dict(name='server1',
                           imageRef=self.image_id,
                           flavorRef=self.flavor_id)
        server = self.api.post_server({'server': server_attr})
        server_id = server['id']
        self.wait_till_active_or_timeout(server_id)

        post = {"os-migrateLive": {
                    "block_migration": False,
                    "disk_over_commit": False,
                    "host": "test_compute1"
                }}

        try:
            # This should succeed
            self.admin_api.post_server_action(server_id, post)
            self.wait_till_active_or_timeout(server_id)
        except Exception as ex:
            self.fail(ex.response.content)

    def wait_till_active_or_timeout(self, server_id):
        timeout = 0.0
        server = self.api.get_server(server_id)
        while server['status'] != "ACTIVE" and timeout < 10.0:
            time.sleep(.1)
            timeout += .1
            server = self.api.get_server(server_id)
        if server['status'] != "ACTIVE":
            self.fail("The server is not active after the timeout.")