# Copyright (c) 2016 Hitachi Data Systems, 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 ddt
import json
import requests
import time
from unittest import mock

from manila import exception
from manila.share.drivers.hitachi.hsp import rest
from manila import test
from manila.tests.share.drivers.hitachi.hsp import fakes


class FakeRequests(object):
    status_code = 0
    headers = {}
    content = ""

    def __init__(self, status_code, content='null'):
        self.status_code = status_code
        self.headers = {'location': 'fake_location'}
        self.content = content

    def json(self):
        return {'messages': [{'message': 'fake_msg'}]}


@ddt.ddt
class HitachiHSPRestTestCase(test.TestCase):
    def setUp(self):
        super(HitachiHSPRestTestCase, self).setUp()
        self.hitachi_hsp_host = '172.24.47.190'
        self.hitachi_hsp_username = 'hds_hnas_user'
        self.hitachi_hsp_password = 'hds_hnas_password'

        self._driver = rest.HSPRestBackend(self.hitachi_hsp_host,
                                           self.hitachi_hsp_username,
                                           self.hitachi_hsp_password)

    @ddt.data(202, 500)
    def test__send_post(self, code):
        self.mock_object(requests, "post", mock.Mock(
            return_value=FakeRequests(code)))

        if code == 202:
            self.mock_object(rest.HSPRestBackend, "_wait_job_status",
                             mock.Mock())
            self._driver._send_post('fake_url')

            rest.HSPRestBackend._wait_job_status.assert_called_once_with(
                'fake_location', 'COMPLETE')
        else:
            self.assertRaises(exception.HSPBackendException,
                              self._driver._send_post, 'fake_url')

    @ddt.data({'code': 200, 'content': 'null'},
              {'code': 200, 'content': 'fake_content'},
              {'code': 500, 'content': 'null'})
    @ddt.unpack
    def test__send_get(self, code, content):
        self.mock_object(requests, "get", mock.Mock(
            return_value=FakeRequests(code, content)))

        if code == 200:
            result = self._driver._send_get('fake_url')
            if content == 'null':
                self.assertIsNone(result)
            else:
                self.assertEqual(FakeRequests(code, content).json(), result)
        else:
            self.assertRaises(exception.HSPBackendException,
                              self._driver._send_get, 'fake_url')

    @ddt.data(202, 500)
    def test__send_delete(self, code):
        self.mock_object(requests, "delete", mock.Mock(
            return_value=FakeRequests(code)))

        if code == 202:
            self.mock_object(rest.HSPRestBackend, "_wait_job_status",
                             mock.Mock())
            self._driver._send_delete('fake_url')

            rest.HSPRestBackend._wait_job_status.assert_called_once_with(
                'fake_location', 'COMPLETE')
        else:
            self.assertRaises(exception.HSPBackendException,
                              self._driver._send_delete, 'fake_url')

    def test_add_file_system(self):
        url = "https://172.24.47.190/hspapi/file-systems/"

        payload = {
            'quota': fakes.file_system['properties']['quota'],
            'auto-access': False,
            'enabled': True,
            'description': '',
            'record-access-time': True,
            'tags': '',
            'space-hwm': 90,
            'space-lwm': 70,
            'name': fakes.file_system['properties']['name'],
        }

        self.mock_object(rest.HSPRestBackend, "_send_post", mock.Mock())
        self._driver.add_file_system(fakes.file_system['properties']['name'],
                                     fakes.file_system['properties']['quota'])

        rest.HSPRestBackend._send_post.assert_called_once_with(
            url, payload=json.dumps(payload))

    def test_get_file_system(self):
        url = ("https://172.24.47.190/hspapi/file-systems/list?name=%s" %
               fakes.file_system['properties']['name'])

        self.mock_object(rest.HSPRestBackend, "_send_get", mock.Mock(
            return_value={'list': [fakes.file_system]}))

        result = self._driver.get_file_system(
            fakes.file_system['properties']['name'])

        self.assertEqual(fakes.file_system, result)

        rest.HSPRestBackend._send_get.assert_called_once_with(url)

    def test_get_file_system_exception(self):
        url = ("https://172.24.47.190/hspapi/file-systems/list?name=%s" %
               fakes.file_system['properties']['name'])

        self.mock_object(rest.HSPRestBackend, "_send_get",
                         mock.Mock(return_value=None))

        self.assertRaises(exception.HSPItemNotFoundException,
                          self._driver.get_file_system,
                          fakes.file_system['properties']['name'])

        rest.HSPRestBackend._send_get.assert_called_once_with(url)

    def test_delete_file_system(self):
        url = ("https://172.24.47.190/hspapi/file-systems/%s" %
               fakes.file_system['id'])

        self.mock_object(rest.HSPRestBackend, "_send_delete", mock.Mock())
        self._driver.delete_file_system(fakes.file_system['id'])

        rest.HSPRestBackend._send_delete.assert_called_once_with(url)

    def test_resize_file_system(self):
        url = ("https://172.24.47.190/hspapi/file-systems/%s" %
               fakes.file_system['id'])
        new_size = 53687091200
        payload = {'quota': new_size}

        self.mock_object(rest.HSPRestBackend, "_send_post", mock.Mock())
        self._driver.resize_file_system(fakes.file_system['id'], new_size)

        rest.HSPRestBackend._send_post.assert_called_once_with(
            url, payload=json.dumps(payload))

    def test_rename_file_system(self):
        url = ("https://172.24.47.190/hspapi/file-systems/%s" %
               fakes.file_system['id'])
        new_name = "fs_rename"
        payload = {'name': new_name}

        self.mock_object(rest.HSPRestBackend, "_send_post", mock.Mock())

        self._driver.rename_file_system(fakes.file_system['id'], new_name)

        rest.HSPRestBackend._send_post.assert_called_once_with(
            url, payload=json.dumps(payload))

    def test_add_share(self):
        url = "https://172.24.47.190/hspapi/shares/"
        payload = {
            'description': '',
            'type': 'NFS',
            'enabled': True,
            'tags': '',
            'name': fakes.share['name'],
            'file-system-id': fakes.share['properties']['file-system-id'],
        }

        self.mock_object(rest.HSPRestBackend, "_send_post", mock.Mock())

        self._driver.add_share(fakes.share['name'],
                               fakes.share['properties']['file-system-id'])

        rest.HSPRestBackend._send_post.assert_called_once_with(
            url, payload=json.dumps(payload))

    @ddt.data({'fs_id': None,
               'name': fakes.share['name'],
               'url': 'https://172.24.47.190/hspapi/shares/list?'
                      'name=aa4a7710-f326-41fb-ad18-b4ad587fc87a'},
              {'fs_id': fakes.share['properties']['file-system-id'],
               'name': None,
               'url': 'https://172.24.47.190/hspapi/shares/list?'
                      'file-system-id=33689245-1806-45d0-8507-0700b5f89750'})
    @ddt.unpack
    def test_get_share(self, fs_id, name, url):
        self.mock_object(rest.HSPRestBackend, "_send_get",
                         mock.Mock(return_value={'list': [fakes.share]}))

        result = self._driver.get_share(fs_id, name)

        self.assertEqual(fakes.share, result)

        rest.HSPRestBackend._send_get.assert_called_once_with(url)

    def test_get_share_exception(self):
        url = ("https://172.24.47.190/hspapi/shares/list?"
               "name=aa4a7710-f326-41fb-ad18-b4ad587fc87a")

        self.mock_object(rest.HSPRestBackend, "_send_get", mock.Mock(
            return_value=None))

        self.assertRaises(exception.HSPItemNotFoundException,
                          self._driver.get_share, None, fakes.share['name'])

        rest.HSPRestBackend._send_get.assert_called_once_with(url)

    def test_delete_share(self):
        url = "https://172.24.47.190/hspapi/shares/%s" % fakes.share['id']

        self.mock_object(rest.HSPRestBackend, "_send_delete")

        self._driver.delete_share(fakes.share['id'])

        rest.HSPRestBackend._send_delete.assert_called_once_with(url)

    def test_add_access_rule(self):
        url = "https://172.24.47.190/hspapi/shares/%s/" % fakes.share['id']
        payload = {
            "action": "add-access-rule",
            "name": fakes.share['id'] + fakes.access_rule['access_to'],
            "host-specification": fakes.access_rule['access_to'],
            "read-write": fakes.access_rule['access_level'],
        }

        self.mock_object(rest.HSPRestBackend, "_send_post", mock.Mock())

        self._driver.add_access_rule(fakes.share['id'],
                                     fakes.access_rule['access_to'],
                                     fakes.access_rule['access_level'])

        rest.HSPRestBackend._send_post.assert_called_once_with(
            url, payload=json.dumps(payload))

    def test_delete_access_rule(self):
        url = "https://172.24.47.190/hspapi/shares/%s/" % fakes.share['id']
        payload = {
            "action": "delete-access-rule",
            "name": fakes.hsp_rules[0]['name'],
        }
        self.mock_object(rest.HSPRestBackend, "_send_post", mock.Mock())

        self._driver.delete_access_rule(fakes.share['id'],
                                        fakes.hsp_rules[0]['name'])

        rest.HSPRestBackend._send_post.assert_called_once_with(
            url, payload=json.dumps(payload))

    @ddt.data({'value': {'list': fakes.hsp_rules}, 'res': fakes.hsp_rules},
              {'value': None, 'res': []})
    @ddt.unpack
    def test_get_access_rules(self, value, res):
        url = ("https://172.24.47.190/hspapi/shares/%s/access-rules" %
               fakes.share['id'])

        self.mock_object(rest.HSPRestBackend, "_send_get", mock.Mock(
            return_value=value))

        result = self._driver.get_access_rules(fakes.share['id'])

        self.assertEqual(res, result)

        rest.HSPRestBackend._send_get.assert_called_once_with(url)

    @ddt.data({'list': [fakes.hsp_cluster]}, None)
    def test_get_clusters(self, value):
        url = "https://172.24.47.190/hspapi/clusters/list"

        self.mock_object(rest.HSPRestBackend, "_send_get", mock.Mock(
            return_value=value))

        if value:
            result = self._driver.get_cluster()

            self.assertEqual(fakes.hsp_cluster, result)
        else:
            self.assertRaises(exception.HSPBackendException,
                              self._driver.get_cluster)

        rest.HSPRestBackend._send_get.assert_called_once_with(url)

    @ddt.data('COMPLETE', 'ERROR', 'RUNNING')
    def test__wait_job_status(self, stat):
        url = "fake_job_url"
        json = {
            'id': 'fake_id',
            'properties': {
                'completion-details': 'Duplicate NFS access rule exists',
                'completion-status': stat,
            },
            'messages': [{
                'id': 'fake_id',
                'message': 'fake_msg',
            }]
        }

        self.mock_object(rest.HSPRestBackend, "_send_get", mock.Mock(
            return_value=json))
        self.mock_object(time, "sleep")

        if stat == 'COMPLETE':
            self._driver._wait_job_status(url, 'COMPLETE')

            rest.HSPRestBackend._send_get.assert_called_once_with(url)
        elif stat == 'ERROR':
            self.assertRaises(exception.HSPBackendException,
                              self._driver._wait_job_status, url, 'COMPLETE')

            rest.HSPRestBackend._send_get.assert_called_once_with(url)
        else:
            self.assertRaises(exception.HSPTimeoutException,
                              self._driver._wait_job_status, url, 'COMPLETE')

            rest.HSPRestBackend._send_get.assert_has_calls([
                mock.call(url), mock.call(url), mock.call(url), mock.call(url),
                mock.call(url),
            ])
