# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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 sys
import unittest
from libcloud.utils.py3 import httplib

try:
    import simplejson as json
except ImportError:
    import json

from libcloud.common.types import InvalidCredsError
from libcloud.compute.drivers.dreamhost import DreamhostNodeDriver
from libcloud.compute.types import NodeState

from libcloud.test import MockHttp
from libcloud.test.compute import TestCaseMixin
from libcloud.test.secrets import DREAMHOST_PARAMS


class DreamhostTest(unittest.TestCase, TestCaseMixin):

    def setUp(self):
        DreamhostNodeDriver.connectionCls.conn_classes = (
            None,
            DreamhostMockHttp
        )
        DreamhostMockHttp.type = None
        DreamhostMockHttp.use_param = 'cmd'
        self.driver = DreamhostNodeDriver(*DREAMHOST_PARAMS)

    def test_invalid_creds(self):
        """
        Tests the error-handling for passing a bad API Key to the DreamHost API
        """
        DreamhostMockHttp.type = 'BAD_AUTH'
        try:
            self.driver.list_nodes()
            self.assertTrue(
                False)  # Above command should have thrown an InvalidCredsException
        except InvalidCredsError:
            self.assertTrue(True)

    def test_list_nodes(self):
        """
        Test list_nodes for DreamHost PS driver.  Should return a list of two nodes:
            -   account_id: 000000
                ip: 75.119.203.51
                memory_mb: 500
                ps: ps22174
                start_date: 2010-02-25
                type: web
            -   account_id: 000000
                ip: 75.119.203.52
                memory_mb: 1500
                ps: ps22175
                start_date: 2010-02-25
                type: mysql
        """

        nodes = self.driver.list_nodes()
        self.assertEqual(len(nodes), 2)
        web_node = nodes[0]
        mysql_node = nodes[1]

        # Web node tests
        self.assertEqual(web_node.id, 'ps22174')
        self.assertEqual(web_node.state, NodeState.UNKNOWN)
        self.assertTrue('75.119.203.51' in web_node.public_ips)
        self.assertTrue(
            'current_size' in web_node.extra and
            web_node.extra['current_size'] == 500
        )
        self.assertTrue(
            'account_id' in web_node.extra and
            web_node.extra['account_id'] == 000000
        )
        self.assertTrue(
            'type' in web_node.extra and
            web_node.extra['type'] == 'web'
        )
        # MySql node tests
        self.assertEqual(mysql_node.id, 'ps22175')
        self.assertEqual(mysql_node.state, NodeState.UNKNOWN)
        self.assertTrue('75.119.203.52' in mysql_node.public_ips)
        self.assertTrue(
            'current_size' in mysql_node.extra and
            mysql_node.extra['current_size'] == 1500
        )
        self.assertTrue(
            'account_id' in mysql_node.extra and
            mysql_node.extra['account_id'] == 000000
        )
        self.assertTrue(
            'type' in mysql_node.extra and
            mysql_node.extra['type'] == 'mysql'
        )

    def test_create_node(self):
        """
        Test create_node for DreamHost PS driver.
        This is not remarkably compatible with libcloud.  The DH API allows
        users to specify what image they want to create and whether to move
        all their data to the (web) PS. It does NOT accept a name, size, or
        location.  The only information it returns is the PS's context id
        Once the PS is ready it will appear in the list generated by list_ps.
        """
        new_node = self.driver.create_node(
            image=self.driver.list_images()[0],
            size=self.driver.list_sizes()[0],
            movedata='no',
        )
        self.assertEqual(new_node.id, 'ps12345')
        self.assertEqual(new_node.state, NodeState.PENDING)
        self.assertTrue(
            'type' in new_node.extra and
            new_node.extra['type'] == 'web'
        )

    def test_destroy_node(self):
        """
        Test destroy_node for DreamHost PS driver
        """
        node = self.driver.list_nodes()[0]
        self.assertTrue(self.driver.destroy_node(node))

    def test_destroy_node_failure(self):
        """
        Test destroy_node failure for DreamHost PS driver
        """
        node = self.driver.list_nodes()[0]

        DreamhostMockHttp.type = 'API_FAILURE'
        self.assertFalse(self.driver.destroy_node(node))

    def test_reboot_node(self):
        """
        Test reboot_node for DreamHost PS driver.
        """
        node = self.driver.list_nodes()[0]
        self.assertTrue(self.driver.reboot_node(node))

    def test_reboot_node_failure(self):
        """
        Test reboot_node failure for DreamHost PS driver
        """
        node = self.driver.list_nodes()[0]

        DreamhostMockHttp.type = 'API_FAILURE'
        self.assertFalse(self.driver.reboot_node(node))

    def test_resize_node(self):
        """
        Test resize_node for DreamHost PS driver
        """
        node = self.driver.list_nodes()[0]
        self.assertTrue(self.driver._resize_node(node, 400))

    def test_resize_node_failure(self):
        """
        Test reboot_node faliure for DreamHost PS driver
        """
        node = self.driver.list_nodes()[0]

        DreamhostMockHttp.type = 'API_FAILURE'
        self.assertFalse(self.driver._resize_node(node, 400))

    def test_list_images(self):
        """
        Test list_images for DreamHost PS driver.
        """
        images = self.driver.list_images()
        self.assertEqual(len(images), 2)
        self.assertEqual(images[0].id, 'web')
        self.assertEqual(images[0].name, 'web')
        self.assertEqual(images[1].id, 'mysql')
        self.assertEqual(images[1].name, 'mysql')

    def test_list_sizes(self):
        sizes = self.driver.list_sizes()
        self.assertEqual(len(sizes), 5)

        size = [s for s in sizes if s.id == 'default'][0]
        self.assertEqual(size.id, 'default')
        self.assertEqual(size.bandwidth, None)
        self.assertEqual(size.disk, None)
        self.assertEqual(size.ram, 2300)
        self.assertEqual(size.price, 115)

    def test_list_locations(self):
        try:
            self.driver.list_locations()
        except NotImplementedError:
            pass

    def test_list_locations_response(self):
        self.assertRaises(NotImplementedError, self.driver.list_locations)


