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:
parent
751fa61919
commit
2256459aa1
5
releasenotes/notes/nsxv-lbaas-l7-704f748300d1a399.yaml
Normal file
5
releasenotes/notes/nsxv-lbaas-l7-704f748300d1a399.yaml
Normal 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.
|
@ -1 +1 @@
|
||||
dd9fe5a3a526
|
||||
01a33f93f5fd
|
||||
|
@ -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'))
|
@ -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):
|
||||
|
@ -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."""
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
|
284
vmware_nsx/services/lbaas/nsx_v/v2/l7policy_mgr.py
Normal file
284
vmware_nsx/services/lbaas/nsx_v/v2/l7policy_mgr.py
Normal 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)
|
67
vmware_nsx/services/lbaas/nsx_v/v2/l7rule_mgr.py
Normal file
67
vmware_nsx/services/lbaas/nsx_v/v2/l7rule_mgr.py
Normal 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)
|
@ -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)
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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}
|
||||
|
Loading…
Reference in New Issue
Block a user