Example #1
0
 def initialize(self):
     """Initialize subnet extension driver."""
     self.subnet_cache = {}
     self.vpc_cidr_cache = {}
     self.aws_obj = AwsUtils()
     self.physical_network_cache = {}
     self.ec2_client_cache = {}
     self.ec2_cache_timer = datetime.datetime.now()
     self._ks_session = None
     LOG.info("SubnetExtensionDriver initialization complete")
Example #2
0
 def test_add_router_interface(self, mock_create, mock_get, mock_vpc,
                               mock_add):
     aws_obj = AwsUtils()
     vpc_id = aws_obj.create_vpc_and_tags(self.context.current['cidr'],
                                          self._get_fake_tags())
     interface_info = {'subnet_id': '00000000-0000-0000-0000-000000000000'}
     response = self._create_router(mock_create)
     router_id = response['id']
     mock_get.return_value = {'network_id': 'fake_network_id'}
     # We need to mock 'get_vpc_from_neutron_network_id' from aws_utils,
     # because we need a valid vpc_id when attaching internet gateway.
     mock_vpc.return_value = vpc_id
     mock_add.return_value = {
         'id': 'fake_id',
         'subnet_id': 'fake_subnet_id'
     }
     response = self._driver.add_router_interface(self.context, router_id,
                                                  interface_info)
     self.assertIsInstance(response, dict)
     mock_add.assert_called_once_with(self.context, router_id,
                                      interface_info)
Example #3
0
 def __init__(self):
     self.aws_utils = AwsUtils()
     super(AwsRouterPlugin, self).__init__()
     l3_db.subscribe()
Example #4
0
class AwsRouterPlugin(
        service_base.ServicePluginBase, common_db_mixin.CommonDbMixin,
        extraroute_db.ExtraRoute_db_mixin, l3_hamode_db.L3_HA_NAT_db_mixin,
        l3_gwmode_db.L3_NAT_db_mixin, l3_dvrscheduler_db.L3_DVRsch_db_mixin,
        l3_hascheduler_db.L3_HA_scheduler_db_mixin):
    """Implementation of the Neutron L3 Router Service Plugin.

    This class implements a L3 service plugin that provides
    router and floatingip resources and manages associated
    request/response.
    All DB related work is implemented in classes
    l3_db.L3_NAT_db_mixin, l3_hamode_db.L3_HA_NAT_db_mixin,
    l3_dvr_db.L3_NAT_with_dvr_db_mixin, and extraroute_db.ExtraRoute_db_mixin.
    """
    supported_extension_aliases = [
        "dvr", "router", "ext-gw-mode", "extraroute", "l3_agent_scheduler",
        "l3-ha", "security-group"
    ]

    @resource_registry.tracked_resources(
        router=l3_db.Router,
        floatingip=l3_db.FloatingIP,
        security_group=securitygroups_db.SecurityGroup)
    def __init__(self):
        self.aws_utils = AwsUtils()
        super(AwsRouterPlugin, self).__init__()
        l3_db.subscribe()

    def get_plugin_type(self):
        return constants.L3_ROUTER_NAT

    def get_plugin_description(self):
        """returns string description of the plugin."""
        return ("AWS L3 Router Service Plugin for basic L3 forwarding"
                " between (L2) Neutron networks and access to external"
                " networks via a NAT gateway.")

    ########## FLOATING IP FEATURES ###############

    def create_floatingip(self, context, floatingip):
        try:
            response = self.aws_utils.allocate_elastic_ip()
            public_ip_allocated = response['PublicIp']
            LOG.info("Created elastic IP %s" % public_ip_allocated)
            if 'floatingip' in floatingip:
                floatingip['floatingip'][
                    'floating_ip_address'] = public_ip_allocated

            if 'port_id' in floatingip['floatingip'] and floatingip[
                    'floatingip']['port_id'] is not None:
                # Associate to a Port
                port_id = floatingip['floatingip']['port_id']
                self._associate_floatingip_to_port(context,
                                                   public_ip_allocated,
                                                   port_id)
        except Exception as e:
            LOG.error("Error in Allocating EIP: %s " % e)
            raise e

        return super(AwsRouterPlugin, self).create_floatingip(
            context, floatingip, initial_status=n_const.FLOATINGIP_STATUS_DOWN)

    def _associate_floatingip_to_port(self, context, floating_ip_address,
                                      port_id):
        port = self._core_plugin.get_port(context, port_id)
        ec2_id = None
        fixed_ip_address = None
        # TODO: Assuming that there is only one fixed IP
        if len(port['fixed_ips']) > 0:
            fixed_ip = port['fixed_ips'][0]
            if 'ip_address' in fixed_ip:
                fixed_ip_address = fixed_ip['ip_address']
                search_opts = {
                    'ip': fixed_ip_address,
                    'tenant_id': context.tenant_id
                }
                server_list = self.aws_utils.get_nova_client().servers.list(
                    search_opts=search_opts)
                if len(server_list) > 0:
                    server = server_list[0]
                    if 'ec2_id' in server.metadata:
                        ec2_id = server.metadata['ec2_id']
        if floating_ip_address is not None and ec2_id is not None:
            self.aws_utils.associate_elastic_ip_to_ec2_instance(
                floating_ip_address, ec2_id)
            LOG.info("EC2 ID found for IP %s : %s" %
                     (fixed_ip_address, ec2_id))
        else:
            LOG.warning("EC2 ID not found to associate the floating IP")
            raise exceptions.AwsException(
                error_code="No Server Found",
                message="No server found with the Required IP")

    def update_floatingip(self, context, id, floatingip):
        floating_ip_dict = super(AwsRouterPlugin,
                                 self).get_floatingip(context, id)
        if 'floatingip' in floatingip and 'port_id' in floatingip['floatingip']:
            port_id = floatingip['floatingip']['port_id']
            if port_id is not None:
                # Associate Floating IP
                LOG.info("Associating elastic IP %s with port %s" %
                         (floating_ip_dict['floating_ip_address'], port_id))
                self._associate_floatingip_to_port(
                    context, floating_ip_dict['floating_ip_address'], port_id)
            else:
                # Port Disassociate
                self.aws_utils.disassociate_elastic_ip_from_ec2_instance(
                    floating_ip_dict['floating_ip_address'])
        return super(AwsRouterPlugin,
                     self).update_floatingip(context, id, floatingip)

    def delete_floatingip(self, context, id):
        floating_ip = super(AwsRouterPlugin, self).get_floatingip(context, id)
        floating_ip_address = floating_ip['floating_ip_address']
        LOG.info("Deleting elastic IP %s" % floating_ip_address)
        self.aws_utils.delete_elastic_ip(floating_ip_address)
        return super(AwsRouterPlugin, self).delete_floatingip(context, id)

    ##### ROUTERS #####

    def create_router(self, context, router):
        try:
            router_name = router['router']['name']
            internet_gw_res = self.aws_utils.create_internet_gateway_resource()
            ret_obj = super(AwsRouterPlugin,
                            self).create_router(context, router)
            internet_gw_res.create_tags(Tags=[{
                'Key': 'Name',
                'Value': router_name
            }, {
                'Key': 'openstack_router_id',
                'Value': ret_obj['id']
            }])
            LOG.info("Created AWS router %s with openstack id %s" %
                     (router_name, ret_obj['id']))
            return ret_obj
        except Exception as e:
            LOG.error("Error while creating router %s" % e)
            raise e

    def delete_router(self, context, id):
        try:
            LOG.info("Deleting router %s" % id)
            self.aws_utils.detach_internet_gateway_by_router_id(id)
            self.aws_utils.delete_internet_gateway_by_router_id(id)
        except Exception as e:
            LOG.error("Error in Deleting Router: %s " % e)
            raise e
        return super(AwsRouterPlugin, self).delete_router(context, id)

    def update_router(self, context, id, router):
        ## get internet gateway resource by openstack router id and update the tags
        try:
            if 'router' in router and 'name' in router['router']:
                router_name = router['router']['name']
                tags_list = [{
                    'Key': 'Name',
                    'Value': router_name
                }, {
                    'Key': 'openstack_router_id',
                    'Value': id
                }]
                LOG.info("Updated router %s" % id)
                self.aws_utils.create_tags_internet_gw_from_router_id(
                    id, tags_list)
        except Exception as e:
            LOG.error("Error in Updating Router: %s " % e)
            raise e
        return super(AwsRouterPlugin, self).update_router(context, id, router)

