#    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 contextlib
import mock

from neutron import context
from neutron import manager
from neutron.plugins.ml2 import config
from neutron.tests.unit.ml2.drivers import ext_test
from neutron.tests.unit.ml2 import test_ml2_plugin


class ExtensionDriverTestCase(test_ml2_plugin.Ml2PluginV2TestCase):

    _extension_drivers = ['test']

    def setUp(self):
        config.cfg.CONF.set_override('extension_drivers',
                                     self._extension_drivers,
                                     group='ml2')
        super(ExtensionDriverTestCase, self).setUp()
        self._plugin = manager.NeutronManager.get_plugin()
        self._ctxt = context.get_admin_context()

    def test_network_attr(self):
        with self.network() as network:
            # Test create network
            ent = network['network'].get('network_extension')
            self.assertIsNotNone(ent)

            # Test list networks
            res = self._list('networks')
            val = res['networks'][0].get('network_extension')
            self.assertEqual('Test_Network_Extension_extend', val)

            # Test network update
            data = {'network':
                    {'network_extension': 'Test_Network_Extension_Update'}}
            res = self._update('networks', network['network']['id'], data)
            val = res['network'].get('network_extension')
            self.assertEqual('Test_Network_Extension_Update_update', val)

    def test_subnet_attr(self):
        with self.subnet() as subnet:
            # Test create subnet
            ent = subnet['subnet'].get('subnet_extension')
            self.assertIsNotNone(ent)

            # Test list subnets
            res = self._list('subnets')
            val = res['subnets'][0].get('subnet_extension')
            self.assertEqual('Test_Subnet_Extension_extend', val)

            # Test subnet update
            data = {'subnet':
                    {'subnet_extension': 'Test_Subnet_Extension_Update'}}
            res = self._update('subnets', subnet['subnet']['id'], data)
            val = res['subnet'].get('subnet_extension')
            self.assertEqual('Test_Subnet_Extension_Update_update', val)

    def test_port_attr(self):
        with self.port() as port:
            # Test create port
            ent = port['port'].get('port_extension')
            self.assertIsNotNone(ent)

            # Test list ports
            res = self._list('ports')
            val = res['ports'][0].get('port_extension')
            self.assertEqual('Test_Port_Extension_extend', val)

            # Test port update
            data = {'port': {'port_extension': 'Test_Port_Extension_Update'}}
            res = self._update('ports', port['port']['id'], data)
            val = res['port'].get('port_extension')
            self.assertEqual('Test_Port_Extension_Update_update', val)

    def test_extend_network_dict(self):
        with contextlib.nested(
            mock.patch.object(ext_test.TestExtensionDriver,
                              'process_update_network'),
            mock.patch.object(ext_test.TestExtensionDriver,
                              'extend_network_dict'),
            self.network()
        ) as (ext_update_net, ext_net_dict, network):
            net_id = network['network']['id']
            net_data = {'network': {'id': net_id}}
            self._plugin.update_network(self._ctxt, net_id, net_data)
            self.assertTrue(ext_update_net.called)
            self.assertTrue(ext_net_dict.called)

    def test_extend_subnet_dict(self):
        with contextlib.nested(
            mock.patch.object(ext_test.TestExtensionDriver,
                              'process_update_subnet'),
            mock.patch.object(ext_test.TestExtensionDriver,
                              'extend_subnet_dict'),
            self.subnet()
        ) as (ext_update_subnet, ext_subnet_dict, subnet):
            subnet_id = subnet['subnet']['id']
            subnet_data = {'subnet': {'id': subnet_id}}
            self._plugin.update_subnet(self._ctxt, subnet_id, subnet_data)
            self.assertTrue(ext_update_subnet.called)
            self.assertTrue(ext_subnet_dict.called)

    def test_extend_port_dict(self):
        with contextlib.nested(
            mock.patch.object(ext_test.TestExtensionDriver,
                              'process_update_port'),
            mock.patch.object(ext_test.TestExtensionDriver,
                              'extend_port_dict'),
            self.port()
        ) as (ext_update_port, ext_port_dict, port):
            port_id = port['port']['id']
            port_data = {'port': {'id': port_id}}
            self._plugin.update_port(self._ctxt, port_id, port_data)
            self.assertTrue(ext_update_port.called)
            self.assertTrue(ext_port_dict.called)


