T2P migration

This patch will allow moving neutron from using the nsx_v3 plugin to the nsx_p plugin.
This includes:
- admin utility to move all resources to the policy api:
  nsxadmin -r nsx-migrate-t2p -o import (--verbose)
  This utility will:
  -- Migrate all neutron used & created resource using the nsx migration api
  -- roll back all resources in case it failed
  -- post migration fix some of the policy resources to better match the expectation
     of the policy plugin
- admin utility that will cleanup left overs in the nsx_v3 db:
  nsxadmin -r nsx-migrate-t2p -o clean-all
  (can be used, but everything should work without calling it as well)
- Some minor changes to the policy plugin and drivers to allow it to handle migrated resource
  which are a bit different than those created with the policy plugin
  -- Delete DHCP server config once a migrated network is deleted
  -- Update LB L7 rules by their name suffix as their full display name is unknown

Change-Id: Ic17e0de1f4b2a2d95afa61ce33ffb0bc9e667b89
This commit is contained in:
Adit Sarfaty 2019-11-03 14:55:45 +02:00
parent bc54e93478
commit 0bad4876dc
16 changed files with 1608 additions and 36 deletions

View File

@ -1,7 +1,7 @@
[run] [run]
branch = True branch = True
source = vmware_nsx source = vmware_nsx
omit = vmware_nsx/tests/*,vmware_nsx/*dvs*,vmware_nsx/api_replay/*,vmware_nsx/dhcp_meta/*,vmware_nsx/nsxlib/*,vmware_nsx/*lsn*,vmware_nsx/*tv*,vmware_nsx/api_client/*,vmware_nsx/common/profile*,vmware_nsx/shell/nsx_instance_if_migrate*,vmware_nsx/plugins/nsx_v/vshield/vcns.*,vmware_nsx/db/migration/alembic_migrations/* omit = vmware_nsx/tests/*,vmware_nsx/*dvs*,vmware_nsx/api_replay/*,vmware_nsx/dhcp_meta/*,vmware_nsx/nsxlib/*,vmware_nsx/*lsn*,vmware_nsx/*tv*,vmware_nsx/api_client/*,vmware_nsx/common/profile*,vmware_nsx/shell/nsx_instance_if_migrate*,vmware_nsx/plugins/nsx_v/vshield/vcns.*,vmware_nsx/db/migration/alembic_migrations/*,vmware_nsx/shell/admin/plugins/nsxv3/resources/migration*
[report] [report]
ignore_errors = True ignore_errors = True

View File

@ -592,6 +592,17 @@ Config
nsxadmin -r config -o validate nsxadmin -r config -o validate
T2P migration
~~~~~~~~~~~~~
- Migrate NSX resources and neutron DB from NSX-T (MP) to Policy::
nsxadmin -r nsx-migrate-t2p -o import (--verbose)
- Delete DB tables related to the MP plugin after migration::
nsxadmin -r nsx-migrate-t2p -o clean-all
NSXtvd Plugin NSXtvd Plugin
------------- -------------
@ -643,6 +654,10 @@ NSX Policy Plugin
- Update tags on a loadbalancer service - Update tags on a loadbalancer service
nsxadmin -r lb-services -o nsx-update-tags nsxadmin -r lb-services -o nsx-update-tags
- Delete DB tables related to the MP plugin after migration from MP plugin to policy::
nsxadmin -r nsx-migrate-t2p -o clean-all
Client Certificate Client Certificate
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~

View File

@ -28,6 +28,7 @@ oslo.utils==3.33.0
oslo.vmware==2.17.0 oslo.vmware==2.17.0
oslotest==3.2.0 oslotest==3.2.0
osc-lib==1.14.0 osc-lib==1.14.0
paramiko==2.4.0
pbr==4.0.0 pbr==4.0.0
pika-pool==0.1.3 pika-pool==0.1.3
pika==0.10.0 pika==0.10.0

View File

@ -26,6 +26,7 @@ oslo.serialization>=2.28.1 # Apache-2.0
oslo.service>=1.31.0 # Apache-2.0 oslo.service>=1.31.0 # Apache-2.0
oslo.utils>=3.33.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0
oslo.vmware>=2.17.0 # Apache-2.0 oslo.vmware>=2.17.0 # Apache-2.0
paramiko>=2.4.0 # LGPLv2.1+
PrettyTable<0.8,>=0.7.2 # BSD PrettyTable<0.8,>=0.7.2 # BSD
tooz>=1.58.0 # Apache-2.0 tooz>=1.58.0 # Apache-2.0
decorator>=4.4.1 # BSD decorator>=4.4.1 # BSD

View File

@ -104,6 +104,11 @@ def is_nsx_version_3_0_0(nsx_version):
version.LooseVersion(v3_const.NSX_VERSION_3_0_0)) version.LooseVersion(v3_const.NSX_VERSION_3_0_0))
def is_nsx_version_3_1_0(nsx_version):
return (version.LooseVersion(nsx_version) >=
version.LooseVersion(v3_const.NSX_VERSION_3_1_0))
def is_nsxv_version_6_2(nsx_version): def is_nsxv_version_6_2(nsx_version):
return (version.LooseVersion(nsx_version) >= return (version.LooseVersion(nsx_version) >=
version.LooseVersion('6.2')) version.LooseVersion('6.2'))

View File

@ -159,35 +159,44 @@ def get_nsxlib_wrapper(nsx_username=None, nsx_password=None, basic_auth=False,
def get_nsxpolicy_wrapper(nsx_username=None, nsx_password=None, def get_nsxpolicy_wrapper(nsx_username=None, nsx_password=None,
basic_auth=False): basic_auth=False, conf_path=None):
if not conf_path:
conf_path = cfg.CONF.nsx_p
client_cert_provider = None client_cert_provider = None
if not basic_auth: if not basic_auth:
# if basic auth requested, dont use cert file even if provided # if basic auth requested, dont use cert file even if provided
client_cert_provider = get_client_cert_provider( client_cert_provider = get_client_cert_provider(
conf_path=cfg.CONF.nsx_p) conf_path=conf_path)
nsxlib_config = config.NsxLibConfig( nsxlib_config = config.NsxLibConfig(
username=nsx_username or cfg.CONF.nsx_p.nsx_api_user, username=nsx_username or conf_path.nsx_api_user,
password=nsx_password or cfg.CONF.nsx_p.nsx_api_password, password=nsx_password or conf_path.nsx_api_password,
client_cert_provider=client_cert_provider, client_cert_provider=client_cert_provider,
retries=cfg.CONF.nsx_p.http_retries, retries=conf_path.http_retries,
insecure=cfg.CONF.nsx_p.insecure, insecure=conf_path.insecure,
ca_file=cfg.CONF.nsx_p.ca_file, ca_file=conf_path.ca_file,
concurrent_connections=cfg.CONF.nsx_p.concurrent_connections, concurrent_connections=conf_path.concurrent_connections,
http_timeout=cfg.CONF.nsx_p.http_timeout, http_timeout=conf_path.http_timeout,
http_read_timeout=cfg.CONF.nsx_p.http_read_timeout, http_read_timeout=conf_path.http_read_timeout,
conn_idle_timeout=cfg.CONF.nsx_p.conn_idle_timeout, conn_idle_timeout=conf_path.conn_idle_timeout,
http_provider=None, http_provider=None,
max_attempts=cfg.CONF.nsx_p.retries, max_attempts=conf_path.retries,
nsx_api_managers=cfg.CONF.nsx_p.nsx_api_managers, nsx_api_managers=conf_path.nsx_api_managers,
plugin_scope=OS_NEUTRON_ID_SCOPE, plugin_scope=OS_NEUTRON_ID_SCOPE,
plugin_tag=NSX_NEUTRON_PLUGIN, plugin_tag=NSX_NEUTRON_PLUGIN,
plugin_ver=n_version.version_info.release_string(), plugin_ver=n_version.version_info.release_string(),
dns_nameservers=cfg.CONF.nsx_p.nameservers, dns_nameservers=conf_path.nameservers,
dns_domain=cfg.CONF.nsx_p.dns_domain, dns_domain=conf_path.dns_domain,
allow_passthrough=cfg.CONF.nsx_p.allow_passthrough, allow_passthrough=(conf_path.allow_passthrough
realization_max_attempts=cfg.CONF.nsx_p.realization_max_attempts, if hasattr(conf_path, 'allow_passthrough')
realization_wait_sec=cfg.CONF.nsx_p.realization_wait_sec) else False),
realization_max_attempts=(conf_path.realization_max_attempts
if hasattr(conf_path,
'realization_max_attempts')
else 50),
realization_wait_sec=(conf_path.realization_wait_sec
if hasattr(conf_path, 'realization_wait_sec')
else 1))
return policy.NsxPolicyLib(nsxlib_config) return policy.NsxPolicyLib(nsxlib_config)

View File

@ -822,13 +822,19 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
# MP MD proxy when this network is created. # MP MD proxy when this network is created.
# If not - the port will not be found, and it is ok. # If not - the port will not be found, and it is ok.
# Note(asarfaty): In the future this code can be removed. # Note(asarfaty): In the future this code can be removed.
if not is_external_net and cfg.CONF.nsx_p.allow_passthrough: # TODO(asarfaty): For migrated networks when the DB was not cleaned up
# This may actually delete a port the policy now control
if (not is_external_net and not is_nsx_net and
cfg.CONF.nsx_p.allow_passthrough):
self._delete_nsx_port_by_network(network_id) self._delete_nsx_port_by_network(network_id)
# Delete the network segment from the backend # Delete the network segment from the backend
if not is_external_net and not is_nsx_net: if not is_external_net and not is_nsx_net:
try: try:
self.nsxpolicy.segment.delete(network_id) self.nsxpolicy.segment.delete(network_id)
# In case of migrated network, a dhcp server config with
# the same id should also be deleted
self.nsxpolicy.dhcp_server_config.delete(network_id)
except nsx_lib_exc.ResourceNotFound: except nsx_lib_exc.ResourceNotFound:
# If the resource was not found on the backend do not worry # If the resource was not found on the backend do not worry
# about it. The conditions has already been logged, so there # about it. The conditions has already been logged, so there
@ -3921,7 +3927,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
rule_entry = self._create_security_group_backend_rule( rule_entry = self._create_security_group_backend_rule(
context, sg_id, rule, secgroup_logging, context, sg_id, rule, secgroup_logging,
is_provider_sg=is_provider_sg, is_provider_sg=is_provider_sg,
create_related_resource=False) create_related_resource=True)
backend_rules.append(rule_entry) backend_rules.append(rule_entry)
# Update the policy with all the rules. # Update the policy with all the rules.

View File

@ -52,6 +52,7 @@ class EdgeL7PolicyManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
vs_client = self.core_plugin.nsxpolicy.load_balancer.virtual_server vs_client = self.core_plugin.nsxpolicy.load_balancer.virtual_server
policy_name = utils.get_name_and_uuid(old_policy['name'] or 'policy', policy_name = utils.get_name_and_uuid(old_policy['name'] or 'policy',
old_policy['id']) old_policy['id'])
short_name = utils.get_name_short_uuid(old_policy['id'])
rule_body = lb_utils.convert_l7policy_to_lb_rule( rule_body = lb_utils.convert_l7policy_to_lb_rule(
self.core_plugin.nsxpolicy, new_policy) self.core_plugin.nsxpolicy, new_policy)
try: try:
@ -59,6 +60,7 @@ class EdgeL7PolicyManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
new_policy['listener_id'], new_policy['listener_id'],
policy_name, policy_name,
position=new_policy.get('position', 0) - 1, position=new_policy.get('position', 0) - 1,
compare_name_suffix=short_name,
**rule_body) **rule_body)
except Exception as e: except Exception as e:
@ -70,11 +72,11 @@ class EdgeL7PolicyManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
def delete(self, context, policy, completor): def delete(self, context, policy, completor):
vs_client = self.core_plugin.nsxpolicy.load_balancer.virtual_server vs_client = self.core_plugin.nsxpolicy.load_balancer.virtual_server
policy_name = utils.get_name_and_uuid(policy['name'] or 'policy', policy_name = utils.get_name_short_uuid(policy['id'])
policy['id'])
try: try:
vs_client.remove_lb_rule(policy['listener_id'], vs_client.remove_lb_rule(policy['listener_id'],
policy_name) policy_name,
check_name_suffix=True)
except nsxlib_exc.ResourceNotFound: except nsxlib_exc.ResourceNotFound:
pass pass
except nsxlib_exc.ManagerError: except nsxlib_exc.ManagerError:

View File

@ -29,6 +29,7 @@ class EdgeL7RuleManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
policy = rule['policy'] policy = rule['policy']
policy_name = utils.get_name_and_uuid(policy['name'] or 'policy', policy_name = utils.get_name_and_uuid(policy['name'] or 'policy',
policy['id']) policy['id'])
short_name = utils.get_name_short_uuid(policy['id'])
if delete: if delete:
lb_utils.remove_rule_from_policy(rule) lb_utils.remove_rule_from_policy(rule)
else: else:
@ -39,6 +40,7 @@ class EdgeL7RuleManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
vs_client.update_lb_rule(policy['listener_id'], vs_client.update_lb_rule(policy['listener_id'],
policy_name, policy_name,
position=policy.get('position', 0) - 1, position=policy.get('position', 0) - 1,
compare_name_suffix=short_name,
**rule_body) **rule_body)
except Exception as e: except Exception as e:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():

View File

@ -51,6 +51,7 @@ LB_ADVERTISEMENT = 'lb-advertisement'
RATE_LIMIT = 'rate-limit' RATE_LIMIT = 'rate-limit'
CLUSTER = 'cluster' CLUSTER = 'cluster'
ORPHANED_FIREWALL_SECTIONS = 'orphaned-firewall-sections' ORPHANED_FIREWALL_SECTIONS = 'orphaned-firewall-sections'
NSX_MIGRATE_T_P = 'nsx-migrate-t2p'
# NSXV only Resource Constants # NSXV only Resource Constants
EDGES = 'edges' EDGES = 'edges'

View File

@ -0,0 +1,31 @@
# Copyright 2020 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 neutron_lib.callbacks import registry
from vmware_nsx.shell.admin.plugins.common import constants
from vmware_nsx.shell.admin.plugins.common import utils as admin_utils
from vmware_nsx.shell.admin.plugins.nsxv3.resources import migration
from vmware_nsx.shell import resources as shell
@admin_utils.output_header
def cleanup_db_mappings(resource, event, trigger, **kwargs):
"""Delete all entries from nsx-t mapping tables in DB"""
return migration.cleanup_db_mappings(resource, event, trigger, **kwargs)
registry.subscribe(cleanup_db_mappings,
constants.NSX_MIGRATE_T_P,
shell.Operations.CLEAN_ALL.value)

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import logging
from oslo_config import cfg from oslo_config import cfg
from neutron.db import l3_dvr_db # noqa from neutron.db import l3_dvr_db # noqa
@ -19,7 +20,6 @@ from neutron import manager
from neutron_lib import context from neutron_lib import context
from neutron_lib.plugins import constants as const from neutron_lib.plugins import constants as const
from neutron_lib.plugins import directory from neutron_lib.plugins import directory
from oslo_log import log as logging
from vmware_nsx.common import config from vmware_nsx.common import config
from vmware_nsx.plugins.common_v3 import utils as v3_utils from vmware_nsx.plugins.common_v3 import utils as v3_utils
@ -41,16 +41,28 @@ def get_nsxp_client(nsx_username=None, nsx_password=None,
def get_connected_nsxpolicy(nsx_username=None, nsx_password=None, def get_connected_nsxpolicy(nsx_username=None, nsx_password=None,
use_basic_auth=False): use_basic_auth=False, conf_path=None,
verbose=False):
global _NSXPOLICY global _NSXPOLICY
# for non-default agruments, initiate new lib if not verbose:
# Suppress logs for nsxpolicy init
logging.disable(logging.INFO)
# for non-default arguments, initiate new lib
if nsx_username or use_basic_auth: if nsx_username or use_basic_auth:
if not verbose:
# Return logs to normal
logging.disable(logging.NOTSET)
return v3_utils.get_nsxpolicy_wrapper(nsx_username, return v3_utils.get_nsxpolicy_wrapper(nsx_username,
nsx_password, nsx_password,
use_basic_auth) use_basic_auth,
conf_path=conf_path)
if _NSXPOLICY is None: if _NSXPOLICY is None:
_NSXPOLICY = v3_utils.get_nsxpolicy_wrapper() _NSXPOLICY = v3_utils.get_nsxpolicy_wrapper(conf_path=conf_path)
if not verbose:
# Return logs to normal
logging.disable(logging.NOTSET)
return _NSXPOLICY return _NSXPOLICY
@ -76,13 +88,21 @@ def get_realization_info(resource, *realization_args):
class NsxPolicyPluginWrapper(plugin.NsxPolicyPlugin): class NsxPolicyPluginWrapper(plugin.NsxPolicyPlugin):
def __init__(self): def __init__(self, verbose=False):
if not verbose:
# Suppress logs for plugin init
logging.disable(logging.INFO)
# initialize the availability zones # initialize the availability zones
config.register_nsxp_azs(cfg.CONF, cfg.CONF.nsx_p.availability_zones) config.register_nsxp_azs(cfg.CONF, cfg.CONF.nsx_p.availability_zones)
super(NsxPolicyPluginWrapper, self).__init__() super(NsxPolicyPluginWrapper, self).__init__()
self.context = context.get_admin_context() self.context = context.get_admin_context()
admin_utils._init_plugin_mock_quota() admin_utils._init_plugin_mock_quota()
if not verbose:
# Return logs to normal
logging.disable(logging.NOTSET)
def __enter__(self): def __enter__(self):
directory.add_plugin(const.CORE, self) directory.add_plugin(const.CORE, self)
return self return self

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import logging
from oslo_config import cfg from oslo_config import cfg
@ -46,17 +47,29 @@ def get_nsxv3_client(nsx_username=None, nsx_password=None,
def get_connected_nsxlib(nsx_username=None, nsx_password=None, def get_connected_nsxlib(nsx_username=None, nsx_password=None,
use_basic_auth=False, use_basic_auth=False,
plugin_conf=None): plugin_conf=None,
verbose=False):
global _NSXLIB global _NSXLIB
# for non-default agruments, initiate new lib if not verbose:
# Suppress logs for nsxlib init
logging.disable(logging.INFO)
# for non-default arguments, initiate new lib
if nsx_username or use_basic_auth: if nsx_username or use_basic_auth:
if not verbose:
# Return logs to normal
logging.disable(logging.NOTSET)
return v3_utils.get_nsxlib_wrapper(nsx_username, return v3_utils.get_nsxlib_wrapper(nsx_username,
nsx_password, nsx_password,
use_basic_auth, use_basic_auth,
plugin_conf) plugin_conf)
if _NSXLIB is None: if _NSXLIB is None:
_NSXLIB = v3_utils.get_nsxlib_wrapper(plugin_conf=plugin_conf) _NSXLIB = v3_utils.get_nsxlib_wrapper(plugin_conf=plugin_conf)
if not verbose:
# Return logs to normal
logging.disable(logging.NOTSET)
return _NSXLIB return _NSXLIB
@ -117,13 +130,21 @@ class NeutronDbClient(db_base_plugin_v2.NeutronDbPluginV2):
class NsxV3PluginWrapper(plugin.NsxV3Plugin): class NsxV3PluginWrapper(plugin.NsxV3Plugin):
def __init__(self): def __init__(self, verbose=False):
if not verbose:
# Suppress logs for plugin init
logging.disable(logging.INFO)
# initialize the availability zones # initialize the availability zones
config.register_nsxv3_azs(cfg.CONF, cfg.CONF.nsx_v3.availability_zones) config.register_nsxv3_azs(cfg.CONF, cfg.CONF.nsx_v3.availability_zones)
super(NsxV3PluginWrapper, self).__init__() super(NsxV3PluginWrapper, self).__init__()
self.context = context.get_admin_context() self.context = context.get_admin_context()
admin_utils._init_plugin_mock_quota() admin_utils._init_plugin_mock_quota()
if not verbose:
# Return logs to normal
logging.disable(logging.NOTSET)
def __enter__(self): def __enter__(self):
directory.add_plugin(const.CORE, self) directory.add_plugin(const.CORE, self)
return self return self
@ -131,6 +152,10 @@ class NsxV3PluginWrapper(plugin.NsxV3Plugin):
def __exit__(self, exc_type, exc_value, traceback): def __exit__(self, exc_type, exc_value, traceback):
directory.add_plugin(const.CORE, None) directory.add_plugin(const.CORE, None)
def _cleanup_duplicates(self, ns_group_id, section_id):
# Do not remove DFW sections during dummy plugin initialization
pass
def _init_fwaas_plugin(self, provider, callbacks_class, plugin_callbacks): def _init_fwaas_plugin(self, provider, callbacks_class, plugin_callbacks):
fwaas_plugin_class = manager.NeutronManager.load_class_for_provider( fwaas_plugin_class = manager.NeutronManager.load_class_for_provider(
'neutron.service_plugins', provider) 'neutron.service_plugins', provider)

View File

@ -154,7 +154,10 @@ nsxv3_resources = {
constants.LB_ADVERTISEMENT: Resource(constants.LB_ADVERTISEMENT, constants.LB_ADVERTISEMENT: Resource(constants.LB_ADVERTISEMENT,
[Operations.NSX_UPDATE.value]), [Operations.NSX_UPDATE.value]),
constants.CLUSTER: Resource(constants.CLUSTER, constants.CLUSTER: Resource(constants.CLUSTER,
[Operations.SHOW.value]) [Operations.SHOW.value]),
constants.NSX_MIGRATE_T_P: Resource(constants.NSX_MIGRATE_T_P,
[Operations.IMPORT.value,
Operations.CLEAN_ALL.value]),
} }
# Add supported NSX-V resources in this dictionary # Add supported NSX-V resources in this dictionary
@ -276,6 +279,8 @@ nsxp_resources = {
Operations.NSX_LIST.value]), Operations.NSX_LIST.value]),
constants.SYSTEM: Resource(constants.SYSTEM, constants.SYSTEM: Resource(constants.SYSTEM,
[Operations.SET.value]), [Operations.SET.value]),
constants.NSX_MIGRATE_T_P: Resource(constants.NSX_MIGRATE_T_P,
[Operations.CLEAN_ALL.value]),
} }
nsxv3_resources_names = list(nsxv3_resources.keys()) nsxv3_resources_names = list(nsxv3_resources.keys())

View File

@ -1893,7 +1893,8 @@ class TestEdgeLbaasV2L7Policy(BaseTestEdgeLbaasV2):
) as mock_vs_remove_rule: ) as mock_vs_remove_rule:
self.edge_driver.l7policy.delete( self.edge_driver.l7policy.delete(
self.context, self.l7policy_dict, self.completor) self.context, self.l7policy_dict, self.completor)
mock_vs_remove_rule.assert_called_with(LB_VS_ID, mock.ANY) mock_vs_remove_rule.assert_called_with(LB_VS_ID, mock.ANY,
check_name_suffix=True)
self.assertTrue(self.last_completor_called) self.assertTrue(self.last_completor_called)
self.assertTrue(self.last_completor_succees) self.assertTrue(self.last_completor_succees)
@ -1903,7 +1904,8 @@ class TestEdgeLbaasV2L7Policy(BaseTestEdgeLbaasV2):
) as mock_vs_remove_rule: ) as mock_vs_remove_rule:
self.edge_driver.l7policy.delete_cascade( self.edge_driver.l7policy.delete_cascade(
self.context, self.l7policy_dict, self.completor) self.context, self.l7policy_dict, self.completor)
mock_vs_remove_rule.assert_called_with(LB_VS_ID, mock.ANY) mock_vs_remove_rule.assert_called_with(LB_VS_ID, mock.ANY,
check_name_suffix=True)
self.assertTrue(self.last_completor_called) self.assertTrue(self.last_completor_called)
self.assertTrue(self.last_completor_succees) self.assertTrue(self.last_completor_succees)