90180300eb
Prefer single style of throwing instances and not classes. Change-Id: If159afcd092de5381309cbe217d64145ed5b45b4 Closes-Bug: #1364392
144 lines
6.0 KiB
Python
144 lines
6.0 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.
|
|
|
|
from oslo.db import exception as db_exc
|
|
|
|
from neutron.common import exceptions as exc
|
|
from neutron.openstack.common import log
|
|
from neutron.plugins.ml2 import driver_api as api
|
|
|
|
|
|
# Number of retries to find a valid segment candidate and allocate it
|
|
DB_MAX_RETRIES = 10
|
|
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
class TypeDriverHelper(api.TypeDriver):
|
|
"""TypeDriver Helper for segment allocation.
|
|
|
|
Provide methods helping to perform segment allocation fully or partially
|
|
specified.
|
|
"""
|
|
|
|
def __init__(self, model):
|
|
self.model = model
|
|
self.primary_keys = set(dict(model.__table__.columns))
|
|
self.primary_keys.remove("allocated")
|
|
|
|
def allocate_fully_specified_segment(self, session, **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 session.begin(subtransactions=True):
|
|
alloc = (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 = (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(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, session, **filters):
|
|
"""Allocate model segment from pool partially specified by filters.
|
|
|
|
Return allocated db object or None.
|
|
"""
|
|
|
|
network_type = self.get_type()
|
|
with session.begin(subtransactions=True):
|
|
select = (session.query(self.model).
|
|
filter_by(allocated=False, **filters))
|
|
|
|
# Selected segment can be allocated before update by someone else,
|
|
# We retry until update success or DB_MAX_RETRIES retries
|
|
for attempt in range(1, DB_MAX_RETRIES + 1):
|
|
alloc = select.first()
|
|
|
|
if not alloc:
|
|
# No resource available
|
|
return
|
|
|
|
raw_segment = dict((k, alloc[k]) for k in self.primary_keys)
|
|
LOG.debug("%(type)s segment allocate from pool, attempt "
|
|
"%(attempt)s started with %(segment)s ",
|
|
{"type": network_type, "attempt": attempt,
|
|
"segment": raw_segment})
|
|
count = (session.query(self.model).
|
|
filter_by(allocated=False, **raw_segment).
|
|
update({"allocated": True}))
|
|
if count:
|
|
LOG.debug("%(type)s segment allocate from pool, attempt "
|
|
"%(attempt)s success with %(segment)s ",
|
|
{"type": network_type, "attempt": attempt,
|
|
"segment": raw_segment})
|
|
return alloc
|
|
|
|
# Segment allocated since select
|
|
LOG.debug("Allocate %(type)s segment from pool, "
|
|
"attempt %(attempt)s failed with segment "
|
|
"%(segment)s",
|
|
{"type": network_type, "attempt": attempt,
|
|
"segment": raw_segment})
|
|
|
|
LOG.warning(_("Allocate %(type)s segment from pool failed "
|
|
"after %(number)s failed attempts"),
|
|
{"type": network_type, "number": DB_MAX_RETRIES})
|
|
raise exc.NoNetworkFoundInMaximumAllowedAttempts()
|