# Copyright 2017 Ericsson
#
# 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 import context
from nova import objects
from nova import test
from nova.tests import fixtures as nova_fixtures
from nova.tests.functional import fixtures as func_fixtures
from nova.tests.functional import integrated_helpers


class IgnoreDeletedServerGroupsTest(
    test.TestCase, integrated_helpers.InstanceHelperMixin,
):
    """Regression test for bug 1890244

    If instance are created as member of server groups it
    should be possible to evacuate them if the server groups are
    deleted prior to the host failure.
    """

    def setUp(self):
        super().setUp()
        # Stub out external dependencies.
        self.useFixture(nova_fixtures.NeutronFixture(self))
        self.useFixture(nova_fixtures.GlanceFixture(self))
        self.useFixture(func_fixtures.PlacementFixture())
        # Start nova controller services.
        api_fixture = self.useFixture(nova_fixtures.OSAPIFixture(
            api_version='v2.1'))
        self.api = api_fixture.admin_api
        self.start_service('conductor')
        # Use a custom weigher to make sure that we have a predictable
        # scheduling sort order.
        self.useFixture(nova_fixtures.HostNameWeigherFixture())
        self.start_service('scheduler')
        # Start two computes, one where the server will be created and another
        # where we'll evacuate it to.
        self.src = self._start_compute('host1')
        self.dest = self._start_compute('host2')
        self.notifier = self.useFixture(
            nova_fixtures.NotificationFixture(self)
        )

    def test_evacuate_after_group_delete(self):
        # Create an anti-affinity group for the server.
        body = {
            'server_group': {
                'name': 'test-group',
                'policies': ['anti-affinity']
            }
        }
        group_id = self.api.api_post(
            '/os-server-groups', body).body['server_group']['id']

        # Create a server in the group which should land on host1 due to our
        # custom weigher.
        body = {'server': self._build_server()}
        body['os:scheduler_hints'] = {'group': group_id}
        server = self.api.post_server(body)
        server = self._wait_for_state_change(server, 'ACTIVE')
        self.assertEqual('host1', server['OS-EXT-SRV-ATTR:host'])

        # Down the source compute to enable the evacuation
        self.api.microversion = '2.11'     # Cap for the force-down call.
        self.api.force_down_service('host1', 'nova-compute', True)
        self.api.microversion = 'latest'
        self.src.stop()

        # assert the server currently has a server group
        reqspec = objects.RequestSpec.get_by_instance_uuid(
            context.get_admin_context(), server['id'])
        self.assertIsNotNone(reqspec.instance_group)
        self.assertIn('group', reqspec.scheduler_hints)
        # then delete it so that we need to clean it up on evac
        self.api.api_delete(f'/os-server-groups/{group_id}')

        # Initiate evacuation
        server = self._evacuate_server(
            server, expected_host='host2', expected_migration_status='done'
        )
        reqspec = objects.RequestSpec.get_by_instance_uuid(
            context.get_admin_context(), server['id'])
        self.assertIsNone(reqspec.instance_group)
        self.assertNotIn('group', reqspec.scheduler_hints)
