# Copyright 2013: Mirantis Inc.
# 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.

import ddt
import mock

from rally import exceptions as rally_exceptions
from rally.plugins.openstack.scenarios.nova import servers
from tests.unit import fakes
from tests.unit import test


NOVA_SERVERS_MODULE = "rally.plugins.openstack.scenarios.nova.servers"
NOVA_SERVERS = NOVA_SERVERS_MODULE + ".NovaServers"


@ddt.ddt
class NovaServersTestCase(test.ScenarioTestCase):

    def test_boot_rescue_unrescue(self):
        actions = [{"rescue_unrescue": 5}]
        fake_server = mock.MagicMock()
        scenario = servers.NovaServers(self.context)
        scenario._boot_server = mock.MagicMock(return_value=fake_server)
        scenario.generate_random_name = mock.MagicMock(return_value="name")
        scenario._rescue_server = mock.MagicMock()
        scenario._unrescue_server = mock.MagicMock()
        scenario._delete_server = mock.MagicMock()

        scenario.boot_and_bounce_server("img", 1, actions=actions)
        scenario._boot_server.assert_called_once_with("img", 1)
        server_calls = []
        for i in range(5):
            server_calls.append(mock.call(fake_server))
        self.assertEqual(5, scenario._rescue_server.call_count,
                         "Rescue not called 5 times")
        self.assertEqual(5, scenario._unrescue_server.call_count,
                         "Unrescue not called 5 times")
        scenario._rescue_server.assert_has_calls(server_calls)
        scenario._unrescue_server.assert_has_calls(server_calls)
        scenario._delete_server.assert_called_once_with(fake_server,
                                                        force=False)

    def test_boot_stop_start(self):
        actions = [{"stop_start": 5}]
        fake_server = mock.MagicMock()
        scenario = servers.NovaServers(self.context)
        scenario._boot_server = mock.MagicMock(return_value=fake_server)
        scenario.generate_random_name = mock.MagicMock(return_value="name")
        scenario._start_server = mock.MagicMock()
        scenario._stop_server = mock.MagicMock()
        scenario._delete_server = mock.MagicMock()

        scenario.boot_and_bounce_server("img", 1, actions=actions)

        scenario._boot_server.assert_called_once_with("img", 1)
        server_calls = []
        for i in range(5):
            server_calls.append(mock.call(fake_server))
        self.assertEqual(5, scenario._stop_server.call_count,
                         "Stop not called 5 times")
        self.assertEqual(5, scenario._start_server.call_count,
                         "Start not called 5 times")
        scenario._stop_server.assert_has_calls(server_calls)
        scenario._start_server.assert_has_calls(server_calls)
        scenario._delete_server.assert_called_once_with(fake_server,
                                                        force=False)

    def test_multiple_bounce_actions(self):
        actions = [{"hard_reboot": 5}, {"stop_start": 8}]
        fake_server = mock.MagicMock()
        scenario = servers.NovaServers(self.context)

        scenario._boot_server = mock.MagicMock(return_value=fake_server)
        scenario._delete_server = mock.MagicMock()
        scenario._reboot_server = mock.MagicMock()
        scenario._stop_and_start_server = mock.MagicMock()
        scenario.generate_random_name = mock.MagicMock(return_value="name")

        scenario.boot_and_bounce_server("img", 1, actions=actions)
        scenario._boot_server.assert_called_once_with("img", 1)
        server_calls = []
        for i in range(5):
            server_calls.append(mock.call(fake_server))
        self.assertEqual(5, scenario._reboot_server.call_count,
                         "Reboot not called 5 times")
        scenario._reboot_server.assert_has_calls(server_calls)
        server_calls = []
        for i in range(8):
            server_calls.append(mock.call(fake_server))
        self.assertEqual(8, scenario._stop_and_start_server.call_count,
                         "Stop/Start not called 8 times")
        scenario._stop_and_start_server.assert_has_calls(server_calls)
        scenario._delete_server.assert_called_once_with(fake_server,
                                                        force=False)

    def test_boot_lock_unlock_and_delete(self):
        server = fakes.FakeServer()
        image = fakes.FakeImage()
        flavor = fakes.FakeFlavor()

        scenario = servers.NovaServers(self.context)
        scenario._boot_server = mock.Mock(return_value=server)
        scenario._lock_server = mock.Mock(side_effect=lambda s: s.lock())
        scenario._unlock_server = mock.Mock(side_effect=lambda s: s.unlock())
        scenario._delete_server = mock.Mock(
            side_effect=lambda s, **kwargs:
                self.assertFalse(getattr(s, "OS-EXT-STS:locked", False)))

        scenario.boot_lock_unlock_and_delete(image, flavor, fakearg="fakearg")

        scenario._boot_server.assert_called_once_with(image, flavor,
                                                      fakearg="fakearg")
        scenario._lock_server.assert_called_once_with(server)
        scenario._unlock_server.assert_called_once_with(server)
        scenario._delete_server.assert_called_once_with(server, force=False)

    def test_validate_actions(self):
        actions = [{"hardd_reboot": 6}]
        scenario = servers.NovaServers(self.context)

        self.assertRaises(rally_exceptions.InvalidConfigException,
                          scenario.boot_and_bounce_server,
                          1, 1, actions=actions)
        actions = [{"hard_reboot": "no"}]
        self.assertRaises(rally_exceptions.InvalidConfigException,
                          scenario.boot_and_bounce_server,
                          1, 1, actions=actions)
        actions = {"hard_reboot": 6}
        self.assertRaises(rally_exceptions.InvalidConfigException,
                          scenario.boot_and_bounce_server,
                          1, 1, actions=actions)
        actions = {"hard_reboot": -1}
        self.assertRaises(rally_exceptions.InvalidConfigException,
                          scenario.boot_and_bounce_server,
                          1, 1, actions=actions)
        actions = {"hard_reboot": 0}
        self.assertRaises(rally_exceptions.InvalidConfigException,
                          scenario.boot_and_bounce_server,
                          1, 1, actions=actions)

    def _verify_reboot(self, soft=True):
        actions = [{"soft_reboot" if soft else "hard_reboot": 5}]
        fake_server = mock.MagicMock()
        scenario = servers.NovaServers(self.context)

        scenario._reboot_server = mock.MagicMock()
        scenario._soft_reboot_server = mock.MagicMock()
        scenario._boot_server = mock.MagicMock(return_value=fake_server)
        scenario._delete_server = mock.MagicMock()
        scenario.generate_random_name = mock.MagicMock(return_value="name")

        scenario.boot_and_bounce_server("img", 1, actions=actions)

        scenario._boot_server.assert_called_once_with("img", 1)
        server_calls = []
        for i in range(5):
            server_calls.append(mock.call(fake_server))
        if soft:
            self.assertEqual(5, scenario._soft_reboot_server.call_count,
                             "Reboot not called 5 times")
            scenario._soft_reboot_server.assert_has_calls(server_calls)
        else:
            self.assertEqual(5, scenario._reboot_server.call_count,
                             "Reboot not called 5 times")
            scenario._reboot_server.assert_has_calls(server_calls)
        scenario._delete_server.assert_called_once_with(fake_server,
                                                        force=False)

    def test_boot_soft_reboot(self):
        self._verify_reboot(soft=True)

    def test_boot_hard_reboot(self):
        self._verify_reboot(soft=False)

    def test_boot_and_delete_server(self):
        fake_server = object()

        scenario = servers.NovaServers(self.context)
        scenario.generate_random_name = mock.MagicMock(return_value="name")
        scenario._boot_server = mock.MagicMock(return_value=fake_server)
        scenario._delete_server = mock.MagicMock()
        scenario.sleep_between = mock.MagicMock()

        scenario.boot_and_delete_server("img", 0, 10, 20, fakearg="fakearg")

        scenario._boot_server.assert_called_once_with("img", 0,
                                                      fakearg="fakearg")
        scenario.sleep_between.assert_called_once_with(10, 20)
        scenario._delete_server.assert_called_once_with(fake_server,
                                                        force=False)

    def test_boot_and_delete_multiple_servers(self):
        scenario = servers.NovaServers(self.context)
        scenario._boot_servers = mock.Mock()
        scenario._delete_servers = mock.Mock()
        scenario.sleep_between = mock.Mock()

        scenario.boot_and_delete_multiple_servers("img", "flavor", count=15,
                                                  min_sleep=10,
                                                  max_sleep=20,
                                                  fakearg="fakearg")

        scenario._boot_servers.assert_called_once_with("img", "flavor", 1,
                                                       instances_amount=15,
                                                       fakearg="fakearg")
        scenario.sleep_between.assert_called_once_with(10, 20)
        scenario._delete_servers.assert_called_once_with(
            scenario._boot_servers.return_value, force=False)

    def test_boot_and_list_server(self):
        scenario = servers.NovaServers(self.context)
        scenario.generate_random_name = mock.MagicMock(return_value="name")
        scenario._boot_server = mock.MagicMock()
        scenario._list_servers = mock.MagicMock()

        scenario.boot_and_list_server("img", 0, fakearg="fakearg")

        scenario._boot_server.assert_called_once_with("img", 0,
                                                      fakearg="fakearg")
        scenario._list_servers.assert_called_once_with(True)

    def test_suspend_and_resume_server(self):
        fake_server = object()

        scenario = servers.NovaServers(self.context)
        scenario.generate_random_name = mock.MagicMock(return_value="name")
        scenario._boot_server = mock.MagicMock(return_value=fake_server)
        scenario._suspend_server = mock.MagicMock()
        scenario._resume_server = mock.MagicMock()
        scenario._delete_server = mock.MagicMock()

        scenario.suspend_and_resume_server("img", 0, fakearg="fakearg")

        scenario._boot_server.assert_called_once_with("img", 0,
                                                      fakearg="fakearg")

        scenario._suspend_server.assert_called_once_with(fake_server)
        scenario._resume_server.assert_called_once_with(fake_server)
        scenario._delete_server.assert_called_once_with(fake_server,
                                                        force=False)

    def test_pause_and_unpause_server(self):
        fake_server = object()

        scenario = servers.NovaServers(self.context)
        scenario.generate_random_name = mock.MagicMock(return_value="name")