# Copyright 2017 OVH SAS
# 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 copy
import testtools

from openstack.cloud import exc
from openstack.tests.unit import base


class TestSubnet(base.TestCase):

    network_name = 'network_name'
    subnet_name = 'subnet_name'
    subnet_id = '1f1696eb-7f47-47f6-835c-4889bff88604'
    subnet_cidr = '192.168.199.0/24'
    subnetpool_cidr = '172.16.0.0/28'
    prefix_length = 28

    mock_network_rep = {
        'id': '881d1bb7-a663-44c0-8f9f-ee2765b74486',
        'name': network_name,
    }

    mock_subnet_rep = {
        'allocation_pools': [{
            'start': u'192.168.199.2',
            'end': u'192.168.199.254'
        }],
        'cidr': subnet_cidr,
        'created_at': '2017-04-24T20:22:23Z',
        'description': '',
        'dns_nameservers': [],
        'enable_dhcp': False,
        'gateway_ip': '192.168.199.1',
        'host_routes': [],
        'id': subnet_id,
        'ip_version': 4,
        'ipv6_address_mode': None,
        'ipv6_ra_mode': None,
        'name': subnet_name,
        'network_id': mock_network_rep['id'],
        'project_id': '861808a93da0484ea1767967c4df8a23',
        'revision_number': 2,
        'service_types': [],
        'subnetpool_id': None,
        'tags': []
    }

    mock_subnetpool_rep = {
        'id': 'f49a1319-423a-4ee6-ba54-1d95a4f6cc68',
        'prefixes': [
            '172.16.0.0/16'
        ]
    }

    def test_get_subnet(self):
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'subnets']),
                 json={'subnets': [self.mock_subnet_rep]})
        ])
        r = self.cloud.get_subnet(self.subnet_name)
        self.assertIsNotNone(r)
        self.assertDictEqual(self.mock_subnet_rep, r)
        self.assert_calls()

    def test_get_subnet_by_id(self):
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0',
                                                  'subnets',
                                                  self.subnet_id]),
                 json={'subnet': self.mock_subnet_rep})
        ])
        r = self.cloud.get_subnet_by_id(self.subnet_id)
        self.assertIsNotNone(r)
        self.assertDictEqual(self.mock_subnet_rep, r)
        self.assert_calls()

    def test_create_subnet(self):
        pool = [{'start': '192.168.199.2', 'end': '192.168.199.254'}]
        dns = ['8.8.8.8']
        routes = [{"destination": "0.0.0.0/0", "nexthop": "123.456.78.9"}]
        mock_subnet_rep = copy.copy(self.mock_subnet_rep)
        mock_subnet_rep['allocation_pools'] = pool
        mock_subnet_rep['dns_nameservers'] = dns
        mock_subnet_rep['host_routes'] = routes
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'networks']),
                 json={'networks': [self.mock_network_rep]}),
            dict(method='POST',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'subnets']),
                 json={'subnet': mock_subnet_rep},
                 validate=dict(
                     json={'subnet': {
                         'cidr': self.subnet_cidr,
                         'enable_dhcp': False,
                         'ip_version': 4,
                         'network_id': self.mock_network_rep['id'],
                         'allocation_pools': pool,
                         'dns_nameservers': dns,
                         'host_routes': routes}}))
        ])
        subnet = self.cloud.create_subnet(self.network_name, self.subnet_cidr,
                                          allocation_pools=pool,
                                          dns_nameservers=dns,
                                          host_routes=routes)
        self.assertDictEqual(mock_subnet_rep, subnet)
        self.assert_calls()

    def test_create_subnet_string_ip_version(self):
        '''Allow ip_version as a string'''
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'networks']),
                 json={'networks': [self.mock_network_rep]}),
            dict(method='POST',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'subnets']),
                 json={'subnet': self.mock_subnet_rep},
                 validate=dict(
                     json={'subnet': {
                         'cidr': self.subnet_cidr,
                         'enable_dhcp': False,
                         'ip_version': 4,
                         'network_id': self.mock_network_rep['id']}}))
        ])
        subnet = self.cloud.create_subnet(
            self.network_name, self.subnet_cidr, ip_version='4')
        self.assertDictEqual(self.mock_subnet_rep, subnet)
        self.assert_calls()

    def test_create_subnet_bad_ip_version(self):
        '''String ip_versions must be convertable to int'''
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'networks']),
                 json={'networks': [self.mock_network_rep]})
        ])
        with testtools.ExpectedException(
                exc.OpenStackCloudException,
                "ip_version must be an integer"
        ):
            self.cloud.create_subnet(
                self.network_name, self.subnet_cidr, ip_version='4x')
        self.assert_calls()

    def test_create_subnet_without_gateway_ip(self):
        pool = [{'start': '192.168.199.2', 'end': '192.168.199.254'}]
        dns = ['8.8.8.8']
        mock_subnet_rep = copy.copy(self.mock_subnet_rep)
        mock_subnet_rep['allocation_pools'] = pool
        mock_subnet_rep['dns_nameservers'] = dns
        mock_subnet_rep['gateway_ip'] = None
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'networks']),
                 json={'networks': [self.mock_network_rep]}),
            dict(method='POST',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'subnets']),
                 json={'subnet': mock_subnet_rep},
                 validate=dict(
                     json={'subnet': {
                         'cidr': self.subnet_cidr,
                         'enable_dhcp': False,
                         'ip_version': 4,
                         'network_id': self.mock_network_rep['id'],
                         'allocation_pools': pool,
                         'gateway_ip': None,
                         'dns_nameservers': dns}}))
        ])
        subnet = self.cloud.create_subnet(self.network_name, self.subnet_cidr,
                                          allocation_pools=pool,
                                          dns_nameservers=dns,
                                          disable_gateway_ip=True)
        self.assertDictEqual(mock_subnet_rep, subnet)
        self.assert_calls()

    def test_create_subnet_with_gateway_ip(self):
        pool = [{'start': '192.168.199.8', 'end': '192.168.199.254'}]
        gateway = '192.168.199.2'
        dns = ['8.8.8.8']
        mock_subnet_rep = copy.copy(self.mock_subnet_rep)
        mock_subnet_rep['allocation_pools'] = pool
        mock_subnet_rep['dns_nameservers'] = dns
        mock_subnet_rep['gateway_ip'] = gateway
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'networks']),
                 json={'networks': [self.mock_network_rep]}),
            dict(method='POST',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'subnets']),
                 json={'subnet': mock_subnet_rep},
                 validate=dict(
                     json={'subnet': {
                         'cidr': self.subnet_cidr,
                         'enable_dhcp': False,
                         'ip_version': 4,
                         'network_id': self.mock_network_rep['id'],
                         'allocation_pools': pool,
                         'gateway_ip': gateway,
                         'dns_nameservers': dns}}))
        ])
        subnet = self.cloud.create_subnet(self.network_name, self.subnet_cidr,
                                          allocation_pools=pool,
                                          dns_nameservers=dns,
                                          gateway_ip=gateway)
        self.assertDictEqual(mock_subnet_rep, subnet)
        self.assert_calls()

    def test_create_subnet_conflict_gw_ops(self):
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'networks']),
                 json={'networks': [self.mock_network_rep]})
        ])
        gateway = '192.168.200.3'
        self.assertRaises(exc.OpenStackCloudException,
                          self.cloud.create_subnet, 'kooky',
                          self.subnet_cidr, gateway_ip=gateway,
                          disable_gateway_ip=True)
        self.assert_calls()

    def test_create_subnet_bad_network(self):
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'networks']),
                 json={'networks': [self.mock_network_rep]})
        ])
        self.assertRaises(exc.OpenStackCloudException,
                          self.cloud.create_subnet,
                          'duck', self.subnet_cidr)
        self.assert_calls()

    def test_create_subnet_non_unique_network(self):
        net1 = dict(id='123', name=self.network_name)
        net2 = dict(id='456', name=self.network_name)
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'networks']),
                 json={'networks': [net1, net2]})
        ])
        self.assertRaises(exc.OpenStackCloudException,
                          self.cloud.create_subnet,
                          self.network_name, self.subnet_cidr)
        self.assert_calls()

    def test_create_subnet_from_subnetpool_with_prefixlen(self):
        pool = [{'start': '172.16.0.2', 'end': '172.16.0.15'}]
        id = '143296eb-7f47-4755-835c-488123475604'
        gateway = '172.16.0.1'
        dns = ['8.8.8.8']
        routes = [{"destination": "0.0.0.0/0", "nexthop": "123.456.78.9"}]
        mock_subnet_rep = copy.copy(self.mock_subnet_rep)
        mock_subnet_rep['allocation_pools'] = pool
        mock_subnet_rep['dns_nameservers'] = dns
        mock_subnet_rep['host_routes'] = routes
        mock_subnet_rep['gateway_ip'] = gateway
        mock_subnet_rep['subnetpool_id'] = self.mock_subnetpool_rep['id']
        mock_subnet_rep['cidr'] = self.subnetpool_cidr
        mock_subnet_rep['id'] = id
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'networks']),
                 json={'networks': [self.mock_network_rep]}),
            dict(method='POST',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'subnets']),
                 json={'subnet': mock_subnet_rep},
                 validate=dict(
                     json={'subnet': {
                         'enable_dhcp': False,
                         'ip_version': 4,
                         'network_id': self.mock_network_rep['id'],
                         'allocation_pools': pool,
                         'dns_nameservers': dns,
                         'use_default_subnetpool': True,
                         'prefixlen': self.prefix_length,
                         'host_routes': routes}}))
        ])
        subnet = self.cloud.create_subnet(self.network_name,
                                          allocation_pools=pool,
                                          dns_nameservers=dns,
                                          use_default_subnetpool=True,
                                          prefixlen=self.prefix_length,
                                          host_routes=routes)
        self.assertDictEqual(mock_subnet_rep, subnet)
        self.assert_calls()

    def test_delete_subnet(self):
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'subnets']),
                 json={'subnets': [self.mock_subnet_rep]}),
            dict(method='DELETE',
                 uri=self.get_mock_url(
                     'network', 'public',
                     append=['v2.0', 'subnets', self.subnet_id]),
                 json={})
        ])
        self.assertTrue(self.cloud.delete_subnet(self.subnet_name))
        self.assert_calls()

    def test_delete_subnet_not_found(self):
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'subnets']),
                 json={'subnets': []})
        ])
        self.assertFalse(self.cloud.delete_subnet('goofy'))
        self.assert_calls()

    def test_delete_subnet_multiple_found(self):
        subnet1 = dict(id='123', name=self.subnet_name)
        subnet2 = dict(id='456', name=self.subnet_name)
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'subnets']),
                 json={'subnets': [subnet1, subnet2]})
        ])
        self.assertRaises(exc.OpenStackCloudException,
                          self.cloud.delete_subnet,
                          self.subnet_name)
        self.assert_calls()

    def test_delete_subnet_multiple_using_id(self):
        subnet1 = dict(id='123', name=self.subnet_name)
        subnet2 = dict(id='456', name=self.subnet_name)
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'subnets']),
                 json={'subnets': [subnet1, subnet2]}),
            dict(method='DELETE',
                 uri=self.get_mock_url(
                     'network', 'public',
                     append=['v2.0', 'subnets', subnet1['id']]),
                 json={})
        ])
        self.assertTrue(self.cloud.delete_subnet(subnet1['id']))
        self.assert_calls()

    def test_update_subnet(self):
        expected_subnet = copy.copy(self.mock_subnet_rep)
        expected_subnet['name'] = 'goofy'
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'subnets']),
                 json={'subnets': [self.mock_subnet_rep]}),
            dict(method='PUT',
                 uri=self.get_mock_url(
                     'network', 'public',
                     append=['v2.0', 'subnets', self.subnet_id]),
                 json={'subnet': expected_subnet},
                 validate=dict(
                     json={'subnet': {'name': 'goofy'}}))
        ])
        subnet = self.cloud.update_subnet(self.subnet_id, subnet_name='goofy')
        self.assertDictEqual(expected_subnet, subnet)
        self.assert_calls()

    def test_update_subnet_gateway_ip(self):
        expected_subnet = copy.copy(self.mock_subnet_rep)
        gateway = '192.168.199.3'
        expected_subnet['gateway_ip'] = gateway
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'subnets']),
                 json={'subnets': [self.mock_subnet_rep]}),
            dict(method='PUT',
                 uri=self.get_mock_url(
                     'network', 'public',
                     append=['v2.0', 'subnets', self.subnet_id]),
                 json={'subnet': expected_subnet},
                 validate=dict(
                     json={'subnet': {'gateway_ip': gateway}}))
        ])
        subnet = self.cloud.update_subnet(self.subnet_id, gateway_ip=gateway)
        self.assertDictEqual(expected_subnet, subnet)
        self.assert_calls()

    def test_update_subnet_disable_gateway_ip(self):
        expected_subnet = copy.copy(self.mock_subnet_rep)
        expected_subnet['gateway_ip'] = None
        self.register_uris([
            dict(method='GET',
                 uri=self.get_mock_url(
                     'network', 'public', append=['v2.0', 'subnets']),
                 json={'subnets': [self.mock_subnet_rep]}),
            dict(method='PUT',
                 uri=self.get_mock_url(
                     'network', 'public',
                     append=['v2.0', 'subnets', self.subnet_id]),
                 json={'subnet': expected_subnet},
                 validate=dict(
                     json={'subnet': {'gateway_ip': None}}))
        ])
        subnet = self.cloud.update_subnet(self.subnet_id,
                                          disable_gateway_ip=True)
        self.assertDictEqual(expected_subnet, subnet)
        self.assert_calls()

    def test_update_subnet_conflict_gw_ops(self):
        self.assertRaises(exc.OpenStackCloudException,
                          self.cloud.update_subnet,
                          self.subnet_id, gateway_ip="192.168.199.3",
                          disable_gateway_ip=True)
