# Copyright 2016 Canonical Ltd
# 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 mock
from nova import context
from nova import test
from nova.tests.unit import fake_instance

from nova.virt.lxd import storage


class TestAttachEphemeral(test.NoDBTestCase):
    """Tests for nova.virt.lxd.storage.attach_ephemeral."""

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

        self.patchers = []

        CONF_patcher = mock.patch('nova.virt.lxd.common.conf.CONF')
        self.patchers.append(CONF_patcher)
        self.CONF = CONF_patcher.start()
        self.CONF.instances_path = '/i'
        self.CONF.lxd.root_dir = '/var/lib/lxd'

    def tearDown(self):
        super(TestAttachEphemeral, self).tearDown()
        for patcher in self.patchers:
            patcher.stop()

    @mock.patch.object(storage.utils, 'execute')
    @mock.patch(
        'nova.virt.lxd.storage.driver.block_device_info_get_ephemerals')
    def test_add_ephemerals_with_zfs(
            self, block_device_info_get_ephemerals, execute):
        ctx = context.get_admin_context()
        block_device_info_get_ephemerals.return_value = [
            {'virtual_name': 'ephemerals0'}]
        instance = fake_instance.fake_instance_obj(
            ctx, name='test', memory_mb=0)
        block_device_info = mock.Mock()
        lxd_config = {'environment': {'storage': 'zfs'},
                      'config': {'storage.zfs_pool_name': 'zfs'}}

        container = mock.Mock()
        container.config = {
            'volatile.last_state.idmap': '[{"Isuid":true,"Isgid":false,'
            '"Hostid":165536,"Nsid":0,'
            '"Maprange":65536}]'
        }
        client = mock.Mock()
        client.containers.get.return_value = container

        storage.attach_ephemeral(
            client, block_device_info, lxd_config, instance)

        block_device_info_get_ephemerals.assert_called_once_with(
            block_device_info)

        expected_calls = [
            mock.call(
                'zfs', 'create', '-o',
                'mountpoint=/i/instance-00000001/storage/ephemerals0', '-o',
                'quota=0G', 'zfs/instance-00000001-ephemeral',
                run_as_root=True),
            mock.call(
                'chown', '165536', '/i/instance-00000001/storage/ephemerals0',
                run_as_root=True)
        ]

        self.assertEqual(expected_calls, execute.call_args_list)

    @mock.patch.object(storage.utils, 'execute')
    @mock.patch(
        'nova.virt.lxd.storage.driver.block_device_info_get_ephemerals')
    def test_add_ephemerals_with_btrfs(
            self, block_device_info_get_ephemerals, execute):
        ctx = context.get_admin_context()
        block_device_info_get_ephemerals.return_value = [
            {'virtual_name': 'ephemerals0'}]
        instance = fake_instance.fake_instance_obj(
            ctx, name='test', memory_mb=0)
        instance.ephemeral_gb = 1
        block_device_info = mock.Mock()
        lxd_config = {'environment': {'storage': 'btrfs'}}
        profile = mock.Mock()
        profile.devices = {
            'root': {
                'path': '/',
                'type': 'disk',
                'size': '1G'
            },
            'ephemerals0': {
                'optional': 'True',
                'path': '/mnt',
                'source': '/path/fake_path',
                'type': 'disk'

            }
        }
        client = mock.Mock()
        client.profiles.get.return_value = profile

        container = mock.Mock()
        container.config = {
            'volatile.last_state.idmap': '[{"Isuid":true,"Isgid":false,'
            '"Hostid":165536,"Nsid":0,'
            '"Maprange":65536}]'
        }
        client.containers.get.return_value = container

        storage.attach_ephemeral(
            client, block_device_info, lxd_config, instance)

        block_device_info_get_ephemerals.assert_called_once_with(
            block_device_info)
        profile.save.assert_called_once_with()

        expected_calls = [
            mock.call(
                'btrfs', 'subvolume', 'create',
                '/var/lib/lxd/containers/instance-00000001/ephemerals0',
                run_as_root=True),
            mock.call(
                'btrfs', 'qgroup', 'limit', '1g',
                '/var/lib/lxd/containers/instance-00000001/ephemerals0',
                run_as_root=True),
            mock.call(
                'chown', '165536',
                '/var/lib/lxd/containers/instance-00000001/ephemerals0',
                run_as_root=True)
        ]
        self.assertEqual(expected_calls, execute.call_args_list)
        self.assertEqual(
            profile.devices['ephemerals0']['source'],
            '/var/lib/lxd/containers/instance-00000001/ephemerals0')

    @mock.patch.object(storage.utils, 'execute')
    @mock.patch(
        'nova.virt.lxd.storage.driver.block_device_info_get_ephemerals')
    def test_ephemeral_with_lvm(
            self, block_device_info_get_ephemerals, execute):
        ctx = context.get_admin_context()
        block_device_info_get_ephemerals.return_value = [
            {'virtual_name': 'ephemerals0'}]
        instance = fake_instance.fake_instance_obj(
            ctx, name='test', memory_mb=0)
        block_device_info = mock.Mock()
        lxd_config = {'environment': {'storage': 'lvm'},
                      'config': {'storage.lvm_vg_name': 'lxd'}}

        storage.fileutils = mock.Mock()

        container = mock.Mock()
        container.config = {
            'volatile.last_state.idmap': '[{"Isuid":true,"Isgid":false,'
            '"Hostid":165536,"Nsid":0,'
            '"Maprange":65536}]'
        }
        client = mock.Mock()
        client.containers.get.return_value = container

        storage.attach_ephemeral(
            client, block_device_info, lxd_config, instance)

        block_device_info_get_ephemerals.assert_called_once_with(
            block_device_info)

        expected_calls = [
            mock.call(
                'lvcreate', '-L', '0G', '-n', 'instance-00000001-ephemerals0',
                'lxd', attempts=3, run_as_root=True),
            mock.call(
                'mkfs', '-t', 'ext4', '/dev/lxd/instance-00000001-ephemerals0',
                run_as_root=True),
            mock.call(
                'mount', '-t', 'ext4',
                '/dev/lxd/instance-00000001-ephemerals0',
                '/i/instance-00000001/storage/ephemerals0',
                run_as_root=True),
            mock.call(
                'chown', '165536', '/i/instance-00000001/storage/ephemerals0',
                run_as_root=True)]
        self.assertEqual(expected_calls, execute.call_args_list)


