OVO for L3HARouter

This patch introduces and integrates OVO for L3 HA Router.

Co-Authored-By: Nguyen Phuong An <AnNP@vn.fujitsu.com>
Co-Authored-By: Vu Cong Tuan <tuanvc@vn.fujitsu.com>
Change-Id: I3463921dec415dd073503ab9470588193d08ce87
Partially-Implements: blueprint adopt-oslo-versioned-objects-for-db
This commit is contained in:
sindhudevale 2016-08-26 19:49:50 +00:00 committed by Vu Cong Tuan
parent 96f8fbc62d
commit c761a08473
12 changed files with 220 additions and 72 deletions

View File

@ -36,7 +36,7 @@ HA_SUBNET_NAME = 'HA subnet tenant %s'
HA_PORT_NAME = 'HA port tenant %s'
HA_ROUTER_STATE_ACTIVE = 'active'
HA_ROUTER_STATE_STANDBY = 'standby'
VALID_HA_STATES = (HA_ROUTER_STATE_ACTIVE, HA_ROUTER_STATE_STANDBY)
PAGINATION_INFINITE = 'infinite'
SORT_DIRECTION_ASC = 'asc'

View File

@ -25,6 +25,7 @@ from neutron_lib.callbacks import registry
from neutron_lib.callbacks import resources
from neutron_lib import constants
from neutron_lib import exceptions as n_exc
from neutron_lib.objects import exceptions as obj_base
from oslo_config import cfg
from oslo_db import exception as db_exc
from oslo_log import helpers as log_helpers
@ -43,10 +44,11 @@ from neutron.db import api as db_api
from neutron.db.availability_zone import router as router_az_db
from neutron.db import l3_dvr_db
from neutron.db.l3_dvr_db import is_distributed_router
from neutron.db.models import agent as agent_model
from neutron.db.models import l3ha as l3ha_model
from neutron.extensions import l3
from neutron.extensions import l3_ext_ha_mode as l3_ha
from neutron.objects import base
from neutron.objects import l3_hamode
from neutron.objects import router as l3_obj
from neutron.plugins.common import utils as p_utils
@ -110,19 +112,16 @@ class L3_HA_NAT_db_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin,
return inst
def get_ha_network(self, context, tenant_id):
return (context.session.query(l3ha_model.L3HARouterNetwork).
filter(l3ha_model.L3HARouterNetwork.tenant_id == tenant_id).
first())
pager = base.Pager(limit=1)
results = l3_hamode.L3HARouterNetwork.get_objects(
context, _pager=pager, project_id=tenant_id)
return results.pop() if results else None
def _get_allocated_vr_id(self, context, network_id):
with context.session.begin(subtransactions=True):
query = (context.session.query(
l3ha_model.L3HARouterVRIdAllocation).
filter(l3ha_model.L3HARouterVRIdAllocation.network_id ==
network_id))
allocated_vr_ids = set(a.vr_id for a in query) - set([0])
vr_id_objs = l3_hamode.L3HARouterVRIdAllocation.get_objects(
context, network_id=network_id)
allocated_vr_ids = set(a.vr_id for a in vr_id_objs) - set([0])
return allocated_vr_ids
@db_api.retry_if_session_inactive()
@ -152,11 +151,11 @@ class L3_HA_NAT_db_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin,
if not available_vr_ids:
raise l3_ha.NoVRIDAvailable(router_id=router_id)
allocation = l3ha_model.L3HARouterVRIdAllocation()
allocation.network_id = network_id
allocation.vr_id = available_vr_ids.pop()
allocation = l3_hamode.L3HARouterVRIdAllocation(
context, network_id=network_id,
vr_id=available_vr_ids.pop())
allocation.create()
context.session.add(allocation)
router_db.extra_attributes.ha_vr_id = allocation.vr_id
LOG.debug(
"Router %(router_id)s has been allocated a ha_vr_id "
@ -165,7 +164,7 @@ class L3_HA_NAT_db_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin,
return allocation.vr_id
except db_exc.DBDuplicateEntry:
except obj_base.NeutronDbObjectDuplicateEntry:
LOG.info("Attempt %(count)s to allocate a VRID in the "
"network %(network)s for the router %(router)s",
{'count': count, 'network': network_id,
@ -177,10 +176,8 @@ class L3_HA_NAT_db_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin,
@db_api.retry_if_session_inactive()
def _delete_vr_id_allocation(self, context, ha_network, vr_id):
with context.session.begin(subtransactions=True):
context.session.query(
l3ha_model.L3HARouterVRIdAllocation).filter_by(
network_id=ha_network.network_id, vr_id=vr_id).delete()
l3_hamode.L3HARouterVRIdAllocation.delete_objects(
context, network_id=ha_network.network_id, vr_id=vr_id)
def _create_ha_subnet(self, context, network_id, tenant_id):
args = {'network_id': network_id,
@ -195,21 +192,18 @@ class L3_HA_NAT_db_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin,
def _create_ha_network_tenant_binding(self, context, tenant_id,
network_id):
with context.session.begin():
ha_network = l3ha_model.L3HARouterNetwork(
tenant_id=tenant_id, network_id=network_id)
context.session.add(ha_network)
ha_network = l3_hamode.L3HARouterNetwork(
context, project_id=tenant_id, network_id=network_id)
ha_network.create()
# we need to check if someone else just inserted at exactly the
# same time as us because there is no constrain in L3HARouterNetwork
# that prevents multiple networks per tenant
with context.session.begin(subtransactions=True):
items = (context.session.query(l3ha_model.L3HARouterNetwork).
filter_by(tenant_id=tenant_id).all())
if len(items) > 1:
# we need to throw an error so our network is deleted
# and the process is started over where the existing
# network will be selected.
raise db_exc.DBDuplicateEntry(columns=['tenant_id'])
if l3_hamode.L3HARouterNetwork.count(
context, project_id=tenant_id) > 1:
# we need to throw an error so our network is deleted
# and the process is started over where the existing
# network will be selected.
raise db_exc.DBDuplicateEntry(columns=['tenant_id'])
return ha_network
def _add_ha_network_settings(self, network):
@ -545,24 +539,14 @@ class L3_HA_NAT_db_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin,
def get_ha_router_port_bindings(self, context, router_ids, host=None):
if not router_ids:
return []
query = context.session.query(l3ha_model.L3HARouterAgentPortBinding)
if host:
query = query.join(agent_model.Agent).filter(
agent_model.Agent.host == host)
query = query.filter(
l3ha_model.L3HARouterAgentPortBinding.router_id.in_(router_ids))
return query.all()
return (
l3_hamode.L3HARouterAgentPortBinding.get_l3ha_filter_host_router(
context, router_ids, host))
@staticmethod
def _check_router_agent_ha_binding(context, router_id, agent_id):
query = context.session.query(l3ha_model.L3HARouterAgentPortBinding)
query = query.filter(
l3ha_model.L3HARouterAgentPortBinding.router_id == router_id,
l3ha_model.L3HARouterAgentPortBinding.l3_agent_id == agent_id)
return query.first() is not None
return l3_hamode.L3HARouterAgentPortBinding.objects_exist(
context, router_id=router_id, l3_agent_id=agent_id)
def _get_bindings_and_update_router_state_for_dead_agents(self, context,
router_id):

View File

@ -29,6 +29,10 @@ from neutron.common import utils
from neutron.plugins.common import constants as plugin_constants
class HARouterEnumField(obj_fields.AutoTypedField):
AUTO_TYPE = obj_fields.Enum(valid_values=constants.VALID_HA_STATES)
class IPV6ModeEnumField(obj_fields.AutoTypedField):
AUTO_TYPE = obj_fields.Enum(valid_values=lib_constants.IPV6_MODES)

View File

@ -0,0 +1,83 @@
# Copyright (c) 2016 Intel Corporation.
#
# 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_versionedobjects import base as obj_base
from oslo_versionedobjects import fields as obj_fields
from neutron.common import constants
from neutron.db.models import agent as agent_model
from neutron.db.models import l3ha
from neutron.objects import base
from neutron.objects import common_types
@obj_base.VersionedObjectRegistry.register
class L3HARouterAgentPortBinding(base.NeutronDbObject):
# Version 1.0: Initial version
VERSION = '1.0'
db_model = l3ha.L3HARouterAgentPortBinding
fields = {
'port_id': common_types.UUIDField(),
'router_id': common_types.UUIDField(),
'l3_agent_id': common_types.UUIDField(nullable=True),
'state': common_types.HARouterEnumField(
default=constants.HA_ROUTER_STATE_STANDBY),
}
primary_keys = ['port_id']
fields_no_update = ['router_id', 'port_id', 'l3_agent_id']
@classmethod
def get_l3ha_filter_host_router(cls, context, router_ids, host):
query = context.session.query(l3ha.L3HARouterAgentPortBinding)
if host:
query = query.join(agent_model.Agent).filter(
agent_model.Agent.host == host)
query = query.filter(
l3ha.L3HARouterAgentPortBinding.router_id.in_(router_ids))
return query.all()
@obj_base.VersionedObjectRegistry.register
class L3HARouterNetwork(base.NeutronDbObject):
# Version 1.0: Initial version
VERSION = '1.0'
db_model = l3ha.L3HARouterNetwork
fields = {
'network_id': common_types.UUIDField(),
'project_id': obj_fields.StringField(),
}
primary_keys = ['network_id', 'project_id']
@obj_base.VersionedObjectRegistry.register
class L3HARouterVRIdAllocation(base.NeutronDbObject):
# Version 1.0: Initial version
VERSION = '1.0'
db_model = l3ha.L3HARouterVRIdAllocation
fields = {
'network_id': common_types.UUIDField(),
'vr_id': obj_fields.IntegerField()
}
primary_keys = ['network_id', 'vr_id']

View File

@ -268,7 +268,7 @@ class L3Scheduler(object):
def _add_port_from_net_and_ensure_vr_id(self, plugin, ctxt, router_db,
tenant_id, ha_net):
plugin._ensure_vr_id(ctxt, router_db, ha_net)
return plugin.add_ha_port(ctxt, router_db.id, ha_net.network.id,
return plugin.add_ha_port(ctxt, router_db.id, ha_net.network_id,
tenant_id)
def create_ha_port_and_bind(self, plugin, context, router_id,

View File

@ -289,6 +289,10 @@ def get_random_flow_direction():
return random.choice(n_const.VALID_DIRECTIONS)
def get_random_ha_states():
return random.choice(n_const.VALID_HA_STATES)
def get_random_ether_type():
return random.choice(n_const.VALID_ETHERTYPES)

View File

@ -23,6 +23,7 @@ from neutron_lib.callbacks import resources
from neutron_lib import constants
from neutron_lib import context
from neutron_lib import exceptions as n_exc
from neutron_lib.objects import exceptions
from neutron_lib.plugins import constants as plugin_constants
from neutron_lib.plugins import directory
from oslo_config import cfg
@ -43,6 +44,7 @@ from neutron.db.models import l3ha as l3ha_model
from neutron.extensions import external_net
from neutron.extensions import l3
from neutron.extensions import l3_ext_ha_mode
from neutron.objects import l3_hamode
from neutron.scheduler import l3_agent_scheduler
from neutron.services.revisions import revision_plugin
from neutron.tests.common import helpers
@ -169,10 +171,8 @@ class L3HATestCase(L3HATestFramework):
def test_get_l3_bindings_hosting_router_with_ha_states_not_scheduled(self):
router = self._create_router(ha=False)
# Check that there no L3 agents scheduled for this router
res = self.admin_ctx.session.query(
l3ha_model.L3HARouterAgentPortBinding).filter(
l3ha_model.L3HARouterAgentPortBinding.router_id == router['id']
).all()
res = l3_hamode.L3HARouterAgentPortBinding.get_objects(
self.admin_ctx, router_id=router['id'])
self.assertEqual([], [r.agent for r in res])
bindings = self.plugin.get_l3_bindings_hosting_router_with_ha_states(
self.admin_ctx, router['id'])
@ -589,7 +589,7 @@ class L3HATestCase(L3HATestFramework):
def test_create_ha_network_binding_failure_rolls_back_network(self):
networks_before = self.core_plugin.get_networks(self.admin_ctx)
with mock.patch.object(l3ha_model, 'L3HARouterNetwork',
with mock.patch.object(l3_hamode, 'L3HARouterNetwork',
side_effect=ValueError):
self.assertRaises(ValueError, self.plugin._create_ha_network,
self.admin_ctx, _uuid())
@ -688,7 +688,8 @@ class L3HATestCase(L3HATestFramework):
router['tenant_id'])
self.plugin._create_ha_network_tenant_binding(
self.admin_ctx, 't1', network['network_id'])
with testtools.ExpectedException(db_exc.DBDuplicateEntry):
with testtools.ExpectedException(
exceptions.NeutronDbObjectDuplicateEntry):
self.plugin._create_ha_network_tenant_binding(
self.admin_ctx, 't1', network['network_id'])
@ -947,9 +948,8 @@ class L3HATestCase(L3HATestFramework):
router1 = self._create_router()
states = {router1['id']: 'active'}
with mock.patch.object(self.plugin, 'get_ha_router_port_bindings'):
(self.admin_ctx.session.query(
l3ha_model.L3HARouterAgentPortBinding).
filter_by(router_id=router1['id']).delete())
(l3_hamode.L3HARouterAgentPortBinding.delete_objects(
self.admin_ctx, router_id=router1['id']))
self.plugin.update_routers_states(
self.admin_ctx, states, self.agent1['host'])

View File

@ -452,6 +452,7 @@ FIELD_TYPE_VALUE_GENERATOR_MAP = {
common_types.EtherTypeEnumField: tools.get_random_ether_type,
common_types.FloatingIPStatusEnumField: tools.get_random_floatingip_status,
common_types.FlowDirectionEnumField: tools.get_random_flow_direction,
common_types.HARouterEnumField: tools.get_random_ha_states,
common_types.IpamAllocationStatusEnumField: tools.get_random_ipam_status,
common_types.IPNetworkField: tools.get_random_ip_network,
common_types.IPNetworkPrefixLenField: tools.get_random_prefixlen,

View File

@ -0,0 +1,74 @@
# Copyright (c) 2016 Intel Corporation.
#
# 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.objects import l3_hamode
from neutron.tests.unit.objects import test_base as base
from neutron.tests.unit import testlib_api
class L3HARouterAgentPortBindingIfaceObjectTestCase(
base.BaseObjectIfaceTestCase):
_test_class = l3_hamode.L3HARouterAgentPortBinding
class L3HARouterAgentPortBindingDbObjectTestCase(base.BaseDbObjectTestCase,
testlib_api.SqlTestCase):
_test_class = l3_hamode.L3HARouterAgentPortBinding
def setUp(self):
super(L3HARouterAgentPortBindingDbObjectTestCase,
self).setUp()
_network_id = self._create_test_network_id()
def get_port():
return self._create_test_port_id(network_id=_network_id)
self.update_obj_fields({'port_id': get_port,
'router_id': self._create_test_router_id,
'l3_agent_id': self._create_test_agent_id})
class L3HARouterNetworkIfaceObjectTestCase(base.BaseObjectIfaceTestCase):
_test_class = l3_hamode.L3HARouterNetwork
class L3HARouterNetworkDbObjectTestCase(base.BaseDbObjectTestCase,
testlib_api.SqlTestCase):
_test_class = l3_hamode.L3HARouterNetwork
def setUp(self):
super(L3HARouterNetworkDbObjectTestCase, self).setUp()
network = self._create_test_network()
self.update_obj_fields({'network_id': network.id})
class L3HARouterVRIdAllocationIfaceObjectTestCase(
base.BaseObjectIfaceTestCase):
_test_class = l3_hamode.L3HARouterVRIdAllocation
class L3HARouterVRIdAllocationDbObjectTestCase(base.BaseDbObjectTestCase,
testlib_api.SqlTestCase):
_test_class = l3_hamode.L3HARouterVRIdAllocation
def setUp(self):
super(L3HARouterVRIdAllocationDbObjectTestCase, self).setUp()
network = self._create_test_network()
self.update_obj_fields({'network_id': network.id})

View File

@ -50,6 +50,9 @@ object_data = {
'IpamAllocation': '1.0-ace65431abd0a7be84cc4a5f32d034a3',
'IpamAllocationPool': '1.0-c4fa1460ed1b176022ede7af7d1510d5',
'IpamSubnet': '1.0-713de401682a70f34891e13af645fa08',
'L3HARouterAgentPortBinding': '1.0-d1d7ee13f35d56d7e225def980612ee5',
'L3HARouterNetwork': '1.0-87acea732853f699580179a94d2baf91',
'L3HARouterVRIdAllocation': '1.0-37502aebdbeadc4f9e3bd5e9da714ab9',
'MeteringLabel': '1.0-cc4b620a3425222447cbe459f62de533',
'MeteringLabelRule': '1.0-b5c5717e7bab8d1af1623156012a5842',
'Log': '1.0-6391351c0f34ed34375a19202f361d24',

View File

@ -20,8 +20,8 @@ from oslo_utils import uuidutils
from neutron.common import constants as n_const
from neutron.db.models import l3 as l3_models
from neutron.db.models import l3ha as l3ha_model
from neutron.db import models_v2
from neutron.objects import l3_hamode
from neutron.objects import network as network_obj
from neutron.objects import router as l3_objs
from neutron.plugins.ml2.drivers.l2pop import db as l2pop_db
@ -128,13 +128,10 @@ class TestL2PopulationDBTestCase(testlib_api.SqlTestCase):
if network_id == TEST_HA_NETWORK_ID:
agent = self.get_l3_agent_by_host(host)
haport_bindings_cls = l3ha_model.L3HARouterAgentPortBinding
habinding_kwarg = {'port_id': port_id,
'router_id': device_id,
'l3_agent_id': agent['id'],
'state': kwargs.get('host_state',
n_const.HA_ROUTER_STATE_ACTIVE)}
self.ctx.session.add(haport_bindings_cls(**habinding_kwarg))
l3_hamode.L3HARouterAgentPortBinding(
self.ctx, port_id=port_id, router_id=device_id,
l3_agent_id=agent['id'], state=kwargs.get(
'host_state', n_const.HA_ROUTER_STATE_ACTIVE)).create()
def test_get_distributed_active_network_ports(self):
self._setup_port_binding(

View File

@ -39,10 +39,10 @@ from neutron.db import l3_hamode_db
from neutron.db import l3_hascheduler_db
from neutron.db.models import agent as agent_model
from neutron.db.models import l3agent as rb_model
from neutron.db.models import l3ha as l3ha_model
from neutron.extensions import l3
from neutron.extensions import l3agentscheduler as l3agent
from neutron import manager
from neutron.objects import l3_hamode
from neutron.objects import l3agent as rb_obj
from neutron.scheduler import l3_agent_scheduler
from neutron.tests import base
@ -1615,11 +1615,9 @@ class L3AgentSchedulerDbMixinTestCase(L3HATestCaseMixin):
agent = agents.pop()
self.plugin.remove_router_from_l3_agent(
self.adminContext, agent.id, router['id'])
session = self.adminContext.session
db = l3ha_model.L3HARouterAgentPortBinding
results = session.query(db).filter_by(
router_id=router['id'])
results = [binding.l3_agent_id for binding in results.all()]
objs = l3_hamode.L3HARouterAgentPortBinding.get_objects(
self.adminContext, router_id=router['id'])
results = [binding.l3_agent_id for binding in objs]
self.assertNotIn(agent.id, results)
def test_add_ha_interface_to_l3_agent(self):