Exemple #1
0
    def __init__(self, driver):
        """Get full service definition from loadbalancer id."""
        self.driver = driver

        self.net_cache = {}
        self.subnet_cache = {}
        self.last_cache_update = datetime.datetime.fromtimestamp(0)
        self.plugin = self.driver.plugin
        self.disconnected_service = DisconnectedService()
    def __init__(self, driver):
        """Get full service definition from loadbalancer id."""
        self.driver = driver

        self.net_cache = {}
        self.subnet_cache = {}
        self.last_cache_update = datetime.datetime.fromtimestamp(0)
        self.plugin = self.driver.plugin
        self.disconnected_service = DisconnectedService()
Exemple #3
0
class LBaaSv2ServiceBuilder(object):
    """The class creates a service definition from neutron database.

    A service definition represents all the information required to
    construct a load-balancing service on BigIP.

    Requests come in to agent as full service definitions, not incremental
    changes. The driver looks up networks, mac entries, segmentation info,
    etc and places all information in a service object (which is a python
    dictionary variable) and passes that to the agent.

    """
    def __init__(self, driver):
        """Get full service definition from loadbalancer id."""
        self.driver = driver

        self.net_cache = {}
        self.subnet_cache = {}
        self.last_cache_update = datetime.datetime.fromtimestamp(0)
        self.plugin = self.driver.plugin
        self.disconnected_service = DisconnectedService()

    def build(self, context, loadbalancer, agent):
        """Get full service definition from loadbalancer ID."""
        # Invalidate cache if it is too old
        if ((datetime.datetime.now() - self.last_cache_update).seconds >
                constants_v2.NET_CACHE_SECONDS):
            self.net_cache = {}
            self.subnet_cache = {}

        service = {}
        with context.session.begin(subtransactions=True):
            LOG.debug('Building service definition entry for %s' %
                      loadbalancer.id)

            # Start with the neutron loadbalancer definition
            service['loadbalancer'] = self._get_extended_loadbalancer(
                context, loadbalancer)

            # Get the subnet network associated with the VIP.
            subnet_map = {}
            subnet_id = loadbalancer.vip_subnet_id
            vip_subnet = self._get_subnet_cached(context, subnet_id)
            subnet_map[subnet_id] = vip_subnet

            # Get the network associated with the Loadbalancer.
            network_map = {}
            vip_port = service['loadbalancer']['vip_port']
            network_id = vip_port['network_id']
            service['loadbalancer']['network_id'] = network_id
            network = self._get_network_cached(context, network_id)
            # Override the segmentation ID and network type for this network
            # if we are running in disconnected service mode
            agent_config = self.deserialize_agent_configurations(
                agent['configurations'])
            segment_data = self.disconnected_service.get_network_segment(
                context, agent_config, network)
            if segment_data:
                network['provider:segmentation_id'] = getattr(
                    segment_data, 'segmentation_id', None)
                network['provider:network_type'] = getattr(
                    segment_data, 'network_type', None)
            network_map[network_id] = network

            # Check if the tenant can create a loadbalancer on the network.
            if (agent and not self._valid_tenant_ids(
                    network, loadbalancer.tenant_id, agent)):
                raise f5_exc.F5MismatchedTenants()

            # Get the network VTEPs if the network provider type is
            # either gre or vxlan.
            if 'provider:network_type' in network:
                net_type = network['provider:network_type']
                if net_type == 'vxlan' or net_type == 'gre':
                    self._populate_loadbalancer_network_vteps(
                        context, service['loadbalancer'], net_type)

            # Get listeners and pools.
            service['listeners'] = []
            service['pools'] = []
            listeners = self.plugin.db.get_listeners(
                context, filters={'loadbalancer_id': [loadbalancer.id]})
            for listener in listeners:
                listener_dict = listener.to_dict(loadbalancer=False,
                                                 default_pool=False)
                if listener.default_pool:
                    listener_dict['default_pool_id'] = listener.default_pool.id

                service['listeners'].append(listener_dict)

                if listener.default_pool:
                    pool = self.plugin.db.get_pool(context,
                                                   listener.default_pool.id)
                    pool_dict = pool.to_api_dict()
                    pool_dict['provisioning_status'] = pool.provisioning_status
                    pool_dict['operating_status'] = pool.operating_status
                    service['pools'].append(pool_dict)

            # Pools have multiple members and one healthmonitor.  Iterate
            # over the list of pools, and popuate the service with members
            # and healthmonitors.
            service['members'] = []
            service['healthmonitors'] = []
            for pool in service['pools']:
                pool_id = pool['id']
                members = self.plugin.db.get_pool_members(
                    context, filters={'pool_id': [pool_id]})
                for member in members:
                    # Get extended member attributes, network, and subnet.
                    (member_dict, subnet,
                     network) = (self._get_extended_member(context, member))
                    subnet_map[subnet['id']] = subnet
                    network_map[network['id']] = network
                    service['members'].append(member_dict)

                healthmonitor_id = pool['healthmonitor_id']
                if healthmonitor_id:
                    healthmonitor = self.plugin.db.get_healthmonitor(
                        context, healthmonitor_id)
                    if healthmonitor:
                        healthmonitor_dict = healthmonitor.to_dict(pool=False)
                        healthmonitor_dict['pool_id'] = pool_id
                        service['healthmonitors'].append(healthmonitor_dict)

            service['subnets'] = subnet_map
            service['networks'] = network_map

        return service

    @log_helpers.log_method_call
    def _get_extended_member(self, context, member):
        """Get extended member attributes and member networking."""
        member_dict = member.to_dict(pool=False)
        subnet_id = member.subnet_id
        subnet = self._get_subnet_cached(context, subnet_id)
        network_id = subnet['network_id']
        network = self._get_network_cached(context, network_id)

        member_dict['network_id'] = network_id

        # Use the fixed ip.
        filter = {
            'fixed_ips': {
                'subnet_id': [subnet_id],
                'ip_address': [member.address]
            }
        }
        ports = self.plugin.db._core_plugin.get_ports(context, filter)

        # There should be only one.
        if len(ports) == 1:
            member_dict['port'] = ports[0]
            self._populate_member_network(context, member_dict, network)
        else:
            # FIXME(RJB: raise an exception here and let the driver handle
            # the port that is not on the network.
            LOG.error("Unexpected number of ports returned for member: ")
            if not ports:
                LOG.error("No port found")
            else:
                LOG.error("Multiple ports found: %s" % ports)

        return (member_dict, subnet, network)

    @log_helpers.log_method_call
    def _get_extended_loadbalancer(self, context, loadbalancer):
        """Get loadbalancer dictionary and add extended data(e.g. VIP)."""
        loadbalancer_dict = loadbalancer.to_api_dict()
        vip_port = self.plugin.db._core_plugin.get_port(
            context, loadbalancer.vip_port_id)
        loadbalancer_dict['vip_port'] = vip_port

        return loadbalancer_dict

    @log_helpers.log_method_call
    def _get_subnet_cached(self, context, subnet_id):
        """Retrieve subnet from cache if available; otherwise, from Neutron."""
        if subnet_id not in self.subnet_cache:
            subnet = self.plugin.db._core_plugin.get_subnet(context, subnet_id)
            self.subnet_cache[subnet_id] = subnet
        return self.subnet_cache[subnet_id]

    @log_helpers.log_method_call
    def _get_network_cached(self, context, network_id):
        """Retrieve network from cache or from Neutron."""
        if network_id not in self.net_cache:
            network = self.plugin.db._core_plugin.get_network(
                context, network_id)
            if 'provider:network_type' not in network:
                network['provider:network_type'] = 'undefined'
            if 'provider:segmentation_id' not in network:
                network['provider:segmentation_id'] = 0
            self.net_cache[network_id] = network

        return self.net_cache[network_id]

    @log_helpers.log_method_call
    def _get_listener(self, context, listener_id):
        """Retrieve listener from Neutron db."""
        listener = self.plugin.db.get_listener(context, listener_id)
        return listener.to_api_dict()

    def _populate_member_network(self, context, member, network):
        """Add vtep networking info to pool member and update the network."""
        member['vxlan_vteps'] = []
        member['gre_vteps'] = []

        if 'provider:network_type' in network:
            net_type = network['provider:network_type']
            if net_type == 'vxlan':
                if 'binding:host_id' in member['port']:
                    host = member['port']['binding:host_id']
                    member['vxlan_vteps'] = self._get_endpoints(
                        context, 'vxlan', host)
            if net_type == 'gre':
                if 'binding:host_id' in member['port']:
                    host = member['port']['binding:host_id']
                    member['gre_vteps'] = self._get_endpoints(
                        context, 'gre', host)
        if 'provider:network_type' not in network:
            network['provider:network_type'] = 'undefined'
        if 'provider:segmentation_id' not in network:
            network['provider:segmentation_id'] = 0

    @log_helpers.log_method_call
    def _populate_loadbalancer_network_vteps(self, context, loadbalancer,
                                             net_type):
        """Put related tunnel endpoints in loadbalancer definiton."""
        loadbalancer['vxlan_vteps'] = []
        loadbalancer['gre_vteps'] = []
        network_id = loadbalancer['vip_port']['network_id']

        ports = self._get_ports_on_network(context, network_id=network_id)

        vtep_hosts = []
        for port in ports:
            if ('binding:host_id' in port
                    and port['binding:host_id'] not in vtep_hosts):
                vtep_hosts.append(port['binding:host_id'])

        for vtep_host in vtep_hosts:
            if net_type == 'vxlan':
                endpoints = self._get_endpoints(context, 'vxlan')
                for ep in endpoints:
                    if ep not in loadbalancer['vxlan_vteps']:
                        loadbalancer['vxlan_vteps'].append(ep)
            elif net_type == 'gre':
                endpoints = self._get_endpoints(context, 'gre')
                for ep in endpoints:
                    if ep not in loadbalancer['gre_vteps']:
                        loadbalancer['gre_vteps'].append(ep)

    def _get_endpoints(self, context, net_type, host=None):
        """Get vxlan or gre tunneling endpoints from all agents."""
        endpoints = []

        agents = self.plugin.db._core_plugin.get_agents(context)
        for agent in agents:
            if ('configurations' in agent
                    and ('tunnel_types' in agent['configurations'])):

                if net_type in agent['configurations']['tunnel_types']:
                    if 'tunneling_ip' in agent['configurations']:
                        if not host or (agent['host'] == host):
                            endpoints.append(
                                agent['configurations']['tunneling_ip'])
                    if 'tunneling_ips' in agent['configurations']:
                        for ip_addr in (
                                agent['configurations']['tunneling_ips']):
                            if not host or (agent['host'] == host):
                                endpoints.append(ip_addr)

        return endpoints

    def deserialize_agent_configurations(self, configurations):
        """Return a dictionary for the agent configuration."""
        agent_conf = configurations
        if not isinstance(agent_conf, dict):
            try:
                agent_conf = json.loads(configurations)
            except ValueError as ve:
                LOG.error('can not JSON decode %s : %s' %
                          (agent_conf, ve.message))
                agent_conf = {}
        return agent_conf

    @log_helpers.log_method_call
    def _is_common_network(self, network, agent):
        if agent and "configurations" in agent:
            agent_configs = self.deserialize_agent_configurations(
                agent['configurations'])

            if 'common_networks' in agent_configs:
                common_networks = agent_configs['common_networks']
            else:
                common_networks = {}
            common_external_networks = (
                agent_configs['f5_common_external_networks'])

            return (network['shared'] or (network['id'] in common_networks)
                    or ('router:external' in network
                        and network['router:external']
                        and common_external_networks))

    def _valid_tenant_ids(self, network, lb_tenant_id, agent):
        if (network['tenant_id'] == lb_tenant_id):
            return True
        else:
            return self._is_common_network(network, agent)

    @log_helpers.log_method_call
    def _get_ports_on_network(self, context, network_id=None):
        """Get ports for network."""
        if not isinstance(network_id, list):
            network_ids = [network_id]
        filters = {'network_id': network_ids}
        return self.driver.plugin.db._core_plugin.get_ports(context,
                                                            filters=filters)
