Skip to content

Commit

Permalink
add db to save host for port
Browse files Browse the repository at this point in the history
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
  • Loading branch information
gongysh authored and openstack-gerrit committed May 13, 2013
1 parent 8581676 commit 73900fd
Show file tree
Hide file tree
Showing 9 changed files with 363 additions and 47 deletions.
6 changes: 6 additions & 0 deletions etc/policy.json
Expand Up @@ -15,6 +15,12 @@

"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",
Expand Down
@@ -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')
124 changes: 124 additions & 0 deletions quantum/db/portbindings_db.py
@@ -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])
3 changes: 2 additions & 1 deletion quantum/extensions/portbindings.py
Expand Up @@ -49,7 +49,8 @@
'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},
Expand Down
38 changes: 20 additions & 18 deletions quantum/plugins/linuxbridge/lb_quantum_plugin.py
Expand Up @@ -32,6 +32,7 @@
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
Expand Down Expand Up @@ -176,7 +177,8 @@ def port_update(self, context, port, physical_network, vlan_id):
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
Expand Down Expand Up @@ -214,10 +216,13 @@ def supported_extension_aliases(self):

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)
Expand Down Expand Up @@ -441,21 +446,12 @@ def get_networks(self, context, filters=None, fields=None,

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):
Expand All @@ -464,14 +460,14 @@ def get_ports(self, context, filters=None, fields=None,
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)
Expand All @@ -480,10 +476,13 @@ def create_port(self, context, port):

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)
Expand All @@ -493,6 +492,9 @@ def update_port(self, context, id, port):
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)

Expand All @@ -504,7 +506,7 @@ def update_port(self, context, id, port):

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):

Expand Down

0 comments on commit 73900fd

Please sign in to comment.