# Copyright 2014 OpenStack Foundation
# 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 oslo_log import log
from tempest.lib import decorators
import testtools

from ec2api_tempest_plugin import base
from ec2api_tempest_plugin import config
from ec2api_tempest_plugin.scenario import base as scenario_base

CONF = config.CONF
LOG = log.getLogger(__name__)


class VpcAddressTest(scenario_base.BaseScenarioTest):

    @base.skip_without_vpc()
    @decorators.idempotent_id('aa667fc6-fd9e-4664-92b8-23263d643d9e')
    @testtools.skipUnless(CONF.aws.image_id, "image id is not defined")
    def test_auto_diassociate_address(self):
        vpc_id, subnet_id = self.create_vpc_and_subnet('10.3.0.0/20')
        ni_id1 = self.create_network_interface(subnet_id)
        gw_id = self.create_and_attach_internet_gateway(vpc_id)
        self.prepare_route(vpc_id, gw_id)
        alloc_id1, public_ip1 = self.allocate_address(True)
        alloc_id2, _ = self.allocate_address(True)

        data = self.client.create_network_interface(SubnetId=subnet_id)
        ni_id2 = data['NetworkInterface']['NetworkInterfaceId']
        clean_ni2 = self.addResourceCleanUp(
            self.client.delete_network_interface, NetworkInterfaceId=ni_id2)
        self.get_network_interface_waiter().wait_available(ni_id2)

        kwargs = {
            'ImageId': CONF.aws.image_id,
            'InstanceType': CONF.aws.instance_type,
            'MinCount': 1,
            'MaxCount': 1,
            'NetworkInterfaces': [
                {'NetworkInterfaceId': ni_id1, 'DeviceIndex': 0}]
        }
        data = self.client.run_instances(*[], **kwargs)
        instance_id = data['Instances'][0]['InstanceId']
        clean_i = self.addResourceCleanUp(self.client.terminate_instances,
                                          InstanceIds=[instance_id])
        self.get_instance_waiter().wait_available(instance_id,
                                                  final_set=('running'))
        data = self.client.attach_network_interface(DeviceIndex=1,
            InstanceId=instance_id, NetworkInterfaceId=ni_id2)
        attachment_id = data['AttachmentId']

        # There are multiple interfaces attached to instance 'i-5310c5af'.
        # Please specify an interface ID for the operation instead.
        self.assertRaises('InvalidInstanceID',
            self.client.associate_address,
            InstanceId=instance_id, AllocationId=alloc_id1)

        # The networkInterface ID 'eni-ffffffff' does not exist
        self.assertRaises('InvalidNetworkInterfaceID.NotFound',
            self.client.associate_address,
            AllocationId=alloc_id1, NetworkInterfaceId='eni-ffffffff')

        # NOTE(andrey-mp): Amazon needs only network interface if several
        # present in instance. Error will be there if instance is passed.
        data = self.client.associate_address(
            AllocationId=alloc_id1, NetworkInterfaceId=ni_id1)
        assoc_id1 = data['AssociationId']
        clean_aa1 = self.addResourceCleanUp(self.client.disassociate_address,
                                            AssociationId=assoc_id1)
        self.get_address_assoc_waiter().wait_available(
            {'AllocationId': alloc_id1})

        instance = self.get_instance(instance_id)
        nis = instance.get('NetworkInterfaces', [])
        self.assertEqual(2, len(nis))
        for ni in nis:
            if ni['NetworkInterfaceId'] == ni_id1:
                self.assertIsNotNone(ni.get('Association'))
                self.assertEqual(public_ip1, ni['Association']['PublicIp'])
            elif ni['NetworkInterfaceId'] == ni_id2:
                self.assertIsNone(ni.get('Association'))
            else:
                self.assertTrue(False, 'Unknown interface found: ' + str(ni))

        data = self.client.describe_network_interfaces(
            NetworkInterfaceIds=[ni_id1, ni_id2])
        self.assertEqual(2, len(data['NetworkInterfaces']))
        self.assertEqual('in-use', data['NetworkInterfaces'][0]['Status'])
        self.assertEqual('in-use', data['NetworkInterfaces'][1]['Status'])

        # NOTE(andrery-mp): associate second address and set delete on
        # termination to True for interface
        data = self.client.associate_address(
            AllocationId=alloc_id2, NetworkInterfaceId=ni_id2)
        assoc_id2 = data['AssociationId']
        clean_aa2 = self.addResourceCleanUp(self.client.disassociate_address,
                                            AssociationId=assoc_id2)
        self.get_address_assoc_waiter().wait_available(
            {'AllocationId': alloc_id2})

        kwargs = {
            'NetworkInterfaceId': ni_id2,
            'Attachment': {
                'AttachmentId': attachment_id,
                'DeleteOnTermination': True,
            }
        }
        self.client.modify_network_interface_attribute(*[], **kwargs)

        # NOTE(andrey-mp): cleanup
        time.sleep(3)

        self.client.terminate_instances(InstanceIds=[instance_id])
        self.cancelResourceCleanUp(clean_i)
        self.get_instance_waiter().wait_delete(instance_id)

        self.assertRaises('InvalidNetworkInterfaceID.NotFound',
            self.client.describe_network_interfaces,
            NetworkInterfaceIds=[ni_id2])
        self.cancelResourceCleanUp(clean_ni2)
        self.cancelResourceCleanUp(clean_aa2)

        data = self.client.describe_network_interfaces(
            NetworkInterfaceIds=[ni_id1])
        self.assertEqual(1, len(data['NetworkInterfaces']))
        self.assertEqual('available', data['NetworkInterfaces'][0]['Status'])
        ni = data['NetworkInterfaces'][0]
        self.assertIsNotNone(ni.get('Association'))
        self.assertEqual(public_ip1, ni['Association']['PublicIp'])

        data = self.client.describe_addresses(AllocationIds=[alloc_id1,
                                                             alloc_id2])
        for address in data['Addresses']:
            if address['AllocationId'] == alloc_id1:
                self.assertIsNotNone(address.get('AssociationId'))
            elif address['AllocationId'] == alloc_id2:
                self.assertIsNone(address.get('AssociationId'))

        self.client.disassociate_address(AssociationId=assoc_id1)
        self.cancelResourceCleanUp(clean_aa1)
        self.get_address_assoc_waiter().wait_delete(
            {'AllocationId': alloc_id1})
