#    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

from neutron import context
from neutron.db import common_db_mixin
from neutron_lib.api import validators
from neutron_lib import constants
from neutron_lib.db import model_base
from oslo_log import helpers as log
from oslo_utils import uuidutils
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.orm import exc

from gbpservice.neutron.extensions import group_policy as gpolicy
from gbpservice.neutron.services.grouppolicy.common import (
    constants as gp_constants)
from gbpservice.neutron.services.grouppolicy.common import utils


MAX_IPV4_SUBNET_PREFIX_LENGTH = 31
MAX_IPV6_SUBNET_PREFIX_LENGTH = 127
ADDRESS_NOT_SPECIFIED = ''


class BaseGbpResource(model_base.HasId, model_base.HasProject):
    name = sa.Column(sa.String(255))
    description = sa.Column(sa.String(255))
    status = sa.Column(sa.String(length=16), nullable=True)
    status_details = sa.Column(sa.String(length=4096), nullable=True)


class BaseSharedGbpResource(BaseGbpResource):
    shared = sa.Column(sa.Boolean)


class PolicyTarget(model_base.BASEV2, BaseGbpResource):
    """Lowest unit of abstraction on which a policy is applied."""
    __tablename__ = 'gp_policy_targets'
    type = sa.Column(sa.String(15))
    __mapper_args__ = {
        'polymorphic_on': type,
        'polymorphic_identity': 'base'
    }
    policy_target_group_id = sa.Column(sa.String(36),
                                       sa.ForeignKey(
                                           'gp_policy_target_groups.id'),
                                       nullable=True)
    cluster_id = sa.Column(sa.String(255))


class PTGToPRSProvidingAssociation(model_base.BASEV2):
    """Many to many providing relation between PTGs and Policy Rule Sets."""
    __tablename__ = 'gp_ptg_to_prs_providing_associations'
    policy_rule_set_id = sa.Column(sa.String(36),
                                   sa.ForeignKey('gp_policy_rule_sets.id'),
                                   primary_key=True)
    policy_target_group_id = sa.Column(sa.String(36),
                                       sa.ForeignKey(
                                           'gp_policy_target_groups.id'),
                                       primary_key=True)


class PTGToPRSConsumingAssociation(model_base.BASEV2):
    """Many to many consuming relation between PTGs and Policy Rule Sets."""
    __tablename__ = 'gp_ptg_to_prs_consuming_associations'
    policy_rule_set_id = sa.Column(sa.String(36),
                                   sa.ForeignKey('gp_policy_rule_sets.id'),
                                   primary_key=True)
    policy_target_group_id = sa.Column(sa.String(36),
                                       sa.ForeignKey(
                                           'gp_policy_target_groups.id'),
                                       primary_key=True)


class PolicyTargetGroup(model_base.BASEV2, BaseSharedGbpResource):
    """It is a collection of policy_targets."""
    __tablename__ = 'gp_policy_target_groups'
    type = sa.Column(sa.String(15))
    __mapper_args__ = {
        'polymorphic_on': type,
        'polymorphic_identity': 'base'
    }
    policy_targets = orm.relationship(PolicyTarget,
                                      backref='policy_target_group')
    application_policy_group_id = sa.Column(
        sa.String(36), sa.ForeignKey('gp_application_policy_groups.id'),
        nullable=True)
    l2_policy_id = sa.Column(sa.String(36),
                             sa.ForeignKey('gp_l2_policies.id'),
                             nullable=True)
    network_service_policy_id = sa.Column(
        sa.String(36), sa.ForeignKey('gp_network_service_policies.id'),
        nullable=True)
    provided_policy_rule_sets = orm.relationship(
        PTGToPRSProvidingAssociation,
        backref='providing_policy_target_group', cascade='all, delete-orphan')
    consumed_policy_rule_sets = orm.relationship(
        PTGToPRSConsumingAssociation,
        backref='consuming_policy_target_group', cascade='all, delete-orphan')
    service_management = sa.Column(sa.Boolean)


class ApplicationPolicyGroup(model_base.BASEV2, BaseSharedGbpResource):
    """It is a collection of policy_targets."""
    __tablename__ = 'gp_application_policy_groups'
    policy_target_groups = orm.relationship(
        PolicyTargetGroup, backref='application_policy_group')


class L2Policy(model_base.BASEV2, BaseSharedGbpResource):
    """Represents a L2 Policy for a collection of policy_target_groups."""
    __tablename__ = 'gp_l2_policies'
    type = sa.Column(sa.String(15))
    __mapper_args__ = {
        'polymorphic_on': type,
        'polymorphic_identity': 'base'
    }
    policy_target_groups = orm.relationship(PolicyTargetGroup,
                                            backref='l2_policy')
    l3_policy_id = sa.Column(sa.String(36),
                             sa.ForeignKey('gp_l3_policies.id'),
                             nullable=True)
    inject_default_route = sa.Column(sa.Boolean, default=True,
                                     server_default=sa.sql.true())


class ESToL3PAssociation(model_base.BASEV2):
    """Many to many consuming relation between ESs and L3Ps."""
    __tablename__ = 'gp_es_to_l3p_associations'
    l3_policy_id = sa.Column(sa.String(36), sa.ForeignKey('gp_l3_policies.id'),
                             primary_key=True)
    external_segment_id = sa.Column(
        sa.String(36), sa.ForeignKey('gp_external_segments.id'),
        primary_key=True)
    allocated_address = sa.Column(sa.String(64), nullable=False,
                                  primary_key=True)


class L3Policy(model_base.BASEV2, BaseSharedGbpResource):
    """Represents a L3 Policy with a non-overlapping IP address space."""
    __tablename__ = 'gp_l3_policies'
    type = sa.Column(sa.String(15))
    __mapper_args__ = {
        'polymorphic_on': type,
        'polymorphic_identity': 'base'
    }
    ip_version = sa.Column(sa.Integer, nullable=False)
    ip_pool = sa.Column(sa.String(255))
    subnet_prefix_length = sa.Column(sa.Integer)
    l2_policies = orm.relationship(L2Policy, backref='l3_policy')
    external_segments = orm.relationship(
        ESToL3PAssociation, backref='l3_policies',
        cascade='all, delete-orphan')


class NetworkServiceParam(model_base.BASEV2, model_base.HasId):
    """Represents a network service param used in a NetworkServicePolicy."""
    __tablename__ = 'gp_network_service_params'
    param_type = sa.Column(sa.String(50), nullable=False)
    param_name = sa.Column(sa.String(50), nullable=False)
    param_value = sa.Column(sa.String(50), nullable=False)
    network_service_policy_id = sa.Column(
        sa.String(36), sa.ForeignKey('gp_network_service_policies.id'),
        nullable=False)


class NetworkServicePolicy(model_base.BASEV2, BaseSharedGbpResource):
    """Represents a Network Service Policy."""
    __tablename__ = 'gp_network_service_policies'
    policy_target_groups = orm.relationship(PolicyTargetGroup,
                                            backref='network_service_policy')
    network_service_params = orm.relationship(
        NetworkServiceParam, backref='network_service_policy',
        cascade='all, delete-orphan')


class PRSToPRAssociation(model_base.BASEV2):
    """Many to many relation between Policy Rule Set and Policy rules."""
    __tablename__ = 'gp_prs_to_pr_associations'
    policy_rule_set_id = sa.Column(sa.String(36),
                                   sa.ForeignKey('gp_policy_rule_sets.id'),
                                   primary_key=True)
    policy_rule_id = sa.Column(sa.String(36),
                               sa.ForeignKey('gp_policy_rules.id'),
                               primary_key=True)


class PolicyRuleActionAssociation(model_base.BASEV2):
    """Many to many relation between PolicyRules and PolicyActions."""
    __tablename__ = 'gp_policy_rule_action_associations'
    policy_rule_id = sa.Column(sa.String(36),
                               sa.ForeignKey('gp_policy_rules.id'),
                               primary_key=True)
    policy_action_id = sa.Column(sa.String(36),
                                 sa.ForeignKey(
                                 'gp_policy_actions.id'),
                                 primary_key=True)


class PolicyRule(model_base.BASEV2, BaseSharedGbpResource):
    """Represents a Group Policy Rule."""
    __tablename__ = 'gp_policy_rules'
    enabled = sa.Column(sa.Boolean)
    policy_classifier_id = sa.Column(sa.String(36),
                                     sa.ForeignKey(
                                     'gp_policy_classifiers.id'),
                                     nullable=False)
    policy_actions = orm.relationship(PolicyRuleActionAssociation,
                                      backref='gp_policy_rules',
                                      cascade='all', lazy="joined")
    policy_rule_sets = orm.relationship(PRSToPRAssociation,
                                        backref='policy_rule', lazy="joined",
                                        cascade='all, delete-orphan')


class PolicyClassifier(model_base.BASEV2, BaseSharedGbpResource):
    """Represents a Group Policy Classifier."""
    __tablename__ = 'gp_policy_classifiers'
    protocol = sa.Column(sa.String(50), nullable=True)
    port_range_min = sa.Column(sa.Integer)
    port_range_max = sa.Column(sa.Integer)
    direction = sa.Column(sa.Enum(gp_constants.GP_DIRECTION_IN,
                                  gp_constants.GP_DIRECTION_OUT,
                                  gp_constants.GP_DIRECTION_BI,
                                  name='direction'))
    policy_rules = orm.relationship(PolicyRule,
                                    backref='gp_policy_classifiers')


class PolicyAction(model_base.BASEV2, BaseSharedGbpResource):
    """Represents a Group Policy Action."""
    __tablename__ = 'gp_policy_actions'
    action_type = sa.Column(sa.Enum(gp_constants.GP_ACTION_ALLOW,
                                    gp_constants.GP_ACTION_REDIRECT,
                                    name='action_type'))
    # Default action_value would be Null when action_type is allow
    # however, value is required if something meaningful needs to be done
    # for redirect
    action_value = sa.Column(sa.String(36), nullable=True)
    policy_rules = orm.relationship(PolicyRuleActionAssociation,
                                    cascade='all', backref='gp_policy_actions')