class DBExtensionDriverTestCase(test_ml2_plugin.Ml2PluginV2TestCase):
    _extension_drivers = ['testdb']

    def setUp(self):
        config.cfg.CONF.set_override('extension_drivers',
                                     self._extension_drivers,
                                     group='ml2')
        super(DBExtensionDriverTestCase, self).setUp()
        self._plugin = manager.NeutronManager.get_plugin()
        self._ctxt = context.get_admin_context()

    def test_network_attr(self):
        with self.network() as network:
            # Test create with default value.
            net_id = network['network']['id']
            val = network['network']['network_extension']
            self.assertEqual("", val)
            res = self._show('networks', net_id)
            val = res['network']['network_extension']
            self.assertEqual("", val)

            # Test list.
            res = self._list('networks')
            val = res['networks'][0]['network_extension']
            self.assertEqual("", val)

        # Test create with explict value.
        res = self._create_network(self.fmt,
                                   'test-network', True,
                                   arg_list=('network_extension', ),
                                   network_extension="abc")
        network = self.deserialize(self.fmt, res)
        net_id = network['network']['id']
        val = network['network']['network_extension']
        self.assertEqual("abc", val)
        res = self._show('networks', net_id)
        val = res['network']['network_extension']
        self.assertEqual("abc", val)

        # Test update.
        data = {'network': {'network_extension': "def"}}
        res = self._update('networks', net_id, data)
        val = res['network']['network_extension']
        self.assertEqual("def", val)
        res = self._show('networks', net_id)
        val = res['network']['network_extension']
        self.assertEqual("def", val)

    def test_subnet_attr(self):
        with self.subnet() as subnet:
            # Test create with default value.
            net_id = subnet['subnet']['id']
            val = subnet['subnet']['subnet_extension']
            self.assertEqual("", val)
            res = self._show('subnets', net_id)
            val = res['subnet']['subnet_extension']
            self.assertEqual("", val)

            # Test list.
            res = self._list('subnets')
            val = res['subnets'][0]['subnet_extension']
            self.assertEqual("", val)

        with self.network() as network:
            # Test create with explict value.
            data = {'subnet':
                    {'network_id': network['network']['id'],
                     'cidr': '10.1.0.0/24',
                     'ip_version': '4',
                     'tenant_id': self._tenant_id,
                     'subnet_extension': 'abc'}}
            req = self.new_create_request('subnets', data, self.fmt)
            res = req.get_response(self.api)
            subnet = self.deserialize(self.fmt, res)
            subnet_id = subnet['subnet']['id']
            val = subnet['subnet']['subnet_extension']
            self.assertEqual("abc", val)
            res = self._show('subnets', subnet_id)
            val = res['subnet']['subnet_extension']
            self.assertEqual("abc", val)

            # Test update.
            data = {'subnet': {'subnet_extension': "def"}}
            res = self._update('subnets', subnet_id, data)
            val = res['subnet']['subnet_extension']
            self.assertEqual("def", val)
            res = self._show('subnets', subnet_id)
            val = res['subnet']['subnet_extension']
            self.assertEqual("def", val)

    def test_port_attr(self):
        with self.port() as port:
            # Test create with default value.
            net_id = port['port']['id']
            val = port['port']['port_extension']
            self.assertEqual("", val)
            res = self._show('ports', net_id)
            val = res['port']['port_extension']
            self.assertEqual("", val)

            # Test list.
            res = self._list('ports')
            val = res['ports'][0]['port_extension']
            self.assertEqual("", val)

        with self.network() as network:
            # Test create with explict value.
            res = self._create_port(self.fmt,
                                    network['network']['id'],
                                    arg_list=('port_extension', ),
                                    port_extension="abc")
            port = self.deserialize(self.fmt, res)
            port_id = port['port']['id']
            val = port['port']['port_extension']
            self.assertEqual("abc", val)
            res = self._show('ports', port_id)
            val = res['port']['port_extension']
            self.assertEqual("abc", val)

            # Test update.
            data = {'port': {'port_extension': "def"}}
            res = self._update('ports', port_id, data)
            val = res['port']['port_extension']
            self.assertEqual("def", val)
            res = self._show('ports', port_id)
            val = res['port']['port_extension']
            self.assertEqual("def", val)