###### ROUTER INTERFACE ######

    def add_router_interface(self, context, router_id, interface_info):
        subnet_id = interface_info['subnet_id']
        subnet_obj = self._core_plugin.get_subnet(context, subnet_id)
        LOG.info("Adding subnet %s to router %s" % (subnet_id, router_id))
        neutron_network_id = subnet_obj['network_id']
        try:
            # Get Internet Gateway ID
            ig_id = self.aws_utils.get_internet_gw_from_router_id(router_id)
            # Get VPC ID
            vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
                neutron_network_id)
            self.aws_utils.attach_internet_gateway(ig_id, vpc_id)
            # Search for a Route table tagged with Router-id
            route_tables = self.aws_utils.get_route_table_by_router_id(
                router_id)
            if len(route_tables) == 0:
                # If not tagged, Fetch all the Route Tables Select one and tag it
                route_tables = self.aws_utils.describe_route_tables_by_vpc_id(
                    vpc_id)
                if len(route_tables) > 0:
                    route_table = route_tables[0]
                    route_table_res = self.aws_utils._get_ec2_resource(
                    ).RouteTable(route_table['RouteTableId'])
                    route_table_res.create_tags(Tags=[{
                        'Key': 'openstack_router_id',
                        'Value': router_id
                    }])
            if len(route_tables) > 0:
                route_table = route_tables[0]
                self.aws_utils.create_default_route_to_ig(
                    route_table['RouteTableId'], ig_id, ignore_errors=True)
        except Exception as e:
            LOG.error("Error in Creating Interface: %s " % e)
            raise e
        return super(AwsRouterPlugin,
                     self).add_router_interface(context, router_id,
                                                interface_info)

    def remove_router_interface(self, context, router_id, interface_info):
        LOG.info("Deleting subnet %s from router %s" %
                 (interface_info['subnet_id'], router_id))
        # TODO: Need to delete the route entry in the Route Table of AWS
        return super(AwsRouterPlugin,
                     self).remove_router_interface(context, router_id,
                                                   interface_info)
Example #5
0
 def initialize(self):
     self.aws_utils = AwsUtils()
     callbacks.subscribe(self)
