neutron/neutron/plugins/ml2/drivers/helpers.py

160 lines
6.5 KiB
Python

# Copyright (c) 2014 Thales Services SAS
# 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.
import functools
from neutron_lib.db import api as db_api
from neutron_lib import exceptions
from neutron_lib.plugins import constants as plugin_constants
from neutron_lib.plugins import directory
from neutron_lib.plugins.ml2 import api
from neutron_lib.plugins import utils as p_utils
from neutron_lib.utils import helpers
from oslo_config import cfg
from oslo_db import exception as db_exc
from oslo_log import log
from neutron.objects import network_segment_range as ns_range
LOG = log.getLogger(__name__)
class BaseTypeDriver(api.ML2TypeDriver):
"""BaseTypeDriver for functions common to Segment and flat."""
def __init__(self):
try:
self.physnet_mtus = helpers.parse_mappings(
cfg.CONF.ml2.physical_network_mtus, unique_values=False
)
except Exception as e:
LOG.error("Failed to parse physical_network_mtus: %s", e)
self.physnet_mtus = []
def get_mtu(self, physical_network=None):
return p_utils.get_deployment_physnet_mtu()
class SegmentTypeDriver(BaseTypeDriver):
"""SegmentTypeDriver for segment allocation.
Provide methods helping to perform segment allocation fully or partially
specified.
"""
def __init__(self, model):
super(SegmentTypeDriver, self).__init__()
self.model = model.db_model
self.segmentation_obj = model
primary_keys_columns = self.model.__table__.primary_key.columns
self.primary_keys = {col.name for col in primary_keys_columns}
def allocate_fully_specified_segment(self, context, **raw_segment):
"""Allocate segment fully specified by raw_segment.
If segment exists, then try to allocate it and return db object
If segment does not exists, then try to create it and return db object
If allocation/creation failed, then return None
"""
network_type = self.get_type()
try:
with db_api.CONTEXT_WRITER.using(context):
alloc = (
context.session.query(self.model).filter_by(**raw_segment).
first())
if alloc:
if alloc.allocated:
# Segment already allocated
return
else:
# Segment not allocated
LOG.debug("%(type)s segment %(segment)s allocate "
"started ",
{"type": network_type,
"segment": raw_segment})
count = (context.session.query(self.model).
filter_by(allocated=False, **raw_segment).
update({"allocated": True}))
if count:
LOG.debug("%(type)s segment %(segment)s allocate "
"done ",
{"type": network_type,
"segment": raw_segment})
return alloc
# Segment allocated or deleted since select
LOG.debug("%(type)s segment %(segment)s allocate "
"failed: segment has been allocated or "
"deleted",
{"type": network_type,
"segment": raw_segment})
# Segment to create or already allocated
LOG.debug("%(type)s segment %(segment)s create started",
{"type": network_type, "segment": raw_segment})
alloc = self.model(allocated=True, **raw_segment)
alloc.save(context.session)
LOG.debug("%(type)s segment %(segment)s create done",
{"type": network_type, "segment": raw_segment})
except db_exc.DBDuplicateEntry:
# Segment already allocated (insert failure)
alloc = None
LOG.debug("%(type)s segment %(segment)s create failed",
{"type": network_type, "segment": raw_segment})
return alloc
def allocate_partially_specified_segment(self, context, **filters):
"""Allocate model segment from pool partially specified by filters.
Return allocated db object or None.
"""
network_type = self.get_type()
if directory.get_plugin(plugin_constants.NETWORK_SEGMENT_RANGE):
calls = [
functools.partial(
ns_range.NetworkSegmentRange.get_segments_for_project,
context, self.model, network_type,
self.model_segmentation_id, **filters),
functools.partial(
ns_range.NetworkSegmentRange.get_segments_shared,
context, self.model, network_type,
self.model_segmentation_id, **filters)]
else:
calls = [functools.partial(
self.segmentation_obj.get_random_unallocated_segment,
context, **filters)]
try_to_allocate = False
for call in calls:
allocations = call()
if not isinstance(allocations, list):
allocations = [allocations] if allocations else []
for alloc in allocations:
segment = dict((k, alloc[k]) for k in self.primary_keys)
try_to_allocate = True
if self.segmentation_obj.allocate(context, **segment):
LOG.debug('%(type)s segment allocate from pool success '
'with %(segment)s ', {'type': network_type,
'segment': segment})
return alloc
if try_to_allocate:
raise db_exc.RetryRequest(
exceptions.NoNetworkFoundInMaximumAllowedAttempts())