# Copyright 2007-2017 UShareSoft SAS, 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 unittest

import pyxb
from mock import patch
from uforge.application import Api
from uforge.objects import uforge

from hammr.utils import constants
from hammr.utils import migration_utils


class TestMigrationTable(unittest.TestCase):
    @patch('texttable.Texttable.add_row')
    def test_migration_table_add_all_migration_in_progress_in_the_table(self, mock_table_add_row):
        # given
        migrations = uforge.migrations()
        migrations.migrations = pyxb.BIND()
        migration1 = self.create_migration(1, "a migration", 50, "In Progress", False, False, False)
        migrations.migrations.append(migration1)
        migration2 = self.create_migration(2, "a second migration", 55, "In Progress", False, False, False)
        migrations.migrations.append(migration2)

        # when
        migration_utils.migration_table(migrations.migrations.migration)

        # then
        self.assertEquals(mock_table_add_row.call_count, 2)
        mock_table_add_row.assert_any_call([1, "a migration", "In Progress (50%)"])
        mock_table_add_row.assert_any_call([2, "a second migration", "In Progress (55%)"])

    @patch('texttable.Texttable.add_row')
    def test_migration_table_add_the_migration_done_in_the_table(self, mock_table_add_row):
        # given
        migrations = uforge.migrations()
        migrations.migrations = pyxb.BIND()
        migration1 = self.create_migration(1, "a migration", 50, "Done", True, False, False)
        migrations.migrations.append(migration1)

        # when
        migration_utils.migration_table(migrations.migrations.migration)

        # then
        self.assertEquals(mock_table_add_row.call_count, 1)
        mock_table_add_row.assert_any_call([1, "a migration", "Done"])

    @patch('texttable.Texttable.add_row')
    def test_migration_table_add_the_migration_failed_in_the_table(self, mock_table_add_row):
        # given
        migrations = uforge.migrations()
        migrations.migrations = pyxb.BIND()
        migration1 = self.create_migration(1, "a migration", 50, "Failed", False, True, False)
        migrations.migrations.append(migration1)

        # when
        migration_utils.migration_table(migrations.migrations.migration)

        # then
        self.assertEquals(mock_table_add_row.call_count, 1)
        mock_table_add_row.assert_any_call([1, "a migration", "Failed"])

    @patch('texttable.Texttable.add_row')
    def test_migration_table_add_the_migration_cancelled_in_the_table(self, mock_table_add_row):
        # given
        migrations = uforge.migrations()
        migrations.migrations = pyxb.BIND()
        migration1 = self.create_migration(1, "a migration", 50, "Cancelled", False, False, True)
        migrations.migrations.append(migration1)

        # when
        migration_utils.migration_table(migrations.migrations.migration)

        # then
        self.assertEquals(mock_table_add_row.call_count, 1)
        mock_table_add_row.assert_any_call([1, "a migration", "Cancelled"])

    @patch("ussclicore.utils.generics_utils.get_file")
    def test_retrieve_migration_configuration_raise_exception_if_no_file_is_retrieved(self, mock_get_file):
        # given
        args_file = "file_no_present.json"
        mock_get_file.return_value = None

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.retrieve_migration_configuration(args_file)

        # then
        self.assertTrue("No such file or directory: " + args_file in e.exception)

    @patch("ussclicore.utils.generics_utils.get_file")
    @patch("hammr.utils.hammr_utils.load_data")
    def test_retrieve_migration_configuration_raise_exception_if_file_contain_no_migration(self, mock_load_data, mock_get_file):
        # given
        args_file = "file_present.json"
        mock_get_file.return_value = "a file"
        mock_load_data.return_value = self.get_migration_config(migration_key="noMigration")

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.retrieve_migration_configuration(args_file)

        # then
        self.assertTrue("no migration section found" in e.exception)

    @patch("ussclicore.utils.generics_utils.get_file")
    @patch("hammr.utils.hammr_utils.load_data")
    @patch("hammr.utils.migration_utils.check_mandatory_migrate")
    def test_retrieve_migration_configuration_check_mandatory_migrate_if_file_contain_migration(self, mock_check_mandatory_migrate, mock_load_data, mock_get_file):
        # given
        args_file = "file_present.json"
        mock_get_file.return_value = "a file"
        data = self.get_migration_config()
        mock_load_data.return_value = data

        # when
        migration_utils.retrieve_migration_configuration(args_file)

        #  then
        mock_check_mandatory_migrate.assert_called_with(data["migration"])

    def test_check_mandatory_migrate_raise_exception_if_not_contain_name(self):
        # given
        data = self.get_migration_config(name_key="noName")

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.check_mandatory_migrate(data["migration"])

        # then
        self.assertTrue("check yours parameters in file, no attribute [name] for [migration]" in e.exception)

    def test_check_mandatory_migrate_raise_exception_if_not_contain_os(self):
        # given
        data = self.get_migration_config(os_key="noOS")

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.check_mandatory_migrate(data["migration"])

        # then
        self.assertTrue("check yours parameters in file, no attribute [os] for [migration]" in e.exception)

    def test_check_mandatory_migrate_raise_exception_if_os_value_not_valid(self):
        # given
        data = self.get_migration_config(os_value="Windows")

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.check_mandatory_migrate(data["migration"])

        # then
        self.assertTrue("check yours parameters in file, attribute [os] for [migration] is not correct. Only 'linux' is supported" in e.exception)

    def test_check_mandatory_migrate_raise_exception_if_not_contain_source(self):
        # given
        data = self.get_migration_config(source_key="noSource")

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.check_mandatory_migrate(data["migration"])

        # then
        self.assertTrue("check yours parameters in file, no attribute [source] for [migration]" in e.exception)

    def test_check_mandatory_migrate_raise_exception_if_not_contain_target(self):
        # given
        data = self.get_migration_config(target_key="noTarget")

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.check_mandatory_migrate(data["migration"])

        # then
        self.assertTrue("check yours parameters in file, no attribute [target] for [migration]" in e.exception)

    @patch("hammr.utils.migration_utils.check_mandatory_target")
    @patch("hammr.utils.migration_utils.check_mandatory_source")
    def test_check_mandatory_migrate_check_mandatory_source_target_if_contain_source_target(self, mock_check_mandatory_source, mock_check_mandatory_target):
        # given
        data = self.get_migration_config()

        # when
        migration_utils.check_mandatory_migrate(data["migration"])

        #  then
        mock_check_mandatory_source.assert_called_with(data["migration"]["source"])
        mock_check_mandatory_target.assert_called_with(data["migration"]["target"])

    def test_check_mandatory_source_raise_exception_if_not_contain_host(self):
        # given
        data = self.get_migration_config(host_key="noHost")

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.check_mandatory_source(data["migration"]["source"])

        # then
        self.assertTrue("check yours parameters in file, no attribute [host] for [migration][source]" in e.exception)

    def test_check_mandatory_source_raise_exception_if_not_contain_user(self):
        # given
        data = self.get_migration_config(user_key="noUser")

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.check_mandatory_source(data["migration"]["source"])

        # then
        self.assertTrue("check yours parameters in file, no attribute [user] for [migration][source]" in e.exception)

    def test_check_mandatory_target_raise_exception_if_not_contain_builder(self):
        # given
        data = self.get_migration_config(builder_key="noBuilder")

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.check_mandatory_target(data["migration"]["target"])

        # then
        self.assertTrue("check yours parameters in file, no attribute [builder] for [migration][target]" in e.exception)

    @patch("hammr.utils.migration_utils.check_mandatory_builder")
    def test_check_mandatory_target_check_mandatory_builder_if_contain_builder(self, mock_check_mandatory_builder):
        # given
        data = self.get_migration_config()

        # when
        migration_utils.check_mandatory_target(data["migration"]["target"])

        #  then
        mock_check_mandatory_builder.assert_called_with(data["migration"]["target"]["builder"])

    def test_check_mandatory_builder_raise_exception_if_not_contain_type(self):
        # given
        data = self.get_migration_config(type_key="noType")

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.check_mandatory_builder(data["migration"]["target"]["builder"])

        # then
        self.assertTrue("check yours parameters in file, no attribute [type] for [migration][target][builder]" in e.exception)

    def test_check_mandatory_builder_raise_exception_if_not_contain_account(self):
        # given
        data = self.get_migration_config(account_key="noAccount")

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.check_mandatory_builder(data["migration"]["target"]["builder"])

        # then
        self.assertTrue("check yours parameters in file, no attribute [account] for [migration][target][builder]" in e.exception)

    @patch("hammr.utils.migration_utils.check_mandatory_account")
    def test_check_mandatory_builder_check_mandatory_account_if_contain_account(self, mock_check_mandatory_account):
        # given
        data = self.get_migration_config()

        # when
        migration_utils.check_mandatory_builder(data["migration"]["target"]["builder"])

        #  then
        mock_check_mandatory_account.assert_called_with(data["migration"]["target"]["builder"]["account"])

    def test_check_mandatory_account_raise_exception_if_not_contain_name(self):
        # given
        data = self.get_migration_config(account_name_key="noAccountName")

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.check_mandatory_account(data["migration"]["target"]["builder"]["account"])

        # then
        self.assertTrue("check yours parameters in file, no attribute [name] for [migration][target][builder][account]" in e.exception)

    @patch("hammr.utils.generate_utils.get_target_format_object")
    def test_retrieve_target_format_return_the_target_format_found(self, mock_get_target_format_object):
        # given
        api = Api("url", username="username", password="password", headers=None,
                  disable_ssl_certificate_validation=False, timeout=constants.HTTP_TIMEOUT)
        target_format = uforge.TargetFormat()
        mock_get_target_format_object.return_value = target_format


        # when
        target_format_retrieved = migration_utils.retrieve_target_format(api, "login", "targetFormatName")

        # then
        self.assertEqual(target_format_retrieved, target_format)

    @patch("hammr.utils.generate_utils.get_target_format_object")
    def test_retrieve_target_format_raise_exception_when_the_target_format_not_found(self, mock_get_target_format_object):
        # given
        api = Api("url", username="username", password="password", headers=None,
                  disable_ssl_certificate_validation=False, timeout=constants.HTTP_TIMEOUT)
        mock_get_target_format_object.return_value = None

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.retrieve_target_format(api, "login", "targetFormatName")

        # then
        self.assertTrue("TargetFormat type unknown: targetFormatName" in e.exception)

    def test_retrieve_image_return_the_image_created(self):
        # given
        api = Api("url", username="username", password="password", headers=None,
                    disable_ssl_certificate_validation=False, timeout=constants.HTTP_TIMEOUT)
        target_format = uforge.TargetFormat()
        image_format = uforge.ImageFormat()
        image_format.name = "vcenter"
        target_format.format = image_format

        builder = {
            "hardwareSettings": {
                "memory": 512,
                "hwType": 4
            }
        }

        # when
        image_retrieved = migration_utils.retrieve_image(builder, target_format, api, "login")

        # then
        self.assertEqual(image_retrieved.installProfile.memorySize, 512)
        self.assertEqual(image_retrieved.installProfile.hwType, "4")
        self.assertFalse(image_retrieved.compress)

    @patch("ussclicore.utils.generics_utils.remove_special_chars")
    def test_retrieve_image_raise_exception_when_format_name_not_found(self, mock_remove_special_chars):
        # given
        api = Api("url", username="username", password="password", headers=None,
                  disable_ssl_certificate_validation=False, timeout=constants.HTTP_TIMEOUT)
        target_format = uforge.TargetFormat()
        image_format = uforge.ImageFormat()
        image_format.name = "vcenter"
        target_format.format = image_format

        builder = {
            "hardwareSettings": {
                "memory": 512,
                "hwType": 4
            }
        }

        mock_remove_special_chars.return_value = "vcenternotfound"

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.retrieve_image(builder, target_format, api, "login")

        # then
        self.assertTrue("TargetFormat type is unsupported: vcenter" in e.exception)

    def test_check_mandatory_installation_raise_exception_when_no_installation_for_format_aws(self):
        # given
        install_profile = uforge.InstallProfile()
        install_profile.diskSize = 0

        builder = {
            "type": "Amazon AWS"
        }

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.check_mandatory_installation("aws", builder)

        # then
        self.assertTrue("check yours parameters in file, no attribute [installation] for [migration][target][builder], mandatory to migrate to [Amazon AWS]" in e.exception)

    def test_set_install_profile_disk_size_set_disk_size_when_format_aws(self):
        # given
        install_profile = uforge.InstallProfile()
        install_profile.diskSize = 0

        builder = {
            "installation": {
                "diskSize": 12
            }
        }

        # when
        install_profile = migration_utils.set_install_profile_disk_size(install_profile, builder, "aws")

        # then
        self.assertEqual(install_profile.diskSize, 12)

    def test_set_install_profile_disk_size_not_set_disk_size_when_format_vcenter(self):
        # given
        install_profile = uforge.InstallProfile()
        install_profile.diskSize = 0

        builder = {
            "installation": {
                "diskSize": 12
            }
        }

        # when
        install_profile = migration_utils.set_install_profile_disk_size(install_profile, builder, "vcenter")

        # then
        self.assertEqual(install_profile.diskSize, 0)

    def test_set_install_profile_disk_size_raise_exception_when_no_diskSize(self):
        # given
        install_profile = uforge.InstallProfile()
        install_profile.diskSize = 0

        builder = {
            "type": "Amazon AWS",
            "installation": {}
        }

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.set_install_profile_disk_size(install_profile, builder, "aws")

        # then
        self.assertTrue("check yours parameters in file, no attribute [disksize] for [migration][target][builder][installation], mandatory to migrate to [Amazon AWS]" in e.exception)

    def test_retrieve_publish_image_return_the_publish_image_created(self):
        # given
        target_format = uforge.TargetFormat()
        image_format = uforge.ImageFormat()
        image_format.name = "vcenter"
        target_format.format = image_format

        builder = {
            "displayName": "vcenter-vm-name",
            "esxHost": "esxhost_vcenter",
            "datastore": "datastore_vcenter",
            "network": "network_vcenter"
        }

        # when
        publish_image_retrieved = migration_utils.retrieve_publish_image(builder, target_format)

        # then
        self.assertEqual(publish_image_retrieved.displayName, builder["displayName"])
        self.assertEqual(publish_image_retrieved.esxHost, builder["esxHost"])
        self.assertEqual(publish_image_retrieved.datastore, builder["datastore"])
        self.assertEqual(publish_image_retrieved.network, builder["network"])

    @patch("ussclicore.utils.generics_utils.remove_special_chars")
    def test_retrieve_publish_image_raise_exception_when_format_name_not_found(self, mock_remove_special_chars):
        # given
        target_format = uforge.TargetFormat()
        image_format = uforge.ImageFormat()
        image_format.name = "vcenter"
        target_format.format = image_format

        builder = {
            "displayName": "vcenter-vm-name",
            "esxHost": "esxhost_vcenter",
            "datastore": "datastore_vcenter",
            "network": "network_vcenter"
        }

        mock_remove_special_chars.return_value = "vcenternotfound"

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.retrieve_publish_image(builder, target_format)

        # then
        self.assertTrue("TargetFormat type is unsupported: vcenter" in e.exception)

    @patch("uforge.application.Api._Users._Accounts.Getall")
    def test_retrieve_account_return_the_cred_account_found(self, mock_api_get_all):
        # given
        api = Api("url", username="username", password="password", headers=None, disable_ssl_certificate_validation=False, timeout=constants.HTTP_TIMEOUT)
        cred_account = uforge.CredAccountVSphere()
        cred_account.name = "accountName"
        cred_account.uri = "/uri/credAccount"
        cred_accounts = self.create_accounts(cred_account, "vsphere")
        mock_api_get_all.return_value = cred_accounts

        # when
        cred_account_retrieved = migration_utils.retrieve_account(api, "login", cred_account.name)

        # then
        self.assertEqual(cred_account_retrieved.name, cred_account.name)
        self.assertEqual(cred_account_retrieved.uri, cred_account.uri)

    @patch("uforge.application.Api._Users._Accounts.Getall")
    def test_retrieve_account_from_platform_raise_exception_when_no_accounts(self, mock_api_get_all):
        # given
        api = Api("url", username="username", password="password", headers=None,
                  disable_ssl_certificate_validation=False, timeout=constants.HTTP_TIMEOUT)
        accounts = uforge.CredAccounts()
        accounts.credAccounts = pyxb.BIND()
        mock_api_get_all.return_value = accounts

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.retrieve_account(api, "login", "account")

        # then
        self.assertTrue("No CredAccounts available.\n You can use the command 'hammr account create' to create an account." in e.exception)

    @patch("uforge.application.Api._Users._Accounts.Getall")
    def test_retrieve_account_from_platform_raise_exception_when_account_not_found(self, mock_api_get_all):
        # given
        api = Api("url", username="username", password="password", headers=None, disable_ssl_certificate_validation=False, timeout=constants.HTTP_TIMEOUT)
        cred_account = uforge.CredAccountVSphere()
        cred_account.name = "accountName"
        cred_account.uri = "/uri/credAccount"
        cred_accounts = self.create_accounts(cred_account, "vsphere")
        mock_api_get_all.return_value = cred_accounts

        # when
        with self.assertRaises(Exception) as e:
            migration_utils.retrieve_account(api, "login", "accountNotFound")

        # then
        self.assertTrue("CredAccount unknown: accountNotFound\n You can use the command 'hammr account create' to create an account." in e.exception)

    def create_migration(self, id, name, percentage, statusMessage, complete, error, cancelled):
        newMigration = uforge.migration()
        newMigration.dbId = id
        newMigration.name = name

        status = uforge.status()
        status.message = statusMessage
        status.percentage = percentage
        status.complete = complete
        status.error = error
        status.cancelled = cancelled

        newMigration.status = status
        return newMigration

    def create_accounts(self, account, target_platform_type):
        target_platform = uforge.TargetPlatform()
        target_platform.name = "targetPlatformName"
        target_platform.type = target_platform_type

        account.targetPlatform = target_platform

        accounts = uforge.CredAccounts()
        accounts.credAccounts = pyxb.BIND()
        accounts.credAccounts.append(account)
        return accounts

    def get_migration_config(self, migration_key="migration", name_key="name", os_key="os", os_value="linux",
                             source_key="source", host_key="host", user_key="user",
                             target_key="target", builder_key="builder", type_key="type", account_key="account", account_name_key="name"):
        migration_config = {
            migration_key: {
                name_key: "myMigration",
                os_key: os_value,
                source_key: {
                    host_key: "127.0.0.1",
                    user_key: "root",
                },
                target_key: {
                    builder_key: {
                        type_key: "VMware vCenter format",
                        account_key: {
                            account_name_key: "credAccountTest"
                        }
                    }
                }
            }
        }
        return migration_config
