]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
add db to save host for port
authorgongysh <gongysh@linux.vnet.ibm.com>
Thu, 21 Mar 2013 06:34:19 +0000 (14:34 +0800)
committerGerrit Code Review <review@openstack.org>
Mon, 13 May 2013 13:04:00 +0000 (13:04 +0000)
blueprint portbinding-ex-db

related patch in nova:
https://review.openstack.org/#/c/21141/

Only OVS and linux bridge plugins now support this feature.

Change-Id: I42d9bc59130e2758dd6a221d8953d63ec10e1f3c

etc/policy.json
quantum/db/migration/alembic_migrations/versions/176a85fc7d79_add_portbindings_db.py [new file with mode: 0644]
quantum/db/portbindings_db.py [new file with mode: 0644]
quantum/extensions/portbindings.py
quantum/plugins/linuxbridge/lb_quantum_plugin.py
quantum/plugins/openvswitch/ovs_quantum_plugin.py
quantum/tests/unit/_test_extension_portbindings.py
quantum/tests/unit/linuxbridge/test_linuxbridge_plugin.py
quantum/tests/unit/openvswitch/test_openvswitch_plugin.py

index 42625665a9b92ba4721f548b3b80745b4d932c8f..121f96c4118ec480d8a26bbe418f68a0e088ed3e 100644 (file)
 
     "extension:port_binding:view": "rule:admin_only",
     "extension:port_binding:set": "rule:admin_only",
+    "get_port:binding:host_id": "rule:admin_only",
+    "get_port:binding:vif_type": "rule:admin_only",
+    "get_port:binding:profile": "rule:admin_only",
+    "get_port:binding:capabilities": "rule:admin_only",
+    "create_port:binding:host_id": "rule:admin_only",
+    "update_port:binding:host_id": "rule:admin_only",
 
     "subnets:private:read": "rule:admin_or_owner",
     "subnets:private:write": "rule:admin_or_owner",
