NSX-v| LBAAS L7 support

Supporting L7 policies and rules in LBAAS-v2
Including a new db table nsxv_lbaas_l7policy_bindings
for mapping between the lbaas policy ID and the nsx application rules.

Depends-on: I3b14d107dbe0a72a6e24239f06bd6c3ac597cfbb
Change-Id: Ic760be8956cea00b972b5f11f6acff294630892d
This commit is contained in:
Adit Sarfaty 2017-01-03 13:23:10 +02:00
parent 751fa61919
commit 2256459aa1
14 changed files with 774 additions and 12 deletions

View File

@ -0,0 +1,5 @@
---
prelude: >
The NSX-V lbaas plugin now supports L7 rules & policies.
features:
- The NSX-V lbaas plugin now supports L7 rules & policies.

View File

@ -1 +1 @@
dd9fe5a3a526
01a33f93f5fd

View File

@ -0,0 +1,45 @@
# Copyright 2017 VMware, Inc.
#
# 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.
"""nsxv_lbv2_l7policy
Revision ID: 01a33f93f5fd
Revises: dd9fe5a3a526
Create Date: 2017-01-04 10:10:59.990122
"""
# revision identifiers, used by Alembic.
revision = '01a33f93f5fd'
down_revision = 'dd9fe5a3a526'
from alembic import op
import sqlalchemy as sa
from neutron.db import migration
def upgrade():
if migration.schema_has_table('lbaas_l7policies'):
op.create_table(
'nsxv_lbaas_l7policy_bindings',
sa.Column('policy_id', sa.String(length=36), nullable=False),
sa.Column('edge_id', sa.String(length=36), nullable=False),
sa.Column('edge_app_rule_id',
sa.String(length=36), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('policy_id'),
sa.ForeignKeyConstraint(['policy_id'],
['lbaas_l7policies.id'],
ondelete='CASCADE'))

View File

@ -779,6 +779,34 @@ def del_nsxv_lbaas_certificate_binding(session, cert_id, edge_id):
edge_id=edge_id).delete())
def add_nsxv_lbaas_l7policy_binding(session, policy_id, edge_id,
edge_app_rule_id):
with session.begin(subtransactions=True):
binding = nsxv_models.NsxvLbaasL7PolicyBinding(
policy_id=policy_id,
edge_id=edge_id,
edge_app_rule_id=edge_app_rule_id)
session.add(binding)
return binding
def get_nsxv_lbaas_l7policy_binding(session, policy_id):
try:
return session.query(
nsxv_models.NsxvLbaasL7PolicyBinding).filter_by(
policy_id=policy_id).one()
except exc.NoResultFound:
return
def del_nsxv_lbaas_l7policy_binding(session, policy_id):
try:
return (session.query(nsxv_models.NsxvLbaasL7PolicyBinding).
filter_by(policy_id=policy_id).delete())
except exc.NoResultFound:
return
def add_nsxv_subnet_ext_attributes(session, subnet_id,
dns_search_domain=None,
dhcp_mtu=None):

View File

@ -331,6 +331,20 @@ class NsxvLbaasCertificateBinding(model_base.BASEV2, models.TimestampMixin):
edge_cert_id = sa.Column(sa.String(36), nullable=False)
class NsxvLbaasL7PolicyBinding(model_base.BASEV2, models.TimestampMixin):
"""Mapping between NSX Edge and LBaaSv2 L7 policy """
__tablename__ = 'nsxv_lbaas_l7policy_bindings'
policy_id = sa.Column(sa.String(36),
sa.ForeignKey('lbaas_l7policies.id',
name='fk_lbaas_l7policies_id',
ondelete="CASCADE"),
primary_key=True)
edge_id = sa.Column(sa.String(36), nullable=False)
edge_app_rule_id = sa.Column(sa.String(36), nullable=False)
class NsxvSubnetExtAttributes(model_base.BASEV2, models.TimestampMixin):
"""Subnet attributes managed by NSX plugin extensions."""

View File

