NSXv3: Refactor LBaaS L7 code

Refactor L7 code to support multiple l7 rules in a policy, and
fix the position of lb rule in the virtual server.

Change-Id: I08e10ecc6ef594c5539fe7315ffa3da51b2184d8
This commit is contained in:
Tong Liu 2017-10-25 02:13:29 +00:00
parent 60c44020eb
commit 410575e5e7
9 changed files with 471 additions and 272 deletions

View File

@ -633,29 +633,22 @@ def delete_nsx_lbaas_monitor_binding(session, loadbalancer_id, pool_id,
pool_id=pool_id, hm_id=hm_id).delete()) pool_id=pool_id, hm_id=hm_id).delete())
def add_nsx_lbaas_l7rule_binding(session, loadbalancer_id, l7policy_id, def add_nsx_lbaas_l7policy_binding(session, l7policy_id, lb_rule_id, lb_vs_id):
l7rule_id, lb_rule_id, lb_vs_id):
with session.begin(subtransactions=True): with session.begin(subtransactions=True):
binding = nsx_models.NsxLbaasL7Rule( binding = nsx_models.NsxLbaasL7Policy(
loadbalancer_id=loadbalancer_id, l7policy_id=l7policy_id, l7policy_id=l7policy_id, lb_rule_id=lb_rule_id, lb_vs_id=lb_vs_id)
l7rule_id=l7rule_id, lb_rule_id=lb_rule_id, lb_vs_id=lb_vs_id)
session.add(binding) session.add(binding)
return binding return binding
def get_nsx_lbaas_l7rule_binding(session, loadbalancer_id, l7policy_id, def get_nsx_lbaas_l7policy_binding(session, l7policy_id):
l7rule_id):
try: try:
return session.query(nsx_models.NsxLbaasL7Rule).filter_by( return session.query(nsx_models.NsxLbaasL7Policy).filter_by(
loadbalancer_id=loadbalancer_id, l7policy_id=l7policy_id, l7policy_id=l7policy_id).one()
l7rule_id=l7rule_id).one()
except exc.NoResultFound: except exc.NoResultFound:
return return
def delete_nsx_lbaas_l7rule_binding(session, loadbalancer_id, l7policy_id, def delete_nsx_lbaas_l7policy_binding(session, l7policy_id):
l7rule_id): return (session.query(nsx_models.NsxLbaasL7Policy).
return (session.query(nsx_models.NsxLbaasL7Rule). filter_by(l7policy_id=l7policy_id).delete())
filter_by(loadbalancer_id=loadbalancer_id,
l7policy_id=l7policy_id,
l7rule_id=l7rule_id).delete())

View File

@ -1 +1 @@
a1be06050b41 717f7f63a219

View File

@ -0,0 +1,62 @@
# 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.
"""nsxv3_lbaas_l7policy
Revision ID: 717f7f63a219
Revises: a1be06050b41
Create Date: 2017-10-26 08:32:40.846088
"""
# revision identifiers, used by Alembic.
revision = '717f7f63a219'
down_revision = 'a1be06050b41'
from alembic import op
import sqlalchemy as sa
from neutron.db import migration
def upgrade():
if migration.schema_has_table('nsxv3_lbaas_l7rules'):
op.drop_constraint('fk_nsxv3_lbaas_l7rules_id', 'nsxv3_lbaas_l7rules',
'foreignkey')
op.drop_constraint('l7rule_id', 'nsxv3_lbaas_l7rules', 'primary')
op.drop_column('nsxv3_lbaas_l7rules', 'loadbalancer_id')
op.drop_column('nsxv3_lbaas_l7rules', 'l7rule_id')
op.rename_table('nsxv3_lbaas_l7rules', 'nsxv3_lbaas_l7policies')
if migration.schema_has_table('lbaas_l7policies'):
op.create_foreign_key(
'fk_nsxv3_lbaas_l7policies_id', 'nsxv3_lbaas_l7policies',
'lbaas_l7policies', ['l7policy_id'], ['id'],
ondelete='CASCADE')
else:
op.create_table(
'nsxv3_lbaas_l7policies',
sa.Column('l7policy_id', sa.String(36), nullable=False),
sa.Column('lb_rule_id', sa.String(36), nullable=False),
sa.Column('lb_vs_id', sa.String(36), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('l7policy_id'))
if migration.schema_has_table('lbaas_l7policies'):
op.create_foreign_key(
'fk_nsxv3_lbaas_l7policies_id', 'nsxv3_lbaas_l7policies',
'lbaas_l7policies', ['l7policy_id'], ['id'],
ondelete='CASCADE')

View File

@ -451,7 +451,14 @@ class NsxLbaasMonitor(model_base.BASEV2, models.TimestampMixin):
class NsxLbaasL7Rule(model_base.BASEV2, models.TimestampMixin): class NsxLbaasL7Rule(model_base.BASEV2, models.TimestampMixin):
"""Stores the mapping between LBaaS monitor and NSX LB monitor""" """Stores the mapping between LBaaS monitor and NSX LB monitor
This table is only used in Pike and obsoleted since Queen as the
mapping has been stored in nsxv3_lbaas_l7policies table instead.
This original table was added in pike so that we cannot change
DB migration script there, but instead we update the table with
a new db migration script in Queen.
"""
__tablename__ = 'nsxv3_lbaas_l7rules' __tablename__ = 'nsxv3_lbaas_l7rules'
loadbalancer_id = sa.Column(sa.String(36), primary_key=True) loadbalancer_id = sa.Column(sa.String(36), primary_key=True)
l7policy_id = sa.Column(sa.String(36), primary_key=True) l7policy_id = sa.Column(sa.String(36), primary_key=True)
@ -462,3 +469,15 @@ class NsxLbaasL7Rule(model_base.BASEV2, models.TimestampMixin):
primary_key=True) primary_key=True)
lb_rule_id = sa.Column(sa.String(36), nullable=False) lb_rule_id = sa.Column(sa.String(36), nullable=False)
lb_vs_id = sa.Column(sa.String(36), nullable=False) lb_vs_id = sa.Column(sa.String(36), nullable=False)
class NsxLbaasL7Policy(model_base.BASEV2, models.TimestampMixin):
"""Stores the mapping between LBaaS l7policy and NSX LB rule"""
__tablename__ = 'nsxv3_lbaas_l7policies'
l7policy_id = sa.Column(sa.String(36),
sa.ForeignKey('lbaas_l7policies.id',
name='fk_nsxv3_lbaas_l7policies_id',
ondelete="CASCADE"),
primary_key=True)
lb_rule_id = sa.Column(sa.String(36), nullable=False)
lb_vs_id = sa.Column(sa.String(36), nullable=False)

