0efce6195f
The following patch adds the concept of allowed address pairs. This allows one to add additional ip/mac address pairs on a port to allow traffic that matches those specified values. This is useful in order to leverage dataplane failover mechanisms like vrrp. This patch adds support for the NVP plugin, the OVS plugin, and Ml2. DocImpact implements blueprint: allowed-address-pairs Change-Id: Ie73b3886c5be8e1fc4ade86a0cfb854267f345ac
122 lines
5.2 KiB
Python
122 lines
5.2 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
#
|
|
# Copyright 2013 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: Aaron Rosen, Nicira, Inc
|
|
|
|
import sqlalchemy as sa
|
|
from sqlalchemy import orm
|
|
|
|
from neutron.api.v2 import attributes as attr
|
|
from neutron.db import db_base_plugin_v2
|
|
from neutron.db import model_base
|
|
from neutron.db import models_v2
|
|
from neutron.extensions import allowedaddresspairs as addr_pair
|
|
|
|
|
|
class AllowedAddressPair(model_base.BASEV2):
|
|
port_id = sa.Column(sa.String(36),
|
|
sa.ForeignKey('ports.id', ondelete="CASCADE"),
|
|
primary_key=True)
|
|
mac_address = sa.Column(sa.String(32), nullable=False, primary_key=True)
|
|
ip_address = sa.Column(sa.String(64), nullable=False, primary_key=True)
|
|
|
|
port = orm.relationship(
|
|
models_v2.Port,
|
|
backref=orm.backref("allowed_address_pairs",
|
|
lazy="joined", cascade="delete"))
|
|
|
|
|
|
class AllowedAddressPairsMixin(object):
|
|
"""Mixin class for allowed address pairs."""
|
|
|
|
def _process_create_allowed_address_pairs(self, context, port,
|
|
allowed_address_pairs):
|
|
if not attr.is_attr_set(allowed_address_pairs):
|
|
return []
|
|
with context.session.begin(subtransactions=True):
|
|
for address_pair in allowed_address_pairs:
|
|
# use port.mac_address if no mac address in address pair
|
|
if 'mac_address' not in address_pair:
|
|
address_pair['mac_address'] = port['mac_address']
|
|
for fixed_ip in port['fixed_ips']:
|
|
if ((fixed_ip['ip_address'] == address_pair['ip_address'])
|
|
and (port['mac_address'] ==
|
|
address_pair['mac_address'])):
|
|
#TODO(arosen) - need to query for address pairs
|
|
# to check for same condition if fixed_ips change to
|
|
# be an address pair.
|
|
raise addr_pair.AddressPairMatchesPortFixedIPAndMac()
|
|
db_pair = AllowedAddressPair(
|
|
port_id=port['id'],
|
|
mac_address=address_pair['mac_address'],
|
|
ip_address=address_pair['ip_address'])
|
|
context.session.add(db_pair)
|
|
|
|
return allowed_address_pairs
|
|
|
|
def get_allowed_address_pairs(self, context, port_id):
|
|
pairs = (context.session.query(AllowedAddressPair).
|
|
filter_by(port_id=port_id))
|
|
return [self._make_allowed_address_pairs_dict(pair)
|
|
for pair in pairs]
|
|
|
|
def _extend_port_dict_allowed_address_pairs(self, port_res, port_db):
|
|
# If port_db is provided, allowed address pairs will be accessed via
|
|
# sqlalchemy models. As they're loaded together with ports this
|
|
# will not cause an extra query.
|
|
allowed_address_pairs = [
|
|
self._make_allowed_address_pairs_dict(address_pair) for
|
|
address_pair in port_db.allowed_address_pairs]
|
|
port_res[addr_pair.ADDRESS_PAIRS] = allowed_address_pairs
|
|
return port_res
|
|
|
|
# Register dict extend functions for ports
|
|
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
|
|
attr.PORTS, ['_extend_port_dict_allowed_address_pairs'])
|
|
|
|
def _delete_allowed_address_pairs(self, context, id):
|
|
query = self._model_query(context, AllowedAddressPair)
|
|
with context.session.begin(subtransactions=True):
|
|
query.filter(AllowedAddressPair.port_id == id).delete()
|
|
|
|
def _make_allowed_address_pairs_dict(self, allowed_address_pairs,
|
|
fields=None):
|
|
res = {'mac_address': allowed_address_pairs['mac_address'],
|
|
'ip_address': allowed_address_pairs['ip_address']}
|
|
return self._fields(res, fields)
|
|
|
|
def _has_address_pairs(self, port):
|
|
return (attr.is_attr_set(port['port'][addr_pair.ADDRESS_PAIRS])
|
|
and port['port'][addr_pair.ADDRESS_PAIRS] != [])
|
|
|
|
def _check_update_has_allowed_address_pairs(self, port):
|
|
"""Determine if request has an allowed address pair.
|
|
|
|
Return True if the port parameter has a non-empty
|
|
'allowed_address_pairs' attribute. Otherwise returns False.
|
|
"""
|
|
return (addr_pair.ADDRESS_PAIRS in port['port'] and
|
|
self._has_address_pairs(port))
|
|
|
|
def _check_update_deletes_allowed_address_pairs(self, port):
|
|
"""Determine if request deletes address pair.
|
|
|
|
Return True if port has as a allowed address pair and its value
|
|
is either [] or not is_attr_set, otherwise return False
|
|
"""
|
|
return (addr_pair.ADDRESS_PAIRS in port['port'] and
|
|
not self._has_address_pairs(port))
|