Add some bulk lookup methods to ML2 for RPC handling

This adds three methods to make working with bulk port
DB lookups easier in ML2:

* partial_port_ids_to_full_ids: takes short port IDs map to
  full port IDs. This will allow us to eliminate many LIKE
  queries and do one just once for all ports on an RPC call.
* get_port_db_objects: Takes a list of port IDs and returns
  a map to port DB objects. This allows us to get access to
  sqla obejcts for a bunch of ports without a custom
  session.query call.
* get_network_contexts: Takes a list of network_ids and does
  a bulk construction of NetworkContext objects and returns
  them as a map of network_id to NetworkContext to avoid
  expensive net lookups when constructing lots of PortContext
  objects.

Conflicts:
	neutron/plugins/ml2/db.py
	neutron/plugins/ml2/plugin.py

Partial-Bug: #1665215
Change-Id: I330eefbf429bd62f2a7e8ebadf7037da15c86815
(cherry picked from commit 323eb7f2e1)
This commit is contained in:
Kevin Benton 2017-04-06 05:42:25 -07:00 committed by Ihar Hrachyshka
parent 66839cff26
commit ce0e4b25d5
2 changed files with 57 additions and 1 deletions

View File

@ -22,10 +22,11 @@ import six
from sqlalchemy import or_ from sqlalchemy import or_
from sqlalchemy.orm import exc from sqlalchemy.orm import exc
from neutron._i18n import _, _LE from neutron._i18n import _, _LE, _LI
from neutron.callbacks import events from neutron.callbacks import events
from neutron.callbacks import registry from neutron.callbacks import registry
from neutron.callbacks import resources from neutron.callbacks import resources
from neutron.db import api as db_api
from neutron.db.models import securitygroup as sg_models from neutron.db.models import securitygroup as sg_models
from neutron.db import models_v2 from neutron.db import models_v2
from neutron.extensions import portbindings from neutron.extensions import portbindings
@ -276,6 +277,47 @@ def get_distributed_port_bindings(context, port_id):
return bindings return bindings
@db_api.context_manager.reader
def partial_port_ids_to_full_ids(context, partial_ids):
"""Takes a list of the start of port IDs and returns full IDs.
Returns dictionary of partial IDs to full IDs if a single match
is found.
"""
result = {}
to_full_query = (context.session.query(models_v2.Port.id).
filter(or_(*[models_v2.Port.id.startswith(p)
for p in partial_ids])))
candidates = [match[0] for match in to_full_query]
for partial_id in partial_ids:
matching = [c for c in candidates if c.startswith(partial_id)]
if len(matching) == 1:
result[partial_id] = matching[0]
continue
if len(matching) < 1:
LOG.info(_LI("No ports have port_id starting with %s"),
partial_id)
elif len(matching) > 1:
LOG.error(_LE("Multiple ports have port_id starting with %s"),
partial_id)
return result
@db_api.context_manager.reader
def get_port_db_objects(context, port_ids):
"""Takes a list of port_ids and returns matching port db objects.
return format is a dictionary keyed by passed in IDs with db objects
for values or None if the port was not present.
"""
port_qry = (context.session.query(models_v2.Port).
filter(models_v2.Port.id.in_(port_ids)))
result = {p: None for p in port_ids}
for port in port_qry:
result[port.id] = port
return result
def is_dhcp_active_on_any_subnet(context, subnet_ids): def is_dhcp_active_on_any_subnet(context, subnet_ids):
if not subnet_ids: if not subnet_ids:
return False return False

View File

@ -895,6 +895,20 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
e, e,
_LE("Exception auto-deleting subnet %s"), subnet_id) _LE("Exception auto-deleting subnet %s"), subnet_id)
def get_network_contexts(self, context, network_ids):
"""Return a map of network_id to NetworkContext for network_ids."""
net_filters = {'id': list(set(network_ids))}
nets_by_netid = {
n['id']: n for n in self.get_networks(context,
filters=net_filters)
}
netctxs_by_netid = {
net_id: driver_context.NetworkContext(
self, context, nets_by_netid[net_id])
for net_id in nets_by_netid.keys()
}
return netctxs_by_netid
@utils.transaction_guard @utils.transaction_guard
@db_api.retry_if_session_inactive() @db_api.retry_if_session_inactive()
def delete_network(self, context, id): def delete_network(self, context, id):