diff --git a/etc/quantum/plugins/bigswitch/restproxy.ini b/etc/quantum/plugins/bigswitch/restproxy.ini index 04168b17c0..265ee8f3f8 100644 --- a/etc/quantum/plugins/bigswitch/restproxy.ini +++ b/etc/quantum/plugins/bigswitch/restproxy.ini @@ -22,6 +22,14 @@ servers=localhost:8080 # default: ovs # vif_type = ovs +# Overrides for vif types based on nova compute node host IDs +# Comma separated list of host IDs to fix to a specific VIF type +# The VIF type is taken from the end of the configuration item +# node_override_vif_ +# For example, the following would set the VIF type to IVS for +# host-id1 and host-id2 +# node_overrride_vif_ivs=host-id1,host-id2 + [router] # Specify the default router rules installed in newly created tenant routers # Specify multiple times for multiple rules diff --git a/quantum/db/migration/alembic_migrations/versions/3cabb850f4a5_table_to_track_port_.py b/quantum/db/migration/alembic_migrations/versions/3cabb850f4a5_table_to_track_port_.py new file mode 100644 index 0000000000..744761d0b0 --- /dev/null +++ b/quantum/db/migration/alembic_migrations/versions/3cabb850f4a5_table_to_track_port_.py @@ -0,0 +1,63 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2013 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. +# + +"""Table to track port to host associations + +Revision ID: 3cabb850f4a5 +Revises: 5918cbddab04 +Create Date: 2013-06-24 14:30:33.533562 + +""" + +# revision identifiers, used by Alembic. +revision = '3cabb850f4a5' +down_revision = '5918cbddab04' + +# Change to ['*'] if this migration applies to all plugins + +migration_for_plugins = [ + 'quantum.plugins.bigswitch.plugin.QuantumRestProxyV2' +] + +from alembic import op +import sqlalchemy as sa + + +from quantum.db import migration + + +def upgrade(active_plugin=None, options=None): + if not migration.should_run(active_plugin, migration_for_plugins): + return + + ### commands auto generated by Alembic - please adjust! ### + op.create_table('portlocations', + sa.Column('port_id', sa.String(length=255), + primary_key=True, nullable=False), + sa.Column('host_id', + sa.String(length=255), nullable=False) + ) + ### end Alembic commands ### + + +def downgrade(active_plugin=None, options=None): + if not migration.should_run(active_plugin, migration_for_plugins): + return + + ### commands auto generated by Alembic - please adjust! ### + op.drop_table('portlocations') + ### end Alembic commands ### diff --git a/quantum/extensions/portbindings.py b/quantum/extensions/portbindings.py index f99c29dfe5..087dea866d 100644 --- a/quantum/extensions/portbindings.py +++ b/quantum/extensions/portbindings.py @@ -44,6 +44,10 @@ VIF_TYPE_802_QBG = '802.1qbg' VIF_TYPE_802_QBH = '802.1qbh' VIF_TYPE_HYPERV = 'hyperv' VIF_TYPE_OTHER = 'other' +VIF_TYPES = [VIF_TYPE_UNBOUND, VIF_TYPE_BINDING_FAILED, VIF_TYPE_OVS, + VIF_TYPE_IVS, VIF_TYPE_BRIDGE, VIF_TYPE_802_QBG, + VIF_TYPE_802_QBH, VIF_TYPE_HYPERV, VIF_TYPE_OTHER] + EXTENDED_ATTRIBUTES_2_0 = { 'ports': { diff --git a/quantum/plugins/bigswitch/db/__init__.py b/quantum/plugins/bigswitch/db/__init__.py new file mode 100644 index 0000000000..c05daecf89 --- /dev/null +++ b/quantum/plugins/bigswitch/db/__init__.py @@ -0,0 +1,18 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 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, Inc. diff --git a/quantum/plugins/bigswitch/db/porttracker_db.py b/quantum/plugins/bigswitch/db/porttracker_db.py new file mode 100644 index 0000000000..c286d637cc --- /dev/null +++ b/quantum/plugins/bigswitch/db/porttracker_db.py @@ -0,0 +1,46 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2013, 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. + +import sqlalchemy as sa + +from quantum.db import model_base +from quantum.openstack.common import log as logging + +LOG = logging.getLogger(__name__) + + +class PortLocation(model_base.BASEV2): + port_id = sa.Column(sa.String(255), primary_key=True) + host_id = sa.Column(sa.String(255), nullable=False) + + +def get_port_hostid(context, port_id): + with context.session.begin(subtransactions=True): + query = context.session.query(PortLocation) + res = query.filter_by(port_id=port_id).first() + if not res: + return False + return res.host_id + + +def put_port_hostid(context, port_id, host_id): + if port_id == '': + LOG.warning(_("Received an empty port ID for host '%s'"), host_id) + return + with context.session.begin(subtransactions=True): + location = PortLocation(port_id=port_id, host_id=host_id) + context.session.add(location) diff --git a/quantum/plugins/bigswitch/plugin.py b/quantum/plugins/bigswitch/plugin.py index a795ddb2f5..e2e9f83d9e 100644 --- a/quantum/plugins/bigswitch/plugin.py +++ b/quantum/plugins/bigswitch/plugin.py @@ -68,6 +68,7 @@ from quantum.extensions import l3 from quantum.extensions import portbindings from quantum.openstack.common import log as logging from quantum.openstack.common import rpc +from quantum.plugins.bigswitch.db import porttracker_db from quantum.plugins.bigswitch import routerrule_db from quantum.plugins.bigswitch.version import version_string_with_vcs @@ -126,6 +127,15 @@ nova_opts = [ "Nova compute nodes")), ] +# Each VIF Type can have a list of nova host IDs that are fixed to that type +for i in portbindings.VIF_TYPES: + opt = cfg.ListOpt('node_override_vif_' + i, default=[], + help=_("Nova compute nodes to manually set VIF " + "type to %s") % i) + nova_opts.append(opt) + +# Add the vif types for reference later +nova_opts.append(cfg.ListOpt('vif_types', default=portbindings.VIF_TYPES)) cfg.CONF.register_opts(nova_opts, "NOVA") @@ -569,6 +579,10 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2, # Update DB port["port"]["admin_state_up"] = False + if (portbindings.HOST_ID in port['port'] + and 'device_id' in port['port']): + porttracker_db.put_port_hostid(context, port['port']['device_id'], + port['port'][portbindings.HOST_ID]) new_port = super(QuantumRestProxyV2, self).create_port(context, port) net = super(QuantumRestProxyV2, self).get_network(context, new_port["network_id"]) @@ -661,7 +675,10 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2, # Update DB new_port = super(QuantumRestProxyV2, self).update_port(context, port_id, port) - + if (portbindings.HOST_ID in port['port'] + and 'device_id' in port['port']): + porttracker_db.put_port_hostid(context, port['port']['device_id'], + port['port'][portbindings.HOST_ID]) # update on networl ctrl try: resource = PORTS_PATH % (orig_port["tenant_id"], @@ -1335,10 +1352,21 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2, "[%s]. Defaulting to ovs. "), cfg_vif_type) cfg_vif_type = portbindings.VIF_TYPE_OVS - + hostid = porttracker_db.get_port_hostid(context, + port.get("device_id")) + if hostid: + override = self._check_hostvif_override(hostid) + if override: + cfg_vif_type = override port[portbindings.VIF_TYPE] = cfg_vif_type port[portbindings.CAPABILITIES] = { portbindings.CAP_PORT_FILTER: 'security-group' in self.supported_extension_aliases} return port + + def _check_hostvif_override(self, hostid): + for v in cfg.CONF.NOVA.vif_types: + if hostid in getattr(cfg.CONF.NOVA, "node_override_vif_" + v, []): + return v + return False diff --git a/quantum/tests/unit/bigswitch/etc/restproxy.ini.test b/quantum/tests/unit/bigswitch/etc/restproxy.ini.test index f19fceb8be..3c1c17f944 100644 --- a/quantum/tests/unit/bigswitch/etc/restproxy.ini.test +++ b/quantum/tests/unit/bigswitch/etc/restproxy.ini.test @@ -30,6 +30,9 @@ serverssl=False # options: ivs or ovs # default: ovs vif_type = ovs +# Overrides for vif types based on nova compute node host IDs +# Comma separated list of host IDs to fix to a specific VIF type +node_override_vif_ivs = ivshost [router] # Specify the default router rules installed in newly created tenant routers diff --git a/quantum/tests/unit/bigswitch/test_restproxy_plugin.py b/quantum/tests/unit/bigswitch/test_restproxy_plugin.py index b6174fb8c6..edbb2d5adf 100644 --- a/quantum/tests/unit/bigswitch/test_restproxy_plugin.py +++ b/quantum/tests/unit/bigswitch/test_restproxy_plugin.py @@ -19,6 +19,7 @@ import os from mock import patch from oslo.config import cfg +import webob.exc import quantum.common.test_lib as test_lib from quantum.extensions import portbindings @@ -106,6 +107,38 @@ class TestBigSwitchProxyPortsV2IVS(test_plugin.TestPortsV2, cfg.CONF.set_override('vif_type', 'ivs', 'NOVA') +class TestBigSwitchVIFOverride(test_plugin.TestPortsV2, + BigSwitchProxyPluginV2TestCase, + test_bindings.PortBindingsTestCase): + VIF_TYPE = portbindings.VIF_TYPE_OVS + HAS_PORT_FILTER = False + + def setUp(self): + super(TestBigSwitchVIFOverride, + self).setUp() + cfg.CONF.set_override('vif_type', 'ovs', 'NOVA') + + def test_port_vif_details(self): + kwargs = {'name': 'name', 'binding:host_id': 'ivshost', + 'device_id': 'override_dev'} + with self.port(**kwargs) as port: + self.assertEqual(port['port']['binding:vif_type'], + portbindings.VIF_TYPE_IVS) + kwargs = {'name': 'name2', 'binding:host_id': 'someotherhost', + 'device_id': 'other_dev'} + with self.port(**kwargs) as port: + self.assertEqual(port['port']['binding:vif_type'], self.VIF_TYPE) + + def _make_port(self, fmt, net_id, expected_res_status=None, **kwargs): + res = self._create_port(fmt, net_id, expected_res_status, + ('binding:host_id', ), **kwargs) + # Things can go wrong - raise HTTP exc with res code only + # so it can be caught by unit tests + if res.status_int >= 400: + raise webob.exc.HTTPClientError(code=res.status_int) + return self.deserialize(fmt, res) + + class TestBigSwitchProxyNetworksV2(test_plugin.TestNetworksV2, BigSwitchProxyPluginV2TestCase):