# Copyright (c) 2013 Rackspace Hosting
# 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.
"""
Tests For CellStateManager
"""

import time

import mock
from oslo_config import cfg
from oslo_db import exception as db_exc
import six

from nova.cells import state
from nova import db
from nova.db.sqlalchemy import models
from nova import exception
from nova import objects
from nova import test
from nova import utils

FAKE_COMPUTES = [
    ('host1', 1024, 100, 0, 0),
    ('host2', 1024, 100, -1, -1),
    ('host3', 1024, 100, 1024, 100),
    ('host4', 1024, 100, 300, 30),
]

FAKE_COMPUTES_N_TO_ONE = [
    ('host1', 1024, 100, 0, 0),
    ('host1', 1024, 100, -1, -1),
    ('host2', 1024, 100, 1024, 100),
    ('host2', 1024, 100, 300, 30),
]

# NOTE(alaski): It's important to have multiple types that end up having the
# same memory and disk requirements.  So two types need the same first value,
# and two need the second and third values to add up to the same thing.
FAKE_ITYPES = [
    (0, 0, 0),
    (50, 12, 13),
    (50, 2, 4),
    (10, 20, 5),
]


def _create_fake_node(host, total_mem, total_disk, free_mem, free_disk):
    return objects.ComputeNode(host=host,
                               memory_mb=total_mem,
                               local_gb=total_disk,
                               free_ram_mb=free_mem,
                               free_disk_gb=free_disk)


@classmethod
def _fake_service_get_all_by_binary(cls, context, binary):
    def _node(host, total_mem, total_disk, free_mem, free_disk):
        return objects.Service(host=host, disabled=False)

    return [_node(*fake) for fake in FAKE_COMPUTES]


@classmethod
def _fake_compute_node_get_all(cls, context):
    return [_create_fake_node(*fake) for fake in FAKE_COMPUTES]


@classmethod
def _fake_compute_node_n_to_one_get_all(cls, context):
    return [_create_fake_node(*fake) for fake in FAKE_COMPUTES_N_TO_ONE]


def _fake_cell_get_all(context):
    return []


def _fake_instance_type_all(context):
    def _type(mem, root, eph):
        return {'root_gb': root,
                'ephemeral_gb': eph,
                'memory_mb': mem}

    return [_type(*fake) for fake in FAKE_ITYPES]


class TestCellsStateManager(test.NoDBTestCase):

    def setUp(self):
        super(TestCellsStateManager, self).setUp()

        self.stubs.Set(objects.ComputeNodeList, 'get_all',
                       _fake_compute_node_get_all)
        self.stubs.Set(objects.ServiceList, 'get_by_binary',
                       _fake_service_get_all_by_binary)
        self.stubs.Set(db, 'flavor_get_all', _fake_instance_type_all)
        self.stubs.Set(db, 'cell_get_all', _fake_cell_get_all)

    def test_cells_config_not_found(self):
        self.flags(cells_config='no_such_file_exists.conf', group='cells')
        e = self.assertRaises(cfg.ConfigFilesNotFoundError,
                              state.CellStateManager)
        self.assertEqual(['no_such_file_exists.conf'], e.config_files)

    @mock.patch.object(cfg.ConfigOpts, 'find_file')
    @mock.patch.object(utils, 'read_cached_file')
    def test_filemanager_returned(self, mock_read_cached_file, mock_find_file):
        mock_find_file.return_value = "/etc/nova/cells.json"
        mock_read_cached_file.return_value = (False, six.StringIO({}))
        self.flags(cells_config='cells.json', group='cells')
        manager = state.CellStateManager()
        self.assertIsInstance(manager,
                              state.CellStateManagerFile)
        self.assertRaises(exception.CellsUpdateUnsupported,
                          manager.cell_create, None, None)
        self.assertRaises(exception.CellsUpdateUnsupported,