Neutron integration with OVN
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

141 lines
5.7 KiB

# Copyright 2017 Red Hat, Inc.
# 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.db import standard_attr
from neutron_lib.db import api as db_api
from oslo_db import api as oslo_db_api
from oslo_log import log
from sqlalchemy.orm import exc
from networking_ovn.common import constants as ovn_const
from networking_ovn.common import exceptions as ovn_exc
from networking_ovn.common import utils
from networking_ovn.db import models
LOG = log.getLogger(__name__)
STD_ATTR_MAP = standard_attr.get_standard_attr_resource_model_map()
# 1:2 mapping for OVN, neutron router ports are simple ports, but
# for OVN we handle LSP & LRP objects
STD_ATTR_MAP[ovn_const.TYPE_ROUTER_PORTS] = STD_ATTR_MAP[ovn_const.TYPE_PORTS]
_wrap_db_retry = oslo_db_api.wrap_db_retry(
max_retries=ovn_const.DB_MAX_RETRIES,
retry_interval=ovn_const.DB_INITIAL_RETRY_INTERVAL,
max_retry_interval=ovn_const.DB_MAX_RETRY_INTERVAL,
inc_retry_interval=True, retry_on_deadlock=True)
def _get_standard_attr_id(session, resource_uuid, resource_type):
try:
row = session.query(STD_ATTR_MAP[resource_type]).filter_by(
id=resource_uuid).one()
return row.standard_attr_id
except exc.NoResultFound:
raise ovn_exc.StandardAttributeIDNotFound(
resource_uuid=resource_uuid)
@_wrap_db_retry
def create_initial_revision(resource_uuid, resource_type, session,
revision_number=ovn_const.INITIAL_REV_NUM,
may_exist=False):
LOG.debug('create_initial_revision uuid=%s, type=%s, rev=%s',
resource_uuid, resource_type, revision_number)
db_func = session.merge if may_exist else session.add
with session.begin(subtransactions=True):
std_attr_id = _get_standard_attr_id(
session, resource_uuid, resource_type)
row = models.OVNRevisionNumbers(
resource_uuid=resource_uuid, resource_type=resource_type,
standard_attr_id=std_attr_id, revision_number=revision_number)
db_func(row)
@_wrap_db_retry
def delete_revision(resource_id, resource_type):
LOG.debug('delete_revision(%s)', resource_id)
session = db_api.get_writer_session()
with session.begin():
row = session.query(models.OVNRevisionNumbers).filter_by(
resource_uuid=resource_id,
resource_type=resource_type).one_or_none()
if row:
session.delete(row)
def _ensure_revision_row_exist(session, resource, resource_type):
"""Ensure the revision row exists.
Ensure the revision row exist before we try to bump its revision
number. This method is part of the migration plan to deal with
resources that have been created prior to the database sync work
getting merged.
"""
# TODO(lucasagomes): As the docstring says, this method was created to
# deal with objects that already existed before the sync work. I believe
# that we can remove this method after few development cycles. Or,
# if we decide to make a migration script as well.
with session.begin(subtransactions=True):
try:
session.query(models.OVNRevisionNumbers).filter_by(
resource_uuid=resource['id'],
resource_type=resource_type).one()
except exc.NoResultFound:
LOG.warning(
'No revision row found for %(res_uuid)s (type: '
'%(res_type)s) when bumping the revision number. '
'Creating one.', {'res_uuid': resource['id'],
'res_type': resource_type})
create_initial_revision(resource['id'], resource_type, session)
@_wrap_db_retry
def get_revision_row(resource_uuid):
try:
session = db_api.get_reader_session()
with session.begin():
return session.query(models.OVNRevisionNumbers).filter_by(
resource_uuid=resource_uuid).one()
except exc.NoResultFound:
pass
@_wrap_db_retry
def bump_revision(resource, resource_type):
session = db_api.get_writer_session()
revision_number = utils.get_revision_number(resource, resource_type)
with session.begin():
_ensure_revision_row_exist(session, resource, resource_type)
std_attr_id = _get_standard_attr_id(
session, resource['id'], resource_type)
row = session.merge(models.OVNRevisionNumbers(
standard_attr_id=std_attr_id, resource_uuid=resource['id'],
resource_type=resource_type))
if revision_number < row.revision_number:
LOG.debug(
'Skip bumping the revision number for %(res_uuid)s (type: '
'%(res_type)s) to %(rev_num)d. A higher version is already '
'registered in the database (%(new_rev)d)',
{'res_type': resource_type, 'res_uuid': resource['id'],
'rev_num': revision_number, 'new_rev': row.revision_number})
return
row.revision_number = revision_number
session.merge(row)
LOG.info('Successfully bumped revision number for resource '
'%(res_uuid)s (type: %(res_type)s) to %(rev_num)d',
{'res_uuid': resource['id'], 'res_type': resource_type,
'rev_num': revision_number})