class EPToPRSProvidingAssociation(model_base.BASEV2):
    """Many to many providing relation between EPs and Policy Rule Sets."""
    __tablename__ = 'gp_ep_to_prs_providing_associations'
    policy_rule_set_id = sa.Column(sa.String(36),
                                   sa.ForeignKey('gp_policy_rule_sets.id'),
                                   primary_key=True)
    external_policy_id = sa.Column(
        sa.String(36), sa.ForeignKey('gp_external_policies.id'),
        primary_key=True)


class EPToPRSConsumingAssociation(model_base.BASEV2):
    """Many to many consuming relation between EPs and Policy Rule Sets."""
    __tablename__ = 'gp_ep_to_prs_consuming_associations'
    policy_rule_set_id = sa.Column(sa.String(36),
                                   sa.ForeignKey('gp_policy_rule_sets.id'),
                                   primary_key=True)
    external_policy_id = sa.Column(
        sa.String(36), sa.ForeignKey('gp_external_policies.id'),
        primary_key=True)


class PolicyRuleSet(model_base.BASEV2, model_base.HasProject):
    """It is a collection of Policy rules."""
    __tablename__ = 'gp_policy_rule_sets'
    id = sa.Column(sa.String(36), primary_key=True,
                   default=uuidutils.generate_uuid)
    name = sa.Column(sa.String(255))
    description = sa.Column(sa.String(255))
    parent_id = sa.Column(sa.String(255),
                          sa.ForeignKey('gp_policy_rule_sets.id'),
                          nullable=True)
    child_policy_rule_sets = orm.relationship(
        'PolicyRuleSet', backref=orm.backref('parent', remote_side=[id]))
    policy_rules = orm.relationship(PRSToPRAssociation,
                                    backref='policy_rule_set', lazy="joined",
                                    cascade='all, delete-orphan')
    providing_policy_target_groups = orm.relationship(
        PTGToPRSProvidingAssociation,
        backref='provided_policy_rule_set', lazy="joined", cascade='all')
    consuming_policy_target_groups = orm.relationship(
        PTGToPRSConsumingAssociation,
        backref='consumed_policy_rule_set', lazy="joined", cascade='all')
    providing_external_policies = orm.relationship(
        EPToPRSProvidingAssociation,
        backref='provided_policy_rule_set', lazy="joined", cascade='all')
    consuming_external_policies = orm.relationship(
        EPToPRSConsumingAssociation,
        backref='consumed_policy_rule_set', lazy="joined", cascade='all')
    shared = sa.Column(sa.Boolean)
    status = sa.Column(sa.String(length=16), nullable=True)
    status_details = sa.Column(sa.String(length=4096), nullable=True)


class NATPool(model_base.BASEV2, BaseSharedGbpResource):
    __tablename__ = 'gp_nat_pools'
    type = sa.Column(sa.String(15))
    __mapper_args__ = {
        'polymorphic_on': type,
        'polymorphic_identity': 'base'
    }
    ip_version = sa.Column(sa.Integer, nullable=False)
    ip_pool = sa.Column(sa.String(64), nullable=False)
    external_segment_id = sa.Column(
        sa.String(36), sa.ForeignKey('gp_external_segments.id'))


class ExternalRoute(model_base.BASEV2):
    __tablename__ = 'gp_external_routes'
    external_segment_id = sa.Column(
        sa.String(36), sa.ForeignKey('gp_external_segments.id',
                                     ondelete='CASCADE'),
        primary_key=True)
    destination = sa.Column(sa.String(64), nullable=False, primary_key=True)
    nexthop = sa.Column(sa.String(64), primary_key=True)


class EPToESAssociation(model_base.BASEV2):
    """Many to many consuming relation between ESs and EPs."""
    __tablename__ = 'gp_es_to_ep_associations'
    external_policy_id = sa.Column(
        sa.String(36), sa.ForeignKey('gp_external_policies.id'),
        primary_key=True)
    external_segment_id = sa.Column(
        sa.String(36), sa.ForeignKey('gp_external_segments.id'),
        primary_key=True)


class ExternalSegment(model_base.BASEV2, BaseSharedGbpResource):
    __tablename__ = 'gp_external_segments'
    type = sa.Column(sa.String(15))
    __mapper_args__ = {
        'polymorphic_on': type,
        'polymorphic_identity': 'base'
    }
    ip_version = sa.Column(sa.Integer, nullable=False)
    cidr = sa.Column(sa.String(64), nullable=False)
    port_address_translation = sa.Column(sa.Boolean)
    nat_pools = orm.relationship(NATPool, backref='external_segment')
    external_policies = orm.relationship(
        EPToESAssociation, backref='external_segments',
        cascade='all, delete-orphan')
    l3_policies = orm.relationship(
        ESToL3PAssociation, backref='external_segments',
        cascade='all, delete-orphan')
    external_routes = orm.relationship(
        ExternalRoute, backref='external_segment',
        cascade='all, delete-orphan')


class ExternalPolicy(model_base.BASEV2, BaseSharedGbpResource):
    __tablename__ = 'gp_external_policies'
    external_segments = orm.relationship(
        EPToESAssociation,
        backref='external_policies', cascade='all, delete-orphan')
    provided_policy_rule_sets = orm.relationship(
        EPToPRSProvidingAssociation,
        backref='providing_external_policies',
        cascade='all, delete-orphan')
    consumed_policy_rule_sets = orm.relationship(
        EPToPRSConsumingAssociation,
        backref='consuming_external_policies',
        cascade='all, delete-orphan')


