neutron/neutron/plugins/ml2/driver_context.py
Kevin Benton bd7055daf2 Allow offloading lookups in driver contexts
This allows segments looked up ahead of time to be passed
into NetworkContext objects and NetworkContext objects to
be passed into PortContext objects. This allows us to avoid
doing segments lookups for every PortContext construction
when handling a bunch of ports (e.g. in RPC handler).

Conflicts:
	neutron/plugins/ml2/driver_context.py

Change-Id: Ib4c43a7894fe1285ecf4bdf9af5e5f1b93b0b39b
Partial-Bug: #1665215
(cherry picked from commit 604e598a7d)
(cherry picked from commit a2ae48c2ce)
2017-05-16 14:39:43 -07:00

284 lines
9.6 KiB
Python

# Copyright (c) 2013 OpenStack Foundation
# All Rights Reserved.
#
# 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.
from neutron_lib import constants
from oslo_log import log
from oslo_serialization import jsonutils
from neutron._i18n import _LW
from neutron.db import segments_db
from neutron.extensions import portbindings
from neutron.plugins.ml2 import driver_api as api
LOG = log.getLogger(__name__)
class MechanismDriverContext(object):
"""MechanismDriver context base class."""
def __init__(self, plugin, plugin_context):
self._plugin = plugin
# This temporarily creates a reference loop, but the
# lifetime of PortContext is limited to a single
# method call of the plugin.
self._plugin_context = plugin_context
class NetworkContext(MechanismDriverContext, api.NetworkContext):
def __init__(self, plugin, plugin_context, network,
original_network=None, segments=None):
super(NetworkContext, self).__init__(plugin, plugin_context)
self._network = network
self._original_network = original_network
if segments is None:
self._segments = segments_db.get_network_segments(
plugin_context.session, network['id'])
else:
self._segments = segments
@property
def current(self):
return self._network
@property
def original(self):
return self._original_network
@property
def network_segments(self):
return self._segments
class SubnetContext(MechanismDriverContext, api.SubnetContext):
def __init__(self, plugin, plugin_context, subnet, network,
original_subnet=None):
super(SubnetContext, self).__init__(plugin, plugin_context)
self._subnet = subnet
self._original_subnet = original_subnet
self._network_context = NetworkContext(plugin, plugin_context,
network)
@property
def current(self):
return self._subnet
@property
def original(self):
return self._original_subnet
@property
def network(self):
return self._network_context
class PortContext(MechanismDriverContext, api.PortContext):
def __init__(self, plugin, plugin_context, port, network, binding,
binding_levels, original_port=None):
super(PortContext, self).__init__(plugin, plugin_context)
self._port = port
self._original_port = original_port
if isinstance(network, NetworkContext):
self._network_context = network
else:
self._network_context = NetworkContext(
plugin, plugin_context, network) if network else None
self._binding = binding
self._binding_levels = binding_levels
self._segments_to_bind = None
self._new_bound_segment = None
self._next_segments_to_bind = None
if original_port:
self._original_vif_type = binding.vif_type
self._original_vif_details = self._plugin._get_vif_details(binding)
self._original_binding_levels = self._binding_levels
else:
self._original_vif_type = None
self._original_vif_details = None
self._original_binding_levels = None
self._new_port_status = None
# The following methods are for use by the ML2 plugin and are not
# part of the driver API.
def _prepare_to_bind(self, segments_to_bind):
self._segments_to_bind = segments_to_bind
self._new_bound_segment = None
self._next_segments_to_bind = None
def _clear_binding_levels(self):
self._binding_levels = []
def _push_binding_level(self, binding_level):
self._binding_levels.append(binding_level)
def _pop_binding_level(self):
return self._binding_levels.pop()
# The following implement the abstract methods and properties of
# the driver API.
@property
def current(self):
return self._port
@property
def original(self):
return self._original_port
@property
def status(self):
# REVISIT(rkukura): Eliminate special DVR case as part of
# resolving bug 1367391?
if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE:
return self._binding.status
return self._port['status']
@property
def original_status(self):
# REVISIT(rkukura): Should return host-specific status for DVR
# ports. Fix as part of resolving bug 1367391.
if self._original_port:
return self._original_port['status']
@property
def network(self):
if not self._network_context:
network = self._plugin.get_network(
self._plugin_context, self.current['network_id'])
self._network_context = NetworkContext(
self._plugin, self._plugin_context, network)
return self._network_context
@property
def binding_levels(self):
if self._binding_levels:
return [{
api.BOUND_DRIVER: level.driver,
api.BOUND_SEGMENT: self._expand_segment(level.segment_id)
} for level in self._binding_levels]
@property
def original_binding_levels(self):
if self._original_binding_levels:
return [{
api.BOUND_DRIVER: level.driver,
api.BOUND_SEGMENT: self._expand_segment(level.segment_id)
} for level in self._original_binding_levels]
@property
def top_bound_segment(self):
if self._binding_levels:
return self._expand_segment(self._binding_levels[0].segment_id)
@property
def original_top_bound_segment(self):
if self._original_binding_levels:
return self._expand_segment(
self._original_binding_levels[0].segment_id)
@property
def bottom_bound_segment(self):
if self._binding_levels:
return self._expand_segment(self._binding_levels[-1].segment_id)
@property
def original_bottom_bound_segment(self):
if self._original_binding_levels:
return self._expand_segment(
self._original_binding_levels[-1].segment_id)
def _expand_segment(self, segment_id):
for s in self.network.network_segments:
if s['id'] == segment_id:
return s
# TODO(kevinbenton): eliminate the query below. The above should
# always return since the port is bound to a network segment. Leaving
# in for now for minimally invasive change for back-port.
segment = segments_db.get_segment_by_id(self._plugin_context.session,
segment_id)
if not segment:
LOG.warning(_LW("Could not expand segment %s"), segment_id)
return segment
@property
def host(self):
# REVISIT(rkukura): Eliminate special DVR case as part of
# resolving bug 1367391?
if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE:
return self._binding.host
return self._port.get(portbindings.HOST_ID)
@property
def original_host(self):
# REVISIT(rkukura): Eliminate special DVR case as part of
# resolving bug 1367391?
if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE:
return self._original_port and self._binding.host
else:
return (self._original_port and
self._original_port.get(portbindings.HOST_ID))
@property
def vif_type(self):
return self._binding.vif_type
@property
def original_vif_type(self):
return self._original_vif_type
@property
def vif_details(self):
return self._plugin._get_vif_details(self._binding)
@property
def original_vif_details(self):
return self._original_vif_details
@property
def segments_to_bind(self):
return self._segments_to_bind
def host_agents(self, agent_type):
return self._plugin.get_agents(self._plugin_context,
filters={'agent_type': [agent_type],
'host': [self._binding.host]})
def set_binding(self, segment_id, vif_type, vif_details,
status=None):
# TODO(rkukura) Verify binding allowed, segment in network
self._new_bound_segment = segment_id
self._binding.vif_type = vif_type
self._binding.vif_details = jsonutils.dumps(vif_details)
self._new_port_status = status
def continue_binding(self, segment_id, next_segments_to_bind):
# TODO(rkukura) Verify binding allowed, segment in network
self._new_bound_segment = segment_id
self._next_segments_to_bind = next_segments_to_bind
def allocate_dynamic_segment(self, segment):
network_id = self._network_context.current['id']
return self._plugin.type_manager.allocate_dynamic_segment(
self._plugin_context, network_id, segment)
def release_dynamic_segment(self, segment_id):
return self._plugin.type_manager.release_dynamic_segment(
self._plugin_context.session, segment_id)