Example #6
0
class AwsMechanismDriver(api.MechanismDriver):
    """Ml2 Mechanism driver for AWS"""
    def __init__(self):
        self.aws_utils = None
        super(AwsMechanismDriver, self).__init__()

    def initialize(self):
        self.aws_utils = AwsUtils()
        callbacks.subscribe(self)

    # NETWORK
    def create_network_precommit(self, context):
        pass

    def create_network_postcommit(self, context):
        pass

    def update_network_precommit(self, context):
        try:
            network_name = context.current['name']
            neutron_network_id = context.current['id']
            tags_list = [{'Key': 'Name', 'Value': network_name}]
            self.aws_utils.create_tags_for_vpc(neutron_network_id, tags_list)
        except Exception as e:
            LOG.error("Error in update subnet precommit: %s" % e)
            raise e

    def update_network_postcommit(self, context):
        pass

    def delete_network_precommit(self, context):
        neutron_network_id = context.current['id']
        # If user is deleting an empty  neutron network then nothing to be done
        # on AWS side
        if len(context.current['subnets']) > 0:
            vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
                neutron_network_id)
            if vpc_id is not None:
                LOG.info("Deleting network %s (VPC_ID: %s)" %
                         (neutron_network_id, vpc_id))
                self.aws_utils.delete_vpc(vpc_id=vpc_id)

    def delete_network_postcommit(self, context):
        pass

    # SUBNET
    def create_subnet_precommit(self, context):
        LOG.info("Create subnet for network %s" %
                 context.network.current['id'])
        # External Network doesn't exist on AWS, so no operations permitted
        if 'provider:physical_network' in context.network.current:
            if context.network.current[
                    'provider:physical_network'] == "external":
                # Do not create subnets for external & provider networks. Only
                # allow tenant network subnet creation at the moment.
                LOG.info('Creating external network {0}'.format(
                    context.network.current['id']))
                return

        if context.current['ip_version'] == 6:
            raise AwsException(error_code="IPv6Error",
                               message="Cannot create subnets with IPv6")
        mask = int(context.current['cidr'][-2:])
        if mask < 16 or mask > 28:
            raise AwsException(error_code="InvalidMask",
                               message="Subnet mask has to be >16 and <28")
        try:
            # Check if this is the first subnet to be added to a network
            neutron_network = context.network.current
            associated_vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
                neutron_network['id'])
            if associated_vpc_id is None:
                # Need to create EC2 VPC
                vpc_cidr = context.current['cidr'][:-2] + '16'
                tags = [{
                    'Key': 'Name',
                    'Value': neutron_network['name']
                }, {
                    'Key': 'openstack_network_id',
                    'Value': neutron_network['id']
                }, {
                    'Key': 'openstack_tenant_id',
                    'Value': context.current['tenant_id']
                }]
                associated_vpc_id = self.aws_utils.create_vpc_and_tags(
                    cidr=vpc_cidr, tags_list=tags)
            # Create Subnet in AWS
            tags = [{
                'Key': 'Name',
                'Value': context.current['name']
            }, {
                'Key': 'openstack_subnet_id',
                'Value': context.current['id']
            }, {
                'Key': 'openstack_tenant_id',
                'Value': context.current['tenant_id']
            }]
            self.aws_utils.create_subnet_and_tags(vpc_id=associated_vpc_id,
                                                  cidr=context.current['cidr'],
                                                  tags_list=tags)
        except Exception as e:
            LOG.error("Error in create subnet precommit: %s" % e)
            raise e

    def create_subnet_postcommit(self, context):
        pass

    def update_subnet_precommit(self, context):
        try:
            subnet_name = context.current['name']
            neutron_subnet_id = context.current['id']
            tags_list = [{'Key': 'Name', 'Value': subnet_name}]
            self.aws_utils.create_subnet_tags(neutron_subnet_id, tags_list)
        except Exception as e:
            LOG.error("Error in update subnet precommit: %s" % e)
            raise e

    def update_subnet_postcommit(self, context):
        pass

    def delete_subnet_precommit(self, context):
        if 'provider:physical_network' in context.network.current:
            if context.network.current[
                    'provider:physical_network'] == "external":
                LOG.error("Deleting provider and external networks not "
                          "supported")
                return
        try:
            LOG.info("Deleting subnet %s" % context.current['id'])
            subnet_id = self.aws_utils.get_subnet_from_neutron_subnet_id(
                context.current['id'])
            if subnet_id is not None:
                self.aws_utils.delete_subnet(subnet_id=subnet_id)
        except Exception as e:
            LOG.error("Error in delete subnet precommit: %s" % e)
            raise e

    def delete_subnet_postcommit(self, context):
        neutron_network = context.network.current
        if 'provider:physical_network' in context.network.current and \
                context.network.current['provider:physical_network'] == \
                "external":
            LOG.info('Deleting %s external network' %
                     context.network.current['id'])
            return
        try:
            subnets = neutron_network['subnets']
            if (len(subnets) == 1 and subnets[0] == context.current['id']
                    or len(subnets) == 0):
                # Last subnet for this network was deleted, so delete VPC
                # because VPC gets created during first subnet creation under
                # an OpenStack network
                vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
                    neutron_network['id'])
                LOG.info("Deleting VPC %s since this was the last subnet in "
                         "the vpc" % vpc_id)
                self.aws_utils.delete_vpc(vpc_id=vpc_id)
        except Exception as e:
            LOG.error("Error in delete subnet postcommit: %s" % e)
            raise e

    def create_port_precommit(self, context):
        pass

    def create_port_postcommit(self, context):
        pass

    def update_port_precommit(self, context):
        pass

    def update_port_postcommit(self, context):
        pass

    def delete_port_precommit(self, context):
        pass

    def delete_port_postcommit(self, context):
        pass

    def bind_port(self, context):
        fixed_ip_dict = dict()
        if 'fixed_ips' in context.current:
            if len(context.current['fixed_ips']) > 0:
                fixed_ip_dict = context.current['fixed_ips'][0]
                fixed_ip_dict['subnet_id'] = \
                    self.aws_utils.get_subnet_from_neutron_subnet_id(
                        fixed_ip_dict['subnet_id'])
                secgroup_ids = context.current['security_groups']
                self.create_security_groups_if_needed(context, secgroup_ids)
        segment_id = random.choice(context.network.network_segments)[api.ID]
        context.set_binding(segment_id,
                            "vip_type_a",
                            json.dumps(fixed_ip_dict),
                            status='ACTIVE')
        return True

    def create_security_groups_if_needed(self, context, secgrp_ids):
        core_plugin = manager.NeutronManager.get_plugin()
        vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
            context.current['network_id'])
        for secgrp_id in secgrp_ids:
            tags = [{
                'Key': 'openstack_id',
                'Value': secgrp_id
            }, {
                'Key': 'openstack_network_id',
                'Value': context.current['network_id']
            }]
            secgrp = core_plugin.get_security_group(context._plugin_context,
                                                    secgrp_id)
            aws_secgrp = self.aws_utils.get_sec_group_by_id(secgrp_id,
                                                            vpc_id=vpc_id)
            if not aws_secgrp and secgrp['name'] != 'default':
                grp_name = secgrp['name']
                desc = secgrp['description']
                rules = secgrp['security_group_rules']
                ec2_secgrp = self.aws_utils.create_security_group(
                    grp_name, desc, vpc_id, secgrp_id, tags)
                self.aws_utils.create_security_group_rules(ec2_secgrp, rules)

    def delete_security_group(self, security_group_id):
        self.aws_utils.delete_security_group(security_group_id)

    def remove_security_group_rule(self, context, rule_id):
        core_plugin = manager.NeutronManager.get_plugin()
        rule = core_plugin.get_security_group_rule(context, rule_id)
        secgrp_id = rule['security_group_id']
        secgrp = core_plugin.get_security_group(context, secgrp_id)
        old_rules = secgrp['security_group_rules']
        for idx in range(len(old_rules) - 1, -1, -1):
            if old_rules[idx]['id'] == rule_id:
                old_rules.pop(idx)
        self.aws_utils.update_sec_group(secgrp_id, old_rules)

    def add_security_group_rule(self, context, rule):
        core_plugin = manager.NeutronManager.get_plugin()
        secgrp_id = rule['security_group_id']
        secgrp = core_plugin.get_security_group(context, secgrp_id)
        old_rules = secgrp['security_group_rules']
        old_rules.append(rule)
        self.aws_utils.update_sec_group(secgrp_id, old_rules)

    def update_security_group_rules(self, context, rule_id):
        core_plugin = manager.NeutronManager.get_plugin()
        rule = core_plugin.get_security_group_rule(context, rule_id)
        secgrp_id = rule['security_group_id']
        secgrp = core_plugin.get_security_group(context, secgrp_id)
        old_rules = secgrp['security_group_rules']
        for idx in range(len(old_rules) - 1, -1, -1):
            if old_rules[idx]['id'] == rule_id:
                old_rules.pop(idx)
                break
        old_rules.append(rule)
        self.aws_utils.update_sec_group(secgrp_id, old_rules)

    def secgroup_callback(self, resource, event, trigger, **kwargs):
        if resource == resources.SECURITY_GROUP:
            if event == events.BEFORE_DELETE:
                security_group_id = kwargs.get('security_group_id')
                if security_group_id:
                    self.delete_security_group(security_group_id)
                else:
                    LOG.warn('Security group ID not found in delete request')
        elif resource == resources.SECURITY_GROUP_RULE:
            context = kwargs['context']
            if event == events.BEFORE_CREATE:
                rule = kwargs['security_group_rule']
                self.add_security_group_rule(context, rule)
            elif event == events.BEFORE_DELETE:
                rule_id = kwargs['security_group_rule_id']
                self.remove_security_group_rule(context, rule_id)
            elif event == events.BEFORE_UPDATE:
                rule_id = kwargs['security_group_rule_id']
                self.update_security_group_rules(context, rule_id)
