summaryrefslogtreecommitdiff
path: root/nova/tests/functional/regressions/test_bug_1888395.py
blob: 8f2e2a0eeb45a38873f3a4686a04cf19fec04c8d (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
154
155
156
157
# 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 fixtures

from lxml import etree
from urllib import parse as urlparse

from nova import context
from nova.network import constants as neutron_constants
from nova.network import neutron
from nova.tests import fixtures as nova_fixtures
from nova.tests.fixtures import libvirt as fakelibvirt
from nova.tests.functional.libvirt import base as libvirt_base


class TestLiveMigrationWithoutMultiplePortBindingsBase(
        libvirt_base.ServersTestBase):

    ADMIN_API = True
    microversion = 'latest'

    def list_extensions(self, *args, **kwargs):
        return {
            'extensions': [
                {
                    # Copied from neutron-lib portbindings.py
                    "updated": "2014-02-03T10:00:00-00:00",
                    "name": neutron_constants.PORT_BINDING,
                    "links": [],
                    "alias": "binding",
                    "description": "Expose port bindings of a virtual port to "
                                   "external application"
                }
            ]
        }

    def setUp(self):
        super().setUp()
        self.neutron.list_extensions = self.list_extensions
        self.neutron_api = neutron.API()

        self.useFixture(nova_fixtures.OSBrickFixture())

        self.start_compute(
            hostname='start_host',
            host_info=fakelibvirt.HostInfo(
                cpu_nodes=1, cpu_sockets=1, cpu_cores=4, cpu_threads=2))
        self.start_compute(
            hostname='end_host',
            host_info=fakelibvirt.HostInfo(
                cpu_nodes=1, cpu_sockets=1, cpu_cores=4, cpu_threads=2))

        self.ctxt = context.get_admin_context()
        # TODO(sean-k-mooney): remove this when it is part of ServersTestBase
        self.useFixture(fixtures.MonkeyPatch(
            'nova.tests.fixtures.libvirt.Domain.migrateToURI3',
            self._migrate_stub))


class TestLiveMigrationWithoutMultiplePortBindings(
        TestLiveMigrationWithoutMultiplePortBindingsBase):
    """Regression test for bug 1888395.

    This regression test asserts that Live migration works when
    neutron does not support the binding-extended api extension
    and the legacy single port binding workflow is used.
    """

    def _migrate_stub(self, domain, destination, params, flags):
        """Stub out migrateToURI3."""

        src_hostname = domain._connection.hostname
        dst_hostname = urlparse.urlparse(destination).netloc

        # In a real live migration, libvirt and QEMU on the source and
        # destination talk it out, resulting in the instance starting to exist
        # on the destination. Fakelibvirt cannot do that, so we have to
        # manually create the "incoming" instance on the destination
        # fakelibvirt.
        dst = self.computes[dst_hostname]
        dst.driver._host.get_connection().createXML(
            params['destination_xml'],
            'fake-createXML-doesnt-care-about-flags')

        src = self.computes[src_hostname]
        conn = src.driver._host.get_connection()

        # because migrateToURI3 is spawned in a background thread, this method
        # does not block the upper nova layers. Because we don't want nova to
        # think the live migration has finished until this method is done, the
        # last thing we do is make fakelibvirt's Domain.jobStats() return
        # VIR_DOMAIN_JOB_COMPLETED.
        server = etree.fromstring(
            params['destination_xml']
        ).find('./uuid').text
        dom = conn.lookupByUUIDString(server)
        dom.complete_job()

    def test_live_migrate(self):
        server = self._create_server(
            host='start_host',
            networks=[{'port': self.neutron.port_1['id']}])

        self.assertFalse(
            self.neutron_api.has_port_binding_extension(self.ctxt))
        # TODO(sean-k-mooney): extend _live_migrate to support passing a host
        self.api.post_server_action(
            server['id'],
            {
                'os-migrateLive': {
                    'host': 'end_host',
                    'block_migration': 'auto'
                }
            }
        )

        self._wait_for_server_parameter(
            server, {'OS-EXT-SRV-ATTR:host': 'end_host', 'status': 'ACTIVE'})
        msg = "NotImplementedError: Cannot load 'vif_type' in the base class"
        self.assertNotIn(msg, self.stdlog.logger.output)


class TestLiveMigrationRollbackWithoutMultiplePortBindings(
        TestLiveMigrationWithoutMultiplePortBindingsBase):

    def _migrate_stub(self, domain, destination, params, flags):
        source = self.computes['start_host']
        conn = source.driver._host.get_connection()
        dom = conn.lookupByUUIDString(self.server['id'])
        dom.fail_job()

    def test_live_migration_rollback(self):
        self.server = self._create_server(
            host='start_host',
            networks=[{'port': self.neutron.port_1['id']}])

        self.assertFalse(
            self.neutron_api.has_port_binding_extension(self.ctxt))
        # FIXME(artom) Until bug 1969980 is fixed, this will fail with a
        # NotImplementedError.
        self._live_migrate(self.server, migration_expected_state='error',
                           server_expected_state='ERROR')
        server = self.api.get_server(self.server['id'])
        self.assertIn(
            "NotImplementedError: Cannot load 'vifs' in the base class",
            server['fault']['details']
        )