This is the initial commit of TaaS sources code.
Change-Id: Id6f0ffaa03bcbe82dd1b910c7bee5b55dfa25a42
This commit is contained in:
parent
deefb05067
commit
44838a6d0a
0
neutron_taas/db/__init__.py
Executable file
0
neutron_taas/db/__init__.py
Executable file
0
neutron_taas/db/migration/__init__.py
Normal file
0
neutron_taas/db/migration/__init__.py
Normal file
52
neutron_taas/db/migration/taas_init_ops.py
Normal file
52
neutron_taas/db/migration/taas_init_ops.py
Normal file
@ -0,0 +1,52 @@
|
||||
# Copyright 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.
|
||||
#
|
||||
|
||||
# Initial schema operations for Tap-as-a-Service service plugin
|
||||
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
direction_types = sa.Enum('IN', 'OUT', 'BOTH', name='tapflows_direction')
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
'tap_services',
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('tenant_id', sa.String(length=255), nullable=True),
|
||||
sa.Column('name', sa.String(length=255), nullable=True),
|
||||
sa.Column('description', sa.String(length=1024), nullable=True),
|
||||
sa.Column('port_id', sa.String(36), nullable=False),
|
||||
sa.Column('network_id', sa.String(36), nullable=True))
|
||||
|
||||
op.create_table(
|
||||
'tap_flows',
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('tenant_id', sa.String(length=255), nullable=True),
|
||||
sa.Column('name', sa.String(length=255), nullable=True),
|
||||
sa.Column('description', sa.String(length=1024), nullable=True),
|
||||
sa.Column('tap_service_id', sa.String(length=36),
|
||||
sa.ForeignKey("tap_services.id",
|
||||
ondelete="CASCADE"), nullable=False),
|
||||
sa.Column('source_port', sa.String(length=36), nullable=False),
|
||||
sa.Column('direction', direction_types, nullable=False))
|
||||
|
||||
op.create_table(
|
||||
'tap_id_associations',
|
||||
sa.Column(sa.String(length=36)),
|
||||
sa.Column(sa.INTEGER, primary_key=True, autoincrement=True))
|
231
neutron_taas/db/taas_db.py
Executable file
231
neutron_taas/db/taas_db.py
Executable file
@ -0,0 +1,231 @@
|
||||
# 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.
|
||||
|
||||
|
||||
from neutron.db import common_db_mixin as base_db
|
||||
from neutron.db import model_base
|
||||
from neutron.db import models_v2
|
||||
from neutron import manager
|
||||
# from neutron.openstack.common import uuidutils
|
||||
from neutron_taas.extensions import taas
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.orm import exc
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TapService(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
||||
|
||||
# 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)
|
||||
network_id = sa.Column(sa.String(36), nullable=True)
|
||||
'''
|
||||
def __init__(self, id, tenant_id, name, description, port_id, network_id):
|
||||
self.id = id
|
||||
self.tenant_id = tenant_id
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.port_id = port_id
|
||||
self.network_id = network_id
|
||||
|
||||
def __repr__(self):
|
||||
return "<TapService> %s" % self.id
|
||||
'''
|
||||
|
||||
|
||||
class TapFlow(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
||||
|
||||
# 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'))
|
||||
|
||||
|
||||
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))
|
||||
taas_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
|
||||
class Tass_db_Mixin(taas.TaasPluginBase, base_db.CommonDbMixin):
|
||||
|
||||
def _core_plugin(self):
|
||||
return manager.NeutronManager.get_plugin()
|
||||
|
||||
def _get_tap_service(self, context, id):
|
||||
try:
|
||||
return self._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 = self._model_query(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 self._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'],
|
||||
'network_id': tap_service['network_id']}
|
||||
|
||||
return self._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']}
|
||||
|
||||
return self._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 = self._get_tenant_id_for_create(context, t_s)
|
||||
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'],
|
||||
network_id=t_s['network_id']
|
||||
)
|
||||
context.session.add(tap_service_db)
|
||||
|
||||
# create the TapIdAssociation object
|
||||
with context.session.begin(subtransactions=True):
|
||||
tap_id_association_db = TapIdAssociation(
|
||||
tap_service_id=tap_service_db['id']
|
||||
)
|
||||
context.session.add(tap_id_association_db)
|
||||
|
||||
return self._make_tap_service_dict(tap_service_db)
|
||||
|
||||
def create_tap_flow(self, context, tap_flow):
|
||||
LOG.debug("create_tap_flow() called")
|
||||
t_f = tap_flow['tap_flow']
|
||||
tenant_id = self._get_tenant_id_for_create(context, t_f)
|
||||
# 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']
|
||||
)
|
||||
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)
|
||||
|
||||
context.session.query(TapIdAssociation).filter_by(
|
||||
tap_service_id=id).delete()
|
||||
|
||||
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 self._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 self._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
|
0
neutron_taas/extensions/__init__.py
Normal file
0
neutron_taas/extensions/__init__.py
Normal file
260
neutron_taas/extensions/taas.py
Normal file
260
neutron_taas/extensions/taas.py
Normal file
@ -0,0 +1,260 @@
|
||||
# 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 abc
|
||||
|
||||
from neutron.api import extensions
|
||||
from neutron.api.v2 import attributes as attr
|
||||
from neutron.api.v2 import resource_helper
|
||||
from neutron.common import exceptions as qexception
|
||||
from neutron.plugins.common import constants
|
||||
from neutron.services import service_base
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
import six
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
# TaaS exception handling classes
|
||||
|
||||
|
||||
class TapServiceNotFound(qexception.NotFound):
|
||||
message = _("Tap Service %(tap_id)s does not exist")
|
||||
|
||||
|
||||
class TapFlowNotFound(qexception.NotFound):
|
||||
message = _("Tap Flow %(flow_id)s does not exist")
|
||||
|
||||
|
||||
class InvalidDestinationPort(qexception.NotFound):
|
||||
message = _("Destination Port %(port)s does not exist")
|
||||
|
||||
|
||||
class InvalidSourcePort(qexception.NotFound):
|
||||
message = _("Source Port %(port)s does not exist")
|
||||
|
||||
|
||||
class PortDoesNotBelongToTenant(qexception.NotAuthorized):
|
||||
message = _("The port specified does not belong to the tenant")
|
||||
|
||||
|
||||
class TapServiceNotBelongToTenant(qexception.NotAuthorized):
|
||||
message = _("Tap Service specified does not belong to the tenant")
|
||||
|
||||
|
||||
class TapServiceLimitReached(qexception.OverQuota):
|
||||
message = _("Reached the upper limit of Tap Services Creatable")
|
||||
|
||||
|
||||
direction_enum = [None, 'IN', 'OUT', 'BOTH']
|
||||
|
||||
|
||||
'''
|
||||
Resource Attribute Map:
|
||||
|
||||
Note:
|
||||
|
||||
'tap_services' data model refers to the Tap Service created. The port_id
|
||||
can be specified by the tenant to which the mirrored data is sent. If port_id
|
||||
is specified then the network_id will not be used. If the port_id is not
|
||||
specified, the TaaS will create a port on the network specified by the
|
||||
network_id.
|
||||
'''
|
||||
|
||||
RESOURCE_ATTRIBUTE_MAP = {
|
||||
'tap_services': {
|
||||
'id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid': None}, 'is_visible': True,
|
||||
'primary_key': True},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:string': None},
|
||||
'required_by_policy': True, 'is_visible': True},
|
||||
'name': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string': None},
|
||||
'is_visible': True, 'default': ''},
|
||||
'description': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string': None},
|
||||
'is_visible': True, 'default': ''},
|
||||
'port_id': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'is_visible': True},
|
||||
'network_id': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'is_visible': False}
|
||||
},
|
||||
'tap_flows': {
|
||||
'id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid': None}, 'is_visible': True,
|
||||
'primary_key': True},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:string': None},
|
||||
'required_by_policy': True, 'is_visible': True},
|
||||
'name': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string': None},
|
||||
'is_visible': True, 'default': ''},
|
||||
'description': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string': None},
|
||||
'is_visible': True, 'default': ''},
|
||||
'tap_service_id': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'required_by_policy': True, 'is_visible': True},
|
||||
'source_port': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'required_by_policy': True, 'is_visible': True},
|
||||
'direction': {'allow_post': True, 'allow_put': False,
|
||||
'validate': {'type:string': direction_enum},
|
||||
'is_visible': True}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
taas_quota_opts = [
|
||||
cfg.IntOpt('quota_tap_service',
|
||||
default=1,
|
||||
help=_('Number of Tap Service instances allowed per tenant')),
|
||||
cfg.IntOpt('quota_tap_flow',
|
||||
default=10,
|
||||
help=_('Number of Tap flows allowed per tenant'))
|
||||
]
|
||||
cfg.CONF.register_opts(taas_quota_opts, 'QUOTAS')
|
||||
|
||||
|
||||
TaasOpts = [
|
||||
cfg.StrOpt(
|
||||
'driver',
|
||||
default='',
|
||||
help=_("Name of the TaaS Driver")),
|
||||
cfg.BoolOpt(
|
||||
'enabled',
|
||||
default=False,
|
||||
help=_("Enable TaaS")),
|
||||
cfg.IntOpt(
|
||||
'vlan_range_start',
|
||||
default=3900,
|
||||
help=_("Starting range of TAAS VLAN IDs")),
|
||||
cfg.IntOpt(
|
||||
'vlan_range_end',
|
||||
default=4000,
|
||||
help=_("End range of TAAS VLAN IDs")),
|
||||
]
|
||||
cfg.CONF.register_opts(TaasOpts, 'taas')
|
||||
|
||||
|
||||
class Taas(extensions.ExtensionDescriptor):
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Neutron Tap as a Service"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return "taas"
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return "Neutron Tap as a Service Extension."
|
||||
|
||||
@classmethod
|
||||
def get_namespace(cls):
|
||||
return "http://wiki.openstack.org/wiki/Neutron/Taas/#API"
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2015-01-14T10:00:00-00:00"
|
||||
|
||||
@classmethod
|
||||
def get_plugin_interface(cls):
|
||||
return TaasPluginBase
|
||||
|
||||
@classmethod
|
||||
def get_resources(cls):
|
||||
"""Returns Ext Resources."""
|
||||
plural_mappings = resource_helper.build_plural_mappings(
|
||||
{}, RESOURCE_ATTRIBUTE_MAP)
|
||||
|
||||
attr.PLURALS.update(plural_mappings)
|
||||
|
||||
return resource_helper.build_resource_info(plural_mappings,
|
||||
RESOURCE_ATTRIBUTE_MAP,
|
||||
constants.TAAS,
|
||||
translate_name=True,
|
||||
allow_bulk=True)
|
||||
|
||||
def update_attributes_map(self, attributes):
|
||||
super(Taas, self).update_attributes_map(
|
||||
attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
return RESOURCE_ATTRIBUTE_MAP
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class TaasPluginBase(service_base.ServicePluginBase):
|
||||
def get_plugin_name(self):
|
||||
return constants.TAAS
|
||||
|
||||
def get_plugin_description(self):
|
||||
return "Tap Service Plugin"
|
||||
|
||||
def get_plugin_type(self):
|
||||
return constants.TAAS
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_tap_service(self, context, tap_service):
|
||||
"""Create a Tap Service."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_tap_service(self, context, id):
|
||||
"""Delete a Tap Service."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_tap_service(self, context, id, fields=None):
|
||||
"""Get a Tap Service."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_tap_services(self, context, filters=None, fields=None,
|
||||
sorts=None, limit=None, marker=None,
|
||||
page_reverse=False):
|
||||
"""List all Tap Services."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_tap_flow(self, context, tap_flow):
|
||||
"""Create a Tap Flow."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_tap_flow(self, context, id, fields=None):
|
||||
"""Get a Tap Flow."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_tap_flow(self, context, id):
|
||||
"""Delete a Tap Flow."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_tap_flows(self, context, filters=None, fields=None,
|
||||
sorts=None, limit=None, marker=None,
|
||||
page_reverse=False):
|
||||
"""List all Tap Flows."""
|
||||
pass
|
98
neutron_taas/neutron_dependencies/constants.py
Normal file
98
neutron_taas/neutron_dependencies/constants.py
Normal file
@ -0,0 +1,98 @@
|
||||
# Copyright 2012 OpenStack Foundation.
|
||||
# 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.
|
||||
|
||||
# Service type constants:
|
||||
CORE = "CORE"
|
||||
DUMMY = "DUMMY"
|
||||
LOADBALANCER = "LOADBALANCER"
|
||||
LOADBALANCERV2 = "LOADBALANCERV2"
|
||||
FIREWALL = "FIREWALL"
|
||||
VPN = "VPN"
|
||||
METERING = "METERING"
|
||||
L3_ROUTER_NAT = "L3_ROUTER_NAT"
|
||||
FLAVORS = "FLAVORS"
|
||||
TAAS = "TAAS"
|
||||
|
||||
# Maps extension alias to service type
|
||||
EXT_TO_SERVICE_MAPPING = {
|
||||
'dummy': DUMMY,
|
||||
'lbaas': LOADBALANCER,
|
||||
'lbaasv2': LOADBALANCERV2,
|
||||
'fwaas': FIREWALL,
|
||||
'vpnaas': VPN,
|
||||
'metering': METERING,
|
||||
'router': L3_ROUTER_NAT,
|
||||
'flavors': FLAVORS,
|
||||
'taas': TAAS
|
||||
}
|
||||
|
||||
# TODO(salvatore-orlando): Move these (or derive them) from conf file
|
||||
ALLOWED_SERVICES = [CORE, DUMMY, LOADBALANCER, FIREWALL, VPN, METERING,
|
||||
L3_ROUTER_NAT, LOADBALANCERV2, TAAS]
|
||||
|
||||
COMMON_PREFIXES = {
|
||||
CORE: "",
|
||||
DUMMY: "/dummy_svc",
|
||||
LOADBALANCER: "/lb",
|
||||
LOADBALANCERV2: "/lbaas",
|
||||
FIREWALL: "/fw",
|
||||
VPN: "/vpn",
|
||||
METERING: "/metering",
|
||||
L3_ROUTER_NAT: "",
|
||||
TAAS: "/taas"
|
||||
}
|
||||
|
||||
# Service operation status constants
|
||||
ACTIVE = "ACTIVE"
|
||||
DOWN = "DOWN"
|
||||
CREATED = "CREATED"
|
||||
PENDING_CREATE = "PENDING_CREATE"
|
||||
PENDING_UPDATE = "PENDING_UPDATE"
|
||||
PENDING_DELETE = "PENDING_DELETE"
|
||||
INACTIVE = "INACTIVE"
|
||||
ERROR = "ERROR"
|
||||
|
||||
ACTIVE_PENDING_STATUSES = (
|
||||
ACTIVE,
|
||||
PENDING_CREATE,
|
||||
PENDING_UPDATE
|
||||
)
|
||||
|
||||
# Network Type constants
|
||||
TYPE_FLAT = 'flat'
|
||||
TYPE_GRE = 'gre'
|
||||
TYPE_LOCAL = 'local'
|
||||
TYPE_VXLAN = 'vxlan'
|
||||
TYPE_VLAN = 'vlan'
|
||||
TYPE_NONE = 'none'
|
||||
|
||||
# Values for network_type
|
||||
|
||||
# For VLAN Network
|
||||
MIN_VLAN_TAG = 1
|
||||
MAX_VLAN_TAG = 4094
|
||||
|
||||
# For GRE Tunnel
|
||||
MIN_GRE_ID = 1
|
||||
MAX_GRE_ID = 2 ** 32 - 1
|
||||
|
||||
# For VXLAN Tunnel
|
||||
MIN_VXLAN_VNI = 1
|
||||
MAX_VXLAN_VNI = 2 ** 24 - 1
|
||||
VXLAN_UDP_PORT = 4789
|
||||
|
||||
# Network Type MTU overhead
|
||||
GRE_ENCAP_OVERHEAD = 42
|
||||
VXLAN_ENCAP_OVERHEAD = 50
|
1058
neutron_taas/neutron_dependencies/neutron.conf
Normal file
1058
neutron_taas/neutron_dependencies/neutron.conf
Normal file
File diff suppressed because it is too large
Load Diff
120
neutron_taas/neutron_dependencies/neutron_taas_db_init.py
Executable file
120
neutron_taas/neutron_dependencies/neutron_taas_db_init.py
Executable file
@ -0,0 +1,120 @@
|
||||
# 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.
|
||||
|
||||
|
||||
from neutron.db import models_v2
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
import sys
|
||||
|
||||
BASE = declarative_base()
|
||||
|
||||
_ENGINE = None
|
||||
_MAKER = None
|
||||
|
||||
direction_types = sa.Enum('IN', 'OUT', 'BOTH', name='tapflows_direction')
|
||||
|
||||
|
||||
class TapService(BASE, models_v2.HasId, models_v2.HasTenant):
|
||||
# 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)
|
||||
network_id = sa.Column(sa.String(36), nullable=True)
|
||||
|
||||
|
||||
class TapFlow(BASE, models_v2.HasId, models_v2.HasTenant):
|
||||
# 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(direction_types, nullable=False)
|
||||
|
||||
|
||||
class TapIdAssociation(BASE):
|
||||
# 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))
|
||||
taas_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
|
||||
|
||||
def configure_db(options):
|
||||
# Establish the database, create an engine if needed, and
|
||||
# register the models.
|
||||
# param options: Mapping of configuration options
|
||||
|
||||
global _ENGINE
|
||||
if not _ENGINE:
|
||||
_ENGINE = create_engine(options['sql_connection'],
|
||||
echo=False,
|
||||
echo_pool=True,
|
||||
pool_recycle=3600)
|
||||
register_models()
|
||||
|
||||
|
||||
def clear_db():
|
||||
global _ENGINE
|
||||
assert _ENGINE
|
||||
for table in reversed(BASE.metadata.sorted_tables):
|
||||
_ENGINE.execute(table.delete())
|
||||
|
||||
|
||||
def get_session(autocommit=True, expire_on_commit=False):
|
||||
# Helper method to grab session
|
||||
global _MAKER, _ENGINE
|
||||
if not _MAKER:
|
||||
assert _ENGINE
|
||||
_MAKER = sessionmaker(bind=_ENGINE,
|
||||
autocommit=autocommit,
|
||||
expire_on_commit=expire_on_commit)
|
||||
return _MAKER()
|
||||
|
||||
|
||||
def register_models():
|
||||
# Register Models and create properties
|
||||
global _ENGINE
|
||||
assert _ENGINE
|
||||
BASE.metadata.create_all(_ENGINE)
|
||||
|
||||
|
||||
def unregister_mode():
|
||||
# Unregister Models, useful clearing out data before testing
|
||||
global _ENGINE
|
||||
assert _ENGINE
|
||||
BASE.metadata.drop_all(_ENGINE)
|
||||
|
||||
|
||||
def main(argv):
|
||||
print'Configuring the Neutron TaaS database'
|
||||
configure_db({'sql_connection': ('mysql://root:%s@127.0.0.1/neutron' %
|
||||
argv[0])})
|
||||
print'Configured the Neutron TaaS database'
|
||||
return
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
80
neutron_taas/neutron_dependencies/patch_conf_file.sh
Executable file
80
neutron_taas/neutron_dependencies/patch_conf_file.sh
Executable file
@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
INSERT_KEY="core_plugin"
|
||||
|
||||
T_CONF_FILE=$1
|
||||
N_CONF_FILE=$2
|
||||
|
||||
if [ -z $T_CONF_FILE ] || [ -z $N_CONF_FILE ]; then
|
||||
echo "Usage: $0 <taas-conf-file> <neutron-conf-file>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ACTION="unspecified"
|
||||
|
||||
while read -r LINE; do
|
||||
# Skip blank lines
|
||||
if [ ${#LINE} -eq 0 ]; then
|
||||
continue;
|
||||
fi
|
||||
|
||||
# Identify action to be performed based on section headers
|
||||
read -a WORD_ARRAY <<< $LINE
|
||||
if [[ ${WORD_ARRAY[0]} == "[DEFAULT]" ]]; then
|
||||
ACTION="merge"
|
||||
|
||||
# Advance to next line
|
||||
continue
|
||||
fi
|
||||
if [[ ${WORD_ARRAY[0]} == "[taas]" ]]; then
|
||||
ACTION="append"
|
||||
|
||||
# Add a blank line (separator) to Neutron config file
|
||||
echo "" >> $N_CONF_FILE
|
||||
fi
|
||||
|
||||
# Execute action
|
||||
case $ACTION in
|
||||
"merge")
|
||||
KEY="${WORD_ARRAY[0]}"
|
||||
VALUE="${WORD_ARRAY[2]}"
|
||||
|
||||
# Add 'value' to the end of the line beginning with 'key'
|
||||
# in the Neutron config file. If such a line does not
|
||||
# exist, create a 'key = value' line and insert it just
|
||||
# after the line beginning with 'INSERT_KEY'.
|
||||
grep ^$KEY $N_CONF_FILE > /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
sed -i "/^$KEY/ s/$/, $VALUE/" $N_CONF_FILE
|
||||
else
|
||||
sed -i "/^$INSERT_KEY/ a $KEY = $VALUE" $N_CONF_FILE
|
||||
fi
|
||||
;;
|
||||
|
||||
"append")
|
||||
# Add entire line to Neutron config file
|
||||
echo "$LINE" >> $N_CONF_FILE
|
||||
;;
|
||||
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done < $T_CONF_FILE
|
||||
|
||||
exit 0
|
102
neutron_taas/neutron_dependencies/repos.py
Normal file
102
neutron_taas/neutron_dependencies/repos.py
Normal file
@ -0,0 +1,102 @@
|
||||
# Copyright (c) 2015, A10 Networks
|
||||
#
|
||||
# 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 ConfigParser
|
||||
import importlib
|
||||
import os
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NeutronModules(object):
|
||||
|
||||
MODULES = {
|
||||
'neutron_fwaas': {
|
||||
'alembic-name': 'fwaas',
|
||||
},
|
||||
'neutron_lbaas': {
|
||||
'alembic-name': 'lbaas',
|
||||
},
|
||||
'neutron_vpnaas': {
|
||||
'alembic-name': 'vpnaas',
|
||||
},
|
||||
'neutron_taas': {
|
||||
'alembic-name': 'taas',
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.repos = {}
|
||||
for repo in self.MODULES:
|
||||
self.repos[repo] = {}
|
||||
self.repos[repo]['mod'] = self._import_or_none(repo)
|
||||
self.repos[repo]['ini'] = None
|
||||
|
||||
def _import_or_none(self, module):
|
||||
try:
|
||||
return importlib.import_module(module)
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
def installed_list(self):
|
||||
z = filter(lambda k: self.repos[k]['mod'] is not None, self.repos)
|
||||
LOG.debug("NeutronModules related repos installed = %s", z)
|
||||
return z
|
||||
|
||||
def module(self, module):
|
||||
return self.repos[module]['mod']
|
||||
|
||||
def alembic_name(self, module):
|
||||
return self.MODULES[module]['alembic-name']
|
||||
|
||||
# Return an INI parser for the child module. oslo.config is a bit too
|
||||
# magical in its INI loading, and in one notable case, we need to merge
|
||||
# together the [service_providers] section across at least four
|
||||
# repositories.
|
||||
def ini(self, module):
|
||||
if self.repos[module]['ini'] is None:
|
||||
neutron_dir = None
|
||||
try:
|
||||
neutron_dir = cfg.CONF.config_dir
|
||||
except cfg.NoSuchOptError:
|
||||
pass
|
||||
|
||||
if neutron_dir is None:
|
||||
neutron_dir = '/etc/neutron'
|
||||
|
||||
ini = ConfigParser.SafeConfigParser()
|
||||
ini_path = os.path.join(neutron_dir, '%s.conf' % module)
|
||||
if os.path.exists(ini_path):
|
||||
ini.read(ini_path)
|
||||
|
||||
self.repos[module]['ini'] = ini
|
||||
|
||||
return self.repos[module]['ini']
|
||||
|
||||
def service_providers(self, module):
|
||||
ini = self.ini(module)
|
||||
|
||||
sp = []
|
||||
try:
|
||||
for name, value in ini.items('service_providers'):
|
||||
if name == 'service_provider':
|
||||
sp.append(value)
|
||||
except ConfigParser.NoSectionError:
|
||||
pass
|
||||
|
||||
return sp
|
8
neutron_taas/neutron_dependencies/taas.conf
Executable file
8
neutron_taas/neutron_dependencies/taas.conf
Executable file
@ -0,0 +1,8 @@
|
||||
[DEFAULT]
|
||||
service_plugins = neutron_taas.services.taas.taas_plugin.TaasPlugin
|
||||
|
||||
[taas]
|
||||
driver = neutron_taas.services.taas.drivers.linux.ovs_taas.OvsTaasDriver
|
||||
enabled = True
|
||||
vlan_range_start = 3000
|
||||
vlan_range_end = 3500
|
60
neutron_taas/neutron_dependencies/topics.py
Normal file
60
neutron_taas/neutron_dependencies/topics.py
Normal file
@ -0,0 +1,60 @@
|
||||
# Copyright (c) 2012 OpenStack Foundation.
|
||||
#
|
||||
# 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.
|
||||
|
||||
NETWORK = 'network'
|
||||
SUBNET = 'subnet'
|
||||
PORT = 'port'
|
||||
SECURITY_GROUP = 'security_group'
|
||||
L2POPULATION = 'l2population'
|
||||
DVR = 'dvr'
|
||||
|
||||
CREATE = 'create'
|
||||
DELETE = 'delete'
|
||||
UPDATE = 'update'
|
||||
|
||||
AGENT = 'q-agent-notifier'
|
||||
PLUGIN = 'q-plugin'
|
||||
L3PLUGIN = 'q-l3-plugin'
|
||||
DHCP = 'q-dhcp-notifer'
|
||||
FIREWALL_PLUGIN = 'q-firewall-plugin'
|
||||
METERING_PLUGIN = 'q-metering-plugin'
|
||||
LOADBALANCER_PLUGIN = 'n-lbaas-plugin'
|
||||
TAAS_PLUGIN = 'n-taas-plugin'
|
||||
|
||||
L3_AGENT = 'l3_agent'
|
||||
DHCP_AGENT = 'dhcp_agent'
|
||||
METERING_AGENT = 'metering_agent'
|
||||
LOADBALANCER_AGENT = 'n-lbaas_agent'
|
||||
TAAS_AGENT = 'n-taas_agent'
|
||||
|
||||
|
||||
def get_topic_name(prefix, table, operation, host=None):
|
||||
"""Create a topic name.
|
||||
|
||||
The topic name needs to be synced between the agent and the
|
||||
plugin. The plugin will send a fanout message to all of the
|
||||
listening agents so that the agents in turn can perform their
|
||||
updates accordingly.
|
||||
|
||||
:param prefix: Common prefix for the plugin/agent message queues.
|
||||
:param table: The table in question (NETWORK, SUBNET, PORT).
|
||||
:param operation: The operation that invokes notification (CREATE,
|
||||
DELETE, UPDATE)
|
||||
:param host: Add host to the topic
|
||||
:returns: The topic name.
|
||||
"""
|
||||
if host:
|
||||
return '%s-%s-%s.%s' % (prefix, table, operation, host)
|
||||
return '%s-%s-%s' % (prefix, table, operation)
|
0
neutron_taas/services/__init__.py
Executable file
0
neutron_taas/services/__init__.py
Executable file
0
neutron_taas/services/taas/__init__.py
Executable file
0
neutron_taas/services/taas/__init__.py
Executable file
280
neutron_taas/services/taas/agents/TaasAgent.sh
Executable file
280
neutron_taas/services/taas/agents/TaasAgent.sh
Executable file
@ -0,0 +1,280 @@
|
||||
#!/bin/bash
|
||||
|
||||
taas_get_mac_addr()
|
||||
{
|
||||
NEUTRON_PORT=$1
|
||||
|
||||
MAC_ADDR=`neutron port-show $NEUTRON_PORT | grep mac_address | cut -d'|' -f 3 | cut -d' ' -f 2`
|
||||
|
||||
echo $MAC_ADDR
|
||||
}
|
||||
|
||||
taas_get_ovs_portname()
|
||||
{
|
||||
NEUTRON_PORT=$1
|
||||
|
||||
echo "qvo${NEUTRON_PORT:0:11}"
|
||||
}
|
||||
|
||||
taas_get_ovs_portid()
|
||||
{
|
||||
BRIDGE=$1
|
||||
OVS_PORT_NAME=$2
|
||||
|
||||
PORT_ID=`ovs-ofctl show $BRIDGE | grep $OVS_PORT_NAME | cut -d' ' -f 2 | cut -d'(' -f 1`
|
||||
|
||||
echo $PORT_ID
|
||||
}
|
||||
|
||||
taas_init()
|
||||
{
|
||||
HOST_IP=$1
|
||||
|
||||
# br-tap
|
||||
BRIDGE="tcp:$HOST_IP:6632"
|
||||
PATCHTAPINT_PORT_ID=`taas_get_ovs_portid $BRIDGE patch-tapint`
|
||||
PATCHTAPTUN_PORT_ID=`taas_get_ovs_portid $BRIDGE patch-taptun`
|
||||
ovs-ofctl add-flow $BRIDGE "table=0,priority=1,in_port=$PATCHTAPINT_PORT_ID,actions=resubmit(,1)"
|
||||
ovs-ofctl add-flow $BRIDGE "table=0,priority=1,in_port=$PATCHTAPTUN_PORT_ID,actions=resubmit(,2)"
|
||||
ovs-ofctl add-flow $BRIDGE "table=0,priority=0,actions=drop"
|
||||
ovs-ofctl add-flow $BRIDGE "table=1,priority=0,actions=output:$PATCHTAPTUN_PORT_ID"
|
||||
ovs-ofctl add-flow $BRIDGE "table=2,priority=0,actions=drop"
|
||||
|
||||
# br-tun
|
||||
BRIDGE="tcp:$HOST_IP:6633"
|
||||
PATCHTUNTAP_PORT_ID=`taas_get_ovs_portid $BRIDGE patch-tuntap`
|
||||
ovs-ofctl add-flow $BRIDGE "table=0,priority=1,in_port=$PATCHTUNTAP_PORT_ID,actions=resubmit(,30)"
|
||||
ovs-ofctl add-flow $BRIDGE "table=30,priority=0,actions=move:NXM_OF_VLAN_TCI[0..11]->NXM_NX_TUN_ID[0..11],mod_vlan_vid:1,output:2,output:3"
|
||||
ovs-ofctl add-flow $BRIDGE "table=31,priority=2,reg0=0,actions=output:$PATCHTUNTAP_PORT_ID"
|
||||
ovs-ofctl add-flow $BRIDGE "table=31,priority=1,reg0=1,actions=output:$PATCHTUNTAP_PORT_ID,move:NXM_OF_VLAN_TCI[0..11]->NXM_NX_TUN_ID[0..11],mod_vlan_vid:2,output:in_port"
|
||||
ovs-ofctl add-flow $BRIDGE "table=31,priority=0,reg0=2,actions=learn(table=30,hard_timeout=60,priority=1,NXM_OF_VLAN_TCI[0..11],load:NXM_OF_VLAN_TCI[0..11]->NXM_NX_TUN_ID[0..11],load:0->NXM_OF_VLAN_TCI[0..11],output:NXM_OF_IN_PORT[])"
|
||||
}
|
||||
|
||||
taas_clean()
|
||||
{
|
||||
HOST_IP=$1
|
||||
|
||||
# br-tap
|
||||
BRIDGE="tcp:$HOST_IP:6632"
|
||||
ovs-ofctl del-flows $BRIDGE "table=0"
|
||||
ovs-ofctl del-flows $BRIDGE "table=1"
|
||||
ovs-ofctl del-flows $BRIDGE "table=2"
|
||||
|
||||
# br-tun
|
||||
BRIDGE="tcp:$HOST_IP:6633"
|
||||
PATCHTUNTAP_PORT_ID=`taas_get_ovs_portid $BRIDGE patch-tuntap`
|
||||
ovs-ofctl del-flows $BRIDGE "table=0,in_port=$PATCHTUNTAP_PORT_ID"
|
||||
ovs-ofctl del-flows $BRIDGE "table=30"
|
||||
ovs-ofctl del-flows $BRIDGE "table=31"
|
||||
}
|
||||
|
||||
taas_create()
|
||||
{
|
||||
HOST_IP=$1
|
||||
TAAS_ID=$2
|
||||
NEUTRON_PORT=$3
|
||||
VLAN=$4
|
||||
|
||||
taas_init $HOST_IP
|
||||
|
||||
# br-int
|
||||
BRIDGE="tcp:$HOST_IP:6631"
|
||||
PATCHINTTAP_PORT_ID=`taas_get_ovs_portid $BRIDGE patch-inttap`
|
||||
OVS_PORT_NAME=`taas_get_ovs_portname $NEUTRON_PORT`
|
||||
OVS_PORT_ID=`taas_get_ovs_portid $BRIDGE $OVS_PORT_NAME`
|
||||
ovs-ofctl add-flow $BRIDGE "table=0,priority=20,in_port=$PATCHINTTAP_PORT_ID,dl_vlan=$TAAS_ID,actions=mod_vlan_vid:$VLAN,output:$OVS_PORT_ID"
|
||||
|
||||
# br-tap
|
||||
BRIDGE="tcp:$HOST_IP:6632"
|
||||
PATCHTAPINT_PORT_ID=`taas_get_ovs_portid $BRIDGE patch-tapint`
|
||||
ovs-ofctl add-flow $BRIDGE "table=1,priority=1,dl_vlan=$TAAS_ID,actions=output:in_port"
|
||||
ovs-ofctl add-flow $BRIDGE "table=2,priority=1,dl_vlan=$TAAS_ID,actions=output:$PATCHTAPINT_PORT_ID"
|
||||
|
||||
# br-tun
|
||||
BRIDGE="tcp:$HOST_IP:6633"
|
||||
ovs-ofctl add-flow $BRIDGE "table=2,priority=1,tun_id=$TAAS_ID,actions=move:NXM_OF_VLAN_TCI[0..11]->NXM_NX_REG0[0..11],move:NXM_NX_TUN_ID[0..11]->NXM_OF_VLAN_TCI[0..11],resubmit(,31)"
|
||||
}
|
||||
|
||||
taas_destroy()
|
||||
{
|
||||
HOST_IP=$1
|
||||
TAAS_ID=$2
|
||||
|
||||
# br-int
|
||||
BRIDGE="tcp:$HOST_IP:6631"
|
||||
PATCHINTTAP_PORT_ID=`taas_get_ovs_portid $BRIDGE patch-inttap`
|
||||
ovs-ofctl del-flows $BRIDGE "table=0,in_port=$PATCHINTTAP_PORT_ID,dl_vlan=$TAAS_ID"
|
||||
|
||||
# br-tap
|
||||
BRIDGE="tcp:$HOST_IP:6632"
|
||||
ovs-ofctl del-flows $BRIDGE "table=1,dl_vlan=$TAAS_ID"
|
||||
ovs-ofctl del-flows $BRIDGE "table=2,dl_vlan=$TAAS_ID"
|
||||
|
||||
# br-tun
|
||||
BRIDGE="tcp:$HOST_IP:6633"
|
||||
ovs-ofctl del-flows $BRIDGE "table=2,tun_id=$TAAS_ID"
|
||||
}
|
||||
|
||||
taas_src_add()
|
||||
{
|
||||
HOST_IP=$1
|
||||
TAAS_ID=$2
|
||||
NEUTRON_PORT=$3
|
||||
VLAN=$4
|
||||
DIR=$5
|
||||
|
||||
taas_init $HOST_IP
|
||||
|
||||
# br-int
|
||||
BRIDGE="tcp:$HOST_IP:6631"
|
||||
if [ $DIR = "e" ] || [ $DIR = "b" ]
|
||||
then
|
||||
PATCHINTTAP_PORT_ID=`taas_get_ovs_portid $BRIDGE patch-inttap`
|
||||
OVS_PORT_NAME=`taas_get_ovs_portname $NEUTRON_PORT`
|
||||
OVS_PORT_ID=`taas_get_ovs_portid $BRIDGE $OVS_PORT_NAME`
|
||||
ovs-ofctl add-flow $BRIDGE "table=0,priority=10,in_port=$OVS_PORT_ID,actions=normal,mod_vlan_vid:$TAAS_ID,output:$PATCHINTTAP_PORT_ID"
|
||||
fi
|
||||
if [ $DIR = "i" ] || [ $DIR = "b" ]
|
||||
then
|
||||
MAC_ADDR=`taas_get_mac_addr $NEUTRON_PORT`
|
||||
ovs-ofctl add-flow $BRIDGE "table=0,priority=10,dl_vlan=$VLAN,dl_dst=$MAC_ADDR,actions=normal,mod_vlan_vid:$TAAS_ID,output:$PATCHINTTAP_PORT_ID"
|
||||
ovs-ofctl add-flow $BRIDGE "table=0,priority=10,dl_vlan=$VLAN,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00,actions=normal,mod_vlan_vid:$TAAS_ID,output:$PATCHINTTAP_PORT_ID"
|
||||
fi
|
||||
|
||||
# br-tun
|
||||
BRIDGE="tcp:$HOST_IP:6633"
|
||||
ovs-ofctl add-flow $BRIDGE "table=2,priority=1,tun_id=$TAAS_ID,actions=move:NXM_OF_VLAN_TCI[0..11]->NXM_NX_REG0[0..11],move:NXM_NX_TUN_ID[0..11]->NXM_OF_VLAN_TCI[0..11],resubmit(,31)"
|
||||
}
|
||||
|
||||
taas_src_delete()
|
||||
{
|
||||
HOST_IP=$1
|
||||
TAAS_ID=$2
|
||||
NEUTRON_PORT=$3
|
||||
VLAN=$4
|
||||
DIR=$5
|
||||
|
||||
# br-int
|
||||
BRIDGE="tcp:$HOST_IP:6631"
|
||||
if [ $DIR = "e" ] || [ $DIR = "b" ]
|
||||
then
|
||||
OVS_PORT_NAME=`taas_get_ovs_portname $NEUTRON_PORT`
|
||||
OVS_PORT_ID=`taas_get_ovs_portid $BRIDGE $OVS_PORT_NAME`
|
||||
ovs-ofctl del-flows $BRIDGE "table=0,in_port=$OVS_PORT_ID"
|
||||
fi
|
||||
if [ $DIR = "i" ] || [ $DIR = "b" ]
|
||||
then
|
||||
MAC_ADDR=`taas_get_mac_addr $NEUTRON_PORT`
|
||||
ovs-ofctl del-flows $BRIDGE "table=0,dl_vlan=$VLAN,dl_dst=$MAC_ADDR"
|
||||
ovs-ofctl del-flows $BRIDGE "table=0,dl_vlan=$VLAN,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00"
|
||||
fi
|
||||
|
||||
# br-tun
|
||||
BRIDGE="tcp:$HOST_IP:6633"
|
||||
ovs-ofctl del-flows $BRIDGE "table=2,tun_id=$TAAS_ID"
|
||||
}
|
||||
|
||||
taas_dumpflows()
|
||||
{
|
||||
HOST_IP=$1
|
||||
|
||||
echo -e "\n*** Tap-aaS Flows ($HOST_IP) ***\n"
|
||||
|
||||
# br-int
|
||||
echo "br-int"
|
||||
BRIDGE="tcp:$HOST_IP:6631"
|
||||
ovs-ofctl dump-flows $BRIDGE table=0
|
||||
echo -e "\n"
|
||||
|
||||
# br-tap
|
||||
echo "br-tap"
|
||||
BRIDGE="tcp:$HOST_IP:6632"
|
||||
ovs-ofctl dump-flows $BRIDGE
|
||||
echo -e "\n"
|
||||
|
||||
# br-tun
|
||||
echo "br-tun"
|
||||
BRIDGE="tcp:$HOST_IP:6633"
|
||||
ovs-ofctl dump-flows $BRIDGE table=0
|
||||
ovs-ofctl dump-flows $BRIDGE table=2
|
||||
ovs-ofctl dump-flows $BRIDGE table=30
|
||||
ovs-ofctl dump-flows $BRIDGE table=31
|
||||
echo -e "\n"
|
||||
}
|
||||
|
||||
|
||||
CMD=$1
|
||||
case $CMD in
|
||||
"clean")
|
||||
HOST_IP=$2
|
||||
if [ -z $HOST_IP ]
|
||||
then
|
||||
echo "Usage: $0 $CMD <host-ip>"
|
||||
exit 1
|
||||
fi
|
||||
taas_clean $HOST_IP
|
||||
;;
|
||||
"create")
|
||||
HOST_IP=$2
|
||||
TAAS_ID=$3
|
||||
NEUTRON_PORT=$4
|
||||
VLAN=$5
|
||||
if [ -z $HOST_IP ] || [ -z $TAAS_ID ] || [ -z $NEUTRON_PORT ] || [ -z $VLAN ]
|
||||
then
|
||||
echo "Usage: $0 $CMD <host-ip> <taas-id> <neutron-port> <vlan>"
|
||||
exit 1
|
||||
fi
|
||||
taas_create $HOST_IP $TAAS_ID $NEUTRON_PORT $VLAN
|
||||
;;
|
||||
"destroy")
|
||||
HOST_IP=$2
|
||||
TAAS_ID=$3
|
||||
if [ -z $HOST_IP ] || [ -z $TAAS_ID ]
|
||||
then
|
||||
echo "Usage: $0 $CMD <host-ip> <taas-id>"
|
||||
exit 1
|
||||
fi
|
||||
taas_destroy $HOST_IP $TAAS_ID
|
||||
;;
|
||||
"src-add")
|
||||
HOST_IP=$2
|
||||
TAAS_ID=$3
|
||||
NEUTRON_PORT=$4
|
||||
VLAN=$5
|
||||
DIR=$6
|
||||
if [ -z $HOST_IP ] || [ -z $TAAS_ID ] || [ -z $NEUTRON_PORT ] || [ -z $VLAN ] || [ -z $DIR ]
|
||||
then
|
||||
echo "Usage: $0 $CMD <host-ip> <taas-id> <neutron-port> <vlan> <dir>"
|
||||
exit 1
|
||||
fi
|
||||
taas_src_add $HOST_IP $TAAS_ID $NEUTRON_PORT $VLAN $DIR
|
||||
;;
|
||||
"src-delete")
|
||||
HOST_IP=$2
|
||||
TAAS_ID=$3
|
||||
NEUTRON_PORT=$4
|
||||
VLAN=$5
|
||||
DIR=$6
|
||||
if [ -z $HOST_IP ] || [ -z $TAAS_ID ] || [ -z $NEUTRON_PORT ] || [ -z $VLAN ] || [ -z $DIR ]
|
||||
then
|
||||
echo "Usage: $0 $CMD <host-ip> <taas-id> <neutron-port> <vlan> <dir>"
|
||||
exit 1
|
||||
fi
|
||||
taas_src_delete $HOST_IP $TAAS_ID $NEUTRON_PORT $VLAN $DIR
|
||||
;;
|
||||
"dumpflows")
|
||||
HOST_IP=$2
|
||||
if [ -z $HOST_IP ]
|
||||
then
|
||||
echo "Usage: $0 $CMD <host-ip>"
|
||||
exit 1
|
||||
fi
|
||||
taas_dumpflows $HOST_IP
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 <cmd> [option] [option] ..."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
0
neutron_taas/services/taas/agents/__init__.py
Executable file
0
neutron_taas/services/taas/agents/__init__.py
Executable file
0
neutron_taas/services/taas/agents/ovs/__init__.py
Executable file
0
neutron_taas/services/taas/agents/ovs/__init__.py
Executable file
74
neutron_taas/services/taas/agents/ovs/agent.py
Executable file
74
neutron_taas/services/taas/agents/ovs/agent.py
Executable file
@ -0,0 +1,74 @@
|
||||
# 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 sys
|
||||
|
||||
import eventlet
|
||||
eventlet.monkey_patch()
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo_service import service
|
||||
|
||||
from neutron.agent.common import config
|
||||
from neutron.common import config as common_config
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron.common import topics
|
||||
# from neutron.openstack.common import service
|
||||
from neutron_taas.services.taas.agents.ovs import taas_ovs_agent
|
||||
|
||||
|
||||
OPTS = [
|
||||
cfg.IntOpt(
|
||||
'taas_agent_periodic_interval',
|
||||
default=5,
|
||||
help=_('Seconds between periodic task runs')
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class TaaSOVSAgentService(n_rpc.Service):
|
||||
def start(self):
|
||||
super(TaaSOVSAgentService, self).start()
|
||||
self.tg.add_timer(
|
||||
cfg.CONF.taas_agent_periodic_interval,
|
||||
self.manager.periodic_tasks,
|
||||
None,
|
||||
None
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
# Load the configuration parameters.
|
||||
cfg.CONF.register_opts(OPTS)
|
||||
config.register_root_helper(cfg.CONF)
|
||||
common_config.init(sys.argv[1:])
|
||||
config.setup_logging()
|
||||
|
||||
# Set up RPC
|
||||
mgr = taas_ovs_agent.TaasOvsAgentRpcCallback(cfg.CONF)
|
||||
endpoints = [mgr]
|
||||
conn = n_rpc.create_connection(new=True)
|
||||
conn.create_consumer(topics.TAAS_AGENT, endpoints, fanout=False)
|
||||
conn.consume_in_threads()
|
||||
|
||||
svc = TaaSOVSAgentService(
|
||||
host=cfg.CONF.host,
|
||||
topic=topics.TAAS_PLUGIN,
|
||||
manager=mgr
|
||||
)
|
||||
service.launch(cfg.CONF, svc).wait()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
124
neutron_taas/services/taas/agents/ovs/taas_ovs_agent.py
Executable file
124
neutron_taas/services/taas/agents/ovs/taas_ovs_agent.py
Executable file
@ -0,0 +1,124 @@
|
||||
# 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.
|
||||
|
||||
|
||||
from neutron.agent.common import config
|
||||
from neutron.common import topics
|
||||
from neutron_taas.services.taas.agents import taas_agent_api as api
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TaasOvsPluginApi(api.TaasPluginApiMixin):
|
||||
# Currently there are not any APIs from the the agent towards plugin
|
||||
|
||||
def __init__(self, topic, host):
|
||||
super(TaasOvsPluginApi, self).__init__(topic, host)
|
||||
return
|
||||
|
||||
|
||||
class TaasOvsAgentRpcCallback(api.TaasAgentRpcCallbackMixin):
|
||||
|
||||
def __init__(self, conf):
|
||||
|
||||
LOG.debug("TaaS OVS Agent initialize called")
|
||||
|
||||
self.conf = conf
|
||||
taas_driver_class_path = cfg.CONF.taas.driver
|
||||
self.taas_enabled = cfg.CONF.taas.enabled
|
||||
|
||||
self.root_helper = config.get_root_helper(conf)
|
||||
|
||||
try:
|
||||
self.taas_driver = importutils.import_object(
|
||||
taas_driver_class_path, self.root_helper)
|
||||
LOG.debug("TaaS Driver Loaded: '%s'", taas_driver_class_path)
|
||||
except ImportError:
|
||||
msg = _('Error importing TaaS device driver: %s')
|
||||
raise ImportError(msg % taas_driver_class_path)
|
||||
|
||||
# setup RPC to msg taas plugin
|
||||
self.taas_plugin_rpc = TaasOvsPluginApi(topics.TAAS_PLUGIN,
|
||||
conf.host)
|
||||
super(TaasOvsAgentRpcCallback, self).__init__()
|
||||
|
||||
return
|
||||
|
||||
def _invoke_driver_for_plugin_api(self, context, args, func_name):
|
||||
LOG.debug("Invoking Driver for %(func_name)s from agent",
|
||||
{'func_name': func_name})
|
||||
|
||||
try:
|
||||
self.taas_driver.__getattribute__(func_name)(args)
|
||||
except Exception:
|
||||
LOG.debug("Failed to invoke the driver")
|
||||
|
||||
return
|
||||
|
||||
def create_tap_service(self, context, tap_service, host):
|
||||
"""Handle Rpc from plugin to create a firewall."""
|
||||
if host != self.conf.host:
|
||||
return
|
||||
LOG.debug("In RPC Call for Create Tap Service: MSG=%s" % tap_service)
|
||||
|
||||
return self._invoke_driver_for_plugin_api(
|
||||
context,
|
||||
tap_service,
|
||||
'create_tap_service')
|
||||
|
||||
def create_tap_flow(self, context, tap_flow_msg, host):
|
||||
if host != self.conf.host:
|
||||
return
|
||||
LOG.debug("In RPC Call for Create Tap Flow: MSG=%s" % tap_flow_msg)
|
||||
|
||||
return self._invoke_driver_for_plugin_api(
|
||||
context,
|
||||
tap_flow_msg,
|
||||
'create_tap_flow')
|
||||
|
||||
def delete_tap_service(self, context, tap_service, host):
|
||||
#
|
||||
# Cleanup operations must be performed by all hosts
|
||||
# where the source and/or destination ports associated
|
||||
# with this tap service were residing.
|
||||
#
|
||||
LOG.debug("In RPC Call for Delete Tap Service: MSG=%s" % tap_service)
|
||||
|
||||
return self._invoke_driver_for_plugin_api(
|
||||
context,
|
||||
tap_service,
|
||||
'delete_tap_service')
|
||||
|
||||
def delete_tap_flow(self, context, tap_flow_msg, host):
|
||||
if host != self.conf.host:
|
||||
return
|
||||
LOG.debug("In RPC Call for Delete Tap Flow: MSG=%s" % tap_flow_msg)
|
||||
|
||||
return self._invoke_driver_for_plugin_api(
|
||||
context,
|
||||
tap_flow_msg,
|
||||
'delete_tap_flow')
|
||||
|
||||
def periodic_tasks(self, argv):
|
||||
#
|
||||
# Regenerate the flow in br-tun's TAAS_SEND_FLOOD table
|
||||
# to ensure all existing tunnel ports are included.
|
||||
#
|
||||
self.taas_driver.update_tunnel_flood_flow()
|
||||
pass
|
70
neutron_taas/services/taas/agents/taas_agent_api.py
Executable file
70
neutron_taas/services/taas/agents/taas_agent_api.py
Executable file
@ -0,0 +1,70 @@
|
||||
# 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.
|
||||
|
||||
from oslo.config import cfg
|
||||
# from oslo_messaging import messaging
|
||||
import oslo_messaging as messaging
|
||||
|
||||
from neutron.common import rpc as n_rpc
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
TaasOpts = [
|
||||
cfg.StrOpt(
|
||||
'driver',
|
||||
default='',
|
||||
help=_("Name of the TaaS Driver")),
|
||||
cfg.BoolOpt(
|
||||
'enabled',
|
||||
default=False,
|
||||
help=_("Enable TaaS")),
|
||||
]
|
||||
cfg.CONF.register_opts(TaasOpts, 'taas')
|
||||
|
||||
|
||||
class TaasPluginApiMixin(object):
|
||||
|
||||
# Currently there are no Calls the Agent makes towards the Plugin.
|
||||
|
||||
def __init__(self, topic, host):
|
||||
self.host = host
|
||||
target = messaging.Target(topic=topic, version='1.0')
|
||||
self.client = n_rpc.get_client(target)
|
||||
super(TaasPluginApiMixin, self).__init__()
|
||||
return
|
||||
|
||||
|
||||
class TaasAgentRpcCallbackMixin(object):
|
||||
"""Mixin for Taas agent Implementations."""
|
||||
|
||||
def __init__(self):
|
||||
super(TaasAgentRpcCallbackMixin, self).__init__()
|
||||
|
||||
def create_tap_service(self, context, tap_service, host):
|
||||
"""Handle RPC cast from plugin to create a tap service."""
|
||||
pass
|
||||
|
||||
def delete_tap_service(self, context, tap_service, host):
|
||||
"""Handle RPC cast from plugin to delete a tap service."""
|
||||
pass
|
||||
|
||||
def create_tap_flow(self, context, tap_flow_msg, host):
|
||||
"""Handle RPC cast from plugin to create a tap flow"""
|
||||
pass
|
||||
|
||||
def delete_tap_flow(self, context, tap_flow_msg, host):
|
||||
"""Handle RPC cast from plugin to delete a tap flow"""
|
||||
pass
|
0
neutron_taas/services/taas/drivers/__init__.py
Executable file
0
neutron_taas/services/taas/drivers/__init__.py
Executable file
0
neutron_taas/services/taas/drivers/linux/__init__.py
Executable file
0
neutron_taas/services/taas/drivers/linux/__init__.py
Executable file
28
neutron_taas/services/taas/drivers/linux/ovs_constants.py
Executable file
28
neutron_taas/services/taas/drivers/linux/ovs_constants.py
Executable file
@ -0,0 +1,28 @@
|
||||
# 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.
|
||||
|
||||
|
||||
# OVS tables used by TaaS in br-tap
|
||||
TAAS_RECV_LOC = 1
|
||||
TAAS_RECV_REM = 2
|
||||
|
||||
# OVS tables used by TaaS in br-tun
|
||||
TAAS_SEND_UCAST = 30
|
||||
TAAS_SEND_FLOOD = 31
|
||||
TAAS_CLASSIFY = 35
|
||||
TAAS_DST_CHECK = 36
|
||||
TAAS_SRC_CHECK = 37
|
||||
TAAS_DST_RESPOND = 38
|
||||
TAAS_SRC_RESPOND = 39
|
472
neutron_taas/services/taas/drivers/linux/ovs_taas.py
Executable file
472
neutron_taas/services/taas/drivers/linux/ovs_taas.py
Executable file
@ -0,0 +1,472 @@
|
||||
# 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.
|
||||
|
||||
|
||||
from neutron.agent.common import ovs_lib
|
||||
from neutron.agent.linux import utils
|
||||
# from neutron.plugins.openvswitch.common import constants as ovs_consts
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \
|
||||
as ovs_consts
|
||||
from neutron_taas.services.taas.drivers import taas_base
|
||||
from oslo_log import log as logging
|
||||
import ovs_constants as taas_ovs_consts
|
||||
import ovs_utils as taas_ovs_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
TaaS_DRIVER_NAME = 'Taas OVS driver'
|
||||
|
||||
|
||||
class OVSBridge_tap_extension(ovs_lib.OVSBridge):
|
||||
def __init__(self, br_name, root_helper):
|
||||
super(OVSBridge_tap_extension, self).__init__(br_name)
|
||||
|
||||
|
||||
class OvsTaasDriver(taas_base.TaasDriverBase):
|
||||
def __init__(self, root_helper):
|
||||
LOG.debug("Initializing Taas OVS Driver")
|
||||
|
||||
self.root_helper = root_helper
|
||||
|
||||
self.int_br = OVSBridge_tap_extension('br-int', self.root_helper)
|
||||
self.tun_br = OVSBridge_tap_extension('br-tun', self.root_helper)
|
||||
self.tap_br = OVSBridge_tap_extension('br-tap', self.root_helper)
|
||||
|
||||
# Prepare OVS bridges for TaaS
|
||||
self.setup_ovs_bridges()
|
||||
|
||||
# Setup key-value manager for ingress BCMC flows
|
||||
self.bcmc_kvm = taas_ovs_utils.key_value_mgr(4096)
|
||||
|
||||
def setup_ovs_bridges(self):
|
||||
#
|
||||
# br-int : Integration Bridge
|
||||
# br-tap : Tap Bridge
|
||||
# br-tun : Tunnel Bridge
|
||||
#
|
||||
|
||||
# Create br-tap
|
||||
self.tap_br.create()
|
||||
|
||||
# Connect br-tap to br-int and br-tun
|
||||
self.int_br.add_patch_port('patch-int-tap', 'patch-tap-int')
|
||||
self.tap_br.add_patch_port('patch-tap-int', 'patch-int-tap')
|
||||
self.tun_br.add_patch_port('patch-tun-tap', 'patch-tap-tun')
|
||||
self.tap_br.add_patch_port('patch-tap-tun', 'patch-tun-tap')
|
||||
|
||||
# Get patch port IDs
|
||||
patch_tap_int_id = self.tap_br.get_port_ofport('patch-tap-int')
|
||||
patch_tap_tun_id = self.tap_br.get_port_ofport('patch-tap-tun')
|
||||
patch_tun_tap_id = self.tun_br.get_port_ofport('patch-tun-tap')
|
||||
|
||||
# Purge all existing Taas flows from br-tap and br-tun
|
||||
self.tap_br.delete_flows(table=0)
|
||||
self.tap_br.delete_flows(table=taas_ovs_consts.TAAS_RECV_LOC)
|
||||
self.tap_br.delete_flows(table=taas_ovs_consts.TAAS_RECV_REM)
|
||||
|
||||
self.tun_br.delete_flows(table=0,
|
||||
in_port=patch_tun_tap_id)
|
||||
self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_SEND_UCAST)
|
||||
self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_SEND_FLOOD)
|
||||
self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_CLASSIFY)
|
||||
self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_DST_CHECK)
|
||||
self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_SRC_CHECK)
|
||||
self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_DST_RESPOND)
|
||||
self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_SRC_RESPOND)
|
||||
|
||||
#
|
||||
# Configure standard TaaS flows in br-tap
|
||||
#
|
||||
self.tap_br.add_flow(table=0,
|
||||
priority=1,
|
||||
in_port=patch_tap_int_id,
|
||||
actions="resubmit(,%s)" %
|
||||
taas_ovs_consts.TAAS_RECV_LOC)
|
||||
|
||||
self.tap_br.add_flow(table=0,
|
||||
priority=1,
|
||||
in_port=patch_tap_tun_id,
|
||||
actions="resubmit(,%s)" %
|
||||
taas_ovs_consts.TAAS_RECV_REM)
|
||||
|
||||
self.tap_br.add_flow(table=0,
|
||||
priority=0,
|
||||
actions="drop")
|
||||
|
||||
self.tap_br.add_flow(table=taas_ovs_consts.TAAS_RECV_LOC,
|
||||
priority=0,
|
||||
actions="output:%s" % str(patch_tap_tun_id))
|
||||
|
||||
self.tap_br.add_flow(table=taas_ovs_consts.TAAS_RECV_REM,
|
||||
priority=0,
|
||||
actions="drop")
|
||||
|
||||
#
|
||||
# Configure standard Taas flows in br-tun
|
||||
#
|
||||
self.tun_br.add_flow(table=0,
|
||||
priority=1,
|
||||
in_port=patch_tun_tap_id,
|
||||
actions="resubmit(,%s)" %
|
||||
taas_ovs_consts.TAAS_SEND_UCAST)
|
||||
|
||||
self.tun_br.add_flow(table=taas_ovs_consts.TAAS_SEND_UCAST,
|
||||
priority=0,
|
||||
actions="resubmit(,%s)" %
|
||||
taas_ovs_consts.TAAS_SEND_FLOOD)
|
||||
|
||||
flow_action = self._create_tunnel_flood_flow_action()
|
||||
if flow_action != "":
|
||||
self.tun_br.add_flow(table=taas_ovs_consts.TAAS_SEND_FLOOD,
|
||||
priority=0,
|
||||
actions=flow_action)
|
||||
|
||||
self.tun_br.add_flow(table=taas_ovs_consts.TAAS_CLASSIFY,
|
||||
priority=2,
|
||||
reg0=0,
|
||||
actions="resubmit(,%s)" %
|
||||
taas_ovs_consts.TAAS_DST_CHECK)
|
||||
|
||||
self.tun_br.add_flow(table=taas_ovs_consts.TAAS_CLASSIFY,
|
||||
priority=1,
|
||||
reg0=1,
|
||||
actions="resubmit(,%s)" %
|
||||
taas_ovs_consts.TAAS_DST_CHECK)
|
||||
|
||||
self.tun_br.add_flow(table=taas_ovs_consts.TAAS_CLASSIFY,
|
||||
priority=1,
|
||||
reg0=2,
|
||||
actions="resubmit(,%s)" %
|
||||
taas_ovs_consts.TAAS_SRC_CHECK)
|
||||
|
||||
self.tun_br.add_flow(table=taas_ovs_consts.TAAS_DST_CHECK,
|
||||
priority=0,
|
||||
actions="drop")
|
||||
|
||||
self.tun_br.add_flow(table=taas_ovs_consts.TAAS_SRC_CHECK,
|
||||
priority=0,
|
||||
actions="drop")
|
||||
|
||||
self.tun_br.add_flow(table=taas_ovs_consts.TAAS_DST_RESPOND,
|
||||
priority=2,
|
||||
reg0=0,
|
||||
actions="output:%s" % str(patch_tun_tap_id))
|
||||
|
||||
self.tun_br.add_flow(table=taas_ovs_consts.TAAS_DST_RESPOND,
|
||||
priority=1,
|
||||
reg0=1,
|
||||
actions=(
|
||||
"output:%s,"
|
||||
"move:NXM_OF_VLAN_TCI[0..11]->NXM_NX_TUN_ID"
|
||||
"[0..11],mod_vlan_vid:2,output:in_port" %
|
||||
str(patch_tun_tap_id)))
|
||||
|
||||
self.tun_br.add_flow(table=taas_ovs_consts.TAAS_SRC_RESPOND,
|
||||
priority=1,
|
||||
actions=(
|
||||
"learn(table=%s,hard_timeout=60,"
|
||||
"priority=1,NXM_OF_VLAN_TCI[0..11],"
|
||||
"load:NXM_OF_VLAN_TCI[0..11]->NXM_NX_TUN_ID"
|
||||
"[0..11],load:0->NXM_OF_VLAN_TCI[0..11],"
|
||||
"output:NXM_OF_IN_PORT[])" %
|
||||
taas_ovs_consts.TAAS_SEND_UCAST))
|
||||
|
||||
return
|
||||
|
||||
def create_tap_service(self, tap_service):
|
||||
taas_id = tap_service['taas_id']
|
||||
port = tap_service['port']
|
||||
|
||||
# Get OVS port id for tap service port
|
||||
ovs_port = self.int_br.get_vif_port_by_id(port['id'])
|
||||
ovs_port_id = ovs_port.ofport
|
||||
|
||||
# Get VLAN id for tap service port
|
||||
port_dict = self.int_br.get_port_tag_dict()
|
||||
port_vlan_id = port_dict[ovs_port.port_name]
|
||||
|
||||
# Get patch port IDs
|
||||
patch_int_tap_id = self.int_br.get_port_ofport('patch-int-tap')
|
||||
patch_tap_int_id = self.tap_br.get_port_ofport('patch-tap-int')
|
||||
|
||||
# Add flow(s) in br-int
|
||||
self.int_br.add_flow(table=0,
|
||||
priority=25,
|
||||
in_port=patch_int_tap_id,
|
||||
dl_vlan=taas_id,
|
||||
actions="mod_vlan_vid:%s,output:%s" %
|
||||
(str(port_vlan_id), str(ovs_port_id)))
|
||||
|
||||
# Add flow(s) in br-tap
|
||||
self.tap_br.add_flow(table=taas_ovs_consts.TAAS_RECV_LOC,
|
||||
priority=1,
|
||||
dl_vlan=taas_id,
|
||||
actions="output:in_port")
|
||||
|
||||
self.tap_br.add_flow(table=taas_ovs_consts.TAAS_RECV_REM,
|
||||
priority=1,
|
||||
dl_vlan=taas_id,
|
||||
actions="output:%s" % str(patch_tap_int_id))
|
||||
|
||||
# Add flow(s) in br-tun
|
||||
for tunnel_type in ovs_consts.TUNNEL_NETWORK_TYPES:
|
||||
self.tun_br.add_flow(table=ovs_consts.TUN_TABLE[tunnel_type],
|
||||
priority=1,
|
||||
tun_id=taas_id,
|
||||
actions=(
|
||||
"move:NXM_OF_VLAN_TCI[0..11]->"
|
||||
"NXM_NX_REG0[0..11],move:NXM_NX_TUN_ID"
|
||||
"[0..11]->NXM_OF_VLAN_TCI[0..11],"
|
||||
"resubmit(,%s)" %
|
||||
taas_ovs_consts.TAAS_CLASSIFY))
|
||||
|
||||
self.tun_br.add_flow(table=taas_ovs_consts.TAAS_DST_CHECK,
|
||||
priority=1,
|
||||
tun_id=taas_id,
|
||||
actions="resubmit(,%s)" %
|
||||
taas_ovs_consts.TAAS_DST_RESPOND)
|
||||
|
||||
#
|
||||
# Disable mac-address learning in the Linux bridge to which
|
||||
# the OVS port is attached (via the veth pair). This will
|
||||
# effectively turn the bridge into a hub, ensuring that all
|
||||
# incoming mirrored traffic reaches the tap interface (used
|
||||
# for attaching a VM to the bridge) irrespective of the
|
||||
# destination mac addresses in mirrored packets.
|
||||
#
|
||||
ovs_port_name = ovs_port.port_name
|
||||
linux_br_name = ovs_port_name.replace('qvo', 'qbr')
|
||||
utils.execute(['brctl', 'setageing', linux_br_name, 0],
|
||||
run_as_root=True)
|
||||
|
||||
return
|
||||
|
||||
def delete_tap_service(self, tap_service):
|
||||
taas_id = tap_service['taas_id']
|
||||
|
||||
# Get patch port ID
|
||||
patch_int_tap_id = self.int_br.get_port_ofport('patch-int-tap')
|
||||
|
||||
# Delete flow(s) from br-int
|
||||
self.int_br.delete_flows(table=0,
|
||||
in_port=patch_int_tap_id,
|
||||
dl_vlan=taas_id)
|
||||
|
||||
# Delete flow(s) from br-tap
|
||||
self.tap_br.delete_flows(table=taas_ovs_consts.TAAS_RECV_LOC,
|
||||
dl_vlan=taas_id)
|
||||
|
||||
self.tap_br.delete_flows(table=taas_ovs_consts.TAAS_RECV_REM,
|
||||
dl_vlan=taas_id)
|
||||
|
||||
# Delete flow(s) from br-tun
|
||||
for tunnel_type in ovs_consts.TUNNEL_NETWORK_TYPES:
|
||||
self.tun_br.delete_flows(table=ovs_consts.TUN_TABLE[tunnel_type],
|
||||
tun_id=taas_id)
|
||||
|
||||
self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_DST_CHECK,
|
||||
tun_id=taas_id)
|
||||
|
||||
self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_SRC_CHECK,
|
||||
tun_id=taas_id)
|
||||
|
||||
return
|
||||
|
||||
def create_tap_flow(self, tap_flow):
|
||||
taas_id = tap_flow['taas_id']
|
||||
port = tap_flow['port']
|
||||
direction = tap_flow['tap_flow']['direction']
|
||||
|
||||
# Get OVS port id for tap flow port
|
||||
ovs_port = self.int_br.get_vif_port_by_id(port['id'])
|
||||
ovs_port_id = ovs_port.ofport
|
||||
|
||||
# Get VLAN id for tap flow port
|
||||
port_dict = self.int_br.get_port_tag_dict()
|
||||
port_vlan_id = port_dict[ovs_port.port_name]
|
||||
|
||||
# Get patch port ID
|
||||
patch_int_tap_id = self.int_br.get_port_ofport('patch-int-tap')
|
||||
|
||||
# Add flow(s) in br-int
|
||||
if direction == 'OUT' or direction == 'BOTH':
|
||||
self.int_br.add_flow(table=0,
|
||||
priority=20,
|
||||
in_port=ovs_port_id,
|
||||
actions="normal,mod_vlan_vid:%s,output:%s" %
|
||||
(str(taas_id), str(patch_int_tap_id)))
|
||||
|
||||
if direction == 'IN' or direction == 'BOTH':
|
||||
port_mac = tap_flow['port_mac']
|
||||
self.int_br.add_flow(table=0,
|
||||
priority=20,
|
||||
dl_vlan=port_vlan_id,
|
||||
dl_dst=port_mac,
|
||||
actions="normal,mod_vlan_vid:%s,output:%s" %
|
||||
(str(taas_id), str(patch_int_tap_id)))
|
||||
|
||||
self._add_update_ingress_bcmc_flow(port_vlan_id,
|
||||
taas_id,
|
||||
patch_int_tap_id)
|
||||
|
||||
# Add flow(s) in br-tun
|
||||
for tunnel_type in ovs_consts.TUNNEL_NETWORK_TYPES:
|
||||
self.tun_br.add_flow(table=ovs_consts.TUN_TABLE[tunnel_type],
|
||||
priority=1,
|
||||
tun_id=taas_id,
|
||||
actions=(
|
||||
"move:NXM_OF_VLAN_TCI[0..11]->"
|
||||
"NXM_NX_REG0[0..11],move:NXM_NX_TUN_ID"
|
||||
"[0..11]->NXM_OF_VLAN_TCI[0..11],"
|
||||
"resubmit(,%s)" %
|
||||
taas_ovs_consts.TAAS_CLASSIFY))
|
||||
|
||||
self.tun_br.add_flow(table=taas_ovs_consts.TAAS_SRC_CHECK,
|
||||
priority=1,
|
||||
tun_id=taas_id,
|
||||
actions="resubmit(,%s)" %
|
||||
taas_ovs_consts.TAAS_SRC_RESPOND)
|
||||
|
||||
return
|
||||
|
||||
def delete_tap_flow(self, tap_flow):
|
||||
taas_id = tap_flow['taas_id']
|
||||
port = tap_flow['port']
|
||||
direction = tap_flow['tap_flow']['direction']
|
||||
|
||||
# Get OVS port id for tap flow port
|
||||
ovs_port = self.int_br.get_vif_port_by_id(port['id'])
|
||||
ovs_port_id = ovs_port.ofport
|
||||
|
||||
# Get VLAN id for tap flow port
|
||||
port_dict = self.int_br.get_port_tag_dict()
|
||||
port_vlan_id = port_dict[ovs_port.port_name]
|
||||
|
||||
# Get patch port ID
|
||||
patch_int_tap_id = self.int_br.get_port_ofport('patch-int-tap')
|
||||
|
||||
# Delete flow(s) from br-int
|
||||
if direction == 'OUT' or direction == 'BOTH':
|
||||
self.int_br.delete_flows(table=0,
|
||||
in_port=ovs_port_id)
|
||||
|
||||
if direction == 'IN' or direction == 'BOTH':
|
||||
port_mac = tap_flow['port_mac']
|
||||
self.int_br.delete_flows(table=0,
|
||||
dl_vlan=port_vlan_id,
|
||||
dl_dst=port_mac)
|
||||
|
||||
self._del_update_ingress_bcmc_flow(port_vlan_id,
|
||||
taas_id,
|
||||
patch_int_tap_id)
|
||||
|
||||
return
|
||||
|
||||
def update_tunnel_flood_flow(self):
|
||||
flow_action = self._create_tunnel_flood_flow_action()
|
||||
if flow_action != "":
|
||||
self.tun_br.mod_flow(table=taas_ovs_consts.TAAS_SEND_FLOOD,
|
||||
actions=flow_action)
|
||||
|
||||
def _create_tunnel_flood_flow_action(self):
|
||||
|
||||
args = ["ovs-vsctl", "list-ports", "br-tun"]
|
||||
res = utils.execute(args, run_as_root=True)
|
||||
|
||||
port_name_list = res.splitlines()
|
||||
|
||||
flow_action = ("move:NXM_OF_VLAN_TCI[0..11]->NXM_NX_TUN_ID[0..11],"
|
||||
"mod_vlan_vid:1")
|
||||
|
||||
tunnel_ports_exist = False
|
||||
|
||||
for port_name in port_name_list:
|
||||
if (port_name != 'patch-int') and (port_name != 'patch-tun-tap'):
|
||||
flow_action += (",output:%d" %
|
||||
self.tun_br.get_port_ofport(port_name))
|
||||
tunnel_ports_exist = True
|
||||
|
||||
if tunnel_ports_exist:
|
||||
return flow_action
|
||||
else:
|
||||
return ""
|
||||
|
||||
def _create_ingress_bcmc_flow_action(self, taas_id_list, out_port_id):
|
||||
flow_action = "normal"
|
||||
for taas_id in taas_id_list:
|
||||
flow_action += (",mod_vlan_vid:%d,output:%d" %
|
||||
(taas_id, out_port_id))
|
||||
|
||||
return flow_action
|
||||
|
||||
#
|
||||
# Adds or updates a special flow in br-int to mirror (duplicate and
|
||||
# redirect to 'out_port_id') all ingress broadcast/multicast traffic,
|
||||
# associated with a VLAN, to possibly multiple tap service instances.
|
||||
#
|
||||
def _add_update_ingress_bcmc_flow(self, vlan_id, taas_id, out_port_id):
|
||||
# Add a tap service instance affiliation with VLAN
|
||||
self.bcmc_kvm.affiliate(vlan_id, taas_id)
|
||||
|
||||
# Find all tap service instances affiliated with VLAN
|
||||
taas_id_list = self.bcmc_kvm.list_affiliations(vlan_id)
|
||||
|
||||
#
|
||||
# Add/update flow to mirror ingress BCMC traffic, associated
|
||||
# with VLAN, to all affiliated tap-service instances.
|
||||
#
|
||||
flow_action = self._create_ingress_bcmc_flow_action(taas_id_list,
|
||||
out_port_id)
|
||||
self.int_br.add_flow(table=0,
|
||||
priority=20,
|
||||
dl_vlan=vlan_id,
|
||||
dl_dst="01:00:00:00:00:00/01:00:00:00:00:00",
|
||||
actions=flow_action)
|
||||
|
||||
return
|
||||
|
||||
#
|
||||
# Removes or updates a special flow in br-int to mirror (duplicate
|
||||
# and redirect to 'out_port_id') all ingress broadcast/multicast
|
||||
# traffic, associated with a VLAN, to possibly multiple tap-service
|
||||
# instances.
|
||||
#
|
||||
def _del_update_ingress_bcmc_flow(self, vlan_id, taas_id, out_port_id):
|
||||
# Remove a tap-service instance affiliation with VLAN
|
||||
self.bcmc_kvm.unaffiliate(vlan_id, taas_id)
|
||||
|
||||
# Find all tap-service instances affiliated with VLAN
|
||||
taas_id_list = self.bcmc_kvm.list_affiliations(vlan_id)
|
||||
|
||||
#
|
||||
# If there are tap service instances affiliated with VLAN, update
|
||||
# the flow to mirror ingress BCMC traffic, associated with VLAN,
|
||||
# to all of them. Otherwise, remove the flow.
|
||||
#
|
||||
if taas_id_list:
|
||||
flow_action = self._create_ingress_bcmc_flow_action(taas_id_list,
|
||||
out_port_id)
|
||||
self.int_br.add_flow(table=0,
|
||||
priority=20,
|
||||
dl_vlan=vlan_id,
|
||||
dl_dst="01:00:00:00:00:00/01:00:00:00:00:00",
|
||||
actions=flow_action)
|
||||
else:
|
||||
self.int_br.delete_flows(table=0,
|
||||
dl_vlan=vlan_id,
|
||||
dl_dst=("01:00:00:00:00:00/"
|
||||
"01:00:00:00:00:00"))
|
||||
|
||||
return
|
99
neutron_taas/services/taas/drivers/linux/ovs_utils.py
Executable file
99
neutron_taas/services/taas/drivers/linux/ovs_utils.py
Executable file
@ -0,0 +1,99 @@
|
||||
# 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.
|
||||
|
||||
|
||||
#
|
||||
# This class implements a simple key-value manager that can support
|
||||
# the following relationships.
|
||||
#
|
||||
# - Multiple values may be affiliated with a key.
|
||||
# - A value may be affiliated with multiple keys.
|
||||
# - A value may be affiliated with a key multiple times.
|
||||
#
|
||||
class key_value_mgr(object):
|
||||
#
|
||||
# Initializes internal state for specified # keys
|
||||
#
|
||||
def __init__(self, nr_keys):
|
||||
self.key_list = []
|
||||
for i in range(nr_keys):
|
||||
self.key_list.append([])
|
||||
|
||||
return
|
||||
|
||||
#
|
||||
# Returns specified key-value affilation, if it exists.
|
||||
#
|
||||
def _find_affiliation(self, key, value):
|
||||
aff_list = self.key_list[key]
|
||||
|
||||
for aff in aff_list:
|
||||
if aff['value'] == value:
|
||||
return aff
|
||||
|
||||
return None
|
||||
|
||||
#
|
||||
# Adds an affiliation of 'value' with 'key'
|
||||
#
|
||||
def affiliate(self, key, value):
|
||||
# Locate key-value affiliation
|
||||
aff = self._find_affiliation(key, value)
|
||||
if aff is None:
|
||||
# Create a (new) key-value affiliation
|
||||
aff = {
|
||||
'value': value,
|
||||
'refcnt': 0,
|
||||
}
|
||||
aff_list = self.key_list[key]
|
||||
aff_list.append(aff)
|
||||
|
||||
# Increment affiliation reference count
|
||||
aff['refcnt'] += 1
|
||||
|
||||
return
|
||||
|
||||
#
|
||||
# Removes an affiliation of 'value' with 'key'
|
||||
#
|
||||
def unaffiliate(self, key, value):
|
||||
# Locate key-value affiliation
|
||||
aff = self._find_affiliation(key, value)
|
||||
if aff is None:
|
||||
return
|
||||
|
||||
# Decrement affiliation reference count
|
||||
aff['refcnt'] -= 1
|
||||
|
||||
# Destroy affiliation iff no outstanding references
|
||||
if aff['refcnt'] <= 0:
|
||||
aff_list = self.key_list[key]
|
||||
aff_list.remove(aff)
|
||||
|
||||
return
|
||||
|
||||
#
|
||||
# Lists all values affiliated with 'key'
|
||||
#
|
||||
# Note: The returned list is a set (contains no duplicates)
|
||||
#
|
||||
def list_affiliations(self, key):
|
||||
aff_list = self.key_list[key]
|
||||
|
||||
value_list = []
|
||||
for aff in aff_list:
|
||||
value_list.append(aff['value'])
|
||||
|
||||
return value_list
|
37
neutron_taas/services/taas/drivers/taas_base.py
Executable file
37
neutron_taas/services/taas/drivers/taas_base.py
Executable file
@ -0,0 +1,37 @@
|
||||
# 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 abc
|
||||
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class TaasDriverBase(object):
|
||||
@abc.abstractmethod
|
||||
def create_tap_service(self, tap_service):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_tap_service(self, tap_service):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_tap_flow(self, tap_flow):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_tap_flow(self, tap_flow):
|
||||
pass
|
259
neutron_taas/services/taas/taas_plugin.py
Executable file
259
neutron_taas/services/taas/taas_plugin.py
Executable file
@ -0,0 +1,259 @@
|
||||
# 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.
|
||||
|
||||
|
||||
from oslo.config import cfg
|
||||
# from oslo import messaging
|
||||
import oslo_messaging as messaging
|
||||
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron.common import topics
|
||||
from neutron_taas.db import taas_db
|
||||
from neutron_taas.extensions import taas as taas_ex
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TaasCallbacks(object):
|
||||
"""Currently there are no callbacks to the Taas Plugin."""
|
||||
|
||||
def __init__(self, plugin):
|
||||
super(TaasCallbacks, self).__init__()
|
||||
self.plugin = plugin
|
||||
return
|
||||
|
||||
|
||||
class TaasAgentApi(object):
|
||||
"""RPC calls to agent APIs"""
|
||||
|
||||
def __init__(self, topic, host):
|
||||
self.host = host
|
||||
target = messaging.Target(topic=topic, version='1.0')
|
||||
self.client = n_rpc.get_client(target)
|
||||
return
|
||||
|
||||
def create_tap_service(self, context, tap_service, host):
|
||||
LOG.debug("In RPC Call for Create Tap Service: Host=%s, MSG=%s" %
|
||||
(host, tap_service))
|
||||
|
||||
cctxt = self.client.prepare(fanout=True)
|
||||
cctxt.cast(context, 'create_tap_service', tap_service=tap_service,
|
||||
host=host)
|
||||
|
||||
return
|
||||
|
||||
def create_tap_flow(self, context, tap_flow_msg, host):
|
||||
LOG.debug("In RPC Call for Create Tap Flow: Host=%s, MSG=%s" %
|
||||
(host, tap_flow_msg))
|
||||
|
||||
cctxt = self.client.prepare(fanout=True)
|
||||
cctxt.cast(context, 'create_tap_flow', tap_flow_msg=tap_flow_msg,
|
||||
host=host)
|
||||
|
||||
return
|
||||
|
||||
def delete_tap_service(self, context, tap_service, host):
|
||||
LOG.debug("In RPC Call for Delete Tap Service: Host=%s, MSG=%s" %
|
||||
(host, tap_service))
|
||||
|
||||
cctxt = self.client.prepare(fanout=True)
|
||||
cctxt.cast(context, 'delete_tap_service', tap_service=tap_service,
|
||||
host=host)
|
||||
|
||||
return
|
||||
|
||||
def delete_tap_flow(self, context, tap_flow_msg, host):
|
||||
LOG.debug("In RPC Call for Delete Tap Flow: Host=%s, MSG=%s" %
|
||||
(host, tap_flow_msg))
|
||||
|
||||
cctxt = self.client.prepare(fanout=True)
|
||||
cctxt.cast(context, 'delete_tap_flow', tap_flow_msg=tap_flow_msg,
|
||||
host=host)
|
||||
|
||||
return
|
||||
|
||||
|
||||
class TaasPlugin(taas_db.Tass_db_Mixin):
|
||||
|
||||
supported_extension_aliases = ["taas"]
|
||||
|
||||
def __init__(self):
|
||||
|
||||
LOG.debug("TAAS PLUGIN INITIALIZED")
|
||||
self.endpoints = [TaasCallbacks(self)]
|
||||
|
||||
self.conn = n_rpc.create_connection(new=True)
|
||||
self.conn.create_consumer(
|
||||
topics.TAAS_PLUGIN, self.endpoints, fanout=False)
|
||||
self.conn.consume_in_threads()
|
||||
|
||||
self.agent_rpc = TaasAgentApi(
|
||||
topics.TAAS_AGENT,
|
||||
cfg.CONF.host
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
def create_tap_service(self, context, tap_service):
|
||||
LOG.debug("create_tap_service() called")
|
||||
|
||||
tenant_id = self._get_tenant_id_for_create(context,
|
||||
tap_service['tap_service'])
|
||||
|
||||
t_s = tap_service['tap_service']
|
||||
port_id = t_s['port_id']
|
||||
|
||||
# Get port details
|
||||
port = self._get_port_details(context, port_id)
|
||||
|
||||
# Check if the port is owned by the tenant.
|
||||
if port['tenant_id'] != tenant_id:
|
||||
raise taas_ex.PortDoesNotBelongToTenant()
|
||||
|
||||
# Extract the host where the port is located
|
||||
host = port['binding:host_id']
|
||||
|
||||
if host is not None:
|
||||
LOG.debug("Host on which the port is created = %s" % host)
|
||||
else:
|
||||
LOG.debug("Host could not be found, Port Binding disbaled!")
|
||||
|
||||
# Create tap service in the db model
|
||||
ts = super(TaasPlugin, self).create_tap_service(context, tap_service)
|
||||
# Get taas id associated with the Tap Service
|
||||
tap_id_association = self.get_tap_id_association(
|
||||
context,
|
||||
tap_service_id=ts['id'])
|
||||
|
||||
taas_vlan_id = (tap_id_association['taas_id'] +
|
||||
cfg.CONF.taas.vlan_range_start)
|
||||
|
||||
if taas_vlan_id > cfg.CONF.taas.vlan_range_end:
|
||||
raise taas_ex.TapServiceLimitReached()
|
||||
|
||||
rpc_msg = {'tap_service': ts, 'taas_id': taas_vlan_id, 'port': port}
|
||||
|
||||
self.agent_rpc.create_tap_service(context, rpc_msg, host)
|
||||
|
||||
return ts
|
||||
|
||||
def delete_tap_service(self, context, id):
|
||||
LOG.debug("delete_tap_service() called")
|
||||
|
||||
# Get taas id associated with the Tap Service
|
||||
tap_id_association = self.get_tap_id_association(
|
||||
context,
|
||||
tap_service_id=id)
|
||||
|
||||
ts = self.get_tap_service(context, id)
|
||||
|
||||
# Get all the tap Flows that are associated with the Tap service
|
||||
# and delete them as well
|
||||
t_f_collection = self.get_tap_flows(
|
||||
context,
|
||||
filters={'tap_service_id': [id]}, fields=['id'])
|
||||
|
||||
for t_f in t_f_collection:
|
||||
self.delete_tap_flow(context, t_f['id'])
|
||||
|
||||
# Get the port and the host that it is on
|
||||
port_id = ts['port_id']
|
||||
|
||||
port = self._get_port_details(context, port_id)
|
||||
|
||||
host = port['binding:host_id']
|
||||
|
||||
super(TaasPlugin, self).delete_tap_service(context, id)
|
||||
|
||||
taas_vlan_id = (tap_id_association['taas_id'] +
|
||||
cfg.CONF.taas.vlan_range_start)
|
||||
|
||||
rpc_msg = {'tap_service': ts,
|
||||
'taas_id': taas_vlan_id,
|
||||
'port': port}
|
||||
|
||||
self.agent_rpc.delete_tap_service(context, rpc_msg, host)
|
||||
|
||||
return ts
|
||||
|
||||
def create_tap_flow(self, context, tap_flow):
|
||||
LOG.debug("create_tap_flow() called")
|
||||
|
||||
tenant_id = self._get_tenant_id_for_create(context,
|
||||
tap_flow['tap_flow'])
|
||||
|
||||
t_f = tap_flow['tap_flow']
|
||||
|
||||
# Check if the tenant id of the source port is the same as the
|
||||
# tenant_id of the tap service we are attaching it to.
|
||||
|
||||
ts = self.get_tap_service(context, t_f['tap_service_id'])
|
||||
ts_tenant_id = ts['tenant_id']
|
||||
|
||||
taas_id = (self.get_tap_id_association(
|
||||
context,
|
||||
tap_service_id=ts['id'])['taas_id'] +
|
||||
cfg.CONF.taas.vlan_range_start)
|
||||
|
||||
if tenant_id != ts_tenant_id:
|
||||
raise taas_ex.TapServiceNotBelongToTenant()
|
||||
|
||||
# Extract the host where the source port is located
|
||||
port = self._get_port_details(context, t_f['source_port'])
|
||||
host = port['binding:host_id']
|
||||
port_mac = port['mac_address']
|
||||
|
||||
# create tap flow in the db model
|
||||
tf = super(TaasPlugin, self).create_tap_flow(context, tap_flow)
|
||||
|
||||
# Send RPC message to both the source port host and
|
||||
# tap service(destination) port host
|
||||
rpc_msg = {'tap_flow': tf,
|
||||
'port_mac': port_mac,
|
||||
'taas_id': taas_id,
|
||||
'port': port}
|
||||
|
||||
self.agent_rpc.create_tap_flow(context, rpc_msg, host)
|
||||
|
||||
return tf
|
||||
|
||||
def delete_tap_flow(self, context, id):
|
||||
LOG.debug("delete_tap_flow() called")
|
||||
|
||||
tf = self.get_tap_flow(context, id)
|
||||
# ts = self.get_tap_service(context, tf['tap_service_id'])
|
||||
|
||||
taas_id = (self.get_tap_id_association(
|
||||
context,
|
||||
tf['tap_service_id'])['taas_id'] +
|
||||
cfg.CONF.taas.vlan_range_start)
|
||||
|
||||
port = self._get_port_details(context, tf['source_port'])
|
||||
host = port['binding:host_id']
|
||||
port_mac = port['mac_address']
|
||||
|
||||
super(TaasPlugin, self).delete_tap_flow(context, id)
|
||||
|
||||
# Send RPC message to both the source port host and
|
||||
# tap service(destination) port host
|
||||
rpc_msg = {'tap_flow': tf,
|
||||
'port_mac': port_mac,
|
||||
'taas_id': taas_id,
|
||||
'port': port}
|
||||
|
||||
self.agent_rpc.delete_tap_flow(context, rpc_msg, host)
|
||||
|
||||
return tf
|
15
neutron_taas/taas_cli/openrc.sh
Executable file
15
neutron_taas/taas_cli/openrc.sh
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# URL for authentication service
|
||||
export OS_AUTH_URL=http://10.0.2.15:5000/v2.0
|
||||
|
||||
# Tenant name
|
||||
export OS_TENANT_NAME="demo"
|
||||
|
||||
# Username
|
||||
export OS_USERNAME="demo"
|
||||
|
||||
# Password
|
||||
echo "Please enter your OpenStack Password: "
|
||||
read -sr OS_PASSWORD_INPUT
|
||||
export OS_PASSWORD=$OS_PASSWORD_INPUT
|
536
neutron_taas/taas_cli/taas_cli.py
Executable file
536
neutron_taas/taas_cli/taas_cli.py
Executable file
@ -0,0 +1,536 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# 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 argparse
|
||||
import json
|
||||
import os
|
||||
import requests
|
||||
|
||||
|
||||
SUCCESS = 0
|
||||
FAILURE = 1
|
||||
|
||||
|
||||
class taas_rest_api(object):
|
||||
|
||||
def __init__(self):
|
||||
#
|
||||
# Initialize authentication service endpoint and user
|
||||
# credentials from environment variables
|
||||
#
|
||||
self.auth_url = os.environ['OS_AUTH_URL']
|
||||
self.tenant_name = os.environ['OS_TENANT_NAME']
|
||||
self.username = os.environ['OS_USERNAME']
|
||||
self.password = os.environ['OS_PASSWORD']
|
||||
|
||||
self.token_id = None
|
||||
|
||||
return
|
||||
|
||||
def authenticate(self):
|
||||
# Generate a new authentication token
|
||||
req_data = {}
|
||||
req_data['auth'] = {
|
||||
'tenantName': self.tenant_name,
|
||||
'passwordCredentials': {
|
||||
'username': self.username,
|
||||
'password': self.password
|
||||
}
|
||||
}
|
||||
|
||||
req_data_json = json.dumps(req_data)
|
||||
|
||||
url = '%s/tokens' % self.auth_url
|
||||
|
||||
response = requests.post(url, req_data_json)
|
||||
res_data = response.json()
|
||||
|
||||
if response.status_code != 200:
|
||||
error = res_data['error']
|
||||
print ('Error code: %s (%s)' % (error['code'], error['title']))
|
||||
return FAILURE
|
||||
|
||||
# Remember new token ID
|
||||
self.token_id = res_data['access']['token']['id']
|
||||
|
||||
# Remember tenant ID
|
||||
self.tenant_id = res_data['access']['token']['tenant']['id']
|
||||
|
||||
# Find network service endpoint
|
||||
found = False
|
||||
service_catalog = res_data['access']['serviceCatalog']
|
||||
for service in service_catalog:
|
||||
if service['type'] == 'network':
|
||||
endpoints = service['endpoints']
|
||||
endpoint = endpoints[0]
|
||||
network_service_url = endpoint['publicURL']
|
||||
found = True
|
||||
break
|
||||
|
||||
if found is False:
|
||||
print ('Error: Could not find network service endpoint')
|
||||
return FAILURE
|
||||
|
||||
# Formulate and remember TaaS endpoint
|
||||
self.taas_url = '%s/v2.0/taas/' % network_service_url
|
||||
|
||||
return SUCCESS
|
||||
|
||||
def tap_service_create(self,
|
||||
name,
|
||||
description,
|
||||
port_id,
|
||||
network_id):
|
||||
header_data = {}
|
||||
header_data['X-Auth-Token'] = self.token_id
|
||||
|
||||
req_data = {}
|
||||
req_data['tap_service'] = {
|
||||
'tenant_id': self.tenant_id,
|
||||
'name': name,
|
||||
'description': description,
|
||||
'port_id': port_id,
|
||||
'network_id': network_id
|
||||
}
|
||||
|
||||
req_data_json = json.dumps(req_data)
|
||||
|
||||
url = '%s/tap-services' % self.taas_url
|
||||
|
||||
response = requests.post(url, req_data_json, headers=header_data)
|
||||
res_data = response.json()
|
||||
|
||||
if response.status_code != 201:
|
||||
error = res_data['NeutronError']
|
||||
print (error['message'])
|
||||
return FAILURE
|
||||
|
||||
tap_service = res_data['tap_service']
|
||||
|
||||
# Show information of the new tap service
|
||||
self.tap_service_show(tap_service['id'])
|
||||
|
||||
return SUCCESS
|
||||
|
||||
def tap_service_delete(self, tap_service_id):
|
||||
header_data = {}
|
||||
header_data['X-Auth-Token'] = self.token_id
|
||||
|
||||
url = '%s/tap-services/%s' % (self.taas_url, tap_service_id)
|
||||
|
||||
response = requests.delete(url, headers=header_data)
|
||||
|
||||
if response.status_code != 204:
|
||||
res_data = response.json()
|
||||
error = res_data['NeutronError']
|
||||
print (error['message'])
|
||||
return FAILURE
|
||||
|
||||
return SUCCESS
|
||||
|
||||
def tap_service_list(self):
|
||||
header_data = {}
|
||||
header_data['X-Auth-Token'] = self.token_id
|
||||
|
||||
url = '%s/tap-services' % self.taas_url
|
||||
|
||||
response = requests.get(url, headers=header_data)
|
||||
res_data = response.json()
|
||||
|
||||
if response.status_code != 200:
|
||||
error = res_data['NeutronError']
|
||||
print (error['message'])
|
||||
return FAILURE
|
||||
|
||||
tap_services = res_data['tap_services']
|
||||
|
||||
sep = '+' + '-' * 38 + '+' + '-' * 12 + '+' + '-' * 38 + '+'
|
||||
print (sep)
|
||||
print ('| {:36s} | {:10s} | {:36s} |'.format('ID', 'Name', 'Port ID'))
|
||||
print (sep)
|
||||
for tap_service in tap_services:
|
||||
print ('| {:36s} | {:10s} | {:36s} |'
|
||||
.format(tap_service['id'],
|
||||
tap_service['name'],
|
||||
tap_service['port_id']))
|
||||
print (sep)
|
||||
|
||||
return SUCCESS
|
||||
|
||||
def tap_service_show(self, tap_service_id):
|
||||
header_data = {}
|
||||
header_data['X-Auth-Token'] = self.token_id
|
||||
|
||||
url = '%s/tap-services/%s' % (self.taas_url, tap_service_id)
|
||||
|
||||
response = requests.get(url, headers=header_data)
|
||||
res_data = response.json()
|
||||
|
||||
if response.status_code != 200:
|
||||
error = res_data['NeutronError']
|
||||
print (error['message'])
|
||||
return FAILURE
|
||||
|
||||
tap_service = res_data['tap_service']
|
||||
|
||||
sep = '+' + '-' * 13 + '+' + '-' * 38 + '+'
|
||||
print (sep)
|
||||
print ('| {:11} | {:36s} |'.format('Field', 'Value'))
|
||||
print (sep)
|
||||
print ('| {:11} | {:36s} |'
|
||||
.format('Name', tap_service['name']))
|
||||
print ('| {:11} | {:36s} |'
|
||||
.format('Description', tap_service['description']))
|
||||
print ('| {:11} | {:36s} |'
|
||||
.format('ID', tap_service['id']))
|
||||
print ('| {:11} | {:36s} |'
|
||||
.format('Port ID', tap_service['port_id']))
|
||||
print ('| {:11} | {:36s} |'
|
||||
.format('Tenant ID', tap_service['tenant_id']))
|
||||
print (sep)
|
||||
|
||||
return SUCCESS
|
||||
|
||||
def tap_flow_create(self,
|
||||
name,
|
||||
description,
|
||||
port_id,
|
||||
direction,
|
||||
tap_service_id):
|
||||
header_data = {}
|
||||
header_data['X-Auth-Token'] = self.token_id
|
||||
|
||||
req_data = {}
|
||||
req_data['tap_flow'] = {
|
||||
'tenant_id': self.tenant_id,
|
||||
'name': name,
|
||||
'description': description,
|
||||
'source_port': port_id,
|
||||
'direction': direction,
|
||||
'tap_service_id': tap_service_id
|
||||
}
|
||||
|
||||
req_data_json = json.dumps(req_data)
|
||||
|
||||
url = '%s/tap-flows' % self.taas_url
|
||||
|
||||
response = requests.post(url, req_data_json, headers=header_data)
|
||||
res_data = response.json()
|
||||
|
||||
if response.status_code != 201:
|
||||
error = res_data['NeutronError']
|
||||
print (error['message'])
|
||||
return FAILURE
|
||||
|
||||
tap_flow = res_data['tap_flow']
|
||||
|
||||
# Show information of the new tap flow
|
||||
self.tap_flow_show(tap_flow['id'])
|
||||
|
||||
return SUCCESS
|
||||
|
||||
def tap_flow_delete(self, tap_flow_id):
|
||||
header_data = {}
|
||||
header_data['X-Auth-Token'] = self.token_id
|
||||
|
||||
url = '%s/tap-flows/%s' % (self.taas_url, tap_flow_id)
|
||||
|
||||
response = requests.delete(url, headers=header_data)
|
||||
|
||||
if response.status_code != 204:
|
||||
res_data = response.json()
|
||||
error = res_data['NeutronError']
|
||||
print (error['message'])
|
||||
return FAILURE
|
||||
|
||||
return SUCCESS
|
||||
|
||||
def tap_flow_list(self, tap_service_id):
|
||||
header_data = {}
|
||||
header_data['X-Auth-Token'] = self.token_id
|
||||
|
||||
url = '%s/tap-flows' % self.taas_url
|
||||
|
||||
response = requests.get(url, headers=header_data)
|
||||
res_data = response.json()
|
||||
|
||||
if response.status_code != 200:
|
||||
error = res_data['NeutronError']
|
||||
print (error['message'])
|
||||
return FAILURE
|
||||
|
||||
tap_flows = res_data['tap_flows']
|
||||
|
||||
sep = '+' + '-' * 38 + '+' + '-' * 12 + \
|
||||
'+' + '-' * 38 + '+' + '-' * 38 + '+'
|
||||
print (sep)
|
||||
print ('| {:36s} | {:10s} | {:36s} | {:36s} |'.format(
|
||||
'ID', 'Name', 'Port ID', 'Tap Service ID'))
|
||||
print (sep)
|
||||
for tap_flow in tap_flows:
|
||||
#
|
||||
# If a tap service has been specified only display tap flows
|
||||
# associated with that tap service; otherwise display all tap
|
||||
# flows that belong to the tenant
|
||||
#
|
||||
if (tap_service_id is None or
|
||||
tap_service_id == tap_flow['tap_service_id']):
|
||||
|
||||
print ('| {:36s} | {:10s} | {:36s} | {:36s} |'
|
||||
.format(tap_flow['id'],
|
||||
tap_flow['name'],
|
||||
tap_flow['source_port'],
|
||||
tap_flow['tap_service_id']))
|
||||
print (sep)
|
||||
|
||||
return SUCCESS
|
||||
|
||||
def tap_flow_show(self, tap_flow_id):
|
||||
header_data = {}
|
||||
header_data['X-Auth-Token'] = self.token_id
|
||||
|
||||
url = '%s/tap-flows/%s' % (self.taas_url, tap_flow_id)
|
||||
|
||||
response = requests.get(url, headers=header_data)
|
||||
res_data = response.json()
|
||||
|
||||
if response.status_code != 200:
|
||||
error = res_data['NeutronError']
|
||||
print (error['message'])
|
||||
return FAILURE
|
||||
|
||||
tap_flow = res_data['tap_flow']
|
||||
|
||||
sep = '+' + '-' * 16 + '+' + '-' * 38 + '+'
|
||||
print (sep)
|
||||
print ('| {:14} | {:36s} |'.format('Field', 'Value'))
|
||||
print (sep)
|
||||
print ('| {:14} | {:36s} |'
|
||||
.format('Name', tap_flow['name']))
|
||||
print ('| {:14} | {:36s} |'
|
||||
.format('Description', tap_flow['description']))
|
||||
print ('| {:14} | {:36s} |'
|
||||
.format('ID', tap_flow['id']))
|
||||
print ('| {:14} | {:36s} |'
|
||||
.format('Port ID', tap_flow['source_port']))
|
||||
print ('| {:14} | {:36s} |'
|
||||
.format('Direction', tap_flow['direction']))
|
||||
print ('| {:14} | {:36s} |'
|
||||
.format('Tap Service ID', tap_flow['tap_service_id']))
|
||||
print ('| {:14} | {:36s} |'
|
||||
.format('Tenant ID', tap_flow['tenant_id']))
|
||||
print (sep)
|
||||
|
||||
return SUCCESS
|
||||
|
||||
|
||||
# Handler for 'tap-service-create' subcommand
|
||||
def tap_service_create(args):
|
||||
api = taas_rest_api()
|
||||
|
||||
retval = api.authenticate()
|
||||
if retval != SUCCESS:
|
||||
return retval
|
||||
|
||||
return api.tap_service_create(args.name,
|
||||
args.description,
|
||||
args.port_id,
|
||||
args.network_id)
|
||||
|
||||
|
||||
# Handler for 'tap-service-delete' subcommand
|
||||
def tap_service_delete(args):
|
||||
api = taas_rest_api()
|
||||
|
||||
retval = api.authenticate()
|
||||
if retval != SUCCESS:
|
||||
return retval
|
||||
|
||||
return api.tap_service_delete(args.id)
|
||||
|
||||
|
||||
# Handler for 'tap-service-list' subcommand
|
||||
def tap_service_list(args):
|
||||
api = taas_rest_api()
|
||||
|
||||
retval = api.authenticate()
|
||||
if retval != SUCCESS:
|
||||
return retval
|
||||
|
||||
return api.tap_service_list()
|
||||
|
||||
|
||||
# Handler for 'tap-service-show' subcommand
|
||||
def tap_service_show(args):
|
||||
api = taas_rest_api()
|
||||
|
||||
retval = api.authenticate()
|
||||
if retval != SUCCESS:
|
||||
return retval
|
||||
|
||||
return api.tap_service_show(args.id)
|
||||
|
||||
|
||||
# Handler for 'tap-flow-create' subcommand
|
||||
def tap_flow_create(args):
|
||||
api = taas_rest_api()
|
||||
|
||||
retval = api.authenticate()
|
||||
if retval != SUCCESS:
|
||||
return retval
|
||||
|
||||
return api.tap_flow_create(args.name,
|
||||
args.description,
|
||||
args.port_id,
|
||||
args.direction,
|
||||
args.tap_service_id)
|
||||
|
||||
|
||||
# Handler for 'tap-flow-delete' subcommand
|
||||
def tap_flow_delete(args):
|
||||
api = taas_rest_api()
|
||||
|
||||
retval = api.authenticate()
|
||||
if retval != SUCCESS:
|
||||
return retval
|
||||
|
||||
return api.tap_flow_delete(args.id)
|
||||
|
||||
|
||||
# Handler for 'tap-flow-list' subcommand
|
||||
def tap_flow_list(args):
|
||||
api = taas_rest_api()
|
||||
|
||||
retval = api.authenticate()
|
||||
if retval != SUCCESS:
|
||||
return retval
|
||||
|
||||
return api.tap_flow_list(args.tap_service_id)
|
||||
|
||||
|
||||
# Handler for 'tap-flow-show' subcommand
|
||||
def tap_flow_show(args):
|
||||
api = taas_rest_api()
|
||||
|
||||
retval = api.authenticate()
|
||||
if retval != SUCCESS:
|
||||
return retval
|
||||
|
||||
return api.tap_flow_show(args.id)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Command-line interface \
|
||||
to the OpenStack Tap-as-a-Service API')
|
||||
subparsers = parser.add_subparsers(title='subcommands')
|
||||
|
||||
# Sub-parser for 'tap-service-create' subcommand
|
||||
parser_tap_service_create = subparsers.add_parser(
|
||||
'tap-service-create',
|
||||
help='Create a tap service for a given tenant')
|
||||
parser_tap_service_create.add_argument('--name',
|
||||
dest='name',
|
||||
required=False,
|
||||
default='')
|
||||
parser_tap_service_create.add_argument('--description',
|
||||
dest='description',
|
||||
required=False,
|
||||
default='')
|
||||
parser_tap_service_create.add_argument('--port-id',
|
||||
dest='port_id',
|
||||
required=True)
|
||||
parser_tap_service_create.add_argument('--network-id',
|
||||
dest='network_id',
|
||||
required=True)
|
||||
parser_tap_service_create.set_defaults(func=tap_service_create)
|
||||
|
||||
# Sub-parser for 'tap-service-delete' subcommand
|
||||
parser_tap_service_delete = subparsers.add_parser(
|
||||
'tap-service-delete',
|
||||
help='Delete a given tap service')
|
||||
parser_tap_service_delete.add_argument('id')
|
||||
parser_tap_service_delete.set_defaults(func=tap_service_delete)
|
||||
|
||||
# Sub-parser for 'tap-service-list' subcommand
|
||||
parser_tap_service_list = subparsers.add_parser(
|
||||
'tap-service-list',
|
||||
help='List tap services that belong to a given tenant')
|
||||
parser_tap_service_list.set_defaults(func=tap_service_list)
|
||||
|
||||
# Sub-parser for 'tap-service-show' subcommand
|
||||
parser_tap_service_show = subparsers.add_parser(
|
||||
'tap-service-show',
|
||||
help='Show information of a given tap service')
|
||||
parser_tap_service_show.add_argument('id')
|
||||
parser_tap_service_show.set_defaults(func=tap_service_show)
|
||||
|
||||
# Sub-parser for 'tap-flow-create' subcommand
|
||||
parser_tap_flow_create = subparsers.add_parser(
|
||||
'tap-flow-create',
|
||||
help='Create a tap flow for a given tap service')
|
||||
parser_tap_flow_create.add_argument('--name',
|
||||
dest='name',
|
||||
required=False,
|
||||
default='')
|
||||
parser_tap_flow_create.add_argument('--description',
|
||||
dest='description',
|
||||
required=False,
|
||||
default='')
|
||||
parser_tap_flow_create.add_argument('--port-id',
|
||||
dest='port_id',
|
||||
required=True)
|
||||
parser_tap_flow_create.add_argument('--direction',
|
||||
dest='direction',
|
||||
required=True)
|
||||
parser_tap_flow_create.add_argument('--tap-service-id',
|
||||
dest='tap_service_id',
|
||||
required=True)
|
||||
parser_tap_flow_create.set_defaults(func=tap_flow_create)
|
||||
|
||||
# Sub-parser for 'tap-flow-delete' subcommand
|
||||
parser_tap_flow_delete = subparsers.add_parser(
|
||||
'tap-flow-delete',
|
||||
help='Delete a given tap flow')
|
||||
parser_tap_flow_delete.add_argument('id')
|
||||
parser_tap_flow_delete.set_defaults(func=tap_flow_delete)
|
||||
|
||||
# Sub-parser for 'tap-flow-list' subcommand
|
||||
parser_tap_flow_list = subparsers.add_parser(
|
||||
'tap-flow-list',
|
||||
help='List tap flows that belong to given tenant')
|
||||
parser_tap_flow_list.add_argument('--tap-service-id',
|
||||
dest='tap_service_id',
|
||||
required=False,
|
||||
default=None)
|
||||
parser_tap_flow_list.set_defaults(func=tap_flow_list)
|
||||
|
||||
# Sub-parser for 'tap-flow-show' subcommand
|
||||
parser_tap_flow_show = subparsers.add_parser(
|
||||
'tap-flow-show',
|
||||
help='Show information of a given tap flow')
|
||||
parser_tap_flow_show.add_argument('id')
|
||||
parser_tap_flow_show.set_defaults(func=tap_flow_show)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
return args.func(args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in New Issue
Block a user