# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# 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.

from unittest import mock

from cinder import exception
from cinder.tests.unit import fake_volume
from cinder.tests.unit.volume.drivers.dell_emc import powerstore
from cinder.volume.drivers.dell_emc.powerstore import client


class TestVolumeCreateDeleteExtend(powerstore.TestPowerStoreDriver):
    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.client."
                "PowerStoreClient.get_chap_config")
    def setUp(self, mock_chap):
        super(TestVolumeCreateDeleteExtend, self).setUp()
        self.driver.check_for_setup_error()
        self.volume = fake_volume.fake_volume_obj(
            self.context,
            host="host@backend",
            provider_id="fake_id",
            size=8
        )

    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.client."
                "PowerStoreClient.create_volume")
    def test_create_volume(self, mock_create):
        mock_create.return_value = "fake_id"
        self.driver.create_volume(self.volume)

    @mock.patch("requests.request")
    def test_create_volume_bad_status(self, mock_create_request):
        mock_create_request.return_value = powerstore.MockResponse(rc=400)
        error = self.assertRaises(exception.VolumeBackendAPIException,
                                  self.driver.create_volume,
                                  self.volume)
        self.assertIn("Failed to create PowerStore volume", error.msg)

    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.adapter."
                "CommonAdapter._detach_volume_from_hosts")
    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.client."
                "PowerStoreClient.delete_volume_or_snapshot")
    def test_delete_volume(self, mock_delete, mock_detach):
        self.driver.delete_volume(self.volume)

    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.adapter."
                "CommonAdapter._detach_volume_from_hosts")
    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.client."
                "PowerStoreClient.delete_volume_or_snapshot")
    def test_delete_volume_no_provider_id(self, mock_delete, mock_detach):
        self.volume.provider_id = None
        self.driver.delete_volume(self.volume)
        mock_detach.assert_not_called()
        mock_delete.assert_not_called()

    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.adapter."
                "CommonAdapter._detach_volume_from_hosts")
    @mock.patch("requests.request")
    def test_delete_volume_not_found(self, mock_delete_request, mock_detach):
        mock_delete_request.return_value = powerstore.MockResponse(rc=404)
        self.driver.delete_volume(self.volume)

    @mock.patch("requests.request")
    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.client."
                "PowerStoreClient.get_volume_mapped_hosts")
    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.client."
                "PowerStoreClient.delete_volume_or_snapshot")
    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.adapter."
                "CommonAdapter._create_or_update_volume_qos_policy")
    def test_delete_volume_detach_not_found(self,
                                            mock_delete,
                                            mock_mapped_hosts,
                                            mock_detach_request,
                                            mock_qos_policy):
        mock_mapped_hosts.return_value = ["fake_host_id"]
        mock_detach_request.return_value = powerstore.MockResponse(
            content={},
            rc=404
        )
        self.driver.delete_volume(self.volume)
        mock_qos_policy.assert_not_called()

    @mock.patch("requests.request")
    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.client."
                "PowerStoreClient.get_volume_mapped_hosts")
    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.client."
                "PowerStoreClient.delete_volume_or_snapshot")
    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.adapter."
                "CommonAdapter._create_or_update_volume_qos_policy")
    def test_delete_volume_detach_not_mapped(self,
                                             mock_delete,
                                             mock_mapped_hosts,
                                             mock_detach_request,
                                             mock_qos_policy):
        mock_mapped_hosts.return_value = ["fake_host_id"]
        mock_detach_request.return_value = powerstore.MockResponse(
            content={
                "messages": [
                    {
                        "code": client.VOLUME_NOT_MAPPED_ERROR,
                    },
                ],
            },
            rc=422
        )
        self.driver.delete_volume(self.volume)
        mock_qos_policy.assert_not_called()

    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.adapter."
                "CommonAdapter._detach_volume_from_hosts")
    @mock.patch("requests.request")
    def test_delete_volume_bad_status(self, mock_delete, mock_detach):
        mock_delete.return_value = powerstore.MockResponse(rc=400)
        error = self.assertRaises(exception.VolumeBackendAPIException,
                                  self.driver.delete_volume,
                                  self.volume)
        self.assertIn("Failed to delete PowerStore volume", error.msg)

    @mock.patch("requests.request")
    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.client."
                "PowerStoreClient.get_volume_mapped_hosts")
    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.client."
                "PowerStoreClient.delete_volume_or_snapshot")
    def test_delete_volume_detach_bad_status(self,
                                             mock_delete,
                                             mock_mapped_hosts,
                                             mock_detach_request):
        mock_mapped_hosts.return_value = ["fake_host_id"]
        mock_detach_request.return_value = powerstore.MockResponse(rc=400)
        error = self.assertRaises(exception.VolumeBackendAPIException,
                                  self.driver.delete_volume,
                                  self.volume)
        self.assertIn("Failed to detach PowerStore volume", error.msg)

    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.client."
                "PowerStoreClient.extend_volume")
    def test_extend_volume(self, mock_extend):
        self.driver.extend_volume(self.volume, 16)

    @mock.patch("requests.request")
    def test_extend_volume_bad_status(self, mock_extend_request):
        mock_extend_request.return_value = powerstore.MockResponse(rc=400)
        error = self.assertRaises(exception.VolumeBackendAPIException,
                                  self.driver.extend_volume,
                                  self.volume,
                                  16)
        self.assertIn("Failed to extend PowerStore volume", error.msg)

    @mock.patch("requests.request")
    def test_post_request_timeout_exception(self, mock_request):
        mock_request.return_value = powerstore.MockResponse(
            rc=501
        )
        error = self.assertRaises(exception.VolumeBackendAPIException,
                                  self.driver.extend_volume,
                                  self.volume,
                                  16)
        self.assertEqual('Bad or unexpected response from the '
                         'storage volume backend API: Failed to '
                         'extend PowerStore volume with id fake_id.',
                         error.msg)

    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.adapter."
                "CommonAdapter", autospec=True)
    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.utils."
                "is_metro_volume")
    def test_create_volume_is_metro_volume(self,
                                           mock_is_metro_volume,
                                           mock_adapetr):
        mock_is_metro_volume.return_value = True
        adapter = mock_adapetr.return_value
        adapter.get_cluster_name.return_value = "fake_cluster"
        adapter.create_volume.return_value = {
            "provider_id": 'vol_1',
            "replication_status": True,
        }
        self.driver.active_backend_id = "default"
        self.driver.adapters = {"default": adapter,
                                "rep_1": adapter}
        update = self.driver.create_volume(self.volume)
        adapter.create_volume.assert_called_once_with(
            self.volume, "fake_cluster")
        self.assertEqual('vol_1', update["provider_id"])

    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.adapter."
                "CommonAdapter.create_volume")
    @mock.patch("cinder.volume.drivers.dell_emc.powerstore.utils."
                "is_metro_volume")
    def test_create_volume_not_metro_volume(self,
                                            mock_is_metro_volume,
                                            mock_adapetr_create_volume):
        mock_is_metro_volume.return_value = False
        self.driver.create_volume(self.volume)
        mock_adapetr_create_volume.assert_called_once_with(
            self.volume, None)
