# Maximum number of rules that a single router may have
# Default is 200
# max_router_rules=200
+
+[restproxyagent]
+
+# Specify the name of the bridge used on compute nodes
+# for attachment.
+# Default: br-int
+# integration_bridge=br-int
+
+# Change the frequency of polling by the restproxy agent.
+# Value is seconds
+# Default: 5
+# polling_interval=5
+
+# Virtual switch type on the compute node.
+# Options: ovs or ivs
+# Default: ovs
+# virtual_switch_type = ovs
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2014 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.
+#
+
+"""bsn_security_groups
+
+Revision ID: f44ab9871cd6
+Revises: e766b19a3bb
+Create Date: 2014-02-26 17:43:43.051078
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'f44ab9871cd6'
+down_revision = 'e766b19a3bb'
+
+# Change to ['*'] if this migration applies to all plugins
+
+migration_for_plugins = [
+ 'neutron.plugins.bigswitch.plugin.NeutronRestProxyV2',
+]
+
+from alembic import op
+import sqlalchemy as sa
+
+from neutron.db import migration
+
+
+def upgrade(active_plugins=None, options=None):
+ if not migration.should_run(active_plugins, migration_for_plugins):
+ return
+
+ ### commands auto generated by Alembic - please adjust! ###
+ op.create_table(
+ 'securitygroups',
+ sa.Column('tenant_id', sa.String(length=255), nullable=True),
+ sa.Column('id', sa.String(length=36), nullable=False),
+ sa.Column('name', sa.String(length=255), nullable=True),
+ sa.Column('description', sa.String(length=255), nullable=True),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_table(
+ 'securitygrouprules',
+ sa.Column('tenant_id', sa.String(length=255), nullable=True),
+ sa.Column('id', sa.String(length=36), nullable=False),
+ sa.Column('security_group_id', sa.String(length=36), nullable=False),
+ sa.Column('remote_group_id', sa.String(length=36), nullable=True),
+ sa.Column('direction',
+ sa.Enum('ingress', 'egress',
+ name='securitygrouprules_direction'),
+ nullable=True),
+ sa.Column('ethertype', sa.String(length=40), nullable=True),
+ sa.Column('protocol', sa.String(length=40), nullable=True),
+ sa.Column('port_range_min', sa.Integer(), nullable=True),
+ sa.Column('port_range_max', sa.Integer(), nullable=True),
+ sa.Column('remote_ip_prefix', sa.String(length=255), nullable=True),
+ sa.ForeignKeyConstraint(['security_group_id'], ['securitygroups.id'],
+ ondelete='CASCADE'),
+ sa.ForeignKeyConstraint(['remote_group_id'], ['securitygroups.id'],
+ ondelete='CASCADE'),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_table(
+ 'securitygroupportbindings',
+ sa.Column('port_id', sa.String(length=36), nullable=False),
+ sa.Column('security_group_id', sa.String(length=36), nullable=False),
+ sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'),
+ sa.ForeignKeyConstraint(['security_group_id'], ['securitygroups.id']),
+ sa.PrimaryKeyConstraint('port_id', 'security_group_id')
+ )
+ ### end Alembic commands ###
+
+
+def downgrade(active_plugins=None, options=None):
+ if not migration.should_run(active_plugins, migration_for_plugins):
+ return
+
+ ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('securitygroupportbindings')
+ op.drop_table('securitygrouprules')
+ op.drop_table('securitygroups')
+ ### end Alembic commands ###
--- /dev/null
+# Copyright 2014 Big Switch Networks, Inc.
+# All Rights Reserved.
+#
+# Copyright 2011 Nicira Networks, Inc.
+# 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.
+# @author: Kevin Benton, kevin.benton@bigswitch.com
+
+import eventlet
+import sys
+import time
+
+from oslo.config import cfg
+
+from neutron.agent.linux import ovs_lib
+from neutron.agent.linux import utils
+from neutron.agent import rpc as agent_rpc
+from neutron.agent import securitygroups_rpc as sg_rpc
+from neutron.common import config
+from neutron.common import topics
+from neutron import context as q_context
+from neutron.extensions import securitygroup as ext_sg
+from neutron.openstack.common import excutils
+from neutron.openstack.common import log
+from neutron.openstack.common.rpc import dispatcher
+from neutron.plugins.bigswitch import config as pl_config
+
+LOG = log.getLogger(__name__)
+
+
+class IVSBridge(ovs_lib.OVSBridge):
+ '''
+ This class does not provide parity with OVS using IVS.
+ It's only the bare minimum necessary to use IVS with this agent.
+ '''
+ def run_vsctl(self, args, check_error=False):
+ full_args = ["ivs-ctl"] + args
+ try:
+ return utils.execute(full_args, root_helper=self.root_helper)
+ except Exception as e:
+ with excutils.save_and_reraise_exception() as ctxt:
+ LOG.error(_("Unable to execute %(cmd)s. "
+ "Exception: %(exception)s"),
+ {'cmd': full_args, 'exception': e})
+ if not check_error:
+ ctxt.reraise = False
+
+ def get_vif_port_set(self):
+ port_names = self.get_port_name_list()
+ edge_ports = set(port_names)
+ return edge_ports
+
+ def get_vif_port_by_id(self, port_id):
+ # IVS in nova uses hybrid method with last 14 chars of UUID
+ name = 'qvo%s' % port_id[:14]
+ if name in self.get_vif_port_set():
+ return name
+ return False
+
+
+class PluginApi(agent_rpc.PluginApi,
+ sg_rpc.SecurityGroupServerRpcApiMixin):
+ pass
+
+
+class SecurityGroupAgent(sg_rpc.SecurityGroupAgentRpcMixin):
+ def __init__(self, context, plugin_rpc, root_helper):
+ self.context = context
+ self.plugin_rpc = plugin_rpc
+ self.root_helper = root_helper
+ self.init_firewall()
+
+
+class RestProxyAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
+
+ RPC_API_VERSION = '1.1'
+
+ def __init__(self, integ_br, polling_interval, root_helper, vs='ovs'):
+ super(RestProxyAgent, self).__init__()
+ self.polling_interval = polling_interval
+ self._setup_rpc()
+ self.sg_agent = SecurityGroupAgent(self.context,
+ self.plugin_rpc,
+ root_helper)
+ if vs == 'ivs':
+ self.int_br = IVSBridge(integ_br, root_helper)
+ else:
+ self.int_br = ovs_lib.OVSBridge(integ_br, root_helper)
+
+ def _setup_rpc(self):
+ self.topic = topics.AGENT
+ self.plugin_rpc = PluginApi(topics.PLUGIN)
+ self.context = q_context.get_admin_context_without_session()
+ self.dispatcher = dispatcher.RpcDispatcher([self])
+ consumers = [[topics.PORT, topics.UPDATE],
+ [topics.SECURITY_GROUP, topics.UPDATE]]
+ self.connection = agent_rpc.create_consumers(self.dispatcher,
+ self.topic,
+ consumers)
+
+ def port_update(self, context, **kwargs):
+ LOG.debug(_("Port update received"))
+ port = kwargs.get('port')
+ vif_port = self.int_br.get_vif_port_by_id(port['id'])
+ if not vif_port:
+ LOG.debug(_("Port %s is not present on this host."), port['id'])
+ return
+
+ LOG.debug(_("Port %s found. Refreshing firewall."), port['id'])
+ if ext_sg.SECURITYGROUPS in port:
+ self.sg_agent.refresh_firewall()
+
+ def _update_ports(self, registered_ports):
+ ports = self.int_br.get_vif_port_set()
+ if ports == registered_ports:
+ return
+ added = ports - registered_ports
+ removed = registered_ports - ports
+ return {'current': ports,
+ 'added': added,
+ 'removed': removed}
+
+ def _process_devices_filter(self, port_info):
+ if 'added' in port_info:
+ self.sg_agent.prepare_devices_filter(port_info['added'])
+ if 'removed' in port_info:
+ self.sg_agent.remove_devices_filter(port_info['removed'])
+
+ def daemon_loop(self):
+ ports = set()
+
+ while True:
+ start = time.time()
+ try:
+ port_info = self._update_ports(ports)
+ if port_info:
+ LOG.debug(_("Agent loop has new device"))
+ self._process_devices_filter(port_info)
+ ports = port_info['current']
+ except Exception:
+ LOG.exception(_("Error in agent event loop"))
+
+ elapsed = max(time.time() - start, 0)
+ if (elapsed < self.polling_interval):
+ time.sleep(self.polling_interval - elapsed)
+ else:
+ LOG.debug(_("Loop iteration exceeded interval "
+ "(%(polling_interval)s vs. %(elapsed)s)!"),
+ {'polling_interval': self.polling_interval,
+ 'elapsed': elapsed})
+
+
+def main():
+ eventlet.monkey_patch()
+ cfg.CONF(project='neutron')
+ config.setup_logging(cfg.CONF)
+ pl_config.register_config()
+
+ integ_br = cfg.CONF.RESTPROXYAGENT.integration_bridge
+ polling_interval = cfg.CONF.RESTPROXYAGENT.polling_interval
+ root_helper = cfg.CONF.AGENT.root_helper
+ bsnagent = RestProxyAgent(integ_br, polling_interval, root_helper,
+ cfg.CONF.RESTPROXYAGENT.virtual_switch_type)
+ bsnagent.daemon_loop()
+ sys.exit(0)
+
+if __name__ == "__main__":
+ main()
from oslo.config import cfg
+from neutron.agent.common import config as agconfig
from neutron.common import utils
from neutron.extensions import portbindings
default=portbindings.VIF_TYPES,
help=_('List of allowed vif_type values.')))
+agent_opts = [
+ cfg.StrOpt('integration_bridge', default='br-int',
+ help=_('Name of integration bridge on compute '
+ 'nodes used for security group insertion.')),
+ cfg.IntOpt('polling_interval', default=5,
+ help=_('Seconds between agent checks for port changes')),
+ cfg.StrOpt('virtual_switch_type', default='ovs',
+ help=_('Virtual switch type.'))
+]
+
def register_config():
cfg.CONF.register_opts(restproxy_opts, "RESTPROXY")
cfg.CONF.register_opts(router_opts, "ROUTER")
cfg.CONF.register_opts(nova_opts, "NOVA")
+ cfg.CONF.register_opts(agent_opts, "RESTPROXYAGENT")
+ agconfig.register_root_helper(cfg.CONF)
"""
import copy
+import re
from oslo.config import cfg
+from neutron.agent import securitygroups_rpc as sg_rpc
from neutron.api import extensions as neutron_extensions
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
from neutron.common import constants as const
from neutron import context as qcontext
from neutron.db import agents_db
from neutron.db import agentschedulers_db
+from neutron.db import api as db
from neutron.db import db_base_plugin_v2
from neutron.db import dhcp_rpc_base
from neutron.db import external_net_db
from neutron.db import extradhcpopt_db
from neutron.db import l3_db
+from neutron.db import models_v2
+from neutron.db import securitygroups_db as sg_db
+from neutron.db import securitygroups_rpc_base as sg_rpc_base
from neutron.extensions import external_net
from neutron.extensions import extra_dhcp_opt as edo_ext
from neutron.extensions import l3
from neutron.extensions import portbindings
+from neutron import manager
from neutron.openstack.common import excutils
from neutron.openstack.common import importutils
from neutron.openstack.common import log as logging
METADATA_SERVER_IP = '169.254.169.254'
-class RpcProxy(dhcp_rpc_base.DhcpRpcCallbackMixin):
+class AgentNotifierApi(rpc.proxy.RpcProxy,
+ sg_rpc.SecurityGroupAgentRpcApiMixin):
+
+ BASE_RPC_API_VERSION = '1.1'
+
+ def __init__(self, topic):
+ super(AgentNotifierApi, self).__init__(
+ topic=topic, default_version=self.BASE_RPC_API_VERSION)
+ self.topic_port_update = topics.get_topic_name(
+ topic, topics.PORT, topics.UPDATE)
+
+ def port_update(self, context, port):
+ self.fanout_cast(context,
+ self.make_msg('port_update',
+ port=port),
+ topic=self.topic_port_update)
+
+
+class RestProxyCallbacks(sg_rpc_base.SecurityGroupServerRpcCallbackMixin,
+ dhcp_rpc_base.DhcpRpcCallbackMixin):
RPC_API_VERSION = '1.1'
return q_rpc.PluginRpcDispatcher([self,
agents_db.AgentExtRpcCallback()])
+ def get_port_from_device(self, device):
+ port_id = re.sub(r"^tap", "", device)
+ port = self.get_port_and_sgs(port_id)
+ if port:
+ port['device'] = device
+ return port
+
+ def get_port_and_sgs(self, port_id):
+ """Get port from database with security group info."""
+
+ LOG.debug(_("get_port_and_sgs() called for port_id %s"), port_id)
+ session = db.get_session()
+ sg_binding_port = sg_db.SecurityGroupPortBinding.port_id
+
+ with session.begin(subtransactions=True):
+ query = session.query(
+ models_v2.Port,
+ sg_db.SecurityGroupPortBinding.security_group_id
+ )
+ query = query.outerjoin(sg_db.SecurityGroupPortBinding,
+ models_v2.Port.id == sg_binding_port)
+ query = query.filter(models_v2.Port.id.startswith(port_id))
+ port_and_sgs = query.all()
+ if not port_and_sgs:
+ return
+ port = port_and_sgs[0][0]
+ plugin = manager.NeutronManager.get_plugin()
+ port_dict = plugin._make_port_dict(port)
+ port_dict['security_groups'] = [
+ sg_id for port_, sg_id in port_and_sgs if sg_id]
+ port_dict['security_group_rules'] = []
+ port_dict['security_group_source_groups'] = []
+ port_dict['fixed_ips'] = [ip['ip_address']
+ for ip in port['fixed_ips']]
+ return port_dict
+
class NeutronRestProxyV2Base(db_base_plugin_v2.NeutronDbPluginV2,
external_net_db.External_net_db_mixin,
class NeutronRestProxyV2(NeutronRestProxyV2Base,
extradhcpopt_db.ExtraDhcpOptMixin,
- agentschedulers_db.DhcpAgentSchedulerDbMixin):
-
- supported_extension_aliases = ["external-net", "router", "binding",
- "router_rules", "extra_dhcp_opt", "quotas",
- "dhcp_agent_scheduler", "agent"]
+ agentschedulers_db.DhcpAgentSchedulerDbMixin,
+ sg_rpc_base.SecurityGroupServerRpcMixin):
+
+ _supported_extension_aliases = ["external-net", "router", "binding",
+ "router_rules", "extra_dhcp_opt", "quotas",
+ "dhcp_agent_scheduler", "agent",
+ "security-group"]
+
+ @property
+ def supported_extension_aliases(self):
+ if not hasattr(self, '_aliases'):
+ aliases = self._supported_extension_aliases[:]
+ sg_rpc.disable_security_group_extension_if_noop_driver(aliases)
+ self._aliases = aliases
+ return self._aliases
def __init__(self, server_timeout=None):
super(NeutronRestProxyV2, self).__init__()
# init network ctrl connections
self.servers = servermanager.ServerPool(server_timeout)
- # init dhcp support
- self.topic = topics.PLUGIN
self.network_scheduler = importutils.import_object(
cfg.CONF.network_scheduler_driver
)
+
+ # setup rpc for security and DHCP agents
+ self._setup_rpc()
+
+ if cfg.CONF.RESTPROXY.sync_data:
+ self._send_all_data()
+
+ LOG.debug(_("NeutronRestProxyV2: initialization done"))
+
+ def _setup_rpc(self):
+ self.conn = rpc.create_connection(new=True)
+ self.topic = topics.PLUGIN
+ self.notifier = AgentNotifierApi(topics.AGENT)
+ # init dhcp agent support
self._dhcp_agent_notifier = dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
self.agent_notifiers[const.AGENT_TYPE_DHCP] = (
self._dhcp_agent_notifier
)
- self.conn = rpc.create_connection(new=True)
- self.callbacks = RpcProxy()
+ self.callbacks = RestProxyCallbacks()
self.dispatcher = self.callbacks.create_rpc_dispatcher()
self.conn.create_consumer(self.topic, self.dispatcher,
fanout=False)
# Consume from all consumers in a thread
self.conn.consume_in_thread()
- if cfg.CONF.RESTPROXY.sync_data:
- self._send_all_data()
-
- LOG.debug(_("NeutronRestProxyV2: initialization done"))
def create_network(self, context, network):
"""Create a network.
self._warn_on_state_status(network['network'])
with context.session.begin(subtransactions=True):
+ self._ensure_default_security_group(
+ context,
+ network['network']["tenant_id"]
+ )
# create network in DB
new_net = super(NeutronRestProxyV2, self).create_network(context,
network)
# Update DB in new session so exceptions rollback changes
with context.session.begin(subtransactions=True):
+ self._ensure_default_security_group_on_port(context, port)
+ sgids = self._get_security_groups_on_port(context, port)
dhcp_opts = port['port'].get(edo_ext.EXTRADHCPOPTS, [])
new_port = super(NeutronRestProxyV2, self).create_port(context,
port)
self.servers.rest_create_port(net_tenant_id,
new_port["network_id"],
mapped_port)
+ self._process_port_create_security_group(context, new_port, sgids)
+ self.notify_security_groups_member_updated(context, new_port)
return new_port
def get_port(self, context, id, fields=None):
self.servers.rest_update_port(net_tenant_id,
new_port["network_id"],
mapped_port)
+ agent_update_required = self.update_security_group_on_port(
+ context, port_id, port, orig_port, new_port)
+ agent_update_required |= self.is_security_group_member_updated(
+ context, orig_port, new_port)
# return new_port
return new_port
def delete_port(self, context, port_id, l3_port_check=True):
"""Delete a port.
-
:param context: neutron api request context
:param id: UUID representing the port to delete.
self.prevent_l3_port_deletion(context, port_id)
with context.session.begin(subtransactions=True):
self.disassociate_floatingips(context, port_id)
+ self._delete_port_security_group_bindings(context, port_id)
super(NeutronRestProxyV2, self).delete_port(context, port_id)
def _delete_port(self, context, port_id):
from neutron.tests.unit.bigswitch import fake_server
RESTPROXY_PKG_PATH = 'neutron.plugins.bigswitch.plugin'
-NOTIFIER = 'neutron.plugins.bigswitch.plugin.RpcProxy'
+NOTIFIER = 'neutron.plugins.bigswitch.plugin.AgentNotifierApi'
+CALLBACKS = 'neutron.plugins.bigswitch.plugin.RestProxyCallbacks'
+CERTFETCH = 'neutron.plugins.bigswitch.servermanager.ServerPool._fetch_cert'
HTTPCON = 'httplib.HTTPConnection'
self.httpPatch = mock.patch(HTTPCON, create=True,
new=fake_server.HTTPConnectionMock)
self.plugin_notifier_p = mock.patch(NOTIFIER)
+ self.callbacks_p = mock.patch(CALLBACKS)
self.addCleanup(mock.patch.stopall)
self.addCleanup(db.clear_db)
+ self.callbacks_p.start()
self.plugin_notifier_p.start()
self.httpPatch.start()
--- /dev/null
+# Copyright 2014 Big Switch Networks, Inc.
+# 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.
+#
+# @author: Kevin Benton, Big Switch Networks
+
+from contextlib import nested
+
+import mock
+
+from neutron.openstack.common import importutils
+from neutron.tests import base
+
+OVSBRIDGE = 'neutron.agent.linux.ovs_lib.OVSBridge'
+PLUGINAPI = 'neutron.plugins.bigswitch.agent.restproxy_agent.PluginApi'
+CONTEXT = 'neutron.context'
+CONSUMERCREATE = 'neutron.agent.rpc.create_consumers'
+SGRPC = 'neutron.agent.securitygroups_rpc'
+SGAGENT = 'neutron.plugins.bigswitch.agent.restproxy_agent.SecurityGroupAgent'
+AGENTMOD = 'neutron.plugins.bigswitch.agent.restproxy_agent'
+NEUTRONCFG = 'neutron.common.config'
+PLCONFIG = 'neutron.plugins.bigswitch.config'
+
+
+class BaseAgentTestCase(base.BaseTestCase):
+
+ def setUp(self):
+ super(BaseAgentTestCase, self).setUp()
+ self.addCleanup(mock.patch.stopall)
+ self.mod_agent = importutils.import_module(AGENTMOD)
+
+
+class TestRestProxyAgentOVS(BaseAgentTestCase):
+ def setUp(self):
+ super(TestRestProxyAgentOVS, self).setUp()
+ self.plapi = mock.patch(PLUGINAPI).start()
+ self.ovsbridge = mock.patch(OVSBRIDGE).start()
+ self.context = mock.patch(CONTEXT).start()
+ self.rpc = mock.patch(CONSUMERCREATE).start()
+ self.sg_rpc = mock.patch(SGRPC).start()
+ self.sg_agent = mock.patch(SGAGENT).start()
+
+ def mock_agent(self):
+ mock_context = mock.Mock(return_value='abc')
+ self.context.get_admin_context_without_session = mock_context
+ return self.mod_agent.RestProxyAgent('int-br', 2, 'helper')
+
+ def mock_port_update(self, **kwargs):
+ agent = self.mock_agent()
+ agent.port_update(mock.Mock(), **kwargs)
+
+ def test_port_update(self):
+ port = {'id': 1, 'security_groups': 'default'}
+
+ with mock.patch.object(self.ovsbridge.return_value,
+ 'get_vif_port_by_id',
+ return_value=1) as get_vif:
+ self.mock_port_update(port=port)
+
+ get_vif.assert_called_once_with(1)
+ self.sg_agent.assert_has_calls([
+ mock.call().refresh_firewall()
+ ])
+
+ def test_port_update_not_vifport(self):
+ port = {'id': 1, 'security_groups': 'default'}
+
+ with mock.patch.object(self.ovsbridge.return_value,
+ 'get_vif_port_by_id',
+ return_value=0) as get_vif:
+ self.mock_port_update(port=port)
+
+ get_vif.assert_called_once_with(1)
+ self.assertFalse(self.sg_agent.return_value.refresh_firewall.called)
+
+ def test_port_update_without_secgroup(self):
+ port = {'id': 1}
+
+ with mock.patch.object(self.ovsbridge.return_value,
+ 'get_vif_port_by_id',
+ return_value=1) as get_vif:
+ self.mock_port_update(port=port)
+
+ get_vif.assert_called_once_with(1)
+ self.assertFalse(self.sg_agent.return_value.refresh_firewall.called)
+
+ def mock_update_ports(self, vif_port_set=None, registered_ports=None):
+ with mock.patch.object(self.ovsbridge.return_value,
+ 'get_vif_port_set',
+ return_value=vif_port_set):
+ agent = self.mock_agent()
+ return agent._update_ports(registered_ports)
+
+ def test_update_ports_unchanged(self):
+ self.assertIsNone(self.mock_update_ports())
+
+ def test_update_ports_changed(self):
+ vif_port_set = set([1, 3])
+ registered_ports = set([1, 2])
+ expected = dict(current=vif_port_set,
+ added=set([3]),
+ removed=set([2]))
+
+ actual = self.mock_update_ports(vif_port_set, registered_ports)
+
+ self.assertEqual(expected, actual)
+
+ def mock_process_devices_filter(self, port_info):
+ agent = self.mock_agent()
+ agent._process_devices_filter(port_info)
+
+ def test_process_devices_filter_add(self):
+ port_info = {'added': 1}
+
+ self.mock_process_devices_filter(port_info)
+
+ self.sg_agent.assert_has_calls([
+ mock.call().prepare_devices_filter(1)
+ ])
+
+ def test_process_devices_filter_remove(self):
+ port_info = {'removed': 2}
+
+ self.mock_process_devices_filter(port_info)
+
+ self.sg_agent.assert_has_calls([
+ mock.call().remove_devices_filter(2)
+ ])
+
+ def test_process_devices_filter_both(self):
+ port_info = {'added': 1, 'removed': 2}
+
+ self.mock_process_devices_filter(port_info)
+
+ self.sg_agent.assert_has_calls([
+ mock.call().prepare_devices_filter(1),
+ mock.call().remove_devices_filter(2)
+ ])
+
+ def test_process_devices_filter_none(self):
+ port_info = {}
+
+ self.mock_process_devices_filter(port_info)
+
+ self.assertFalse(
+ self.sg_agent.return_value.prepare_devices_filter.called)
+ self.assertFalse(
+ self.sg_agent.return_value.remove_devices_filter.called)
+
+
+class TestRestProxyAgent(BaseAgentTestCase):
+ def mock_main(self):
+ cfg_attrs = {'CONF.RESTPROXYAGENT.integration_bridge': 'integ_br',
+ 'CONF.RESTPROXYAGENT.polling_interval': 5,
+ 'CONF.RESTPROXYAGENT.virtual_switch_type': 'ovs',
+ 'CONF.AGENT.root_helper': 'helper'}
+ with nested(
+ mock.patch(AGENTMOD + '.cfg', **cfg_attrs),
+ mock.patch(NEUTRONCFG),
+ mock.patch(PLCONFIG),
+ ) as (mock_conf, mock_log_conf, mock_pluginconf):
+ self.mod_agent.main()
+
+ mock_log_conf.assert_has_calls([
+ mock.call(mock_conf),
+ ])
+
+ def test_main(self):
+ agent_attrs = {'daemon_loop.side_effect': SystemExit(0)}
+ with mock.patch(AGENTMOD + '.RestProxyAgent',
+ **agent_attrs) as mock_agent:
+ self.assertRaises(SystemExit, self.mock_main)
+
+ mock_agent.assert_has_calls([
+ mock.call('integ_br', 5, 'helper', 'ovs'),
+ mock.call().daemon_loop()
+ ])
--- /dev/null
+# Copyright 2014, Big Switch Networks
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from neutron import manager
+from neutron.tests.unit.bigswitch import test_base
+from neutron.tests.unit import test_extension_security_group as test_sg
+from neutron.tests.unit import test_security_groups_rpc as test_sg_rpc
+
+
+class RestProxySecurityGroupsTestCase(test_sg.SecurityGroupDBTestCase,
+ test_base.BigSwitchTestBase):
+ plugin_str = ('%s.NeutronRestProxyV2' %
+ test_base.RESTPROXY_PKG_PATH)
+
+ def setUp(self, plugin=None):
+ test_sg_rpc.set_firewall_driver(test_sg_rpc.FIREWALL_HYBRID_DRIVER)
+ self.setup_config_files()
+ self.setup_patches()
+ self._attribute_map_bk_ = {}
+ super(RestProxySecurityGroupsTestCase, self).setUp(self.plugin_str)
+ plugin = manager.NeutronManager.get_plugin()
+ self.notifier = plugin.notifier
+ self.rpc = plugin.callbacks
+
+
+class TestSecServerRpcCallBack(test_sg_rpc.SGServerRpcCallBackMixinTestCase,
+ RestProxySecurityGroupsTestCase):
+ pass
+
+
+class TestSecurityGroupsMixin(test_sg.TestSecurityGroups,
+ test_sg_rpc.SGNotificationTestMixin,
+ RestProxySecurityGroupsTestCase):
+ pass
neutron-nsx-manage = neutron.plugins.nicira.shell:main
neutron-openvswitch-agent = neutron.plugins.openvswitch.agent.ovs_neutron_agent:main
neutron-ovs-cleanup = neutron.agent.ovs_cleanup_util:main
+ neutron-restproxy-agent = neutron.plugins.bigswitch.agent.restproxy_agent:main
neutron-ryu-agent = neutron.plugins.ryu.agent.ryu_neutron_agent:main
neutron-server = neutron.server:main
neutron-rootwrap = oslo.rootwrap.cmd:main