Exemple #4
0
class LBaaSv2ServiceBuilder(object):
    """The class creates a service definition from neutron database.

    A service definition represents all the information required to
    construct a load-balancing service on BigIP.

    Requests come in to agent as full service definitions, not incremental
    changes. The driver looks up networks, mac entries, segmentation info,
    etc and places all information in a service object (which is a python
    dictionary variable) and passes that to the agent.

    """
    def __init__(self, driver):
        """Get full service definition from loadbalancer id."""
        self.driver = driver

        self.net_cache = {}
        self.subnet_cache = {}
        self.last_cache_update = datetime.datetime.fromtimestamp(0)
        self.plugin = self.driver.plugin
        self.disconnected_service = DisconnectedService()
        self.q_client = q_client.F5NetworksNeutronClient(self.plugin)

    def build(self, context, loadbalancer, agent):
        """Get full service definition from loadbalancer ID."""
        # Invalidate cache if it is too old
        if ((datetime.datetime.now() - self.last_cache_update).seconds >
                constants_v2.NET_CACHE_SECONDS):
            self.net_cache = {}
            self.subnet_cache = {}

        service = {}
        with context.session.begin(subtransactions=True):
            LOG.debug('Building service definition entry for %s' %
                      loadbalancer.id)

            # Start with the neutron loadbalancer definition
            service['loadbalancer'] = self._get_extended_loadbalancer(
                context, loadbalancer)

            # Get the subnet network associated with the VIP.
            subnet_map = {}
            subnet_id = loadbalancer.vip_subnet_id
            vip_subnet = self._get_subnet_cached(context, subnet_id)
            subnet_map[subnet_id] = vip_subnet

            # Get the network associated with the Loadbalancer.
            network_map = {}
            vip_port = service['loadbalancer']['vip_port']
            network_id = vip_port['network_id']
            service['loadbalancer']['network_id'] = network_id
            network = self._get_network_cached(context, network_id)
            # Override the segmentation ID and network type for this network
            # if we are running in disconnected service mode
            agent_config = self.deserialize_agent_configurations(
                agent['configurations'])
            segment_data = self.disconnected_service.get_network_segment(
                context, agent_config, network)
            if segment_data:
                network['provider:segmentation_id'] = \
                    segment_data.get('segmentation_id', None)
                network['provider:network_type'] = \
                    segment_data.get('network_type', None)
                network['provider:physical_network'] = \
                    segment_data.get('physical_network', None)
            network_map[network_id] = network

            # Check if the tenant can create a loadbalancer on the network.
            if (agent and not self._valid_tenant_ids(
                    network, loadbalancer.tenant_id, agent)):
                LOG.error("Creating a loadbalancer %s for tenant %s on a"
                          "  non-shared network %s owned by %s." %
                          (loadbalancer.id, loadbalancer.tenant_id,
                           network['id'], network['tenant_id']))

            # Get the network VTEPs if the network provider type is
            # either gre or vxlan.
            if 'provider:network_type' in network:
                net_type = network['provider:network_type']
                if net_type == 'vxlan' or net_type == 'gre':
                    self._populate_loadbalancer_network_vteps(
                        context, service['loadbalancer'], net_type)

            # Get listeners and pools.
            service['listeners'] = self._get_listeners(context, loadbalancer)

            service['pools'], service['healthmonitors'] = \
                self._get_pools_and_healthmonitors(context, loadbalancer)

            service['members'] = self._get_members(context, service['pools'],
                                                   subnet_map, network_map)

            service['subnets'] = subnet_map
            service['networks'] = network_map

            service['l7policies'] = self._get_l7policies(
                context, service['listeners'])
            service['l7policy_rules'] = self._get_l7policy_rules(
                context, service['l7policies'])

        return service

    @log_helpers.log_method_call
    def _get_extended_member(self, context, member):
        """Get extended member attributes and member networking."""
        member_dict = member.to_dict(pool=False)
        subnet_id = member.subnet_id
        subnet = self._get_subnet_cached(context, subnet_id)
        network_id = subnet['network_id']
        network = self._get_network_cached(context, network_id)

        member_dict['network_id'] = network_id

        # Use the fixed ip.
        filter = {
            'fixed_ips': {
                'subnet_id': [subnet_id],
                'ip_address': [member.address]
            }
        }
        ports = self.plugin.db._core_plugin.get_ports(context, filter)

        # we no longer support member port creation
        if len(ports) == 1:
            member_dict['port'] = ports[0]
            self._populate_member_network(context, member_dict, network)
        elif len(ports) == 0:
            LOG.warning("Lbaas member %s has no associated neutron port" %
                        member.address)
        elif len(ports) > 1:
            LOG.warning("Multiple ports found for member: %s" % member.address)

        return (member_dict, subnet, network)

    @log_helpers.log_method_call
    def _get_extended_loadbalancer(self, context, loadbalancer):
        """Get loadbalancer dictionary and add extended data(e.g. VIP)."""
        loadbalancer_dict = loadbalancer.to_api_dict()
        vip_port = self.plugin.db._core_plugin.get_port(
            context, loadbalancer.vip_port_id)
        loadbalancer_dict['vip_port'] = vip_port

        return loadbalancer_dict

    @log_helpers.log_method_call
    def _get_subnet_cached(self, context, subnet_id):
        """Retrieve subnet from cache if available; otherwise, from Neutron."""
        if subnet_id not in self.subnet_cache:
            subnet = self.plugin.db._core_plugin.get_subnet(context, subnet_id)
            self.subnet_cache[subnet_id] = subnet
        return self.subnet_cache[subnet_id]

    @log_helpers.log_method_call
    def _get_network_cached(self, context, network_id):
        """Retrieve network from cache or from Neutron."""
        if network_id not in self.net_cache:
            network = self.plugin.db._core_plugin.get_network(
                context, network_id)
            if 'provider:network_type' not in network:
                network['provider:network_type'] = 'undefined'
            if 'provider:segmentation_id' not in network:
                network['provider:segmentation_id'] = 0
            self.net_cache[network_id] = network

        return self.net_cache[network_id]

    @log_helpers.log_method_call
    def _get_listener(self, context, listener_id):
        """Retrieve listener from Neutron db."""
        listener = self.plugin.db.get_listener(context, listener_id)
        return listener.to_api_dict()

    def _populate_member_network(self, context, member, network):
        """Add vtep networking info to pool member and update the network."""
        member['vxlan_vteps'] = []
        member['gre_vteps'] = []

        agent_config = {}
        segment_data = self.disconnected_service.get_network_segment(
            context, agent_config, network)
        if segment_data:
            network['provider:segmentation_id'] = \
                segment_data.get('segmentation_id', None)
            network['provider:network_type'] = \
                segment_data.get('network_type', None)
            network['provider:physical_network'] = \
                segment_data.get('physical_network', None)

        net_type = network.get('provider:network_type', "undefined")
        if net_type == 'vxlan':
            if 'binding:host_id' in member['port']:
                host = member['port']['binding:host_id']
                member['vxlan_vteps'] = self._get_endpoints(
                    context, 'vxlan', host)
        if net_type == 'gre':
            if 'binding:host_id' in member['port']:
                host = member['port']['binding:host_id']
                member['gre_vteps'] = self._get_endpoints(context, 'gre', host)
        if 'provider:network_type' not in network:
            network['provider:network_type'] = 'undefined'
        if 'provider:segmentation_id' not in network:
            network['provider:segmentation_id'] = 0

    @log_helpers.log_method_call
    def _populate_loadbalancer_network_vteps(self, context, loadbalancer,
                                             net_type):
        """Put related tunnel endpoints in loadbalancer definiton."""
        loadbalancer['vxlan_vteps'] = []
        loadbalancer['gre_vteps'] = []
        network_id = loadbalancer['vip_port']['network_id']

        ports = self._get_ports_on_network(context, network_id=network_id)

        vtep_hosts = []
        for port in ports:
            if ('binding:host_id' in port
                    and port['binding:host_id'] not in vtep_hosts):
                vtep_hosts.append(port['binding:host_id'])

        for vtep_host in vtep_hosts:
            if net_type == 'vxlan':
                endpoints = self._get_endpoints(context, 'vxlan')
                for ep in endpoints:
                    if ep not in loadbalancer['vxlan_vteps']:
                        loadbalancer['vxlan_vteps'].append(ep)
            elif net_type == 'gre':
                endpoints = self._get_endpoints(context, 'gre')
                for ep in endpoints:
                    if ep not in loadbalancer['gre_vteps']:
                        loadbalancer['gre_vteps'].append(ep)

    def _get_endpoints(self, context, net_type, host=None):
        """Get vxlan or gre tunneling endpoints from all agents."""
        endpoints = []

        agents = self.plugin.db._core_plugin.get_agents(context)
        for agent in agents:
            if ('configurations' in agent
                    and ('tunnel_types' in agent['configurations'])):

                if net_type in agent['configurations']['tunnel_types']:
                    if 'tunneling_ip' in agent['configurations']:
                        if not host or (agent['host'] == host):
                            endpoints.append(
                                agent['configurations']['tunneling_ip'])
                    if 'tunneling_ips' in agent['configurations']:
                        for ip_addr in (
                                agent['configurations']['tunneling_ips']):
                            if not host or (agent['host'] == host):
                                endpoints.append(ip_addr)

        return endpoints

    def deserialize_agent_configurations(self, configurations):
        """Return a dictionary for the agent configuration."""
        agent_conf = configurations
        if not isinstance(agent_conf, dict):
            try:
                agent_conf = json.loads(configurations)
            except ValueError as ve:
                LOG.error('can not JSON decode %s : %s' %
                          (agent_conf, ve.message))
                agent_conf = {}
        return agent_conf

    @log_helpers.log_method_call
    def _is_common_network(self, network, agent):
        common_external_networks = False
        common_networks = {}

        if agent and "configurations" in agent:
            agent_configs = self.deserialize_agent_configurations(
                agent['configurations'])

            if 'common_networks' in agent_configs:
                common_networks = agent_configs['common_networks']

            if 'f5_common_external_networks' in agent_configs:
                common_external_networks = (
                    agent_configs['f5_common_external_networks'])

        return (network['shared'] or (network['id'] in common_networks)
                or ('router:external' in network and network['router:external']
                    and common_external_networks))

    def _valid_tenant_ids(self, network, lb_tenant_id, agent):
        if (network['tenant_id'] == lb_tenant_id):
            return True
        else:
            return self._is_common_network(network, agent)

    @log_helpers.log_method_call
    def _get_ports_on_network(self, context, network_id=None):
        """Get ports for network."""
        if not isinstance(network_id, list):
            network_ids = [network_id]
        filters = {'network_id': network_ids}
        return self.driver.plugin.db._core_plugin.get_ports(context,
                                                            filters=filters)

    @log_helpers.log_method_call
    def _get_l7policies(self, context, listeners):
        """Get l7 policies filtered by listeners."""
        l7policies = []
        if listeners:
            listener_ids = [l['id'] for l in listeners]
            policies = self.plugin.db.get_l7policies(
                context, filters={'listener_id': listener_ids})
            l7policies.extend(self._l7policy_to_dict(p) for p in policies)

        for index, pol in enumerate(l7policies):
            try:
                assert len(pol['listeners']) == 1
            except AssertionError:
                msg = 'A policy should have only one listener, but found ' \
                    '{0} for policy {1}'.format(
                        len(pol['listeners']), pol['id'])
                raise f5_exc.PolicyHasMoreThanOneListener(msg)
            else:
                listener = pol.pop('listeners')[0]
                l7policies[index]['listener_id'] = listener['id']

        return l7policies

    @log_helpers.log_method_call
    def _get_l7policy_rules(self, context, l7policies):
        """Get l7 policy rules filtered by l7 policies."""
        l7policy_rules = []
        if l7policies:
            policy_ids = [p['id'] for p in l7policies]
            for pol_id in policy_ids:
                rules = self.plugin.db.get_l7policy_rules(context, pol_id)
                l7policy_rules.extend(
                    self._l7rule_to_dict(rule) for rule in rules)

        for index, rule in enumerate(l7policy_rules):
            try:
                assert len(rule['policies']) == 1
            except AssertionError:
                msg = 'A rule should have only one policy, but found ' \
                    '{0} for rule {1}'.format(
                        len(rule['policies']), rule['id'])
                raise f5_exc.RuleHasMoreThanOnePolicy(msg)
            else:
                pol = rule['policies'][0]
                l7policy_rules[index]['policy_id'] = pol['id']

        return l7policy_rules

    @log_helpers.log_method_call
    def _get_listeners(self, context, loadbalancer):
        listeners = []
        db_listeners = self.plugin.db.get_listeners(
            context, filters={'loadbalancer_id': [loadbalancer.id]})

        for listener in db_listeners:
            listener_dict = listener.to_dict(loadbalancer=False,
                                             default_pool=False,
                                             l7_policies=False)
            listener_dict['l7_policies'] = \
                [{'id': l7_policy.id} for l7_policy in listener.l7_policies]
            if listener.default_pool:
                listener_dict['default_pool_id'] = listener.default_pool.id

            listeners.append(listener_dict)

        return listeners

    @log_helpers.log_method_call
    def _get_pools_and_healthmonitors(self, context, loadbalancer):
        """Return list of pools and list of healthmonitors as dicts."""
        healthmonitors = []
        pools = []

        if loadbalancer and loadbalancer.id:
            db_pools = self.plugin.db.get_pools(
                context, filters={'loadbalancer_id': [loadbalancer.id]})

            for pool in db_pools:
                pools.append(self._pool_to_dict(pool))
                pool_id = pool.id
                healthmonitor_id = pool.healthmonitor_id
                if healthmonitor_id:
                    healthmonitor = self.plugin.db.get_healthmonitor(
                        context, healthmonitor_id)
                    if healthmonitor:
                        healthmonitor_dict = healthmonitor.to_dict(pool=False)
                        healthmonitor_dict['pool_id'] = pool_id
                        healthmonitors.append(healthmonitor_dict)

        return pools, healthmonitors

    @log_helpers.log_method_call
    def _get_members(self, context, pools, subnet_map, network_map):
        pool_members = []
        if pools:
            members = self.plugin.db.get_pool_members(
                context, filters={'pool_id': [p['id'] for p in pools]})

            for member in members:
                # Get extended member attributes, network, and subnet.
                member_dict, subnet, network = (self._get_extended_member(
                    context, member))

                subnet_map[subnet['id']] = subnet
                network_map[network['id']] = network
                pool_members.append(member_dict)

        return pool_members

    @log_helpers.log_method_call
    def _pool_to_dict(self, pool):
        """Convert Pool data model to dict.

        Provides an alternative to_api_dict() in order to get additional
        object IDs without exploding object references.
        """

        pool_dict = pool.to_dict(healthmonitor=False,
                                 listener=False,
                                 listeners=False,
                                 loadbalancer=False,
                                 l7_policies=False,
                                 members=False,
                                 session_persistence=False)

        pool_dict['members'] = [{'id': member.id} for member in pool.members]
        pool_dict['listeners'] = [{
            'id': listener.id
        } for listener in pool.listeners]
        pool_dict['l7_policies'] = [{
            'id': l7_policy.id
        } for l7_policy in pool.l7_policies]
        if pool.session_persistence:
            pool_dict['session_persistence'] = (
                pool.session_persistence.to_api_dict())
        if pool.listener:
            pool_dict['listener_id'] = pool.listener.id
        else:
            pool_dict['listener_id'] = None

        return pool_dict

    def _l7policy_to_dict(self, l7policy):
        """Convert l7Policy to dict.

        Adds provisioning_status to dict from to_api_dict()
        """
        l7policy_dict = l7policy.to_api_dict()
        l7policy_dict['provisioning_status'] = l7policy.provisioning_status
        return l7policy_dict

    def _l7rule_to_dict(self, l7rule):
        """Convert l7Policy rule to dict.

        Adds provisioning_status to dict from to_api_dict()
        """
        l7rule_dict = l7rule.to_api_dict()
        l7rule_dict['provisioning_status'] = l7rule.provisioning_status
        return l7rule_dict