Example #7
0
class SubnetExtensionDriver(api.ExtensionDriver):
    """Subnet extension driver to process and extend AZ data."""

    _supported_extension_alias = 'subnet_availability_zone'

    def initialize(self):
        """Initialize subnet extension driver."""
        self.subnet_cache = {}
        self.vpc_cidr_cache = {}
        self.aws_obj = AwsUtils()
        self.physical_network_cache = {}
        self.ec2_client_cache = {}
        self.ec2_cache_timer = datetime.datetime.now()
        self._ks_session = None
        LOG.info("SubnetExtensionDriver initialization complete")

    @property
    def ks_session(self):
        if self._ks_session is None:
            self._ks_session = self.aws_obj.get_keystone_session()
        return self._ks_session

    def get_context(self, project_id):
        ctx = context.Context(tenant_id=project_id)
        ctx.auth_token = self.ks_session.get_token()
        return ctx

    def get_ec2_client(self, project_id):
        tdiff = datetime.datetime.now() - self.ec2_cache_timer
        if tdiff.total_seconds() > 900:
            self.ec2_cache_timer = datetime.datetime.now()
            self.ec2_client_cache = {}
        if project_id in self.ec2_client_cache:
            return self.ec2_client_cache[project_id]
        ctx = self.get_context(project_id=project_id)
        ec2_client = self.aws_obj._get_ec2_client(ctx, project_id=project_id)
        self.ec2_client_cache[project_id] = ec2_client
        return ec2_client

    def _get_phynet_from_network(self, network_id, tenant_id):
        if network_id in self.physical_network_cache:
            return self.physical_network_cache[network_id]

        ctx = self.get_context(project_id=tenant_id)
        try:
            plugin = directory.get_plugin()
            network = plugin.get_network(ctx, network_id)
            if providernet.PHYSICAL_NETWORK in network:
                phy_net = network[providernet.PHYSICAL_NETWORK]
                self.physical_network_cache[network_id] = phy_net
                return phy_net
        except Exception as e:
            LOG.exception(e)
        return None

    @property
    def extension_alias(self):
        """Extension alias to load extension."""
        return self._supported_extension_alias

    def process_create_subnet(self, plugin_context, data, result):
        """Set AZ data in result to use in AWS mechanism."""
        result[az_ext.RESOURCE_NAME] = data[az_ext.RESOURCE_NAME]
        self.subnet_cache[result['id']] = data[az_ext.RESOURCE_NAME]

    def _check_for_vpc_cidr(self, vpc, result):
        cidr = result['cidr']
        if (vpc, cidr) in self.vpc_cidr_cache:
            result[az_ext.RESOURCE_NAME] = self.vpc_cidr_cache[(vpc, cidr)]
            return True
        project_id = result['tenant_id']
        ec2_client = self.get_ec2_client(project_id)
        response = ec2_client.describe_subnets(Filters=[
            {
                'Name': 'vpc-id',
                'Values': [vpc]
            },
        ])
        if 'Subnets' in response:
            for subnet in response['Subnets']:
                self.vpc_cidr_cache[(subnet['VpcId'], subnet['CidrBlock'])] = \
                    subnet['AvailabilityZone']
        if (vpc, cidr) in self.vpc_cidr_cache:
            result[az_ext.RESOURCE_NAME] = self.vpc_cidr_cache[(vpc, cidr)]
            return True
        return False

    def _check_for_openstack_subnet(self, result):
        ostack_id = result['id']
        if ostack_id in self.subnet_cache:
            result[az_ext.RESOURCE_NAME] = self.subnet_cache[ostack_id]
            return True
        project_id = result['tenant_id']
        ec2_client = self.get_ec2_client(project_id)
        response = ec2_client.describe_subnets(Filters=[{
            'Name': 'tag-value',
            'Values': [ostack_id]
        }])
        if 'Subnets' in response:
            for subnet in response['Subnets']:
                if 'SubnetId' in subnet:
                    self.subnet_cache[ostack_id] = subnet['AvailabilityZone']
                    result[az_ext.RESOURCE_NAME] = subnet['AvailabilityZone']
                    return True
        return False

    def extend_subnet_dict(self, session, db_data, result):
        """Extend subnet dict."""
        phynet = self._get_phynet_from_network(result['network_id'],
                                               result['tenant_id'])
        if isinstance(phynet, six.string_types):
            if phynet == 'external':
                return
            elif phynet.startswith('vpc') and self._check_for_vpc_cidr(
                    phynet, result):
                return
        if self._check_for_openstack_subnet(result):
            return
Example #8
0
 def initialize(self):
     self.aws_utils = AwsUtils()
Example #9
0
class AwsMechanismDriver(api.MechanismDriver):
    """Ml2 Mechanism driver for AWS"""
    def __init__(self):
        self.aws_utils = None

    def initialize(self):
        self.aws_utils = AwsUtils()

    # NETWORK
    def create_network_precommit(self, context):
        pass

    def create_network_postcommit(self, context):
        pass

    def update_network_precommit(self, context):
        try:
            network_name = context.current['name']
            neutron_network_id = context.current['id']
            tags_list = [{'Key': 'Name', 'Value': network_name}]
            self.aws_utils.create_tags_for_vpc(neutron_network_id, tags_list)
        except Exception as e:
            LOG.error("Error in update subnet precommit: %s" % e)
            raise e

    def update_network_postcommit(self, context):
        pass

    def delete_network_precommit(self, context):
        neutron_network_id = context.current['id']
        # If user is deleting an empty  neutron network then nothing to be done on AWS side
        if len(context.current['subnets']) > 0:
            vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
                neutron_network_id)
            if vpc_id is not None:
                LOG.info("Deleting network %s (VPC_ID: %s)" %
                         (neutron_network_id, vpc_id))
                self.aws_utils.delete_vpc(vpc_id=vpc_id)

    def delete_network_postcommit(self, context):
        pass

    # SUBNET
    def create_subnet_precommit(self, context):
        LOG.info("Create subnet for network %s" %
                 context.network.current['id'])
        # External Network doesn't exist on AWS, so no operations permitted
        if 'provider:physical_network' in context.network.current and context.network.current[
                'provider:physical_network'] == "external":
            # Do not create subnets for external & provider networks. Only allow tenant network
            # subnet creation at the moment.
            return

        if context.current['ip_version'] == 6:
            raise AwsException(error_code="IPv6Error",
                               message="Cannot create subnets with IPv6")
        mask = int(context.current['cidr'][-2:])
        if mask < 16 or mask > 28:
            raise AwsException(error_code="InvalidMask",
                               message="Subnet mask has to be >16 and <28")
        try:
            # Check if this is the first subnet to be added to a network
            neutron_network = context.network.current
            associated_vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
                neutron_network['id'])
            if associated_vpc_id is None:
                # Need to create EC2 VPC
                vpc_cidr = context.current['cidr'][:-2] + '16'
                tags = [{
                    'Key': 'Name',
                    'Value': neutron_network['name']
                }, {
                    'Key': 'openstack_network_id',
                    'Value': neutron_network['id']
                }]
                associated_vpc_id = self.aws_utils.create_vpc_and_tags(
                    cidr=vpc_cidr, tags_list=tags)
            # Create Subnet in AWS
            tags = [{
                'Key': 'Name',
                'Value': context.current['name']
            }, {
                'Key': 'openstack_subnet_id',
                'Value': context.current['id']
            }]
            self.aws_utils.create_subnet_and_tags(vpc_id=associated_vpc_id,
                                                  cidr=context.current['cidr'],
                                                  tags_list=tags)
        except Exception as e:
            LOG.error("Error in create subnet precommit: %s" % e)
            raise e

    def create_subnet_postcommit(self, context):
        pass

    def update_subnet_precommit(self, context):
        try:
            subnet_name = context.current['name']
            neutron_subnet_id = context.current['id']
            tags_list = [{'Key': 'Name', 'Value': subnet_name}]
            self.aws_utils.create_subnet_tags(neutron_subnet_id, tags_list)
        except Exception as e:
            LOG.error("Error in update subnet precommit: %s" % e)
            raise e

    def update_subnet_postcommit(self, context):
        pass

    def delete_subnet_precommit(self, context):
        if 'provider:physical_network' in context.network.current and context.network.current[
                'provider:physical_network'] == "external":
            LOG.error("Deleting provider and external networks not supported")
            return
        try:
            LOG.info("Deleting subnet %s" % context.current['id'])
            subnet_id = self.aws_utils.get_subnet_from_neutron_subnet_id(
                context.current['id'])
            if subnet_id is not None:
                self.aws_utils.delete_subnet(subnet_id=subnet_id)
        except Exception as e:
            LOG.error("Error in delete subnet precommit: %s" % e)
            raise e

    def delete_subnet_postcommit(self, context):
        neutron_network = context.network.current
        if 'provider:physical_network' in context.network.current and context.network.current[
                'provider:physical_network'] == "external":
            return
        try:
            subnets = neutron_network['subnets']
            if len(subnets) == 1 and subnets[0] == context.current[
                    'id'] or len(subnets) == 0:
                # Last subnet for this network was deleted, so delete VPC
                # because VPC gets created during first subnet creation under
                # an OpenStack network
                vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
                    neutron_network['id'])
                LOG.info(
                    "Deleting VPC %s since this was the last subnet in the vpc"
                    % vpc_id)
                self.aws_utils.delete_vpc(vpc_id=vpc_id)
        except Exception as e:
            LOG.error("Error in delete subnet postcommit: %s" % e)
            raise e

    def create_port_precommit(self, context):
        pass

    def create_port_postcommit(self, context):
        pass

    def update_port_precommit(self, context):
        pass

    def update_port_postcommit(self, context):
        pass

    def delete_port_precommit(self, context):
        pass

    def delete_port_postcommit(self, context):
        pass

    def bind_port(self, context):
        fixed_ip_dict = dict()
        if 'fixed_ips' in context.current:
            if len(context.current['fixed_ips']) > 0:
                fixed_ip_dict = context.current['fixed_ips'][0]
                fixed_ip_dict[
                    'subnet_id'] = self.aws_utils.get_subnet_from_neutron_subnet_id(
                        fixed_ip_dict['subnet_id'])

        segment_id = random.choice(context.network.network_segments)[api.ID]
        context.set_binding(segment_id,
                            "vip_type_a",
                            json.dumps(fixed_ip_dict),
                            status='ACTIVE')
        return True