class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
                          common_db_mixin.CommonDbMixin):
    """GroupPolicy plugin interface implementation using SQLAlchemy models."""

    # TODO(Sumit): native bulk support
    __native_bulk_support = False
    __native_pagination_support = True
    __native_sorting_support = True

    def __init__(self, *args, **kwargs):
        super(GroupPolicyDbPlugin, self).__init__(*args, **kwargs)

    def _find_gbp_resource(self, context, type, id, on_fail=None):
        try:
            return self._get_by_id(context, type, id)
        except exc.NoResultFound:
            if on_fail:
                raise on_fail(id=id)

    def _get_policy_target(self, context, policy_target_id):
        try:
            return self._get_by_id(context, PolicyTarget, policy_target_id)
        except exc.NoResultFound:
            raise gpolicy.PolicyTargetNotFound(
                policy_target_id=policy_target_id)

    def _get_policy_target_group(self, context, policy_target_group_id):
        try:
            return self._get_by_id(
                context, PolicyTargetGroup, policy_target_group_id)
        except exc.NoResultFound:
            raise gpolicy.PolicyTargetGroupNotFound(
                policy_target_group_id=policy_target_group_id)

    def _get_application_policy_group(self, context,
                                      application_policy_group_id):
        try:
            return self._get_by_id(
                context, ApplicationPolicyGroup, application_policy_group_id)
        except exc.NoResultFound:
            raise gpolicy.ApplicationPolicyGroupNotFound(
                application_policy_group_id=application_policy_group_id)

    def _get_l2_policy(self, context, l2_policy_id):
        try:
            return self._get_by_id(context, L2Policy, l2_policy_id)
        except exc.NoResultFound:
            raise gpolicy.L2PolicyNotFound(l2_policy_id=l2_policy_id)

    def _get_l3_policy(self, context, l3_policy_id):
        try:
            return self._get_by_id(context, L3Policy, l3_policy_id)
        except exc.NoResultFound:
            raise gpolicy.L3PolicyNotFound(l3_policy_id=l3_policy_id)

    def _get_network_service_policy(self, context, network_service_policy_id):
        try:
            return self._get_by_id(context, NetworkServicePolicy,
                                   network_service_policy_id)
        except exc.NoResultFound:
            raise gpolicy.NetworkServicePolicyNotFound(
                network_service_policy_id=network_service_policy_id)

    def _get_policy_classifier(self, context, policy_classifier_id):
        try:
            return self._get_by_id(context, PolicyClassifier,
                                   policy_classifier_id)
        except exc.NoResultFound:
            raise gpolicy.PolicyClassifierNotFound(
                policy_classifier_id=policy_classifier_id)

    def _get_policy_action(self, context, policy_action_id):
        try:
            policy_action = self._get_by_id(context, PolicyAction,
                                            policy_action_id)
        except exc.NoResultFound:
            raise gpolicy.PolicyActionNotFound(
                policy_action_id=policy_action_id)
        return policy_action

    def _get_policy_rule(self, context, policy_rule_id):
        try:
            policy_rule = self._get_by_id(context, PolicyRule,
                                          policy_rule_id)
        except exc.NoResultFound:
            raise gpolicy.PolicyRuleNotFound(
                policy_rule_id=policy_rule_id)
        return policy_rule

    def _get_policy_rule_set(self, context, policy_rule_set_id):
        try:
            policy_rule_set = self._get_by_id(
                context, PolicyRuleSet, policy_rule_set_id)
        except exc.NoResultFound:
            raise gpolicy.PolicyRuleSetNotFound(
                policy_rule_set_id=policy_rule_set_id)
        return policy_rule_set

    def _get_external_policy(self, context, external_policy_id):
        return self._find_gbp_resource(
            context, ExternalPolicy, external_policy_id,
            gpolicy.ExternalPolicyNotFound)

    def _get_external_segment(self, context, external_segment_id):
        return self._find_gbp_resource(
            context, ExternalSegment, external_segment_id,
            gpolicy.ExternalSegmentNotFound)

    def _get_nat_pool(self, context, nat_pool_id):
        return self._find_gbp_resource(
            context, NATPool, nat_pool_id,
            gpolicy.NATPoolNotFound)

    @staticmethod
    def _get_min_max_ports_from_range(port_range):
        if not port_range:
            return [None, None]
        min_port, sep, max_port = port_range.partition(":")
        if not max_port:
            max_port = min_port
        return [int(min_port), int(max_port)]

    def _get_port_range_from_min_max_ports(self, min_port, max_port):
        if not min_port:
            return None
        if min_port == max_port:
            return str(min_port)
        else:
            return '%d:%d' % (min_port, max_port)

    def _set_actions_for_rule(self, context, policy_rule_db, action_id_list):
        pr_db = policy_rule_db
        if not action_id_list:
            pr_db.policy_actions = []
            return
        with context.session.begin(subtransactions=True):
            # We will first check if the new list of actions is valid
            filters = {'id': [a_id for a_id in action_id_list]}
            actions_in_db = self._get_collection_query(context, PolicyAction,
                                                       filters=filters)
            actions_set = set(a_db['id'] for a_db in actions_in_db)
            for action_id in action_id_list:
                if action_id not in actions_set:
                    # If we find an invalid action in the list we
                    # do not perform the update
                    raise gpolicy.PolicyActionNotFound(
                        policy_action_id=action_id)
            # New list of actions is valid so we will first reset the existing
            # list and then add each action in order.
            # Note that the list could be empty in which case we interpret
            # it as clering existing rules.
            for pr_action_assoc in context.session.query(
                PolicyRuleActionAssociation).filter_by(
                    policy_rule_id=pr_db.id):
                    context.session.delete(pr_action_assoc)
            pr_db.policy_actions = []
            for action_id in action_id_list:
                assoc = PolicyRuleActionAssociation(policy_rule_id=pr_db.id,
                                                    policy_action_id=action_id)
                pr_db.policy_actions.append(assoc)

    def _validate_policy_rule_set_list(self, context,
                                       policy_rule_sets_id_list):
        with context.session.begin(subtransactions=True):
            filters = {'id': [c_id for c_id in policy_rule_sets_id_list]}
            policy_rule_sets_in_db = self._get_collection_query(
                context, PolicyRuleSet, filters=filters)
            existing_policy_rule_set_ids = set(
                c_db['id'] for c_db in policy_rule_sets_in_db)
            for policy_rule_set_id in policy_rule_sets_id_list:
                if policy_rule_set_id not in existing_policy_rule_set_ids:
                    # If we find an invalid policy_rule_set id in the list we
                    # dont process the entire list
                    raise gpolicy.PolicyRuleSetNotFound(
                        policy_rule_set_id=policy_rule_set_id)
            return policy_rule_sets_in_db

    def _set_providers_or_consumers_for_policy_target_group(
            self, context, ptg_db, policy_rule_sets_dict, provider=True):
        assoc_table = (PTGToPRSProvidingAssociation if provider else
                       PTGToPRSConsumingAssociation)
        self._set_providers_or_consumers_for_res(
            context, 'policy_target_group', ptg_db, policy_rule_sets_dict,
            assoc_table, provider=provider)

    def _set_providers_or_consumers_for_ep(
            self, context, ep_db, policy_rule_sets_dict, provider=True):
        assoc_table = (EPToPRSProvidingAssociation if provider else
                       EPToPRSConsumingAssociation)
        self._set_providers_or_consumers_for_res(
            context, 'external_policy', ep_db, policy_rule_sets_dict,
            assoc_table, provider=provider)

    def _set_providers_or_consumers_for_res(
            self, context, type, db_res, policy_rule_sets_dict, assoc_table,
            provider=True):
        # TODO(Sumit): Check that the same policy_rule_set ID does not belong
        # to provider and consumer dicts
        if not policy_rule_sets_dict:
            if provider:
                db_res.provided_policy_rule_sets = []
                return
            else:
                db_res.consumed_policy_rule_sets = []
                return
        with context.session.begin(subtransactions=True):
            policy_rule_sets_id_list = policy_rule_sets_dict.keys()
            # We will first check if the new list of policy_rule_sets is valid
            self._validate_policy_rule_set_list(
                context, policy_rule_sets_id_list)
            # New list of policy_rule_sets is valid so we will first reset the
            # existing list and then add each policy_rule_set.
            # Note that the list could be empty in which case we interpret
            # it as clering existing rules.
            if provider:
                db_res.provided_policy_rule_sets = []
            else:
                db_res.consumed_policy_rule_sets = []
            for policy_rule_set_id in policy_rule_sets_id_list:
                kwargs = {type + '_id': db_res.id,
                          'policy_rule_set_id': policy_rule_set_id}
                assoc = assoc_table(**kwargs)
                if provider:
                    db_res.provided_policy_rule_sets.append(assoc)
                else:
                    db_res.consumed_policy_rule_sets.append(assoc)

    def _set_children_for_policy_rule_set(self, context,
                                          policy_rule_set_db, child_id_list):
        if not child_id_list:
            policy_rule_set_db.child_policy_rule_sets = []
            return
        if policy_rule_set_db['parent_id']:
            # Only one hierarchy level allowed for now
            raise gpolicy.ThreeLevelPolicyRuleSetHierarchyNotSupported(
                policy_rule_set_id=policy_rule_set_db['id'])
        with context.session.begin(subtransactions=True):
            # We will first check if the new list of policy_rule_sets is valid

            policy_rule_sets_in_db = self._validate_policy_rule_set_list(
                context, child_id_list)
            for child in policy_rule_sets_in_db:
                if (child['child_policy_rule_sets'] or
                        child['id'] == policy_rule_set_db['id']):
                    # Only one level policy_rule_set relationship supported for
                    # now. No loops allowed
                    raise gpolicy.BadPolicyRuleSetRelationship(
                        parent_id=policy_rule_set_db['id'],
                        child_id=child['id'])
            # New list of child policy_rule_sets is valid so we will first
            # reset the existing list and then add each policy_rule_set.
            # Note that the list could be empty in which case we interpret
            # it as clering existing child policy_rule_sets.
            policy_rule_set_db.child_policy_rule_sets = []
            for child in policy_rule_sets_in_db:
                policy_rule_set_db.child_policy_rule_sets.append(child)

    def _set_rules_for_policy_rule_set(self, context,
                                       policy_rule_set_db, rule_id_list):
        prs_db = policy_rule_set_db
        if not rule_id_list:
            prs_db.policy_rules = []
            return
        with context.session.begin(subtransactions=True):
            # We will first check if the new list of rules is valid
            filters = {'id': [r_id for r_id in rule_id_list]}
            rules_in_db = self._get_collection_query(context, PolicyRule,
                                                     filters=filters)
            rule_ids = set(r_db['id'] for r_db in rules_in_db)
            for rule_id in rule_id_list:
                if rule_id not in rule_ids:
                    # If we find an invalid rule in the list we
                    # do not perform the update
                    raise gpolicy.PolicyRuleNotFound(policy_rule_id=rule_id)
            # New list of rules is valid so we will first reset the existing
            # list and then add each rule in order.
            # Note that the list could be empty in which case we interpret
            # it as clering existing rules.
            prs_db.policy_rules = []
            for rule_id in rule_id_list:
                prs_rule_db = PRSToPRAssociation(
                    policy_rule_id=rule_id,
                    policy_rule_set_id=prs_db.id)
                prs_db.policy_rules.append(prs_rule_db)

    def _process_policy_rule_sets_for_ptg(self, context, db_res, ptg):
        if 'provided_policy_rule_sets' in ptg:
            self._set_providers_or_consumers_for_policy_target_group(
                context, db_res, ptg['provided_policy_rule_sets'])
            del ptg['provided_policy_rule_sets']
        if 'consumed_policy_rule_sets' in ptg:
            self._set_providers_or_consumers_for_policy_target_group(
                context, db_res, ptg['consumed_policy_rule_sets'], False)
            del ptg['consumed_policy_rule_sets']
        return ptg

    def _process_policy_rule_sets_for_ep(self, context, db_res, res):
        if 'provided_policy_rule_sets' in res:
            self._set_providers_or_consumers_for_ep(
                context, db_res, res['provided_policy_rule_sets'])
            del res['provided_policy_rule_sets']
        if 'consumed_policy_rule_sets' in res:
            self._set_providers_or_consumers_for_ep(
                context, db_res, res['consumed_policy_rule_sets'], False)
            del res['consumed_policy_rule_sets']
        return res

    def _set_l3_policy_for_l2_policy(self, context, l2p_id, l3p_id):
        with context.session.begin(subtransactions=True):
            l2p_db = self._get_l2_policy(context, l2p_id)
            l2p_db.l3_policy_id = l3p_id

    def _set_l2_policy_for_policy_target_group(self, context, ptg_id, l2p_id):
        with context.session.begin(subtransactions=True):
            ptg_db = self._get_policy_target_group(context, ptg_id)
            ptg_db.l2_policy_id = l2p_id

    def _set_application_policy_group_for_policy_target_group(
        self, context, ptg_id, apg_id):
        with context.session.begin(subtransactions=True):
            ptg_db = self._get_policy_target_group(context, ptg_id)
            ptg_db.application_policy_group_id = apg_id

    def _set_network_service_policy_for_policy_target_group(
            self, context, ptg_id, nsp_id):
        with context.session.begin(subtransactions=True):
            ptg_db = self._get_policy_target_group(context, ptg_id)
            ptg_db.network_service_policy_id = nsp_id

    def _set_params_for_network_service_policy(
            self, context, network_service_policy_db, network_service_policy):
        nsp_db = network_service_policy_db
        params = network_service_policy['network_service_params']
        if not params:
            nsp_db.network_service_params = []
            return
        with context.session.begin(subtransactions=True):
            nsp_db.network_service_params = []
            for param in params:
                param_db = NetworkServiceParam(
                    param_type=param['type'],
                    param_name=param['name'],
                    param_value=param['value'])
                nsp_db.network_service_params.append(param_db)
            del network_service_policy['network_service_params']

    def _set_ess_for_ep(self, context, ep_db, es_id_list):
        if not es_id_list:
            ep_db.external_segments = []
            return
        with context.session.begin(subtransactions=True):
            filters = {'id': es_id_list}
            eps_in_db = self._get_collection_query(
                context, ExternalSegment, filters=filters)
            not_found = set(es_id_list) - set(ep['id'] for ep in eps_in_db)
            if not_found:
                raise gpolicy.ExternalSegmentNotFound(
                    id=not_found.pop())
            ep_db.external_segments = []
            for ep_id in es_id_list:
                assoc = EPToESAssociation(
                    external_policy_id=ep_db.id,
                    external_segment_id=ep_id)
                ep_db.external_segments.append(assoc)

    def _process_segment_ers(self, context, es_db, es):
        if es['external_routes'] is not constants.ATTR_NOT_SPECIFIED:
            es_db.external_routes = []
            for rt in es['external_routes']:
                target = ExternalRoute(
                    external_segment_id=es_db.id,
                    destination=rt['destination'],
                    nexthop=rt['nexthop'] or ADDRESS_NOT_SPECIFIED)

                es_db.external_routes.append(target)

    def _set_ess_for_l3p(self, context, l3p_db, es_dict):
        if es_dict is constants.ATTR_NOT_SPECIFIED:
            return
        if not es_dict:
            l3p_db.external_segments = []
            return
        with context.session.begin(subtransactions=True):
            # Validate ESs exist
            es_set = set(es_dict.keys())
            filters = {'id': es_set}
            es_in_db = self._get_collection_query(
                context, ExternalSegment, filters=filters)
            not_found = es_set - set(es['id'] for es in es_in_db)
            if not_found:
                raise gpolicy.ExternalSegmentNotFound(
                    id=not_found.pop())
            l3p_db.external_segments = []
            for es in es_in_db:
                ips = [x for x in es_dict[es['id']] if x]
                if not ips:
                    assoc = ESToL3PAssociation(
                        external_segment_id=es['id'],
                        l3_policy_id=l3p_db['id'],
                        allocated_address=ADDRESS_NOT_SPECIFIED)
                    l3p_db.external_segments.append(assoc)
                else:
                    # Create address allocation
                    existing = context.session.query(
                        ESToL3PAssociation).filter_by(
                            external_segment_id=es['id']).filter(
                                ESToL3PAssociation.allocated_address.in_(
                                    ips)).all()
                    if existing:
                        raise gpolicy.IpAddressOverlappingInExternalSegment(
                            es_id=es['id'])
                    for ip in es_dict[es['id']]:
                        assoc = ESToL3PAssociation(
                            external_segment_id=es['id'],
                            l3_policy_id=l3p_db['id'], allocated_address=ip)
                        l3p_db.external_segments.append(assoc)

    def _populate_common_fields_in_dict(self, db_ref):
        res = {'id': db_ref['id'],
               'tenant_id': db_ref['tenant_id'],
               'name': db_ref['name'],
               'description': db_ref['description'],
               'status': db_ref['status'],
               'status_details': db_ref['status_details'],
               'shared': db_ref.get('shared', False)}
        return res

    def _make_policy_target_dict(self, pt, fields=None):
        res = {'id': pt['id'],
               'tenant_id': pt['tenant_id'],
               'name': pt['name'],
               'description': pt['description'],
               'status': pt['status'],
               'status_details': pt['status_details'],
               'policy_target_group_id': pt['policy_target_group_id'],
               'cluster_id': pt['cluster_id']}
        return self._fields(res, fields)

    def _make_policy_target_group_dict(self, ptg, fields=None):
        res = self._populate_common_fields_in_dict(ptg)
        res['l2_policy_id'] = ptg['l2_policy_id']
        res['application_policy_group_id'] = ptg.get(
            'application_policy_group_id', None)
        res['network_service_policy_id'] = ptg['network_service_policy_id']
        res['service_management'] = ptg.get('service_management', False)
        res['policy_targets'] = [
            pt['id'] for pt in ptg['policy_targets']]
        res['provided_policy_rule_sets'] = (
            [pprs['policy_rule_set_id'] for pprs in ptg[
                'provided_policy_rule_sets']])
        res['consumed_policy_rule_sets'] = (
            [cprs['policy_rule_set_id'] for cprs in ptg[
                'consumed_policy_rule_sets']])
        return self._fields(res, fields)

    def _make_application_policy_group_dict(self, apg, fields=None):
        res = self._populate_common_fields_in_dict(apg)
        res['policy_target_groups'] = [
            ptg['id'] for ptg in apg['policy_target_groups']]
        return self._fields(res, fields)

    def _make_l2_policy_dict(self, l2p, fields=None):
        res = self._populate_common_fields_in_dict(l2p)
        res['l3_policy_id'] = l2p['l3_policy_id']
        res['inject_default_route'] = l2p.get('inject_default_route', True)
        res['policy_target_groups'] = [
            ptg['id'] for ptg in l2p['policy_target_groups']]
        return self._fields(res, fields)

    def _make_l3_policy_dict(self, l3p, fields=None):
        res = self._populate_common_fields_in_dict(l3p)
        res['ip_version'] = l3p['ip_version']
        res['ip_pool'] = l3p['ip_pool']
        res['subnet_prefix_length'] = l3p['subnet_prefix_length']
        res['l2_policies'] = [l2p['id']
                              for l2p in l3p['l2_policies']]
        es_dict = {}
        for es in l3p['external_segments']:
            es_id = es['external_segment_id']
            if es_id not in es_dict:
                es_dict[es_id] = []
            es_dict[es_id].append(es['allocated_address'])
        res['external_segments'] = es_dict
        return self._fields(res, fields)

    def _make_network_service_policy_dict(self, nsp, fields=None):
        res = self._populate_common_fields_in_dict(nsp)
        res['policy_target_groups'] = [
            ptg['id'] for ptg in nsp['policy_target_groups']]
        params = []
        for param in nsp['network_service_params']:
            params.append({
                gp_constants.GP_NETWORK_SVC_PARAM_TYPE: param['param_type'],
                gp_constants.GP_NETWORK_SVC_PARAM_NAME: param['param_name'],
                gp_constants.GP_NETWORK_SVC_PARAM_VALUE: param['param_value']})
        res['network_service_params'] = params
        return self._fields(res, fields)

    def _make_policy_classifier_dict(self, pc, fields=None):
        res = self._populate_common_fields_in_dict(pc)
        port_range = self._get_port_range_from_min_max_ports(
            pc['port_range_min'],
            pc['port_range_max'])
        res['protocol'] = pc['protocol']
        res['port_range'] = port_range
        res['direction'] = pc['direction']
        res['policy_rules'] = [pr['id']
                               for pr in pc['policy_rules']]
        return self._fields(res, fields)

    def _make_policy_action_dict(self, pa, fields=None):
        res = self._populate_common_fields_in_dict(pa)
        res['action_type'] = pa['action_type']
        res['action_value'] = pa['action_value']
        res['policy_rules'] = [pr['policy_rule_id'] for
                               pr in pa['policy_rules']]
        return self._fields(res, fields)

    def _make_policy_rule_dict(self, pr, fields=None):
        res = self._populate_common_fields_in_dict(pr)
        res['enabled'] = pr['enabled']
        res['policy_classifier_id'] = pr['policy_classifier_id']
        res['policy_actions'] = [pa['policy_action_id']
                                 for pa in pr['policy_actions']]
        res['policy_rule_sets'] = [prs['policy_rule_set_id'] for prs in
                                   pr['policy_rule_sets']]
        return self._fields(res, fields)

    def _make_policy_rule_set_dict(self, prs, fields=None):
        res = self._populate_common_fields_in_dict(prs)
        if prs['parent']:
            res['parent_id'] = prs['parent']['id']
        else:
            res['parent_id'] = None
        ctx = context.get_admin_context()
        if 'child_policy_rule_sets' in prs:
            # They have been updated
            res['child_policy_rule_sets'] = [
                child_prs['id'] for child_prs in prs['child_policy_rule_sets']]
        else:
            with ctx.session.begin(subtransactions=True):
                filters = {'parent_id': [prs['id']]}
                child_prs_in_db = self._get_collection_query(
                    ctx, PolicyRuleSet, filters=filters)
                res['child_policy_rule_sets'] = [child_prs['id']
                                                 for child_prs
                                                 in child_prs_in_db]

        res['policy_rules'] = [pr['policy_rule_id']
                               for pr in prs['policy_rules']]
        res['providing_policy_target_groups'] = [
            ptg['policy_target_group_id']
            for ptg in prs['providing_policy_target_groups']]

        res['consuming_policy_target_groups'] = [
            ptg['policy_target_group_id']
            for ptg in prs['consuming_policy_target_groups']]

        res['providing_external_policies'] = [
            ptg['external_policy_id']
            for ptg in prs['providing_external_policies']]

        res['consuming_external_policies'] = [
            ptg['external_policy_id']
            for ptg in prs['consuming_external_policies']]
        return self._fields(res, fields)

    def _make_external_segment_dict(self, es, fields=None):
        res = self._populate_common_fields_in_dict(es)
        res['ip_version'] = es['ip_version']
        res['cidr'] = es['cidr']
        res['port_address_translation'] = es['port_address_translation']
        res['external_routes'] = [{'destination': er['destination'],
                                   'nexthop': er['nexthop']} for er in
                                  es['external_routes']]
        res['nat_pools'] = [np['id'] for np in es['nat_pools']]
        res['external_policies'] = [
            ep['external_policy_id']
            for ep in es['external_policies']]

        res['l3_policies'] = [
            l3p['l3_policy_id'] for l3p in es['l3_policies']]
        return self._fields(res, fields)

    def _make_external_policy_dict(self, ep, fields=None):
        res = self._populate_common_fields_in_dict(ep)
        res['external_segments'] = [
            es['external_segment_id']
            for es in ep['external_segments']]
        res['provided_policy_rule_sets'] = [
            pprs['policy_rule_set_id'] for pprs in
            ep['provided_policy_rule_sets']]
        res['consumed_policy_rule_sets'] = [
            cprs['policy_rule_set_id'] for cprs in
            ep['consumed_policy_rule_sets']]
        return self._fields(res, fields)

    def _make_nat_pool_dict(self, np, fields=None):
        res = self._populate_common_fields_in_dict(np)
        res['ip_version'] = np['ip_version']
        res['ip_pool'] = np['ip_pool']
        res['external_segment_id'] = np['external_segment_id']
        return self._fields(res, fields)

    def _get_ptgs_for_providing_policy_rule_set(self, context,
                                                policy_rule_set_id):
        return [x['policy_target_group_id'] for x in
                context.session.query(PTGToPRSProvidingAssociation).filter_by(
                    policy_rule_set_id=policy_rule_set_id)]

    def _get_ptgs_for_consuming_policy_rule_set(self, context,
                                                policy_rule_set_id):
        return [x['policy_target_group_id'] for x in
                context.session.query(PTGToPRSConsumingAssociation).filter_by(
                    policy_rule_set_id=policy_rule_set_id)]

    def _get_eps_for_providing_policy_rule_set(self, context,
                                               policy_rule_set_id):
        return [x['external_policy_id'] for x in
                context.session.query(EPToPRSProvidingAssociation).filter_by(
                    policy_rule_set_id=policy_rule_set_id)]

    def _get_eps_for_consuming_policy_rule_set(self, context,
                                               policy_rule_set_id):
        return [x['external_policy_id'] for x in
                context.session.query(EPToPRSConsumingAssociation).filter_by(
                    policy_rule_set_id=policy_rule_set_id)]

    def _get_policy_rule_policy_rule_sets(self, context, policy_rule_id):
        return [x['policy_rule_set_id'] for x in
                context.session.query(PRSToPRAssociation).filter_by(
                    policy_rule_id=policy_rule_id)]

    def _get_policy_classifier_rules(self, context, policy_classifier_id):
        return [x['id'] for x in
                context.session.query(PolicyRule).filter_by(
                    policy_classifier_id=policy_classifier_id)]

    def _get_policy_action_rules(self, context, policy_action_id):
        return [x['policy_rule_id'] for x in
                context.session.query(PolicyRuleActionAssociation).filter_by(
                    policy_action_id=policy_action_id)]

    def _get_external_segment_external_policies(self, context, es_id):
        return [x['external_policy_id'] for x in
                context.session.query(EPToESAssociation).filter_by(
                    external_segment_id=es_id)]

    def _get_l3p_ptgs(self, context, l3p_id, l3p_klass=L3Policy,
                      ptg_klass=PolicyTargetGroup, l2p_klass=L2Policy):
        return [self._make_policy_target_group_dict(x) for x in
                context.session.query(ptg_klass).join(
                    l2p_klass).join(l3p_klass).filter(
                        l2p_klass.l3_policy_id == l3p_id).all()]

    def _get_attribute(self, attrs, key):
        value = attrs.get(key)
        if value is constants.ATTR_NOT_SPECIFIED:
            value = None
        return value

    def _validate_service_management_ptg(self, context, tenant_id):
        # Verify whether a Management PTG already exists for this tenant
        if self.get_policy_target_groups(
                context, {'tenant_id': [tenant_id],
                          'service_management': [True]}):
            raise gpolicy.ManagementPolicyTargetGroupExists()

    @staticmethod
    def validate_ip_pool(ip_pool, ip_version):
        # An empty pool is allowed, as it can be used
        # with the default subnetpools extension
        if not ip_pool:
            return
        if ip_version == 46:
            valid_versions = [4, 6]
        else:
            valid_versions = [ip_version]
        ip_pool_list = utils.convert_ip_pool_string_to_list(ip_pool)
        for pool in ip_pool_list:
            validators.validate_subnet(pool)
            ip_net = netaddr.IPNetwork(pool)
            if ip_net.version not in valid_versions:
                raise gpolicy.InvalidIpPoolVersion(ip_pool=pool,
                                                   version=ip_version)
            if (ip_net.size <= 3):
                err_msg = "Too few available IPs in the pool."
                raise gpolicy.InvalidIpPoolSize(ip_pool=pool, err_msg=err_msg,
                                                size=ip_net.size)
            if (ip_net.prefixlen == 0):
                err_msg = "Prefix length of 0 is invalid."
                raise gpolicy.InvalidIpPoolPrefixLength(ip_pool=pool,
                                            err_msg=err_msg,
                                            prefixlen=ip_net.prefixlen)

    @staticmethod
    def validate_subnet_prefix_length(ip_version, new_prefix_length,
                                      ip_pool=None):
        if (ip_version == 4 or ip_version == 46) and ((new_prefix_length >
                MAX_IPV4_SUBNET_PREFIX_LENGTH) or (new_prefix_length < 2)):
            raise gpolicy.InvalidDefaultSubnetPrefixLength(
                length=new_prefix_length, protocol=ip_version)

        ip_pool_list = utils.convert_ip_pool_string_to_list(ip_pool)
        for pool in ip_pool_list:
            # Check if subnet_prefix_length is smaller
            # than size of the ip_pool's subnet.
            if netaddr.IPNetwork(pool).version == 4:
                ip_pool_prefix_length = netaddr.IPNetwork(pool).prefixlen
                if(ip_pool_prefix_length > new_prefix_length):
                    raise gpolicy.SubnetPrefixLengthExceedsIpPool(
                        ip_pool=pool, subnet_size=new_prefix_length)

    @log.log_method_call
    def create_policy_target(self, context, policy_target):
        pt = policy_target['policy_target']
        tenant_id = self._get_tenant_id_for_create(context, pt)
        with context.session.begin(subtransactions=True):
            pt_db = PolicyTarget(
                id=uuidutils.generate_uuid(), tenant_id=tenant_id,
                name=pt['name'], description=pt['description'],
                policy_target_group_id=pt['policy_target_group_id'],
                cluster_id=pt['cluster_id'],
                status=pt.get('status'),
                status_details=pt.get('status_details'))
            context.session.add(pt_db)
        return self._make_policy_target_dict(pt_db)

    @log.log_method_call
    def update_policy_target(self, context, policy_target_id, policy_target):
        pt = policy_target['policy_target']
        with context.session.begin(subtransactions=True):
            pt_db = self._get_policy_target(context, policy_target_id)
            pt_db.update(pt)
        return self._make_policy_target_dict(pt_db)

    @log.log_method_call
    def delete_policy_target(self, context, policy_target_id):
        with context.session.begin(subtransactions=True):
            pt_db = self._get_policy_target(context, policy_target_id)
            context.session.delete(pt_db)

    @log.log_method_call
    def get_policy_target(self, context, policy_target_id, fields=None):
        pt = self._get_policy_target(context, policy_target_id)
        return self._make_policy_target_dict(pt, fields)

    @log.log_method_call
    def get_policy_targets(self, context, filters=None, fields=None,
                           sorts=None, limit=None, marker=None,
                           page_reverse=False):
        marker_obj = self._get_marker_obj(context, 'policy_target', limit,
                                          marker)
        return self._get_collection(context, PolicyTarget,
                                    self._make_policy_target_dict,
                                    filters=filters, fields=fields,
                                    sorts=sorts, limit=limit,
                                    marker_obj=marker_obj,
                                    page_reverse=page_reverse)

    @log.log_method_call
    def get_policy_targets_count(self, context, filters=None):
        return self._get_collection_count(context, PolicyTarget,
                                          filters=filters)

    @log.log_method_call
    def create_policy_target_group(self, context, policy_target_group):
        ptg = policy_target_group['policy_target_group']
        tenant_id = self._get_tenant_id_for_create(context, ptg)
        with context.session.begin(subtransactions=True):
            if ptg['service_management']:
                self._validate_service_management_ptg(context, tenant_id)
            ptg_db = PolicyTargetGroup(
                id=uuidutils.generate_uuid(), tenant_id=tenant_id,
                name=ptg['name'], description=ptg['description'],
                l2_policy_id=ptg['l2_policy_id'],
                application_policy_group_id=ptg.get(
                    'application_policy_group_id', None),
                network_service_policy_id=ptg['network_service_policy_id'],
                shared=ptg.get('shared', False),
                service_management=ptg.get('service_management', False),
                status=ptg.get('status'),
                status_details=ptg.get('status_details'))
            context.session.add(ptg_db)
            self._process_policy_rule_sets_for_ptg(context, ptg_db, ptg)
        return self._make_policy_target_group_dict(ptg_db)

    @log.log_method_call
    def update_policy_target_group(self, context, policy_target_group_id,
                                   policy_target_group):
        ptg = policy_target_group['policy_target_group']
        with context.session.begin(subtransactions=True):
            ptg_db = self._get_policy_target_group(
                context, policy_target_group_id)
            ptg = self._process_policy_rule_sets_for_ptg(context, ptg_db, ptg)
            ptg_db.update(ptg)
        return self._make_policy_target_group_dict(ptg_db)

    @log.log_method_call
    def delete_policy_target_group(self, context, policy_target_group_id):
        with context.session.begin(subtransactions=True):
            ptg_db = self._get_policy_target_group(
                context, policy_target_group_id)
            # REVISIT(rkukura): An exception should be raised here if
            # the PTG is referenced by any PTs, but code added to
            # GroupPolicyPlugin.delete_policy_target_group() in
            # https://review.openstack.org/#/c/144214/ handles this
            # based on whether the PTs referencing it have ports. Note
            # that the ports are only removed from the PTs due to
            # https://bugs.launchpad.net/nova/+bug/1158684. Once that
            # bug is fixed, Nova will no longer be deleting the ports,
            # we'll need some way to delete the PTs from Horizon, and
            # then we might as well check for in-use EPGs here as with
            # other resources.
            context.session.delete(ptg_db)

    @log.log_method_call
    def get_policy_target_group(self, context, policy_target_group_id,
                                fields=None):
        ptg = self._get_policy_target_group(context, policy_target_group_id)
        return self._make_policy_target_group_dict(ptg, fields)

    @log.log_method_call
    def get_policy_target_groups(self, context, filters=None, fields=None,
                                 sorts=None, limit=None, marker=None,
                                 page_reverse=False):
        marker_obj = self._get_marker_obj(
            context, 'policy_target_group', limit, marker)
        return self._get_collection(context, PolicyTargetGroup,
                                    self._make_policy_target_group_dict,
                                    filters=filters, fields=fields,
                                    sorts=sorts, limit=limit,
                                    marker_obj=marker_obj,
                                    page_reverse=page_reverse)

    @log.log_method_call
    def get_policy_target_groups_count(self, context, filters=None):
        return self._get_collection_count(context, PolicyTargetGroup,
                                          filters=filters)

    @log.log_method_call
    def create_application_policy_group(self, context,
                                        application_policy_group):
        apg = application_policy_group['application_policy_group']
        tenant_id = self._get_tenant_id_for_create(context, apg)
        with context.session.begin(subtransactions=True):
            apg_db = ApplicationPolicyGroup(
                id=uuidutils.generate_uuid(), tenant_id=tenant_id,
                name=apg['name'], description=apg['description'],
                shared=apg.get('shared', False),
                status=apg.get('status'),
                status_details=apg.get('status_details'))
            context.session.add(apg_db)
        return self._make_application_policy_group_dict(apg_db)

    @log.log_method_call
    def update_application_policy_group(self, context,
                                        application_policy_group_id,
                                        application_policy_group):
        apg = application_policy_group['application_policy_group']
        with context.session.begin(subtransactions=True):
            apg_db = self._get_application_policy_group(
                context, application_policy_group_id)
            apg_db.update(apg)
        return self._make_application_policy_group_dict(apg_db)

    @log.log_method_call
    def delete_application_policy_group(self, context,
                                        application_policy_group_id):
        with context.session.begin(subtransactions=True):
            apg_db = self._get_application_policy_group(
                context, application_policy_group_id)
            context.session.delete(apg_db)

    @log.log_method_call
    def get_application_policy_group(self, context,
                                     application_policy_group_id, fields=None):
        apg = self._get_application_policy_group(context,
                                                 application_policy_group_id)
        return self._make_application_policy_group_dict(apg, fields)

    @log.log_method_call
    def get_application_policy_groups(self, context, filters=None,
                                      fields=None, sorts=None, limit=None,
                                      marker=None, page_reverse=False):
        marker_obj = self._get_marker_obj(
            context, 'application_policy_group', limit, marker)
        return self._get_collection(context, ApplicationPolicyGroup,
                                    self._make_application_policy_group_dict,
                                    filters=filters, fields=fields,
                                    sorts=sorts, limit=limit,
                                    marker_obj=marker_obj,
                                    page_reverse=page_reverse)

    @log.log_method_call
    def get_application_policy_groups_count(self, context, filters=None):
        return self._get_collection_count(context, ApplicationPolicyGroup,
                                          filters=filters)

    @log.log_method_call
    def create_l2_policy(self, context, l2_policy):
        l2p = l2_policy['l2_policy']
        tenant_id = self._get_tenant_id_for_create(context, l2p)
        with context.session.begin(subtransactions=True):
            l2p_db = L2Policy(id=uuidutils.generate_uuid(),
                              tenant_id=tenant_id, name=l2p['name'],
                              description=l2p['description'],
                              l3_policy_id=l2p.get('l3_policy_id'),
                              inject_default_route=l2p.get(
                                  'inject_default_route', True),
                              shared=l2p.get('shared', False),
                              status=l2p.get('status'),
                              status_details=l2p.get('status_details'))
            context.session.add(l2p_db)
        return self._make_l2_policy_dict(l2p_db)

    @log.log_method_call
    def update_l2_policy(self, context, l2_policy_id, l2_policy):
        l2p = l2_policy['l2_policy']
        with context.session.begin(subtransactions=True):
            l2p_db = self._get_l2_policy(context, l2_policy_id)
            l2p_db.update(l2p)
        return self._make_l2_policy_dict(l2p_db)

    @log.log_method_call
    def delete_l2_policy(self, context, l2_policy_id):
        with context.session.begin(subtransactions=True):
            l2p_db = self._get_l2_policy(context, l2_policy_id)
            # When delete_l2_policy is called implicitly (as a
            # side effect of the last PTG deletion), the L2P's
            # backref to PTGs is not getting reflected correctly
            # here (at least in the UTs). This is fixed by the explicit
            # call to refresh.
            context.session.refresh(l2p_db)
            if l2p_db.policy_target_groups:
                raise gpolicy.L2PolicyInUse(l2_policy_id=l2_policy_id)
            context.session.delete(l2p_db)

    @log.log_method_call
    def get_l2_policy(self, context, l2_policy_id, fields=None):
        l2p = self._get_l2_policy(context, l2_policy_id)
        return self._make_l2_policy_dict(l2p, fields)

    @log.log_method_call
    def get_l2_policies(self, context, filters=None, fields=None,
                        sorts=None, limit=None, marker=None,
                        page_reverse=False):
        marker_obj = self._get_marker_obj(context, 'l2_policy', limit,
                                          marker)
        return self._get_collection(context, L2Policy,
                                    self._make_l2_policy_dict,
                                    filters=filters, fields=fields,
                                    sorts=sorts, limit=limit,
                                    marker_obj=marker_obj,
                                    page_reverse=page_reverse)

    @log.log_method_call
    def get_l2_policies_count(self, context, filters=None):
        return self._get_collection_count(context, L2Policy,
                                          filters=filters)

    @log.log_method_call
    def create_l3_policy(self, context, l3_policy):
        l3p = l3_policy['l3_policy']
        tenant_id = self._get_tenant_id_for_create(context, l3p)
        self.validate_ip_pool(l3p.get('ip_pool', None), l3p['ip_version'])
        self.validate_subnet_prefix_length(
            l3p['ip_version'], l3p['subnet_prefix_length'],
            l3p.get('ip_pool', None))
        with context.session.begin(subtransactions=True):
            l3p_db = L3Policy(
                id=uuidutils.generate_uuid(),
                tenant_id=tenant_id, name=l3p['name'],
                description=l3p['description'],
                ip_version=l3p['ip_version'],
                ip_pool=l3p['ip_pool'],
                subnet_prefix_length=l3p['subnet_prefix_length'],
                shared=l3p.get('shared', False),
                status=l3p.get('status'),
                status_details=l3p.get('status_details'))
            if 'external_segments' in l3p:
                self._set_ess_for_l3p(context, l3p_db,
                                      l3p['external_segments'])
                context.session.add(l3p_db)
        return self._make_l3_policy_dict(l3p_db)

    @log.log_method_call
    def update_l3_policy(self, context, l3_policy_id, l3_policy):
        l3p = l3_policy['l3_policy']
        with context.session.begin(subtransactions=True):
            l3p_db = self._get_l3_policy(context, l3_policy_id)
            if 'subnet_prefix_length' in l3p:
                self.validate_subnet_prefix_length(
                    l3p_db.ip_version,
                    l3p['subnet_prefix_length'], l3p_db.ip_pool)
            if 'external_segments' in l3p:
                self._set_ess_for_l3p(context, l3p_db,
                                      l3p['external_segments'])
                del l3p['external_segments']
            l3p_db.update(l3p)
        return self._make_l3_policy_dict(l3p_db)

    @log.log_method_call
    def delete_l3_policy(self, context, l3_policy_id):
        with context.session.begin(subtransactions=True):
            l3p_db = self._get_l3_policy(context, l3_policy_id)
            if l3p_db.l2_policies:
                raise gpolicy.L3PolicyInUse(l3_policy_id=l3_policy_id)
            context.session.delete(l3p_db)

    @log.log_method_call
    def get_l3_policy(self, context, l3_policy_id, fields=None):
        l3p = self._get_l3_policy(context, l3_policy_id)
        return self._make_l3_policy_dict(l3p, fields)

    @log.log_method_call
    def get_l3_policies(self, context, filters=None, fields=None,
                        sorts=None, limit=None, marker=None,
                        page_reverse=False):
        marker_obj = self._get_marker_obj(context, 'l2_policy', limit,
                                          marker)
        return self._get_collection(context, L3Policy,
                                    self._make_l3_policy_dict,
                                    filters=filters, fields=fields,
                                    sorts=sorts, limit=limit,
                                    marker_obj=marker_obj,
                                    page_reverse=page_reverse)

    @log.log_method_call
    def get_l3_policies_count(self, context, filters=None):
        return self._get_collection_count(context, L3Policy,
                                          filters=filters)

    @log.log_method_call
    def create_network_service_policy(self, context, network_service_policy):
        nsp = network_service_policy['network_service_policy']
        tenant_id = self._get_tenant_id_for_create(context, nsp)
        with context.session.begin(subtransactions=True):
            nsp_db = NetworkServicePolicy(id=uuidutils.generate_uuid(),
                                          tenant_id=tenant_id,
                                          name=nsp['name'],
                                          description=nsp['description'],
                                          shared=nsp.get('shared', False),
                                          status=nsp.get('status'),
                                          status_details=
                                          nsp.get('status_details'))
            context.session.add(nsp_db)
            self._set_params_for_network_service_policy(
                context, nsp_db, nsp)
        return self._make_network_service_policy_dict(nsp_db)

    @log.log_method_call
    def update_network_service_policy(
        self, context, network_service_policy_id, network_service_policy):
        nsp = network_service_policy['network_service_policy']
        with context.session.begin(subtransactions=True):
            nsp_db = self._get_network_service_policy(
                context, network_service_policy_id)
            if 'network_service_params' in network_service_policy:
                self._set_params_for_network_service_policy(
                    context, nsp_db, nsp)
            nsp_db.update(nsp)
        return self._make_network_service_policy_dict(nsp_db)

    @log.log_method_call
    def delete_network_service_policy(
        self, context, network_service_policy_id):
        with context.session.begin(subtransactions=True):
            nsp_db = self._get_network_service_policy(
                context, network_service_policy_id)
            if nsp_db.policy_target_groups:
                raise gpolicy.NetworkServicePolicyInUse(
                        network_service_policy_id=network_service_policy_id)
            context.session.delete(nsp_db)

    @log.log_method_call
    def get_network_service_policy(
            self, context, network_service_policy_id, fields=None):
        nsp = self._get_network_service_policy(
            context, network_service_policy_id)
        return self._make_network_service_policy_dict(nsp, fields)

    @log.log_method_call
    def get_network_service_policies(
            self, context, filters=None, fields=None, sorts=None, limit=None,
            marker=None, page_reverse=False):
        marker_obj = self._get_marker_obj(
            context, 'network_service_policy', limit, marker)
        return self._get_collection(context, NetworkServicePolicy,
                                    self._make_network_service_policy_dict,
                                    filters=filters, fields=fields,
                                    sorts=sorts, limit=limit,
                                    marker_obj=marker_obj,
                                    page_reverse=page_reverse)

    @log.log_method_call
    def get_network_service_policies_count(self, context, filters=None):
        return self._get_collection_count(context, NetworkServicePolicy,
                                          filters=filters)

    @log.log_method_call
    def create_policy_classifier(self, context, policy_classifier):
        pc = policy_classifier['policy_classifier']
        tenant_id = self._get_tenant_id_for_create(context, pc)
        port_min, port_max = GroupPolicyDbPlugin._get_min_max_ports_from_range(
            pc['port_range'])
        with context.session.begin(subtransactions=True):
            pc_db = PolicyClassifier(id=uuidutils.generate_uuid(),
                                     tenant_id=tenant_id,
                                     name=pc['name'],
                                     description=pc['description'],
                                     protocol=pc['protocol'],
                                     port_range_min=port_min,
                                     port_range_max=port_max,
                                     direction=pc['direction'],
                                     shared=pc.get('shared', False),
                                     status=pc.get('status'),
                                     status_details=
                                     pc.get('status_details'))
            context.session.add(pc_db)
        return self._make_policy_classifier_dict(pc_db)

    @log.log_method_call
    def update_policy_classifier(self, context, policy_classifier_id,
                                 policy_classifier):
        pc = policy_classifier['policy_classifier']
        with context.session.begin(subtransactions=True):
            pc_db = self._get_policy_classifier(context, policy_classifier_id)
            if 'port_range' in pc:
                port_min, port_max = (GroupPolicyDbPlugin.
                                      _get_min_max_ports_from_range(
                                          pc['port_range']))
                pc.update({'port_range_min': port_min,
                           'port_range_max': port_max})
                del pc['port_range']
            pc_db.update(pc)
        return self._make_policy_classifier_dict(pc_db)

    @log.log_method_call
    def delete_policy_classifier(self, context, policy_classifier_id):
        with context.session.begin(subtransactions=True):
            pc_db = self._get_policy_classifier(context, policy_classifier_id)
            pc_ids = self._get_policy_classifier_rules(context,
                                                       policy_classifier_id)
            if pc_ids:
                raise gpolicy.PolicyClassifierInUse(
                    policy_classifier_id=policy_classifier_id)
            context.session.delete(pc_db)

    @log.log_method_call
    def get_policy_classifier(self, context, policy_classifier_id,
                              fields=None):
        pc = self._get_policy_classifier(context, policy_classifier_id)
        return self._make_policy_classifier_dict(pc, fields)

    @log.log_method_call
    def get_policy_classifiers(self, context, filters=None, fields=None,
                               sorts=None, limit=None, marker=None,
                               page_reverse=False):
        marker_obj = self._get_marker_obj(context, 'policy_classifier', limit,
                                          marker)
        return self._get_collection(context, PolicyClassifier,
                                    self._make_policy_classifier_dict,
                                    filters=filters, fields=fields,
                                    sorts=sorts, limit=limit,
                                    marker_obj=marker_obj,
                                    page_reverse=page_reverse)

    @log.log_method_call
    def get_policy_classifiers_count(self, context, filters=None):
        return self._get_collection_count(context, PolicyClassifier,
                                          filters=filters)

    @log.log_method_call
    def create_policy_action(self, context, policy_action):
        pa = policy_action['policy_action']
        tenant_id = self._get_tenant_id_for_create(context, pa)
        with context.session.begin(subtransactions=True):
            pa_db = PolicyAction(id=uuidutils.generate_uuid(),
                                 tenant_id=tenant_id,
                                 name=pa['name'],
                                 description=pa['description'],
                                 action_type=pa['action_type'],
                                 action_value=pa['action_value'],
                                 shared=pa.get('shared', False),
                                 status=pa.get('status'),
                                 status_details=
                                 pa.get('status_details'))
            context.session.add(pa_db)
        return self._make_policy_action_dict(pa_db)

    @log.log_method_call
    def update_policy_action(self, context, policy_action_id, policy_action):
        pa = policy_action['policy_action']
        with context.session.begin(subtransactions=True):
            pa_db = self._get_policy_action(context, policy_action_id)
            pa_db.update(pa)
        return self._make_policy_action_dict(pa_db)

    @log.log_method_call
    def delete_policy_action(self, context, policy_action_id):
        with context.session.begin(subtransactions=True):
            pa_db = self._get_policy_action(context, policy_action_id)
            pa_ids = self._get_policy_action_rules(context, policy_action_id)
            if pa_ids:
                raise gpolicy.PolicyActionInUse(
                    policy_action_id=policy_action_id)
            context.session.delete(pa_db)

    @log.log_method_call
    def get_policy_action(self, context, id, fields=None):
        pa = self._get_policy_action(context, id)
        return self._make_policy_action_dict(pa, fields)

    @log.log_method_call
    def get_policy_actions(self, context, filters=None, fields=None,
                           sorts=None, limit=None, marker=None,
                           page_reverse=False):
        marker_obj = self._get_marker_obj(context, 'policy_action', limit,
                                          marker)
        return self._get_collection(context, PolicyAction,
                                    self._make_policy_action_dict,
                                    filters=filters, fields=fields,
                                    sorts=sorts, limit=limit,
                                    marker_obj=marker_obj,
                                    page_reverse=page_reverse)

    @log.log_method_call
    def get_policy_actions_count(self, context, filters=None):
        return self._get_collection_count(context, PolicyAction,
                                          filters=filters)

    @log.log_method_call
    def create_policy_rule(self, context, policy_rule):
        pr = policy_rule['policy_rule']
        tenant_id = self._get_tenant_id_for_create(context, pr)
        with context.session.begin(subtransactions=True):
            pr_db = PolicyRule(id=uuidutils.generate_uuid(),
                               tenant_id=tenant_id, name=pr['name'],
                               description=pr['description'],
                               enabled=pr['enabled'],
                               policy_classifier_id=pr['policy_classifier_id'],
                               shared=pr.get('shared', False),
                               status=pr.get('status'),
                               status_details=pr.get('status_details'))
            context.session.add(pr_db)
            self._set_actions_for_rule(context, pr_db,
                                       pr['policy_actions'])
        return self._make_policy_rule_dict(pr_db)

    @log.log_method_call
    def update_policy_rule(self, context, policy_rule_id, policy_rule):
        pr = policy_rule['policy_rule']
        with context.session.begin(subtransactions=True):
            pr_db = self._get_policy_rule(context, policy_rule_id)
            if 'policy_actions' in pr:
                self._set_actions_for_rule(context, pr_db,
                                           pr['policy_actions'])
                del pr['policy_actions']
            pr_db.update(pr)
        return self._make_policy_rule_dict(pr_db)

    @log.log_method_call
    def delete_policy_rule(self, context, policy_rule_id):
        with context.session.begin(subtransactions=True):
            pr_db = self._get_policy_rule(context, policy_rule_id)
            prs_ids = self._get_policy_rule_policy_rule_sets(context,
                                                             policy_rule_id)
            if prs_ids:
                raise gpolicy.PolicyRuleInUse(policy_rule_id=policy_rule_id)
            context.session.delete(pr_db)

    @log.log_method_call
    def get_policy_rule(self, context, policy_rule_id, fields=None):
        pr = self._get_policy_rule(context, policy_rule_id)
        return self._make_policy_rule_dict(pr, fields)

    @log.log_method_call
    def get_policy_rules(self, context, filters=None, fields=None,
                         sorts=None, limit=None, marker=None,
                         page_reverse=False):
        marker_obj = self._get_marker_obj(context, 'policy_rule', limit,
                                          marker)
        return self._get_collection(context, PolicyRule,
                                    self._make_policy_rule_dict,
                                    filters=filters, fields=fields,
                                    sorts=sorts, limit=limit,
                                    marker_obj=marker_obj,
                                    page_reverse=page_reverse)

    @log.log_method_call
    def get_policy_rules_count(self, context, filters=None):
        return self._get_collection_count(context, PolicyRule,
                                          filters=filters)

    @log.log_method_call
    def create_policy_rule_set(self, context, policy_rule_set):
        prs = policy_rule_set['policy_rule_set']
        tenant_id = self._get_tenant_id_for_create(context, prs)
        with context.session.begin(subtransactions=True):
            prs_db = PolicyRuleSet(id=uuidutils.generate_uuid(),
                                   tenant_id=tenant_id,
                                   name=prs['name'],
                                   description=prs['description'],
                                   shared=prs.get('shared', False),
                                   status=prs.get('status'),
                                   status_details=prs.get('status_details'))
            context.session.add(prs_db)
            self._set_rules_for_policy_rule_set(context, prs_db,
                                                prs['policy_rules'])
            self._set_children_for_policy_rule_set(
                context, prs_db, prs['child_policy_rule_sets'])
        return self._make_policy_rule_set_dict(prs_db)

    @log.log_method_call
    def update_policy_rule_set(self, context, policy_rule_set_id,
                               policy_rule_set):
        prs = policy_rule_set['policy_rule_set']
        with context.session.begin(subtransactions=True):
            prs_db = self._get_policy_rule_set(context, policy_rule_set_id)
            if 'policy_rules' in prs:
                self._set_rules_for_policy_rule_set(
                    context, prs_db, prs['policy_rules'])
                del prs['policy_rules']
            if 'child_policy_rule_sets' in prs:
                self._set_children_for_policy_rule_set(
                    context, prs_db, prs['child_policy_rule_sets'])
                del prs['child_policy_rule_sets']
            prs_db.update(prs)
        return self._make_policy_rule_set_dict(prs_db)

    @log.log_method_call
    def delete_policy_rule_set(self, context, policy_rule_set_id):
        with context.session.begin(subtransactions=True):
            prs_db = self._get_policy_rule_set(context, policy_rule_set_id)
            prs_ids = (
                self._get_ptgs_for_providing_policy_rule_set(
                    context, policy_rule_set_id) or
                self._get_ptgs_for_consuming_policy_rule_set(
                    context, policy_rule_set_id) or
                self._get_eps_for_providing_policy_rule_set(
                    context, policy_rule_set_id) or
                self._get_eps_for_consuming_policy_rule_set(
                    context, policy_rule_set_id))

            if prs_ids:
                raise gpolicy.PolicyRuleSetInUse(policy_rule_set_id=
                                                 policy_rule_set_id)
            context.session.delete(prs_db)

    @log.log_method_call
    def get_policy_rule_set(self, context, policy_rule_set_id, fields=None):
        prs = self._get_policy_rule_set(context, policy_rule_set_id)
        return self._make_policy_rule_set_dict(prs, fields)

    @log.log_method_call
    def get_policy_rule_sets(self, context, filters=None, fields=None,
                             sorts=None, limit=None, marker=None,
                             page_reverse=False):
        marker_obj = self._get_marker_obj(context, 'policy_rule_set', limit,
                                          marker)
        return self._get_collection(context, PolicyRuleSet,
                                    self._make_policy_rule_set_dict,
                                    filters=filters, fields=fields,
                                    sorts=sorts, limit=limit,
                                    marker_obj=marker_obj,
                                    page_reverse=page_reverse)

    @log.log_method_call
    def get_policy_rule_sets_count(self, context, filters=None):
        return self._get_collection_count(context, PolicyRuleSet,
                                          filters=filters)

    @log.log_method_call
    def create_external_policy(self, context, external_policy):
        ep = external_policy['external_policy']
        tenant_id = self._get_tenant_id_for_create(context, ep)
        with context.session.begin(subtransactions=True):
            ep_db = ExternalPolicy(
                id=uuidutils.generate_uuid(), tenant_id=tenant_id,
                name=ep['name'], description=ep['description'],
                shared=ep.get('shared', False),
                status=ep.get('status'),
                status_details=ep.get('status_details'))
            context.session.add(ep_db)
            if 'external_segments' in ep:
                self._set_ess_for_ep(context, ep_db,
                                     ep['external_segments'])
            self._process_policy_rule_sets_for_ep(context, ep_db, ep)
        return self._make_external_policy_dict(ep_db)

    @log.log_method_call
    def update_external_policy(self, context, external_policy_id,
                               external_policy):
        ep = external_policy['external_policy']
        with context.session.begin(subtransactions=True):
            ep_db = self._get_external_policy(
                context, external_policy_id)
            if 'external_segments' in ep:
                self._set_ess_for_ep(context, ep_db,
                                     ep['external_segments'])
                del ep['external_segments']
            self._process_policy_rule_sets_for_ep(context, ep_db, ep)
            ep_db.update(ep)
        return self._make_external_policy_dict(ep_db)

    @log.log_method_call
    def get_external_policies(self, context, filters=None, fields=None,
                              sorts=None, limit=None, marker=None,
                              page_reverse=False):
        marker_obj = self._get_marker_obj(context, 'external_policy', limit,
                                          marker)
        return self._get_collection(context, ExternalPolicy,
                                    self._make_external_policy_dict,
                                    filters=filters, fields=fields,
                                    sorts=sorts, limit=limit,
                                    marker_obj=marker_obj,
                                    page_reverse=page_reverse)

    @log.log_method_call
    def get_external_policies_count(self, context, filters=None):
        return self._get_collection_count(context, ExternalPolicy,
                                          filters=filters)

    @log.log_method_call
    def get_external_policy(self, context, external_policy_id, fields=None):
        ep = self._get_external_policy(
            context, external_policy_id)
        return self._make_external_policy_dict(ep, fields)

    @log.log_method_call
    def delete_external_policy(self, context, external_policy_id):
        with context.session.begin(subtransactions=True):
            ep_db = self._get_external_policy(
                context, external_policy_id)
            context.session.delete(ep_db)

    @log.log_method_call
    def create_external_segment(self, context, external_segment):
        es = external_segment['external_segment']
        tenant_id = self._get_tenant_id_for_create(context, es)
        with context.session.begin(subtransactions=True):
            es_db = ExternalSegment(
                id=uuidutils.generate_uuid(), tenant_id=tenant_id,
                name=es['name'], description=es['description'],
                shared=es.get('shared', False), ip_version=es['ip_version'],
                cidr=es['cidr'],
                port_address_translation=es['port_address_translation'],
                status=es.get('status'),
                status_details=es.get('status_details'))
            context.session.add(es_db)
            if 'external_routes' in es:
                self._process_segment_ers(context, es_db, es)
        return self._make_external_segment_dict(es_db)

    @log.log_method_call
    def update_external_segment(self, context, external_segment_id,
                                external_segment):
        es = external_segment['external_segment']
        with context.session.begin(subtransactions=True):
            es_db = self._get_external_segment(
                context, external_segment_id)
            if 'external_routes' in es:
                self._process_segment_ers(context, es_db, es)
                del es['external_routes']
            es_db.update(es)
        return self._make_external_segment_dict(es_db)

    @log.log_method_call
    def get_external_segments(self, context, filters=None, fields=None,
                              sorts=None, limit=None, marker=None,
                              page_reverse=False):
        marker_obj = self._get_marker_obj(context, 'external_segment', limit,
                                          marker)
        return self._get_collection(context, ExternalSegment,
                                    self._make_external_segment_dict,
                                    filters=filters, fields=fields,
                                    sorts=sorts, limit=limit,
                                    marker_obj=marker_obj,
                                    page_reverse=page_reverse)

    @log.log_method_call
    def get_external_segments_count(self, context, filters=None):
        return self._get_collection_count(context, ExternalSegment,
                                          filters=filters)

    @log.log_method_call
    def get_external_segment(self, context, external_segment_id, fields=None):
        es = self._get_external_segment(
            context, external_segment_id)
        return self._make_external_segment_dict(es, fields)

    @log.log_method_call
    def delete_external_segment(self, context, external_segment_id):
        with context.session.begin(subtransactions=True):
            es_db = self._get_external_segment(
                context, external_segment_id)
            context.session.delete(es_db)

    @log.log_method_call
    def create_nat_pool(self, context, nat_pool):
        np = nat_pool['nat_pool']
        tenant_id = self._get_tenant_id_for_create(context, np)
        with context.session.begin(subtransactions=True):
            np_db = NATPool(
                id=uuidutils.generate_uuid(), tenant_id=tenant_id,
                name=np['name'], description=np['description'],
                shared=np.get('shared', False), ip_version=np['ip_version'],
                ip_pool=np['ip_pool'],
                external_segment_id=np['external_segment_id'],
                status=np.get('status'),
                status_details=np.get('status_details'))
            context.session.add(np_db)
        return self._make_nat_pool_dict(np_db)

    @log.log_method_call
    def update_nat_pool(self, context, nat_pool_id, nat_pool):
        np = nat_pool['nat_pool']
        with context.session.begin(subtransactions=True):
            np_db = self._get_nat_pool(
                context, nat_pool_id)
            np_db.update(np)
        return self._make_nat_pool_dict(np_db)

    @log.log_method_call
    def get_nat_pools(self, context, filters=None, fields=None,
                      sorts=None, limit=None, marker=None,
                      page_reverse=False):
        marker_obj = self._get_marker_obj(context, 'nat_pool', limit,
                                          marker)
        return self._get_collection(context, NATPool,
                                    self._make_nat_pool_dict,
                                    filters=filters, fields=fields,
                                    sorts=sorts, limit=limit,
                                    marker_obj=marker_obj,
                                    page_reverse=page_reverse)

    @log.log_method_call
    def get_nat_pools_count(self, context, filters=None):
        return self._get_collection_count(context, NATPool, filters=filters)

    @log.log_method_call
    def get_nat_pool(self, context, nat_pool_id, fields=None):
        np = self._get_nat_pool(context, nat_pool_id)
        return self._make_nat_pool_dict(np, fields)

    @log.log_method_call
    def delete_nat_pool(self, context, nat_pool_id):
        with context.session.begin(subtransactions=True):
            np_db = self._get_nat_pool(context, nat_pool_id)
            context.session.delete(np_db)