class LBaaSv2ServiceBuilder(object):
    """The class creates a service definition from neutron database.

    A service definition represents all the information required to
    construct a load-balancing service on BigIP.

    Requests come in to agent as full service definitions, not incremental
    changes. The driver looks up networks, mac entries, segmentation info,
    etc and places all information in a service object (which is a python
    dictionary variable) and passes that to the agent.

    """

    def __init__(self, driver):
        """Get full service definition from loadbalancer id."""
        self.driver = driver

        self.net_cache = {}
        self.subnet_cache = {}
        self.last_cache_update = datetime.datetime.fromtimestamp(0)
        self.plugin = self.driver.plugin
        self.disconnected_service = DisconnectedService()

    def build(self, context, loadbalancer, agent):
        """Get full service definition from loadbalancer ID."""
        # Invalidate cache if it is too old
        if (datetime.datetime.now() - self.last_cache_update).seconds > constants_v2.NET_CACHE_SECONDS:
            self.net_cache = {}
            self.subnet_cache = {}

        service = {}
        with context.session.begin(subtransactions=True):
            LOG.debug("Building service definition entry for %s" % loadbalancer.id)

            # Start with the neutron loadbalancer definition
            service["loadbalancer"] = self._get_extended_loadbalancer(context, loadbalancer)

            # Get the subnet network associated with the VIP.
            subnet_map = {}
            subnet_id = loadbalancer.vip_subnet_id
            vip_subnet = self._get_subnet_cached(context, subnet_id)
            subnet_map[subnet_id] = vip_subnet

            # Get the network associated with the Loadbalancer.
            network_map = {}
            vip_port = service["loadbalancer"]["vip_port"]
            network_id = vip_port["network_id"]
            service["loadbalancer"]["network_id"] = network_id
            network = self._get_network_cached(context, network_id)
            # Override the segmentation ID and network type for this network
            # if we are running in disconnected service mode
            agent_config = self.deserialize_agent_configurations(agent["configurations"])
            segment_data = self.disconnected_service.get_network_segment(context, agent_config, network)
            if segment_data:
                network["provider:segmentation_id"] = segment_data.get("segmentation_id", None)
                if "provider:network_type" in network:
                    network["provider:network_type"] = segment_data.get("network_type", None)
            network_map[network_id] = network

            # Check if the tenant can create a loadbalancer on the network.
            if agent and not self._valid_tenant_ids(network, loadbalancer.tenant_id, agent):
                LOG.error(
                    "Creating a loadbalancer %s for tenant %s on a"
                    "  non-shared network %s owned by %s."
                    % (loadbalancer.id, loadbalancer.tenant_id, network["id"], network["tenant_id"])
                )
                raise f5_exc.F5MismatchedTenants()

            # Get the network VTEPs if the network provider type is
            # either gre or vxlan.
            if "provider:network_type" in network:
                net_type = network["provider:network_type"]
                if net_type == "vxlan" or net_type == "gre":
                    self._populate_loadbalancer_network_vteps(context, service["loadbalancer"], net_type)

            # Get listeners and pools.
            service["listeners"] = []
            service["pools"] = []
            listeners = self.plugin.db.get_listeners(context, filters={"loadbalancer_id": [loadbalancer.id]})
            for listener in listeners:
                listener_dict = listener.to_dict(loadbalancer=False, default_pool=False)
                if listener.default_pool:
                    listener_dict["default_pool_id"] = listener.default_pool.id

                service["listeners"].append(listener_dict)

                if listener.default_pool:
                    pool = self.plugin.db.get_pool(context, listener.default_pool.id)
                    pool_dict = pool.to_api_dict()
                    pool_dict["provisioning_status"] = pool.provisioning_status
                    pool_dict["operating_status"] = pool.operating_status
                    service["pools"].append(pool_dict)

            # Pools have multiple members and one healthmonitor.  Iterate
            # over the list of pools, and popuate the service with members
            # and healthmonitors.
            service["members"] = []
            service["healthmonitors"] = []
            for pool in service["pools"]:
                pool_id = pool["id"]
                members = self.plugin.db.get_pool_members(context, filters={"pool_id": [pool_id]})
                for member in members:
                    # Get extended member attributes, network, and subnet.
                    (member_dict, subnet, network) = self._get_extended_member(context, member)
                    subnet_map[subnet["id"]] = subnet
                    network_map[network["id"]] = network
                    service["members"].append(member_dict)

                healthmonitor_id = pool["healthmonitor_id"]
                if healthmonitor_id:
                    healthmonitor = self.plugin.db.get_healthmonitor(context, healthmonitor_id)
                    if healthmonitor:
                        healthmonitor_dict = healthmonitor.to_dict(pool=False)
                        healthmonitor_dict["pool_id"] = pool_id
                        service["healthmonitors"].append(healthmonitor_dict)

            service["subnets"] = subnet_map
            service["networks"] = network_map

        return service

    @log_helpers.log_method_call
    def _get_extended_member(self, context, member):
        """Get extended member attributes and member networking."""
        member_dict = member.to_dict(pool=False)
        subnet_id = member.subnet_id
        subnet = self._get_subnet_cached(context, subnet_id)
        network_id = subnet["network_id"]
        network = self._get_network_cached(context, network_id)

        member_dict["network_id"] = network_id

        # Use the fixed ip.
        filter = {"fixed_ips": {"subnet_id": [subnet_id], "ip_address": [member.address]}}
        ports = self.plugin.db._core_plugin.get_ports(context, filter)

        # There should be only one.
        if len(ports) == 1:
            member_dict["port"] = ports[0]
            self._populate_member_network(context, member_dict, network)
        else:
            # FIXME(RJB: raise an exception here and let the driver handle
            # the port that is not on the network.
            LOG.error("Unexpected number of ports returned for member: ")
            if not ports:
                LOG.error("No port found")
            else:
                LOG.error("Multiple ports found: %s" % ports)

        return (member_dict, subnet, network)

    @log_helpers.log_method_call
    def _get_extended_loadbalancer(self, context, loadbalancer):
        """Get loadbalancer dictionary and add extended data(e.g. VIP)."""
        loadbalancer_dict = loadbalancer.to_api_dict()
        vip_port = self.plugin.db._core_plugin.get_port(context, loadbalancer.vip_port_id)
        loadbalancer_dict["vip_port"] = vip_port

        return loadbalancer_dict

    @log_helpers.log_method_call
    def _get_subnet_cached(self, context, subnet_id):
        """Retrieve subnet from cache if available; otherwise, from Neutron."""
        if subnet_id not in self.subnet_cache:
            subnet = self.plugin.db._core_plugin.get_subnet(context, subnet_id)
            self.subnet_cache[subnet_id] = subnet
        return self.subnet_cache[subnet_id]

    @log_helpers.log_method_call
    def _get_network_cached(self, context, network_id):
        """Retrieve network from cache or from Neutron."""
        if network_id not in self.net_cache:
            network = self.plugin.db._core_plugin.get_network(context, network_id)
            if "provider:network_type" not in network:
                network["provider:network_type"] = "undefined"
            if "provider:segmentation_id" not in network:
                network["provider:segmentation_id"] = 0
            self.net_cache[network_id] = network

        return self.net_cache[network_id]

    @log_helpers.log_method_call
    def _get_listener(self, context, listener_id):
        """Retrieve listener from Neutron db."""
        listener = self.plugin.db.get_listener(context, listener_id)
        return listener.to_api_dict()

    def _populate_member_network(self, context, member, network):
        """Add vtep networking info to pool member and update the network."""
        member["vxlan_vteps"] = []
        member["gre_vteps"] = []

        if "provider:network_type" in network:
            net_type = network["provider:network_type"]
            if net_type == "vxlan":
                if "binding:host_id" in member["port"]:
                    host = member["port"]["binding:host_id"]
                    member["vxlan_vteps"] = self._get_endpoints(context, "vxlan", host)
            if net_type == "gre":
                if "binding:host_id" in member["port"]:
                    host = member["port"]["binding:host_id"]
                    member["gre_vteps"] = self._get_endpoints(context, "gre", host)
        if "provider:network_type" not in network:
            network["provider:network_type"] = "undefined"
        if "provider:segmentation_id" not in network:
            network["provider:segmentation_id"] = 0

    @log_helpers.log_method_call
    def _populate_loadbalancer_network_vteps(self, context, loadbalancer, net_type):
        """Put related tunnel endpoints in loadbalancer definiton."""
        loadbalancer["vxlan_vteps"] = []
        loadbalancer["gre_vteps"] = []
        network_id = loadbalancer["vip_port"]["network_id"]

        ports = self._get_ports_on_network(context, network_id=network_id)

        vtep_hosts = []
        for port in ports:
            if "binding:host_id" in port and port["binding:host_id"] not in vtep_hosts:
                vtep_hosts.append(port["binding:host_id"])

        for vtep_host in vtep_hosts:
            if net_type == "vxlan":
                endpoints = self._get_endpoints(context, "vxlan")
                for ep in endpoints:
                    if ep not in loadbalancer["vxlan_vteps"]:
                        loadbalancer["vxlan_vteps"].append(ep)
            elif net_type == "gre":
                endpoints = self._get_endpoints(context, "gre")
                for ep in endpoints:
                    if ep not in loadbalancer["gre_vteps"]:
                        loadbalancer["gre_vteps"].append(ep)

    def _get_endpoints(self, context, net_type, host=None):
        """Get vxlan or gre tunneling endpoints from all agents."""
        endpoints = []

        agents = self.plugin.db._core_plugin.get_agents(context)
        for agent in agents:
            if "configurations" in agent and ("tunnel_types" in agent["configurations"]):

                if net_type in agent["configurations"]["tunnel_types"]:
                    if "tunneling_ip" in agent["configurations"]:
                        if not host or (agent["host"] == host):
                            endpoints.append(agent["configurations"]["tunneling_ip"])
                    if "tunneling_ips" in agent["configurations"]:
                        for ip_addr in agent["configurations"]["tunneling_ips"]:
                            if not host or (agent["host"] == host):
                                endpoints.append(ip_addr)

        return endpoints

    def deserialize_agent_configurations(self, configurations):
        """Return a dictionary for the agent configuration."""
        agent_conf = configurations
        if not isinstance(agent_conf, dict):
            try:
                agent_conf = json.loads(configurations)
            except ValueError as ve:
                LOG.error("can not JSON decode %s : %s" % (agent_conf, ve.message))
                agent_conf = {}
        return agent_conf

    @log_helpers.log_method_call
    def _is_common_network(self, network, agent):
        common_external_networks = False
        common_networks = {}

        if agent and "configurations" in agent:
            agent_configs = self.deserialize_agent_configurations(agent["configurations"])

            if "common_networks" in agent_configs:
                common_networks = agent_configs["common_networks"]

            if "f5_common_external_networks" in agent_configs:
                common_external_networks = agent_configs["f5_common_external_networks"]

        return (
            network["shared"]
            or (network["id"] in common_networks)
            or ("router:external" in network and network["router:external"] and common_external_networks)
        )

    def _valid_tenant_ids(self, network, lb_tenant_id, agent):
        if network["tenant_id"] == lb_tenant_id:
            return True
        else:
            return self._is_common_network(network, agent)

    @log_helpers.log_method_call
    def _get_ports_on_network(self, context, network_id=None):
        """Get ports for network."""
        if not isinstance(network_id, list):
            network_ids = [network_id]
        filters = {"network_id": network_ids}
        return self.driver.plugin.db._core_plugin.get_ports(context, filters=filters)