@ -56,3 +56,19 @@ SESSION_PERSISTENCE_METHOD_MAP = {
SESSION_PERSISTENCE_COOKIE_MAP = {
LB_SESSION_PERSISTENCE_APP_COOKIE: 'app',
LB_SESSION_PERSISTENCE_HTTP_COOKIE: 'insert'}
L7_POLICY_ACTION_REJECT = 'REJECT'
L7_POLICY_ACTION_REDIRECT_TO_POOL = 'REDIRECT_TO_POOL'
L7_POLICY_ACTION_REDIRECT_TO_URL = 'REDIRECT_TO_URL'
L7_RULE_TYPE_HOST_NAME = 'HOST_NAME'
L7_RULE_TYPE_PATH = 'PATH'
L7_RULE_TYPE_FILE_TYPE = 'FILE_TYPE'
L7_RULE_TYPE_HEADER = 'HEADER'
L7_RULE_TYPE_COOKIE = 'COOKIE'
L7_RULE_COMPARE_TYPE_REGEX = 'REGEX'
L7_RULE_COMPARE_TYPE_STARTS_WITH = 'STARTS_WITH'
L7_RULE_COMPARE_TYPE_ENDS_WITH = 'ENDS_WITH'
L7_RULE_COMPARE_TYPE_CONTAINS = 'CONTAINS'
L7_RULE_COMPARE_TYPE_EQUAL_TO = 'EQUAL_TO'

View File

@ -17,6 +17,8 @@
from oslo_log import helpers as log_helpers
from vmware_nsx.services.lbaas.nsx_v.v2 import healthmon_mgr as hm_mgr
from vmware_nsx.services.lbaas.nsx_v.v2 import l7policy_mgr
from vmware_nsx.services.lbaas.nsx_v.v2 import l7rule_mgr
from vmware_nsx.services.lbaas.nsx_v.v2 import listener_mgr
from vmware_nsx.services.lbaas.nsx_v.v2 import loadbalancer_mgr as lb_mgr
from vmware_nsx.services.lbaas.nsx_v.v2 import member_mgr
@ -32,3 +34,5 @@ class EdgeLoadbalancerDriverV2(object):
self.pool = pool_mgr.EdgePoolManager(self)
self.member = member_mgr.EdgeMemberManager(self)
self.healthmonitor = hm_mgr.EdgeHealthMonitorManager(self)
self.l7policy = l7policy_mgr.EdgeL7PolicyManager(self)
self.l7rule = l7rule_mgr.EdgeL7RuleManager(self)

View File

@ -0,0 +1,284 @@
# Copyright 2017 VMware, 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.
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
from oslo_utils import excutils
from neutron_lib import constants
from neutron_lib import exceptions as n_exc
from vmware_nsx._i18n import _, _LE, _LW
from vmware_nsx.common import locking
from vmware_nsx.db import nsxv_db
from vmware_nsx.services.lbaas.nsx_v import lbaas_common as lb_common
from vmware_nsx.services.lbaas.nsx_v import lbaas_const as lb_const
from vmware_nsx.services.lbaas.nsx_v.v2 import base_mgr
LOG = logging.getLogger(__name__)
type_by_compare_type = {
lb_const.L7_RULE_COMPARE_TYPE_EQUAL_TO: '',
lb_const.L7_RULE_COMPARE_TYPE_REGEX: '_reg',
lb_const.L7_RULE_COMPARE_TYPE_STARTS_WITH: '_beg',
lb_const.L7_RULE_COMPARE_TYPE_ENDS_WITH: '_end',
lb_const.L7_RULE_COMPARE_TYPE_CONTAINS: '_sub'
}
def policy_to_application_rule(policy):
condition = ''
rule_lines = []
for rule in policy.rules:
if rule.provisioning_status == constants.PENDING_DELETE:
# skip this rule as it is being deleted
continue
type_by_comp = type_by_compare_type.get(rule.compare_type)
if type_by_comp is None:
type_by_comp = ''
LOG.warnning(_LW('Unsupported compare type %(type)s is used in '
'policy %(id)s'), {'type': rule.compare_type,
'id': policy.id})
if rule.type == lb_const.L7_RULE_TYPE_COOKIE:
# Example: acl <id> hdr_sub(cookie) SEEN=1
hdr_type = 'hdr' + type_by_comp
rule_line = ('acl %(rule_id)s %(hdr_type)s(cookie) '
'%(key)s=%(val)s' % {'rule_id': rule.id,
'hdr_type': hdr_type,
'key': rule.key,
'val': rule.value})
elif rule.type == lb_const.L7_RULE_TYPE_HEADER:
# Example: acl <id> hdr(user-agent) -i test
hdr_type = 'hdr' + type_by_comp
rule_line = ('acl %(rule_id)s %(hdr_type)s(%(key)s) '
'-i %(val)s' % {'rule_id': rule.id,
'hdr_type': hdr_type,
'key': rule.key,
'val': rule.value})
elif rule.type == lb_const.L7_RULE_TYPE_HOST_NAME:
# Example: acl <id> hdr_beg(host) -i abcd
hdr_type = 'hdr' + type_by_comp
# -i for case insensitive host name
rule_line = ('acl %(rule_id)s %(hdr_type)s(host) '
'-i %(val)s' % {'rule_id': rule.id,
'hdr_type': hdr_type,
'val': rule.value})
elif rule.type == lb_const.L7_RULE_TYPE_PATH:
# Example: acl <id> path_beg -i /images
# Regardless of the compare type, always look at the beginning of
# the path.
# -i for case insensitive path
rule_line = ('acl %(rule_id)s path_beg '
'-i %(val)s' % {'rule_id': rule.id,
'val': rule.value})
elif rule.type == lb_const.L7_RULE_TYPE_FILE_TYPE:
# Example: acl <id> path_sub -i .jpg
# Regardless of the compare type, always check contained in path.
# -i for case insensitive file type
val = rule.value
if not val.startswith('.'):
val = '.' + val
rule_line = ('acl %(rule_id)s path_sub '
'-i %(val)s' % {'rule_id': rule.id,
'val': val})
else:
msg = _('Unsupported L7rule type %s') % rule.type
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
rule_lines.append(rule_line)
invert_sign = '!' if rule.invert else ''
condition = condition + invert_sign + rule.id + ' '
if rule_lines:
# concatenate all the rules with new lines
all_rules = '\n'.join(rule_lines + [''])
# remove he last space from the condition
condition = condition[:-1]
else:
all_rules = ''
condition = 'TRUE'
# prepare the action
if policy.action == lb_const.L7_POLICY_ACTION_REJECT:
action = 'tcp-request content reject'
elif policy.action == lb_const.L7_POLICY_ACTION_REDIRECT_TO_POOL:
action = 'use_backend pool_%s' % policy.redirect_pool_id
elif policy.action == lb_const.L7_POLICY_ACTION_REDIRECT_TO_URL:
action = 'redirect location %s' % policy.redirect_url
else:
msg = _('Unsupported L7policy action %s') % policy.action
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
# Build the final script
script = all_rules + '%(action)s if %(cond)s' % {
'action': action, 'cond': condition}
app_rule = {'name': 'pol_' + policy.id, 'script': script}
return app_rule
def policy_to_edge_and_rule_id(context, policy_id):
# get the nsx application rule id and edge id
binding = nsxv_db.get_nsxv_lbaas_l7policy_binding(
context.session, policy_id)
if not binding:
msg = _('No suitable Edge found for policy %s') % policy_id
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
return binding['edge_id'], binding['edge_app_rule_id']
class EdgeL7PolicyManager(base_mgr.EdgeLoadbalancerBaseManager):
@log_helpers.log_method_call
def __init__(self, vcns_driver):
super(EdgeL7PolicyManager, self).__init__(vcns_driver)
def _add_app_rule_to_virtual_server(self, edge_id, vse_id, app_rule_id,
policy_position):
"""Add the new nsx application rule to the virtual server"""
# Get the current virtual server configuration
vse = self.vcns.get_vip(edge_id, vse_id)[1]
if 'applicationRuleId' not in vse:
vse['applicationRuleId'] = []
# Add the policy (=application rule) in the correct position
# (position begins at 1)
if len(vse['applicationRuleId']) < policy_position:
vse['applicationRuleId'].append(app_rule_id)
else:
vse['applicationRuleId'].insert(policy_position - 1, app_rule_id)
# update the backend with the new configuration
self.vcns.update_vip(edge_id, vse_id, vse)
def _del_app_rule_from_virtual_server(self, edge_id, vse_id, app_rule_id):
"""Delete nsx application rule from the virtual server"""
# Get the current virtual server configuration
vse = self.vcns.get_vip(edge_id, vse_id)[1]
if 'applicationRuleId' not in vse:
vse['applicationRuleId'] = []
# Remove the rule from the list
if (app_rule_id in vse['applicationRuleId']):
vse['applicationRuleId'].remove(app_rule_id)
# update the backend with the new configuration
self.vcns.update_vip(edge_id, vse_id, vse)
@log_helpers.log_method_call
def create(self, context, pol):
# find out the edge to be updated, by the listener of this policy
lb_id = pol.listener.loadbalancer_id
lb_binding = nsxv_db.get_nsxv_lbaas_loadbalancer_binding(
context.session, lb_id)
if not lb_binding:
msg = _(
'No suitable Edge found for listener %s') % pol.listener_id
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
edge_id = lb_binding['edge_id']
app_rule = policy_to_application_rule(pol)
app_rule_id = None
try:
with locking.LockManager.get_lock(edge_id):
# create the backend application rule for this policy
h = (self.vcns.create_app_rule(edge_id, app_rule))[0]
app_rule_id = lb_common.extract_resource_id(h['location'])
# add the nsx application rule (neutron policy) to the nsx
# virtual server (neutron listener)
listener_binding = nsxv_db.get_nsxv_lbaas_listener_binding(
context.session, lb_id, pol.listener.id)
if listener_binding:
self._add_app_rule_to_virtual_server(
edge_id, listener_binding['vse_id'], app_rule_id,
pol.position)
except Exception as e:
with excutils.save_and_reraise_exception():
self.lbv2_driver.l7policy.failed_completion(context, pol)
LOG.error(_LE('Failed to create L7policy on edge %(edge)s: '
'%(err)s'),
{'edge': edge_id, 'err': e})
if app_rule_id:
# Failed to add the rule to the vip: delete the rule
# from the backend.
try:
self.vcns.delete_app_rule(edge_id, app_rule_id)
except Exception:
pass
# save the nsx application rule id in the DB
nsxv_db.add_nsxv_lbaas_l7policy_binding(context.session, pol.id,
edge_id, app_rule_id)
# complete the transaction
self.lbv2_driver.l7policy.successful_completion(context, pol)
@log_helpers.log_method_call
def update(self, context, old_pol, new_pol):
# get the nsx application rule id and edge id from the nsx DB
edge_id, app_rule_id = policy_to_edge_and_rule_id(context, new_pol.id)
# create the script for the new policy data
app_rule = policy_to_application_rule(new_pol)
try:
with locking.LockManager.get_lock(edge_id):
# update the backend application rule for the new policy
self.vcns.update_app_rule(edge_id, app_rule_id, app_rule)
except Exception as e:
with excutils.save_and_reraise_exception():
self.lbv2_driver.l7policy.failed_completion(context, new_pol)
LOG.error(_LE('Failed to update L7policy on edge %(edge)s: '
'%(err)s'),
{'edge': edge_id, 'err': e})
# complete the transaction
self.lbv2_driver.l7policy.successful_completion(context, new_pol)
@log_helpers.log_method_call
def delete(self, context, pol):
# get the nsx application rule id and edge id from the nsx DB
try:
edge_id, app_rule_id = policy_to_edge_and_rule_id(context, pol.id)
except n_exc.BadRequest:
# This is probably a policy that we failed to create properly.
# We should allow deleting it
self.lbv2_driver.l7policy.successful_completion(context, pol,
delete=True)
return
with locking.LockManager.get_lock(edge_id):
try:
# remove the nsx application rule from the virtual server
lb_id = pol.listener.loadbalancer_id
listener_binding = nsxv_db.get_nsxv_lbaas_listener_binding(
context.session, lb_id, pol.listener.id)
if listener_binding:
vse_id = listener_binding['vse_id']
self._del_app_rule_from_virtual_server(
edge_id, vse_id, app_rule_id)
# delete the nsx application rule
self.vcns.delete_app_rule(edge_id, app_rule_id)
except Exception as e:
with excutils.save_and_reraise_exception():
self.lbv2_driver.l7policy.failed_completion(context, pol)
LOG.error(_LE('Failed to delete L7policy on edge '
'%(edge)s: %(err)s'),
{'edge': edge_id, 'err': e})
# delete the nsxv db entry
nsxv_db.del_nsxv_lbaas_l7policy_binding(context.session, pol.id)
# complete the transaction
self.lbv2_driver.l7policy.successful_completion(context, pol,
delete=True)

View File

@ -0,0 +1,67 @@
# Copyright 2017 VMware, 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.
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
from oslo_utils import excutils
from vmware_nsx._i18n import _LE
from vmware_nsx.common import locking
from vmware_nsx.services.lbaas.nsx_v.v2 import base_mgr
from vmware_nsx.services.lbaas.nsx_v.v2 import l7policy_mgr
LOG = logging.getLogger(__name__)
class EdgeL7RuleManager(base_mgr.EdgeLoadbalancerBaseManager):
@log_helpers.log_method_call
def __init__(self, vcns_driver):
super(EdgeL7RuleManager, self).__init__(vcns_driver)
def _handle_l7policy_rules_change(self, context, rule, delete=False):
# Get the nsx application rule id and edge id
edge_id, app_rule_id = l7policy_mgr.policy_to_edge_and_rule_id(
context, rule.l7policy_id)
# Create the script for the new policy data.
# The policy obj on the rule is already updated with the
# created/updated/deleted rule.
app_rule = l7policy_mgr.policy_to_application_rule(rule.policy)
try:
with locking.LockManager.get_lock(edge_id):
# update the backend application rule for the updated policy
self.vcns.update_app_rule(edge_id, app_rule_id, app_rule)
except Exception as e:
with excutils.save_and_reraise_exception():
self.lbv2_driver.l7rule.failed_completion(context, rule)
LOG.error(_LE('Failed to update L7rules on edge %(edge)s: '
'%(err)s'),
{'edge': edge_id, 'err': e})
# complete the transaction
self.lbv2_driver.l7rule.successful_completion(context, rule,
delete=delete)
@log_helpers.log_method_call
def create(self, context, rule):
self._handle_l7policy_rules_change(context, rule)
@log_helpers.log_method_call
def update(self, context, old_rule, new_rule):
self._handle_l7policy_rules_change(context, new_rule)
@log_helpers.log_method_call
def delete(self, context, rule):
self._handle_l7policy_rules_change(context, rule, delete=True)

View File

@ -71,13 +71,14 @@ def listener_to_edge_app_profile(listener, edge_cert_id):
return edge_app_profile
def listener_to_edge_vse(listener, vip_address, default_pool, app_profile_id):
def listener_to_edge_vse(context, listener, vip_address, default_pool,
app_profile_id):
if listener.connection_limit:
connection_limit = max(0, listener.connection_limit)
else:
connection_limit = 0
return {
vse = {
'name': 'vip_' + listener.id,
'description': listener.description,
'ipAddress': vip_address,
@ -89,6 +90,18 @@ def listener_to_edge_vse(listener, vip_address, default_pool, app_profile_id):
listener.protocol == lb_const.LB_PROTOCOL_TCP),
'applicationProfileId': app_profile_id}
# Add the L7 policies
if listener.l7_policies:
app_rule_ids = []
for pol in listener.l7_policies:
binding = nsxv_db.get_nsxv_lbaas_l7policy_binding(
context.session, pol.id)
if binding:
app_rule_ids.append(binding['edge_app_rule_id'])
vse['applicationRuleId'] = app_rule_ids
return vse
class EdgeListenerManager(base_mgr.EdgeLoadbalancerBaseManager):
@log_helpers.log_method_call
@ -157,7 +170,8 @@ class EdgeListenerManager(base_mgr.EdgeLoadbalancerBaseManager):
LOG.error(_LE('Failed to create app profile on edge: %s'),
lb_binding['edge_id'])
vse = listener_to_edge_vse(listener, lb_binding['vip_address'],
vse = listener_to_edge_vse(context, listener,
lb_binding['vip_address'],
default_pool,
app_profile_id)
@ -227,7 +241,7 @@ class EdgeListenerManager(base_mgr.EdgeLoadbalancerBaseManager):
self.vcns.update_app_profile(
edge_id, app_profile_id, app_profile)
vse = listener_to_edge_vse(new_listener,
vse = listener_to_edge_vse(context, new_listener,
lb_binding['vip_address'],
default_pool,
app_profile_id)

View File

@ -33,10 +33,17 @@ class EdgeMemberManager(base_mgr.EdgeLoadbalancerBaseManager):
super(EdgeMemberManager, self).__init__(vcns_driver)
self._fw_section_id = None
def _get_pool_lb_id(self, member):
listener = member.pool.listener
if listener:
lb_id = listener.loadbalancer_id
else:
lb_id = member.pool.loadbalancer.id
return lb_id
@log_helpers.log_method_call
def create(self, context, member):
listener = member.pool.listener
lb_id = listener.loadbalancer_id
lb_id = self._get_pool_lb_id(member)
lb_binding = nsxv_db.get_nsxv_lbaas_loadbalancer_binding(
context.session, lb_id)
pool_binding = nsxv_db.get_nsxv_lbaas_pool_binding(
@ -72,8 +79,7 @@ class EdgeMemberManager(base_mgr.EdgeLoadbalancerBaseManager):
@log_helpers.log_method_call
def update(self, context, old_member, new_member):
listener = new_member.pool.listener
lb_id = listener.loadbalancer_id
lb_id = self._get_pool_lb_id(new_member)
lb_binding = nsxv_db.get_nsxv_lbaas_loadbalancer_binding(
context.session, lb_id)
pool_binding = nsxv_db.get_nsxv_lbaas_pool_binding(context.session,
@ -121,8 +127,7 @@ class EdgeMemberManager(base_mgr.EdgeLoadbalancerBaseManager):
@log_helpers.log_method_call
def delete(self, context, member):
listener = member.pool.listener
lb_id = listener.loadbalancer_id
lb_id = self._get_pool_lb_id(member)
lb_binding = nsxv_db.get_nsxv_lbaas_loadbalancer_binding(
context.session, lb_id)
pool_binding = nsxv_db.get_nsxv_lbaas_pool_binding(

View File

@ -17,6 +17,8 @@ from oslo_log import helpers as log_helpers
from oslo_log import log as logging
from oslo_utils import excutils
from neutron_lib import exceptions as n_exc
from vmware_nsx._i18n import _LE
from vmware_nsx.common import locking
from vmware_nsx.db import nsxv_db
@ -48,7 +50,10 @@ class EdgePoolManager(base_mgr.EdgeLoadbalancerBaseManager):
lb_id = pool.loadbalancer_id
lb_binding = nsxv_db.get_nsxv_lbaas_loadbalancer_binding(
context.session, lb_id)
if not lb_binding:
msg = _(
'No suitable Edge found for pool %s') % pool.id
raise n_exc.BadRequest(resource='edge-lbaas', msg=msg)
edge_id = lb_binding['edge_id']
try:
@ -64,6 +69,7 @@ class EdgePoolManager(base_mgr.EdgeLoadbalancerBaseManager):
context.session, lb_id, pool.listener.id)
# Associate listener with pool
vse = listener_mgr.listener_to_edge_vse(
context,
pool.listener,
lb_binding['vip_address'],
edge_pool_id,
@ -133,6 +139,7 @@ class EdgePoolManager(base_mgr.EdgeLoadbalancerBaseManager):
listener_binding = nsxv_db.get_nsxv_lbaas_listener_binding(
context.session, lb_id, listener.id)
vse = listener_mgr.listener_to_edge_vse(
context,
listener,
lb_binding['vip_address'],
None,

View File

@ -72,6 +72,17 @@ HM_BINDING = {'loadbalancer_id': LB_ID,
'edge_id': LB_EDGE_ID,
'edge_mon_id': EDGE_HM_ID}
L7POL_ID = 'l7pol-l7pol'
EDGE_RULE_ID = 'app-rule-xx'
L7POL_BINDING = {'policy_id': L7POL_ID,
'edge_id': LB_EDGE_ID,
'edge_app_rule_id': EDGE_RULE_ID}
EDGE_L7POL_DEF = {'script': 'tcp-request content reject if TRUE',
'name': 'pol_' + L7POL_ID}
L7RULE_ID1 = 'l7rule-111'
L7RULE_ID2 = 'l7rule-222'
class BaseTestEdgeLbaasV2(base.BaseTestCase):
def _tested_entity(self):
@ -106,6 +117,28 @@ class BaseTestEdgeLbaasV2(base.BaseTestCase):
MEMBER_ADDRESS, 80, 1, pool=self.pool)
self.hm = lb_models.HealthMonitor(HM_ID, LB_TENANT_ID, 'PING', 3, 3,
1, pool=self.pool)
self.l7policy = lb_models.L7Policy(L7POL_ID, LB_TENANT_ID,
name='policy-test',
description='policy-desc',
listener_id=LISTENER_ID,
action='REJECT',
listener=self.listener,
position=1)
self.l7rule1 = lb_models.L7Rule(L7RULE_ID1, LB_TENANT_ID,
l7policy_id=L7POL_ID,
compare_type='EQUAL_TO',
invert=False,
type='HEADER',
key='key1',
value='val1',
policy=self.l7policy)
self.l7rule2 = lb_models.L7Rule(L7RULE_ID2, LB_TENANT_ID,
l7policy_id=L7POL_ID,
compare_type='STARTS_WITH',
invert=True,
type='PATH',
value='/images',
policy=self.l7policy)
def tearDown(self):
self._unpatch_lb_plugin(self.lbv2_driver, self._tested_entity)
@ -606,3 +639,228 @@ class TestEdgeLbaasV2HealthMonitor(BaseTestEdgeLbaasV2):
mock_successful_completion.assert_called_with(self.context,
self.hm,
delete=True)
class TestEdgeLbaasV2L7Policy(BaseTestEdgeLbaasV2):
def setUp(self):
super(TestEdgeLbaasV2L7Policy, self).setUp()
@property
def _tested_entity(self):
return 'l7policy'
def test_create(self):
with mock.patch.object(nsxv_db, 'get_nsxv_lbaas_l7policy_binding'
) as mock_get_l7policy_binding, \
mock.patch.object(nsxv_db, 'get_nsxv_lbaas_loadbalancer_binding'
) as mock_get_lb_binding, \
mock.patch.object(nsxv_db, 'get_nsxv_lbaas_listener_binding'
) as mock_get_listener_binding, \
mock.patch.object(nsxv_db, 'add_nsxv_lbaas_l7policy_binding'
) as mock_add_l7policy_binding,\
mock.patch.object(self.edge_driver.vcns, 'create_app_rule'
) as mock_create_rule, \
mock.patch.object(self.edge_driver.vcns, 'get_vip'
) as mock_get_vip, \
mock.patch.object(self.edge_driver.vcns, 'update_vip'
) as mock_upd_vip:
mock_get_lb_binding.return_value = LB_BINDING
mock_get_l7policy_binding.return_value = L7POL_BINDING
mock_get_listener_binding.return_value = LISTENER_BINDING
mock_create_rule.return_value = (
{'location': 'x/' + EDGE_RULE_ID}, None)
mock_get_vip.return_value = (None, EDGE_VIP_DEF.copy())
self.edge_driver.l7policy.create(self.context, self.l7policy)
mock_create_rule.assert_called_with(LB_EDGE_ID,
EDGE_L7POL_DEF.copy())
mock_add_l7policy_binding.assert_called_with(
self.context.session, L7POL_ID, LB_EDGE_ID, EDGE_RULE_ID)
edge_vip_def = EDGE_VIP_DEF.copy()
edge_vip_def['applicationRuleId'] = [EDGE_RULE_ID]
mock_upd_vip.assert_called_with(LB_EDGE_ID, EDGE_VIP_ID,
edge_vip_def)
mock_successful_completion = (
self.lbv2_driver.l7policy.successful_completion)
mock_successful_completion.assert_called_with(self.context,
self.l7policy)
def test_update(self):
url = 'http://www.test.com'
new_pol = lb_models.L7Policy(L7POL_ID, LB_TENANT_ID,
name='policy-test',
description='policy-desc',
listener_id=LISTENER_ID,
action='REDIRECT_TO_URL',
redirect_url=url,
listener=self.listener,
position=1)
with mock.patch.object(nsxv_db, 'get_nsxv_lbaas_l7policy_binding'
) as mock_get_l7policy_binding, \
mock.patch.object(nsxv_db, 'get_nsxv_lbaas_loadbalancer_binding'
) as mock_get_lb_binding, \
mock.patch.object(self.edge_driver.vcns, 'update_app_rule'
) as mock_update_rule:
mock_get_lb_binding.return_value = LB_BINDING
mock_get_l7policy_binding.return_value = L7POL_BINDING
self.edge_driver.l7policy.update(self.context, self.l7policy,
new_pol)
edge_rule_def = EDGE_L7POL_DEF.copy()
edge_rule_def['script'] = "redirect location %s if TRUE" % url
mock_update_rule.assert_called_with(
LB_EDGE_ID, EDGE_RULE_ID, edge_rule_def)
mock_successful_completion = (
self.lbv2_driver.l7policy.successful_completion)
mock_successful_completion.assert_called_with(self.context,
new_pol)
def test_delete(self):
with mock.patch.object(nsxv_db, 'get_nsxv_lbaas_l7policy_binding'
) as mock_get_l7policy_binding, \
mock.patch.object(nsxv_db, 'del_nsxv_lbaas_l7policy_binding'
) as mock_del_l7policy_binding, \
mock.patch.object(nsxv_db, 'get_nsxv_lbaas_loadbalancer_binding'
) as mock_get_lb_binding, \
mock.patch.object(nsxv_db, 'get_nsxv_lbaas_pool_binding'
) as mock_get_pool_binding,\
mock.patch.object(nsxv_db, 'get_nsxv_lbaas_listener_binding'
) as mock_get_listener_binding, \
mock.patch.object(self.edge_driver.vcns, 'delete_app_rule'
) as mock_del_app_rule, \
mock.patch.object(self.edge_driver.vcns, 'get_vip'
) as mock_get_vip, \
mock.patch.object(self.edge_driver.vcns, 'update_vip'
) as mock_upd_vip:
mock_get_lb_binding.return_value = LB_BINDING
mock_get_pool_binding.return_value = POOL_BINDING
mock_get_listener_binding.return_value = LISTENER_BINDING
mock_get_l7policy_binding.return_value = L7POL_BINDING
edge_vip_def = EDGE_VIP_DEF.copy()
edge_vip_def['applicationRuleId'] = [EDGE_RULE_ID]
mock_get_vip.return_value = (None, edge_vip_def)
self.edge_driver.l7policy.delete(self.context, self.l7policy)
edge_vip_def2 = EDGE_VIP_DEF.copy()
edge_vip_def2['applicationRuleId'] = []
mock_upd_vip.assert_called_with(LB_EDGE_ID, EDGE_VIP_ID,
edge_vip_def2)
mock_del_app_rule.assert_called_with(LB_EDGE_ID, EDGE_RULE_ID)
mock_del_l7policy_binding.assert_called_with(
self.context.session, L7POL_ID)
mock_successful_completion = (
self.lbv2_driver.l7policy.successful_completion)
mock_successful_completion.assert_called_with(self.context,
self.l7policy,
delete=True)
class TestEdgeLbaasV2L7Rule(BaseTestEdgeLbaasV2):
def setUp(self):
super(TestEdgeLbaasV2L7Rule, self).setUp()
@property
def _tested_entity(self):
return 'l7rule'
def test_create(self):
with mock.patch.object(nsxv_db, 'get_nsxv_lbaas_l7policy_binding'
) as mock_get_l7policy_binding, \
mock.patch.object(self.edge_driver.vcns, 'update_app_rule'
) as mock_update_rule:
mock_get_l7policy_binding.return_value = L7POL_BINDING
# Create the first rule
self.l7rule1.policy.rules = [self.l7rule1]
self.edge_driver.l7rule.create(self.context, self.l7rule1)
edge_rule_def = EDGE_L7POL_DEF.copy()
edge_rule_def['script'] = (
"acl %(rule_id)s hdr(key1) -i val1\n"
"tcp-request content reject if %(rule_id)s" %
{'rule_id': L7RULE_ID1})
mock_update_rule.assert_called_with(
LB_EDGE_ID, EDGE_RULE_ID, edge_rule_def)
mock_successful_completion = (
self.lbv2_driver.l7rule.successful_completion)
mock_successful_completion.assert_called_with(
self.context, self.l7rule1, delete=False)
# Create the 2nd rule
self.l7rule2.policy.rules = [self.l7rule1, self.l7rule2]
self.edge_driver.l7rule.create(self.context, self.l7rule2)
edge_rule_def = EDGE_L7POL_DEF.copy()
edge_rule_def['script'] = (
"acl %(rule_id1)s hdr(key1) -i val1\n"
"acl %(rule_id2)s path_beg -i /images\n"
"tcp-request content reject if %(rule_id1)s !%(rule_id2)s" %
{'rule_id1': L7RULE_ID1,
'rule_id2': L7RULE_ID2})
mock_update_rule.assert_called_with(
LB_EDGE_ID, EDGE_RULE_ID, edge_rule_def)
mock_successful_completion = (
self.lbv2_driver.l7rule.successful_completion)
mock_successful_completion.assert_called_with(
self.context, self.l7rule2, delete=False)
def test_update(self):
new_rule = lb_models.L7Rule(L7RULE_ID1, LB_TENANT_ID,
l7policy_id=L7POL_ID,
compare_type='EQUAL_TO',
invert=False,
type='HEADER',
key='key2',
value='val1',
policy=self.l7policy)
with mock.patch.object(nsxv_db, 'get_nsxv_lbaas_l7policy_binding'
) as mock_get_l7policy_binding, \
mock.patch.object(self.edge_driver.vcns, 'update_app_rule'
) as mock_update_rule:
mock_get_l7policy_binding.return_value = L7POL_BINDING
new_rule.policy.rules = [new_rule]
self.edge_driver.l7rule.update(
self.context, self.l7rule1, new_rule)
edge_rule_def = EDGE_L7POL_DEF.copy()
edge_rule_def['script'] = (
"acl %(rule_id)s hdr(key2) -i val1\n"
"tcp-request content reject if %(rule_id)s" %
{'rule_id': L7RULE_ID1})
mock_update_rule.assert_called_with(
LB_EDGE_ID, EDGE_RULE_ID, edge_rule_def)
mock_successful_completion = (
self.lbv2_driver.l7rule.successful_completion)
mock_successful_completion.assert_called_with(
self.context, new_rule, delete=False)
def test_delete(self):
with mock.patch.object(nsxv_db, 'get_nsxv_lbaas_l7policy_binding'
) as mock_get_l7policy_binding, \
mock.patch.object(self.edge_driver.vcns, 'update_app_rule'
) as mock_update_rule:
mock_get_l7policy_binding.return_value = L7POL_BINDING
self.l7rule1.policy.rules = []
self.edge_driver.l7rule.delete(self.context, self.l7rule1)
edge_rule_def = EDGE_L7POL_DEF.copy()
edge_rule_def['script'] = (
"tcp-request content reject if TRUE")
mock_update_rule.assert_called_with(
LB_EDGE_ID, EDGE_RULE_ID, edge_rule_def)
mock_successful_completion = (
self.lbv2_driver.l7rule.successful_completion)
mock_successful_completion.assert_called_with(
self.context, self.l7rule1, delete=True)

View File

@ -735,6 +735,21 @@ class FakeVcns(object):
del self._fake_app_profiles_dict[edge_id][app_profileid]
return self.return_helper(header, response)
def create_app_rule(self, edge_id, app_rule):
app_ruleid = uuidutils.generate_uuid()
header = {
'status': 204,
'location': "https://host/api/4.0/edges/edge_id"
"/loadbalancer/config/%s" % app_ruleid}
response = ""
return self.return_helper(header, response)
def update_app_rule(self, edge_id, app_ruleid, app_rule):
pass
def delete_app_rule(self, edge_id, app_ruleid):
pass
def get_loadbalancer_config(self, edge_id):
header = {'status': 204}
response = {'config': False}