From d3dd33bc30656532a1860929ead5ee601a71099b Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Mon, 24 Jun 2013 14:44:10 -0700 Subject: [PATCH] Port location tracking for BigSwitch Plugin Adds a new table to the Big Switch plugin to keep track of the nova compute node host IDs that ports reside on. This table is then used to allow users to override the VIF type for a compute node based on the host ID. This allows quantum to control an environment with multiple VIF types. Change-Id: I63eb66d970650237aed2d5dc6676a6d097988f8d Implements: blueprint hostid-vif-override --- etc/quantum/plugins/bigswitch/restproxy.ini | 8 +++ .../3cabb850f4a5_table_to_track_port_.py | 63 +++++++++++++++++++ quantum/extensions/portbindings.py | 4 ++ quantum/plugins/bigswitch/db/__init__.py | 18 ++++++ .../plugins/bigswitch/db/porttracker_db.py | 46 ++++++++++++++ quantum/plugins/bigswitch/plugin.py | 32 +++++++++- .../unit/bigswitch/etc/restproxy.ini.test | 3 + .../unit/bigswitch/test_restproxy_plugin.py | 33 ++++++++++ 8 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 quantum/db/migration/alembic_migrations/versions/3cabb850f4a5_table_to_track_port_.py create mode 100644 quantum/plugins/bigswitch/db/__init__.py create mode 100644 quantum/plugins/bigswitch/db/porttracker_db.py diff --git a/etc/quantum/plugins/bigswitch/restproxy.ini b/etc/quantum/plugins/bigswitch/restproxy.ini index e69d5bb75..bfc5719b0 100644 --- a/etc/quantum/plugins/bigswitch/restproxy.ini +++ b/etc/quantum/plugins/bigswitch/restproxy.ini @@ -51,6 +51,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 000000000..744761d0b --- /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 f99c29dfe..087dea866 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 000000000..c05daecf8 --- /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 000000000..c286d637c --- /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 a795ddb2f..e2e9f83d9 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 a54a7b60d..0a2eedb7a 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 b6174fb8c..edbb2d5ad 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): -- 2.45.2