diff --git a/quantum/db/migration/alembic_migrations/versions/176a85fc7d79_add_portbindings_db.py b/quantum/db/migration/alembic_migrations/versions/176a85fc7d79_add_portbindings_db.py
new file mode 100644 (file)
index 0000000..0cda7b9
--- /dev/null
@@ -0,0 +1,59 @@
+# 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.
+#
+
+"""Add portbindings db
+
+Revision ID: 176a85fc7d79
+Revises: grizzly
+Create Date: 2013-03-21 14:59:53.052600
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '176a85fc7d79'
+down_revision = 'grizzly'
+
+# Change to ['*'] if this migration applies to all plugins
+
+migration_for_plugins = [
+    'quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2',
+    'quantum.plugins.linuxbridge.lb_quantum_plugin.LinuxBridgePluginV2',
+]
+
+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
+
+    op.create_table(
+        'portbindingports',
+        sa.Column('port_id', sa.String(length=36), nullable=False),
+        sa.Column('host', sa.String(length=255), nullable=False),
+        sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'),
+        sa.PrimaryKeyConstraint('port_id')
+    )
+
+
+def downgrade(active_plugin=None, options=None):
+    if not migration.should_run(active_plugin, migration_for_plugins):
+        return
+    op.drop_table('portbindingports')
diff --git a/quantum/db/portbindings_db.py b/quantum/db/portbindings_db.py
new file mode 100644 (file)
index 0000000..d93b281
--- /dev/null
@@ -0,0 +1,124 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp.
+# 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: Yong Sheng Gong, IBM, Corp.
+
+import sqlalchemy as sa
+from sqlalchemy import orm
+
+from quantum.api.v2 import attributes
+from quantum.db import db_base_plugin_v2
+from quantum.db import model_base
+from quantum.db import models_v2
+from quantum.extensions import portbindings
+from quantum.openstack.common import log as logging
+from quantum import policy
+
+
+LOG = logging.getLogger(__name__)
+
+
+class PortBindingPort(model_base.BASEV2):
+    port_id = sa.Column(sa.String(36),
+                        sa.ForeignKey('ports.id', ondelete="CASCADE"),
+                        primary_key=True)
+    host = sa.Column(sa.String(255), nullable=False)
+    port = orm.relationship(
+        models_v2.Port,
+        backref=orm.backref("portbinding",
+                            lazy='joined', uselist=False,
+                            cascade='delete'))
+
+
+class PortBindingMixin(object):
+    extra_binding_dict = None
+
+    def _port_model_hook(self, context, original_model, query):
+        query = query.outerjoin(PortBindingPort,
+                                (original_model.id ==
+                                 PortBindingPort.port_id))
+        return query
+
+    def _port_result_filter_hook(self, query, filters):
+        values = filters and filters.get(portbindings.HOST_ID, [])
+        if not values:
+            return query
+        if len(values) == 1:
+            query = query.filter(PortBindingPort.host == values[0])
+        else:
+            query = query.filter(PortBindingPort.host.in_(values))
+        return query
+
+    db_base_plugin_v2.QuantumDbPluginV2.register_model_query_hook(
+        models_v2.Port,
+        "portbindings_port",
+        _port_model_hook,
+        None,
+        _port_result_filter_hook)
+
+    def _check_portbindings_view_auth(self, context, port):
+        #TODO(salv-orlando): Remove this as part of bp/make-authz-orthogonal
+        keys_to_delete = []
+        for key in port:
+            if key.startswith('binding'):
+                policy_rule = "get_port:%s" % key
+                if not policy.check(context, policy_rule, port):
+                    keys_to_delete.append(key)
+        for key in keys_to_delete:
+            del port[key]
+        return port
+
+    def _process_portbindings_create_and_update(self, context, port_data,
+                                                port):
+        host = port_data.get(portbindings.HOST_ID)
+        host_set = attributes.is_attr_set(host)
+        if not host_set:
+            _extend_port_dict_binding_host(self, port, None)
+            return
+        with context.session.begin(subtransactions=True):
+            bind_port = context.session.query(
+                PortBindingPort).filter_by(port_id=port['id']).first()
+            if not bind_port:
+                context.session.add(PortBindingPort(port_id=port['id'],
+                                                    host=host))
+            else:
+                bind_port.host = host
+        _extend_port_dict_binding_host(self, port, host)
+
+    def get_port_host(self, context, port_id):
+        with context.session.begin(subtransactions=True):
+            bind_port = context.session.query(
+                PortBindingPort).filter_by(port_id=port_id).first()
+            return bind_port and bind_port.host or None
+
+
+def _extend_port_dict_binding_host(plugin, port_res, host):
+    port_res[portbindings.HOST_ID] = host
+    if plugin.extra_binding_dict:
+        port_res.update(plugin.extra_binding_dict)
+    return port_res
+
+
+def _extend_port_dict_binding(plugin, port_res, port_db):
+    if not isinstance(plugin, PortBindingMixin):
+        return
+    host = (port_db.portbinding and port_db.portbinding.host or None)
+    return _extend_port_dict_binding_host(
+        plugin, port_res, host)
+
+    # Register dict extend functions for ports
+db_base_plugin_v2.QuantumDbPluginV2.register_dict_extend_funcs(
+    attributes.PORTS, [_extend_port_dict_binding])
index 403c7532a1fc63e47dcc51a7b353a5c0a627c4ed..0bc3b65e3812630a13bd6e942da5f64bbecb5218 100644 (file)
@@ -49,7 +49,8 @@ EXTENDED_ATTRIBUTES_2_0 = {
                    'is_visible': True},
         HOST_ID: {'allow_post': True, 'allow_put': True,
                   'default': attributes.ATTR_NOT_SPECIFIED,
-                  'is_visible': True},
+                  'is_visible': True,
+                  'enforce_policy': True},
         PROFILE: {'allow_post': True, 'allow_put': True,
                   'default': attributes.ATTR_NOT_SPECIFIED,
                   'validate': {'type:dict': None},
index 4a1716418f9abc6ad05da76edad18773cdc36bf5..57a83c5efd2542ba42afa328a4b7add6427765f1 100644 (file)
@@ -32,6 +32,7 @@ from quantum.db import db_base_plugin_v2
 from quantum.db import dhcp_rpc_base
 from quantum.db import extraroute_db
 from quantum.db import l3_rpc_base
+from quantum.db import portbindings_db
 from quantum.db import quota_db  # noqa
 from quantum.db import securitygroups_rpc_base as sg_db_rpc
 from quantum.extensions import portbindings
@@ -176,7 +177,8 @@ class AgentNotifierApi(proxy.RpcProxy,
 class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                           extraroute_db.ExtraRoute_db_mixin,
                           sg_db_rpc.SecurityGroupServerRpcMixin,
-                          agentschedulers_db.AgentSchedulerDbMixin):
+                          agentschedulers_db.AgentSchedulerDbMixin,
+                          portbindings_db.PortBindingMixin):
     """Implement the Quantum abstractions using Linux bridging.
 
     A new VLAN is created for each network.  An agent is relied upon
