# Copyright (c) 2018 Huawei Technologies Co., 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 json
from unittest import mock

import requests

from cinder.tests.unit import test
from cinder.tests.unit.volume.drivers.fusionstorage import test_utils
from cinder.volume.drivers.fusionstorage import fs_client


class FakeSession(test_utils.FakeBaseSession):
    method_map = {
        'get': {
            'rest/version':
                {'currentVersion': 'fake_version'},
            '/storagePool$':
                {'storagePools': [{'poolName': 'fake_pool_name',
                                   'poolId': 'fake_pool_id'}]},
            r'/storagePool\?poolId=0':
                {'storagePools': [{'poolName': 'fake_pool_name1',
                                   'poolId': 0}]},
            r'/volume/queryByName\?volName=fake_name':
                {'errorCode': 0, 'lunDetailInfo':
                    [{'volume_id': 'fake_id',
                      'volume_name': 'fake_name'}]},
            r'/volume/queryById\?volId=fake_id':
                {'errorCode': 0, 'lunDetailInfo':
                    [{'volume_id': 'fake_id',
                      'volume_name': 'fake_name'}]},
            r'/lun/wwn/list\?wwn=fake_wwn':
                {'errorCode': 0, 'lunDetailInfo':
                    [{'volume_id': 'fake_id',
                      'volume_wwn': 'fake_wwn'}]},
        },
        'post': {
            '/sec/login': {},
            '/sec/logout': {'res': 'fake_logout'},
            '/sec/keepAlive': {'res': 'fake_keepAlive'},
            '/volume/list': {'errorCode': 0, 'volumeList': [
                {'volName': 'fake_name1', 'volId': 'fake_id1'},
                {'volName': 'fake_name2', 'volId': 'fake_id2'}]},
            '/volume/create': {'ID': 'fake_volume_create_id'},
            '/volume/delete': {'ID': 'fake_volume_delete_id'},
            '/volume/attach':
                {'fake_name': [{'errorCode': '0', 'ip': 'fake_ip'}]},
            '/volume/detach/': {'ID': 'fake_volume_detach_id'},
            '/volume/expand': {'ID': 'fake_volume_expend_id'},
            '/volume/snapshot/list':
                {"snapshotList": [{"snapshot": "fake_name",
                                   "size": "fake_size"}]},
            '/snapshot/list': {'totalNum': 'fake_snapshot_num',
                               'snapshotList':
                                   [{'snapName': 'fake_snapName'}]},
            '/snapshot/create/': {'ID': 'fake_snapshot_create_id'},
            '/snapshot/delete/': {'ID': 'fake_snapshot_delete_id'},
            '/snapshot/rollback': {'ID': 'fake_snapshot_delete_id'},
            '/snapshot/volume/create/': {'ID': 'fake_vol_from_snap_id'},
        }
    }


