# 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.

from nova.compute import manager as compute_manager
from nova.tests.functional import integrated_helpers


class TestServerResizeReschedule(integrated_helpers.ProviderUsageBaseTestCase):
    """Regression test for bug #1741125

    During testing in the alternate host series, it was found that retries
    when resizing an instance would always fail. This turned out to be true
    even before alternate hosts for resize was introduced. Further
    investigation showed that there was a race in call to retry the resize
    and the revert of the original attempt.

    This adds a functional regression test to show the failure. A follow up
    patch with the fix will modify the test to show it passing again.
    """

    compute_driver = 'fake.SmallFakeDriver'

    def setUp(self):
        super(TestServerResizeReschedule, self).setUp()
        self.compute1 = self._start_compute(host='host1')
        self.compute2 = self._start_compute(host='host2')
        self.compute3 = self._start_compute(host='host3')
        self.compute4 = self._start_compute(host='host4')

        flavors = self.api.get_flavors()
        self.flavor1 = flavors[0]
        self.flavor2 = flavors[1]
        if self.flavor1["disk"] > self.flavor2["disk"]:
            # Make sure that flavor1 is smaller
            self.flavor1, self.flavor2 = self.flavor2, self.flavor1

    def test_resize_reschedule_uses_host_lists(self):
        """Test that when a resize attempt fails, the retry comes from the
        supplied host_list, and does not call the scheduler.
        """
        server_req = self._build_server(
            image_uuid='155d900f-4e14-4e4c-a73d-069cbf4541e6',
            flavor_id=self.flavor1['id'],
            networks='none')

        self.first_attempt = True
        created_server = self.api.post_server({'server': server_req})
        server = self._wait_for_state_change(created_server,
                'ACTIVE')

        actual_prep_resize = compute_manager.ComputeManager._prep_resize

        def fake_prep_resize(*args, **kwargs):
            if self.first_attempt:
                # Only fail the first time
                self.first_attempt = False
                raise Exception('fake_prep_resize')
            actual_prep_resize(*args, **kwargs)

        # Yes this isn't great in a functional test, but it's simple.
        self.stub_out('nova.compute.manager.ComputeManager._prep_resize',
                      fake_prep_resize)

        server_uuid = server["id"]
        data = {"resize": {"flavorRef": self.flavor2['id']}}
        self.api.post_server_action(server_uuid, data)

        server = self._wait_for_state_change(created_server,
                'VERIFY_RESIZE')
        self.assertEqual(self.flavor2['name'],
                         server['flavor']['original_name'])


class TestServerResizeRescheduleWithNoAllocations(
        TestServerResizeReschedule):
    """Tests the reschedule scenario using a scheduler driver which does not
    use Placement.
    """

    def setUp(self):
        super(TestServerResizeRescheduleWithNoAllocations, self).setUp()
        # We need to mock the FilterScheduler to not use Placement so that
        # allocations won't be created during scheduling.
        self.scheduler_service.manager.driver.USES_ALLOCATION_CANDIDATES = \
            False