@@ -214,10 +216,13 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
 
     network_view = "extension:provider_network:view"
     network_set = "extension:provider_network:set"
-    binding_view = "extension:port_binding:view"
-    binding_set = "extension:port_binding:set"
 
     def __init__(self):
+        self.extra_binding_dict = {
+            portbindings.VIF_TYPE: portbindings.VIF_TYPE_BRIDGE,
+            portbindings.CAPABILITIES: {
+                portbindings.CAP_PORT_FILTER:
+                'security-group' in self.supported_extension_aliases}}
         db.initialize()
         self._parse_network_vlan_ranges()
         db.sync_network_states(self.network_vlan_ranges)
@@ -441,21 +446,12 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
 
         return [self._fields(net, fields) for net in nets]
 
-    def _extend_port_dict_binding(self, context, port):
-        if self._check_view_auth(context, port, self.binding_view):
-            port[portbindings.VIF_TYPE] = portbindings.VIF_TYPE_BRIDGE
-            port[portbindings.CAPABILITIES] = {
-                portbindings.CAP_PORT_FILTER:
-                'security-group' in self.supported_extension_aliases}
-        return port
-
     def get_port(self, context, id, fields=None):
         with context.session.begin(subtransactions=True):
             port = super(LinuxBridgePluginV2, self).get_port(context,
                                                              id,
                                                              fields)
-        self._extend_port_dict_binding(context, port),
-        return self._fields(port, fields)
+        return self._check_portbindings_view_auth(context, port)
 
     def get_ports(self, context, filters=None, fields=None,
                   sorts=None, limit=None, marker=None, page_reverse=False):
@@ -464,14 +460,14 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
             ports = super(LinuxBridgePluginV2,
                           self).get_ports(context, filters, fields, sorts,
                                           limit, marker, page_reverse)
-            #TODO(nati) filter by security group
             for port in ports:
-                self._extend_port_dict_binding(context, port)
-                res_ports.append(self._fields(port, fields))
+                self._check_portbindings_view_auth(context, port)
+                res_ports.append(port)
         return res_ports
 
     def create_port(self, context, port):
         session = context.session
+        port_data = port['port']
         with session.begin(subtransactions=True):
             self._ensure_default_security_group_on_port(context, port)
             sgids = self._get_security_groups_on_port(context, port)
@@ -480,10 +476,13 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
 
             port = super(LinuxBridgePluginV2,
                          self).create_port(context, port)
+            self._process_portbindings_create_and_update(context,
+                                                         port_data,
+                                                         port)
             self._process_port_create_security_group(
                 context, port, sgids)
         self.notify_security_groups_member_updated(context, port)
-        return self._extend_port_dict_binding(context, port)
+        return self._check_portbindings_view_auth(context, port)
 
     def update_port(self, context, id, port):
         original_port = self.get_port(context, id)
@@ -493,6 +492,9 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         with session.begin(subtransactions=True):
             updated_port = super(LinuxBridgePluginV2, self).update_port(
                 context, id, port)
+            self._process_portbindings_create_and_update(context,
+                                                         port['port'],
+                                                         updated_port)
             need_port_update_notify = self.update_security_group_on_port(
                 context, id, port, original_port, updated_port)
 
@@ -504,7 +506,7 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
 
         if need_port_update_notify:
             self._notify_port_updated(context, updated_port)
-        return self._extend_port_dict_binding(context, updated_port)
+        return self._check_portbindings_view_auth(context, updated_port)
 
     def delete_port(self, context, id, l3_port_check=True):
 
