
In vmware-nsx lbaas service folder, the base_mgr module can be shared between nsxv and nsxv3. Thus move it under lbaas folder so it can be use by nsxv3 as well. To have minimum impact on nsxv side, rename base to LoadbalancerBaseManager and subclass EdgeLoadbalancerBaseManager from it. Also, this patch fixes a few pep8 error. Change-Id: I994d39a5dbdb38e1b7805b2eec97e8ef7719f556
318 lines
14 KiB
Python
318 lines
14 KiB
Python
# 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 _
|
|
from vmware_nsx.common import locking
|
|
from vmware_nsx.db import nsxv_db
|
|
from vmware_nsx.services.lbaas import base_mgr
|
|
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
|
|
|
|
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('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
|
|
# -i for case insensitive path
|
|
path_type = 'path' + type_by_comp
|
|
rule_line = ('acl %(rule_id)s %(path_type)s '
|
|
'-i %(val)s' % {'rule_id': rule.id,
|
|
'path_type': path_type,
|
|
'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:
|
|
# return HTTP 403 response
|
|
action = 'http-request deny'
|
|
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)
|
|
|
|
def _update_app_rule_possition_in_virtual_server(self, edge_id, vse_id,
|
|
app_rule_id,
|
|
policy_position):
|
|
"""Move the new nsx application rule to another position"""
|
|
# Get the current virtual server configuration
|
|
vse = self.vcns.get_vip(edge_id, vse_id)[1]
|
|
|
|
# delete the policy (= application rule) from the list
|
|
if app_rule_id in vse['applicationRuleId']:
|
|
vse['applicationRuleId'].remove(app_rule_id)
|
|
|
|
# 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 _get_vse_id(self, context, pol):
|
|
lb_id = pol.listener.loadbalancer_id
|
|
list_id = pol.listener.id
|
|
listener_binding = nsxv_db.get_nsxv_lbaas_listener_binding(
|
|
context.session, lb_id, list_id)
|
|
if listener_binding:
|
|
return listener_binding['vse_id']
|
|
|
|
@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)
|
|
vse_id = self._get_vse_id(context, pol)
|
|
if vse_id:
|
|
self._add_app_rule_to_virtual_server(
|
|
edge_id, 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('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)
|
|
|
|
# if the position changed - update it too
|
|
if old_pol.position != new_pol.position:
|
|
vse_id = self._get_vse_id(context, new_pol)
|
|
if vse_id:
|
|
self._update_app_rule_possition_in_virtual_server(
|
|
edge_id, vse_id, app_rule_id, new_pol.position)
|
|
|
|
except Exception as e:
|
|
with excutils.save_and_reraise_exception():
|
|
self.lbv2_driver.l7policy.failed_completion(context, new_pol)
|
|
LOG.error('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
|
|
vse_id = self._get_vse_id(context, pol)
|
|
if 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('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)
|