text: #    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 netaddr
  import operator
  
  from keystoneclient import exceptions as k_exceptions
  from keystoneclient.v2_0 import client as k_client
  from neutron.api.v2 import attributes
  from neutron.common import exceptions as neutron_exc
  from neutron.db import models_v2
  from neutron.extensions import l3 as ext_l3
  from neutron.extensions import securitygroup as ext_sg
  from neutron_lib import constants as n_const
  from neutron_lib import context as n_context
  from neutron_lib.db import model_base
  from neutron_lib import exceptions as n_exc
  from neutron_lib.plugins import directory
  from oslo_config import cfg
  from oslo_db import exception as oslo_db_excp
  from oslo_log import helpers as log
  from oslo_log import log as logging
  from oslo_utils import excutils
  import sqlalchemy as sa
  from sqlalchemy.orm import exc as sa_exc
  
  from gbpservice.common import utils
  from gbpservice.network.neutronv2 import local_api
  from gbpservice.neutron.db.grouppolicy import group_policy_db as gpdb
  from gbpservice.neutron.db.grouppolicy import group_policy_mapping_db as gpmdb
  from gbpservice.neutron.extensions import driver_proxy_group as proxy_ext
  from gbpservice.neutron.extensions import group_policy as gp_ext
  from gbpservice.neutron.services.grouppolicy import (
      group_policy_driver_api as api)
  from gbpservice.neutron.services.grouppolicy.common import constants as gconst
  from gbpservice.neutron.services.grouppolicy.common import exceptions as exc
  from gbpservice.neutron.services.grouppolicy.common import utils as gbp_utils
  from gbpservice.neutron.services.grouppolicy.drivers import nsp_manager
  
  
  LOG = logging.getLogger(__name__)
  DEFAULT_SG_PREFIX = 'gbp_%s'
  SCI_CONSUMER_NOT_AVAILABLE = 'N/A'
  
  opts = [
      cfg.ListOpt('dns_nameservers',
                  default=[],
                  help=_("List of DNS nameservers to be configured for the "
                         "PTG subnets")),
      cfg.StrOpt('default_ipv6_ra_mode',
                 default=None,
                 help=_("default IPv6 Router Advertisement mode for subnets "
                        "created implicitly for L3 policies. Valid values are "
                        "'slaac', 'dhcpv6-stateful', and 'dhcpv6-stateless'")),
      cfg.StrOpt('default_ipv6_address_mode',
                 default=None,
                 help=_("default IPv6 address assignment mode for subnets "
                        "created implicitly for L3 policies. Valid values are "
                        "'slaac', 'dhcpv6-stateful', and 'dhcpv6-stateless'")),
      cfg.BoolOpt('use_subnetpools',
                 default=True,
                 help=_("make use of neutron subnet pools and address scopes "
                        "as L3 policy resource")),
  ]
  
  cfg.CONF.register_opts(opts, "resource_mapping")
  
  # something to shorten the config family name
  MAPPING_CFG = cfg.CONF.resource_mapping
  
  
  class OwnedPort(model_base.BASEV2):
      """A Port owned by the resource_mapping driver."""
  
      __tablename__ = 'gpm_owned_ports'
      port_id = sa.Column(sa.String(36),
                          sa.ForeignKey('ports.id', ondelete='CASCADE'),
                          nullable=False, primary_key=True)
  
  
  class OwnedSubnet(model_base.BASEV2):
      """A Subnet owned by the resource_mapping driver."""
  
      __tablename__ = 'gpm_owned_subnets'
      subnet_id = sa.Column(sa.String(36),
                            sa.ForeignKey('subnets.id', ondelete='CASCADE'),
                            nullable=False, primary_key=True)
  
  
  class OwnedNetwork(model_base.BASEV2):
      """A Network owned by the resource_mapping driver."""
  
      __tablename__ = 'gpm_owned_networks'
      network_id = sa.Column(sa.String(36),
                             sa.ForeignKey('networks.id', ondelete='CASCADE'),
                             nullable=False, primary_key=True)
  
  
  class OwnedAddressScope(model_base.BASEV2):
      """An Address Scope owned by the resource_mapping driver."""
  
      __tablename__ = 'gpm_owned_address_scopes'
      address_scope_id = sa.Column(sa.String(36),
                                   sa.ForeignKey('address_scopes.id',
                                                 ondelete='CASCADE'),
                                   nullable=False, primary_key=True)
  
  
  class OwnedSubnetpool(model_base.BASEV2):
      """A Subnetpool owned by the resource_mapping driver."""
  
      __tablename__ = 'gpm_owned_subnetpools'
      subnetpool_id = sa.Column(sa.String(36),
                                sa.ForeignKey('subnetpools.id',
                                              ondelete='CASCADE'),
                                nullable=False, primary_key=True)
  
  
  class OwnedRouter(model_base.BASEV2):
      """A Router owned by the resource_mapping driver."""
  
      __tablename__ = 'gpm_owned_routers'
      router_id = sa.Column(sa.String(36),
                            sa.ForeignKey('routers.id', ondelete='CASCADE'),
                            nullable=False, primary_key=True)
  
  
  class PolicyRuleSetSGsMapping(model_base.BASEV2):
      """PolicyRuleSet to SGs mapping DB."""
  
      __tablename__ = 'gpm_policy_rule_set_sg_mapping'
      policy_rule_set_id = sa.Column(sa.String(36),
                                     sa.ForeignKey('gp_policy_rule_sets.id',
                                                   ondelete='CASCADE'),
                                     nullable=False, primary_key=True)
      provided_sg_id = sa.Column(sa.String(36),
                                 sa.ForeignKey('securitygroups.id'))
      consumed_sg_id = sa.Column(sa.String(36),
                                 sa.ForeignKey('securitygroups.id'))
  
  
  # This exception should never escape the driver.
  class CidrInUse(exc.GroupPolicyInternalError):
      message = _("CIDR %(cidr)s in-use within L3 policy %(l3p_id)s")
  
  
  class OwnedResourcesOperations(object):
  
      # TODO(Sumit): All the following operations can be condensed into
      # a single _mark_resource_owned() and _resource_is_owned() method,
      # by creating a resource to DB class name mapping.
  
      def _mark_port_owned(self, session, port_id):
          with session.begin(subtransactions=True):
              owned = OwnedPort(port_id=port_id)
              session.add(owned)
  
      def _port_is_owned(self, session, port_id):
          with session.begin(subtransactions=True):
              return (session.query(OwnedPort).
                      filter_by(port_id=port_id).
                      first() is not None)
  
      def _mark_subnet_owned(self, session, subnet_id):
          with session.begin(subtransactions=True):
              owned = OwnedSubnet(subnet_id=subnet_id)
              session.add(owned)
  
      def _subnet_is_owned(self, session, subnet_id):
          with session.begin(subtransactions=True):
              return (session.query(OwnedSubnet).
                      filter_by(subnet_id=subnet_id).
                      first() is not None)
  
      def _mark_network_owned(self, session, network_id):
          with session.begin(subtransactions=True):
              owned = OwnedNetwork(network_id=network_id)
              session.add(owned)
  
      def _network_is_owned(self, session, network_id):
          with session.begin(subtransactions=True):
              return (session.query(OwnedNetwork).
                      filter_by(network_id=network_id).
                      first() is not None)
  
      def _mark_router_owned(self, session, router_id):
          with session.begin(subtransactions=True):
              owned = OwnedRouter(router_id=router_id)
              session.add(owned)
  
      def _router_is_owned(self, session, router_id):
          with session.begin(subtransactions=True):
              return (session.query(OwnedRouter).
                      filter_by(router_id=router_id).
                      first() is not None)
  
      def _mark_address_scope_owned(self, session, address_scope_id):
          with session.begin(subtransactions=True):
              owned = OwnedAddressScope(address_scope_id=address_scope_id)
              session.add(owned)
  
      def _address_scope_is_owned(self, session, address_scope_id):
          with session.begin(subtransactions=True):
              return (session.query(OwnedAddressScope).
                      filter_by(address_scope_id=address_scope_id).
                      first() is not None)
  
      def _mark_subnetpool_owned(self, session, subnetpool_id):
          with session.begin(subtransactions=True):
              owned = OwnedSubnetpool(subnetpool_id=subnetpool_id)
              session.add(owned)
  
      def _subnetpool_is_owned(self, session, subnetpool_id):
          with session.begin(subtransactions=True):
              return (session.query(OwnedSubnetpool).
                      filter_by(subnetpool_id=subnetpool_id).
                      first() is not None)
  
  
  class ImplicitResourceOperations(local_api.LocalAPI,
                                   nsp_manager.NetworkServicePolicyMappingMixin):
  
      L3P_ADDRESS_SCOPE_KEYS = {4: 'address_scope_v4_id',
                               6: 'address_scope_v6_id'}
      L3P_SUBNETPOOLS_KEYS = {4: 'subnetpools_v4',
                              6: 'subnetpools_v6'}
  
      def _sg_rule(self, plugin_context, tenant_id, sg_id, direction,
                   protocol=None, port_range=None, cidr=None,
                   ethertype=n_const.IPv4, unset=False):
          if port_range:
              port_min, port_max = (gpdb.GroupPolicyDbPlugin.
                                    _get_min_max_ports_from_range(port_range))
          else:
              port_min, port_max = None, None
  
          attrs = {'tenant_id': tenant_id,
                   'security_group_id': sg_id,
                   'direction': direction,
                   'ethertype': ethertype,
                   'protocol': protocol,
                   'port_range_min': port_min,
                   'port_range_max': port_max,
                   'remote_ip_prefix': cidr,
                   'remote_group_id': None}
          filters = {}
          for key in attrs:
              value = attrs[key]
              if value:
                  filters[key] = [value]
          rule = self._get_sg_rules(plugin_context, filters)
          if unset:
              if rule:
                  self._delete_sg_rule(plugin_context, rule[0]['id'])
          else:
              if not rule:
                  # There was a bug in Neutron until stable/mitaka
                  # which prevented duplicate SG rules being added.
                  # Since that is fixed in stable/newton overlapping
                  # SG rules can be added within the same tenant. We
                  # actually dont want to add overlapping rules,
                  # hence we check before adding.
                  return self._create_sg_rule(plugin_context, attrs)
              else:
                  return rule[0]
  
      def _create_gbp_sg(self, plugin_context, tenant_id, name, **kwargs):
          # This method sets up the attributes of security group
          attrs = {'tenant_id': tenant_id,
                   'name': name,
                   'description': '',
                   'security_group_rules': ''}
          attrs.update(kwargs)
          sg = self._create_sg(plugin_context, attrs)
          # Cleanup default rules
          for rule in self._get_sg_rules(plugin_context,
                                         filters={'security_group_id':
                                                  [sg['id']]}):
              self._delete_sg_rule(plugin_context, rule['id'])
          return sg
  
      def _create_implicit_address_scope(self, context, **kwargs):
          attrs = {'tenant_id': context.current['tenant_id'],
                   'name': context.current['name'], 'ip_version':
                   context.current['ip_version'],
                   'shared': context.current.get('shared', False)}
          attrs.update(**kwargs)
          address_scope = self._create_address_scope(
              context._plugin_context, attrs)
          as_id = address_scope['id']
          self._mark_address_scope_owned(context._plugin_context.session, as_id)
          return address_scope
  
      def _use_implicit_address_scope(self, context, ip_version=4, **kwargs):
          address_scope = self._create_implicit_address_scope(
              context, name='l3p_' + context.current['name'] +
              '_' + str(ip_version), ip_version=ip_version, **kwargs)
          context.set_address_scope_id(address_scope['id'], ip_version)
          return address_scope
  
      def _cleanup_address_scope(self, plugin_context, address_scope_id):
          if self._address_scope_is_owned(plugin_context.session,
                                          address_scope_id):
              subpools = self._get_subnetpools(plugin_context,
                                               filters={'address_scope_id':
                                                        [address_scope_id]})
              if subpools:
                  LOG.warning("Cannot delete implicitly created "
                              "address_scope %(id)s since it has "
                              "associated subnetpools: %(pools)s",
                              {'id': address_scope_id, 'pools': subpools})
              else:
                  self._delete_address_scope(plugin_context, address_scope_id)
  
      def _create_implicit_subnetpool(self, context, **kwargs):
          attrs = {'tenant_id': context.current['tenant_id'],
                   'name': context.current['name'], 'ip_version':
                   context.current['ip_version'],
                   'default_prefixlen': context.current['subnet_prefix_length'],
                   'prefixes': gbp_utils.convert_ip_pool_string_to_list(
                       context.current['ip_pool']),
                   'shared': context.current.get('shared', False),
                   # Per current understanding, is_default is used for
                   # auto_allocation and is a per-tenant setting.
                   'is_default': False}
          attrs.update(**kwargs)
          subnetpool = self._create_subnetpool(
              context._plugin_context, attrs)
          sp_id = subnetpool['id']
          self._mark_subnetpool_owned(context._plugin_context.session, sp_id)
          return subnetpool
  
      def _use_implicit_subnetpool(self, context, address_scope_id,
                                   ip_version=4, **kwargs):
          subnetpool = self._create_implicit_subnetpool(
              context, name='l3p_' + context.current['name'],
              address_scope_id=address_scope_id, ip_version=ip_version, **kwargs)
          context.add_subnetpool(subnetpool_id=subnetpool['id'],
                                 ip_version=ip_version)
  
      def _cleanup_subnetpool(self, plugin_context, subnetpool_id):
          if self._subnetpool_is_owned(plugin_context.session,
                                       subnetpool_id):
              subnets = self._get_subnets(plugin_context,
                                          filters={'subnetpool_id':
                                                   [subnetpool_id]})
              if subnets:
                  LOG.warning("Cannot delete implicitly created "
                              "subnetpool %(id)s since it has "
                              "associated subnets: %(subnets)s",
                              {'id': subnetpool_id, 'subnets': subnets})
              else:
                  self._delete_subnetpool(plugin_context, subnetpool_id)
  
      def _create_implicit_network(self, context, **kwargs):
          attrs = {'tenant_id': context.current['tenant_id'],
                   'name': context.current['name'], 'admin_state_up': True,
                   'shared': context.current.get('shared', False)}
          attrs.update(**kwargs)
          network = self._create_network(context._plugin_context, attrs)
          network_id = network['id']
          self._mark_network_owned(context._plugin_context.session, network_id)
          return network
  
      def _use_implicit_network(self, context, address_scope_v4=None,
                                address_scope_v6=None):
          network = self._create_implicit_network(
              context, name='l2p_' + context.current['name'],
              ipv4_address_scope=address_scope_v4,
              ipv6_address_scope=address_scope_v6)
          context.set_network_id(network['id'])
  
      def _cleanup_network(self, plugin_context, network_id):
          if self._network_is_owned(plugin_context.session, network_id):
              self._delete_network(plugin_context, network_id)
  
      def _generate_subnets_from_cidrs(self, context, l2p, l3p, cidrs,
                                       subnet_specifics):
          for usable_cidr in cidrs:
              try:
                  attrs = {'tenant_id': context.current['tenant_id'],
                           'name': 'ptg_' + context.current['name'],
                           'network_id': l2p['network_id'],
                           'ip_version': l3p['ip_version'],
                           'cidr': usable_cidr,
                           'enable_dhcp': True,
                           'gateway_ip': n_const.ATTR_NOT_SPECIFIED,
                           'allocation_pools': n_const.ATTR_NOT_SPECIFIED,
                           'dns_nameservers': (
                               MAPPING_CFG.dns_nameservers or
                               n_const.ATTR_NOT_SPECIFIED),
                           'host_routes': n_const.ATTR_NOT_SPECIFIED}
                  attrs.update(subnet_specifics)
                  subnet = self._create_subnet(
                      context._plugin_context, attrs)
                  yield subnet
              except n_exc.BadRequest:
                  # This is expected (CIDR overlap within network) until
                  # we have a proper subnet allocation algorithm. We
                  # ignore the exception and repeat with the next CIDR.
                  pass
  
      def _get_ptg_cidrs(self, context, ptgs, ptg_dicts=None):
          cidrs = []
          if ptg_dicts:
              ptgs = ptg_dicts
          else:
              ptgs = context._plugin.get_policy_target_groups(
                  context._plugin_context.elevated(), filters={'id': ptgs})
          subnets = []
          for ptg in ptgs:
              subnets.extend(ptg['subnets'])
  
          if subnets:
              cidrs = [x['cidr'] for x in self._get_subnets(
                  context._plugin_context.elevated(), {'id': subnets})]
          return cidrs
  
      def _get_subnet(self, context, subnet_id):
          context.session.expire_all()
          return super(ImplicitResourceOperations, self)._get_subnet(
              context, subnet_id)
  
      def _get_l3p_allocated_subnets(self, context, l3p_id):
          ptgs = context._plugin._get_l3p_ptgs(
              context._plugin_context.elevated(), l3p_id)
          return self._get_ptg_cidrs(context, None, ptg_dicts=ptgs)
  
      def _validate_and_add_subnet(self, context, subnet, l3p_id):
          subnet_id = subnet['id']
          session = context._plugin_context.session
          with session.begin(subtransactions=True):
              LOG.debug("starting validate_and_add_subnet transaction for "
                        "subnet %s", subnet_id)
              ptgs = context._plugin._get_l3p_ptgs(
                  context._plugin_context.elevated(), l3p_id)
              allocated = netaddr.IPSet(
                  iterable=self._get_ptg_cidrs(context, None,
                                               ptg_dicts=ptgs))
              cidr = subnet['cidr']
              if cidr in allocated:
                  LOG.debug("CIDR %s in-use for L3P %s, allocated: %s",
                            cidr, l3p_id, allocated)
                  raise CidrInUse(cidr=cidr, l3p_id=l3p_id)
              context.add_subnet(subnet_id)
              LOG.debug("ending validate_and_add_subnet transaction for "
                        "subnet %s", subnet_id)
  
      def _use_l2_proxy_implicit_subnets(self, context,
                                         subnet_specifics, l2p, l3p):
          LOG.debug("allocate subnets for L2 Proxy %s",
                    context.current['id'])
          proxied = context._plugin.get_policy_target_group(
              context._plugin_context, context.current['proxied_group_id'])
          subnets = self._get_subnets(context._plugin_context,
                                      {'id': proxied['subnets']})
          # Use the same subnets as the Proxied PTG
          generator = self._generate_subnets_from_cidrs(
              context, l2p, l3p, [x['cidr'] for x in subnets],
              subnet_specifics)
          # Unroll the generator
          subnets = [x for x in generator]
          subnet_ids = [x['id'] for x in subnets]
          for subnet_id in subnet_ids:
              self._mark_subnet_owned(
                  context._plugin_context.session, subnet_id)
              context.add_subnet(subnet_id)
          return subnets
  
      def _use_normal_implicit_subnet(self, context, is_proxy, prefix_len,
                                      subnet_specifics, l2p, l3p):
          LOG.debug("allocate subnets for L3 Proxy or normal PTG %s",
                    context.current['id'])
  
          # REVISIT(rkukura): The folowing is a temporary allocation
          # algorithm that should be replaced with use of a neutron
          # subnet pool.
          pool = netaddr.IPSet(gbp_utils.convert_ip_pool_string_to_list(
              l3p['proxy_ip_pool']) if is_proxy else
              gbp_utils.convert_ip_pool_string_to_list(l3p['ip_pool']))
          prefixlen = prefix_len or (
              l3p['proxy_subnet_prefix_length'] if is_proxy
              else l3p['subnet_prefix_length'])
          l3p_id = l3p['id']
          allocated = netaddr.IPSet(
              iterable=self._get_l3p_allocated_subnets(
                  context, l3p_id))
          available = pool - allocated
          available.compact()
  
          for cidr in sorted(available.iter_cidrs(),
                             key=operator.attrgetter('prefixlen'),
                             reverse=True):
              if prefixlen < cidr.prefixlen:
                  # Close the loop, no remaining subnet is big enough
                  # for this allocation
                  break
              generator = self._generate_subnets_from_cidrs(
                  context, l2p, l3p, cidr.subnet(prefixlen),
                  subnet_specifics)
              for subnet in generator:
                  LOG.debug("Trying subnet %s for PTG %s", subnet,
                            context.current['id'])
                  subnet_id = subnet['id']
                  try:
                      self._mark_subnet_owned(context._plugin_context.session,
                                              subnet_id)
                      self._validate_and_add_subnet(context, subnet, l3p_id)
                      LOG.debug("Using subnet %s for PTG %s", subnet,
                                context.current['id'])
                      return [subnet]
                  except CidrInUse:
                      # This exception is expected when a concurrent
                      # request has beat this one to calling
                      # _validate_and_add_subnet() using the same
                      # available CIDR. We delete the subnet and try the
                      # next available CIDR.
                      self._delete_subnet(context._plugin_context,
                                          subnet['id'])
                  except n_exc.InvalidInput:
                      # This exception is not expected. We catch this
                      # here so that it isn't caught below and handled
                      # as if the CIDR is already in use.
                      self._delete_subnet(context._plugin_context,
                                          subnet['id'])
                      raise exc.GroupPolicyInternalError()
  
          raise exc.NoSubnetAvailable()
  
      def _use_implicit_subnet(self, context, is_proxy=False, prefix_len=None,
                               subnet_specifics=None):
          subnet_specifics = subnet_specifics or {}
          l2p_id = context.current['l2_policy_id']
          l2p = context._plugin.get_l2_policy(context._plugin_context, l2p_id)
          l3p_id = l2p['l3_policy_id']
          l3p = context._plugin.get_l3_policy(context._plugin_context, l3p_id)
          if (is_proxy and
                  context.current['proxy_type'] == proxy_ext.PROXY_TYPE_L2):
              # In case of L2 proxy
              return self._use_l2_proxy_implicit_subnets(
                  context, subnet_specifics, l2p, l3p)
          else:
              # In case of non proxy PTG or L3 Proxy
              return self._use_normal_implicit_subnet(
                  context, is_proxy, prefix_len, subnet_specifics, l2p, l3p)
  
      def _use_implicit_subnet_from_subnetpool(
          self, context, subnet_specifics=None):
          # If a subnet needs to be created with a prefix_length other than
          # the subnet_prefix_length set for the l3_policy, a 'prefixlen' can be
          # passed explicitly in the subnet_specifics dict.
          # If a subnet with a specific CIDR needs to be created, the 'cidr' can
          # be passed explicitly in the subnet_specifics dict.
          # Note that either 'prefixlen' or 'cidr' can be requested, not both.
          # If a 'subnetpool_id' other than the one considered default is to be
          # used, it can be passed explicitly in the subnet_specifics dict.
          subnet_specifics = subnet_specifics or {}
          l2p_id = context.current['l2_policy_id']
          l2p = context._plugin.get_l2_policy(context._plugin_context, l2p_id)
          l3p_id = l2p['l3_policy_id']
          l3p_db = context._plugin.get_l3_policy(context._plugin_context, l3p_id)
          # Only allocate from subnetpools that belong to this tenant
          subnets = []
          for ip_version in (4, 6):
              # continue if no address scope defined for this ip version
              if not l3p_db[self.L3P_ADDRESS_SCOPE_KEYS[ip_version]]:
                  continue
  
              filters = {'tenant_id': [context.current['tenant_id']],
                         'id': l3p_db[self.L3P_SUBNETPOOLS_KEYS[ip_version]]}
              # All relevant subnetpools owned by this tenant
              candidate_subpools = self._get_subnetpools(
                  context._plugin_context, filters) or []
              del filters['tenant_id']
              filters['shared'] = [True]
              # All relevant shared subnetpools
              shared_subpools = self._get_subnetpools(
                  context._plugin_context, filters) or []
              # Union of the above two lists of subnetpools
              candidate_subpools = {x['id']: x for x in candidate_subpools +
                                    shared_subpools}.values()
              subnet = None
              for pool in candidate_subpools:
                  try:
                      attrs = {'tenant_id': context.current['tenant_id'],
                               'name': 'ptg_' + context.current['name'],
                               'network_id': l2p['network_id'],
                               'ip_version': ip_version,
                               'subnetpool_id': pool['id'],
                               'cidr': n_const.ATTR_NOT_SPECIFIED,
                               'prefixlen': n_const.ATTR_NOT_SPECIFIED,
                               'enable_dhcp': True,
                               'gateway_ip': n_const.ATTR_NOT_SPECIFIED,
                               'allocation_pools': n_const.ATTR_NOT_SPECIFIED,
                               'dns_nameservers': (
                                   MAPPING_CFG.dns_nameservers or
                                   n_const.ATTR_NOT_SPECIFIED),
                               'host_routes': n_const.ATTR_NOT_SPECIFIED}
                      if ip_version == 6:
                          if 'ipv6_ra_mode' not in subnet_specifics:
                              subnet_specifics['ipv6_ra_mode'] = (
                                  MAPPING_CFG.default_ipv6_ra_mode or
                                  n_const.ATTR_NOT_SPECIFIED)
                          if 'ipv6_address_mode' not in subnet_specifics:
                              subnet_specifics['ipv6_address_mode'] = (
                                  MAPPING_CFG.default_ipv6_address_mode or
                                  n_const.ATTR_NOT_SPECIFIED)
                      attrs.update(subnet_specifics)
                      subnet = self._create_subnet(context._plugin_context,
                                                   attrs)
                      self._mark_subnet_owned(context._plugin_context.session,
                                              subnet['id'])
                      LOG.debug("Allocated subnet %(sub)s from subnetpool: "
                                "%(sp)s.", {'sub': subnet['id'],
                                            'sp': pool['id']})
                      subnets.append(subnet)
                      break
                  except Exception as e:
                      if isinstance(e, oslo_db_excp.RetryRequest):
                          raise e
                      LOG.info("Allocating subnet from subnetpool %(sp)s "
                               "failed. Allocation will be attempted "
                               "from any other configured "
                               "subnetpool(s). Exception: %(excp)s",
                               {'sp': pool['id'], 'excp': type(e)})
                      last = e
                      continue
  
          if subnets:
              return subnets
          else:
              # In the case of multiple subnetpools configured, the failure
              # condition for subnet allocation on earlier subnetpools might
              # be different from that on the last one, however it might still
              # be more helpful to propagate this last exception instead of
              # a generic exception.
              raise last
  
      def _cleanup_subnet(self, plugin_context, subnet_id, router_id=None):
          interface_info = {'subnet_id': subnet_id}
          if router_id:
              try:
                  self._remove_router_interface(plugin_context, router_id,
                                                interface_info)
              except ext_l3.RouterInterfaceNotFoundForSubnet:
                  LOG.debug("Ignoring RouterInterfaceNotFoundForSubnet cleaning "
                            "up subnet: %s", subnet_id)
          if self._subnet_is_owned(plugin_context.session, subnet_id):
              self._delete_subnet(plugin_context, subnet_id)
  
      def _get_default_security_group(self, plugin_context, ptg_id,
                                      tenant_id):
          port_name = DEFAULT_SG_PREFIX % ptg_id
          filters = {'name': [port_name], 'tenant_id': [tenant_id]}
          default_group = self._get_sgs(plugin_context, filters)
          return default_group[0]['id'] if default_group else None
  
      def _use_implicit_port(self, context, subnets=None):
          ptg_id = context.current['policy_target_group_id']
          ptg = context._plugin.get_policy_target_group(
              context._plugin_context, ptg_id)
          l2p_id = ptg['l2_policy_id']
          l2p = context._plugin.get_l2_policy(context._plugin_context, l2p_id)
          sg_id = self._get_default_security_group(
              context._plugin_context, ptg_id, context.current['tenant_id'])
          last = exc.NoSubnetAvailable()
          subnets = subnets or self._get_subnets(context._plugin_context,
                                                 {'id': ptg['subnets']})
          v4_subnets = [subnet for subnet in subnets
                        if subnet['ip_version'] == 4]
          v6_subnets = [subnet for subnet in subnets
                        if subnet['ip_version'] == 6]
          for subnet in subnets:
              def subnet_family_generator(family_subnets):
                  def inner():
                      for subnet in family_subnets:
                          yield subnet
                  return inner
              # For dual-stack, try to create with this subnet and
              # a subnet from the other address family. Try this for
              # each address family
              if not (v4_subnets and v6_subnets):
                  # for single stack, we need the empty list to
                  # guarantee a single iteration
                  subnet_generator = subnet_family_generator([''])
              elif subnet['ip_version'] == 4:
                  subnet_generator = subnet_family_generator(v6_subnets)
              else:
                  subnet_generator = subnet_family_generator(v4_subnets)
              for alt_subnet in subnet_generator():
                  fixed_ips = [{'subnet_id': subnet['id']}]
                  if alt_subnet:
                      fixed_ips.append({'subnet_id': alt_subnet['id']})
                  try:
                      attrs = {'tenant_id': context.current['tenant_id'],
                               'name': 'pt_' + context.current['name'],
                               'network_id': l2p['network_id'],
                               'mac_address': n_const.ATTR_NOT_SPECIFIED,
                               'fixed_ips': fixed_ips,
                               'device_id': '',
                               'device_owner': '',
                               'security_groups': [sg_id] if sg_id else None,
                               'admin_state_up': True}
                      if context.current.get('group_default_gateway'):
                          attrs['fixed_ips'][0]['ip_address'] = subnet[
                              'gateway_ip']
                      attrs.update(context.current.get('port_attributes', {}))
                      port = self._create_port(context._plugin_context, attrs)
                      port_id = port['id']
                      self._mark_port_owned(context._plugin_context.session,
                                            port_id)
                      context.set_port_id(port_id)
                      return
                  except n_exc.IpAddressGenerationFailure as ex:
                      LOG.warning("No more address available in subnet %s",
                                  subnet['id'])
                      last = ex
          raise last
  
      def _cleanup_port(self, plugin_context, port_id):
          if self._port_is_owned(plugin_context.session, port_id):
              try:
                  self._delete_port(plugin_context, port_id)
              except n_exc.PortNotFound:
                  LOG.warning("Port %s is missing", port_id)
  
      def _reject_invalid_router_access(self, context):
          # Validate if the explicit router(s) belong to the tenant.
          # Are routers shared across tenants ??
          # How to check if admin and if admin can access all routers ??
          for router_id in context.current['routers']:
              router = None
              try:
                  router = self._get_router(context._plugin_context, router_id)
              except n_exc.NotFound:
                  raise exc.InvalidRouterAccess(
                      msg="Can't access other tenants router",
                      router_id=router_id,
                      tenant_id=context.current['tenant_id'])
  
              if router:
                  tenant_id_of_explicit_router = router['tenant_id']
                  curr_tenant_id = context.current['tenant_id']
                  if tenant_id_of_explicit_router != curr_tenant_id:
                      raise exc.InvalidRouterAccess(
                          msg="Can't access other tenants router",
                          router_id=router_id,
                          tenant_id=context.current['tenant_id'])
  
      def _use_implicit_router(self, context, router_name=None):
          attrs = {'tenant_id': context.current['tenant_id'],
                   'name': router_name or ('l3p_' + context.current['name']),
                   'external_gateway_info': None,
                   'admin_state_up': True}
          router = self._create_router(context._plugin_context, attrs)
          router_id = router['id']
          self._mark_router_owned(context._plugin_context.session, router_id)
          context.add_router(router_id)
          return router_id
  
      def _cleanup_router(self, plugin_context, router_id):
          if self._router_is_owned(plugin_context.session, router_id):
              self._delete_router(plugin_context, router_id)
  
      def _plug_router_to_subnet(self, plugin_context, subnet_id, router_id):
          interface_info = {'subnet_id': subnet_id}
          if router_id:
              try:
                  self._add_router_interface(plugin_context, router_id,
                                             interface_info)
              except n_exc.BadRequest as e:
                  LOG.exception("Adding subnet to router failed, exception:"
                                "%s", e)
                  raise exc.GroupPolicyInternalError()
  
      def _add_router_interface_for_subnet(self, context, router_id, subnet_id):
          self._plug_router_to_subnet(
              context._plugin_context, subnet_id, router_id)
  
      def _get_l3p_for_l2policy(self, context, l2p_id):
          l2p = context._plugin.get_l2_policy(context._plugin_context, l2p_id)
          l3p_id = l2p['l3_policy_id']
          l3p = context._plugin.get_l3_policy(context._plugin_context, l3p_id)
          return l3p
  
      def _create_router_gw_for_external_segment(self, plugin_context, es,
                                                 es_dict, router_id):
          subnet = self._get_subnet(plugin_context.elevated(), es['subnet_id'])
          external_fixed_ips = [
              {'subnet_id': es['subnet_id'], 'ip_address': x}
              if x else {'subnet_id': es['subnet_id']}
              for x in es_dict[es['id']]
          ] if es_dict[es['id']] else [{'subnet_id': es['subnet_id']}]
          interface_info = {
              'network_id': subnet['network_id'],
              'enable_snat': es['port_address_translation'],
              'external_fixed_ips': external_fixed_ips}
          router = self._add_router_gw_interface(
              plugin_context, router_id, interface_info)
          return router
  
      def _reject_non_shared_net_on_shared_l2p(self, context):
          if context.current.get('shared') and context.current['network_id']:
              net = self._get_network(
                  context._plugin_context, context.current['network_id'])
              if not net.get('shared'):
                  raise exc.NonSharedNetworkOnSharedL2PolicyNotSupported()
  
      def _reject_invalid_network_access(self, context):
          # Validate if the explicit network belongs to the tenant.
          # Are networks shared across tenants ??
          # How to check if admin and if admin can access all networks ??
          if context.current['network_id']:
              network_id = context.current['network_id']
              plugin_context = context._plugin_context
              network = None
              try:
                  network = self._get_network(plugin_context, network_id)
              except n_exc.NetworkNotFound:
                  raise exc.InvalidNetworkAccess(
                      msg="Can't access other tenants networks",
                      network_id=context.current['network_id'],
                      tenant_id=context.current['tenant_id'])
  
              if network:
                  tenant_id_of_explicit_net = network['tenant_id']
                  if tenant_id_of_explicit_net != context.current['tenant_id']:
                      raise exc.InvalidNetworkAccess(
                          msg="Can't access other tenants networks",
                          network_id=context.current['network_id'],
                          tenant_id=context.current['tenant_id'])
  
      def _add_nat_pool_to_segment(self, context):
          external_segment = context._plugin.get_external_segment(
              context._plugin_context, context.current['external_segment_id'])
          if not external_segment['subnet_id']:
              raise exc.ESSubnetRequiredForNatPool()
          ext_sub = self._get_subnet(context._plugin_context,
                                     external_segment['subnet_id'])
          # Verify there's no overlap. This will also be verified by Neutron at
          # subnet creation, but we try to fail as soon as possible to return
          # a nicer error to the user (concurrency may still need to fallback on
          # Neutron's validation).
          ext_subs = self._get_subnets(context._plugin_context,
                                       {'network_id': [ext_sub['network_id']]})
          peer_pools = context._plugin.get_nat_pools(
                  context._plugin_context.elevated(),
                  {'id': external_segment['nat_pools']})
          peer_set = netaddr.IPSet(
              [x['ip_pool'] for x in peer_pools if
               x['id'] != context.current['id']])
          curr_ip_set = netaddr.IPSet([context.current['ip_pool']])
          if peer_set & curr_ip_set:
              # Raise for overlapping CIDRs
              raise exc.OverlappingNATPoolInES(
                  es_id=external_segment['id'], np_id=context.current['id'])
          # A perfect subnet overlap is allowed as long as the subnet can be
          # assigned to the pool.
          match = [x for x in ext_subs if x['cidr'] ==
                   context.current['ip_pool']]
          if match:
              # There's no owning peer given the overlapping check above.
              # Use this subnet on the current Nat pool
              context._plugin._set_db_np_subnet(
                  context._plugin_context, context.current, match[0]['id'])
          elif netaddr.IPSet([x['cidr'] for x in ext_subs]) & curr_ip_set:
              # Partial overlapp not allowed
              raise exc.OverlappingSubnetForNATPoolInES(
                  net_id=ext_sub['network_id'], np_id=context.current['id'])
          # At this point, either a subnet was assigned to the NAT Pool, or a new
          # one needs to be created by the postcommit operation.
  
      def _use_implicit_nat_pool_subnet(self, context):
          es = context._plugin.get_external_segment(
              context._plugin_context, context.current['external_segment_id'])
          ext_sub = self._get_subnet(context._plugin_context, es['subnet_id'])
          attrs = {'tenant_id': context.current['tenant_id'],
                   'name': 'ptg_' + context.current['name'],
                   'network_id': ext_sub['network_id'],
                   'ip_version': context.current['ip_version'],
                   'cidr': context.current['ip_pool'],
                   'enable_dhcp': False,
                   'gateway_ip': n_const.ATTR_NOT_SPECIFIED,
                   'allocation_pools': n_const.ATTR_NOT_SPECIFIED,
                   'dns_nameservers': n_const.ATTR_NOT_SPECIFIED,
                   'host_routes': n_const.ATTR_NOT_SPECIFIED}
          subnet = self._create_subnet(context._plugin_context, attrs)
          context._plugin._set_db_np_subnet(
              context._plugin_context, context.current, subnet['id'])
          self._mark_subnet_owned(context._plugin_context.session, subnet['id'])
          return subnet
  
      def _process_ext_segment_update_for_nat_pool(self, context):
          nsps_using_nat_pool = self._get_nsps_using_nat_pool(context)
          if (context.original['external_segment_id'] !=
                  context.current['external_segment_id']):
              if nsps_using_nat_pool:
                  raise exc.NatPoolinUseByNSP()
              # Clean the current subnet_id. The subnet itself will be
              # cleaned by the postcommit operation
              context._plugin._set_db_np_subnet(
                  context._plugin_context, context.current, None)
              self._add_nat_pool_to_segment(context)
  
      def _add_implicit_subnet_for_nat_pool_update(self, context):
          # For backward compatibility, do the following only if the external
          # segment changed
          if (context.original['external_segment_id'] !=
                  context.current['external_segment_id']):
              if context.original['subnet_id']:
                  if self._subnet_is_owned(context._plugin_context.session,
                                           context.original['subnet_id']):
                      self._delete_subnet(context._plugin_context,
                                          context.original['subnet_id'])
              if (context.current['external_segment_id'] and not
                      context.current['subnet_id']):
                  self._use_implicit_nat_pool_subnet(context)
  
      def _add_implicit_subnet_for_nat_pool_create(self, context):
          if (context.current['external_segment_id'] and not
                  context.current['subnet_id']):
              self._use_implicit_nat_pool_subnet(context)
  
      def _get_nsps_using_nat_pool(self, context):
          external_segment = context._plugin.get_external_segment(
              context._plugin_context, context.current['external_segment_id'])
          l3_policies = external_segment['l3_policies']
          l3_policies = context._plugin.get_l3_policies(
                      context._plugin_context, filters={'id': l3_policies})
          l2_policies = []
          for x in l3_policies:
              l2_policies.extend(x['l2_policies'])
          l2_policies = context._plugin.get_l2_policies(
                      context._plugin_context, filters={'id': l2_policies})
          ptgs = []
          for l2_policy in l2_policies:
              ptgs.extend(l2_policy['policy_target_groups'])
          ptgs = context._plugin.get_policy_target_groups(
                      context._plugin_context, filters={'id': ptgs})
          nsps = [x['network_service_policy_id'] for x in ptgs
                  if x['network_service_policy_id']]
          nsps = context._plugin.get_network_service_policies(
              context._plugin_context, filters={'id': nsps})
          nsps_using_nat_pool = []
          for nsp in nsps:
              nsp_params = nsp.get("network_service_params")
              for nsp_param in nsp_params:
                  if nsp_param['value'] == "nat_pool":
                      nsps_using_nat_pool.append(nsp)
                      break
          return nsps_using_nat_pool
  
      def _check_nat_pool_subnet_in_use(self, plugin_context, nat_pool):
          if not self._subnet_is_owned(plugin_context.session,
                                       nat_pool['subnet_id']):
              return
          # check if there are any ports with an address in nat-pool subnet
          ports = self._get_ports(plugin_context.elevated(),
              filters={'fixed_ips': {'subnet_id': [nat_pool['subnet_id']]}})
          if ports:
              raise exc.NatPoolInUseByPort()
  
      def _nat_pool_in_use(self, context):
          nsps_using_nat_pool = self._get_nsps_using_nat_pool(context)
          if nsps_using_nat_pool:
              raise exc.NatPoolinUseByNSP()
          self._check_nat_pool_subnet_in_use(context._plugin_context,
                                             context.current)
  
      def _delete_subnet_on_nat_pool_delete(self, context):
          if context.current['subnet_id']:
              if self._subnet_is_owned(context._plugin_context.session,
                                       context.current['subnet_id']):
                  self._delete_subnet(context._plugin_context,
                                      context.current['subnet_id'])
  
      def _validate_nsp_parameters(self, context):
          nsp = context.current
          nsp_params = nsp.get("network_service_params")
  
          supported_static_nsp_pars = {
              gconst.GP_NETWORK_SVC_PARAM_TYPE_IP_SINGLE: [
                  gconst.GP_NETWORK_SVC_PARAM_VALUE_SELF_SUBNET,
                  gconst.GP_NETWORK_SVC_PARAM_VALUE_NAT_POOL],
              gconst.GP_NETWORK_SVC_PARAM_TYPE_IP_POOL: [
                  gconst.GP_NETWORK_SVC_PARAM_VALUE_NAT_POOL]}
  
          # for params without a static value - later evaluation needed:
          supported_flexible_nsp_params = (
              gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST,
              gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX)
  
          # validate unique param types:
          types_inside = set((d['type'] for d in nsp_params))
          if len(types_inside) != len(nsp_params):
              raise exc.InvalidNetworkServiceParameters()
  
          for params in nsp_params:
              type_ = params.get("type")
              value_ = params.get("value")
              if (type_ not in supported_flexible_nsp_params):
                  if (type_ not in supported_static_nsp_pars or
                      value_ not in supported_static_nsp_pars[type_]):
                      raise exc.InvalidNetworkServiceParameters()
              else:
                  try:
                      if int(value_) < 0:
                          raise exc.InvalidNetworkServiceParameters()
                  except ValueError:
                      raise exc.InvalidNetworkServiceParameters()
  
      def _validate_in_use_by_nsp(self, context):
          # We do not allow ES update for L3p when it is used by NSP
          # At present we do not support multiple ES, so adding a new ES is
          # not an issue here
          if (context.original['external_segments'] !=
              context.current['external_segments'] and
              context.original['external_segments']):
              l2_policies = context.current['l2_policies']
              l2_policies = context._plugin.get_l2_policies(
                      context._plugin_context, filters={'id': l2_policies})
              ptgs = []
              for l2p in l2_policies:
                  ptgs.extend(l2p['policy_target_groups'])
              ptgs = context._plugin.get_policy_target_groups(
                      context._plugin_context, filters={'id': ptgs})
              nsps = [x['network_service_policy_id'] for x in ptgs
                      if x['network_service_policy_id']]
              if nsps:
                  nsps = context._plugin.get_network_service_policies(
                      context._plugin_context, filters={'id': nsps})
                  for nsp in nsps:
                      nsp_params = nsp.get("network_service_params")
                      for nsp_param in nsp_params:
                          if nsp_param['value'] == "nat_pool":
                              raise exc.L3PEsinUseByNSP()
  
      def _associate_fip_to_pt(self, context):
          ptg_id = context.current['policy_target_group_id']
          ptg = context._plugin.get_policy_target_group(
              context._plugin_context, ptg_id)
          network_service_policy_id = ptg.get(
              "network_service_policy_id")
          if not network_service_policy_id:
              return
  
          nsp = context._plugin.get_network_service_policy(
              context._plugin_context, network_service_policy_id)
          nsp_params = nsp.get("network_service_params")
          for nsp_parameter in nsp_params:
              if (nsp_parameter["type"] == "ip_pool" and
                  nsp_parameter["value"] == "nat_pool"):
                  fip_ids = self._allocate_floating_ips(
                      context, ptg['l2_policy_id'], context.current['port_id'])
                  self._set_pt_floating_ips_mapping(
                      context._plugin_context.session,
                      context.current['id'],
                      fip_ids)
                  return
  
      def _retrieve_es_with_nat_pools(self, context, l2_policy_id):
          es_list_with_nat_pools = []
          l2p = context._plugin.get_l2_policy(
                      context._plugin_context, l2_policy_id)
          l3p = context._plugin.get_l3_policy(context._plugin_context,
                                              l2p['l3_policy_id'])
          external_segments = l3p.get('external_segments').keys()
          if not external_segments:
              return es_list_with_nat_pools
          external_segments = context._plugin.get_external_segments(
              context._plugin_context,
              filters={'id': external_segments})
          for es in external_segments:
              if es['nat_pools']:
                  es_list_with_nat_pools.append(es)
          return es_list_with_nat_pools
  
      def _gen_nat_pool_in_ext_seg(self, context, tenant_id, es):
          nat_pools = context._plugin.get_nat_pools(
              context._plugin_context.elevated(), {'id': es['nat_pools']})
          no_subnet_pools = []
          for nat_pool in nat_pools:
              # For backward compatibility
              if not nat_pool['subnet_id']:
                  no_subnet_pools.append(nat_pool)
              else:
                  yield nat_pool
          for nat_pool in no_subnet_pools:
              # Use old allocation method
              yield nat_pool
  
      def _allocate_floating_ips(self, context, l2_policy_id, fixed_port=None,
                                 external_segments=None):
          if not external_segments:
              external_segments = self._retrieve_es_with_nat_pools(
                                              context, l2_policy_id)
          fip_ids = []
          if not external_segments:
              LOG.error("Network Service Policy to allocate Floating IP "
                        "could not be applied because l3policy does "
                        "not have an attached external segment")
              return fip_ids
          tenant_id = context.current['tenant_id']
  
          # Retrieve Router ID
          l2p = context._plugin.get_l2_policy(context._plugin_context,
                                              l2_policy_id)
          l3p = context._plugin.get_l3_policy(context._plugin_context,
                                              l2p['l3_policy_id'])
          if l3p.get('routers'):
              routers = self._get_routers(context._plugin_context,
                                          filters={'id': l3p['routers']})
          else:
              routers = []
          for es in external_segments:
              ext_sub = self._get_subnet(context._plugin_context,
                                         es['subnet_id'])
              ext_net_id = ext_sub['network_id']
              fip_id = None
              # For each L3P for an ES, we need to find the router
              # that's connected to the external segment, so we can
              # use that router ID in the floating IP allocated by
              # a NAT pool on that ES.
              rid = None
              for router in routers:
                  if not router['external_gateway_info']:
                      continue
                  net = router['external_gateway_info'].get('network_id')
                  if net == ext_net_id:
                      rid = router['id']
                      break
  
              for nat_pool in self._gen_nat_pool_in_ext_seg(
                  context, tenant_id, es):
                  try:
                      fip_id = self._create_floatingip(
                          context._plugin_context, tenant_id, ext_net_id,
                          fixed_port, subnet_id=nat_pool['subnet_id'],
                          router_id=rid)
                      # FIP allocated, no need to try further allocation
                      break
                  except n_exc.IpAddressGenerationFailure as ex:
                      LOG.warning("Floating allocation failed: %s",
                                  ex.message)
              if fip_id:
                  fip_ids.append(fip_id)
          return fip_ids
  
      def _create_floatingip(self, plugin_context, tenant_id, ext_net_id,
                             internal_port_id=None, floating_ip_address=None,
                             subnet_id=None, router_id=None):
          attrs = {'tenant_id': tenant_id,
                   'floating_network_id': ext_net_id}
          if subnet_id:
              attrs.update({"subnet_id": subnet_id})
          if router_id:
              attrs['router_id'] = router_id
          if internal_port_id:
              attrs.update({"port_id": internal_port_id})
          if floating_ip_address:
              attrs.update({"floating_ip_address": floating_ip_address})
          fip = self._create_fip(plugin_context, attrs)
          return fip['id']
  
      def _cleanup_network_service_policy(self, context, ptg,
                                          ipaddress=None, fip_maps=None):
          if not ipaddress:
              ipaddress = self._get_ptg_policy_ipaddress_mapping(
                  context._plugin_context.session, ptg['id'])
          if ipaddress and ptg['subnets']:
              # TODO(rkukura): Loop on subnets?
              self._restore_ip_to_allocation_pool(
                  context, ptg['subnets'][0], ipaddress.ipaddress)
          self._delete_policy_ipaddress_mapping(
              context._plugin_context.session, ptg['id'])
          if not fip_maps:
              fip_maps = self._get_ptg_policy_fip_mapping(
                  context._plugin_context.session, ptg['id'])
          for fip_map in fip_maps:
              self._delete_fip(context._plugin_context, fip_map.floatingip_id)
          self._delete_ptg_policy_fip_mapping(
              context._plugin_context.session, ptg['id'])
  
          for pt in ptg['policy_targets']:
              pt_fip_maps = self._get_pt_floating_ip_mapping(
                      context._plugin_context.session, pt)
              for pt_fip_map in pt_fip_maps:
                  self._delete_fip(context._plugin_context,
                                   pt_fip_map.floatingip_id)
              self._delete_pt_floating_ip_mapping(
                  context._plugin_context.session, pt)
  
      def _handle_nsp_update_on_ptg(self, context):
          old_nsp = context.original.get("network_service_policy_id")
          new_nsp = context.current.get("network_service_policy_id")
          if old_nsp != new_nsp:
              if old_nsp:
                  self._cleanup_network_service_policy(
                                          context,
                                          context.original)
              if new_nsp:
                  self._handle_network_service_policy(context)
  
      def _validate_nat_pool_for_nsp(self, context):
          network_service_policy_id = context.current.get(
              "network_service_policy_id")
          if not network_service_policy_id:
              return
  
          nsp = context._plugin.get_network_service_policy(
              context._plugin_context, network_service_policy_id)
          nsp_params = nsp.get("network_service_params")
          for nsp_parameter in nsp_params:
              external_segments = []
              if ((nsp_parameter["type"] == "ip_single" or
                   nsp_parameter["type"] == "ip_pool") and
                  nsp_parameter["value"] == "nat_pool"):
                  if context.current['l2_policy_id']:
                      l2p = context._plugin.get_l2_policy(
                          context._plugin_context,
                          context.current['l2_policy_id'])
                      l3p = context._plugin.get_l3_policy(
                          context._plugin_context, l2p['l3_policy_id'])
                      external_segments = l3p.get('external_segments').keys()
                      if external_segments:
                          external_segments = (
                              context._plugin.get_external_segments(
                                  context._plugin_context,
                                  filters={'id': external_segments}))
                  else:
                      gpip = cfg.CONF.group_policy_implicit_policy
                      filter = {'tenant_id': [context.current['tenant_id']],
                                'name': [gpip.default_l3_policy_name]}
                      l3ps = context._plugin.get_l3_policies(
                                      context._plugin_context, filter)
                      if l3ps:
                          external_segments = l3ps[0].get(
                                                  'external_segments').keys()
                          if external_segments:
                              external_segments = (
                                  context._plugin.get_external_segments(
                                      context._plugin_context,
                                      filters={'id': external_segments}))
                      else:
                          external_segments = (
                              context._plugin.get_external_segments(
                                  context._plugin_context,
                                  filters={'name': [
                                          gpip.default_external_segment_name]}))
                  if not external_segments:
                      LOG.error(
                          "Network Service Policy to allocate Floating "
                          "IP could not be associated because l3policy "
                          "does not have an attached external segment")
                      raise exc.NSPRequiresES()
                  for es in external_segments:
                      if not es['nat_pools']:
                          raise exc.NSPRequiresNatPool()
  
      def _handle_network_service_policy(self, context):
          network_service_policy_id = context.current.get(
              "network_service_policy_id")
          if not network_service_policy_id:
              return
          nsp = context._plugin.get_network_service_policy(
              context._plugin_context, network_service_policy_id)
          nsp_params = nsp.get("network_service_params")
  
          for nsp_parameter in nsp_params:
              if (nsp_parameter["type"] == "ip_single" and
                  nsp_parameter["value"] == "self_subnet"):
                  # TODO(Magesh):Handle concurrency issues
                  free_ip = self._get_last_free_ip(context._plugin_context,
                                                   context.current['subnets'])
                  if not free_ip:
                      LOG.error("Reserving IP Addresses failed for Network "
                                "Service Policy. No more IP Addresses on "
                                "subnet")
                      return
                  # TODO(Magesh):Fetch subnet from PTG to which NSP is attached
                  self._remove_ip_from_allocation_pool(
                      context, context.current['subnets'][0], free_ip)
                  self._set_policy_ipaddress_mapping(
                      context._plugin_context.session,
                      network_service_policy_id,
                      context.current['id'],
                      free_ip)
              elif (nsp_parameter["type"] == "ip_single" and
                    nsp_parameter["value"] == "nat_pool"):
                  # REVISIT(Magesh): We are logging an error when FIP allocation
                  # fails. Should we fail PT create instead ?
                  fip_ids = self._allocate_floating_ips(
                      context, context.current['l2_policy_id'])
                  for fip_id in fip_ids:
                      self._set_ptg_policy_fip_mapping(
                          context._plugin_context.session,
                          network_service_policy_id,
                          context.current['id'],
                          fip_id)
              elif (nsp_parameter["type"] == "ip_pool" and
                    nsp_parameter["value"] == "nat_pool"):
                  policy_targets = context.current['policy_targets']
                  policy_targets = context._plugin.get_policy_targets(
                      context._plugin_context, filters={'id': policy_targets})
                  es_list = self._retrieve_es_with_nat_pools(
                          context, context.current['l2_policy_id'])
                  pt_fip_map = {}
                  for policy_target in policy_targets:
                      fip_ids = self._allocate_floating_ips(
                          context,
                          context.current['l2_policy_id'],
                          fixed_port=policy_target['port_id'],
                          external_segments=es_list)
                      if fip_ids:
                          pt_fip_map[policy_target['id']] = fip_ids
                  if pt_fip_map:
                      self._set_pts_floating_ips_mapping(
                          context._plugin_context.session, pt_fip_map)
  
      def _restore_ip_to_allocation_pool(self, context, subnet_id, ip_address):
          # TODO(Magesh):Pass subnets and loop on subnets. Better to add logic
          # to Merge the pools together after Fragmentation
          subnet = self._get_subnet(context._plugin_context, subnet_id)
          allocation_pools = subnet['allocation_pools']
          for allocation_pool in allocation_pools:
              pool_end_ip = allocation_pool.get('end')
              if ip_address == str(netaddr.IPAddress(pool_end_ip) + 1):
                  new_last_ip = ip_address
                  allocation_pool['end'] = new_last_ip
                  del subnet['gateway_ip']
                  subnet = self._update_subnet(context._plugin_context,
                                               subnet['id'], subnet)
                  return
  
          # TODO(Magesh):Have to test this logic. Add proper unit tests
          subnet['allocation_pools'].append({"start": ip_address,
                                            "end": ip_address})
          del subnet['gateway_ip']
          subnet = self._update_subnet(context._plugin_context,
                                       subnet['id'], subnet)
  
      def _remove_ip_from_allocation_pool(self, context, subnet_id, ip_address):
          # TODO(Magesh):Pass subnets and loop on subnets
          subnet = self._get_subnet(context._plugin_context, subnet_id)
          allocation_pools = subnet['allocation_pools']
          for allocation_pool in reversed(allocation_pools):
              if ip_address == allocation_pool.get('end'):
                  new_last_ip = str(netaddr.IPAddress(ip_address) - 1)
                  allocation_pool['end'] = new_last_ip
                  del subnet['gateway_ip']
                  self._update_subnet(context._plugin_context,
                                      subnet['id'], subnet)
                  break
  
      def _get_last_free_ip(self, context, subnets):
          # Hope lock_mode update is not needed
          # REVISIT: Temp workaround, always assumes last IP in subnet is
          # available
          range_qry = context.session.query(models_v2.IPAllocationPool)
          for subnet_id in subnets:
              ip_range = range_qry.filter_by(subnet_id=subnet_id).first()
              if not ip_range:
                  continue
              ip_address = ip_range['last_ip']
              return ip_address
  
      def _get_in_use_subnetpools_for_l3p(self, context):
          return [x.subnetpool_id for x in
                  context._plugin_context.session.query(models_v2.Subnet).join(
                      gpmdb.PTGToSubnetAssociation,
                      gpmdb.PTGToSubnetAssociation.subnet_id ==
                      models_v2.Subnet.id
                  ).join(gpmdb.PolicyTargetGroupMapping,
                         gpmdb.PTGToSubnetAssociation.policy_target_group_id ==
                         gpmdb.PolicyTargetGroupMapping.id).join(
                             gpmdb.L2PolicyMapping).join(
                                 gpmdb.L3PolicyMapping).filter(
                                     gpmdb.L2PolicyMapping.l3_policy_id ==
                                     context.current['id']).all()]
  
      def _check_subnetpools_for_same_scope(self, context, subnetpools,
                                            ascp, prefixes=None):
          sp_ascp = None
          for sp_id in subnetpools:
              sp = self._get_subnetpool(
                  # admin context to retrieve subnetpools from
                  # other tenants
                  context._plugin_context.elevated(), sp_id)
              if not sp['address_scope_id']:
                  raise exc.NoAddressScopeForSubnetpool()
              if not sp_ascp:
                  if ascp:
                      # This is the case where the address_scope
                      # was explicitly set for the l3p  and we need to
                      # check if it conflicts with the address_scope of
                      # the first subnetpool
                      if sp['address_scope_id'] != ascp:
                          raise exc.InconsistentAddressScopeSubnetpool()
                  else:
                      # No address_scope was explicitly set for the l3p,
                      # so set it to that of the first subnetpool
                      ascp = sp['address_scope_id']
                  sp_ascp = sp['address_scope_id']
              elif sp_ascp != sp['address_scope_id']:
                  # all subnetpools do not have the same address_scope
                  raise exc.InconsistentAddressScopeSubnetpool()
              # aggregate subnetpool prefixes
              sp_prefixlist = [prefix for prefix in sp['prefixes']]
              if prefixes:
                  stripped = [prefix.strip() for prefix in prefixes.split(',')]
                  prefixes = ', '.join(stripped + sp_prefixlist)
              else:
                  prefixes = ', '.join(sp_prefixlist)
          return ascp, prefixes
  
      def _configure_l3p_for_multiple_subnetpools(self, context,
                                                  l3p_db, ip_version=4,
                                                  address_scope_id=None):
          l3p_req = context.current
          ascp_id_key = 'address_scope_v4_id' if ip_version == 4 else (
              'address_scope_v6_id')
          subpool_ids_key = 'subnetpools_v4' if ip_version == 4 else (
              'subnetpools_v6')
          # admin context to retrieve subnetpools from a different tenant
          address_scope_id, prefixes = self._check_subnetpools_for_same_scope(
              context, l3p_req[subpool_ids_key], address_scope_id,
              prefixes=l3p_db['ip_pool'])
          l3p_db[ascp_id_key] = address_scope_id
          l3p_db['ip_pool'] = prefixes
          if l3p_req['subnet_prefix_length']:
              l3p_db['subnet_prefix_length'] = l3p_req['subnet_prefix_length']
  
      def _create_l3p_subnetpools(self, context):
          l3p_req = context.current
          l3p_db = context._plugin._get_l3_policy(
              context._plugin_context, l3p_req['id'])
          # The ip_version tells us what should be supported
          ip_version = l3p_req['ip_version']
          l3p_db['ip_version'] = ip_version
          # First determine the address scope for the address
          # families specified in ip_version. We look first at
          # explicitly passed address scopes, then the address
          # scopes of the subnetpools, then the address scopes
          # of default defined subnetpool (via that extension),
          # or just create one if none are present
          ip_dict = {}
          ascp = None
          # for pools that need to be created, we
          # want to use subnet_prefix_length as the
          # default for v4 subnets, and /64 for v6
          # subnets. If a subnet_prefix_length wasn't
          # provided, we use the implict default
          if ip_version == 4 or ip_version == 46:
              ip_dict[4] = {'default_prefixlen':
                            l3p_req['subnet_prefix_length'] or 24}
          if ip_version == 6 or ip_version == 46:
              ip_dict[6] = {'default_prefixlen': 64}
  
          for family in ip_dict.keys():
              explicit_scope = l3p_req[self.L3P_ADDRESS_SCOPE_KEYS[family]]
              explicit_pools = l3p_req[self.L3P_SUBNETPOOLS_KEYS[family]]
              default_pool = self._core_plugin.get_default_subnetpool(
                  context._plugin_context.elevated(), ip_version=family)
              ip_pool = gbp_utils.convert_ip_pool_string_to_list(
                  l3p_req['ip_pool'])
              family_prefixes = [prefix for prefix in ip_pool
                                 if netaddr.IPNetwork(prefix).version == family]
              if explicit_scope:
                  ascp = explicit_scope
              elif explicit_pools:
                  ascp, _ = self._check_subnetpools_for_same_scope(context,
                      explicit_pools, None)
                  l3p_db[self.L3P_ADDRESS_SCOPE_KEYS[family]] = ascp
              elif family_prefixes:
                  ascp = self._use_implicit_address_scope(
                      context, ip_version=family)['id']
  
              elif default_pool and default_pool.get('address_scope_id'):
                  ascp = default_pool['address_scope_id']
              else:
                  raise exc.NoValidAddressScope()
  
              if explicit_scope or explicit_pools:
                  # In the case of explicitly provided address_scope or
                  # subnetpools, set shared flag of L3P to the address_scope
                  ascp_db = self._get_address_scope(
                      context._plugin_context, ascp)
                  l3p_db['shared'] = ascp_db['shared']
                  context.current['shared'] = l3p_db['shared']
  
              if not explicit_pools and family_prefixes:
                  self._use_implicit_subnetpool(context,
                      address_scope_id=ascp,
                      ip_version=family, prefixes=family_prefixes,
                      default_prefixlen=ip_dict[family]['default_prefixlen'])
              elif not explicit_pools and default_pool:
                  l3p_req[self.L3P_SUBNETPOOLS_KEYS[family]] = [
                      default_pool['id']]
                  context._plugin._add_subnetpools_to_l3_policy(
                      context._plugin_context, l3p_db, [default_pool['id']],
                      ip_version=family)
  
              # TODO(Sumit): check that l3p['ip_pool'] does not overlap with an
              # existing subnetpool associated with the explicit address_scope
              self._configure_l3p_for_multiple_subnetpools(context,
                  l3p_db, ip_version=family,
                  address_scope_id=ascp)
  
      def _update_l3p_subnetpools(self, context):
          l3p_orig = context.original
          l3p_curr = context.current
          for family in (4, 6):
              subnetpools_key = self.L3P_SUBNETPOOLS_KEYS[family]
              address_scope_key = self.L3P_ADDRESS_SCOPE_KEYS[family]
              if (l3p_curr[subnetpools_key] and (
                  l3p_curr[subnetpools_key] != l3p_orig[subnetpools_key])):
                  l3p_db = context._plugin._get_l3_policy(
                      context._plugin_context, l3p_curr['id'])
                  self._configure_l3p_for_multiple_subnetpools(context,
                      l3p_db, ip_version=family,
                      address_scope_id=l3p_db[address_scope_key])
                  removed = list(set(l3p_orig[subnetpools_key]) -
                                 set(l3p_curr[subnetpools_key]))
                  for sp_id in removed:
                      if sp_id in self._get_in_use_subnetpools_for_l3p(context):
                          raise exc.IncorrectSubnetpoolUpdate(
                              subnetpool_id=sp_id, l3p_id=l3p_curr['id'])
                      # If an implicitly created subnetpool is being
                      # disassocaited we try to delete it
                      self._cleanup_subnetpool(context._plugin_context, sp_id)
  
      def _delete_l3p_subnetpools(self, context):
  
          subpools = []
          for sp_key in self.L3P_SUBNETPOOLS_KEYS.values():
              subpools += context.current[sp_key]
          for sp_id in subpools:
              self._cleanup_subnetpool(context._plugin_context, sp_id)
  
          for ascp_key in self.L3P_ADDRESS_SCOPE_KEYS.values():
              if context.current[ascp_key]:
                  self._cleanup_address_scope(context._plugin_context,
                                              context.current[ascp_key])
  
  
  class ResourceMappingDriver(api.PolicyDriver, ImplicitResourceOperations,
                              OwnedResourcesOperations):
      """Resource Mapping driver for Group Policy plugin.
  
      This driver implements group policy semantics by mapping group
      policy resources to various other neutron resources.
      """
  
      @log.log_method_call
      def initialize(self):
          self._cached_agent_notifier = None
          self._resource_owner_tenant_id = None
  
      @property
      def gbp_plugin(self):
          if not self._gbp_plugin:
              self._gbp_plugin = directory.get_plugin("GROUP_POLICY")
          return self._gbp_plugin
  
      def _reject_shared(self, object, type):
          if object.get('shared'):
              raise exc.InvalidSharedResource(type=type,
                                              driver='resource_mapping')
  
      def _reject_cross_tenant_ptg_l2p(self, context):
          if context.current['l2_policy_id']:
              l2p = context._plugin.get_l2_policy(
                  context._plugin_context, context.current['l2_policy_id'])
              if l2p['tenant_id'] != context.current['tenant_id']:
                  raise (
                      exc.
                      CrossTenantPolicyTargetGroupL2PolicyNotSupported())
  
      def _reject_cross_tenant_l2p_l3p(self, context):
          if context.current['tenant_id'] == self.resource_owner_tenant_id:
              # Relax cross tenancy condition when current tenant id is admin.
              # Relaxing when l2policy tenant id is of admin, to address the
              # case for proxy group where l2policy belongs to admin tenant
              # but l3policy belongs to user tenant.
              return
          # Can't create non shared L2p on a shared L3p
          if context.current['l3_policy_id']:
              l3p = context._plugin.get_l3_policy(
                  context._plugin_context,
                  context.current['l3_policy_id'])
              if l3p['tenant_id'] != context.current['tenant_id']:
                  raise exc.CrossTenantL2PolicyL3PolicyNotSupported()
  
      def _associate_qosp_to_pt(self, context):
          ptg_id = context.current['policy_target_group_id']
          ptg = context._plugin.get_policy_target_group(
              context._plugin_context, ptg_id)
          network_service_policy_id = ptg.get(
              "network_service_policy_id")
          if not network_service_policy_id:
              return
  
          nsp = context._plugin.get_network_service_policy(
              context._plugin_context, network_service_policy_id)
          nsp_params = nsp.get("network_service_params")
          # Check if at least a QoS NSP p. is defined (a QoS policy was created)
          for nsp_parameter in nsp_params:
              if nsp_parameter["type"] in (
                      gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX,
                      gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST):
  
                  # get QoS Policy associated to NSP
                  mapping = self._get_nsp_qos_mapping(
                      context._plugin_context.session,
                      network_service_policy_id)
  
                  # apply QoS policy to PT's Neutron port
                  port_id = context.current['port_id']
                  port = {attributes.PORT:
                          {'qos_policy_id': mapping['qos_policy_id']}}
                  self._core_plugin.update_port(context._plugin_context,
                                                port_id, port)
                  break
  
      def _disassociate_qosp_from_pt(self, context, pt_id):
          try:
              policy_target = context._plugin.get_policy_target(
                  context._plugin_context, pt_id)
          except gp_ext.PolicyTargetNotFound:
              LOG.warning("Attempted to fetch deleted Service Target (QoS)")
          else:
              port_id = policy_target['port_id']
              port = {attributes.PORT: {'qos_policy_id': None}}
              self._core_plugin.update_port(context._plugin_context,
                                            port_id, port)
  
      def _cleanup_network_service_policy(self, context, ptg,
                                          ipaddress=None, fip_maps=None):
          super(ResourceMappingDriver, self)._cleanup_network_service_policy(
              context, ptg, ipaddress, fip_maps)
          for pt in ptg['policy_targets']:
              self._disassociate_qosp_from_pt(context, pt)
  
      def _handle_network_service_policy(self, context):
          network_service_policy_id = context.current.get(
              "network_service_policy_id")
          if not network_service_policy_id:
              return
          super(ResourceMappingDriver, self)._handle_network_service_policy(
              context)
          nsp = context._plugin.get_network_service_policy(
              context._plugin_context, network_service_policy_id)
          nsp_params = nsp.get("network_service_params")
  
          for nsp_parameter in nsp_params:
              if nsp_parameter["type"] in (
                      gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX,
                      gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST):
  
                  # get PTs/ports
                  policy_targets = context.current['policy_targets']
                  policy_targets = context._plugin.get_policy_targets(
                      context._plugin_context, filters={'id': policy_targets})
                  # get QoS Policy associated to NSP
                  mapping = self._get_nsp_qos_mapping(
                      context._plugin_context.session,
                      nsp['id'])
                  # apply QoS policy to each PT's Neutron port
                  for pt in policy_targets:
                      port_id = pt['port_id']
                      port = {attributes.PORT:
                              {'qos_policy_id': mapping['qos_policy_id']}}
                      self._core_plugin.update_port(context._plugin_context,
                                                    port_id, port)
  
      @property
      def resource_owner_tenant_id(self):
          if not self._resource_owner_tenant_id:
              self._resource_owner_tenant_id = (
                  self._get_resource_owner_tenant_id())
          return self._resource_owner_tenant_id
  
      def _get_resource_owner_tenant_id(self):
          # Returns service tenant id, which specified in neutron conf
          try:
              user, pwd, tenant, auth_url = utils.get_keystone_creds()
              keystoneclient = k_client.Client(username=user, password=pwd,
                                               auth_url=auth_url)
              tenant = keystoneclient.tenants.find(name=tenant)
              return tenant.id
          except k_exceptions.NotFound:
              with excutils.save_and_reraise_exception(reraise=True):
                  LOG.error('No tenant with name %s exists.', tenant)
          except k_exceptions.NoUniqueMatch:
              with excutils.save_and_reraise_exception(reraise=True):
                  LOG.error('Multiple tenants matches found for %s', tenant)
          except k_exceptions.AuthorizationFailure:
              LOG.error("User: %(user)s dont have permissions",
                       {'user': user})
          except k_exceptions.Unauthorized:
              LOG.error("Wrong credentials provided: user: %(user)s, "
                        "password: %(pwd)s, tenant: %(tenant)s",
                       {'user': user, 'pwd': pwd, 'tenant': tenant})
  
      @log.log_method_call
      def create_policy_target_precommit(self, context):
          self._check_create_policy_target(context)
  
      def _check_create_policy_target(self, context, verify_port_subnet=True):
          self._validate_cluster_id(context)
          if not context.current['policy_target_group_id']:
              raise exc.PolicyTargetRequiresPolicyTargetGroup()
          if context.current['port_id'] and verify_port_subnet:
              # Validate if explicit port's subnet
              # is same as the subnet of PTG.
              self._validate_pt_port_subnets(context)
          group_id = context.current['policy_target_group_id']
          if context.current.get('proxy_gateway'):
              pts = context._plugin.get_policy_targets(
                  context._plugin_context, {'policy_target_group_id': group_id,
                                            'proxy_gateway': True})
              pts = [x['id'] for x in pts if x['id'] != context.current['id']]
              if pts:
                  exc.OnlyOneProxyGatewayAllowed(group_id=group_id)
          if context.current.get('group_default_gateway'):
              pts = context._plugin.get_policy_targets(
                  context._plugin_context, {'policy_target_group_id': group_id,
                                            'group_default_gateway': True})
              pts = [x['id'] for x in pts if x['id'] != context.current['id']]
              if pts:
                  exc.OnlyOneGroupDefaultGatewayAllowed(group_id=group_id)
  
      @log.log_method_call
      def create_policy_target_postcommit(self, context):
          if not context.current['port_id']:
              self._use_implicit_port(context)
          self._update_cluster_membership(
              context, new_cluster_id=context.current['cluster_id'])
          self._assoc_ptg_sg_to_pt(context, context.current['id'],
                                   context.current['policy_target_group_id'])
          self._associate_fip_to_pt(context)
          self._associate_qosp_to_pt(context)
          if context.current.get('proxy_gateway'):
              self._set_proxy_gateway_routes(context, context.current)
  
      @log.log_method_call
      def update_policy_target_precommit(self, context):
          self._validate_cluster_id(context)
          if (context.current['policy_target_group_id'] !=
              context.original['policy_target_group_id']):
              raise exc.PolicyTargetGroupUpdateOfPolicyTargetNotSupported()
  
      @log.log_method_call
      def update_policy_target_postcommit(self, context):
          if context.current['cluster_id'] != context.original['cluster_id']:
              self._update_cluster_membership(
                  context, new_cluster_id=context.current['cluster_id'],
                  old_cluster_id=context.original['cluster_id'])
          if not context.current.get('port_id') and context.current.get(
                  'proxy_gateway'):
              self._unset_proxy_gateway_routes(context, context.original)
  
      @log.log_method_call
      def delete_policy_target_precommit(self, context):
          self._validate_pt_in_use_by_cluster(context)
          context.fips = self._get_pt_floating_ip_mapping(
                      context._plugin_context.session,
                      context.current['id'])
  
      @log.log_method_call
      def delete_policy_target_postcommit(self, context):
          sg_list = self._generate_list_of_sg_from_ptg(
              context, context.current['policy_target_group_id'])
          self._disassoc_sgs_from_port(context._plugin_context,
                                       context.current['port_id'], sg_list)
          port_id = context.current['port_id']
          for fip in context.fips:
              self._delete_fip(context._plugin_context,
                               fip.floatingip_id)
          if context.current.get('proxy_gateway'):
              self._unset_proxy_gateway_routes(context, context.current)
          self._cleanup_port(context._plugin_context, port_id)
  
      @log.log_method_call
      def create_policy_target_group_precommit(self, context):
          self._reject_cross_tenant_ptg_l2p(context)
          self._validate_ptg_subnets(context)
          self._validate_nat_pool_for_nsp(context)
          self._validate_proxy_ptg(context)
  
      @log.log_method_call
      def create_policy_target_group_postcommit(self, context):
          # REVISIT(ivar) this validates the PTG L2P after the IPD creates it
          # (which happens in the postcommit phase)
          self._validate_proxy_ptg(context)
  
          # connect router to subnets of the PTG
          l2p_id = context.current['l2_policy_id']
          l2p = context._plugin.get_l2_policy(context._plugin_context,
                                              l2p_id)
          l3p_id = l2p['l3_policy_id']
          l3p = context._plugin.get_l3_policy(context._plugin_context,
                                              l3p_id)
  
          if not context.current['subnets']:
              is_proxy = bool(context.current.get('proxied_group_id'))
              if (not MAPPING_CFG.use_subnetpools or
                 (is_proxy and
                  context.current.get('proxy_type') == proxy_ext.PROXY_TYPE_L2)):
                  self._use_implicit_subnet(context, is_proxy=is_proxy)
              else:
                  try:
                      subnet_specifics = {}
                      if context.current.get('proxied_group_id'):
                          # Since this is proxy group, we need to allocate
                          # subnet with proxy-specific prefix len
                          subnet_specifics = {
                              'prefixlen': l3p['proxy_subnet_prefix_length']}
  
                      subnets = self._use_implicit_subnet_from_subnetpool(
                          context, subnet_specifics)
                      context.add_subnets([sub['id'] for sub in subnets])
                  except neutron_exc.SubnetAllocationError:
                      # Translate to GBP exception
                      raise exc.NoSubnetAvailable()
  
          self._stitch_ptg_to_l3p(context, context.current, l3p,
                                  context.current['subnets'])
  
          self._handle_network_service_policy(context)
          self._handle_policy_rule_sets(context)
          self._update_default_security_group(context._plugin_context,
                                              context.current['id'],
                                              context.current['tenant_id'],
                                              context.current['subnets'])
  
      @log.log_method_call
      def update_policy_target_group_precommit(self, context):
          if set(context.original['subnets']) - set(context.current['subnets']):
              raise exc.PolicyTargetGroupSubnetRemovalNotSupported()
  
          self._validate_ptg_subnets(context, context.current['subnets'])
          self._reject_cross_tenant_ptg_l2p(context)
          if (context.current['network_service_policy_id'] !=
              context.original['network_service_policy_id']):
              self._validate_nat_pool_for_nsp(context)
  
      @log.log_method_call
      def update_policy_target_group_postcommit(self, context):
          # Three conditions where SG association needs to be changed
          # (a) list of policy_targets change
          # (b) provided_policy_rule_sets change
          # (c) consumed_policy_rule_sets change
          ptg_id = context.current['id']
          new_policy_targets = list(
              set(context.current['policy_targets']) - set(
                  context.original['policy_targets']))
          if new_policy_targets:
              self._update_sgs_on_pt_with_ptg(context, ptg_id,
                                              new_policy_targets, "ASSOCIATE")
          removed_policy_targets = list(
              set(context.original['policy_targets']) - set(
                  context.current['policy_targets']))
          if removed_policy_targets:
              self._update_sgs_on_pt_with_ptg(context, ptg_id,
                                              new_policy_targets, "DISASSOCIATE")
          # generate a list of policy_rule_sets (SGs) to update on the PTG
          orig_provided_policy_rule_sets = context.original[
              'provided_policy_rule_sets']
          curr_provided_policy_rule_sets = context.current[
              'provided_policy_rule_sets']
          new_provided_policy_rule_sets = list(
              set(curr_provided_policy_rule_sets) - set(
                  orig_provided_policy_rule_sets))
          orig_consumed_policy_rule_sets = context.original[
              'consumed_policy_rule_sets']
          curr_consumed_policy_rule_sets = context.current[
              'consumed_policy_rule_sets']
          new_consumed_policy_rule_sets = list(
              set(curr_consumed_policy_rule_sets) - set(
                  orig_consumed_policy_rule_sets))
  
          self._handle_nsp_update_on_ptg(context)
  
          # if PTG associated policy_rule_sets are updated, we need to update
          # the policy rules, then assoicate SGs to ports
          if new_provided_policy_rule_sets or new_consumed_policy_rule_sets:
              subnets = context.current['subnets']
              self._set_sg_rules_for_subnets(context, subnets,
                                    new_provided_policy_rule_sets,
                                    new_consumed_policy_rule_sets)
              self._update_sgs_on_ptg(context, ptg_id,
                                      new_provided_policy_rule_sets,
                                      new_consumed_policy_rule_sets, "ASSOCIATE")
          # generate the list of contracts (SGs) to remove from current ports
          removed_provided_prs = list(set(orig_provided_policy_rule_sets) -
                                      set(curr_provided_policy_rule_sets))
          removed_consumed_prs = list(set(orig_consumed_policy_rule_sets) -
                                      set(curr_consumed_policy_rule_sets))
          if removed_provided_prs or removed_consumed_prs:
              self._update_sgs_on_ptg(context, ptg_id,
                                      removed_provided_prs,
                                      removed_consumed_prs, "DISASSOCIATE")
              subnets = context.original['subnets']
              self._unset_sg_rules_for_subnets(
                  context, subnets, removed_provided_prs, removed_consumed_prs)
          # Deal with new added subnets for default SG
          # Subnet removal not possible for now
          new_subnets = list(set(context.current['subnets']) -
                             set(context.original['subnets']))
          self._update_default_security_group(
              context._plugin_context, context.current['id'],
              context.current['tenant_id'], subnets=new_subnets)
  
      @log.log_method_call
      def delete_policy_target_group_precommit(self, context):
          context.nsp_cleanup_ipaddress = self._get_ptg_policy_ipaddress_mapping(
              context._plugin_context.session, context.current['id'])
          context.nsp_cleanup_fips = self._get_ptg_policy_fip_mapping(
              context._plugin_context.session, context.current['id'])
  
      @log.log_method_call
      def delete_policy_target_group_postcommit(self, context):
          try:
              self._cleanup_network_service_policy(context,
                                                   context.current,
                                                   context.nsp_cleanup_ipaddress,
                                                   context.nsp_cleanup_fips)
          except sa_exc.ObjectDeletedError as err:
              LOG.warning("Object already got deleted. Error: %(err)s",
                      {'err': err})
          # Cleanup SGs
          self._unset_sg_rules_for_subnets(
              context, context.current['subnets'],
              context.current['provided_policy_rule_sets'],
              context.current['consumed_policy_rule_sets'])
  
          l2p_id = context.current['l2_policy_id']
          l3p = None
          if l2p_id:
              l3p = self._get_l3p_for_l2policy(context, l2p_id)
              for subnet_id in context.current['subnets']:
                  self._cleanup_subnet(context._plugin_context, subnet_id,
                                       l3p['routers'][0])
          self._delete_default_security_group(
              context._plugin_context, context.current['id'],
              context.current['tenant_id'])
          if context.current.get('proxied_group_id') and l3p:
              # Attach the Router interfaces to the proxied group
              # Note that proxy PTGs are always deleted starting from the last
              # one in the list.
              proxied = context._plugin.get_policy_target_group(
                  context._plugin_context.elevated(),
                  context.current['proxied_group_id'])
  
              self._stitch_ptg_to_l3p(context, proxied, l3p, proxied['subnets'])
  
      @log.log_method_call
      def create_l2_policy_precommit(self, context):
          self._reject_cross_tenant_l2p_l3p(context)
          self._reject_non_shared_net_on_shared_l2p(context)
          self._reject_invalid_network_access(context)
          if not context.current['inject_default_route']:
              raise exc.UnsettingInjectDefaultRouteOfL2PolicyNotSupported()
  
      @log.log_method_call
      def create_l2_policy_postcommit(self, context):
          if not context.current['network_id']:
              self._use_implicit_network(context)
  
      @log.log_method_call
      def update_l2_policy_precommit(self, context):
          if (context.current['inject_default_route'] !=
              context.original['inject_default_route']):
              raise exc.UnsettingInjectDefaultRouteOfL2PolicyNotSupported()
          if (context.current['l3_policy_id'] !=
              context.original['l3_policy_id']):
              raise exc.L3PolicyUpdateOfL2PolicyNotSupported()
          self._reject_cross_tenant_l2p_l3p(context)
          self._reject_non_shared_net_on_shared_l2p(context)
  
      @log.log_method_call
      def update_l2_policy_postcommit(self, context):
          pass
  
      @log.log_method_call
      def delete_l2_policy_precommit(self, context):
          pass
  
      @log.log_method_call
      def delete_l2_policy_postcommit(self, context):
          network_id = context.current['network_id']
          self._cleanup_network(context._plugin_context, network_id)
  
      @log.log_method_call
      def create_l3_policy_precommit(self, context):
          curr = context.current
          if len(curr['routers']) > 1:
              raise exc.L3PolicyMultipleRoutersNotSupported()
          # Validate non overlapping IPs in the same tenant
          l3ps = context._plugin.get_l3_policies(
              context._plugin_context, {'tenant_id': [curr['tenant_id']]})
          subnets = []
          for l3p in l3ps:
              if l3p['id'] != curr['id']:
                  for prefix in gbp_utils.convert_ip_pool_string_to_list(
                          l3p['ip_pool']):
                      if prefix:
                          subnets.append(prefix)
                  if 'proxy_ip_pool' in l3p:
                      subnets.extend(gbp_utils.convert_ip_pool_string_to_list(
                          l3p['proxy_ip_pool']))
          l3p_subnets = gbp_utils.convert_ip_pool_string_to_list(curr['ip_pool'])
          if 'proxy_ip_pool' in curr:
              l3p_subnets.extend(gbp_utils.convert_ip_pool_string_to_list(
                  curr['proxy_ip_pool']))
  
          current_set = netaddr.IPSet(subnets)
          l3p_set = netaddr.IPSet(l3p_subnets)
  
          if l3p_set & current_set:
              raise exc.OverlappingIPPoolsInSameTenantNotAllowed(
                  ip_pool=l3p_subnets, overlapping_pools=subnets)
          # In Neutron, one external gateway per router is allowed. Therefore
          # we have to limit the number of ES per L3P to 1
          if len(context.current['external_segments']) > 1:
              raise exc.MultipleESPerL3PolicyNotSupported()
          self._reject_invalid_router_access(context)
  
      @log.log_method_call
      def create_l3_policy_postcommit(self, context):
  
          if MAPPING_CFG.use_subnetpools:
              self._create_l3p_subnetpools(context)
  
          l3p = context.current
          if not l3p['routers']:
              self._use_implicit_router(context)
          if l3p['external_segments']:
              self._plug_router_to_external_segment(
                  context, l3p['external_segments'])
              self._set_l3p_external_routes(context)
          if not MAPPING_CFG.use_subnetpools:
              self._process_new_l3p_ip_pool(context, context.current['ip_pool'])
  
      @log.log_method_call
      def update_l3_policy_precommit(self, context):
          if context.current['routers'] != context.original['routers']:
              raise exc.L3PolicyRoutersUpdateNotSupported()
          if len(context.current['external_segments']) > 1:
              raise exc.MultipleESPerL3PolicyNotSupported()
  
          if MAPPING_CFG.use_subnetpools:
              self._update_l3p_subnetpools(context)
  
          # Currently there is no support for router update in l3p update.
          # Added this check just in case it is supported in future.
          self._reject_invalid_router_access(context)
          self._validate_in_use_by_nsp(context)
  
      @log.log_method_call
      def update_l3_policy_postcommit(self, context):
          new, old = context.current, context.original
          if new['external_segments'] != old['external_segments']:
              added = (set(new['external_segments'].keys()) -
                       set(old['external_segments'].keys()))
              removed = (set(old['external_segments'].keys()) -
                         set(new['external_segments'].keys()))
              if context.current['routers']:
                  if removed:
                      self._unplug_router_from_external_segment(
                          context, dict((x, old['external_segments'][x])
                                        for x in removed))
                  if added:
                      self._plug_router_to_external_segment(
                          context, dict((x, new['external_segments'][x])
                                        for x in added))
                  self._set_l3p_external_routes(context, removed=removed)
  
      @log.log_method_call
      def delete_l3_policy_precommit(self, context):
          pass
  
      @log.log_method_call
      def delete_l3_policy_postcommit(self, context):
          for router_id in context.current['routers']:
              self._cleanup_router(context._plugin_context, router_id)
  
          if MAPPING_CFG.use_subnetpools:
              self._delete_l3p_subnetpools(context)
          else:
              self._process_remove_l3p_ip_pool(context,
                                               context.current['ip_pool'])
  
      @log.log_method_call
      def create_policy_classifier_precommit(self, context):
          pass
  
      @log.log_method_call
      def create_policy_classifier_postcommit(self, context):
          pass
  
      @log.log_method_call
      def update_policy_classifier_precommit(self, context):
          pass
  
      @log.log_method_call
      def update_policy_classifier_postcommit(self, context):
          policy_rules = (context._plugin.get_policy_classifier(
                  context._plugin_context,
                  context.current['id'])['policy_rules'])
          policy_rules = context._plugin.get_policy_rules(
              context._plugin_context,
              filters={'id': policy_rules})
          policy_rulesets_to_update = []
          for policy_rule in policy_rules:
              pr_id = policy_rule['id']
              pr_sets = context._plugin._get_policy_rule_policy_rule_sets(
                  context._plugin_context, pr_id)
              policy_rulesets_to_update.extend(pr_sets)
              self._update_policy_rule_sg_rules(context, pr_sets,
                  policy_rule, context.original, context.current)
  
      @log.log_method_call
      def delete_policy_classifier_precommit(self, context):
          pass
  
      @log.log_method_call
      def delete_policy_classifier_postcommit(self, context):
          pass
  
      @log.log_method_call
      def create_policy_action_precommit(self, context):
          pass
  
      @log.log_method_call
      def create_policy_action_postcommit(self, context):
          pass
  
      @log.log_method_call
      def update_policy_action_precommit(self, context):
          pass
  
      @log.log_method_call
      def update_policy_action_postcommit(self, context):
          pass
  
      @log.log_method_call
      def delete_policy_action_precommit(self, context):
          pass
  
      @log.log_method_call
      def delete_policy_action_postcommit(self, context):
          pass
  
      @log.log_method_call
      def create_policy_rule_precommit(self, context):
          pass
  
      @log.log_method_call
      def create_policy_rule_postcommit(self, context):
          pass
  
      @log.log_method_call
      def update_policy_rule_precommit(self, context):
          pass
  
      @log.log_method_call
      def update_policy_rule_postcommit(self, context):
          old_classifier_id = context.original['policy_classifier_id']
          new_classifier_id = context.current['policy_classifier_id']
          old_action_set = set(context.current['policy_actions'])
          new_action_set = set(context.original['policy_actions'])
          if (old_classifier_id != new_classifier_id or
                  old_action_set != new_action_set):
              policy_rule_sets = (
                  context._plugin._get_policy_rule_policy_rule_sets(
                      context._plugin_context, context.current['id']))
              for prs in context._plugin.get_policy_rule_sets(
                      context._plugin_context, filters={'id': policy_rule_sets}):
                  self._remove_policy_rule_set_rules(context, prs,
                                                     [context.original])
                  self._apply_policy_rule_set_rules(context, prs,
                                                    [context.current])
  
      @log.log_method_call
      def delete_policy_rule_precommit(self, context):
          # REVISIT(ivar): This will be removed once navigability issue is
          # solved (bug/1384397)
          context._rmd_policy_rule_sets_temp = (
              context._plugin._get_policy_rule_policy_rule_sets(
                  context._plugin_context, context.current['id']))
  
      @log.log_method_call
      def delete_policy_rule_postcommit(self, context):
          for prs in context._plugin.get_policy_rule_sets(
                  context._plugin_context,
                  filters={'id': context.current['policy_rule_sets']}):
              self._remove_policy_rule_set_rules(context, prs, [context.current])
  
      @log.log_method_call
      def create_policy_rule_set_precommit(self, context):
          self._reject_shared(context.current, 'policy_rule_set')
  
      @log.log_method_call
      def create_policy_rule_set_postcommit(self, context):
          # creating SGs
          policy_rule_set_id = context.current['id']
          consumed_sg = self._create_policy_rule_set_sg(context, 'consumed')
          provided_sg = self._create_policy_rule_set_sg(context, 'provided')
          consumed_sg_id = consumed_sg['id']
          provided_sg_id = provided_sg['id']
          self._set_policy_rule_set_sg_mapping(
              context._plugin_context.session, policy_rule_set_id,
              consumed_sg_id, provided_sg_id)
          rules = context._plugin.get_policy_rules(
              context._plugin_context,
              {'id': context.current['policy_rules']})
          self._apply_policy_rule_set_rules(context, context.current, rules)
          if context.current['child_policy_rule_sets']:
              self._recompute_policy_rule_sets(
                  context, context.current['child_policy_rule_sets'])
  
      @log.log_method_call
      def update_policy_rule_set_precommit(self, context):
          self._reject_shared(context.current, 'policy_rule_set')
  
      @log.log_method_call
      def update_policy_rule_set_postcommit(self, context):
          # Update policy_rule_set rules
          old_rules = set(context.original['policy_rules'])
          new_rules = set(context.current['policy_rules'])
          to_add = context._plugin.get_policy_rules(
              context._plugin_context, {'id': new_rules - old_rules})
          to_remove = context._plugin.get_policy_rules(
              context._plugin_context, {'id': old_rules - new_rules})
          self._remove_policy_rule_set_rules(context, context.current, to_remove)
          self._apply_policy_rule_set_rules(context, context.current, to_add)
          # Update children contraint
          to_recompute = (set(context.original['child_policy_rule_sets']) ^
                          set(context.current['child_policy_rule_sets']))
          self._recompute_policy_rule_sets(context, to_recompute)
          if to_add or to_remove:
              to_recompute = (set(context.original['child_policy_rule_sets']) &
                              set(context.current['child_policy_rule_sets']))
              self._recompute_policy_rule_sets(context, to_recompute)
  
      @log.log_method_call
      def delete_policy_rule_set_precommit(self, context):
          mapping = self._get_policy_rule_set_sg_mapping(
              context._plugin_context.session, context.current['id'])
          context._rmd_sg_list_temp = [mapping['provided_sg_id'],
                                       mapping['consumed_sg_id']]
  
      @log.log_method_call
      def delete_policy_rule_set_postcommit(self, context):
          # Disassociate SGs
          sg_list = context._rmd_sg_list_temp
          ptg_mapping = [context.current['providing_policy_target_groups'],
                         context.current['consuming_policy_target_groups']]
          for ptgs in ptg_mapping:
              for ptg in ptgs:
                  policy_target_list = ptg['policy_targets']
                  for pt_id in policy_target_list:
                      self._disassoc_sgs_from_pt(context, pt_id, sg_list)
          # Delete SGs
          for sg in sg_list:
              self._delete_sg(context._plugin_context, sg)
  
      @log.log_method_call
      def create_network_service_policy_precommit(self, context):
          self._validate_nsp_parameters(context)
  
      @log.log_method_call
      def create_network_service_policy_postcommit(self, context):
          p = context.current['network_service_params']
          max = burst = 0
          setting_qos = False
          # assumes single value per parameter type, as the API currently states
          params = {p[n]['type']: p[n]['value'] for n in range(len(p))}
          # check for QoS param types..
          if gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX in params:
              max = params[gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX]
              setting_qos = True
          if gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST in params:
              burst = params[gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST]
              setting_qos = True
          # ..and create needed Neutron resources
          if setting_qos:
              qos_policy_id = self._create_implicit_qos_policy(context)
              nsp_id = context.current['id']
              self._create_implicit_qos_rule(context, qos_policy_id, max, burst)
              self._set_nsp_qos_mapping(context._plugin_context.session,
                                        nsp_id,
                                        qos_policy_id)
  
      @log.log_method_call
      def delete_network_service_policy_precommit(self, context):
          nsp = context.current
          mapping = self._get_nsp_qos_mapping(context._plugin_context.session,
                                              nsp['id'])
          if mapping:
              qos_policy_id = mapping['qos_policy_id']
              context.current['qos_policy_id'] = qos_policy_id
  
      @log.log_method_call
      def delete_network_service_policy_postcommit(self, context):
          qos_policy_id = context.current.get('qos_policy_id')
          if qos_policy_id:
              self._delete_ptg_qos_policy(context, qos_policy_id)
  
      def create_external_segment_precommit(self, context):
          if context.current['subnet_id']:
              subnet = self._get_subnet(context._plugin_context,
                                        context.current['subnet_id'])
              network = self._get_network(context._plugin_context,
                                          subnet['network_id'])
              if not network['router:external']:
                  raise exc.InvalidSubnetForES(sub_id=subnet['id'],
                                               net_id=network['id'])
              db_es = context._plugin._get_external_segment(
                  context._plugin_context, context.current['id'])
              db_es.cidr = subnet['cidr']
              db_es.ip_version = subnet['ip_version']
              context.current['cidr'] = db_es.cidr
              context.current['ip_version'] = db_es.ip_version
          else:
              raise exc.ImplicitSubnetNotSupported()
  
      def create_external_segment_postcommit(self, context):
          pass
  
      def update_external_segment_precommit(self, context):
          invalid = ['port_address_translation']
          for attr in invalid:
              if context.current[attr] != context.original[attr]:
                  raise exc.InvalidAttributeUpdateForES(attribute=attr)
  
      def update_external_segment_postcommit(self, context):
          # REVISIT(ivar): concurrency issues
          if (context.current['external_routes'] !=
                  context.original['external_routes']):
              # Update SG rules for each EP
              # Get all the EP using this ES
              admin_context = n_context.get_admin_context()
              ep_ids = context._plugin._get_external_segment_external_policies(
                  context._plugin_context, context.current['id'])
              eps = context._plugin.get_external_policies(
                  admin_context, {'id': ep_ids})
              eps_by_tenant = {}
              for ep in eps:
                  if ep['tenant_id'] not in eps_by_tenant:
                      eps_by_tenant[ep['tenant_id']] = []
                  eps_by_tenant[ep['tenant_id']].append(ep['id'])
              # Process their routes
              visited_tenants = set()
              for l3p in context._plugin.get_l3_policies(
                      admin_context, {'id': context.current['l3_policies']}):
                  if l3p['tenant_id'] in visited_tenants:
                      continue
                  visited_tenants.add(l3p['tenant_id'])
                  old_cidrs = [x['destination']
                               for x in context.original['external_routes']]
                  old_cidrs = self._process_external_cidrs(
                      context, old_cidrs, tenant_id=l3p['tenant_id'])
                  new_cidrs = [x['destination']
                               for x in context.current['external_routes']]
                  new_cidrs = self._process_external_cidrs(
                      context, new_cidrs, tenant_id=l3p['tenant_id'])
                  # Recompute PRS rules
                  self._recompute_external_policy_rules(
                      context, eps_by_tenant[l3p['tenant_id']],
                      new_cidrs, old_cidrs)
              old_routes = set((x['destination'], x['nexthop'])
                               for x in context.original['external_routes'])
              new_routes = set((x['destination'], x['nexthop'])
                               for x in context.current['external_routes'])
              # Set the correct list of routes for each L3P
              self._recompute_l3_policy_routes(context, new_routes, old_routes)
  
      def delete_external_segment_precommit(self, context):
          pass
  
      def delete_external_segment_postcommit(self, context):
          pass
  
      def create_external_policy_precommit(self, context):
          self._reject_shared(context.current, 'external_policy')
          # REVISIT(ivar): For security reasons, only one ES allowed per EP.
          # see bug #1398156
          if len(context.current['external_segments']) > 1:
              raise exc.MultipleESPerEPNotSupported()
          # REVISIT(ivar): bug #1398156 only one EP is allowed per tenant
          ep_number = context._plugin.get_external_policies_count(
              context._plugin_context,
              filters={'tenant_id': [context.current['tenant_id']]})
          if ep_number > 1:
              raise exc.OnlyOneEPPerTenantAllowed()
  
      def create_external_policy_postcommit(self, context):
          # Only *North to South* rules are actually effective.
          # The rules will be calculated as the symmetric difference between
          # the union of all the Tenant's L3P supernets and the union of all the
          # ES routes.
          # REVISIT(ivar): Remove when ES update is supported for EP
          if not context.current['external_segments']:
              raise exc.ESIdRequiredWhenCreatingEP()
          ep = context.current
          if ep['external_segments']:
              if (ep['provided_policy_rule_sets'] or
                      ep['consumed_policy_rule_sets']):
                  # Get the full processed list of external CIDRs
                  cidr_list = self._get_processed_ep_cidr_list(context, ep)
                  # set the rules on the proper SGs
                  self._set_sg_rules_for_cidrs(
                      context, cidr_list, ep['provided_policy_rule_sets'],
                      ep['consumed_policy_rule_sets'])
  
      def update_external_policy_precommit(self, context):
          self._reject_shared(context.current, 'external_policy')
          if context.original['external_segments']:
              if (set(context.current['external_segments']) !=
                      set(context.original['external_segments'])):
                  raise exc.ESUpdateNotSupportedForEP()
  
      def update_external_policy_postcommit(self, context):
          # REVISIT(ivar): Concurrency issue, the cidr_list could be different
          # in the time from adding new PRS to removing old ones. The consequence
          # is that the rules added/removed could be completely wrong.
          prov_cons = {'provided_policy_rule_sets': [],
                       'consumed_policy_rule_sets': []}
          cidr_list = None
          # Removed PRS
          for attr in prov_cons:
              orig_policy_rule_sets = context.original[attr]
              curr_policy_rule_sets = context.current[attr]
              prov_cons[attr] = list(set(orig_policy_rule_sets) -
                                     set(curr_policy_rule_sets))
          if any(prov_cons.values()):
              cidr_list = self._get_processed_ep_cidr_list(
                  context, context.current)
              self._unset_sg_rules_for_cidrs(
                  context, cidr_list, prov_cons['provided_policy_rule_sets'],
                  prov_cons['consumed_policy_rule_sets'])
  
          # Added PRS
          for attr in prov_cons:
              orig_policy_rule_sets = context.original[attr]
              curr_policy_rule_sets = context.current[attr]
              prov_cons[attr] = list(set(curr_policy_rule_sets) -
                                     set(orig_policy_rule_sets))
  
          if any(prov_cons.values()):
              cidr_list = cidr_list or self._get_processed_ep_cidr_list(
                  context, context.current)
              self._set_sg_rules_for_cidrs(
                  context, cidr_list, prov_cons['provided_policy_rule_sets'],
                  prov_cons['consumed_policy_rule_sets'])
  
      def delete_external_policy_precommit(self, context):
          pass
  
      def delete_external_policy_postcommit(self, context):
          if (context.current['provided_policy_rule_sets'] or
                  context.current['consumed_policy_rule_sets']):
              # REVISIT(ivar): concurrency issue, ES may not exist anymore
              cidr_list = self._get_processed_ep_cidr_list(
                  context, context.current)
              self._unset_sg_rules_for_cidrs(
                  context, cidr_list,
                  context.current['provided_policy_rule_sets'],
                  context.current['consumed_policy_rule_sets'])
  
      @log.log_method_call
      def create_network_service_policy_precommit(self, context):
          self._validate_nsp_parameters(context)
  
      def update_network_service_policy_precommit(self, context):
          self._validate_nsp_parameters(context)
  
      def create_nat_pool_precommit(self, context):
          self._add_nat_pool_to_segment(context)
  
      def create_nat_pool_postcommit(self, context):
          self._add_implicit_subnet_for_nat_pool_create(context)
  
      def update_nat_pool_precommit(self, context):
          self._process_ext_segment_update_for_nat_pool(context)
  
      def update_nat_pool_postcommit(self, context):
          self._add_implicit_subnet_for_nat_pool_update(context)
  
      def delete_nat_pool_precommit(self, context):
          self._nat_pool_in_use(context)
  
      def delete_nat_pool_postcommit(self, context):
          self._delete_subnet_on_nat_pool_delete(context)
  
      def _plug_router_to_external_segment(self, context, es_dict):
          es_list = context._plugin.get_external_segments(
              context._plugin_context, filters={'id': es_dict.keys()})
          if context.current['routers']:
              router_id = context.current['routers'][0]
              for es in es_list:
                  router = self._create_router_gw_for_external_segment(
                      context._plugin_context, es, es_dict, router_id)
  
                  if not es_dict[es['id']] or not es_dict[es['id']][0]:
                      # Update L3P assigned address
                      efi = router['external_gateway_info']['external_fixed_ips']
                      assigned_ips = [x['ip_address'] for x in efi
                                      if x['subnet_id'] == es['subnet_id']]
                      context.set_external_fixed_ips(es['id'], assigned_ips)
  
      def _unplug_router_from_external_segment(self, context, es_ids):
          es_list = context._plugin.get_external_segments(
              context._plugin_context, filters={'id': es_ids})
          if context.current['routers']:
              router_id = context.current['routers'][0]
              for es in es_list:
                  subnet = self._get_subnet(context._plugin_context,
                                            es['subnet_id'])
                  interface_info = {'network_id': subnet['network_id']}
                  self._remove_router_gw_interface(context._plugin_context,
                                                   router_id, interface_info)
  
      def _stitch_ptg_to_l3p(self, context, ptg, l3p, subnet_ids):
          if l3p['routers']:
              router_id = l3p['routers'][0]
              if ptg.get('proxied_group_id'):
                  self._stitch_proxy_ptg_to_l3p(context, ptg, l3p, subnet_ids)
              else:
                  try:
                      for subnet_id in subnet_ids:
                          self._plug_router_to_subnet(
                              context._plugin_context, subnet_id, router_id)
                  except n_exc.InvalidInput:
                      # This exception is not expected.
                      LOG.exception("adding subnet to router failed")
                      for subnet_id in subnet_ids:
                          self._delete_subnet(context._plugin_context, subnet_id)
                      raise exc.GroupPolicyInternalError()
  
      def _stitch_proxy_ptg_to_l3p(self, context, ptg, l3p, subnet_ids):
          """Attach the Proxy PTG properly.
          When a proxy PTG is set, the proxied PTG needs to be detached from
          the current L3P. The proxied PTG will be attached instead on the proper
          subnets. This will completely isolate the proxied PTG, therefore the
          expectation is for a third entity (eg. service chain driver) to create
          a bridging service across the proxy and the proxied PTG.
          This will guarantee that all the traffic goes through the proxy PTG
          before reaching the destination.
          """
  
          proxied = context._plugin.get_policy_target_group(
              context._plugin_context, ptg['proxied_group_id'])
          try:
              # If the detached PTG is a proxy itself and has a proxy
              # gateway, then the routes should be removed from the L3P and
              # added to the current proxy subnet instead.
              gateway_pt = None
              if proxied.get('proxied_group_id'):
                  # Verify if a gateway PT exists
                  gateway_pt = context._plugin.get_policy_targets(
                      context._plugin_context.elevated(),
                      {'policy_target_group_id': [proxied['id']],
                       'proxy_gateway': [True]})
                  if gateway_pt:
                      self._unset_proxy_gateway_routes(context, gateway_pt[0])
  
              # Detach Proxied PTG
              for subnet_id in proxied['subnets']:
                  self._remove_router_interface(
                      context._plugin_context, l3p['routers'][0],
                      {'subnet_id': subnet_id})
  
              # Attach Proxy PTG
              for subnet_id in subnet_ids:
                  self._plug_router_to_subnet(
                      context._plugin_context, subnet_id, l3p['routers'][0])
  
              # Reset the proxy gateway PT routes
              if gateway_pt:
                  self._set_proxy_gateway_routes(context, gateway_pt[0])
          except n_exc.InvalidInput:
              # This exception is not expected.
              # TODO(ivar): find a better way to rollback
              LOG.exception("adding subnet to router failed")
              for subnet_id in subnet_ids:
                  self._delete_subnet(context._plugin_context, subnet_id)
                  raise exc.GroupPolicyInternalError()
  
      def _create_policy_rule_set_sg(self, context, sg_name_prefix):
          return self._create_gbp_sg(
              context._plugin_context, context.current['tenant_id'],
              sg_name_prefix + '_' + context.current['name'])
  
      def _handle_policy_rule_sets(self, context):
          # This method handles policy_rule_set => SG mapping
          # context is PTG context
  
          # for all consumed policy_rule_sets, simply associate
          # each EP's port from the PTG
          # rules are expected to be filled out already
          consumed_policy_rule_sets = context.current[
              'consumed_policy_rule_sets']
          provided_policy_rule_sets = context.current[
              'provided_policy_rule_sets']
          subnets = context.current['subnets']
          ptg_id = context.current['id']
          self._set_sg_rules_for_subnets(context, subnets,
                                         provided_policy_rule_sets,
                                         consumed_policy_rule_sets)
          self._update_sgs_on_ptg(context, ptg_id, provided_policy_rule_sets,
                                  consumed_policy_rule_sets, "ASSOCIATE")
  
      # updates sg rules corresponding to a policy rule
      def _update_policy_rule_sg_rules(self, context, policy_rule_sets,
                                       policy_rule, old_classifier=None,
                                       new_classifier=None):
          policy_rule_set_list = context._plugin.get_policy_rule_sets(
                  context._plugin_context, filters={'id': policy_rule_sets})
          for policy_rule_set in policy_rule_set_list:
              filtered_rules = self._get_enforced_prs_rules(
                  context, policy_rule_set, subset=[policy_rule['id']])
              if filtered_rules:
                  policy_rule_set_sg_mappings = (
                      self._get_policy_rule_set_sg_mapping(
                          context._plugin_context.session,
                          policy_rule_set['id']))
                  cidr_mapping = self._get_cidrs_mapping(
                      context, policy_rule_set)
                  self._add_or_remove_policy_rule_set_rule(
                      context, policy_rule, policy_rule_set_sg_mappings,
                      cidr_mapping, unset=True, unset_egress=True,
                      classifier=old_classifier)
                  self._add_or_remove_policy_rule_set_rule(
                      context, policy_rule, policy_rule_set_sg_mappings,
                      cidr_mapping, classifier=new_classifier)
  
      def _get_rule_ids_for_actions(self, context, action_id):
          policy_rule_qry = context.session.query(
                              gpdb.PolicyRuleActionAssociation.policy_rule_id)
          policy_rule_qry.filter_by(policy_action_id=action_id)
          return policy_rule_qry.all()
  
      def _set_policy_rule_set_sg_mapping(
          self, session, policy_rule_set_id, consumed_sg_id, provided_sg_id):
          with session.begin(subtransactions=True):
              mapping = PolicyRuleSetSGsMapping(
                  policy_rule_set_id=policy_rule_set_id,
                  consumed_sg_id=consumed_sg_id, provided_sg_id=provided_sg_id)
              session.add(mapping)
  
      @staticmethod
      def _get_policy_rule_set_sg_mapping(session, policy_rule_set_id):
          with session.begin(subtransactions=True):
              return (session.query(PolicyRuleSetSGsMapping).
                      filter_by(policy_rule_set_id=policy_rule_set_id).one())
  
      def _sg_ingress_rule(self, context, sg_id, protocol, port_range, cidr,
                           tenant_id, unset=False):
          return self._sg_rule(
              context._plugin_context, tenant_id, sg_id,
              'ingress', protocol, port_range, cidr, unset=unset)
  
      def _sg_egress_rule(self, context, sg_id, protocol, port_range,
                          cidr, tenant_id, unset=False):
          return self._sg_rule(
              context._plugin_context, tenant_id, sg_id,
              'egress', protocol, port_range, cidr, unset=unset)
  
      def _assoc_sgs_to_pt(self, context, pt_id, sg_list):
          try:
              pt = context._plugin.get_policy_target(context._plugin_context,
                                                     pt_id)
          except gp_ext.PolicyTargetNotFound:
              LOG.warning("PT %s doesn't exist anymore", pt_id)
              return
          try:
              port_id = pt['port_id']
              port = self._get_port(context._plugin_context, port_id)
              if ('port_security_enabled' in port and
                      not port['port_security_enabled']):
                  LOG.debug("Port security disabled for port %s ", port_id)
                  return
              cur_sg_list = port[ext_sg.SECURITYGROUPS]
              new_sg_list = cur_sg_list + sg_list
              port[ext_sg.SECURITYGROUPS] = new_sg_list
              self._update_port(context._plugin_context, port_id, port)
          except n_exc.PortNotFound:
              LOG.warning("Port %s is missing", port_id)
  
      def _disassoc_sgs_from_pt(self, context, pt_id, sg_list):
          try:
              pt = context._plugin.get_policy_target(context._plugin_context,
                                                     pt_id)
          except gp_ext.PolicyTargetNotFound:
              LOG.warning("PT %s doesn't exist anymore", pt_id)
              return
          port_id = pt['port_id']
          self._disassoc_sgs_from_port(context._plugin_context, port_id, sg_list)
  
      def _disassoc_sgs_from_port(self, plugin_context, port_id, sg_list):
          try:
              port = self._get_port(plugin_context, port_id)
              if ('port_security_enabled' in port and
                      not port['port_security_enabled']):
                  LOG.debug("Port security disabled for port %s ", port_id)
                  return
              cur_sg_list = port[ext_sg.SECURITYGROUPS]
              new_sg_list = list(set(cur_sg_list) - set(sg_list))
              port[ext_sg.SECURITYGROUPS] = new_sg_list
              self._update_port(plugin_context, port_id, port)
          except n_exc.PortNotFound:
              LOG.warning("Port %s is missing", port_id)
  
      def _generate_list_of_sg_from_ptg(self, context, ptg_id):
          ptg = context._plugin.get_policy_target_group(
              context._plugin_context, ptg_id)
          provided_policy_rule_sets = ptg['provided_policy_rule_sets']
          consumed_policy_rule_sets = ptg['consumed_policy_rule_sets']
          return(self._generate_list_sg_from_policy_rule_set_list(
              context, provided_policy_rule_sets, consumed_policy_rule_sets))
  
      def _generate_list_sg_from_policy_rule_set_list(self, context,
                                                      provided_policy_rule_sets,
                                                      consumed_policy_rule_sets):
          ret_list = []
          for policy_rule_set_id in provided_policy_rule_sets:
              policy_rule_set_sg_mappings = self._get_policy_rule_set_sg_mapping(
                  context._plugin_context.session, policy_rule_set_id)
              provided_sg_id = policy_rule_set_sg_mappings['provided_sg_id']
              ret_list.append(provided_sg_id)
  
          for policy_rule_set_id in consumed_policy_rule_sets:
              policy_rule_set_sg_mappings = self._get_policy_rule_set_sg_mapping(
                  context._plugin_context.session, policy_rule_set_id)
              consumed_sg_id = policy_rule_set_sg_mappings['consumed_sg_id']
              ret_list.append(consumed_sg_id)
          return ret_list
  
      def _assoc_ptg_sg_to_pt(self, context, pt_id, ptg_id):
          sg_list = self._generate_list_of_sg_from_ptg(context, ptg_id)
          self._assoc_sgs_to_pt(context, pt_id, sg_list)
  
      def _update_sgs_on_pt_with_ptg(self, context, ptg_id, new_pt_list, op):
          sg_list = self._generate_list_of_sg_from_ptg(context, ptg_id)
          for pt_id in new_pt_list:
              if op == "ASSOCIATE":
                  self._assoc_sgs_to_pt(context, pt_id, sg_list)
              else:
                  self._disassoc_sgs_from_pt(context, pt_id, sg_list)
  
      def _update_sgs_on_ptg(self, context, ptg_id, provided_policy_rule_sets,
                             consumed_policy_rule_sets, op):
          sg_list = self._generate_list_sg_from_policy_rule_set_list(
              context, provided_policy_rule_sets, consumed_policy_rule_sets)
          ptg = context._plugin.get_policy_target_group(
              context._plugin_context, ptg_id)
          policy_target_list = ptg['policy_targets']
          for pt_id in policy_target_list:
              if op == "ASSOCIATE":
                  self._assoc_sgs_to_pt(context, pt_id, sg_list)
              else:
                  self._disassoc_sgs_from_pt(context, pt_id, sg_list)
  
      def _set_or_unset_rules_for_subnets(
              self, context, subnets, provided_policy_rule_sets,
              consumed_policy_rule_sets, unset=False):
          if not provided_policy_rule_sets and not consumed_policy_rule_sets:
              return
  
          cidr_list = []
          for subnet_id in subnets:
              subnet = self._get_subnet(context._plugin_context, subnet_id)
              cidr = subnet['cidr']
              cidr_list.append(cidr)
          self._set_or_unset_rules_for_cidrs(
              context, cidr_list, provided_policy_rule_sets,
              consumed_policy_rule_sets, unset=unset)
  
      # context should be PTG
      def _set_sg_rules_for_subnets(
              self, context, subnets, provided_policy_rule_sets,
              consumed_policy_rule_sets):
          self._set_or_unset_rules_for_subnets(
              context, subnets, provided_policy_rule_sets,
              consumed_policy_rule_sets)
  
      def _unset_sg_rules_for_subnets(
              self, context, subnets, provided_policy_rule_sets,
              consumed_policy_rule_sets):
          self._set_or_unset_rules_for_subnets(
              context, subnets, provided_policy_rule_sets,
              consumed_policy_rule_sets, unset=True)
  
      def _set_sg_rules_for_cidrs(self, context, cidr_list,
                                  provided_policy_rule_sets,
                                  consumed_policy_rule_sets):
          self._set_or_unset_rules_for_cidrs(
              context, cidr_list, provided_policy_rule_sets,
              consumed_policy_rule_sets)
  
      def _unset_sg_rules_for_cidrs(self, context, cidr_list,
                                    provided_policy_rule_sets,
                                    consumed_policy_rule_sets):
          self._set_or_unset_rules_for_cidrs(
              context, cidr_list, provided_policy_rule_sets,
              consumed_policy_rule_sets, unset=True)
  
      def _set_or_unset_rules_for_cidrs(self, context, cidr_list,
                                        provided_policy_rule_sets,
                                        consumed_policy_rule_sets, unset=False):
          prov_cons = ['providing_cidrs', 'consuming_cidrs']
          for pos, policy_rule_sets in enumerate(
                  [provided_policy_rule_sets, consumed_policy_rule_sets]):
              for policy_rule_set_id in policy_rule_sets:
                  policy_rule_set = context._plugin.get_policy_rule_set(
                      context._plugin_context, policy_rule_set_id)
                  policy_rule_set_sg_mappings = (
                      self._get_policy_rule_set_sg_mapping(
                          context._plugin_context.session, policy_rule_set_id))
                  cidr_mapping = {prov_cons[pos]: cidr_list,
                                  prov_cons[pos - 1]: []}
                  if not unset:
                      policy_rules = self._get_enforced_prs_rules(
                          context, policy_rule_set)
                  else:
                      # Not need to filter when removing rules
                      policy_rules = context._plugin.get_policy_rules(
                          context._plugin_context,
                          {'id': policy_rule_set['policy_rules']})
                  for policy_rule in policy_rules:
                      self._add_or_remove_policy_rule_set_rule(
                          context, policy_rule, policy_rule_set_sg_mappings,
                          cidr_mapping, unset=unset)
  
      def _manage_policy_rule_set_rules(self, context, policy_rule_set,
                                        policy_rules, unset=False,
                                        unset_egress=False):
          policy_rule_set_sg_mappings = self._get_policy_rule_set_sg_mapping(
              context._plugin_context.session, policy_rule_set['id'])
          policy_rule_set = context._plugin.get_policy_rule_set(
              context._plugin_context, policy_rule_set['id'])
          cidr_mapping = self._get_cidrs_mapping(context, policy_rule_set)
          for policy_rule in policy_rules:
              self._add_or_remove_policy_rule_set_rule(
                  context, policy_rule, policy_rule_set_sg_mappings,
                  cidr_mapping, unset=unset, unset_egress=unset_egress)
  
      def _add_or_remove_policy_rule_set_rule(self, context, policy_rule,
                                              policy_rule_set_sg_mappings,
                                              cidr_mapping, unset=False,
                                              unset_egress=False,
                                              classifier=None):
          in_out = [gconst.GP_DIRECTION_IN, gconst.GP_DIRECTION_OUT]
          prov_cons = [policy_rule_set_sg_mappings['provided_sg_id'],
                       policy_rule_set_sg_mappings['consumed_sg_id']]
          cidr_prov_cons = [cidr_mapping['providing_cidrs'],
                            cidr_mapping['consuming_cidrs']]
  
          if not classifier:
              classifier_id = policy_rule['policy_classifier_id']
              classifier = context._plugin.get_policy_classifier(
                  context._plugin_context, classifier_id)
  
          protocol = classifier['protocol']
          port_range = classifier['port_range']
          admin_context = n_context.get_admin_context()
          prs = context._plugin.get_policy_rule_set(
              admin_context, policy_rule_set_sg_mappings.policy_rule_set_id)
          tenant_id = prs['tenant_id']
          for pos, sg in enumerate(prov_cons):
              if classifier['direction'] in [gconst.GP_DIRECTION_BI,
                                             in_out[pos]]:
                  for cidr in cidr_prov_cons[pos - 1]:
                      self._sg_ingress_rule(context, sg, protocol, port_range,
                                            cidr, tenant_id, unset=unset)
              if classifier['direction'] in [gconst.GP_DIRECTION_BI,
                                             in_out[pos - 1]]:
                  for cidr in cidr_prov_cons[pos - 1]:
                      self._sg_egress_rule(context, sg, protocol, port_range,
                                           cidr, tenant_id,
                                           unset=unset or unset_egress)
  
      def _apply_policy_rule_set_rules(self, context, policy_rule_set,
                                       policy_rules):
          policy_rules = self._get_enforced_prs_rules(
              context, policy_rule_set, subset=[x['id'] for x in policy_rules])
          # Don't add rules unallowed by the parent
          self._manage_policy_rule_set_rules(
              context, policy_rule_set, policy_rules)
  
      def _remove_policy_rule_set_rules(self, context, policy_rule_set,
                                        policy_rules):
          self._manage_policy_rule_set_rules(
              context, policy_rule_set, policy_rules, unset=True,
              unset_egress=True)
  
      def _recompute_policy_rule_sets(self, context, children):
          # Rules in child but not in parent shall be removed
          # Child rules will be set after being filtered by the parent
          for child in children:
              child = context._plugin.get_policy_rule_set(
                  context._plugin_context, child)
              child_rule_ids = set(child['policy_rules'])
              if child['parent_id']:
                  parent = context._plugin.get_policy_rule_set(
                      context._plugin_context, child['parent_id'])
                  parent_policy_rules = context._plugin.get_policy_rules(
                                          context._plugin_context,
                                          filters={'id': parent['policy_rules']})
                  child_rules = context._plugin.get_policy_rules(
                                          context._plugin_context,
                                          filters={'id': child['policy_rules']})
                  parent_classifier_ids = [x['policy_classifier_id']
                                       for x in parent_policy_rules]
                  delta_rules = [x['id'] for x in child_rules
                                 if x['policy_classifier_id']
                                 not in set(parent_classifier_ids)]
                  delta_rules = context._plugin.get_policy_rules(
                                  context._plugin_context, {'id': delta_rules})
                  self._remove_policy_rule_set_rules(context, child, delta_rules)
              # Old parent may have filtered some rules, need to add them again
              child_rules = context._plugin.get_policy_rules(
                  context._plugin_context, filters={'id': child_rule_ids})
              self._apply_policy_rule_set_rules(context, child, child_rules)
  
      def _update_default_security_group(self, plugin_context, ptg_id,
                                         tenant_id, subnets=None):
  
          sg_id = self._get_default_security_group(plugin_context, ptg_id,
                                                   tenant_id)
          ip_v = {4: n_const.IPv4, 6: n_const.IPv6}
          if not sg_id:
              sg_name = DEFAULT_SG_PREFIX % ptg_id
              sg = self._create_gbp_sg(plugin_context, tenant_id, sg_name,
                                       description='default GBP security group')
              sg_id = sg['id']
  
          for subnet in self._get_subnets(
                  plugin_context, filters={'id': subnets or []}):
              self._sg_rule(plugin_context, tenant_id, sg_id,
                            'ingress', cidr=subnet['cidr'],
                            ethertype=ip_v[subnet['ip_version']])
              self._sg_rule(plugin_context, tenant_id, sg_id,
                            'egress', cidr=subnet['cidr'],
                            ethertype=ip_v[subnet['ip_version']])
  
          # The following rules are added for access to the link local
          # network (metadata server in most cases), and to the DNS
          # port.
          # TODO(Sumit): The following can be optimized by creating
          # the rules once and then referrig to them in every default
          # SG that gets created. If we do that, then when we delete the
          # default SG we cannot delete all the rules in it.
          # We can also consider reading these rules from a config which
          # would make it more flexible to add any rules if required.
          self._sg_rule(plugin_context, tenant_id, sg_id, 'egress',
                        cidr='169.254.0.0/16', ethertype=ip_v[4])
          for ether_type in ip_v:
              for proto in [n_const.PROTO_NAME_TCP, n_const.PROTO_NAME_UDP]:
                  self._sg_rule(plugin_context, tenant_id, sg_id, 'egress',
                                protocol=proto, port_range='53',
                                ethertype=ip_v[ether_type])
  
          return sg_id
  
      def _delete_default_security_group(self, plugin_context, ptg_id,
                                         tenant_id):
          sg_id = self._get_default_security_group(plugin_context, ptg_id,
                                                   tenant_id)
          if sg_id:
              self._delete_sg(plugin_context, sg_id)
  
      def _get_ep_cidrs(self, context, eps):
          cidrs = []
          eps = context._plugin.get_external_policies(
              context._plugin_context, filters={'id': eps})
          for ep in eps:
              cidrs.extend(self._get_processed_ep_cidr_list(context, ep))
          return cidrs
  
      def _get_cidrs_mapping(self, context, policy_rule_set):
          providing_eps = policy_rule_set['providing_external_policies']
          consuming_eps = policy_rule_set['consuming_external_policies']
          providing_ptgs = policy_rule_set['providing_policy_target_groups']
          consuming_ptgs = policy_rule_set['consuming_policy_target_groups']
          return {
              'providing_cidrs': self._get_ptg_cidrs(
                  context, providing_ptgs) + self._get_ep_cidrs(context,
                                                                providing_eps),
              'consuming_cidrs': self._get_ptg_cidrs(
                  context, consuming_ptgs) + self._get_ep_cidrs(context,
                                                                consuming_eps)}
  
      def _get_ep_cidr_list(self, context, ep):
          es_list = context._plugin.get_external_segments(
              context._plugin_context,
              filters={'id': ep['external_segments']})
          cidr_list = []
          for es in es_list:
              cidr_list += [x['destination'] for x in es['external_routes']]
          return cidr_list
  
      def _process_external_cidrs(self, context, cidrs, exclude=None,
                                  tenant_id=None):
          # Get all the tenant's L3P
          exclude = exclude or []
          admin_context = n_context.get_admin_context()
          l3ps = context._plugin.get_l3_policies(
              admin_context,
              filters={'tenant_id': [tenant_id or context.current['tenant_id']]})
  
          ip_pool_list = []
          for l3p in l3ps:
              for prefix in gbp_utils.convert_ip_pool_string_to_list(
                      l3p['ip_pool']):
                  if prefix not in exclude:
                      ip_pool_list.append(prefix)
          l3p_set = netaddr.IPSet(ip_pool_list)
          return [str(x) for x in (netaddr.IPSet(cidrs) - l3p_set).iter_cidrs()]
  
      def _get_processed_ep_cidr_list(self, context, ep):
          cidr_list = self._get_ep_cidr_list(context, ep)
          return self._process_external_cidrs(context, cidr_list)
  
      def _recompute_external_policy_rules(self, context, ep_ids, new_cidrs,
                                           old_cidrs):
          # the EPs could belong to different tenants, need admin context
          admin_context = n_context.get_admin_context()
          ep_list = context._plugin.get_external_policies(admin_context,
                                                          filters={'id': ep_ids})
          for ep in ep_list:
              self._refresh_ep_cidrs_rules(context, ep, new_cidrs, old_cidrs)
  
      def _recompute_l3_policy_routes(self, context, new_routes, old_routes):
          # the L3Ps could belong to different tenants, need admin context
          admin_context = n_context.get_admin_context()
          added_routes = new_routes - old_routes
          removed_routes = old_routes - new_routes
          l3ps = context._plugin.get_l3_policies(
              admin_context, filters={'id': context.current['l3_policies']})
          for l3p in l3ps:
              self._update_l3p_routes(l3p, add=added_routes,
                                      remove=removed_routes)
  
      def _refresh_ep_cidrs_rules(self, context, ep, new_cidrs, old_cidrs):
          # REVISIT(ivar): calculate cidrs delta to minimize disruption
          # Unset old rules
          self._unset_sg_rules_for_cidrs(
              context, old_cidrs, ep['provided_policy_rule_sets'],
              ep['consumed_policy_rule_sets'])
          # Set new rules
          self._set_sg_rules_for_cidrs(
              context, new_cidrs, ep['provided_policy_rule_sets'],
              ep['consumed_policy_rule_sets'])
  
      def _process_new_l3p_ip_pool(self, context, ip_pool):
          # Get all the EP for this tenant
          ep_list = context._plugin.get_external_policies(
              context._plugin_context,
              filters={'tenant_id': context.current['tenant_id']})
          for ep in ep_list:
              # Remove rules before the new ip_pool came
              ip_pool_list = gbp_utils.convert_ip_pool_string_to_list(ip_pool)
              cidr_list = self._get_ep_cidr_list(context, ep)
              old_cidrs = self._process_external_cidrs(context, cidr_list,
                                                       exclude=ip_pool_list)
              new_cidrs = [str(x) for x in
                           (netaddr.IPSet(old_cidrs) -
                            netaddr.IPSet(ip_pool_list)).iter_cidrs()]
              self._refresh_ep_cidrs_rules(context, ep, new_cidrs, old_cidrs)
  
      def _process_remove_l3p_ip_pool(self, context, ip_pool):
          # Get all the EP for this tenant
          ep_list = context._plugin.get_external_policies(
              context._plugin_context,
              filters={'tenant_id': context.current['tenant_id']})
          for ep in ep_list:
              # Cidrs before the ip_pool removal
              ip_pool_list = gbp_utils.convert_ip_pool_string_to_list(ip_pool)
              cidr_list = self._get_ep_cidr_list(context, ep)
              new_cidrs = self._process_external_cidrs(context, cidr_list,
                                                       exclude=ip_pool_list)
              # Cidrs after the ip_pool removal
              old_cidrs = [str(x) for x in
                           (netaddr.IPSet(new_cidrs) |
                            netaddr.IPSet(ip_pool_list)).iter_cidrs()]
              self._refresh_ep_cidrs_rules(context, ep, new_cidrs, old_cidrs)
  
      def _set_l3p_external_routes(self, context, added=None, removed=None):
  
          def _routes_from_es_ids(context, es_ids):
              routes = []
              if es_ids:
                  es_list = context._plugin.get_external_segments(
                      context._plugin_context, filters={'id': es_ids})
                  for es in es_list:
                      routes += es['external_routes']
              return routes
  
          add = _routes_from_es_ids(
              context, added or context.current['external_segments'].keys())
          remove = _routes_from_es_ids(context, removed)
  
          self._update_l3p_routes(
              context.current,
              add=set((x['destination'], x['nexthop']) for x in add),
              remove=set((x['destination'], x['nexthop']) for x in remove))
  
      def _update_l3p_routes(self, l3p, add=None, remove=None):
          add = add or set()
          remove = remove or set()
          # NOTE(ivar): the context needs to be admin because the external
          # gateway port is created by Neutron without any tenant_id! Which makes
          # it visible only from an admin context.
          admin_context = n_context.get_admin_context()
          routers = self._get_routers(admin_context, {'id': l3p['routers']})
          for router in routers:
              current_routes = set((x['destination'], x['nexthop']) for x in
                                   router['routes'])
              current_routes = (current_routes - remove | add)
              current_routes = [{'destination': x[0], 'nexthop': x[1]} for x
                                in current_routes if x[1]]
              self._update_router(admin_context, router['id'],
                                  {'routes': current_routes})
  
      def _update_ptg_routes(self, ptg, add=None, remove=None):
          add = add or set()
          remove = remove or set()
          admin_context = n_context.get_admin_context()
          subnets = self._get_subnets(admin_context, {'id': ptg['subnets']})
          for subnet in subnets:
              current_routes = set((x['destination'], x['nexthop']) for x in
                                   subnet['host_routes'])
              current_routes = (current_routes - remove | add)
              current_routes = [{'destination': x[0], 'nexthop': x[1]} for x
                                in current_routes if x[1]]
              self._update_subnet(admin_context, subnet['id'],
                                  {'host_routes': current_routes})
  
      def _validate_ptg_subnets(self, context, subnets=None):
          if subnets or context.current['subnets']:
              l2p_id = context.current['l2_policy_id']
              l2p = context._plugin.get_l2_policy(context._plugin_context,
                                                  l2p_id)
              # Validate explicit subnet belongs to L2P's network
              network_id = l2p['network_id']
              network = self._get_network(context._plugin_context, network_id)
              for subnet_id in subnets or context.current['subnets']:
                  if subnet_id not in network['subnets']:
                      raise exc.InvalidSubnetForPTG(subnet_id=subnet_id,
                                                    network_id=network_id,
                                                    l2p_id=l2p['id'],
                                                    ptg_id=context.current['id'])
  
      def _get_enforced_prs_rules(self, context, prs, subset=None):
          subset = subset or prs['policy_rules']
          if prs['parent_id']:
              parent = context._plugin.get_policy_rule_set(
                  context._plugin_context, prs['parent_id'])
              parent_policy_rules = context._plugin.get_policy_rules(
                  context._plugin_context,
                  filters={'id': parent['policy_rules']})
              subset_rules = context._plugin.get_policy_rules(
                                          context._plugin_context,
                                          filters={'id': subset})
              parent_classifier_ids = set(x['policy_classifier_id']
                                          for x in parent_policy_rules)
              policy_rules = [x['id'] for x in subset_rules
                              if x['policy_classifier_id']
                              in parent_classifier_ids]
              return context._plugin.get_policy_rules(
                  context._plugin_context,
                  {'id': policy_rules})
          else:
              return context._plugin.get_policy_rules(
                  context._plugin_context, {'id': set(subset)})
  
      def _validate_pt_port_subnets(self, context, subnets=None):
          # Validate if explicit port's subnet
          # is same as the subnet of PTG.
          port_id = context.current['port_id']
          port = self._get_port(context._plugin_context, port_id)
  
          port_subnet_id = None
          fixed_ips = port['fixed_ips']
          if fixed_ips:
              # TODO(krishna-sunitha): Check if there is a case when
              # there is more than one fixed_ip?
              port_subnet_id = fixed_ips[0]['subnet_id']
  
          ptg_id = context.current['policy_target_group_id']
          ptg = context._plugin.get_policy_target_group(
                                  context._plugin_context,
                                  ptg_id)
          for subnet in ptg.get('subnets') or subnets:
              if subnet == port_subnet_id:
                  break
          else:
              raise exc.InvalidPortForPTG(
                  port_id=port_id, ptg_subnet_id=",".join(ptg.get('subnets')),
                  port_subnet_id=port_subnet_id,
                  policy_target_group_id=ptg_id)
  
      def _get_ptg_l3p(self, context, ptg):
          l3p_id = context._plugin.get_l2_policy(
              context._plugin_context, ptg['l2_policy_id'])['l3_policy_id']
          return context._plugin.get_l3_policy(context._plugin_context, l3p_id)
  
      def _validate_proxy_ptg(self, context):
          # Validate that proxied PTG is in the same L3P
          current = context.current
          if current.get('proxied_group_id') and current.get('l2_policy_id'):
              l3p_curr = self._get_ptg_l3p(context, current)
  
              proxied = context._plugin.get_policy_target_group(
                  context._plugin_context, current['proxied_group_id'])
              l3p_proxied = self._get_ptg_l3p(context, proxied)
              if l3p_curr['id'] != l3p_proxied['id']:
                  raise exc.InvalidProxiedGroupL3P(
                      ptg_id=proxied['id'], l3p_id=l3p_proxied['id'])
              if (context.current['proxy_type'] == proxy_ext.PROXY_TYPE_L2 and
                      context.current['l2_policy_id'] ==
                      proxied['l2_policy_id']):
                  raise exc.InvalidProxiedGroupL2P(ptg_id=proxied['id'])
  
      def _update_proxy_gateway_routes(self, context, pt, unset=False):
          ptg = context._plugin.get_policy_target_group(
              context._plugin_context, pt['policy_target_group_id'])
          l3p = self._get_ptg_l3p(context, ptg)
          port = self._get_port(context._plugin_context, pt['port_id'])
          nexthop = None
          for fixed_ip in port['fixed_ips']:
              if fixed_ip.get('ip_address'):
                  nexthop = fixed_ip.get('ip_address')
                  break
          routes = set()
          if nexthop:
              # Add all the subnets in the chain
              curr = ptg
              while curr['proxied_group_id']:
                  proxied = context._plugin.get_policy_target_group(
                      context._plugin_context.elevated(),
                      curr['proxied_group_id'])
                  subnets = self._get_subnets(context._plugin_context,
                                              {'id': proxied['subnets']})
                  routes |= set((subnet['cidr'], nexthop) for subnet in subnets)
                  curr = proxied
  
              if unset:
                  # Remove from L3P anyways, since it could be a consequence of
                  # L3 stitching
                  self._update_l3p_routes(l3p, remove=routes)
                  # In any case, routes should be set in self proxy subnets
                  self._update_ptg_routes(ptg, remove=routes)
              else:
                  if not ptg['proxy_group_id']:
                      self._update_l3p_routes(l3p, add=routes)
                  self._update_ptg_routes(ptg, add=routes)
  
      def _set_proxy_gateway_routes(self, context, pt):
          self._update_proxy_gateway_routes(context, pt)
  
      def _unset_proxy_gateway_routes(self, context, pt):
          self._update_proxy_gateway_routes(context, pt, unset=True)
  
      def _validate_cluster_id(self, context):
          # In RMD, cluster_id can only point to a preexisting PT.
          if context.current['cluster_id']:
              try:
                  pt = self._get_policy_target(
                      context._plugin_context, context.current['cluster_id'])
                  if pt['policy_target_group_id'] != context.current[
                          'policy_target_group_id']:
                      raise exc.InvalidClusterPtg()
              except gp_ext.PolicyTargetNotFound:
                  raise exc.InvalidClusterId()
  
      def _validate_pt_in_use_by_cluster(self, context):
          # Useful for avoiding to delete a cluster master
          pts = [x for x in self._get_policy_targets(
              context._plugin_context.elevated(),
              {'cluster_id': [context.current['id']]})
                 if x['id'] != context.current['id']]
          if pts:
              raise exc.PolicyTargetInUse()
  
      def _check_allowed_address_pairs(self):
          return ("allowed-address-pairs" in
                  self._core_plugin.supported_extension_aliases)
  
      def _update_cluster_membership(self, context, new_cluster_id=None,
                                     old_cluster_id=None):
          if self._check_allowed_address_pairs():
              curr_port = self._get_port(
                      context._plugin_context, context.current['port_id'])
              curr_pairs = curr_port['allowed_address_pairs']
              if old_cluster_id:
                  # Remove allowed address
                  master_mac, master_ips = self._get_cluster_master_pairs(
                      context._plugin_context, old_cluster_id)
                  curr_pairs = [x for x in curr_port['allowed_address_pairs']
                                if not ((x['ip_address'] in master_ips) and
                                        (x['mac_address'] == master_mac))]
              if new_cluster_id:
                  master_mac, master_ips = self._get_cluster_master_pairs(
                      context._plugin_context, new_cluster_id)
                  curr_pairs += [
                      {'mac_address': master_mac,
                       'ip_address': x} for x in master_ips]
              self._update_port(context._plugin_context, curr_port['id'],
                                {'allowed_address_pairs': curr_pairs})
  
      def _get_cluster_master_pairs(self, plugin_context, cluster_id):
          master_pt = self._get_policy_target(plugin_context, cluster_id)
          master_port = self._get_port(plugin_context,
                                       master_pt['port_id'])
          master_mac = master_port['mac_address']
          master_ips = [x['ip_address'] for x in master_port['fixed_ips']]
          return master_mac, master_ips
  
      def _create_implicit_qos_policy(self, context):
          attrs = {
              'name': 'gbp_' + context.current['name'],
              'description': 'Group-Based Policy QoS policy',
              'project_id': context.current['tenant_id']}
          qos_policy = self._create_qos_policy(context._plugin_context, attrs)
          qos_policy_id = qos_policy['id']
          return qos_policy_id
  
      def _delete_ptg_qos_policy(self, context, qos_policy_id):
          qos_rules = self._get_qos_rules(context._plugin_context, qos_policy_id)
          with context._plugin_context.session.begin(subtransactions=True):
              for qos_rule in qos_rules:
                  self._delete_qos_rule(context._plugin_context,
                                        qos_rule['id'], qos_policy_id)
              self._delete_qos_policy(context._plugin_context, qos_policy_id)
  
      def _create_implicit_qos_rule(self, context, qos_policy_id, max, burst):
          attrs = {
              'max_kbps': max,
              'max_burst_kbps': burst}
          qos_rule = self._create_qos_rule(context._plugin_context,
                                           qos_policy_id, attrs)
          qos_rule_id = qos_rule['id']
          return qos_rule_id
  