index f896b69ce37ea4f88d5f7d6cc881a0ce9f19a773..e2bb77f1f42e4abfe372ee30133a58df75cc8bec 100644 (file)
@@ -38,6 +38,7 @@ from quantum.db import db_base_plugin_v2
 from quantum.db import dhcp_rpc_base
 from quantum.db import extraroute_db
 from quantum.db import l3_rpc_base
+from quantum.db import portbindings_db
 from quantum.db import quota_db  # noqa
 from quantum.db import securitygroups_rpc_base as sg_db_rpc
 from quantum.extensions import portbindings
@@ -214,7 +215,8 @@ class AgentNotifierApi(proxy.RpcProxy,
 class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                          extraroute_db.ExtraRoute_db_mixin,
                          sg_db_rpc.SecurityGroupServerRpcMixin,
-                         agentschedulers_db.AgentSchedulerDbMixin):
+                         agentschedulers_db.AgentSchedulerDbMixin,
+                         portbindings_db.PortBindingMixin):
 
     """Implement the Quantum abstractions using Open vSwitch.
 
@@ -254,10 +256,13 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
 
     network_view = "extension:provider_network:view"
     network_set = "extension:provider_network:set"
-    binding_view = "extension:port_binding:view"
-    binding_set = "extension:port_binding:set"
 
     def __init__(self, configfile=None):
+        self.extra_binding_dict = {
+            portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS,
+            portbindings.CAPABILITIES: {
+                portbindings.CAP_PORT_FILTER:
+                'security-group' in self.supported_extension_aliases}}
         ovs_db_v2.initialize()
         self._parse_network_vlan_ranges()
         ovs_db_v2.sync_vlan_allocations(self.network_vlan_ranges)
@@ -530,44 +535,39 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
 
         return [self._fields(net, fields) for net in nets]
 
-    def _extend_port_dict_binding(self, context, port):
-        if self._check_view_auth(context, port, self.binding_view):
-            port[portbindings.VIF_TYPE] = portbindings.VIF_TYPE_OVS
-            port[portbindings.CAPABILITIES] = {
-                portbindings.CAP_PORT_FILTER:
-                'security-group' in self.supported_extension_aliases}
-        return port
-
     def create_port(self, context, port):
         # Set port status as 'DOWN'. This will be updated by agent
         port['port']['status'] = q_const.PORT_STATUS_DOWN
+        port_data = port['port']
         session = context.session
         with session.begin(subtransactions=True):
             self._ensure_default_security_group_on_port(context, port)
             sgids = self._get_security_groups_on_port(context, port)
             port = super(OVSQuantumPluginV2, self).create_port(context, port)
+            self._process_portbindings_create_and_update(context,
+                                                         port_data, port)
             self._process_port_create_security_group(context, port, sgids)
         self.notify_security_groups_member_updated(context, port)
-        return self._extend_port_dict_binding(context, port)
+        return self._check_portbindings_view_auth(context, port)
 
     def get_port(self, context, id, fields=None):
         with context.session.begin(subtransactions=True):
             port = super(OVSQuantumPluginV2, self).get_port(context,
-                                                            id, fields)
-            self._extend_port_dict_binding(context, port)
-        return self._fields(port, fields)
+                                                            id,
+                                                            fields)
+        return self._check_portbindings_view_auth(context, port)
 
     def get_ports(self, context, filters=None, fields=None,
-                  sorts=None, limit=None, marker=None,
-                  page_reverse=False):
+                  sorts=None, limit=None, marker=None, page_reverse=False):
+        res_ports = []
         with context.session.begin(subtransactions=True):
-            ports = super(OVSQuantumPluginV2, self).get_ports(
-                context, filters, fields, sorts, limit, marker,
-                page_reverse)
-            #TODO(nati) filter by security group
+            ports = super(OVSQuantumPluginV2,
+                          self).get_ports(context, filters, fields, sorts,
+                                          limit, marker, page_reverse)
             for port in ports:
-                self._extend_port_dict_binding(context, port)
-        return [self._fields(port, fields) for port in ports]
+                self._check_portbindings_view_auth(context, port)
+                res_ports.append(port)
+        return res_ports
 
     def update_port(self, context, id, port):
         session = context.session
@@ -579,6 +579,9 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                 context, id, port)
             need_port_update_notify = self.update_security_group_on_port(
                 context, id, port, original_port, updated_port)
+            self._process_portbindings_create_and_update(context,
+                                                         port['port'],
+                                                         updated_port)
         need_port_update_notify |= self.is_security_group_member_updated(
             context, original_port, updated_port)
         if original_port['admin_state_up'] != updated_port['admin_state_up']:
@@ -591,7 +594,7 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                                       binding.network_type,
                                       binding.segmentation_id,
                                       binding.physical_network)
-        return self._extend_port_dict_binding(context, updated_port)
+        return self._check_portbindings_view_auth(context, updated_port)
 
     def delete_port(self, context, id, l3_port_check=True):
 
index 15ed8ab6cbf5bcf241f3347878a2afde35b1ed57..57a1afd9008a00a0117ce2356ae57f44a949f511 100644 (file)
@@ -21,6 +21,7 @@
 import contextlib
 
 from oslo.config import cfg
+from webob import exc
 
 from quantum import context
 from quantum.extensions import portbindings
@@ -48,21 +49,21 @@ class PortBindingsTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
         self.assertFalse(portbindings.CAPABILITIES in port)
 
     def test_port_vif_details(self):
-        plugin = QuantumManager.get_plugin()
         with self.port(name='name') as port:
             port_id = port['port']['id']
             # Check a response of create_port
             self._check_response_portbindings(port['port'])
             # Check a response of get_port
             ctx = context.get_admin_context()
-            port = plugin.get_port(ctx, port_id)
+            port = self._show('ports', port_id, quantum_context=ctx)['port']
             self._check_response_portbindings(port)
             # By default user is admin - now test non admin user
             ctx = context.Context(user_id=None,
                                   tenant_id=self._tenant_id,
                                   is_admin=False,
                                   read_deleted="no")
-            non_admin_port = plugin.get_port(ctx, port_id)
+            non_admin_port = self._show(
+                'ports', port_id, quantum_context=ctx)['port']
             self._check_response_no_portbindings(non_admin_port)
 
     def test_ports_vif_details(self):
@@ -79,7 +80,115 @@ class PortBindingsTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
                                   tenant_id=self._tenant_id,
                                   is_admin=False,
                                   read_deleted="no")
-            ports = plugin.get_ports(ctx)
+            ports = self._list('ports', quantum_context=ctx)['ports']
             self.assertEqual(len(ports), 2)
             for non_admin_port in ports:
                 self._check_response_no_portbindings(non_admin_port)
+
+
+class PortBindingsHostTestCaseMixin(object):
+    fmt = 'json'
+    hostname = 'testhost'
+
+    def _check_response_portbindings_host(self, port):
+        self.assertEqual(port[portbindings.HOST_ID], self.hostname)
+
+    def _check_response_no_portbindings_host(self, port):
+        self.assertIn('status', port)
+        self.assertNotIn(portbindings.HOST_ID, port)
+
+    def test_port_vif_non_admin(self):
+        with self.network(set_context=True,
+                          tenant_id='test') as net1:
+            with self.subnet(network=net1) as subnet1:
+                host_arg = {portbindings.HOST_ID: self.hostname}
+                try:
+                    with self.port(subnet=subnet1,
+                                   expected_res_status=403,
+                                   arg_list=(portbindings.HOST_ID,),
+                                   set_context=True,
+                                   tenant_id='test',
+                                   **host_arg):
+                        pass
+                except exc.HTTPClientError:
+                    pass
+
+    def test_port_vif_host(self):
+        host_arg = {portbindings.HOST_ID: self.hostname}
+        with self.port(name='name', arg_list=(portbindings.HOST_ID,),
+                       **host_arg) as port:
+            port_id = port['port']['id']
+            # Check a response of create_port
+            self._check_response_portbindings_host(port['port'])
+            # Check a response of get_port
+            ctx = context.get_admin_context()
+            port = self._show('ports', port_id, quantum_context=ctx)['port']
+            self._check_response_portbindings_host(port)
+            # By default user is admin - now test non admin user
+            ctx = context.Context(user_id=None,
+                                  tenant_id=self._tenant_id,
+                                  is_admin=False,
+                                  read_deleted="no")
+            non_admin_port = self._show(
+                'ports', port_id, quantum_context=ctx)['port']
+            self._check_response_no_portbindings_host(non_admin_port)
+
+    def test_ports_vif_host(self):
+        cfg.CONF.set_default('allow_overlapping_ips', True)
+        host_arg = {portbindings.HOST_ID: self.hostname}
+        with contextlib.nested(
+            self.port(name='name1',
+                      arg_list=(portbindings.HOST_ID,),
+                      **host_arg),
+            self.port(name='name2')):
+            ctx = context.get_admin_context()
+            ports = self._list('ports', quantum_context=ctx)['ports']
+            self.assertEqual(2, len(ports))
+            for port in ports:
+                if port['name'] == 'name1':
+                    self._check_response_portbindings_host(port)
+                else:
+                    self.assertFalse(port[portbindings.HOST_ID])
+            # By default user is admin - now test non admin user
+            ctx = context.Context(user_id=None,
+                                  tenant_id=self._tenant_id,
+                                  is_admin=False,
+                                  read_deleted="no")
+            ports = self._list('ports', quantum_context=ctx)['ports']
+            self.assertEqual(2, len(ports))
+            for non_admin_port in ports:
+                self._check_response_no_portbindings_host(non_admin_port)
+
+    def test_ports_vif_host_update(self):
+        cfg.CONF.set_default('allow_overlapping_ips', True)
+        host_arg = {portbindings.HOST_ID: self.hostname}
+        with contextlib.nested(
+            self.port(name='name1',
+                      arg_list=(portbindings.HOST_ID,),
+                      **host_arg),
+            self.port(name='name2')) as (port1, port2):
+            data = {'port': {portbindings.HOST_ID: 'testhosttemp'}}
+            req = self.new_update_request('ports', data, port1['port']['id'])
+            req.get_response(self.api)
+            req = self.new_update_request('ports', data, port2['port']['id'])
+            ctx = context.get_admin_context()
+            req.get_response(self.api)
+            ports = self._list('ports', quantum_context=ctx)['ports']
+        self.assertEqual(2, len(ports))
+        for port in ports:
+            self.assertEqual('testhosttemp', port[portbindings.HOST_ID])
+
+    def test_ports_vif_host_list(self):
+        cfg.CONF.set_default('allow_overlapping_ips', True)
+        host_arg = {portbindings.HOST_ID: self.hostname}
+        with contextlib.nested(
+            self.port(name='name1',
+                      arg_list=(portbindings.HOST_ID,),
+                      **host_arg),
+            self.port(name='name2'),
+            self.port(name='name3',
+                      arg_list=(portbindings.HOST_ID,),
+                      **host_arg),) as (port1, _port2, port3):
+            self._test_list_resources(
+                'port', (port1, port3),
+                query_params='%s=%s' % (portbindings.HOST_ID, self.hostname))
index da36a86a7cf4a431e57947ec7474483042b22c1a..4e581949f3917c5a2cb518930f0625af4d98c5d4 100644 (file)
@@ -69,3 +69,9 @@ class TestLinuxBridgePortBinding(LinuxBridgePluginV2TestCase,
 class TestLinuxBridgePortBindingNoSG(TestLinuxBridgePortBinding):
     HAS_PORT_FILTER = False
     FIREWALL_DRIVER = test_sg_rpc.FIREWALL_NOOP_DRIVER
+
+
+class TestOpenvswitchPortBindingHost(
+    LinuxBridgePluginV2TestCase,
+    test_bindings.PortBindingsHostTestCaseMixin):
+    pass
index 08bb85adb43cc073fde70183ca8a575b9d5b4008..a23925ee60beec12efc583dd24aca0d6c36f3263 100644 (file)
@@ -67,3 +67,9 @@ class TestOpenvswitchPortBinding(OpenvswitchPluginV2TestCase,
 class TestOpenvswitchPortBindingNoSG(TestOpenvswitchPortBinding):
     HAS_PORT_FILTER = False
     FIREWALL_DRIVER = test_sg_rpc.FIREWALL_NOOP_DRIVER
+
+
+class TestOpenvswitchPortBindingHost(
+    OpenvswitchPluginV2TestCase,
+    test_bindings.PortBindingsHostTestCaseMixin):
+    pass