class DreamhostMockHttp(MockHttp):

    def _BAD_AUTH_dreamhost_ps_list_ps(self, method, url, body, headers):
        body = json.dumps({'data': 'invalid_api_key', 'result': 'error'})
        return (httplib.OK, body, {}, httplib.responses[httplib.OK])

    def _dreamhost_ps_add_ps(self, method, url, body, headers):
        body = json.dumps(
            {'data': {'added_web': 'ps12345'}, 'result': 'success'})
        return (httplib.OK, body, {}, httplib.responses[httplib.OK])

    def _dreamhost_ps_list_ps(self, method, url, body, headers):
        data = [{
            'account_id': 000000,
            'ip': '75.119.203.51',
            'memory_mb': 500,
            'ps': 'ps22174',
            'start_date': '2010-02-25',
            'type': 'web'
        },
            {
                'account_id': 000000,
                'ip': '75.119.203.52',
                'memory_mb': 1500,
                'ps': 'ps22175',
                'start_date': '2010-02-25',
                'type': 'mysql'
            }]
        result = 'success'
        body = json.dumps({'data': data, 'result': result})
        return (httplib.OK, body, {}, httplib.responses[httplib.OK])

    def _dreamhost_ps_list_images(self, method, url, body, headers):
        data = [{
            'description': 'Private web server',
            'image': 'web'
        },
            {
                'description': 'Private MySQL server',
                'image': 'mysql'
            }]
        result = 'success'
        body = json.dumps({'data': data, 'result': result})
        return (httplib.OK, body, {}, httplib.responses[httplib.OK])

    def _dreamhost_ps_reboot(self, method, url, body, headers):
        body = json.dumps({'data': 'reboot_scheduled', 'result': 'success'})
        return (httplib.OK, body, {}, httplib.responses[httplib.OK])

    def _API_FAILURE_dreamhost_ps_reboot(self, method, url, body, headers):
        body = json.dumps({'data': 'no_such_ps', 'result': 'error'})
        return (httplib.OK, body, {}, httplib.responses[httplib.OK])

    def _dreamhost_ps_set_size(self, method, url, body, headers):
        body = json.dumps(
            {'data': {'memory-mb': '500'}, 'result': 'success'})
        return (httplib.OK, body, {}, httplib.responses[httplib.OK])

    def _API_FAILURE_dreamhost_ps_set_size(self, method, url, body, headers):
        body = json.dumps(
            {'data': 'internal_error_setting_size', 'result': 'error'})
        return (httplib.OK, body, {}, httplib.responses[httplib.OK])

    def _dreamhost_ps_remove_ps(self, method, url, body, headers):
        body = json.dumps({'data': 'removed_web', 'result': 'success'})
        return (httplib.OK, body, {}, httplib.responses[httplib.OK])

    def _API_FAILURE_dreamhost_ps_remove_ps(self, method, url, body, headers):
        body = json.dumps({'data': 'no_such_ps', 'result': 'error'})
        return (httplib.OK, body, {}, httplib.responses[httplib.OK])

if __name__ == '__main__':
    sys.exit(unittest.main())
