tap-as-a-service/neutron_taas/db/taas_db.py
Deepak Tiwari 8332a396b1 Enhancements to TaaS agent driver failure handling
As per current handling if there is any failure in the taas agent driver
there is no way to convey that to the plugin. Due to which end user gets
the impression that everything went well. This is confusing.

Now, as part of this enhancement, when the tap resources are :-

1. Created: iniitally they shall be created with status 'DOWN' by TaaS plugin. Once
the TaaS agent driver exectuion is finished, it will ask the plugin to
set the status to either 'ACTIVE' or 'ERROR' depending on whether the
driver was able to do its tasks successfully or not.

2. Deleted: as per curretn handling plugin used to first delete the
resources from DB and then inform the agent. Now the sequence would be
that plugin informs agent first. Agent shall ask the plugin to set the
resource status t either INACTIVE (indicates successful deletion,
whereby plugin shall clear the resources from the DB) or
PENDING_DELETE (failure in deleting the resources from driver, whereby
plugin would simply update the status for the resource in DB).

Change-Id: If8b1aba3b3955fd705f2a13a79c7225a03369da6
2020-08-19 14:32:46 +00:00

292 lines
11 KiB
Python

# Copyright (C) 2018 AT&T
# Copyright (C) 2015 Ericsson AB
# Copyright (c) 2015 Gigamon
#
# 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 sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.orm import exc
from neutron_lib import constants
from neutron_lib.db import model_base
from neutron_lib.db import model_query
from neutron_lib.db import utils as db_utils
from neutron_lib.plugins import directory
from neutron_taas.extensions import taas
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import uuidutils
LOG = logging.getLogger(__name__)
class TapService(model_base.BASEV2, model_base.HasId,
model_base.HasProjectNoIndex):
# Represents a V2 TapService Object
__tablename__ = 'tap_services'
name = sa.Column(sa.String(255), nullable=True)
description = sa.Column(sa.String(1024), nullable=True)
port_id = sa.Column(sa.String(36), nullable=False)
status = sa.Column(sa.String(16), nullable=False,
server_default=constants.ACTIVE)
class TapFlow(model_base.BASEV2, model_base.HasId,
model_base.HasProjectNoIndex):
# Represents a V2 TapFlow Object
__tablename__ = 'tap_flows'
name = sa.Column(sa.String(255), nullable=True)
description = sa.Column(sa.String(1024), nullable=True)
tap_service_id = sa.Column(sa.String(36),
sa.ForeignKey("tap_services.id",
ondelete="CASCADE"),
nullable=False)
source_port = sa.Column(sa.String(36), nullable=False)
direction = sa.Column(sa.Enum('IN', 'OUT', 'BOTH',
name='tapflows_direction'),
nullable=False)
status = sa.Column(sa.String(16), nullable=False,
server_default=constants.ACTIVE)
vlan_filter = sa.Column(sa.String(1024), nullable=True)
class TapIdAssociation(model_base.BASEV2):
# Internal mapping between a TAP Service and
# id to be used by the Agents
__tablename__ = 'tap_id_associations'
tap_service_id = sa.Column(sa.String(36),
sa.ForeignKey("tap_services.id",
ondelete='SET NULL'),
nullable=True)
taas_id = sa.Column(sa.Integer, primary_key=True, unique=True)
tap_service = orm.relationship(
TapService,
backref=orm.backref("tap_service_id",
lazy="joined"),
primaryjoin='TapService.id==TapIdAssociation.tap_service_id')
class Taas_db_Mixin(taas.TaasPluginBase):
def _core_plugin(self):
return directory.get_plugin()
def _get_tap_service(self, context, id):
try:
return model_query.get_by_id(context, TapService, id)
except exc.NoResultFound:
raise taas.TapServiceNotFound(tap_id=id)
def _get_tap_id_association(self, context, tap_service_id):
try:
query = model_query.query_with_hooks(context, TapIdAssociation)
return query.filter(TapIdAssociation.tap_service_id ==
tap_service_id).one()
except exc.NoResultFound:
raise taas.TapServiceNotFound(tap_id=tap_service_id)
def _get_tap_flow(self, context, id):
try:
return model_query.get_by_id(context, TapFlow, id)
except Exception:
raise taas.TapFlowNotFound(flow_id=id)
def _make_tap_service_dict(self, tap_service, fields=None):
res = {'id': tap_service['id'],
'tenant_id': tap_service['tenant_id'],
'name': tap_service['name'],
'description': tap_service['description'],
'port_id': tap_service['port_id'],
'status': tap_service['status']}
return db_utils.resource_fields(res, fields)
def _make_tap_id_association_dict(self, tap_id_association):
res = {'tap_service_id': tap_id_association['tap_service_id'],
'taas_id': tap_id_association['taas_id']}
return res
def _make_tap_flow_dict(self, tap_flow, fields=None):
res = {'id': tap_flow['id'],
'tenant_id': tap_flow['tenant_id'],
'tap_service_id': tap_flow['tap_service_id'],
'name': tap_flow['name'],
'description': tap_flow['description'],
'source_port': tap_flow['source_port'],
'direction': tap_flow['direction'],
'status': tap_flow['status'],
'vlan_filter': tap_flow['vlan_filter']}
return db_utils.resource_fields(res, fields)
def create_tap_service(self, context, tap_service):
LOG.debug("create_tap_service() called")
t_s = tap_service['tap_service']
tenant_id = t_s['tenant_id']
with context.session.begin(subtransactions=True):
tap_service_db = TapService(
id=uuidutils.generate_uuid(),
tenant_id=tenant_id,
name=t_s['name'],
description=t_s['description'],
port_id=t_s['port_id'],
status=constants.DOWN,
)
context.session.add(tap_service_db)
return self._make_tap_service_dict(tap_service_db)
def _rebuild_taas_id_allocation_range(self, context):
query = context.session.query(
TapIdAssociation).all()
allocate_taas_id_list = [_q.taas_id for _q in query]
first_taas_id = cfg.CONF.taas.vlan_range_start
# Exclude range end
last_taas_id = cfg.CONF.taas.vlan_range_end
all_taas_id_set = set(range(first_taas_id, last_taas_id))
vaild_taas_id_set = all_taas_id_set - set(allocate_taas_id_list)
for _id in vaild_taas_id_set:
# new taas id
context.session.add(TapIdAssociation(
taas_id=_id))
def _allocate_taas_id_with_tap_service_id(self, context, tap_service_id):
query = context.session.query(TapIdAssociation).filter_by(
tap_service_id=None).first()
if not query:
self._rebuild_taas_id_allocation_range(context)
# try again
query = context.session.query(TapIdAssociation).filter_by(
tap_service_id=None).first()
if query:
query.update({"tap_service_id": tap_service_id})
return query
# not found
raise taas.TapServiceLimitReached()
def create_tap_id_association(self, context, tap_service_id):
LOG.debug("create_tap_id_association() called")
# create the TapIdAssociation object
with context.session.begin(subtransactions=True):
# allocate Taas id.
# if conflict happened, it will raise db.DBDuplicateEntry.
# this will be retry request again in neutron controller framework.
# so we just make sure TapIdAssociation field taas_id is unique
tap_id_association_db = self._allocate_taas_id_with_tap_service_id(
context, tap_service_id)
return self._make_tap_id_association_dict(tap_id_association_db)
def create_tap_flow(self, context, tap_flow):
LOG.debug("create_tap_flow() called")
t_f = tap_flow['tap_flow']
tenant_id = t_f['tenant_id']
# TODO(Vinay): Check for the tenant_id validation
# TODO(Vinay): Check for the source port validation
with context.session.begin(subtransactions=True):
tap_flow_db = TapFlow(
id=uuidutils.generate_uuid(),
tenant_id=tenant_id,
name=t_f['name'],
description=t_f['description'],
tap_service_id=t_f['tap_service_id'],
source_port=t_f['source_port'],
direction=t_f['direction'],
status=constants.DOWN,
vlan_filter=t_f['vlan_filter'],
)
context.session.add(tap_flow_db)
return self._make_tap_flow_dict(tap_flow_db)
def delete_tap_service(self, context, id):
LOG.debug("delete_tap_service() called")
count = context.session.query(TapService).filter_by(id=id).delete()
if not count:
raise taas.TapServiceNotFound(tap_id=id)
def delete_tap_flow(self, context, id):
LOG.debug("delete_tap_flow() called")
count = context.session.query(TapFlow).filter_by(id=id).delete()
if not count:
raise taas.TapFlowNotFound(flow_id=id)
def get_tap_service(self, context, id, fields=None):
LOG.debug("get_tap_service() called")
t_s = self._get_tap_service(context, id)
return self._make_tap_service_dict(t_s, fields)
def get_tap_id_association(self, context, tap_service_id):
LOG.debug("get_tap_id_association() called")
t_a = self._get_tap_id_association(context, tap_service_id)
return self._make_tap_id_association_dict(t_a)
def get_tap_flow(self, context, id, fields=None):
LOG.debug("get_tap_flow() called")
t_f = self._get_tap_flow(context, id)
return self._make_tap_flow_dict(t_f, fields)
def get_tap_services(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
LOG.debug("get_tap_services() called")
return model_query.get_collection(context, TapService,
self._make_tap_service_dict,
filters=filters, fields=fields)
def get_tap_flows(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
LOG.debug("get_tap_flows() called")
return model_query.get_collection(context, TapFlow,
self._make_tap_flow_dict,
filters=filters, fields=fields)
def _get_port_details(self, context, port_id):
with context.session.begin(subtransactions=True):
port = self._core_plugin().get_port(context, port_id)
return port
def update_tap_service(self, context, id, tap_service):
LOG.debug("update_tap_service() called")
t_s = tap_service['tap_service']
with context.session.begin(subtransactions=True):
tap_service_db = self._get_tap_service(context, id)
tap_service_db.update(t_s)
return self._make_tap_service_dict(tap_service_db)
def update_tap_flow(self, context, id, tap_flow):
LOG.debug("update_tap_flow() called")
t_f = tap_flow['tap_flow']
with context.session.begin(subtransactions=True):
tap_flow_db = self._get_tap_flow(context, id)
tap_flow_db.update(t_f)
return self._make_tap_flow_dict(tap_flow_db)