# 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

import botocore.exceptions
from oslo_log import log
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
import testtools

from ec2api_tempest_plugin import base
from ec2api_tempest_plugin import config

CONF = config.CONF

LOG = log.getLogger(__name__)


class NetworkInterfaceTest(base.EC2TestCase):

    VPC_CIDR = '10.7.0.0/20'
    vpc_id = None
    SUBNET_CIDR = '10.7.0.0/28'
    subnet_id = None

    @classmethod
    @base.safe_setup
    def setUpClass(cls):
        super(NetworkInterfaceTest, cls).setUpClass()
        if not base.TesterStateHolder().get_vpc_enabled():
            raise cls.skipException('VPC is disabled')

        data = cls.client.create_vpc(CidrBlock=cls.VPC_CIDR)
        cls.vpc_id = data['Vpc']['VpcId']
        cls.addResourceCleanUpStatic(cls.client.delete_vpc, VpcId=cls.vpc_id)
        cls.get_vpc_waiter().wait_available(cls.vpc_id)

        aws_zone = CONF.aws.aws_zone
        data = cls.client.create_subnet(VpcId=cls.vpc_id,
                                             CidrBlock=cls.SUBNET_CIDR,
                                             AvailabilityZone=aws_zone)
        cls.subnet_id = data['Subnet']['SubnetId']
        cls.addResourceCleanUpStatic(cls.client.delete_subnet,
                                     SubnetId=cls.subnet_id)
        cls.get_subnet_waiter().wait_available(cls.subnet_id)

    def _wait_assignment(self, ni_id, data):
        # NOTE(andrey-mp): Amazon don't do it quickly and there is no way
        # to wait this request
        time.sleep(5)

    @decorators.idempotent_id('d03f49b1-a77e-439b-96e2-5e152b968863')
    def test_delete_subnet_with_network_interface(self):
        data = self.client.create_subnet(VpcId=self.vpc_id,
                                              CidrBlock='10.7.1.0/28')
        subnet_id = data['Subnet']['SubnetId']
        res_clean_subnet = self.addResourceCleanUp(self.client.delete_subnet,
                                                   SubnetId=subnet_id)
        self.get_subnet_waiter().wait_available(subnet_id)

        data = self.client.create_network_interface(SubnetId=subnet_id)
        ni_id = data['NetworkInterface']['NetworkInterfaceId']
        res_clean_ni = self.addResourceCleanUp(
            self.client.delete_network_interface, NetworkInterfaceId=ni_id)
        self.get_network_interface_waiter().wait_available(ni_id)
        time.sleep(2)

        self.assertRaises('DependencyViolation',
                          self.client.delete_subnet,
                          SubnetId=subnet_id)

        self.client.delete_network_interface(NetworkInterfaceId=ni_id)
        self.cancelResourceCleanUp(res_clean_ni)
        self.get_network_interface_waiter().wait_delete(ni_id)

        self.client.delete_subnet(SubnetId=subnet_id)
        self.cancelResourceCleanUp(res_clean_subnet)
        self.get_subnet_waiter().wait_delete(subnet_id)

    @decorators.idempotent_id('e19e450d-5c24-47b1-9814-4a65a78e5a31')
    def test_create_network_interface(self):
        desc = data_utils.rand_name('ni')
        data = self.client.create_network_interface(SubnetId=self.subnet_id,
                                                    Description=desc)
        ni_id = data['NetworkInterface']['NetworkInterfaceId']
        res_clean = self.addResourceCleanUp(
            self.client.delete_network_interface, NetworkInterfaceId=ni_id)
        ni = data['NetworkInterface']
        self.assertEqual(self.vpc_id, ni['VpcId'])
        self.assertEqual(self.subnet_id, ni['SubnetId'])
        self.assertEqual(desc, ni['Description'])

        self.assertNotEmpty(ni.get('Groups'))
        self.assertEqual('default', ni['Groups'][0]['GroupName'])

        address = ni.get('PrivateIpAddress')
        self.assertIsNotNone(address)
        addresses = ni.get('PrivateIpAddresses')
        self.assertIsNotNone(addresses)
        self.assertEqual(1, len(addresses))
        self.assertTrue(addresses[0]['Primary'])
        self.assertEqual(address, addresses[0]['PrivateIpAddress'])

        self.assertIsNotNone(ni.get('MacAddress'))
        self.assertIsNotNone(ni.get('OwnerId'))
        self.assertIsNotNone(ni.get('RequesterManaged'))
        self.assertIsNotNone(ni.get('SourceDestCheck'))

        self.get_network_interface_waiter().wait_available(ni_id)

        self.client.delete_network_interface(NetworkInterfaceId=ni_id)
        self.cancelResourceCleanUp(res_clean)
        self.get_network_interface_waiter().wait_delete(ni_id)

        self.assertRaises('InvalidNetworkInterfaceID.NotFound',
                          self.client.describe_network_interfaces,
                          NetworkInterfaceIds=[ni_id])

    # TODO(andrey-mp): add creation with addresses

    @decorators.idempotent_id('61e16648-7736-4647-b618-27d3f4f0c9c6')
    def test_create_max_network_interface(self):
        # NOTE(andrey-mp): wait some time while all ports will be deleted
        # for this subnet(that are deleting after previous test)
        time.sleep(5)

        data = self.client.describe_subnets(SubnetIds=[self.subnet_id])
        count_before = data['Subnets'][0]['AvailableIpAddressCount']
        addresses = []
        while True:
            try:
                data = self.client.create_network_interface(
                    SubnetId=self.subnet_id)
            except botocore.exceptions.ClientError as e:
                error_code = e.response['Error']['Code']
                self.assertEqual('InsufficientFreeAddressesInSubnet',
                                 error_code)
                break
            ni_id = data['NetworkInterface']['NetworkInterfaceId']
            res_clean = self.addResourceCleanUp(
                self.client.delete_network_interface,
                NetworkInterfaceId=ni_id)
            addresses.append((ni_id, res_clean))

        data = self.client.describe_subnets(SubnetIds=[self.subnet_id])
        count_after = data['Subnets'][0]['AvailableIpAddressCount']
        # NOTE(andrey-mp): This is strange but Amazon can't create last NI
        # and Openstack can
        self.assertIn(count_after, [0, 1])
        self.assertEqual(len(addresses), count_before - count_after)

        for addr in addresses:
            self.client.delete_network_interface(NetworkInterfaceId=addr[0])
            self.cancelResourceCleanUp(addr[1])
            self.get_network_interface_waiter().wait_delete(addr[0])

    @decorators.idempotent_id('8c174e5f-e377-4bf2-9315-b868a8199c17')
    def test_unassign_primary_addresses(self):
        data = self.client.create_network_interface(SubnetId=self.subnet_id)
        ni_id = data['NetworkInterface']['NetworkInterfaceId']
        res_clean = self.addResourceCleanUp(
            self.client.delete_network_interface, NetworkInterfaceId=ni_id)
        primary_address = data['NetworkInterface'].get('PrivateIpAddress')
        self.get_network_interface_waiter().wait_available(ni_id)

        self.assertRaises('InvalidParameterValue',
                          self.client.unassign_private_ip_addresses,
                          NetworkInterfaceId=ni_id,
                          PrivateIpAddresses=[primary_address])

        self.client.delete_network_interface(NetworkInterfaceId=ni_id)
        self.cancelResourceCleanUp(res_clean)
        self.get_network_interface_waiter().wait_delete(ni_id)

    @decorators.idempotent_id('de0d0375-d99a-476c-939a-0e15c4e431a8')
    def test_assign_unassign_private_addresses_by_count(self):
        data = self.client.describe_subnets(SubnetIds=[self.subnet_id])
        count = data['Subnets'][0]['AvailableIpAddressCount']
        data = self.client.create_network_interface(SubnetId=self.subnet_id)
        ni_id = data['NetworkInterface']['NetworkInterfaceId']
        res_clean = self.addResourceCleanUp(
            self.client.delete_network_interface, NetworkInterfaceId=ni_id)
        self.get_network_interface_waiter().wait_available(ni_id)

        data = self.client.assign_private_ip_addresses(
            NetworkInterfaceId=ni_id,
            SecondaryPrivateIpAddressCount=2)
        self._wait_assignment(ni_id, data)

        data = self.client.describe_subnets(SubnetIds=[self.subnet_id])
        count_after = data['Subnets'][0]['AvailableIpAddressCount']
        self.assertEqual(count - 3, count_after)

        data = self.client.describe_network_interfaces(
            NetworkInterfaceIds=[ni_id])

        addresses = []
        for addr in data['NetworkInterfaces'][0]['PrivateIpAddresses']:
            if not addr['Primary']:
                addresses.append(addr['PrivateIpAddress'])
        self.assertEqual(2, len(addresses))

        data = self.client.unassign_private_ip_addresses(
            NetworkInterfaceId=ni_id,
            PrivateIpAddresses=addresses)
        self._wait_assignment(ni_id, data)

        data = self.client.describe_subnets(SubnetIds=[self.subnet_id])
        count_after = data['Subnets'][0]['AvailableIpAddressCount']
        self.assertEqual(count - 1, count_after)

        self.client.delete_network_interface(NetworkInterfaceId=ni_id)
        self.cancelResourceCleanUp(res_clean)
        self.get_network_interface_waiter().wait_delete(ni_id)

    @decorators.idempotent_id('5d7bda42-d23e-4cbf-9e66-8ca052ac28ff')
    def test_assign_unassign_private_addresses_by_addresses(self):
        data = self.client.describe_subnets(SubnetIds=[self.subnet_id])
        count = data['Subnets'][0]['AvailableIpAddressCount']
        data = self.client.create_network_interface(SubnetId=self.subnet_id)
        ni_id = data['NetworkInterface']['NetworkInterfaceId']
        res_clean = self.addResourceCleanUp(
            self.client.delete_network_interface, NetworkInterfaceId=ni_id)
        self.get_network_interface_waiter().wait_available(ni_id)

        ni = self.client.describe_network_interfaces(
            NetworkInterfaceIds=[ni_id])['NetworkInterfaces']
        ni_addr = ni[0]['PrivateIpAddresses'][0]['PrivateIpAddress']

        # add two more addresses to interface.
        # check that they does not equal to current.
        addresses = []
        for i in range(10, 13):
            addr = '10.7.0.%d' % i
            if addr != ni_addr:
                addresses.append(addr)
            if len(addresses) >= 2:
                break
        data = self.client.assign_private_ip_addresses(
            NetworkInterfaceId=ni_id,
            PrivateIpAddresses=addresses)
        self._wait_assignment(ni_id, data)

        data = self.client.describe_subnets(SubnetIds=[self.subnet_id])
        count_after = data['Subnets'][0]['AvailableIpAddressCount']
        # NOTE(Alex): Amazon misses 1 IP address by some reason.
        self.assertIn(count_after, [count - 3, count - 4])

        data = self.client.describe_network_interfaces(
            NetworkInterfaceIds=[ni_id])

        assigned_addresses = []
        for addr in data['NetworkInterfaces'][0]['PrivateIpAddresses']:
            if not addr['Primary']:
                self.assertIn(addr['PrivateIpAddress'], addresses)
                assigned_addresses.append(addr['PrivateIpAddress'])
        self.assertEqual(2, len(assigned_addresses))

        data = self.client.unassign_private_ip_addresses(
            NetworkInterfaceId=ni_id,
            PrivateIpAddresses=addresses)
        self._wait_assignment(ni_id, data)

        data = self.client.describe_subnets(SubnetIds=[self.subnet_id])
        count_after = data['Subnets'][0]['AvailableIpAddressCount']
        self.assertIn(count_after, [count - 1, count - 2])

        self.client.delete_network_interface(NetworkInterfaceId=ni_id)
        self.cancelResourceCleanUp(res_clean)
        self.get_network_interface_waiter().wait_delete(ni_id)

    @decorators.idempotent_id('0c514bb4-5800-4db0-9032-0aa3ab998612')
    @testtools.skipUnless(CONF.aws.image_id, "image id is not defined")
    def test_attach_network_interface(self):
        data = self.client.create_network_interface(SubnetId=self.subnet_id)
        ni_id = data['NetworkInterface']['NetworkInterfaceId']
        self.addResourceCleanUp(self.client.delete_network_interface,
                                NetworkInterfaceId=ni_id)
        ni = data['NetworkInterface']
        address = ni.get('PrivateIpAddress')
        self.assertIsNotNone(address)
        self.get_network_interface_waiter().wait_available(ni_id)

        instance_id = self.run_instance(SubnetId=self.subnet_id)

        # NOTE(andrey-mp): Amazon can't attach to device index = 0
        kwargs = {
            'DeviceIndex': 0,
            'InstanceId': instance_id,
            'NetworkInterfaceId': ni_id
        }
        self.assertRaises('InvalidParameterValue',
                          self.client.attach_network_interface,
                          **kwargs)

        kwargs = {
            'DeviceIndex': 2,
            'InstanceId': instance_id,
            'NetworkInterfaceId': ni_id
        }
        data = self.client.attach_network_interface(*[], **kwargs)
        attachment_id = data['AttachmentId']

        instance = self.get_instance(instance_id)
        nis = instance.get('NetworkInterfaces', [])
        self.assertEqual(2, len(nis))
        ids = [nis[0]['Attachment']['AttachmentId'],
               nis[1]['Attachment']['AttachmentId']]
        self.assertIn(attachment_id, ids)

        self.assertRaises('InvalidParameterValue',
                          self.client.delete_network_interface,
                          NetworkInterfaceId=ni_id)

        self.client.detach_network_interface(AttachmentId=attachment_id)

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

    @decorators.idempotent_id('381c9995-bc83-4e7e-b716-25a451660ace')
    @testtools.skipUnless(CONF.aws.image_id, "image id is not defined")
    def test_network_interfaces_are_not_deleted_on_termination(self):
        instance_id = self.run_instance(SubnetId=self.subnet_id)

        instance = self.get_instance(instance_id)
        nis = instance.get('NetworkInterfaces', [])
        self.assertEqual(1, len(nis))
        self.assertTrue(nis[0]['Attachment']['DeleteOnTermination'])
        ni_id = nis[0]['NetworkInterfaceId']
        attachment_id = nis[0]['Attachment']['AttachmentId']

        kwargs = {
            'NetworkInterfaceId': ni_id,
            'Attachment': {
                'AttachmentId': attachment_id,
                'DeleteOnTermination': False,
            }
        }
        self.client.modify_network_interface_attribute(*[], **kwargs)
        clean_ni = self.addResourceCleanUp(
            self.client.delete_network_interface, NetworkInterfaceId=ni_id)

        data = self.client.create_network_interface(SubnetId=self.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 = {
            'DeviceIndex': 2,
            'InstanceId': instance_id,
            'NetworkInterfaceId': ni_id2
        }
        data = self.client.attach_network_interface(*[], **kwargs)
        attachment_id = data['AttachmentId']

        instance = self.get_instance(instance_id)
        nis = instance.get('NetworkInterfaces', [])
        self.assertEqual(2, len(nis))
        ni = nis[0]
        if ni['Attachment']['AttachmentId'] != attachment_id:
            ni = nis[1]
        self.assertEqual(attachment_id, ni['Attachment']['AttachmentId'])
        self.assertFalse(ni['Attachment']['DeleteOnTermination'])

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

        self.get_network_interface_waiter().wait_available(ni_id)
        self.get_network_interface_waiter().wait_available(ni_id2)

        self.client.delete_network_interface(NetworkInterfaceId=ni_id)
        self.cancelResourceCleanUp(clean_ni)
        self.get_network_interface_waiter().wait_delete(ni_id)

        self.client.delete_network_interface(NetworkInterfaceId=ni_id2)
        self.cancelResourceCleanUp(clean_ni2)
        self.get_network_interface_waiter().wait_delete(ni_id2)

    @decorators.idempotent_id('de910bc7-008a-40c2-b4b2-4587a489fc1c')
    @testtools.skipUnless(CONF.aws.image_id, "image id is not defined")
    def test_network_interfaces_are_deleted_on_termination(self):
        instance_id = self.run_instance(SubnetId=self.subnet_id)

        instance = self.get_instance(instance_id)
        nis = instance.get('NetworkInterfaces', [])
        self.assertEqual(1, len(nis))
        self.assertTrue(nis[0]['Attachment']['DeleteOnTermination'])
        ni_id = nis[0]['NetworkInterfaceId']

        data = self.client.create_network_interface(SubnetId=self.subnet_id)
        ni_id2 = data['NetworkInterface']['NetworkInterfaceId']
        self.addResourceCleanUp(self.client.delete_network_interface,
                                NetworkInterfaceId=ni_id2)
        self.get_network_interface_waiter().wait_available(ni_id2)
        kwargs = {
            'DeviceIndex': 2,
            'InstanceId': instance_id,
            'NetworkInterfaceId': ni_id2
        }
        data = self.client.attach_network_interface(*[], **kwargs)
        attachment_id = data['AttachmentId']

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

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

        self.get_network_interface_waiter().wait_delete(ni_id)
        self.get_network_interface_waiter().wait_delete(ni_id2)

    @decorators.idempotent_id('028eb864-59e9-4ed6-a062-9d5de9eba652')
    def test_network_interface_attribute_description(self):
        desc = data_utils.rand_name('ni')
        data = self.client.create_network_interface(
            SubnetId=self.subnet_id, Description=desc)
        ni_id = data['NetworkInterface']['NetworkInterfaceId']
        res_clean = self.addResourceCleanUp(
            self.client.delete_network_interface, NetworkInterfaceId=ni_id)
        self.get_network_interface_waiter().wait_available(ni_id)

        data = self.client.describe_network_interface_attribute(
            NetworkInterfaceId=ni_id, Attribute='description')
        self.assertEqual(desc, data['Description']['Value'])

        new_desc = data_utils.rand_name('new-ni')
        self.client.modify_network_interface_attribute(
            NetworkInterfaceId=ni_id, Description={'Value': new_desc})

        data = self.client.describe_network_interface_attribute(
            NetworkInterfaceId=ni_id, Attribute='description')
        self.assertEqual(new_desc, data['Description']['Value'])

        self.client.delete_network_interface(NetworkInterfaceId=ni_id)
        self.cancelResourceCleanUp(res_clean)
        self.get_network_interface_waiter().wait_delete(ni_id)

    @decorators.idempotent_id('9428b5e6-42f2-495f-a535-df53d1fcf4af')
    def test_network_interface_attribute_source_dest_check(self):
        data = self.client.create_network_interface(SubnetId=self.subnet_id)
        ni_id = data['NetworkInterface']['NetworkInterfaceId']
        res_clean = self.addResourceCleanUp(
            self.client.delete_network_interface, NetworkInterfaceId=ni_id)
        self.get_network_interface_waiter().wait_available(ni_id)

        self.client.modify_network_interface_attribute(
            NetworkInterfaceId=ni_id, SourceDestCheck={'Value': False})

        data = self.client.describe_network_interface_attribute(
            NetworkInterfaceId=ni_id, Attribute='sourceDestCheck')
        self.assertFalse(data['SourceDestCheck']['Value'])

        # NOTE(andrey-mp): ResetNetworkInterfaceAttribute had inadequate json
        # scheme in botocore and doesn't work against Amazon.

        self.client.modify_network_interface_attribute(
            NetworkInterfaceId=ni_id, SourceDestCheck={'Value': True})

        data = self.client.describe_network_interface_attribute(
            NetworkInterfaceId=ni_id, Attribute='sourceDestCheck')
        self.assertEqual(True, data['SourceDestCheck']['Value'])

        self.client.delete_network_interface(NetworkInterfaceId=ni_id)
        self.cancelResourceCleanUp(res_clean)
        self.get_network_interface_waiter().wait_delete(ni_id)

    @decorators.idempotent_id('19d25f59-5b32-4314-b4da-7c8f679b7a96')
    @testtools.skipUnless(CONF.aws.image_id, "image id is not defined")
    def test_network_interface_attribute_attachment(self):
        instance_id = self.run_instance(SubnetId=self.subnet_id)

        instance = self.get_instance(instance_id)
        nis = instance.get('NetworkInterfaces', [])
        self.assertEqual(1, len(nis))
        self.assertTrue(nis[0]['Attachment']['DeleteOnTermination'])
        ni_id = nis[0]['NetworkInterfaceId']

        data = self.client.describe_network_interface_attribute(
            NetworkInterfaceId=ni_id, Attribute='attachment')
        self.assertIn('Attachment', data)
        self.assertTrue(data['Attachment'].get('AttachmentId'))
        self.assertTrue(data['Attachment'].get('DeleteOnTermination'))
        self.assertEqual(0, data['Attachment'].get('DeviceIndex'))
        self.assertEqual(instance_id, data['Attachment'].get('InstanceId'))
        self.assertEqual('attached', data['Attachment'].get('Status'))

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

    @decorators.idempotent_id('74967cd0-155f-4cfe-994e-2c6803dad04c')
    def test_network_interface_attribute_empty_attachment(self):
        data = self.client.create_network_interface(SubnetId=self.subnet_id)
        ni_id = data['NetworkInterface']['NetworkInterfaceId']
        res_clean = self.addResourceCleanUp(
            self.client.delete_network_interface, NetworkInterfaceId=ni_id)
        self.get_network_interface_waiter().wait_available(ni_id)

        data = self.client.describe_network_interface_attribute(
            NetworkInterfaceId=ni_id, Attribute='attachment')
        self.assertNotIn('Attachment', data)

        self.client.delete_network_interface(NetworkInterfaceId=ni_id)
        self.cancelResourceCleanUp(res_clean)
        self.get_network_interface_waiter().wait_delete(ni_id)

    @decorators.idempotent_id('a55f1169-d302-4166-b74e-e84a0d79129c')
    def test_network_interface_attribute_group_set(self):
        data = self.client.create_network_interface(SubnetId=self.subnet_id)
        ni_id = data['NetworkInterface']['NetworkInterfaceId']
        res_clean = self.addResourceCleanUp(
            self.client.delete_network_interface, NetworkInterfaceId=ni_id)
        self.get_network_interface_waiter().wait_available(ni_id)

        data = self.client.describe_network_interface_attribute(
            NetworkInterfaceId=ni_id, Attribute='groupSet')
        self.assertIn('Groups', data)
        self.assertEqual(1, len(data['Groups']))
        self.assertEqual('default', data['Groups'][0]['GroupName'])

        self.client.delete_network_interface(NetworkInterfaceId=ni_id)
        self.cancelResourceCleanUp(res_clean)
        self.get_network_interface_waiter().wait_delete(ni_id)

    @decorators.idempotent_id('7832976f-27cb-405e-ab05-466b102d86f8')
    def test_instance_attributes_negative(self):
        data = self.client.create_network_interface(SubnetId=self.subnet_id)
        ni_id = data['NetworkInterface']['NetworkInterfaceId']
        res_clean = self.addResourceCleanUp(
            self.client.delete_network_interface, NetworkInterfaceId=ni_id)
        self.get_network_interface_waiter().wait_available(ni_id)

        self.assertRaises('InvalidParameterCombination',
            self.client.describe_network_interface_attribute,
            NetworkInterfaceId=ni_id)
        self.assertRaises('InvalidParameterValue',
            self.client.describe_network_interface_attribute,
            NetworkInterfaceId=ni_id, Attribute='fake')
        self.assertRaises('InvalidNetworkInterfaceID.NotFound',
            self.client.describe_network_interface_attribute,
            NetworkInterfaceId='eni-0', Attribute='description')

        self.assertRaises('InvalidParameterCombination',
            self.client.modify_network_interface_attribute,
            NetworkInterfaceId=ni_id)
        self.assertRaises('MissingParameter',
            self.client.modify_network_interface_attribute,
            NetworkInterfaceId=ni_id,
            Attachment={'AttachmentId': 'fake'})
        self.assertRaises('InvalidAttachmentID.NotFound',
            self.client.modify_network_interface_attribute,
            NetworkInterfaceId=ni_id,
            Attachment={'AttachmentId': 'eni-attach-0',
                        'DeleteOnTermination': True})
        self.assertRaises('InvalidGroup.NotFound',
            self.client.modify_network_interface_attribute,
            NetworkInterfaceId=ni_id, Groups=['sg-0'])
        self.assertRaises('InvalidParameterCombination',
            self.client.modify_network_interface_attribute,
            NetworkInterfaceId=ni_id, Description={})

        self.client.delete_network_interface(NetworkInterfaceId=ni_id)
        self.cancelResourceCleanUp(res_clean)
        self.get_network_interface_waiter().wait_delete(ni_id)
