# Copyright 2021 Huawei, Inc.
# 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 time

from neutron_lib import constants
from oslo_utils import uuidutils

from neutron.tests.fullstack import base
from neutron.tests.fullstack.resources import environment
from neutron.tests.fullstack.resources import machine
from neutron.tests.unit import testlib_api

load_tests = testlib_api.module_load_tests


class LocalIPTestCase(base.BaseFullStackTestCase):
    number_of_hosts = 2

    scenarios = [
        ('with_conntrack_rules', {'firewall_driver': 'noop'}),
        # with ovs firewall driver - test config will set static_nat=True
        ('static_nat', {'firewall_driver': 'openvswitch'}),
    ]

    def setUp(self):
        host_desc = [
            environment.HostDescription(
                l2_agent_type=constants.AGENT_TYPE_OVS,
                firewall_driver=self.firewall_driver,
                l3_agent=False,
                dhcp_agent=False) for _ in range(self.number_of_hosts)]
        env_desc = environment.EnvironmentDescription(
            mech_drivers='openvswitch',
            local_ip_ext=True)
        env = environment.Environment(env_desc, host_desc)
        super().setUp(env)
        self.project_id = uuidutils.generate_uuid()

        self.network = self.safe_client.create_network(
            self.project_id, 'network-local-ip-test')
        self.subnet_v4 = self.safe_client.create_subnet(
            self.project_id, self.network['id'],
            cidr='10.0.0.0/24',
            gateway_ip='10.0.0.1',
            name='subnet-v4-test')

    def _prepare_vms(self):
        port1 = self.safe_client.create_port(
            self.project_id, self.network['id'],
            self.environment.hosts[0].hostname,
            device_owner="compute:test_local_ip")

        port2 = self.safe_client.create_port(
            self.project_id, self.network['id'],
            self.environment.hosts[0].hostname,
            device_owner="compute:test_local_ip")

        port3 = self.safe_client.create_port(
            self.project_id, self.network['id'],
            self.environment.hosts[1].hostname,
            device_owner="compute:test_local_ip")

        vm1 = self.useFixture(
            machine.FakeFullstackMachine(
                self.environment.hosts[0],
                self.network['id'],
                self.project_id,
                self.safe_client,
                neutron_port=port1))

        vm2 = self.useFixture(
            machine.FakeFullstackMachine(
                self.environment.hosts[0],
                self.network['id'],
                self.project_id,
                self.safe_client,
                neutron_port=port2))

        vm_diff_host = self.useFixture(
            machine.FakeFullstackMachine(
                self.environment.hosts[1],
                self.network['id'],
                self.project_id,
                self.safe_client,
                neutron_port=port3))
        return machine.FakeFullstackMachinesList([vm1, vm2, vm_diff_host])

    def _create_local_ip(self):
        return self.safe_client.create_local_ip(
            self.project_id, network_id=self.network['id'])

    def _associate_local_ip(self, local_ip_id, port_id):
        return self.safe_client.create_local_ip_association(
            local_ip_id, port_id)

    def test_vm_is_accessible_by_local_ip(self):
        vms = self._prepare_vms()
        vms.block_until_all_boot()
        # first check basic connectivity between VMs
        vms.ping_all()

        local_ip = self._create_local_ip()
        self._associate_local_ip(
            local_ip['id'], vms[0].neutron_port['id'])
        # VM on same host should have access to this Local IP
        vms[1].block_until_ping(local_ip['local_ip_address'])

        # VM on different host shouldn't have access to this Local IP
        vms[2].assert_no_ping(local_ip['local_ip_address'])

        # NOTE(ykarel): response comes from local_ip assigned to vm0,
        # as a workaround sleep for arp cache expiry. It requires
        # fix in Local IP feature
        time.sleep(31)
        # check that VMs can still access each other with fixed IPs
        vms.ping_all()
