# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2010-2012 ????
# 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.
#    @author: Salvatore Orlando, Citrix Systems

import unittest

from lxml import etree
from webob import exc

import quantum.api.attachments as atts
import quantum.api.networks as nets
import quantum.api.ports as ports
import quantum.api.versions as versions
from quantum.common.test_lib import test_config
from quantum.openstack.common import jsonutils
import quantum.tests.unit._test_api as test_api
import quantum.tests.unit.testlib_api as testlib


class APITestV10(test_api.BaseAPIOperationsTest):

    def assert_network(self, **kwargs):
        self.assertEqual({'id': kwargs['id'],
                          'name': kwargs['name']},
                         kwargs['network_data'])

    def assert_network_details(self, **kwargs):
        self.assertEqual({'id': kwargs['id'],
                          'name': kwargs['name'],
                          'ports': [{'id': kwargs['port_id'],
                                     'state': 'ACTIVE'}]},
                         kwargs['network_data'])

    def assert_port(self, **kwargs):
        self.assertEqual({'id': kwargs['id'],
                          'state': kwargs['state']},
                         kwargs['port_data'])

    def assert_port_attachment(self, **kwargs):
        self.assertEqual({'id': kwargs['id'], 'state': kwargs['state'],
                          'attachment': {'id': kwargs['interface_id']}},
                         kwargs['port_data'])

    def setUp(self):
        super(APITestV10, self).setUp(
            'quantum.api.APIRouterV10',
            {
                test_api.NETS: nets.ControllerV10._serialization_metadata,
                test_api.PORTS: ports.ControllerV10._serialization_metadata,
                test_api.ATTS: atts.ControllerV10._serialization_metadata,
            }
        )
        self._successful_create_code = exc.HTTPOk.code
        self._network_not_found_code = 420
        self._network_in_use_code = 421
        self._port_not_found_code = 430
        self._port_state_invalid_code = 431
        self._port_in_use_code = 432
        self._already_attached_code = 440


class APITestV11(test_api.BaseAPIOperationsTest):

    def assert_network(self, **kwargs):
        self.assertEqual({'id': kwargs['id'],
                          'name': kwargs['name'],
                          'op-status': self.net_op_status},
                         kwargs['network_data'])

    def assert_network_details(self, **kwargs):
        self.assertEqual({'id': kwargs['id'],
                          'name': kwargs['name'],
                          'op-status': self.net_op_status,
                          'ports': [{'id': kwargs['port_id'],
                                     'state': 'ACTIVE',
                                     'op-status': self.port_op_status}]},
                         kwargs['network_data'])

    def assert_port(self, **kwargs):
        self.assertEqual({'id': kwargs['id'],
                          'state': kwargs['state'],
                          'op-status': self.port_op_status},
                         kwargs['port_data'])

    def assert_port_attachment(self, **kwargs):
        self.assertEqual({'id': kwargs['id'], 'state': kwargs['state'],
                          'op-status': self.port_op_status,
                          'attachment': {'id': kwargs['interface_id']}},
                         kwargs['port_data'])

    def setUp(self):
        self.net_op_status = test_config.get('default_net_op_status',
                                             'UNKNOWN')
        self.port_op_status = test_config.get('default_port_op_status',
                                              'UNKNOWN')
        super(APITestV11, self).setUp(
            'quantum.api.APIRouterV11',
            {
                test_api.NETS: nets.ControllerV11._serialization_metadata,
                test_api.PORTS: ports.ControllerV11._serialization_metadata,
                test_api.ATTS: atts.ControllerV11._serialization_metadata,
            }
        )
        self._successful_create_code = exc.HTTPAccepted.code
        self._network_not_found_code = exc.HTTPNotFound.code
        self._network_in_use_code = exc.HTTPConflict.code
        self._port_not_found_code = exc.HTTPNotFound.code
        self._port_state_invalid_code = exc.HTTPBadRequest.code
        self._port_in_use_code = exc.HTTPConflict.code
        self._already_attached_code = exc.HTTPConflict.code


