neutron/neutron/db/bgp_dragentscheduler_db.py
vikram.choudhary bede37f5e2 BGP Dynamic Routing: introduce BgpDrScheduler model
This patch implements a new extension called "bgp_dragentscheduler" which
does instant & auto scheuling of BgpSpeakers to an active BgpDrAgent. In
addition to this the patch also implements the basic CRUD requirement for
binding BgpSpeakers and BgpDrAgent.

BgpSpeaker to BgpDrAgent association can be 1-to-n. An admin user can only
associate/disassociate BgpSpeaker to/from a BgpDRAgent. Default scheduler
class will only assign non-scheduled BgpSpeaker to an active BgpDrAgent.

Partially-Implements: blueprint bgp-dynamic-routing
Co-Authored-By: Ryan Tidwell <ryan.tidwell@hpe.com>
Co-Authored-By: Jaume Devesa <devvesa@gmail.com>
Co-Authored-By: vikram.choudhary <vikram.choudhary@huawei.com>
Co-Authored-By: Numan Siddique <nusiddiq@redhat.com>
Change-Id: Id305d9a583116e155441ac5979bf3f6aa6a8258b
2016-02-12 14:17:57 +05:30

178 lines
7.3 KiB
Python

# Copyright 2016 Huawei Technologies India Pvt. Ltd.
# 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_config import cfg
from oslo_db import exception as db_exc
from oslo_log import log as logging
import sqlalchemy as sa
from sqlalchemy import orm
from neutron._i18n import _
from neutron._i18n import _LW
from neutron.db import agents_db
from neutron.db import agentschedulers_db as as_db
from neutron.db import model_base
from neutron.extensions import bgp_dragentscheduler as bgp_dras_ext
from neutron.services.bgp.common import constants as bgp_consts
LOG = logging.getLogger(__name__)
BGP_DRAGENT_SCHEDULER_OPTS = [
cfg.StrOpt(
'bgp_drscheduler_driver',
default='neutron.services.bgp.scheduler'
'.bgp_dragent_scheduler.ChanceScheduler',
help=_('Driver used for scheduling BGP speakers to BGP DrAgent')),
]
cfg.CONF.register_opts(BGP_DRAGENT_SCHEDULER_OPTS)
class BgpSpeakerDrAgentBinding(model_base.BASEV2):
"""Represents a mapping between BGP speaker and BGP DRAgent"""
__tablename__ = 'bgp_speaker_dragent_bindings'
bgp_speaker_id = sa.Column(sa.String(length=36),
sa.ForeignKey("bgp_speakers.id",
ondelete='CASCADE'),
nullable=False)
dragent = orm.relation(agents_db.Agent)
agent_id = sa.Column(sa.String(length=36),
sa.ForeignKey("agents.id",
ondelete='CASCADE'),
primary_key=True)
class BgpDrAgentSchedulerDbMixin(bgp_dras_ext.BgpDrSchedulerPluginBase,
as_db.AgentSchedulerDbMixin):
bgp_drscheduler = None
def schedule_unscheduled_bgp_speakers(self, context, host):
if self.bgp_drscheduler:
return self.bgp_drscheduler.schedule_unscheduled_bgp_speakers(
context, host)
else:
LOG.warning(_LW("Cannot schedule BgpSpeaker to DrAgent. "
"Reason: No scheduler registered."))
def schedule_bgp_speaker(self, context, created_bgp_speaker):
if self.bgp_drscheduler:
self.bgp_drscheduler.schedule(context, created_bgp_speaker)
else:
LOG.warning(_LW("Cannot schedule BgpSpeaker to DrAgent. "
"Reason: No scheduler registered."))
def add_bgp_speaker_to_dragent(self, context, agent_id, speaker_id):
"""Associate a BgpDrAgent with a BgpSpeaker."""
try:
self._save_bgp_speaker_dragent_binding(context,
agent_id,
speaker_id)
except db_exc.DBDuplicateEntry:
raise bgp_dras_ext.DrAgentAssociationError(
agent_id=agent_id)
LOG.debug('BgpSpeaker %(bgp_speaker_id)s added to '
'BgpDrAgent %(agent_id)s',
{'bgp_speaker_id': speaker_id, 'agent_id': agent_id})
def _save_bgp_speaker_dragent_binding(self, context,
agent_id, speaker_id):
with context.session.begin(subtransactions=True):
agent_db = self._get_agent(context, agent_id)
agent_up = agent_db['admin_state_up']
is_agent_bgp = (agent_db['agent_type'] ==
bgp_consts.AGENT_TYPE_BGP_ROUTING)
if not is_agent_bgp or not agent_up:
raise bgp_dras_ext.DrAgentInvalid(id=agent_id)
binding = BgpSpeakerDrAgentBinding()
binding.bgp_speaker_id = speaker_id
binding.agent_id = agent_id
context.session.add(binding)
def remove_bgp_speaker_from_dragent(self, context, agent_id, speaker_id):
with context.session.begin(subtransactions=True):
agent_db = self._get_agent(context, agent_id)
is_agent_bgp = (agent_db['agent_type'] ==
bgp_consts.AGENT_TYPE_BGP_ROUTING)
if not is_agent_bgp:
raise bgp_dras_ext.DrAgentInvalid(id=agent_id)
query = context.session.query(BgpSpeakerDrAgentBinding)
query = query.filter_by(bgp_speaker_id=speaker_id,
agent_id=agent_id)
num_deleted = query.delete()
if not num_deleted:
raise bgp_dras_ext.DrAgentNotHostingBgpSpeaker(
bgp_speaker_id=speaker_id,
agent_id=agent_id)
LOG.debug('BgpSpeaker %(bgp_speaker_id)s removed from '
'BgpDrAgent %(agent_id)s',
{'bgp_speaker_id': speaker_id,
'agent_id': agent_id})
def get_dragents_hosting_bgp_speakers(self, context, bgp_speaker_ids,
active=None, admin_state_up=None):
query = context.session.query(BgpSpeakerDrAgentBinding)
query = query.options(orm.contains_eager(
BgpSpeakerDrAgentBinding.dragent))
query = query.join(BgpSpeakerDrAgentBinding.dragent)
if len(bgp_speaker_ids) == 1:
query = query.filter(
BgpSpeakerDrAgentBinding.bgp_speaker_id == (
bgp_speaker_ids[0]))
elif bgp_speaker_ids:
query = query.filter(
BgpSpeakerDrAgentBinding.bgp_speaker_id in bgp_speaker_ids)
if admin_state_up is not None:
query = query.filter(agents_db.Agent.admin_state_up ==
admin_state_up)
return [binding.dragent
for binding in query
if as_db.AgentSchedulerDbMixin.is_eligible_agent(
active, binding.dragent)]
def get_dragent_bgp_speaker_bindings(self, context):
return context.session.query(BgpSpeakerDrAgentBinding).all()
def list_dragent_hosting_bgp_speaker(self, context, speaker_id):
dragents = self.get_dragents_hosting_bgp_speakers(context,
[speaker_id])
agent_ids = [dragent.id for dragent in dragents]
if not agent_ids:
return {'agents': []}
return {'agents': self.get_agents(context, filters={'id': agent_ids})}
def list_bgp_speaker_on_dragent(self, context, agent_id):
query = context.session.query(BgpSpeakerDrAgentBinding.bgp_speaker_id)
query = query.filter_by(agent_id=agent_id)
bgp_speaker_ids = [item[0] for item in query]
if not bgp_speaker_ids:
# Exception will be thrown if the requested agent does not exist.
self._get_agent(context, agent_id)
return {'bgp_speakers': []}
return {'bgp_speakers':
self.get_bgp_speakers(context,
filters={'id': bgp_speaker_ids})}