import unittest

from network_prepare import prepare
from network_readback import verify_host_overlap
from network_runtime import NetworkFailure
from network_test_fixture import Host, config, network


class RaceTests(unittest.TestCase):
    def test_created_bridge_routes_require_exact_interface_kind_and_prefix(self):
        created = network('first', '172.28.1.0/24')
        bridge = 'br-' + created['Id'][:12]
        records = [{'subnet': '172.28.1.0/24', 'kind': 'route_connected', 'device': bridge},
                   {'subnet': '172.28.1.1/32', 'kind': 'address_local', 'device': bridge},
                   {'subnet': '172.28.1.255/32', 'kind': 'route_broadcast', 'device': bridge}]
        inventory = {'host_records': records, 'host_excluded': [row['subnet'] for row in records]}
        verify_host_overlap(inventory, created, '172.28.1.0/24')
        for change in ({'device': 'foreign0'}, {'kind': 'resolver'}, {'subnet': '172.28.1.0/25'}):
            altered = dict(records[0], **change)
            with self.assertRaisesRegex(NetworkFailure, 'created_network_overlap'):
                verify_host_overlap({'host_records': [altered]}, created, '172.28.1.0/24')

    def test_host_only_conflict_does_not_authorize_an_uncertain_create_retry(self):
        host = Host()
        calls = []

        def command(argv, seconds=10):
            calls.append(argv)
            raise NetworkFailure('network_command_unavailable')

        def inventory(command, require_pools=True):
            result = host.inventory(command, require_pools=require_pools)
            if calls:
                result['excluded'].append('172.28.1.0/25')
            return result

        with self.assertRaisesRegex(NetworkFailure, 'network_create_unconfirmed'):
            prepare(config('first'), 'first', **dict(host.arguments(), reader=inventory, command=command))
        self.assertEqual(len(calls), 1)

    def test_verified_competing_docker_allocation_permits_a_fresh_candidate(self):
        host = Host()
        attempts = []

        def command(argv, seconds=10):
            attempts.append(argv)
            if len(attempts) == 1:
                host.networks.append(network('competing', '172.28.1.0/24'))
                raise NetworkFailure('network_command_unavailable')
            return host.command(argv, seconds=seconds)

        result = prepare(config('first'), 'first', **dict(host.arguments(), command=command))
        self.assertEqual(result['networks'][0]['subnets'], ['172.28.2.0/24'])
        self.assertEqual(len(attempts), 2)
        self.assertEqual(len(host.networks), 3)

    def test_preserved_and_authored_networks_skip_allocation_only_host_admission(self):
        host = Host()
        host.networks.append(network('existing', '172.29.0.0/16'))

        def refused(command, require_pools=True):
            self.fail('Existing/authored networks must not require allocation host admission')

        for project, settings in (('existing', {}), ('external', {'external': True}),
                                  ('custom', {'name': 'custom-name'}),
                                  ('authored', {'ipam': {'config': [{'subnet': '10.23.0.0/24'}]}})):
            result = prepare(config(project, settings), project, **dict(host.arguments(), reader=refused))
            self.assertTrue(result['ok'])
        self.assertEqual(host.calls, [])


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