# Copyright 2012 OpenStack Foundation
# All Rights Reserved.
#
#    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 tempest.common import utils
from tempest.common import waiters
from tempest import config
from tempest.lib import decorators
from tempest.lib import exceptions
from tempest.scenario import manager

CONF = config.CONF


class TestServerMultinode(manager.ScenarioTest):
    """This is a set of tests specific to multinode testing."""
    credentials = ['primary', 'admin']

    @classmethod
    def skip_checks(cls):
        super(TestServerMultinode, cls).skip_checks()

        if CONF.compute.min_compute_nodes < 2:
            raise cls.skipException(
                "Less than 2 compute nodes, skipping multinode tests.")

    @decorators.idempotent_id('9cecbe35-b9d4-48da-a37e-7ce70aa43d30')
    @decorators.attr(type=['smoke', 'multinode'])
    @utils.services('compute', 'network')
    def test_schedule_to_all_nodes(self):
        available_zone = \
            self.os_admin.availability_zone_client.list_availability_zones(
                detail=True)['availabilityZoneInfo']
        hosts = []
        for zone in available_zone:
            if zone['zoneState']['available']:
                for host in zone['hosts']:
                    if 'nova-compute' in zone['hosts'][host] and \
                        zone['hosts'][host]['nova-compute']['available'] and \
                        CONF.compute.target_hosts_to_avoid not in host:
                        hosts.append({'zone': zone['zoneName'],
                                      'host_name': host})

        # ensure we have at least as many compute hosts as we expect
        if len(hosts) < CONF.compute.min_compute_nodes:
            raise exceptions.InvalidConfiguration(
                "Host list %s is shorter than min_compute_nodes. "
                "Did a compute worker not boot correctly?" % hosts)

        # create 1 compute for each node, up to the min_compute_nodes
        # threshold (so that things don't get crazy if you have 1000
        # compute nodes but set min to 3).
        servers = []
        host_server_ids = {}

        for host in hosts[:CONF.compute.min_compute_nodes]:
            # by getting to active state here, this means this has
            # landed on the host in question.
            # in order to use the availability_zone:host scheduler hint,
            # admin client is need here.
            inst = self.create_server(
                wait_until=None,
                clients=self.os_admin,
                availability_zone='%(zone)s:%(host_name)s' % host)
            host_server_ids[host['host_name']] = inst['id']

        for host_name, server_id in host_server_ids.items():
            waiters.wait_for_server_status(self.os_admin.servers_client,
                                           server_id, 'ACTIVE')
            server = self.os_admin.servers_client.show_server(
                server_id)['server']
            # ensure server is located on the requested host
            self.assertEqual(host_name, server['OS-EXT-SRV-ATTR:host'])
            servers.append(server)

        # make sure we really have the number of servers we think we should
        self.assertEqual(
            len(servers), CONF.compute.min_compute_nodes,
            "Incorrect number of servers built %s" % servers)

        # ensure that every server ended up on a different host
        host_ids = [x['hostId'] for x in servers]
        self.assertEqual(
            len(set(host_ids)), len(servers),
            "Incorrect number of distinct host_ids scheduled to %s" % servers)