class APIFiltersTest(test_api.AbstractAPITest):
    """ Test case for API filters.
        Uses controller for API v1.1
    """

    def _do_filtered_network_list_request(self, flt):
        list_network_req = testlib.network_list_request(self.tenant_id,
                                                        self.fmt,
                                                        query_string=flt)
        list_network_res = list_network_req.get_response(self.api)
        self.assertEqual(list_network_res.status_int, 200)
        network_data = (self._net_deserializers[self.content_type].
                        deserialize(list_network_res.body)['body'])
        return network_data

    def _do_filtered_port_list_request(self, flt, network_id):
        list_port_req = testlib.port_list_request(self.tenant_id,
                                                  network_id,
                                                  self.fmt,
                                                  query_string=flt)
        list_port_res = list_port_req.get_response(self.api)
        self.assertEqual(list_port_res.status_int, 200)
        port_data = (self._port_deserializers[self.content_type].
                     deserialize(list_port_res.body)['body'])
        return port_data

    def setUp(self):
        super(APIFiltersTest, self).setUp(
            'quantum.api.APIRouterV11',
            {
                test_api.NETS: nets.ControllerV11._serialization_metadata,
                test_api.PORTS: ports.ControllerV11._serialization_metadata,
                test_api.ATTS: atts.ControllerV11._serialization_metadata,
            }
        )
        self._successful_create_code = exc.HTTPAccepted.code
        self.net_op_status = test_config.get('default_net_op_status',
                                             'UNKNOWN')
        self.port_op_status = test_config.get('default_port_op_status',
                                              'UNKNOWN')
        self.fmt = "xml"
        self.content_type = "application/%s" % self.fmt
        # create data for validating filters
        # Create network "test-1"
        self.net1_id = self._create_network(self.fmt, name="test-1")
        # Add 2 ports, 1 ACTIVE, 1 DOWN
        self.port11_id = self._create_port(self.net1_id, "ACTIVE", self.fmt)
        self.port12_id = self._create_port(self.net1_id, "DOWN", self.fmt)
        # Put attachment "test-1-att" in active port
        self._set_attachment(self.net1_id,
                             self.port11_id,
                             "test-1-att",
                             self.fmt)
        # Create network "test-2"
        # Add 2 ports, 2 ACTIVE, 0 DOWN
        self.net2_id = self._create_network(self.fmt, name="test-2")
        self.port21_id = self._create_port(self.net2_id, "ACTIVE", self.fmt)
        self.port22_id = self._create_port(self.net2_id, "ACTIVE", self.fmt)

    def test_network_name_filter(self):
        flt = "name=test-1"
        network_data = self._do_filtered_network_list_request(flt)
        # Check network count: should return 1
        self.assertEqual(len(network_data['networks']), 1)
        self.assertEqual(network_data['networks'][0]['id'], self.net1_id)

        flt = "name=non-existent"
        network_data = self._do_filtered_network_list_request(flt)
        # Check network count: should return 0
        self.assertEqual(len(network_data['networks']), 0)

    def test_network_op_status_filter(self):
        # First filter for networks in default status
        flt = "op-status=%s" % self.net_op_status
        network_data = self._do_filtered_network_list_request(flt)
        # Check network count: should return 2
        self.assertEqual(len(network_data['networks']), 2)

        # And then for networks in 'DOWN' status
        flt = "op-status=DOWN"
        network_data = self._do_filtered_network_list_request(flt)
        # Check network count: should return 0
        self.assertEqual(len(network_data['networks']), 0)

    def test_network_port_op_status_filter(self):
        # First filter for networks with ports in default op status
        flt = "port-op-status=%s" % self.port_op_status
        network_data = self._do_filtered_network_list_request(flt)
        # Check network count: should return 2
        self.assertEqual(len(network_data['networks']), 2)

    def test_network_port_state_filter(self):
        # First filter for networks with ports 'ACTIVE'
        flt = "port-state=ACTIVE"
        network_data = self._do_filtered_network_list_request(flt)
        # Check network count: should return 2
        self.assertEqual(len(network_data['networks']), 2)

        # And then for networks with ports in 'DOWN' admin state
        flt = "port-state=DOWN"
        network_data = self._do_filtered_network_list_request(flt)
        # Check network count: should return 1
        self.assertEqual(len(network_data['networks']), 1)

    def test_network_has_attachment_filter(self):
        # First filter for networks with ports 'ACTIVE'
        flt = "has-attachment=True"
        network_data = self._do_filtered_network_list_request(flt)
        # Check network count: should return 1
        self.assertEqual(len(network_data['networks']), 1)

        # And then for networks with ports in 'DOWN' admin state
        flt = "has-attachment=False"
        network_data = self._do_filtered_network_list_request(flt)
        # Check network count: should return 1
        self.assertEqual(len(network_data['networks']), 1)

    def test_network_port_filter(self):
        flt = "port=%s" % self.port11_id
        network_data = self._do_filtered_network_list_request(flt)
        # Check network count: should return 1
        self.assertEqual(len(network_data['networks']), 1)
        self.assertEqual(network_data['networks'][0]['id'], self.net1_id)

        flt = "port=%s" % self.port21_id
        network_data = self._do_filtered_network_list_request(flt)
        # Check network count: should return 1
        self.assertEqual(len(network_data['networks']), 1)
        self.assertEqual(network_data['networks'][0]['id'], self.net2_id)

    def test_network_attachment_filter(self):
        flt = "attachment=test-1-att"
        network_data = self._do_filtered_network_list_request(flt)
        # Check network count: should return 1
        self.assertEqual(len(network_data['networks']), 1)
        self.assertEqual(network_data['networks'][0]['id'], self.net1_id)

        flt = "attachment=non-existent"
        network_data = self._do_filtered_network_list_request(flt)
        # Check network count: should return 0
        self.assertEqual(len(network_data['networks']), 0)

    def test_network_multiple_filters(self):
        # Add some data for having more fun
        another_net_id = self._create_network(self.fmt, name="test-1")
        # Add 1 ACTIVE port
        self._create_port(another_net_id, "ACTIVE", self.fmt)
        # Do the filtering
        flt = "name=test-1&port-state=ACTIVE&attachment=test-1-att"
        network_data = self._do_filtered_network_list_request(flt)
        # Check network count: should return 1
        self.assertEqual(len(network_data['networks']), 1)
        self.assertEqual(network_data['networks'][0]['id'], self.net1_id)

    def test_port_state_filter(self):
        # First filter for 'ACTIVE' ports in 1st network
        flt = "state=ACTIVE"
        port_data = self._do_filtered_port_list_request(flt, self.net1_id)
        # Check port count: should return 1
        self.assertEqual(len(port_data['ports']), 1)

        # And then in 2nd network
        port_data = self._do_filtered_port_list_request(flt, self.net2_id)
        # Check port count: should return 2
        self.assertEqual(len(port_data['ports']), 2)

    def test_port_op_status_filter(self):
        # First filter for 'UP' ports in 1st network
        flt = "op-status=%s" % self.port_op_status
        port_data = self._do_filtered_port_list_request(flt, self.net1_id)
        # Check port count: should return 2
        self.assertEqual(len(port_data['ports']), 2)

    def test_port_has_attachment_filter(self):
        # First search for ports with attachments in 1st network
        flt = "has-attachment=True"
        port_data = self._do_filtered_port_list_request(flt, self.net1_id)
        # Check port count: should return 1
        self.assertEqual(len(port_data['ports']), 1)
        self.assertEqual(port_data['ports'][0]['id'], self.port11_id)

        # And then for ports without attachment in 2nd network
        flt = "has-attachment=False"
        port_data = self._do_filtered_port_list_request(flt, self.net2_id)
        # Check port count: should return 2
        self.assertEqual(len(port_data['ports']), 2)

    def test_port_attachment_filter(self):
        # First search for ports with attachments in 1st network
        flt = "attachment=test-1-att"
        port_data = self._do_filtered_port_list_request(flt, self.net1_id)
        # Check port count: should return 1
        self.assertEqual(len(port_data['ports']), 1)
        self.assertEqual(port_data['ports'][0]['id'], self.port11_id)

        # And then for a non-existent attachment in 2nd network
        flt = "attachment=non-existent"
        port_data = self._do_filtered_port_list_request(flt, self.net2_id)
        # Check port count: should return 0
        self.assertEqual(len(port_data['ports']), 0)

    def test_port_multiple_filters(self):
        flt = "op-status=%s&state=DOWN" % self.port_op_status
        port_data = self._do_filtered_port_list_request(flt, self.net1_id)
        # Check port count: should return 1
        self.assertEqual(len(port_data['ports']), 1)
        self.assertEqual(port_data['ports'][0]['id'], self.port12_id)

        flt = "state=ACTIVE&attachment=test-1-att"
        port_data = self._do_filtered_port_list_request(flt, self.net1_id)
        # Check port count: should return 1
        self.assertEqual(len(port_data['ports']), 1)
        self.assertEqual(port_data['ports'][0]['id'], self.port11_id)

        flt = "state=ACTIVE&has-attachment=False"
        port_data = self._do_filtered_port_list_request(flt, self.net2_id)
        # Check port count: should return 2
        self.assertEqual(len(port_data['ports']), 2)


class APIRootTest(unittest.TestCase):
    def setUp(self):
        self.app = versions.Versions()

    def _test_root_responds_with_versions(self, content_type):
        req = testlib.create_request('/', '', content_type)
        response = self.app(req)
        self.assertEquals(response.status_int, 200)
        return response.body

    def test_root_responds_with_versions_json(self):
        body = self._test_root_responds_with_versions('application/json')
        data = jsonutils.loads(body)
        self.assertEquals('versions', data.keys()[0])

    def test_root_responds_with_versions_xml(self):
        body = self._test_root_responds_with_versions('application/xml')
        root = etree.fromstring(body)
        self.assertEquals(root.tag, 'versions')

    def test_invalid_version(self):
        req = testlib.create_request('/v99.99/tenants/tenantX/networks',
                                     '',
                                     'application/json')
        response = self.app(req)
        self.assertEquals(response.status_int, 404)