Example #10
0
 def __init__(self):
     self.aws_utils = AwsUtils()
     super(AwsRouterPlugin, self).__init__()
Example #11
0
class AwsRouterPlugin(service_plugin_class, common_db_mixin.CommonDbMixin,
                      extraroute_db.ExtraRoute_db_mixin,
                      l3_hamode_db.L3_HA_NAT_db_mixin,
                      l3_gwmode_db.L3_NAT_db_mixin,
                      l3_dvrscheduler_db.L3_DVRsch_db_mixin,
                      l3_hascheduler_db.L3_HA_scheduler_db_mixin):
    """Implementation of the Neutron L3 Router Service Plugin.

    This class implements a L3 service plugin that provides
    router and floatingip resources and manages associated
    request/response.
    All DB related work is implemented in classes
    l3_db.L3_NAT_db_mixin, l3_hamode_db.L3_HA_NAT_db_mixin,
    l3_dvr_db.L3_NAT_with_dvr_db_mixin, and extraroute_db.ExtraRoute_db_mixin.
    """
    supported_extension_aliases = [
        "dvr", "router", "ext-gw-mode", "extraroute", "l3_agent_scheduler",
        "l3-ha", "security-group"
    ]

    @resource_registry.tracked_resources(router=router, floatingip=floating_ip)
    def __init__(self):
        self.aws_utils = AwsUtils()
        super(AwsRouterPlugin, self).__init__()

    def get_plugin_type(self):
        return plugin_type

    def get_plugin_description(self):
        """returns string description of the plugin."""
        return ("AWS L3 Router Service Plugin for basic L3 forwarding"
                " between (L2) Neutron networks and access to external"
                " networks via a NAT gateway.")

    # FLOATING IP FEATURES

    def create_floatingip(self, context, floatingip):
        public_ip_allocated = floatingip['floatingip']['floating_ip_address']
        if public_ip_allocated:
            LOG.info("Discovered floating ip %s", public_ip_allocated)
        else:
            public_ip_allocated = None
            try:
                response = self.aws_utils.allocate_elastic_ip(context)
                public_ip_allocated = response['PublicIp']
                LOG.info("Created elastic IP %s" % public_ip_allocated)
                if 'floatingip' in floatingip:
                    floatingip['floatingip'][
                        'floating_ip_address'] = public_ip_allocated

                if ('port_id' in floatingip['floatingip']
                        and floatingip['floatingip']['port_id'] is not None):
                    # Associate to a Port
                    port_id = floatingip['floatingip']['port_id']
                    self._associate_floatingip_to_port(context,
                                                       public_ip_allocated,
                                                       port_id)
            except Exception as e:
                LOG.error("Error in Creation/Allocating EIP")
                if public_ip_allocated:
                    LOG.error("Deleting Elastic IP: %s" % public_ip_allocated)
                    self.aws_utils.delete_elastic_ip(public_ip_allocated,
                                                     context)
                raise e

        try:
            res = super(AwsRouterPlugin, self).create_floatingip(
                context,
                floatingip,
                initial_status=const.FLOATINGIP_STATUS_DOWN)
        except Exception as e:
            LOG.error("Error when adding floating ip in openstack. "
                      "Deleting Elastic IP: %s" % public_ip_allocated)
            raise e
        return res

    def _associate_floatingip_to_port(self, context, floating_ip_address,
                                      port_id):
        port = self._core_plugin.get_port(context, port_id)
        ec2_id = None
        fixed_ip_address = None
        # TODO(fixed_ip): Assuming that there is only one fixed IP
        if len(port['fixed_ips']) > 0:
            fixed_ip = port['fixed_ips'][0]
            if 'ip_address' in fixed_ip:
                fixed_ip_address = fixed_ip['ip_address']
                search_opts = {
                    'ip': fixed_ip_address,
                    'tenant_id': context.tenant_id
                }
                server_list = self.aws_utils.get_nova_client().servers.list(
                    search_opts=search_opts)
                if len(server_list) > 0:
                    server = server_list[0]
                    if 'ec2_id' in server.metadata:
                        ec2_id = server.metadata['ec2_id']
        if floating_ip_address is not None and ec2_id is not None:
            self.aws_utils.associate_elastic_ip_to_ec2_instance(
                floating_ip_address, ec2_id, context=context)
            LOG.info("EC2 ID found for IP %s : %s" %
                     (fixed_ip_address, ec2_id))
        else:
            LOG.warning("EC2 ID not found to associate the floating IP")
            raise AwsException(error_code="No Server Found",
                               message="No server found with the Required IP")

    def update_floatingip(self, context, fip_id, floatingip):
        floating_ip_dict = super(AwsRouterPlugin,
                                 self).get_floatingip(context, fip_id)
        if ('floatingip' in floatingip
                and 'port_id' in floatingip['floatingip']):
            port_id = floatingip['floatingip']['port_id']
            if port_id is not None:
                # Associate Floating IP
                LOG.info("Associating elastic IP %s with port %s" %
                         (floating_ip_dict['floating_ip_address'], port_id))
                self._associate_floatingip_to_port(
                    context, floating_ip_dict['floating_ip_address'], port_id)
            else:
                try:
                    # Port Disassociate
                    self.aws_utils.disassociate_elastic_ip_from_ec2_instance(
                        floating_ip_dict['floating_ip_address'], context)
                except AwsException as e:
                    if 'Association ID not found' in e.msg:
                        # Since its already disassociated on EC2, we continue
                        # and remove the association here.
                        LOG.warn("Association for Elastic IP not found. "
                                 "Probable out of band change on EC2.")
                    elif 'InvalidAddress.NotFound' in e.msg:
                        LOG.warn("Elastic IP cannot be found in EC2. Probably "
                                 "removed out of band on EC2.")
                    else:
                        raise e
        return super(AwsRouterPlugin,
                     self).update_floatingip(context, fip_id, floatingip)

    def delete_floatingip(self, context, fip_id):
        floating_ip = super(AwsRouterPlugin,
                            self).get_floatingip(context, fip_id)
        floating_ip_address = floating_ip['floating_ip_address']
        project_id = floating_ip['project_id']
        LOG.info("Deleting elastic IP %s" % floating_ip_address)
        try:
            self.aws_utils.delete_elastic_ip(floating_ip_address,
                                             context,
                                             project_id=project_id)
        except AwsException as e:
            if 'InvalidAddress.NotFound' in e.msg:
                LOG.warn("Elastic IP not found on AWS. Cleaning up neutron db")
            else:
                raise e
        return super(AwsRouterPlugin, self).delete_floatingip(context, fip_id)

    # ROUTERS

    def create_router(self, context, router):
        try:
            router_name = router['router']['name']
            ret_obj = super(AwsRouterPlugin,
                            self).create_router(context, router)
            if router_name and router_name.startswith('igw-'):
                omni_resources.add_mapping(ret_obj['id'], router_name)
                LOG.info("Created discovered AWS router %s", router_name)
                return ret_obj

            internet_gw_res = self.aws_utils.create_internet_gateway_resource(
                context)
            tags = [{
                'Key': 'Name',
                'Value': router_name
            }, {
                'Key': 'openstack_router_id',
                'Value': ret_obj['id']
            }]
            self.aws_utils.create_resource_tags(internet_gw_res, tags)
            omni_resources.add_mapping(ret_obj['id'],
                                       internet_gw_res.internet_gateway_id)
            LOG.info("Created AWS router %s with openstack id %s" %
                     (router_name, ret_obj['id']))
            return ret_obj
        except Exception as e:
            LOG.error("Error while creating router %s" % e)
            raise e

    def delete_router(self, context, router_id):
        LOG.info("Deleting router %s" % router_id)
        if omni_resources.get_omni_resource(router_id) is None:
            raise RouterIdInvalidException(router_id=router_id)

        try:
            router_obj = self._get_router(context, router_id)
            if omni_resources.get_omni_resource(router_id) is None:
                raise AwsException(
                    "Router deletion failed, no AWS mapping found for %s" %
                    (router_id, ))
            project_id = router_obj['project_id']
            router_name = router_obj['name']
            try:
                if router_name and router_name.startswith('igw-'):
                    self.aws_utils.detach_internet_gateway(
                        router_name, context, project_id=project_id)
                    self.aws_utils.delete_internet_gateway(
                        router_name, context, project_id=project_id)
                else:
                    self.aws_utils.detach_internet_gateway_by_router_id(
                        router_id, context, project_id=project_id)
                    self.aws_utils.delete_internet_gateway_by_router_id(
                        router_id, context, project_id=project_id)
            except AwsException as e:
                if 'InvalidInternetGatewayID.NotFound' in e.msg:
                    LOG.warn(e.msg)
                else:
                    raise e
            omni_resources.delete_mapping(router_id)
        except Exception as e:
            LOG.error("Error in Deleting Router: %s " % e)
            raise e
        return super(AwsRouterPlugin, self).delete_router(context, router_id)

    def update_router(self, context, router_id, router):
        # get internet gateway resource by openstack router id and update the
        # tags
        try:
            router_obj = self._get_router(context, router_id)
            router_name = router_obj['name']
            if router_name and router_name.startswith('igw-'):
                return super(AwsRouterPlugin,
                             self).update_router(context, router_id, router)

            if 'router' in router and 'name' in router['router']:
                router_name = router['router']['name']
                tags_list = [{
                    'Key': 'Name',
                    'Value': router_name
                }, {
                    'Key': 'openstack_router_id',
                    'Value': router_id
                }]
                LOG.info("Updated router %s" % router_id)
                self.aws_utils.create_tags_internet_gw_from_router_id(
                    router_id, tags_list, context)
        except Exception as e:
            LOG.error("Error in Updating Router: %s " % e)
            raise e
        return super(AwsRouterPlugin,
                     self).update_router(context, router_id, router)

