diff --git a/devstack/README.rst b/devstack/README.rst index 6b486eb7..dd311965 100644 --- a/devstack/README.rst +++ b/devstack/README.rst @@ -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 diff --git a/devstack/plugin.sh b/devstack/plugin.sh index 7d763016..a4489963 100644 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -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 diff --git a/neutron_taas/services/taas/agents/extensions/__init__.py b/neutron_taas/services/taas/agents/extensions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/neutron_taas/services/taas/agents/extensions/taas.py b/neutron_taas/services/taas/agents/extensions/taas.py new file mode 100644 index 00000000..96ebab95 --- /dev/null +++ b/neutron_taas/services/taas/agents/extensions/taas.py @@ -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 diff --git a/neutron_taas/services/taas/agents/ovs/agent.py b/neutron_taas/services/taas/agents/ovs/agent.py deleted file mode 100644 index a7748cf8..00000000 --- a/neutron_taas/services/taas/agents/ovs/agent.py +++ /dev/null @@ -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() diff --git a/neutron_taas/services/taas/agents/ovs/taas_ovs_agent.py b/neutron_taas/services/taas/agents/ovs/taas_ovs_agent.py index 59927f9d..1ee78176 100644 --- a/neutron_taas/services/taas/agents/ovs/taas_ovs_agent.py +++ b/neutron_taas/services/taas/agents/ovs/taas_ovs_agent.py @@ -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 + ) diff --git a/neutron_taas/services/taas/agents/taas_agent_api.py b/neutron_taas/services/taas/agents/taas_agent_api.py index f287ebf3..2dde63d4 100644 --- a/neutron_taas/services/taas/agents/taas_agent_api.py +++ b/neutron_taas/services/taas/agents/taas_agent_api.py @@ -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 diff --git a/neutron_taas/services/taas/drivers/linux/ovs_taas.py b/neutron_taas/services/taas/drivers/linux/ovs_taas.py index 843cdfdf..d3c3e8fe 100644 --- a/neutron_taas/services/taas/drivers/linux/ovs_taas.py +++ b/neutron_taas/services/taas/drivers/linux/ovs_taas.py @@ -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'] diff --git a/neutron_taas/services/taas/drivers/taas_base.py b/neutron_taas/services/taas/drivers/taas_base.py deleted file mode 100644 index 020c1f7a..00000000 --- a/neutron_taas/services/taas/drivers/taas_base.py +++ /dev/null @@ -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 diff --git a/neutron_taas/tests/unit/extensions/__init__.py b/neutron_taas/tests/unit/extensions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/neutron_taas/tests/unit/extensions/test_taas.py b/neutron_taas/tests/unit/extensions/test_taas.py new file mode 100644 index 00000000..d7ddf811 --- /dev/null +++ b/neutron_taas/tests/unit/extensions/test_taas.py @@ -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') diff --git a/setup.cfg b/setup.cfg index ebbc01c2..21f38791 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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 =