L2 agent extension support for Tap-as-a-Service
This patch adds L2 agent extension for TaaS. Change-Id: Ife1807c04d8f0f44f067e1905890262ae2f7e426
This commit is contained in:
parent
7fa6d23311
commit
33b116ee7e
@ -7,5 +7,4 @@ A `local.conf` recipe to enable tap-as-a-service::
|
||||
[[local|localrc]]
|
||||
enable_plugin tap-as-a-service https://github.com/openstack/tap-as-a-service
|
||||
enable_service taas
|
||||
enable_service taas_openvswitch_agent
|
||||
TAAS_SERVICE_DRIVER=TAAS:TAAS:neutron_taas.services.taas.service_drivers.taas_rpc.TaasRpcDriver:default
|
||||
|
@ -31,20 +31,6 @@ function configure_taas_plugin {
|
||||
_neutron_service_plugin_class_add taas
|
||||
}
|
||||
|
||||
function configure_taas_openvswitch_agent {
|
||||
local conf=$TAAS_OVS_AGENT_CONF_FILE
|
||||
|
||||
cp $TAAS_PLUGIN_PATH/etc/taas.ini $conf
|
||||
iniset $conf taas driver neutron_taas.services.taas.drivers.linux.ovs_taas.OvsTaasDriver
|
||||
iniset $conf taas enabled True
|
||||
iniset $conf taas vlan_range_start 3000
|
||||
iniset $conf taas vlan_range_end 3500
|
||||
}
|
||||
|
||||
function start_taas_openvswitch_agent {
|
||||
run_process taas_openvswitch_agent "$TAAS_OVS_AGENT_BINARY --config-file $NEUTRON_CONF --config-file $TAAS_OVS_AGENT_CONF_FILE"
|
||||
}
|
||||
|
||||
if is_service_enabled taas; then
|
||||
if [[ "$1" == "stack" ]]; then
|
||||
if [[ "$2" == "pre-install" ]]; then
|
||||
@ -67,20 +53,22 @@ if is_service_enabled taas; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if is_service_enabled taas_openvswitch_agent; then
|
||||
if is_service_enabled q-agt neutron-agent; then
|
||||
if [[ "$1" == "stack" ]]; then
|
||||
if [[ "$2" == "pre-install" ]]; then
|
||||
:
|
||||
elif [[ "$2" == "install" ]]; then
|
||||
install_taas
|
||||
elif [[ "$2" == "post-config" ]]; then
|
||||
configure_taas_openvswitch_agent
|
||||
if is_service_enabled q-agt neutron-agent; then
|
||||
source $NEUTRON_DIR/devstack/lib/l2_agent
|
||||
plugin_agent_add_l2_agent_extension taas
|
||||
configure_l2_agent
|
||||
fi
|
||||
elif [[ "$2" == "extra" ]]; then
|
||||
# NOTE(yamamoto): This agent should be run after ovs-agent
|
||||
# sets up its bridges. (bug 1515104)
|
||||
start_taas_openvswitch_agent
|
||||
:
|
||||
fi
|
||||
elif [[ "$1" == "unstack" ]]; then
|
||||
stop_process taas_openvswitch_agent
|
||||
:
|
||||
fi
|
||||
fi
|
||||
|
91
neutron_taas/services/taas/agents/extensions/taas.py
Normal file
91
neutron_taas/services/taas/agents/extensions/taas.py
Normal file
@ -0,0 +1,91 @@
|
||||
# Copyright 2017 FUJITSU LABORATORIES LTD.
|
||||
# Copyright 2016 NEC Technologies India Pvt. Ltd.
|
||||
#
|
||||
# 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
|
||||
|
||||
from neutron.agent.l2 import l2_agent_extension
|
||||
|
||||
from neutron_taas.services.taas.agents.ovs import taas_ovs_agent
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
OPTS = [
|
||||
cfg.IntOpt(
|
||||
'taas_agent_periodic_interval',
|
||||
default=5,
|
||||
help=_('Seconds between periodic task runs')
|
||||
)
|
||||
]
|
||||
cfg.CONF.register_opts(OPTS)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class TaasAgentDriver(object):
|
||||
"""Defines stable abstract interface for TaaS Agent Driver."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def initialize(self):
|
||||
"""Perform Taas agent driver initialization."""
|
||||
|
||||
def consume_api(self, agent_api):
|
||||
"""Consume the AgentAPI instance from the TaasAgentExtension class
|
||||
|
||||
:param agent_api: An instance of an agent specific API
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_tap_service(self, tap_service):
|
||||
"""Create a Tap Service request in driver."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_tap_flow(self, tap_flow):
|
||||
"""Create a tap flow request in driver."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_tap_service(self, tap_service):
|
||||
"""delete a Tap Service request in driver."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_tap_flow(self, tap_flow):
|
||||
"""Delete a tap flow request in driver."""
|
||||
|
||||
|
||||
class TaasAgentExtension(l2_agent_extension.L2AgentExtension):
|
||||
|
||||
def initialize(self, connection, driver_type):
|
||||
"""Initialize agent extension."""
|
||||
self.taas_agent = taas_ovs_agent.TaasOvsAgentRpcCallback(
|
||||
cfg.CONF, driver_type)
|
||||
self.taas_agent.consume_api(self.agent_api)
|
||||
self.taas_agent.initialize()
|
||||
|
||||
def consume_api(self, agent_api):
|
||||
"""Receive neutron agent API object
|
||||
|
||||
Allows an extension to gain access to resources internal to the
|
||||
neutron agent and otherwise unavailable to the extension.
|
||||
"""
|
||||
self.agent_api = agent_api
|
||||
|
||||
def handle_port(self, context, port):
|
||||
pass
|
||||
|
||||
def delete_port(self, context, port):
|
||||
pass
|
@ -1,75 +0,0 @@
|
||||
# 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_taas._i18n import _
|
||||
from neutron_taas.common import topics
|
||||
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()
|
||||
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()
|
@ -14,15 +14,15 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
from neutron.agent.common import config
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron import manager
|
||||
|
||||
from neutron_taas._i18n import _
|
||||
from neutron_taas.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
|
||||
from oslo_service import service
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -37,30 +37,25 @@ class TaasOvsPluginApi(api.TaasPluginApiMixin):
|
||||
|
||||
class TaasOvsAgentRpcCallback(api.TaasAgentRpcCallbackMixin):
|
||||
|
||||
def __init__(self, conf):
|
||||
|
||||
def __init__(self, conf, driver_type):
|
||||
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.driver_type = driver_type
|
||||
|
||||
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 initialize(self):
|
||||
self.taas_driver = manager.NeutronManager.load_class_for_provider(
|
||||
'neutron_taas.taas.agent_drivers', self.driver_type)()
|
||||
self.taas_driver.consume_api(self.agent_api)
|
||||
self.taas_driver.initialize()
|
||||
|
||||
self._taas_rpc_setup()
|
||||
TaasOvsAgentService(self).start()
|
||||
|
||||
def consume_api(self, agent_api):
|
||||
self.agent_api = agent_api
|
||||
|
||||
def _invoke_driver_for_plugin_api(self, context, args, func_name):
|
||||
LOG.debug("Invoking Driver for %(func_name)s from agent",
|
||||
@ -117,10 +112,33 @@ class TaasOvsAgentRpcCallback(api.TaasAgentRpcCallbackMixin):
|
||||
tap_flow_msg,
|
||||
'delete_tap_flow')
|
||||
|
||||
def periodic_tasks(self, argv):
|
||||
def _taas_rpc_setup(self):
|
||||
# setup RPC to msg taas plugin
|
||||
self.taas_plugin_rpc = TaasOvsPluginApi(
|
||||
topics.TAAS_PLUGIN, self.conf.host)
|
||||
|
||||
endpoints = [self]
|
||||
conn = n_rpc.create_connection()
|
||||
conn.create_consumer(topics.TAAS_AGENT, endpoints, fanout=False)
|
||||
conn.consume_in_threads()
|
||||
|
||||
def periodic_tasks(self):
|
||||
#
|
||||
# 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
|
||||
|
||||
|
||||
class TaasOvsAgentService(service.Service):
|
||||
def __init__(self, driver):
|
||||
super(TaasOvsAgentService, self).__init__()
|
||||
self.driver = driver
|
||||
|
||||
def start(self):
|
||||
super(TaasOvsAgentService, self).start()
|
||||
self.tg.add_timer(
|
||||
int(cfg.CONF.taas_agent_periodic_interval),
|
||||
self.driver.periodic_tasks,
|
||||
None
|
||||
)
|
||||
|
@ -50,6 +50,14 @@ class TaasAgentRpcCallbackMixin(object):
|
||||
def __init__(self):
|
||||
super(TaasAgentRpcCallbackMixin, self).__init__()
|
||||
|
||||
def consume_api(self, agent_api):
|
||||
"""Receive neutron agent API object
|
||||
|
||||
Allows an extension to gain access to resources internal to the
|
||||
neutron agent and otherwise unavailable to the extension.
|
||||
"""
|
||||
self.agent_api = agent_api
|
||||
|
||||
def create_tap_service(self, context, tap_service, host):
|
||||
"""Handle RPC cast from plugin to create a tap service."""
|
||||
pass
|
||||
|
@ -16,10 +16,12 @@
|
||||
|
||||
from neutron.agent.common import ovs_lib
|
||||
from neutron.agent.linux import utils
|
||||
from neutron.conf.agent import common
|
||||
# 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 neutron_taas.services.taas.agents.extensions import taas as taas_base
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import ovs_constants as taas_ovs_consts
|
||||
import ovs_utils as taas_ovs_utils
|
||||
@ -34,14 +36,16 @@ class OVSBridge_tap_extension(ovs_lib.OVSBridge):
|
||||
super(OVSBridge_tap_extension, self).__init__(br_name)
|
||||
|
||||
|
||||
class OvsTaasDriver(taas_base.TaasDriverBase):
|
||||
def __init__(self, root_helper):
|
||||
class OvsTaasDriver(taas_base.TaasAgentDriver):
|
||||
def __init__(self):
|
||||
super(OvsTaasDriver, self).__init__()
|
||||
LOG.debug("Initializing Taas OVS Driver")
|
||||
self.agent_api = None
|
||||
self.root_helper = common.get_root_helper(cfg.CONF)
|
||||
|
||||
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)
|
||||
def initialize(self):
|
||||
self.int_br = self.agent_api.request_int_br()
|
||||
self.tun_br = self.agent_api.request_tun_br()
|
||||
self.tap_br = OVSBridge_tap_extension('br-tap', self.root_helper)
|
||||
|
||||
# Prepare OVS bridges for TaaS
|
||||
@ -185,6 +189,9 @@ class OvsTaasDriver(taas_base.TaasDriverBase):
|
||||
|
||||
return
|
||||
|
||||
def consume_api(self, agent_api):
|
||||
self.agent_api = agent_api
|
||||
|
||||
def create_tap_service(self, tap_service):
|
||||
taas_id = tap_service['taas_id']
|
||||
port = tap_service['port']
|
||||
|
@ -1,37 +0,0 @@
|
||||
# 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
|
0
neutron_taas/tests/unit/extensions/__init__.py
Normal file
0
neutron_taas/tests/unit/extensions/__init__.py
Normal file
105
neutron_taas/tests/unit/extensions/test_taas.py
Normal file
105
neutron_taas/tests/unit/extensions/test_taas.py
Normal file
@ -0,0 +1,105 @@
|
||||
# Copyright 2017 FUJITSU LABORATORIES LTD.
|
||||
|
||||
# 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 copy
|
||||
import mock
|
||||
from webob import exc
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from neutron.tests.unit.api.v2 import test_base as test_api_v2
|
||||
from neutron.tests.unit.extensions import base as test_api_v2_extension
|
||||
|
||||
from neutron_taas.extensions import taas as taas_ext
|
||||
|
||||
_uuid = uuidutils.generate_uuid
|
||||
_get_path = test_api_v2._get_path
|
||||
|
||||
TAP_SERVICE_PATH = 'taas/tap_services'
|
||||
TAP_FLOW_PATH = 'taas/tap_flows'
|
||||
|
||||
|
||||
class TaasExtensionTestCase(test_api_v2_extension.ExtensionTestCase):
|
||||
fmt = 'json'
|
||||
|
||||
def setUp(self):
|
||||
super(TaasExtensionTestCase, self).setUp()
|
||||
self._setUpExtension(
|
||||
'neutron_taas.extensions.taas.TaasPluginBase',
|
||||
'TAAS',
|
||||
taas_ext.RESOURCE_ATTRIBUTE_MAP,
|
||||
taas_ext.Taas,
|
||||
'taas',
|
||||
plural_mappings={}
|
||||
)
|
||||
|
||||
def test_create_tap_service(self):
|
||||
tenant_id = _uuid()
|
||||
tap_service_data = {
|
||||
'tenant_id': tenant_id,
|
||||
'name': 'MyTap',
|
||||
'description': 'This is my tap service',
|
||||
'port_id': _uuid(),
|
||||
'project_id': tenant_id,
|
||||
}
|
||||
data = {'tap_service': tap_service_data}
|
||||
expected_ret_val = copy.copy(data['tap_service'])
|
||||
expected_ret_val.update({'id': _uuid()})
|
||||
instance = self.plugin.return_value
|
||||
instance.create_tap_service.return_value = expected_ret_val
|
||||
|
||||
res = self.api.post(_get_path(TAP_SERVICE_PATH, fmt=self.fmt),
|
||||
self.serialize(data),
|
||||
content_type='application/%s' % self.fmt)
|
||||
instance.create_tap_service.assert_called_with(
|
||||
mock.ANY,
|
||||
tap_service=data)
|
||||
self.assertEqual(exc.HTTPCreated.code, res.status_int)
|
||||
res = self.deserialize(res)
|
||||
self.assertIn('tap_service', res)
|
||||
self.assertEqual(expected_ret_val, res['tap_service'])
|
||||
|
||||
def test_delete_tap_service(self):
|
||||
self._test_entity_delete('tap_service')
|
||||
|
||||
def test_create_tap_flow(self):
|
||||
tenant_id = _uuid()
|
||||
tap_flow_data = {
|
||||
'tenant_id': tenant_id,
|
||||
'name': 'MyTapFlow',
|
||||
'description': 'This is my tap flow',
|
||||
'direction': 'BOTH',
|
||||
'tap_service_id': _uuid(),
|
||||
'source_port': _uuid(),
|
||||
'project_id': tenant_id,
|
||||
}
|
||||
data = {'tap_flow': tap_flow_data}
|
||||
expected_ret_val = copy.copy(data['tap_flow'])
|
||||
expected_ret_val.update({'id': _uuid()})
|
||||
instance = self.plugin.return_value
|
||||
instance.create_tap_flow.return_value = expected_ret_val
|
||||
|
||||
res = self.api.post(_get_path(TAP_FLOW_PATH, fmt=self.fmt),
|
||||
self.serialize(data),
|
||||
content_type='application/%s' % self.fmt)
|
||||
instance.create_tap_flow.assert_called_with(
|
||||
mock.ANY,
|
||||
tap_flow=data)
|
||||
self.assertEqual(exc.HTTPCreated.code, res.status_int)
|
||||
res = self.deserialize(res)
|
||||
self.assertIn('tap_flow', res)
|
||||
self.assertEqual(expected_ret_val, res['tap_flow'])
|
||||
|
||||
def test_delete_tap_flow(self):
|
||||
self._test_entity_delete('tap_flow')
|
@ -45,8 +45,10 @@ mapping_file = babel.cfg
|
||||
output_file = neutron_taas/locale/neutron_taas.pot
|
||||
|
||||
[entry_points]
|
||||
console_scripts =
|
||||
neutron-taas-openvswitch-agent = neutron_taas.services.taas.agents.ovs.agent:main
|
||||
neutron.agent.l2.extensions =
|
||||
taas = neutron_taas.services.taas.agents.extensions.taas:TaasAgentExtension
|
||||
neutron_taas.taas.agent_drivers =
|
||||
ovs = neutron_taas.services.taas.drivers.linux.ovs_taas:OvsTaasDriver
|
||||
neutron.service_plugins =
|
||||
taas = neutron_taas.services.taas.taas_plugin:TaasPlugin
|
||||
neutron.db.alembic_migrations =
|
||||
|
Loading…
Reference in New Issue
Block a user