# ROUTER INTERFACE

    def add_router_interface(self, context, router_id, interface_info):
        subnet_id = interface_info['subnet_id']
        subnet_obj = self._core_plugin.get_subnet(context, subnet_id)
        router_obj = self._get_router(context, router_id)
        router_name = router_obj['name']
        if router_name and router_name.startswith('igw-'):
            LOG.info("Adding subnet %s to router %s", subnet_id, router_name)
            return super(AwsRouterPlugin,
                         self).add_router_interface(context, router_id,
                                                    interface_info)

        LOG.info("Adding subnet %s to router %s" % (subnet_id, router_id))
        neutron_network_id = subnet_obj['network_id']
        try:
            # Get Internet Gateway ID
            ig_id = self.aws_utils.get_internet_gw_from_router_id(
                router_id, context)
            # Get VPC ID
            vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
                neutron_network_id, context)
            self.aws_utils.attach_internet_gateway(ig_id, vpc_id, context)
            # Search for a Route table tagged with Router-id
            route_tables = self.aws_utils.get_route_table_by_router_id(
                router_id, context)
            if len(route_tables) == 0:
                # If not tagged, Fetch all the Route Tables Select one and tag
                # it
                route_tables = self.aws_utils.describe_route_tables_by_vpc_id(
                    vpc_id, context)
                if len(route_tables) > 0:
                    route_table = route_tables[0]
                    route_table_res = self.aws_utils._get_ec2_resource(
                        context).RouteTable(route_table['RouteTableId'])
                    route_table_res.create_tags(Tags=[{
                        'Key': 'openstack_router_id',
                        'Value': router_id
                    }])
            if len(route_tables) > 0:
                route_table = route_tables[0]
                self.aws_utils.create_default_route_to_ig(
                    route_table['RouteTableId'],
                    ig_id,
                    context=context,
                    ignore_errors=True)
        except Exception as e:
            LOG.error("Error in Creating Interface: %s " % e)
            raise e
        return super(AwsRouterPlugin,
                     self).add_router_interface(context, router_id,
                                                interface_info)

    def remove_router_interface(self, context, router_id, interface_info):
        if 'port_id' in interface_info:
            interface_id = interface_info['port_id']
            deleting_by = "port_id"
        elif 'subnet_id' in interface_info:
            interface_id = interface_info['subnet_id']
            deleting_by = "subnet_id"
        LOG.info("Deleting interface by {0} {1} from router {2}".format(
            deleting_by, interface_id, router_id))
        router_obj = self._get_router(context, router_id)
        project_id = router_obj['project_id']
        try:
            self.aws_utils.detach_internet_gateway_by_router_id(
                router_id, context, project_id=project_id)
        except AwsException as e:
            if 'InvalidInternetGatewayID.NotFound' in e.msg:
                LOG.warn(e.msg)
            else:
                raise e
        route_tables = self.aws_utils.get_route_table_by_router_id(
            router_id, context, project_id=project_id)
        if route_tables:
            route_table_id = route_tables[0]['RouteTableId']
            self.aws_utils.delete_default_route_to_ig(route_table_id,
                                                      context,
                                                      project_id=project_id)
        return super(AwsRouterPlugin,
                     self).remove_router_interface(context, router_id,
                                                   interface_info)