View File

@ -79,7 +79,7 @@ LB_LB_NAME = 'os-lbaas-lb-name'
LB_LISTENER_TYPE = 'os-lbaas-listener-id' LB_LISTENER_TYPE = 'os-lbaas-listener-id'
LB_HM_TYPE = 'os-lbaas-hm-id' LB_HM_TYPE = 'os-lbaas-hm-id'
LB_POOL_TYPE = 'os-lbaas-pool-id' LB_POOL_TYPE = 'os-lbaas-pool-id'
LB_L7RULE_TYPE = 'os-lbaas-l7rule-id' LB_L7POLICY_TYPE = 'os-lbaas-l7policy-id'
LB_HTTP_PROFILE = 'LbHttpProfile' LB_HTTP_PROFILE = 'LbHttpProfile'
LB_TCP_PROFILE = 'LbFastTcpProfile' LB_TCP_PROFILE = 'LbFastTcpProfile'
LB_UDP_PROFILE = 'LbFastUdpProfile' LB_UDP_PROFILE = 'LbFastUdpProfile'

View File

@ -16,10 +16,13 @@
from neutron_lib import exceptions as n_exc from neutron_lib import exceptions as n_exc
from oslo_log import helpers as log_helpers from oslo_log import helpers as log_helpers
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import excutils
from vmware_nsx._i18n import _ from vmware_nsx._i18n import _
from vmware_nsx.db import db as nsx_db from vmware_nsx.db import db as nsx_db
from vmware_nsx.services.lbaas import base_mgr from vmware_nsx.services.lbaas import base_mgr
from vmware_nsx.services.lbaas import lb_const
from vmware_nsx.services.lbaas.nsx_v3 import lb_utils
from vmware_nsxlib.v3 import exceptions as nsxlib_exc from vmware_nsxlib.v3 import exceptions as nsxlib_exc
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -31,51 +34,111 @@ class EdgeL7PolicyManager(base_mgr.Nsxv3LoadbalancerBaseManager):
super(EdgeL7PolicyManager, self).__init__() super(EdgeL7PolicyManager, self).__init__()
@log_helpers.log_method_call @log_helpers.log_method_call
def _l7policy_action(self, context, policy, action, delete=False): def _update_policy_position(self, vs_id, rule_id, position):
try: vs_client = self.core_plugin.nsxlib.load_balancer.virtual_server
self.lbv2_driver.l7policy.successful_completion( vs = vs_client.get(vs_id)
context, policy, delete=delete) lb_rules = vs.get('rule_ids', [])
except Exception as e: if rule_id in lb_rules:
self.lbv2_driver.l7policy.failed_completion(context, policy) lb_rules.remove(rule_id)
msg = (_('Failed to %(action)s l7policy %(err)s') % if len(lb_rules) < position:
{'action': action, 'err': e}) lb_rules.append(rule_id)
resource = 'lbaas-l7policy-%s' % action else:
raise n_exc.BadRequest(resource=resource, msg=msg) lb_rules.insert(position - 1, rule_id)
vs_client.update(vs_id, rule_ids=lb_rules)
@log_helpers.log_method_call @log_helpers.log_method_call
def create(self, context, policy): def create(self, context, policy):
self._l7policy_action(context, policy, 'create') lb_id = policy.listener.loadbalancer_id
listener_id = policy.listener_id
rule_client = self.core_plugin.nsxlib.load_balancer.rule
tags = lb_utils.get_tags(self.core_plugin, policy.id,
lb_const.LB_L7POLICY_TYPE,
policy.tenant_id, context.project_name)
binding = nsx_db.get_nsx_lbaas_listener_binding(
context.session, lb_id, listener_id)
if not binding:
self.lbv2_driver.l7policy.failed_completion(context, policy)
msg = _('Cannot find nsx lbaas binding for listener '
'%(listener_id)s') % {'listener_id': listener_id}
raise n_exc.BadRequest(resource='lbaas-l7policy-create', msg=msg)
vs_id = binding['lb_vs_id']
rule_body = lb_utils.convert_l7policy_to_lb_rule(context, policy)
try:
lb_rule = rule_client.create(tags=tags, **rule_body)
except nsxlib_exc.ManagerError:
with excutils.save_and_reraise_exception():
self.lbv2_driver.l7policy.failed_completion(context, policy)
LOG.error('Failed to create lb rule at NSX backend')
try:
self._update_policy_position(vs_id, lb_rule['id'],
policy.position)
except nsxlib_exc.ManagerError:
with excutils.save_and_reraise_exception():
self.lbv2_driver.l7policy.failed_completion(context, policy)
LOG.error('Failed to add rule %(rule)% to virtual server '
'%(vs)s at NSX backend', {'rule': lb_rule['id'],
'vs': vs_id})
nsx_db.add_nsx_lbaas_l7policy_binding(
context.session, policy.id, lb_rule['id'], vs_id)
self.lbv2_driver.l7policy.successful_completion(context, policy)
@log_helpers.log_method_call @log_helpers.log_method_call
def update(self, context, old_policy, new_policy): def update(self, context, old_policy, new_policy):
self._l7policy_action(context, new_policy, 'update') rule_client = self.core_plugin.nsxlib.load_balancer.rule
binding = nsx_db.get_nsx_lbaas_l7policy_binding(context.session,
old_policy.id)
if not binding:
self.lbv2_driver.l7rule.failed_completion(context, new_policy)
msg = _('Cannot find nsx lbaas binding for policy '
'%(policy_id)s') % {'policy_id': old_policy.id}
raise n_exc.BadRequest(resource='lbaas-l7policy-update', msg=msg)
vs_id = binding['lb_vs_id']
lb_rule_id = binding['lb_rule_id']
rule_body = lb_utils.convert_l7policy_to_lb_rule(context, new_policy)
try:
rule_client.update(lb_rule_id, **rule_body)
if new_policy.position != old_policy.position:
self._update_policy_position(vs_id, lb_rule_id,
new_policy.position)
except Exception as e:
with excutils.save_and_reraise_exception():
self.lbv2_driver.l7policy.failed_completion(context,
new_policy)
LOG.error('Failed to update L7policy %(policy)s: '
'%(err)s', {'policy': old_policy.id, 'err': e})
self.lbv2_driver.l7policy.successful_completion(context, new_policy)
@log_helpers.log_method_call @log_helpers.log_method_call
def delete(self, context, policy): def delete(self, context, policy):
lb_id = policy.listener.loadbalancer_id
vs_client = self.core_plugin.nsxlib.load_balancer.virtual_server vs_client = self.core_plugin.nsxlib.load_balancer.virtual_server
rule_client = self.core_plugin.nsxlib.load_balancer.rule rule_client = self.core_plugin.nsxlib.load_balancer.rule
for rule in policy.rules: binding = nsx_db.get_nsx_lbaas_l7policy_binding(context.session,
binding = nsx_db.get_nsx_lbaas_l7rule_binding( policy.id)
context.session, lb_id, policy.id, rule.id) if binding:
if binding: vs_id = binding['lb_vs_id']
vs_id = binding['lb_vs_id'] rule_id = binding['lb_rule_id']
rule_id = binding['lb_rule_id'] try:
try: # Update virtual server to remove lb rule
# Update virtual server to remove lb rule vs_client.remove_rule(vs_id, rule_id)
vs_client.remove_rule(vs_id, rule_id) rule_client.delete(rule_id)
rule_client.delete(rule_id) except nsxlib_exc.ResourceNotFound:
except nsxlib_exc.ResourceNotFound: LOG.warning('LB rule %(rule)s is not found on NSX',
LOG.warning('LB rule %(rule)s is not found on NSX', {'rule': rule_id})
{'rule': rule_id}) except nsxlib_exc.ManagerError:
except nsxlib_exc.ManagerError: self.lbv2_driver.l7policy.failed_completion(
self.lbv2_driver.l7policy.failed_completion( context, policy)
context, policy) msg = (_('Failed to delete lb rule: %(rule)s') %
msg = (_('Failed to delete lb rule: %(rule)s') % {'rule': rule_id})
{'rule': rule.id}) raise n_exc.BadRequest(resource='lbaas-l7policy-delete',
raise n_exc.BadRequest(resource='lbaas-l7rule-delete', msg=msg)
msg=msg) nsx_db.delete_nsx_lbaas_l7policy_binding(
nsx_db.delete_nsx_lbaas_l7rule_binding( context.session, policy.id)
context.session, lb_id, policy.id, rule.id)
self.lbv2_driver.l7policy.successful_completion( self.lbv2_driver.l7policy.successful_completion(
context, policy, delete=True) context, policy, delete=True)