class TestFsclient(test.TestCase):
    def setUp(self):
        super(TestFsclient, self).setUp()
        self.mock_object(requests, 'Session', FakeSession)
        self.client = fs_client.RestCommon('https://fake_rest_site',
                                           'fake_user',
                                           'fake_password')
        self.client.login()

    def tearDown(self):
        super(TestFsclient, self).tearDown()

    def test_login(self):
        self.assertEqual('fake_version',
                         self.client.version)
        self.assertEqual('fake_token',
                         self.client.session.headers['X-Auth-Token'])

    def test_keep_alive(self):
        retval = self.client.keep_alive()
        self.assertIsNone(retval)

    def test_logout(self):
        self.assertIsNone(self.client.logout())

    def test_query_all_pool_info(self):
        with mock.patch.object(self.client.session, 'get',
                               wraps=self.client.session.get) as mocker:
            retval = self.client.query_pool_info()
            mocker.assert_called_once_with(
                'https://fake_rest_site/dsware/service/'
                'fake_version/storagePool', timeout=50)
            self.assertListEqual(
                [{'poolName': 'fake_pool_name',
                  'poolId': 'fake_pool_id'}], retval)

    def test_query_pool_info(self):
        with mock.patch.object(self.client.session, 'get',
                               wraps=self.client.session.get) as mocker:
            retval = self.client.query_pool_info(pool_id=0)
            mocker.assert_called_once_with(
                'https://fake_rest_site/dsware/service/'
                'fake_version/storagePool?poolId=0', timeout=50)
            self.assertListEqual(
                [{'poolName': 'fake_pool_name1', 'poolId': 0}], retval)

    def test_query_volume_by_name(self):
        with mock.patch.object(self.client.session, 'get',
                               wraps=self.client.session.get) as mocker:
            retval = self.client.query_volume_by_name(vol_name='fake_name')
            mocker.assert_called_once_with(
                'https://fake_rest_site/dsware/service/fake_version/'
                'volume/queryByName?volName=fake_name', timeout=50)
            self.assertListEqual(
                [{'volume_id': 'fake_id', 'volume_name': 'fake_name'}], retval)

    def test_query_volume_by_id(self):
        with mock.patch.object(self.client.session, 'get',
                               wraps=self.client.session.get) as mocker:
            retval = self.client.query_volume_by_id(vol_id='fake_id')
            mocker.assert_called_once_with(
                'https://fake_rest_site/dsware/service/fake_version/'
                'volume/queryById?volId=fake_id', timeout=50)
            self.assertListEqual(
                [{'volume_id': 'fake_id', 'volume_name': 'fake_name'}], retval)

    def test_create_volume(self):
        with mock.patch.object(self.client.session, 'post',
                               wraps=self.client.session.post) as mocker:
            retval = self.client.create_volume(
                vol_name='fake_name', vol_size=1, pool_id='fake_id')
            except_data = json.dumps(
                {"volName": "fake_name", "volSize": 1, "poolId": "fake_id"})
            mocker.assert_called_once_with(
                'https://fake_rest_site/dsware/service/fake_version/'
                'volume/create', data=except_data, timeout=50)
            self.assertIsNone(retval)

    def test_delete_volume(self):
        with mock.patch.object(self.client.session, 'post',
                               wraps=self.client.session.post) as mocker:
            retval = self.client.delete_volume(vol_name='fake_name')
            except_data = json.dumps({"volNames": ['fake_name']})
            mocker.assert_called_once_with(
                'https://fake_rest_site/dsware/service/fake_version/'
                'volume/delete', data=except_data, timeout=50)
            self.assertIsNone(retval)

    def test_attach_volume(self):
        with mock.patch.object(self.client.session, 'post',
                               wraps=self.client.session.post) as mocker:
            retval = self.client.attach_volume(
                vol_name='fake_name', manage_ip='fake_ip')
            except_data = json.dumps(
                {"volName": ['fake_name'], "ipList": ['fake_ip']})
            mocker.assert_called_once_with(
                'https://fake_rest_site/dsware/service/fake_version/'
                'volume/attach', data=except_data, timeout=50)
            self.assertDictEqual(
                {'result': 0,
                 'fake_name': [{'errorCode': '0', 'ip': 'fake_ip'}]},
                retval)

    def test_detach_volume(self):
        with mock.patch.object(self.client.session, 'post',
                               wraps=self.client.session.post) as mocker:
            retval = self.client.detach_volume(
                vol_name='fake_name', manage_ip='fake_ip')
            except_data = json.dumps(
                {"volName": ['fake_name'], "ipList": ['fake_ip']})
            mocker.assert_called_once_with(
                'https://fake_rest_site/dsware/service/fake_version/'
                'volume/detach/', data=except_data, timeout=50)
            self.assertIsNone(retval)

    def test_expand_volume(self):
        with mock.patch.object(self.client.session, 'post',
                               wraps=self.client.session.post) as mocker:
            retval = self.client.expand_volume(
                vol_name='fake_name', new_vol_size=2)
            except_data = json.dumps({"volName": 'fake_name', "newVolSize": 2})
            mocker.assert_called_once_with(
                'https://fake_rest_site/dsware/service/fake_version/'
                'volume/expand', data=except_data, timeout=50)
            self.assertIsNone(retval)

    def test_query_snapshot_by_name(self):
        with mock.patch.object(self.client.session, 'post',
                               wraps=self.client.session.post) as mocker:
            retval = self.client.query_snapshot_by_name(
                pool_id='fake_id', snapshot_name='fake_name')
            except_data = json.dumps(
                {"poolId": 'fake_id', "pageNum": 1,
                 "pageSize": 1000, "filters": {"volumeName": 'fake_name'}})
            mocker.assert_called_once_with(
                'https://fake_rest_site/dsware/service/fake_version/'
                'snapshot/list', data=except_data, timeout=50)
            self.assertDictEqual(
                {'result': 0, 'totalNum': 'fake_snapshot_num',
                 'snapshotList': [{'snapName': 'fake_snapName'}]}, retval)

    def test_create_snapshot(self):
        with mock.patch.object(self.client.session, 'post',
                               wraps=self.client.session.post) as mocker:
            retval = self.client.create_snapshot(
                snapshot_name='fake_snap', vol_name='fake_name')
            except_data = json.dumps(
                {"volName": "fake_name", "snapshotName": "fake_snap"})
            mocker.assert_called_once_with(
                'https://fake_rest_site/dsware/service/fake_version/'
                'snapshot/create/', data=except_data, timeout=50)
            self.assertIsNone(retval)

    def test_delete_snapshot(self):
        with mock.patch.object(self.client.session, 'post',
                               wraps=self.client.session.post) as mocker:
            retval = self.client.delete_snapshot(snapshot_name='fake_snap')
            except_data = json.dumps({"snapshotName": "fake_snap"})
            mocker.assert_called_once_with(
                'https://fake_rest_site/dsware/service/fake_version/'
                'snapshot/delete/', data=except_data, timeout=50)
            self.assertIsNone(retval)

    def test_create_volume_from_snapshot(self):
        with mock.patch.object(self.client.session, 'post',
                               wraps=self.client.session.post) as mocker:
            retval = self.client.create_volume_from_snapshot(
                snapshot_name='fake_snap', vol_name='fake_name', vol_size=2)
            except_data = json.dumps({"src": 'fake_snap',
                                      "volName": 'fake_name',
                                      "volSize": 2})
            mocker.assert_called_once_with(
                'https://fake_rest_site/dsware/service/fake_version/'
                'snapshot/volume/create/', data=except_data, timeout=50)
            self.assertIsNone(retval)

    @mock.patch.object(fs_client.RestCommon, 'create_snapshot')
    @mock.patch.object(fs_client.RestCommon, 'create_volume_from_snapshot')
    @mock.patch.object(fs_client.RestCommon, 'delete_snapshot')
    def test_create_volume_from_volume(
            self, mock_delete_snapshot, mock_volume_from_snapshot,
            mock_create_snapshot):
        vol_name = 'fake_name'
        vol_size = 3
        src_vol_name = 'src_fake_name'
        temp_snapshot_name = "temp" + src_vol_name + "clone" + vol_name

        retval = self.client.create_volume_from_volume(
            vol_name, vol_size, src_vol_name)
        mock_create_snapshot.assert_called_once_with(
            vol_name=src_vol_name, snapshot_name=temp_snapshot_name)
        mock_volume_from_snapshot.assert_called_once_with(
            snapshot_name=temp_snapshot_name,
            vol_name=vol_name, vol_size=vol_size)
        mock_delete_snapshot.assert_called_once_with(
            snapshot_name=temp_snapshot_name)
        self.assertIsNone(retval)