Example #12
0
class AwsMechanismDriver(api.MechanismDriver):
    """Ml2 Mechanism driver for AWS"""
    def __init__(self):
        self.aws_utils = None
        self._default_sgr_to_remove = []
        super(AwsMechanismDriver, self).__init__()

    def initialize(self):
        self.aws_utils = AwsUtils()
        callbacks.subscribe(self)

    # NETWORK
    def create_network_precommit(self, context):
        pass

    def create_network_postcommit(self, context):
        pass

    def update_network_precommit(self, context):
        try:
            network_name = context.current['name']
            original_network_name = context.original['name']
            LOG.debug("Update network original: %s current: %s",
                      original_network_name, network_name)

            if network_name == original_network_name:
                return
            neutron_network_id = context.current['id']
            project_id = context.current['project_id']
            tags_list = [{'Key': 'Name', 'Value': network_name}]
            self.aws_utils.create_tags_for_vpc(neutron_network_id,
                                               tags_list,
                                               context=context._plugin_context,
                                               project_id=project_id)
        except Exception as e:
            LOG.error("Error in update subnet precommit: %s" % e)
            raise e

    def update_network_postcommit(self, context):
        pass

    def delete_network_precommit(self, context):
        neutron_network_id = context.current['id']
        project_id = context.current['project_id']
        # If user is deleting an empty  neutron network then nothing to be done
        # on AWS side
        if len(context.current['subnets']) > 0:
            vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
                neutron_network_id,
                context=context._plugin_context,
                project_id=project_id)
            if vpc_id is not None:
                LOG.info("Deleting network %s (VPC_ID: %s)" %
                         (neutron_network_id, vpc_id))
                try:
                    self.aws_utils.delete_vpc(vpc_id=vpc_id,
                                              context=context._plugin_context,
                                              project_id=project_id)
                except AwsException as e:
                    if 'InvalidVpcID.NotFound' in e.msg:
                        LOG.warn(e.msg)
                    else:
                        raise e
                omni_resources.delete_mapping(context.current['id'])

    def delete_network_postcommit(self, context):
        pass

    # SUBNET
    def create_subnet_precommit(self, context):
        network_id = context.network.current['id']
        LOG.info("Create subnet for network %s" % network_id)
        # External Network doesn't exist on AWS, so no operations permitted
        physical_network = context.network.current.get(
            'provider:physical_network')
        if physical_network == "external":
            # Do not create subnets for external & provider networks. Only
            # allow tenant network subnet creation at the moment.
            LOG.info('Creating external network {0}'.format(network_id))
            return
        elif physical_network and physical_network.startswith('vpc'):
            LOG.info('Registering AWS network with vpc %s', physical_network)
            subnet_cidr = context.current['cidr']
            subnet_id = self.aws_utils.get_subnet_from_vpc_and_cidr(
                context._plugin_context, physical_network, subnet_cidr,
                context.current['project_id'])
            omni_resources.add_mapping(network_id, physical_network)
            omni_resources.add_mapping(context.current['id'], subnet_id)
            return
        if context.current['ip_version'] == 6:
            raise AwsException(error_code="IPv6Error",
                               message="Cannot create subnets with IPv6")
        mask = int(context.current['cidr'][-2:])
        if mask < 16 or mask > 28:
            raise AwsException(error_code="InvalidMask",
                               message="Subnet mask has to be >16 and <28")
        try:
            # Check if this is the first subnet to be added to a network
            neutron_network = context.network.current
            associated_vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
                neutron_network['id'], context=context._plugin_context)
            if associated_vpc_id is None:
                # Need to create EC2 VPC
                vpc_cidr = context.current['cidr'][:-2] + '16'
                tags = [{
                    'Key': 'Name',
                    'Value': neutron_network['name']
                }, {
                    'Key': 'openstack_network_id',
                    'Value': neutron_network['id']
                }, {
                    'Key': 'openstack_tenant_id',
                    'Value': context.current['tenant_id']
                }]
                associated_vpc_id = self.aws_utils.create_vpc_and_tags(
                    cidr=vpc_cidr,
                    tags_list=tags,
                    context=context._plugin_context)
                omni_resources.add_mapping(neutron_network['id'],
                                           associated_vpc_id)
            # Create Subnet in AWS
            tags = [{
                'Key': 'Name',
                'Value': context.current['name']
            }, {
                'Key': 'openstack_subnet_id',
                'Value': context.current['id']
            }, {
                'Key': 'openstack_tenant_id',
                'Value': context.current['tenant_id']
            }]
            if AZ in context.current and context.current[AZ]:
                aws_az = context.current[AZ]
            elif context.network.current[AZ_HINT]:
                network_az_hints = context.network.current[AZ_HINT]
                if len(network_az_hints) > 1:
                    # We use only one AZ hint even if multiple AZ values
                    # are passed while creating network.
                    raise NetworkWithMultipleAZs()
                aws_az = network_az_hints[0]
            else:
                raise AzNotProvided()
            self._validate_az(aws_az)
            ec2_subnet_id = self.aws_utils.create_subnet_and_tags(
                vpc_id=associated_vpc_id,
                cidr=context.current['cidr'],
                tags_list=tags,
                aws_az=aws_az,
                context=context._plugin_context)
            omni_resources.add_mapping(context.current['id'], ec2_subnet_id)
        except Exception as e:
            LOG.error("Error in create subnet precommit: %s" % e)
            raise e

    def _send_request(self, session, url):
        headers = {
            'Content-Type': 'application/json',
            'X-Auth-Token': session.get_token()
        }
        response = requests.get(url + "/v1/zones", headers=headers)
        response.raise_for_status()
        return response.json()

    def _validate_az(self, aws_az):
        if not isinstance(aws_az, six.string_types):
            raise InvalidAzValue()
        if ',' in aws_az:
            raise NetworkWithMultipleAZs()
        session = self.aws_utils.get_keystone_session()
        azmgr_url = session.get_endpoint(service_type='azmanager',
                                         region_name=cfg.CONF.nova_region_name)
        zones = self._send_request(session, azmgr_url)
        if aws_az not in zones:
            LOG.error("Provided az %s not found in zones %s", aws_az, zones)
            raise InvalidAzValue()

    def create_subnet_postcommit(self, context):
        pass

    def update_subnet_precommit(self, context):
        try:
            subnet_name = context.current['name']
            neutron_subnet_id = context.current['id']
            tags_list = [{'Key': 'Name', 'Value': subnet_name}]
            self.aws_utils.create_subnet_tags(neutron_subnet_id,
                                              tags_list,
                                              context=context._plugin_context)
        except Exception as e:
            LOG.error("Error in update subnet precommit: %s" % e)
            raise e

    def update_subnet_postcommit(self, context):
        pass

    def delete_subnet_precommit(self, context):
        try:
            LOG.info("Deleting subnet %s" % context.current['id'])
            project_id = context.current['project_id']
            subnet_id = self.aws_utils.get_subnet_from_neutron_subnet_id(
                context.current['id'],
                context=context._plugin_context,
                project_id=project_id)
            if not subnet_id:
                raise Exception("Subnet mapping %s not found" %
                                (context.current['id']))
            try:
                self.aws_utils.delete_subnet(subnet_id=subnet_id,
                                             context=context._plugin_context,
                                             project_id=project_id)
                omni_resources.delete_mapping(context.current['id'])
            except AwsException as e:
                if 'InvalidSubnetID.NotFound' in e.msg:
                    LOG.warn(e.msg)
                    omni_resources.delete_mapping(context.current['id'])
                else:
                    raise e
        except Exception as e:
            LOG.error("Error in delete subnet precommit: %s" % e)
            raise e

    def delete_subnet_postcommit(self, context):
        neutron_network = context.network.current
        try:
            subnets = neutron_network['subnets']
            if (len(subnets) == 1 and subnets[0] == context.current['id']
                    or len(subnets) == 0):
                # Last subnet for this network was deleted, so delete VPC
                # because VPC gets created during first subnet creation under
                # an OpenStack network
                project_id = context.current['project_id']
                vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
                    neutron_network['id'],
                    context=context._plugin_context,
                    project_id=project_id)
                if not vpc_id:
                    raise Exception("Network mapping %s not found",
                                    neutron_network['id'])
                LOG.info("Deleting VPC %s since this was the last subnet in "
                         "the vpc" % vpc_id)
                self.aws_utils.delete_vpc(vpc_id=vpc_id,
                                          context=context._plugin_context,
                                          project_id=project_id)
                omni_resources.delete_mapping(context.network.current['id'])
        except Exception as e:
            LOG.error("Error in delete subnet postcommit: %s" % e)
            raise e

    def create_port_precommit(self, context):
        pass

    def create_port_postcommit(self, context):
        pass

    def update_port_precommit(self, context):
        original_port = context._original_port
        updated_port = context._port
        sorted_original_sgs = sorted(original_port['security_groups'])
        sorted_updated_sgs = sorted(updated_port['security_groups'])
        aws_sgs = []
        project_id = context.current['project_id']
        if sorted_updated_sgs != sorted_original_sgs:
            for sg in updated_port['security_groups']:
                aws_secgrps = self.aws_utils.get_sec_group_by_id(
                    sg, context._plugin_context, project_id=project_id)
                aws_sgs.append(aws_secgrps[0]['GroupId'])
        if aws_sgs:
            self.aws_utils.modify_ports(aws_sgs, updated_port['name'],
                                        context._plugin_context, project_id)

    def update_port_postcommit(self, context):
        pass

    def delete_port_precommit(self, context):
        pass

    def delete_port_postcommit(self, context):
        pass

    def bind_port(self, context):
        fixed_ip_dict = dict()
        if 'fixed_ips' in context.current:
            if len(context.current['fixed_ips']) > 0:
                fixed_ip_dict = context.current['fixed_ips'][0]
                openstack_subnet_id = fixed_ip_dict['subnet_id']
                aws_subnet_id = \
                    self.aws_utils.get_subnet_from_neutron_subnet_id(
                        openstack_subnet_id, context._plugin_context,
                        project_id=context.current['project_id'])
                fixed_ip_dict['subnet_id'] = aws_subnet_id
                secgroup_ids = context.current['security_groups']
                ec2_secgroup_ids = self.create_security_groups_if_needed(
                    context, secgroup_ids)
                fixed_ip_dict['ec2_security_groups'] = ec2_secgroup_ids
        segment_id = random.choice(context.network.network_segments)[api.ID]
        context.set_binding(segment_id,
                            "vip_type_a",
                            json.dumps(fixed_ip_dict),
                            status='ACTIVE')
        return True

    def create_security_groups_if_needed(self, context, secgrp_ids):
        project_id = context.current.get('project_id')
        core_plugin = directory.get_plugin()
        vpc_id = self.aws_utils.get_vpc_from_neutron_network_id(
            context.current['network_id'],
            context=context._plugin_context,
            project_id=project_id)
        ec2_secgroup_ids = []
        for secgrp_id in secgrp_ids:
            tags = [{
                'Key': 'openstack_id',
                'Value': secgrp_id
            }, {
                'Key': 'openstack_network_id',
                'Value': context.current['network_id']
            }]
            secgrp = core_plugin.get_security_group(context._plugin_context,
                                                    secgrp_id)
            aws_secgrps = self.aws_utils.get_sec_group_by_id(
                secgrp_id,
                group_name=secgrp['name'],
                vpc_id=vpc_id,
                context=context._plugin_context,
                project_id=project_id)
            if not aws_secgrps and secgrp['name'] != 'default':
                grp_name = secgrp['name']
                tags.append({"Key": "Name", "Value": grp_name})
                desc = secgrp['description']
                rules = secgrp['security_group_rules']
                ec2_secgrp = self.aws_utils.create_security_group(
                    grp_name,
                    desc,
                    vpc_id,
                    secgrp_id,
                    tags,
                    context=context._plugin_context,
                    project_id=project_id)
                self.aws_utils.create_security_group_rules(ec2_secgrp, rules)
                # Make sure that omni_resources table is populated with newly
                # created security group
                aws_secgrps = self.aws_utils.get_sec_group_by_id(
                    secgrp_id,
                    group_name=secgrp['name'],
                    vpc_id=vpc_id,
                    context=context._plugin_context,
                    project_id=project_id)
            for aws_secgrp in aws_secgrps:
                ec2_secgroup_ids.append(aws_secgrp['GroupId'])
        return ec2_secgroup_ids

    def delete_security_group(self, security_group_id, context, project_id):
        core_plugin = directory.get_plugin()
        secgrp = core_plugin.get_security_group(context, security_group_id)
        self.aws_utils.delete_security_group(security_group_id,
                                             context,
                                             project_id,
                                             group_name=secgrp['name'])

    def remove_security_group_rule(self, context, rule_id):
        core_plugin = directory.get_plugin()
        rule = core_plugin.get_security_group_rule(context, rule_id)
        secgrp_id = rule['security_group_id']
        secgrp = core_plugin.get_security_group(context, secgrp_id)
        if "project_id" in rule:
            project_id = rule['project_id']
        else:
            project_id = context.tenant
        self.aws_utils.delete_security_group_rule_if_needed(
            context, secgrp_id, secgrp['name'], project_id, rule)

    def add_security_group_rule(self, context, rule):
        core_plugin = directory.get_plugin()
        secgrp_id = rule['security_group_id']
        secgrp = core_plugin.get_security_group(context, secgrp_id)
        if "project_id" in rule:
            project_id = rule['project_id']
        else:
            project_id = context.tenant
        self.aws_utils.create_security_group_rule_if_needed(
            context, secgrp_id, secgrp['name'], project_id, rule)

    def update_security_group_rules(self, context, rule_id):
        core_plugin = directory.get_plugin()
        rule = core_plugin.get_security_group_rule(context, rule_id)
        secgrp_id = rule['security_group_id']
        secgrp = core_plugin.get_security_group(context, secgrp_id)
        old_rules = secgrp['security_group_rules']
        for idx in range(len(old_rules) - 1, -1, -1):
            if old_rules[idx]['id'] == rule_id:
                old_rules.pop(idx)
                break
        old_rules.append(rule)
        if "project_id" in rule:
            project_id = rule['project_id']
        else:
            project_id = context.tenant
        self.aws_utils.update_sec_group(secgrp_id,
                                        old_rules,
                                        context=context,
                                        project_id=project_id,
                                        group_name=secgrp['name'])

    def secgroup_callback(self, resource, event, trigger, **kwargs):
        context = kwargs['context']
        if resource == resources.SECURITY_GROUP:
            if event == events.AFTER_CREATE:
                project_id = kwargs.get('security_group')['project_id']
                secgrp = kwargs.get('security_group')
                security_group_id = secgrp.get('id')
                core_plugin = directory.get_plugin()
                aws_secgrps = self.aws_utils.get_sec_group_by_id(
                    security_group_id,
                    group_name=secgrp.get('name'),
                    context=context,
                    project_id=project_id)
                if len(aws_secgrps) == 0:
                    return
                for sgr in secgrp.get('security_group_rules', []):
                    # This is invoked for discovered security groups only. For
                    # discovered security groups we do not need default egress
                    # rules. Those should be reported by discovery service.
                    # When removing these default security group rules we do
                    # not need to check against AWS. Store the security group
                    # rule IDs so that we can ignore them when delete security
                    # group rule is called here.
                    self._default_sgr_to_remove.append(sgr.get('id'))
                    core_plugin.delete_security_group_rule(
                        context, sgr.get('id'))
            if event == events.BEFORE_DELETE:
                project_id = kwargs.get('security_group')['project_id']
                security_group_id = kwargs.get('security_group_id')
                if security_group_id:
                    self.delete_security_group(security_group_id, context,
                                               project_id)
                else:
                    LOG.warn('Security group ID not found in delete request')
        elif resource == resources.SECURITY_GROUP_RULE:
            if event == events.BEFORE_CREATE:
                rule = kwargs['security_group_rule']
                self.add_security_group_rule(context, rule)
            elif event == events.BEFORE_DELETE:
                rule_id = kwargs['security_group_rule_id']
                if rule_id in self._default_sgr_to_remove:
                    # Check the comment above in security group rule
                    # AFTER_CREATE event handling
                    self._default_sgr_to_remove.remove(rule_id)
                else:
                    self.remove_security_group_rule(context, rule_id)
            elif event == events.BEFORE_UPDATE:
                rule_id = kwargs['security_group_rule_id']
                self.update_security_group_rules(context, rule_id)