class LBaaSv2ServiceBuilder(object):
    """The class creates a service definition from neutron database.

    A service definition represents all the information required to
    construct a load-balancing service on BigIP.

    Requests come in to agent as full service definitions, not incremental
    changes. The driver looks up networks, mac entries, segmentation info,
    etc and places all information in a service object (which is a python
    dictionary variable) and passes that to the agent.

    """

    def __init__(self, driver):
        """Get full service definition from loadbalancer id."""
        self.driver = driver

        self.net_cache = {}
        self.subnet_cache = {}
        self.last_cache_update = datetime.datetime.fromtimestamp(0)
        self.plugin = self.driver.plugin
        self.disconnected_service = DisconnectedService()
        self.q_client = q_client.F5NetworksNeutronClient(self.plugin)

    def build(self, context, loadbalancer, agent):
        """Get full service definition from loadbalancer ID."""
        # Invalidate cache if it is too old
        if ((datetime.datetime.now() - self.last_cache_update).seconds >
                constants_v2.NET_CACHE_SECONDS):
            self.net_cache = {}
            self.subnet_cache = {}

        service = {}
        with context.session.begin(subtransactions=True):
            LOG.debug('Building service definition entry for %s'
                      % loadbalancer.id)

            # Start with the neutron loadbalancer definition
            service['loadbalancer'] = self._get_extended_loadbalancer(
                context,
                loadbalancer
            )

            # Get the subnet network associated with the VIP.
            subnet_map = {}
            subnet_id = loadbalancer.vip_subnet_id
            vip_subnet = self._get_subnet_cached(
                context,
                subnet_id
            )
            subnet_map[subnet_id] = vip_subnet

            # Get the network associated with the Loadbalancer.
            network_map = {}
            vip_port = service['loadbalancer']['vip_port']
            network_id = vip_port['network_id']
            service['loadbalancer']['network_id'] = network_id
            network = self._get_network_cached(
                context,
                network_id
            )
            # Override the segmentation ID and network type for this network
            # if we are running in disconnected service mode
            agent_config = self.deserialize_agent_configurations(
                agent['configurations'])
            segment_data = self.disconnected_service.get_network_segment(
                context, agent_config, network)
            if segment_data:
                network['provider:segmentation_id'] = \
                    segment_data.get('segmentation_id', None)
                network['provider:network_type'] = \
                    segment_data.get('network_type', None)
                network['provider:physical_network'] = \
                    segment_data.get('physical_network', None)
            network_map[network_id] = network

            # Check if the tenant can create a loadbalancer on the network.
            if (agent and not self._valid_tenant_ids(network,
                                                     loadbalancer.tenant_id,
                                                     agent)):
                LOG.error("Creating a loadbalancer %s for tenant %s on a"
                          "  non-shared network %s owned by %s." % (
                              loadbalancer.id,
                              loadbalancer.tenant_id,
                              network['id'],
                              network['tenant_id']))

            # Get the network VTEPs if the network provider type is
            # either gre or vxlan.
            if 'provider:network_type' in network:
                net_type = network['provider:network_type']
                if net_type == 'vxlan' or net_type == 'gre':
                    self._populate_loadbalancer_network_vteps(
                        context,
                        service['loadbalancer'],
                        net_type
                    )

            # Get listeners and pools.
            service['listeners'] = self._get_listeners(context, loadbalancer)

            service['pools'], service['healthmonitors'] = \
                self._get_pools_and_healthmonitors(context, loadbalancer)

            service['members'] = self._get_members(
                context, service['pools'], subnet_map, network_map)

            service['subnets'] = subnet_map
            service['networks'] = network_map

            service['l7policies'] = self._get_l7policies(
                context, service['listeners'])
            service['l7policy_rules'] = self._get_l7policy_rules(
                context, service['l7policies'])

        return service

    @log_helpers.log_method_call
    def _get_extended_member(self, context, member):
        """Get extended member attributes and member networking."""
        member_dict = member.to_dict(pool=False)
        subnet_id = member.subnet_id
        subnet = self._get_subnet_cached(
            context,
            subnet_id
        )
        network_id = subnet['network_id']
        network = self._get_network_cached(
            context,
            network_id
        )

        member_dict['network_id'] = network_id

        # Use the fixed ip.
        filter = {'fixed_ips': {'subnet_id': [subnet_id],
                                'ip_address': [member.address]}}
        ports = self.plugin.db._core_plugin.get_ports(
            context,
            filter
        )

        # we no longer support member port creation
        if len(ports) == 1:
            member_dict['port'] = ports[0]
            self._populate_member_network(context, member_dict, network)
        elif len(ports) == 0:
            self._populate_member_network(context, member_dict, network)
            LOG.warning("Lbaas member %s has no associated neutron port"
                        % member.address)
        elif len(ports) > 1:
            LOG.warning("Multiple ports found for member: %s" % member.address)

        return (member_dict, subnet, network)

    @log_helpers.log_method_call
    def _get_extended_loadbalancer(self, context, loadbalancer):
        """Get loadbalancer dictionary and add extended data(e.g. VIP)."""
        loadbalancer_dict = loadbalancer.to_api_dict()
        vip_port = self.plugin.db._core_plugin.get_port(
            context,
            loadbalancer.vip_port_id
        )
        loadbalancer_dict['vip_port'] = vip_port

        return loadbalancer_dict

    @log_helpers.log_method_call
    def _get_subnet_cached(self, context, subnet_id):
        """Retrieve subnet from cache if available; otherwise, from Neutron."""
        if subnet_id not in self.subnet_cache:
            subnet = self.plugin.db._core_plugin.get_subnet(
                context,
                subnet_id
            )
            self.subnet_cache[subnet_id] = subnet
        return self.subnet_cache[subnet_id]

    @log_helpers.log_method_call
    def _get_network_cached(self, context, network_id):
        """Retrieve network from cache or from Neutron."""
        if network_id not in self.net_cache:
            network = self.plugin.db._core_plugin.get_network(
                context,
                network_id
            )
            if 'provider:network_type' not in network:
                network['provider:network_type'] = 'undefined'
            if 'provider:segmentation_id' not in network:
                network['provider:segmentation_id'] = 0
            self.net_cache[network_id] = network

        return self.net_cache[network_id]

    def _populate_member_network(self, context, member, network):
        """Add vtep networking info to pool member and update the network."""
        member['vxlan_vteps'] = []
        member['gre_vteps'] = []

        agent_config = {}
        segment_data = self.disconnected_service.get_network_segment(
            context, agent_config, network)
        if segment_data:
            network['provider:segmentation_id'] = \
                segment_data.get('segmentation_id', None)
            network['provider:network_type'] = \
                segment_data.get('network_type', None)
            network['provider:physical_network'] = \
                segment_data.get('physical_network', None)

        net_type = network.get('provider:network_type', "undefined")
        if net_type == 'vxlan':
            if 'port' in member and 'binding:host_id' in member['port']:
                host = member['port']['binding:host_id']
                member['vxlan_vteps'] = self._get_endpoints(
                    context, 'vxlan', host)
        if net_type == 'gre':
            if 'port' in member and 'binding:host_id' in member['port']:
                host = member['port']['binding:host_id']
                member['gre_vteps'] = self._get_endpoints(
                    context, 'gre', host)
        if 'provider:network_type' not in network:
            network['provider:network_type'] = 'undefined'
        if 'provider:segmentation_id' not in network:
            network['provider:segmentation_id'] = 0

    @log_helpers.log_method_call
    def _populate_loadbalancer_network_vteps(
            self,
            context,
            loadbalancer,
            net_type):
        """Put related tunnel endpoints in loadbalancer definiton."""
        loadbalancer['vxlan_vteps'] = []
        loadbalancer['gre_vteps'] = []
        network_id = loadbalancer['vip_port']['network_id']

        ports = self._get_ports_on_network(
            context,
            network_id=network_id
        )

        vtep_hosts = []
        for port in ports:
            if ('binding:host_id' in port and
                    port['binding:host_id'] not in vtep_hosts):
                vtep_hosts.append(port['binding:host_id'])

        for vtep_host in vtep_hosts:
            if net_type == 'vxlan':
                endpoints = self._get_endpoints(context, 'vxlan')
                for ep in endpoints:
                    if ep not in loadbalancer['vxlan_vteps']:
                        loadbalancer['vxlan_vteps'].append(ep)
            elif net_type == 'gre':
                endpoints = self._get_endpoints(context, 'gre')
                for ep in endpoints:
                    if ep not in loadbalancer['gre_vteps']:
                        loadbalancer['gre_vteps'].append(ep)

    def _get_endpoints(self, context, net_type, host=None):
        """Get vxlan or gre tunneling endpoints from all agents."""
        endpoints = []

        agents = self.plugin.db._core_plugin.get_agents(context)
        for agent in agents:
            if ('configurations' in agent and (
                    'tunnel_types' in agent['configurations'])):

                if net_type in agent['configurations']['tunnel_types']:
                    if 'tunneling_ip' in agent['configurations']:
                        if not host or (agent['host'] == host):
                            endpoints.append(
                                agent['configurations']['tunneling_ip']
                            )
                    if 'tunneling_ips' in agent['configurations']:
                        for ip_addr in (
                                agent['configurations']['tunneling_ips']):
                            if not host or (agent['host'] == host):
                                endpoints.append(ip_addr)

        return endpoints

    def deserialize_agent_configurations(self, configurations):
        """Return a dictionary for the agent configuration."""
        agent_conf = configurations
        if not isinstance(agent_conf, dict):
            try:
                agent_conf = json.loads(configurations)
            except ValueError as ve:
                LOG.error('can not JSON decode %s : %s'
                          % (agent_conf, ve.message))
                agent_conf = {}
        return agent_conf

    @log_helpers.log_method_call
    def _is_common_network(self, network, agent):
        common_external_networks = False
        common_networks = {}

        if agent and "configurations" in agent:
            agent_configs = self.deserialize_agent_configurations(
                agent['configurations'])

            if 'common_networks' in agent_configs:
                common_networks = agent_configs['common_networks']

            if 'f5_common_external_networks' in agent_configs:
                common_external_networks = (
                    agent_configs['f5_common_external_networks'])

        return (network['shared'] or
                (network['id'] in common_networks) or
                ('router:external' in network and
                 network['router:external'] and
                 common_external_networks))

    def _valid_tenant_ids(self, network, lb_tenant_id, agent):
        if (network['tenant_id'] == lb_tenant_id):
            return True
        else:
            return self._is_common_network(network, agent)

    @log_helpers.log_method_call
    def _get_ports_on_network(self, context, network_id=None):
        """Get ports for network."""
        if not isinstance(network_id, list):
            network_ids = [network_id]
        filters = {'network_id': network_ids}
        return self.driver.plugin.db._core_plugin.get_ports(
            context,
            filters=filters
        )

    @log_helpers.log_method_call
    def _get_l7policies(self, context, listeners):
        """Get l7 policies filtered by listeners."""
        l7policies = []
        if listeners:
            listener_ids = [l['id'] for l in listeners]
            policies = self.plugin.db.get_l7policies(
                context, filters={'listener_id': listener_ids})
            l7policies.extend(self._l7policy_to_dict(p) for p in policies)

        for index, pol in enumerate(l7policies):
            try:
                assert len(pol['listeners']) == 1
            except AssertionError:
                msg = 'A policy should have only one listener, but found ' \
                    '{0} for policy {1}'.format(
                        len(pol['listeners']), pol['id'])
                raise f5_exc.PolicyHasMoreThanOneListener(msg)
            else:
                listener = pol.pop('listeners')[0]
                l7policies[index]['listener_id'] = listener['id']

        return l7policies

    @log_helpers.log_method_call
    def _get_l7policy_rules(self, context, l7policies):
        """Get l7 policy rules filtered by l7 policies."""
        l7policy_rules = []
        if l7policies:
            policy_ids = [p['id'] for p in l7policies]
            for pol_id in policy_ids:
                rules = self.plugin.db.get_l7policy_rules(context, pol_id)
                l7policy_rules.extend(
                    self._l7rule_to_dict(rule) for rule in rules)

        for index, rule in enumerate(l7policy_rules):
            try:
                assert len(rule['policies']) == 1
            except AssertionError:
                msg = 'A rule should have only one policy, but found ' \
                    '{0} for rule {1}'.format(
                        len(rule['policies']), rule['id'])
                raise f5_exc.RuleHasMoreThanOnePolicy(msg)
            else:
                pol = rule['policies'][0]
                l7policy_rules[index]['policy_id'] = pol['id']

        return l7policy_rules

    @log_helpers.log_method_call
    def _get_listeners(self, context, loadbalancer):
        listeners = []
        db_listeners = self.plugin.db.get_listeners(
            context,
            filters={'loadbalancer_id': [loadbalancer.id]}
        )

        for listener in db_listeners:
            listener_dict = listener.to_dict(
                loadbalancer=False,
                default_pool=False,
                l7_policies=False
            )
            listener_dict['l7_policies'] = \
                [{'id': l7_policy.id} for l7_policy in listener.l7_policies]
            if listener.default_pool:
                listener_dict['default_pool_id'] = listener.default_pool.id

            listeners.append(listener_dict)

        return listeners

    @log_helpers.log_method_call
    def _get_pools_and_healthmonitors(self, context, loadbalancer):
        """Return list of pools and list of healthmonitors as dicts."""
        healthmonitors = []
        pools = []

        if loadbalancer and loadbalancer.id:
            db_pools = self.plugin.db.get_pools(
                context,
                filters={'loadbalancer_id': [loadbalancer.id]}
            )

            for pool in db_pools:
                pools.append(self._pool_to_dict(pool))
                pool_id = pool.id
                healthmonitor_id = pool.healthmonitor_id
                if healthmonitor_id:
                    healthmonitor = self.plugin.db.get_healthmonitor(
                        context,
                        healthmonitor_id)
                    if healthmonitor:
                        healthmonitor_dict = healthmonitor.to_dict(pool=False)
                        healthmonitor_dict['pool_id'] = pool_id
                        healthmonitors.append(healthmonitor_dict)

        return pools, healthmonitors

    @log_helpers.log_method_call
    def _get_members(self, context, pools, subnet_map, network_map):
        pool_members = []
        if pools:
            members = self.plugin.db.get_pool_members(
                context,
                filters={'pool_id': [p['id'] for p in pools]}
            )

            for member in members:
                # Get extended member attributes, network, and subnet.
                member_dict, subnet, network = (
                    self._get_extended_member(context, member)
                )

                subnet_map[subnet['id']] = subnet
                network_map[network['id']] = network
                pool_members.append(member_dict)

        return pool_members

    @log_helpers.log_method_call
    def _pool_to_dict(self, pool):
        """Convert Pool data model to dict.

        Provides an alternative to_api_dict() in order to get additional
        object IDs without exploding object references.
        """

        pool_dict = pool.to_dict(healthmonitor=False,
                                 listener=False,
                                 listeners=False,
                                 loadbalancer=False,
                                 l7_policies=False,
                                 members=False,
                                 session_persistence=False)

        pool_dict['members'] = [{'id': member.id} for member in pool.members]
        pool_dict['l7_policies'] = [{'id': l7_policy.id}
                                    for l7_policy in pool.l7_policies]
        if pool.session_persistence:
            pool_dict['session_persistence'] = (
                pool.session_persistence.to_api_dict())

        return pool_dict

    def _l7policy_to_dict(self, l7policy):
        """Convert l7Policy to dict.

        Adds provisioning_status to dict from to_api_dict()
        """
        l7policy_dict = l7policy.to_api_dict()
        l7policy_dict['provisioning_status'] = l7policy.provisioning_status
        return l7policy_dict

    def _l7rule_to_dict(self, l7rule):
        """Convert l7Policy rule to dict.

        Adds provisioning_status to dict from to_api_dict()
        """
        l7rule_dict = l7rule.to_api_dict()
        l7rule_dict['provisioning_status'] = l7rule.provisioning_status
        return l7rule_dict