From ce0e4b25d5cbabaff147cc7212fe033a95233a54 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Thu, 6 Apr 2017 05:42:25 -0700 Subject: [PATCH] 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 323eb7f2e146ecc2f7c25d59e57d1e1e006cb71f) --- neutron/plugins/ml2/db.py | 44 ++++++++++++++++++++++++++++++++++- neutron/plugins/ml2/plugin.py | 14 +++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/neutron/plugins/ml2/db.py b/neutron/plugins/ml2/db.py index 5de9838911c..7371cc82933 100644 --- a/neutron/plugins/ml2/db.py +++ b/neutron/plugins/ml2/db.py @@ -22,10 +22,11 @@ import six from sqlalchemy import or_ 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 registry 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 import models_v2 from neutron.extensions import portbindings @@ -276,6 +277,47 @@ def get_distributed_port_bindings(context, port_id): 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): if not subnet_ids: return False diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index 1540c6184b8..303fc9e1966 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -895,6 +895,20 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, e, _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 @db_api.retry_if_session_inactive() def delete_network(self, context, id):