# 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 os

import jsonschema
import mock
import netaddr
from oslotest import mockpatch

from rally.deploy.serverprovider.providers import virsh
from tests.unit import test


class VirshProviderTestCase(test.TestCase):
    def setUp(self):
        super(VirshProviderTestCase, self).setUp()
        self.deployment = mock.Mock()
        self.config = {
            "type": "VirshProvider",
            "connection": "user@host",
            "template_name": "prefix",
            "template_user": "user",
            "template_password": "password",
        }
        self.provider = virsh.VirshProvider(self.deployment, self.config)
        self.useFixture(mockpatch.PatchObject(self.provider, "resources"))

    @mock.patch(
        "rally.deploy.serverprovider.providers.virsh.netaddr.IPAddress")
    @mock.patch("rally.deploy.serverprovider.providers.virsh.subprocess")
    @mock.patch("time.sleep")
    def test_create_vm(self, mock_sleep, mock_subp, mock_ipaddress):
        mock_subp.check_output.return_value = "10.0.0.1"
        mock_ipaddress.return_value = "10.0.0.2"
        server = self.provider.create_vm("name")
        script_path = "%(virsh_path)s/virsh/get_domain_ip.sh" % dict(
                virsh_path=os.path.split(virsh.__file__)[0])
        mock_subp.assert_has_calls([
            mock.call.check_call("virt-clone --connect=qemu+ssh://user@host/"
                                 "system -o prefix -n name --auto-clone",
                                 shell=True),
            mock.call.check_call("virsh --connect=qemu+ssh://user@host/system "
                                 "start name", shell=True),
            mock.call.check_call("scp -o StrictHostKeyChecking=no  %s u"
                                 "ser@host:~/get_domain_ip.sh" % script_path,
                                 shell=True),
            mock.call.check_output("ssh -o StrictHostKeyChecking=no user@host "
                                   "./get_domain_ip.sh name", shell=True),
        ])
        mock_ipaddress.assert_called_once_with("10.0.0.1")
        self.assertEqual(server.host, "10.0.0.2")
        self.assertEqual(server.user, "user")
        self.assertIsNone(server.key)
        self.assertEqual(server.password, "password")
        self.provider.resources.create.assert_called_once_with({
            "name": "name",
        })

    @mock.patch(
        "rally.deploy.serverprovider.providers.virsh.netaddr.IPAddress")
    @mock.patch("rally.deploy.serverprovider.providers.virsh.subprocess")
    @mock.patch("time.sleep")
    def test_create_vm_ip_failed(self, mock_sleep, mock_subp, mock_ipaddress):
        mock_ipaddress.side_effect = netaddr.core.AddrFormatError
        server = self.provider.create_vm("name")
        mock_subp.assert_has_calls(3 * [
            mock.call.check_output("ssh -o StrictHostKeyChecking=no user@host "
                                   "./get_domain_ip.sh name", shell=True),
        ])
        self.assertEqual(server.host, "None")

    @mock.patch("rally.deploy.serverprovider.providers.virsh.subprocess")
    def test_destroy_vm(self, mock_subp):
        self.provider.destroy_vm("uuid")
        mock_subp.assert_has_calls([
            mock.call.check_call("virsh --connect=qemu+ssh://user@host/system "
                                 "destroy uuid", shell=True),
            mock.call.check_call("virsh --connect=qemu+ssh://user@host/system "
                                 "undefine uuid --remove-all-storage",
                                 shell=True),
        ])

    @mock.patch("rally.deploy.serverprovider.providers.virsh.uuid")
    @mock.patch.object(virsh.VirshProvider, "create_vm")
    def test_create_servers(self, mock_create, mock_uuid):
        mock_uuid.uuid4.side_effect = ["1", "2", "3"]
        mock_create.side_effect = ["s1", "s2", "s3"]
        servers = self.provider.create_servers(amount=3)
        self.assertEqual(servers, ["s1", "s2", "s3"])
        mock_create.assert_has_calls([
            mock.call("1"),
            mock.call("2"),
            mock.call("3"),
        ])

    @mock.patch.object(virsh.VirshProvider, "destroy_vm")
    def test_destroy_servers(self, mock_destroy):
        self.provider.resources.get_all.return_value = [
            {"info": {"name": "1"}},
            {"info": {"name": "2"}},
            {"info": {"name": "3"}},
        ]
        self.provider.destroy_servers()
        mock_destroy.assert_has_calls([
            mock.call("1"),
            mock.call("2"),
            mock.call("3"),
        ])
        self.provider.resources.get_all.assert_called_once_with()

    def test_invalid_config(self):
        self.config["type"] = 42
        self.assertRaises(jsonschema.ValidationError, virsh.VirshProvider,
                          self.deployment, self.config)

    def test_invalid_connection(self):
        self.config["connection"] = "user host"
        self.assertRaises(jsonschema.ValidationError, virsh.VirshProvider,
                          self.deployment, self.config)