class TestDetachEphemeral(test.NoDBTestCase):
    """Tests for nova.virt.lxd.storage.detach_ephemeral."""

    @mock.patch.object(storage.utils, 'execute')
    @mock.patch(
        'nova.virt.lxd.storage.driver.block_device_info_get_ephemerals')
    def test_remove_ephemeral_with_zfs(
            self, block_device_info_get_ephemerals, execute):
        block_device_info_get_ephemerals.return_value = [
            {'virtual_name': 'ephemerals0'}]

        ctx = context.get_admin_context()
        instance = fake_instance.fake_instance_obj(
            ctx, name='test', memory_mb=0)
        block_device_info = mock.Mock()
        lxd_config = {'environment': {'storage': 'zfs'},
                      'config': {'storage.zfs_pool_name': 'zfs'}}

        storage.detach_ephemeral(block_device_info, lxd_config, instance)

        block_device_info_get_ephemerals.assert_called_once_with(
            block_device_info)

        expected_calls = [
            mock.call('zfs', 'destroy', 'zfs/instance-00000001-ephemeral',
                      run_as_root=True)
        ]
        self.assertEqual(expected_calls, execute.call_args_list)

    @mock.patch.object(storage.utils, 'execute')
    @mock.patch(
        'nova.virt.lxd.storage.driver.block_device_info_get_ephemerals')
    def test_remove_ephemeral_with_lvm(
            self, block_device_info_get_ephemerals, execute):
        block_device_info_get_ephemerals.return_value = [
            {'virtual_name': 'ephemerals0'}]

        ctx = context.get_admin_context()
        instance = fake_instance.fake_instance_obj(
            ctx, name='test', memory_mb=0)
        block_device_info = mock.Mock()
        lxd_config = {'environment': {'storage': 'lvm'},
                      'config': {'storage.lvm_vg_name': 'lxd'}}

        storage.detach_ephemeral(block_device_info, lxd_config, instance)

        block_device_info_get_ephemerals.assert_called_once_with(
            block_device_info)

        expected_calls = [
            mock.call(
                'umount', '/dev/lxd/instance-00000001-ephemerals0',
                run_as_root=True),
            mock.call('lvremove', '-f',
                      '/dev/lxd/instance-00000001-ephemerals0',
                      run_as_root=True)
        ]
        self.assertEqual(expected_calls, execute.call_args_list)