View File

@ -16,14 +16,12 @@
from neutron_lib import exceptions as n_exc from neutron_lib import exceptions as n_exc
from oslo_log import helpers as log_helpers from oslo_log import helpers as log_helpers
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import excutils
from vmware_nsx._i18n import _ from vmware_nsx._i18n import _
from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.db import db as nsx_db from vmware_nsx.db import db as nsx_db
from vmware_nsx.services.lbaas import base_mgr from vmware_nsx.services.lbaas import base_mgr
from vmware_nsx.services.lbaas import lb_const
from vmware_nsx.services.lbaas.nsx_v3 import lb_utils from vmware_nsx.services.lbaas.nsx_v3 import lb_utils
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -33,171 +31,39 @@ class EdgeL7RuleManager(base_mgr.Nsxv3LoadbalancerBaseManager):
def __init__(self): def __init__(self):
super(EdgeL7RuleManager, self).__init__() super(EdgeL7RuleManager, self).__init__()
@log_helpers.log_method_call def _update_l7rule_change(self, context, rule, delete=False):
def _get_rule_match_conditions(self, rule): rule_client = self.core_plugin.nsxlib.load_balancer.rule
match_conditions = [] binding = nsx_db.get_nsx_lbaas_l7policy_binding(context.session,
# values in rule have already been validated in LBaaS API, rule.policy.id)
# we won't need to valid anymore in driver, and just get if not binding:
# the LB rule mapping from the dict. self.lbv2_driver.l7rule.failed_completion(context, rule)
match_type = lb_const.LB_RULE_MATCH_TYPE[rule.compare_type] msg = _('Cannot find nsx lbaas binding for policy '
if rule.type == lb_const.L7_RULE_TYPE_COOKIE: '%(policy_id)s') % {'policy_id': rule.policy.id}
header_value = rule.key + '=' + rule.value raise n_exc.BadRequest(resource='lbaas-l7policy-update', msg=msg)
match_conditions.append(
{'type': 'LbHttpRequestHeaderCondition',
'match_type': match_type,
'header_name': 'Cookie',
'header_value': header_value})
elif rule.type == lb_const.L7_RULE_TYPE_FILE_TYPE:
match_conditions.append(
{'type': 'LbHttpRequestUriCondition',
'match_type': match_type,
'uri': '*.' + rule.value})
elif rule.type == lb_const.L7_RULE_TYPE_HEADER:
match_conditions.append(
{'type': 'LbHttpRequestHeaderCondition',
'match_type': match_type,
'header_name': rule.key,
'header_value': rule.value})
elif rule.type == lb_const.L7_RULE_TYPE_HOST_NAME:
match_conditions.append(
{'type': 'LbHttpRequestHeaderCondition',
'match_type': match_type,
'header_name': 'Host',
'header_value': rule.value})
elif rule.type == lb_const.L7_RULE_TYPE_PATH:
match_conditions.append(
{'type': 'LbHttpRequestUriCondition',
'match_type': match_type,
'uri': rule.value})
else:
msg = (_('l7rule type %(type)s is not supported in LBaaS') %
{'type': rule.type})
LOG.error(msg)
raise n_exc.BadRequest(resource='lbaas-l7rule', msg=msg)
return match_conditions
@log_helpers.log_method_call lb_rule_id = binding['lb_rule_id']
def _get_rule_actions(self, context, rule): if delete:
lb_id = rule.policy.listener.loadbalancer_id lb_utils.remove_rule_from_policy(rule)
l7policy = rule.policy rule_body = lb_utils.convert_l7policy_to_lb_rule(context, rule.policy)
if l7policy.action == lb_const.L7_POLICY_ACTION_REDIRECT_TO_POOL: try:
pool_binding = nsx_db.get_nsx_lbaas_pool_binding( rule_client.update(lb_rule_id, **rule_body)
context.session, lb_id, l7policy.redirect_pool_id) except Exception as e:
if pool_binding: with excutils.save_and_reraise_exception():
lb_pool_id = pool_binding['lb_pool_id'] self.lbv2_driver.l7rule.failed_completion(context, rule)
actions = [{'type': lb_const.LB_SELECT_POOL_ACTION, LOG.error('Failed to update L7policy %(policy)s: '
'pool_id': lb_pool_id}] '%(err)s', {'policy': rule.policy.id, 'err': e})
else:
msg = _('Failed to get LB pool binding from nsx db')
raise n_exc.BadRequest(resource='lbaas-l7rule-create',
msg=msg)
elif l7policy.action == lb_const.L7_POLICY_ACTION_REDIRECT_TO_URL:
actions = [{'type': lb_const.LB_HTTP_REDIRECT_ACTION,
'redirect_status': lb_const.LB_HTTP_REDIRECT_STATUS,
'redirect_url': l7policy.redirect_url}]
elif l7policy.action == lb_const.L7_POLICY_ACTION_REJECT:
actions = [{'type': lb_const.LB_REJECT_ACTION,
'reply_status': lb_const.LB_HTTP_REJECT_STATUS}]
else:
msg = (_('Invalid l7policy action: %(action)s') %
{'action': l7policy.action})
raise n_exc.BadRequest(resource='lbaas-l7rule-create',
msg=msg)
return actions
@log_helpers.log_method_call self.lbv2_driver.l7rule.successful_completion(context, rule,
def _convert_l7policy_to_lb_rule(self, context, rule): delete=delete)
body = {}
body['match_conditions'] = self._get_rule_match_conditions(rule)
body['actions'] = self._get_rule_actions(context, rule)
body['phase'] = lb_const.LB_RULE_HTTP_FORWARDING
body['match_strategy'] = 'ANY'
return body
@log_helpers.log_method_call @log_helpers.log_method_call
def create(self, context, rule): def create(self, context, rule):
lb_id = rule.policy.listener.loadbalancer_id self._update_l7rule_change(context, rule)
listener_id = rule.policy.listener_id
vs_client = self.core_plugin.nsxlib.load_balancer.virtual_server
rule_client = self.core_plugin.nsxlib.load_balancer.rule
tags = lb_utils.get_tags(self.core_plugin, rule.id,
lb_const.LB_L7RULE_TYPE,
rule.tenant_id, context.project_name)
binding = nsx_db.get_nsx_lbaas_listener_binding(
context.session, lb_id, listener_id)
if not binding:
msg = _('Cannot find nsx lbaas binding for listener '
'%(listener_id)s') % {'listener_id': listener_id}
raise n_exc.BadRequest(resource='lbaas-l7rule-create', msg=msg)
vs_id = binding['lb_vs_id']
rule_body = self._convert_l7policy_to_lb_rule(context, rule)
try:
lb_rule = rule_client.create(tags=tags, **rule_body)
except nsxlib_exc.ManagerError:
self.lbv2_driver.l7rule.failed_completion(context, rule)
msg = _('Failed to create lb rule at NSX backend')
raise n_exc.BadRequest(resource='lbaas-l7rule-create',
msg=msg)
try:
vs_client.add_rule(vs_id, lb_rule['id'])
except nsxlib_exc.ManagerError:
self.lbv2_driver.l7rule.failed_completion(context, rule)
msg = (_('Failed to add rule %(rule)s to virtual server '
'%(vs)s at NSX backend') %
{'rule': lb_rule['id'], 'vs': vs_id})
raise n_exc.BadRequest(resource='lbaas-l7rule-create',
msg=msg)
nsx_db.add_nsx_lbaas_l7rule_binding(
context.session, lb_id, rule.l7policy_id, rule.id,
lb_rule['id'], vs_id)
self.lbv2_driver.l7rule.successful_completion(context, rule)
@log_helpers.log_method_call @log_helpers.log_method_call
def update(self, context, old_rule, new_rule): def update(self, context, old_rule, new_rule):
self.lbv2_driver.l7rule.successful_completion(context, new_rule) self._update_l7rule_change(context, new_rule)
@log_helpers.log_method_call @log_helpers.log_method_call
def delete(self, context, rule): def delete(self, context, rule):
lb_id = rule.policy.listener.loadbalancer_id self._update_l7rule_change(context, rule, delete=True)
vs_client = self.core_plugin.nsxlib.load_balancer.virtual_server
rule_client = self.core_plugin.nsxlib.load_balancer.rule
binding = nsx_db.get_nsx_lbaas_l7rule_binding(
context.session, lb_id, rule.l7policy_id, rule.id)
if binding:
vs_id = binding['lb_vs_id']
rule_id = binding['lb_rule_id']
try:
vs_client.remove_rule(vs_id, rule_id)
except nsx_exc.NsxResourceNotFound:
msg = (_("virtual server cannot be found on nsx: %(vs)s") %
{'vs': vs_id})
raise n_exc.BadRequest(resource='lbaas-l7rule-delete',
msg=msg)
except nsxlib_exc.ManagerError:
self.lbv2_driver.l7rule.failed_completion(context,
rule)
msg = (_('Failed to update rule %(rule)s on virtual server '
'%(vs)s') % {'rule': rule_id, 'vs': vs_id})
raise n_exc.BadRequest(resource='lbaas-l7rule-delete',
msg=msg)
try:
rule_client.delete(rule_id)
except nsx_exc.NsxResourceNotFound:
LOG.warning("LB rule cannot be found on nsx: %(rule)s",
{'rule': rule_id})
except nsxlib_exc.ManagerError:
self.lbv2_driver.l7rule.failed_completion(context,
rule)
msg = (_('Failed to delete lb rule: %(rule)s') %
{'rule': rule.id})
raise n_exc.BadRequest(resource='lbaas-l7rule-delete',
msg=msg)
nsx_db.delete_nsx_lbaas_l7rule_binding(
context.session, lb_id, rule.l7policy_id, rule.id)
self.lbv2_driver.l7rule.successful_completion(context, rule,
delete=True)

View File

@ -18,6 +18,7 @@ from neutron.services.flavors import flavors_plugin
from neutron_lib import exceptions as n_exc from neutron_lib import exceptions as n_exc
from vmware_nsx._i18n import _ from vmware_nsx._i18n import _
from vmware_nsx.db import db as nsx_db
from vmware_nsx.services.lbaas import lb_const from vmware_nsx.services.lbaas import lb_const
from vmware_nsxlib.v3 import utils from vmware_nsxlib.v3 import utils
@ -92,3 +93,88 @@ def validate_lb_subnet(context, plugin, subnet_id):
return True return True
else: else:
return False return False
def get_rule_match_conditions(policy):
match_conditions = []
# values in rule have already been validated in LBaaS API,
# we won't need to valid anymore in driver, and just get
# the LB rule mapping from the dict.
for rule in policy.rules:
match_type = lb_const.LB_RULE_MATCH_TYPE[rule.compare_type]
if rule.type == lb_const.L7_RULE_TYPE_COOKIE:
header_value = rule.key + '=' + rule.value
match_conditions.append(
{'type': 'LbHttpRequestHeaderCondition',
'match_type': match_type,
'header_name': 'Cookie',
'header_value': header_value})
elif rule.type == lb_const.L7_RULE_TYPE_FILE_TYPE:
match_conditions.append(
{'type': 'LbHttpRequestUriCondition',
'match_type': match_type,
'uri': '*.' + rule.value})
elif rule.type == lb_const.L7_RULE_TYPE_HEADER:
match_conditions.append(
{'type': 'LbHttpRequestHeaderCondition',
'match_type': match_type,
'header_name': rule.key,
'header_value': rule.value})
elif rule.type == lb_const.L7_RULE_TYPE_HOST_NAME:
match_conditions.append(
{'type': 'LbHttpRequestHeaderCondition',
'match_type': match_type,
'header_name': 'Host',
'header_value': rule.value})
elif rule.type == lb_const.L7_RULE_TYPE_PATH:
match_conditions.append(
{'type': 'LbHttpRequestUriCondition',
'match_type': match_type,
'uri': rule.value})
else:
msg = (_('l7rule type %(type)s is not supported in LBaaS') %
{'type': rule.type})
raise n_exc.BadRequest(resource='lbaas-l7rule', msg=msg)
return match_conditions
def get_rule_actions(context, l7policy):
lb_id = l7policy.listener.loadbalancer_id
if l7policy.action == lb_const.L7_POLICY_ACTION_REDIRECT_TO_POOL:
pool_binding = nsx_db.get_nsx_lbaas_pool_binding(
context.session, lb_id, l7policy.redirect_pool_id)
if pool_binding:
lb_pool_id = pool_binding['lb_pool_id']
actions = [{'type': lb_const.LB_SELECT_POOL_ACTION,
'pool_id': lb_pool_id}]
else:
msg = _('Failed to get LB pool binding from nsx db')
raise n_exc.BadRequest(resource='lbaas-l7rule-create',
msg=msg)
elif l7policy.action == lb_const.L7_POLICY_ACTION_REDIRECT_TO_URL:
actions = [{'type': lb_const.LB_HTTP_REDIRECT_ACTION,
'redirect_status': lb_const.LB_HTTP_REDIRECT_STATUS,
'redirect_url': l7policy.redirect_url}]
elif l7policy.action == lb_const.L7_POLICY_ACTION_REJECT:
actions = [{'type': lb_const.LB_REJECT_ACTION,
'reply_status': lb_const.LB_HTTP_REJECT_STATUS}]
else:
msg = (_('Invalid l7policy action: %(action)s') %
{'action': l7policy.action})
raise n_exc.BadRequest(resource='lbaas-l7rule-create',
msg=msg)
return actions
def convert_l7policy_to_lb_rule(context, policy):
return {
'match_conditions': get_rule_match_conditions(policy),
'actions': get_rule_actions(context, policy),
'phase': lb_const.LB_RULE_HTTP_FORWARDING,
'match_strategy': 'ALL'
}
def remove_rule_from_policy(rule):
l7rules = rule.policy.rules
rule.policy.rules = [r for r in l7rules if r.id != rule.id]

View File

@ -98,11 +98,9 @@ HM_BINDING = {'loadbalancer_id': LB_ID,
L7POLICY_ID = 'l7policy-xxx' L7POLICY_ID = 'l7policy-xxx'
LB_RULE_ID = 'lb-rule-xx' LB_RULE_ID = 'lb-rule-xx'
L7RULE_ID = 'l7rule-111' L7RULE_ID = 'l7rule-111'
L7RULE_BINDING = {'loadbalancer_id': LB_ID, L7POLICY_BINDING = {'l7policy_id': L7POLICY_ID,
'policy_id': L7POLICY_ID, 'lb_vs_id': LB_VS_ID,
'rule_id': L7RULE_ID, 'lb_rule_id': LB_RULE_ID}
'lb_vs_id': LB_VS_ID,
'lb_rule_id': LB_RULE_ID}
FAKE_CERT = {'id': 'cert-xyz'} FAKE_CERT = {'id': 'cert-xyz'}
@ -152,7 +150,7 @@ class BaseTestEdgeLbaasV2(base.BaseTestCase):
description='policy-desc', description='policy-desc',
listener_id=LISTENER_ID, listener_id=LISTENER_ID,
action='REDIRECT_TO_POOL', action='REDIRECT_TO_POOL',
redirect_pool_id=LB_POOL_ID, redirect_pool_id=POOL_ID,
listener=self.listener, listener=self.listener,
position=1) position=1)
self.l7rule = lb_models.L7Rule(L7RULE_ID, LB_TENANT_ID, self.l7rule = lb_models.L7Rule(L7RULE_ID, LB_TENANT_ID,
@ -660,11 +658,33 @@ class TestEdgeLbaasV2L7Policy(BaseTestEdgeLbaasV2):
return 'l7policy' return 'l7policy'
def test_create(self): def test_create(self):
self.edge_driver.l7policy.create(self.context, self.l7policy) with mock.patch.object(nsx_db, 'get_nsx_lbaas_listener_binding'
mock_successful_completion = ( ) as mock_get_listener_binding, \
self.lbv2_driver.l7policy.successful_completion) mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding'
mock_successful_completion.assert_called_with( ) as mock_get_pool_binding, \
self.context, self.l7policy, delete=False) mock.patch.object(self.rule_client, 'create'
) as mock_create_rule, \
mock.patch.object(self.vs_client, 'get'
) as mock_get_virtual_server, \
mock.patch.object(self.vs_client, 'update'
) as mock_update_virtual_server, \
mock.patch.object(nsx_db, 'add_nsx_lbaas_l7policy_binding'
) as mock_add_l7policy_binding:
mock_get_listener_binding.return_value = LISTENER_BINDING
mock_get_pool_binding.return_value = POOL_BINDING
mock_create_rule.return_value = {'id': LB_RULE_ID}
mock_get_virtual_server.return_value = {'id': LB_VS_ID}
self.edge_driver.l7policy.create(self.context, self.l7policy)
mock_update_virtual_server.assert_called_with(
LB_VS_ID, rule_ids=[LB_RULE_ID])
mock_add_l7policy_binding.assert_called_with(
self.context.session, L7POLICY_ID, LB_RULE_ID, LB_VS_ID)
mock_successful_completion = (
self.lbv2_driver.l7policy.successful_completion)
mock_successful_completion.assert_called_with(self.context,
self.l7policy)
def test_update(self): def test_update(self):
new_l7policy = lb_models.L7Policy(L7POLICY_ID, LB_TENANT_ID, new_l7policy = lb_models.L7Policy(L7POLICY_ID, LB_TENANT_ID,
@ -672,20 +692,66 @@ class TestEdgeLbaasV2L7Policy(BaseTestEdgeLbaasV2):
listener_id=LISTENER_ID, listener_id=LISTENER_ID,
action='REJECT', action='REJECT',
listener=self.listener, listener=self.listener,
position=1) position=2)
self.edge_driver.l7policy.update(self.context, self.l7policy, vs_with_rules = {
new_l7policy) 'id': LB_VS_ID,
mock_successful_completion = ( 'rule_ids': [LB_RULE_ID, 'abc', 'xyz']
self.lbv2_driver.l7policy.successful_completion) }
mock_successful_completion.assert_called_with( rule_body = {
self.context, new_l7policy, delete=False) 'match_conditions': [],
'actions': [{
'type': 'LbHttpRejectAction',
'reply_status': '403'}],
'phase': 'HTTP_FORWARDING',
'match_strategy': 'ALL'
}
with mock.patch.object(nsx_db, 'get_nsx_lbaas_l7policy_binding'
) as mock_get_l7policy_binding, \
mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding'
) as mock_get_pool_binding, \
mock.patch.object(self.rule_client, 'update'
) as mock_update_rule, \
mock.patch.object(self.vs_client, 'get'
) as mock_get_virtual_server, \
mock.patch.object(self.vs_client, 'update'
) as mock_update_virtual_server:
mock_get_l7policy_binding.return_value = L7POLICY_BINDING
mock_get_pool_binding.return_value = POOL_BINDING
mock_get_virtual_server.return_value = vs_with_rules
self.edge_driver.l7policy.update(self.context, self.l7policy,
new_l7policy)
mock_update_rule.assert_called_with(LB_RULE_ID,
**rule_body)
mock_update_virtual_server.assert_called_with(
LB_VS_ID, rule_ids=['abc', LB_RULE_ID, 'xyz'])
mock_successful_completion = (
self.lbv2_driver.l7policy.successful_completion)
mock_successful_completion.assert_called_with(self.context,
new_l7policy)
def test_delete(self): def test_delete(self):
self.edge_driver.l7policy.delete(self.context, self.l7policy) with mock.patch.object(nsx_db, 'get_nsx_lbaas_l7policy_binding'
mock_successful_completion = ( ) as mock_get_l7policy_binding, \
self.lbv2_driver.l7policy.successful_completion) mock.patch.object(self.vs_client, 'remove_rule'
mock_successful_completion.assert_called_with( ) as mock_vs_remove_rule, \
self.context, self.l7policy, delete=True) mock.patch.object(self.rule_client, 'delete'
) as mock_delete_rule, \
mock.patch.object(nsx_db, 'delete_nsx_lbaas_l7policy_binding'
) as mock_delete_l7policy_binding:
mock_get_l7policy_binding.return_value = L7POLICY_BINDING
self.edge_driver.l7policy.delete(self.context, self.l7policy)
mock_vs_remove_rule.assert_called_with(LB_VS_ID, LB_RULE_ID)
mock_delete_rule.assert_called_with(LB_RULE_ID)
mock_delete_l7policy_binding.assert_called_with(
self.context.session, L7POLICY_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): class TestEdgeLbaasV2L7Rule(BaseTestEdgeLbaasV2):
@ -697,31 +763,39 @@ class TestEdgeLbaasV2L7Rule(BaseTestEdgeLbaasV2):
return 'l7rule' return 'l7rule'
def test_create(self): def test_create(self):
with mock.patch.object(nsx_db, 'get_nsx_lbaas_listener_binding', self.l7policy.rules = [self.l7rule]
) as mock_get_listnener_binding, \ create_rule_body = {
mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding', 'match_conditions': [{
'type': 'LbHttpRequestHeaderCondition',
'match_type': 'EQUALS',
'header_name': self.l7rule.key,
'header_value': self.l7rule.value}],
'actions': [{
'type': 'LbSelectPoolAction',
'pool_id': LB_POOL_ID}],
'phase': 'HTTP_FORWARDING',
'match_strategy': 'ALL'
}
with mock.patch.object(nsx_db, 'get_nsx_lbaas_l7policy_binding'
) as mock_get_l7policy_binding, \
mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding'
) as mock_get_pool_binding, \ ) as mock_get_pool_binding, \
mock.patch.object(self.rule_client, 'create', mock.patch.object(self.rule_client, 'update'
) as mock_create_rule, \ ) as mock_update_rule:
mock.patch.object(self.vs_client, 'add_rule', mock_get_l7policy_binding.return_value = L7POLICY_BINDING
) as mock_add_rule, \
mock.patch.object(nsx_db, 'add_nsx_lbaas_l7rule_binding',
) as mock_add_l7rule_binding:
mock_get_listnener_binding.return_value = LISTENER_BINDING
mock_get_pool_binding.return_value = POOL_BINDING mock_get_pool_binding.return_value = POOL_BINDING
mock_create_rule.return_value = {'id': LB_RULE_ID}
self.edge_driver.l7rule.create(self.context, self.l7rule) self.edge_driver.l7rule.create(self.context, self.l7rule)
mock_add_rule.assert_called_with(LB_VS_ID, LB_RULE_ID) mock_update_rule.assert_called_with(LB_RULE_ID,
mock_add_l7rule_binding.assert_called_with( **create_rule_body)
self.context.session, LB_ID, L7POLICY_ID, L7RULE_ID,
LB_RULE_ID, LB_VS_ID)
mock_successful_completion = ( mock_successful_completion = (
self.lbv2_driver.l7rule.successful_completion) self.lbv2_driver.l7rule.successful_completion)
mock_successful_completion.assert_called_with(self.context, mock_successful_completion.assert_called_with(self.context,
self.l7rule) self.l7rule,
delete=False)
def test_update(self): def test_update(self):
new_l7rule = lb_models.L7Rule(L7RULE_ID, LB_TENANT_ID, new_l7rule = lb_models.L7Rule(L7RULE_ID, LB_TENANT_ID,
@ -732,29 +806,65 @@ class TestEdgeLbaasV2L7Rule(BaseTestEdgeLbaasV2):
key='cookie1', key='cookie1',
value='xxxxx', value='xxxxx',
policy=self.l7policy) policy=self.l7policy)
self.edge_driver.l7rule.update(self.context, self.l7rule, new_l7rule) self.l7policy.rules = [new_l7rule]
mock_successful_completion = ( update_rule_body = {
self.lbv2_driver.l7rule.successful_completion) 'match_conditions': [{
mock_successful_completion.assert_called_with( 'type': 'LbHttpRequestHeaderCondition',
self.context, new_l7rule) 'match_type': 'STARTS_WITH',
'header_name': 'Cookie',
'header_value': 'cookie1=xxxxx'}],
'actions': [{
'type': 'LbSelectPoolAction',
'pool_id': LB_POOL_ID}],
'phase': 'HTTP_FORWARDING',
'match_strategy': 'ALL'
}
def test_delete_pool_without_members(self): with mock.patch.object(nsx_db, 'get_nsx_lbaas_l7policy_binding'
with mock.patch.object(nsx_db, 'get_nsx_lbaas_l7rule_binding', ) as mock_get_l7policy_binding, \
) as mock_get_l7rule_binding, \ mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding'
mock.patch.object(self.vs_client, 'remove_rule' ) as mock_get_pool_binding, \
) as mock_remove_rule, \ mock.patch.object(self.rule_client, 'update'
mock.patch.object(self.rule_client, 'delete', ) as mock_update_rule:
) as mock_delete_rule, \ mock_get_l7policy_binding.return_value = L7POLICY_BINDING
mock.patch.object(nsx_db, 'delete_nsx_lbaas_l7rule_binding', mock_get_pool_binding.return_value = POOL_BINDING
) as mock_delete_l7rule_binding:
mock_get_l7rule_binding.return_value = L7RULE_BINDING self.edge_driver.l7rule.update(self.context, self.l7rule,
new_l7rule)
mock_update_rule.assert_called_with(LB_RULE_ID,
**update_rule_body)
mock_successful_completion = (
self.lbv2_driver.l7rule.successful_completion)
mock_successful_completion.assert_called_with(self.context,
new_l7rule,
delete=False)
def test_delete(self):
self.l7policy.rules = [self.l7rule]
delete_rule_body = {
'match_conditions': [],
'actions': [{
'type': 'LbSelectPoolAction',
'pool_id': LB_POOL_ID}],
'phase': 'HTTP_FORWARDING',
'match_strategy': 'ALL'
}
with mock.patch.object(nsx_db, 'get_nsx_lbaas_l7policy_binding'
) as mock_get_l7policy_binding, \
mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding'
) as mock_get_pool_binding, \
mock.patch.object(self.rule_client, 'update'
) as mock_update_rule:
mock_get_l7policy_binding.return_value = L7POLICY_BINDING
mock_get_pool_binding.return_value = POOL_BINDING
self.edge_driver.l7rule.delete(self.context, self.l7rule) self.edge_driver.l7rule.delete(self.context, self.l7rule)
mock_remove_rule.assert_called_with(LB_VS_ID, LB_RULE_ID) mock_update_rule.assert_called_with(LB_RULE_ID,
mock_delete_rule.assert_called_with(LB_RULE_ID) **delete_rule_body)
mock_delete_l7rule_binding.assert_called_with(
self.context.session, LB_ID, L7POLICY_ID, L7RULE_ID)
mock_successful_completion = ( mock_successful_completion = (
self.lbv2_driver.l7rule.successful_completion) self.lbv2_driver.l7rule.successful_completion)