Merge "[AIM][SFC] Add support for health check policy"
This commit is contained in:
commit
e231340d20
|
@ -227,6 +227,7 @@ try:
|
|||
from networking_sfc.db import flowclassifier_db
|
||||
from networking_sfc.db import sfc_db
|
||||
from networking_sfc.extensions import flowclassifier as fc_ext
|
||||
from networking_sfc.extensions import sfc as sfc_ext # noqa
|
||||
from networking_sfc.services.flowclassifier.common import (
|
||||
exceptions as fc_exc)
|
||||
from networking_sfc.services.flowclassifier import driver_manager as fc_mgr
|
||||
|
@ -247,6 +248,14 @@ try:
|
|||
('networking_sfc.extensions.'
|
||||
'flowclassifier')].SUPPORTED_L7_PARAMETERS.update(
|
||||
sfc_cts.AIM_FLC_L7_PARAMS)
|
||||
if 'sfc' in sys.modules:
|
||||
sys.modules['sfc'].RESOURCE_ATTRIBUTE_MAP['port_pair_groups'][
|
||||
'port_pair_group_parameters']['validate']['type:dict'].update(
|
||||
sfc_cts.AIM_PPG_PARAMS)
|
||||
if 'networking_sfc.extensions.sfc' in sys.modules:
|
||||
sys.modules['networking_sfc.extensions.sfc'].RESOURCE_ATTRIBUTE_MAP[
|
||||
'port_pair_groups']['port_pair_group_parameters']['validate'][
|
||||
'type:dict'].update(sfc_cts.AIM_PPG_PARAMS)
|
||||
# REVISIT(ivar): The following diff will fix flow classifier creation
|
||||
# method when using L7 parameters.
|
||||
# - key: L7Parameter(key, val)
|
||||
|
|
|
@ -15,10 +15,14 @@
|
|||
# could be dangerous for compatibility once they suddenly start supporting
|
||||
# them. We create our own resource type and make sure to modify it once
|
||||
# support is added to the SFC project.
|
||||
|
||||
from networking_sfc.extensions import flowclassifier
|
||||
|
||||
GBP_FLOW_CLASSIFIER = 'gbp_flowclassifier'
|
||||
GBP_PORT = 'gbp_port'
|
||||
LOGICAL_SRC_NET = 'logical_source_network'
|
||||
LOGICAL_DST_NET = 'logical_destination_network'
|
||||
HEALTHCHECK_POLICY = 'healthcheck_policy'
|
||||
AIM_FLC_L7_PARAMS = {
|
||||
LOGICAL_SRC_NET: {
|
||||
'allow_post': True, 'allow_put': False,
|
||||
|
@ -29,6 +33,17 @@ AIM_FLC_L7_PARAMS = {
|
|||
'is_visible': True, 'default': None,
|
||||
'validate': {'type:uuid_or_none': None}}
|
||||
}
|
||||
AIM_PPG_PARAMS = {
|
||||
'healthcheck_type': {
|
||||
'type:values': ['', 'icmp', 'tcp']
|
||||
},
|
||||
'healthcheck_frequency': {
|
||||
'type:non_negative': None
|
||||
},
|
||||
'healthcheck_tcp_port': {
|
||||
'convert_to': flowclassifier.normalize_port_value
|
||||
},
|
||||
}
|
||||
AIM_FLC_PARAMS = ['source_ip_prefix', 'destination_ip_prefix']
|
||||
GBP_NETWORK_VRF = 'gbp_network_vrf'
|
||||
GBP_NETWORK_EPG = 'gbp_network_epg'
|
||||
|
|
|
@ -333,6 +333,7 @@ class SfcAIMDriver(SfcAIMDriverBase):
|
|||
aim_ctx = aim_context.AimContext(session)
|
||||
# Create Logical device model, container for all the PPG port pairs.
|
||||
dc = self._get_ppg_device_cluster(session, ppg, tenant)
|
||||
mp = self._get_ppg_monitoring_policy(session, ppg, tenant)
|
||||
type, domain = self._get_ppg_domain(plugin_context, ppg)
|
||||
if not type and not domain:
|
||||
raise exceptions.PortPairsNoUniqueDomain(id=ppg['port_pairs'])
|
||||
|
@ -342,7 +343,23 @@ class SfcAIMDriver(SfcAIMDriverBase):
|
|||
else:
|
||||
dc.device_type = 'VIRTUAL'
|
||||
dc.vmm_domain = [{'type': type, 'name': domain}]
|
||||
internal_dci = aim_sg.DeviceClusterInterface(
|
||||
tenant_name=dc.tenant_name, device_cluster_name=dc.name,
|
||||
name=INGRESS, display_name=INGRESS)
|
||||
external_dci = aim_sg.DeviceClusterInterface(
|
||||
tenant_name=dc.tenant_name, device_cluster_name=dc.name,
|
||||
name=EGRESS, display_name=EGRESS)
|
||||
# Create 2 PBR rules per PPG, one per direction.
|
||||
ipbr = self._get_ppg_service_redirect_policy(session, ppg, INGRESS,
|
||||
tenant)
|
||||
epbr = self._get_ppg_service_redirect_policy(session, ppg, EGRESS,
|
||||
tenant)
|
||||
self.aim.create(aim_ctx, dc)
|
||||
if mp:
|
||||
for pbr in [ipbr, epbr]:
|
||||
pbr.monitoring_policy_tenant_name = mp.tenant_name
|
||||
pbr.monitoring_policy_name = mp.name
|
||||
self.aim.create(aim_ctx, mp)
|
||||
# For each port pair, create the corresponding Concrete Devices
|
||||
# (represented by the static path of each interface)
|
||||
ingress_cdis = []
|
||||
|
@ -357,8 +374,14 @@ class SfcAIMDriver(SfcAIMDriverBase):
|
|||
cd = aim_sg.ConcreteDevice(
|
||||
tenant_name=dc.tenant_name, device_cluster_name=dc.name,
|
||||
name=pp_id, display_name=pp_name)
|
||||
# Create ConcreteDevice
|
||||
self.aim.create(aim_ctx, cd)
|
||||
srhg = None
|
||||
if mp:
|
||||
# Create health groups
|
||||
srhg = aim_sg.ServiceRedirectHealthGroup(
|
||||
tenant_name=dc.tenant_name, name=pp_id,
|
||||
display_name=pp_name)
|
||||
self.aim.create(aim_ctx, srhg)
|
||||
for p, store in [(ingress_port, ingress_cdis),
|
||||
(egress_port, egress_cdis)]:
|
||||
p_id = self.name_mapper.port(session, p['id'])
|
||||
|
@ -377,39 +400,27 @@ class SfcAIMDriver(SfcAIMDriverBase):
|
|||
device_name=cd.name, name=p_id, display_name=p_name,
|
||||
path=path, host=host)
|
||||
cdi = self.aim.create(aim_ctx, cdi)
|
||||
store.append((cdi, encap, pp_name, p))
|
||||
# Ingress and Egress CDIs have the same length.
|
||||
# All the ingress devices must be load balanced, and so must the egress
|
||||
# (for reverse path). Create the proper PBR policies as well as
|
||||
# the Logical Interfaces (which see all the physical interfaces of a
|
||||
# specific direction as they were one).
|
||||
internal_dci = aim_sg.DeviceClusterInterface(
|
||||
tenant_name=dc.tenant_name, device_cluster_name=dc.name,
|
||||
name=INGRESS, display_name=INGRESS)
|
||||
external_dci = aim_sg.DeviceClusterInterface(
|
||||
tenant_name=dc.tenant_name, device_cluster_name=dc.name,
|
||||
name=EGRESS, display_name=EGRESS)
|
||||
# Create 2 PBR rules per PPG, one per direction.
|
||||
ipbr = self._get_ppg_service_redirect_policy(session, ppg, INGRESS,
|
||||
tenant)
|
||||
epbr = self._get_ppg_service_redirect_policy(session, ppg, EGRESS,
|
||||
tenant)
|
||||
store.append((cdi, encap, p, srhg, pp_id))
|
||||
|
||||
for i in range(len(ingress_cdis)):
|
||||
icdi, iencap, pp_name, iport = ingress_cdis[i]
|
||||
ecdi, eencap, _, eport = egress_cdis[i]
|
||||
icdi, iencap, iport, isrhg, pp_id = ingress_cdis[i]
|
||||
ecdi, eencap, eport, esrhg, _ = egress_cdis[i]
|
||||
internal_dci.encap = iencap
|
||||
external_dci.encap = eencap
|
||||
internal_dci.concrete_interfaces.append(icdi.dn)
|
||||
external_dci.concrete_interfaces.append(ecdi.dn)
|
||||
if iport['fixed_ips']:
|
||||
ipbr.destinations.append(
|
||||
{'ip': iport['fixed_ips'][0]['ip_address'],
|
||||
'mac': iport['mac_address'], 'name': pp_name})
|
||||
destination = {'ip': iport['fixed_ips'][0]['ip_address'],
|
||||
'mac': iport['mac_address'], 'name': pp_id}
|
||||
if isrhg:
|
||||
destination['redirect_health_group_dn'] = isrhg.dn
|
||||
ipbr.destinations.append(destination)
|
||||
if eport['fixed_ips']:
|
||||
epbr.destinations.append(
|
||||
{'ip': eport['fixed_ips'][0]['ip_address'],
|
||||
'mac': eport['mac_address'], 'name': pp_name})
|
||||
destination = {'ip': eport['fixed_ips'][0]['ip_address'],
|
||||
'mac': eport['mac_address'], 'name': pp_id}
|
||||
if esrhg:
|
||||
destination['redirect_health_group_dn'] = esrhg.dn
|
||||
epbr.destinations.append(destination)
|
||||
|
||||
self.aim.create(aim_ctx, internal_dci)
|
||||
self.aim.create(aim_ctx, external_dci)
|
||||
|
@ -421,7 +432,15 @@ class SfcAIMDriver(SfcAIMDriverBase):
|
|||
session = plugin_context.session
|
||||
aim_ctx = aim_context.AimContext(session)
|
||||
dc = self._get_ppg_device_cluster(session, ppg, tenant)
|
||||
mp = self._get_ppg_monitoring_policy(session, ppg, tenant)
|
||||
self.aim.delete(aim_ctx, dc, cascade=True)
|
||||
if mp:
|
||||
self.aim.delete(aim_ctx, mp, cascade=True)
|
||||
for pp in ppg['port_pairs']:
|
||||
pp_id = self.name_mapper.port_pair(session, pp)
|
||||
srhg = aim_sg.ServiceRedirectHealthGroup(
|
||||
tenant_name=dc.tenant_name, name=pp_id)
|
||||
self.aim.delete(aim_ctx, srhg)
|
||||
for prefix in [PBR_INGR_PREFIX, PBR_EGR_PREFIX]:
|
||||
pbr_id = self.name_mapper.port_pair_group(session, ppg['id'],
|
||||
prefix=prefix)
|
||||
|
@ -696,7 +715,11 @@ class SfcAIMDriver(SfcAIMDriverBase):
|
|||
|
||||
def _should_regenerate_ppg(self, context):
|
||||
attrs = ['port_pairs', 'name']
|
||||
return any(context.current[a] != context.original[a] for a in attrs)
|
||||
param_curr = context.current['port_pair_group_parameters']
|
||||
param_orig = context.original['port_pair_group_parameters']
|
||||
return (any(context.current[a] != context.original[a] for a in attrs)
|
||||
or any(param_curr[x] != param_orig[x] for x in
|
||||
sfc_cts.AIM_PPG_PARAMS.keys()))
|
||||
|
||||
def _should_regenerate_pc(self, context):
|
||||
attrs = ['flow_classifiers', 'port_pair_groups', 'name']
|
||||
|
@ -720,6 +743,24 @@ class SfcAIMDriver(SfcAIMDriverBase):
|
|||
return aim_sg.DeviceCluster(tenant_name=tenant_aid, name=ppg_aid,
|
||||
display_name=ppg_aname, managed=False)
|
||||
|
||||
def _get_ppg_monitoring_policy(self, session, ppg, tenant):
|
||||
tenant_aid = tenant
|
||||
ppg_aid = self.name_mapper.port_pair_group(session, ppg['id'])
|
||||
ppg_aname = aim_utils.sanitize_display_name(ppg['name'])
|
||||
result = None
|
||||
ppg_params = ppg.get('port_pair_group_parameters', {})
|
||||
monitoring_policy = ppg_params.get('healthcheck_type')
|
||||
if monitoring_policy:
|
||||
kwargs = {'type': monitoring_policy}
|
||||
if 'healthcheck_frequency' in ppg_params:
|
||||
kwargs['frequency'] = ppg_params['healthcheck_frequency']
|
||||
if 'healthcheck_tcp_port' in ppg_params:
|
||||
kwargs['tcp_port'] = ppg_params['healthcheck_tcp_port']
|
||||
result = aim_sg.ServiceRedirectMonitoringPolicy(
|
||||
tenant_name=tenant_aid, name=ppg_aid,
|
||||
display_name=ppg_aname, **kwargs)
|
||||
return result
|
||||
|
||||
def _get_ppg_domain(self, plugin_context, ppg):
|
||||
pp = self.sfc_plugin.get_port_pair(plugin_context,
|
||||
ppg['port_pairs'][0])
|
||||
|
|
|
@ -143,7 +143,8 @@ class TestAIMServiceFunctionChainingBase(test_aim_base.AIMBaseTestCase):
|
|||
self._plugin.mechanism_manager.mech_drivers['apic_aim'].obj)
|
||||
return self._aim_mech_driver
|
||||
|
||||
def _create_simple_ppg(self, pairs=2, leftn_id=None, rightn_id=None):
|
||||
def _create_simple_ppg(self, pairs=2, leftn_id=None, rightn_id=None,
|
||||
check_type=None, check_freq=None, check_port=None):
|
||||
nets = []
|
||||
# Pairs go in 2 networks
|
||||
if not leftn_id or not rightn_id:
|
||||
|
@ -167,9 +168,19 @@ class TestAIMServiceFunctionChainingBase(test_aim_base.AIMBaseTestCase):
|
|||
expected_res_status=201)['port_pair']
|
||||
port_pairs.append(pp)
|
||||
# This goes through
|
||||
kwargs = {}
|
||||
port_pair_group_parameters = {}
|
||||
if check_type:
|
||||
port_pair_group_parameters['healthcheck_type'] = check_type
|
||||
if check_freq:
|
||||
port_pair_group_parameters['healthcheck_frequency'] = check_freq
|
||||
if check_port:
|
||||
port_pair_group_parameters['healthcheck_tcp_port'] = check_port
|
||||
if port_pair_group_parameters:
|
||||
kwargs['port_pair_group_parameters'] = port_pair_group_parameters
|
||||
return self.create_port_pair_group(
|
||||
port_pairs=[pp['id'] for pp in port_pairs],
|
||||
expected_res_status=201)['port_pair_group']
|
||||
expected_res_status=201, **kwargs)['port_pair_group']
|
||||
|
||||
def _create_simple_flowc(self, src_svi=False, dst_svi=False):
|
||||
kwargs = {}
|
||||
|
@ -258,15 +269,42 @@ class TestAIMServiceFunctionChainingBase(test_aim_base.AIMBaseTestCase):
|
|||
# DeviceCluster. Only one created
|
||||
dc = self.aim_mgr.get(ctx, aim_sg.DeviceCluster(
|
||||
tenant_name=apic_tn, name='ppg_' + ppg['id']))
|
||||
ppg_params = ppg['port_pair_group_parameters']
|
||||
hcheck_type = ppg_params.get('healthcheck_type')
|
||||
if hcheck_type:
|
||||
hcheck_frequency = ppg_params.get('healthcheck_frequency')
|
||||
hcheck_tcp_port = ppg_params.get('healthcheck_tcp_port')
|
||||
mp = self.aim_mgr.get(ctx, aim_sg.ServiceRedirectMonitoringPolicy(
|
||||
tenant_name=apic_tn, name='ppg_' + ppg['id']))
|
||||
if hcheck_type:
|
||||
self.assertIsNotNone(mp)
|
||||
self.assertEqual(hcheck_type, mp.type)
|
||||
if hcheck_frequency:
|
||||
self.assertEqual(str(hcheck_frequency), mp.frequency)
|
||||
if hcheck_tcp_port:
|
||||
self.assertEqual(str(hcheck_tcp_port), mp.tcp_port)
|
||||
else:
|
||||
# Test hcheck toggle
|
||||
self.assertIsNone(mp)
|
||||
|
||||
self.assertIsNotNone(dc)
|
||||
self.assertEqual(self.physdom.name, dc.physical_domain_name)
|
||||
pps = [self.show_port_pair(x)['port_pair'] for x in ppg['port_pairs']]
|
||||
|
||||
srgh_dn_by_pp = {}
|
||||
for pp in pps:
|
||||
self.assertIsNotNone(self.aim_mgr.get(
|
||||
ctx, aim_sg.ConcreteDevice(tenant_name=apic_tn,
|
||||
device_cluster_name=dc.name,
|
||||
name='pp_' + pp['id'])))
|
||||
srhg = aim_sg.ServiceRedirectHealthGroup(
|
||||
tenant_name=dc.tenant_name, name='pp_' + pp['id'])
|
||||
srgh_dn_by_pp[pp['id']] = srhg.dn
|
||||
srhg = self.aim_mgr.get(ctx, srhg)
|
||||
if hcheck_type:
|
||||
self.assertIsNotNone(srhg)
|
||||
else:
|
||||
self.assertIsNone(srhg)
|
||||
|
||||
for pp in pps:
|
||||
# Each of these CD have 2 CDIs
|
||||
|
@ -317,7 +355,6 @@ class TestAIMServiceFunctionChainingBase(test_aim_base.AIMBaseTestCase):
|
|||
|
||||
self.assertEqual({egr.dn for egr in egr_cdis},
|
||||
set(edci.concrete_interfaces))
|
||||
|
||||
# Redirect Policy Ingress
|
||||
irp = self.aim_mgr.get(ctx, aim_sg.ServiceRedirectPolicy(
|
||||
tenant_name=dc.tenant_name, name='ingr_ppg_' + ppg['id']))
|
||||
|
@ -326,18 +363,17 @@ class TestAIMServiceFunctionChainingBase(test_aim_base.AIMBaseTestCase):
|
|||
self.assertIsNotNone(irp)
|
||||
self.assertIsNotNone(erp)
|
||||
|
||||
iprts = [(self._show_port(pp['ingress']), pp['name']) for pp in pps]
|
||||
self.assertEqual(
|
||||
sorted([{'ip': iprt[0]['fixed_ips'][0]['ip_address'],
|
||||
'mac': iprt[0]['mac_address'],
|
||||
'name': iprt[1]} for iprt in iprts]),
|
||||
irp.destinations)
|
||||
eprts = [(self._show_port(pp['egress']), pp['name']) for pp in pps]
|
||||
self.assertEqual(
|
||||
sorted([{'ip': eprt[0]['fixed_ips'][0]['ip_address'],
|
||||
'mac': eprt[0]['mac_address'],
|
||||
'name': eprt[1]} for eprt in eprts]),
|
||||
erp.destinations)
|
||||
# Ingress Ports
|
||||
for type, pbr in [('ingress', irp), ('egress', erp)]:
|
||||
prts = [(self._show_port(pp[type]), pp) for pp in pps]
|
||||
observed_destinations = []
|
||||
for port, pp in prts:
|
||||
dst = {'ip': port['fixed_ips'][0]['ip_address'],
|
||||
'mac': port['mac_address'], 'name': 'pp_' + pp['id']}
|
||||
if hcheck_type:
|
||||
dst['redirect_health_group_dn'] = srgh_dn_by_pp[pp['id']]
|
||||
observed_destinations.append(dst)
|
||||
self.assertEqual(sorted(observed_destinations), pbr.destinations)
|
||||
|
||||
def _verify_pc_mapping(self, pc, multiple=False):
|
||||
ctx = self._aim_context
|
||||
|
@ -509,6 +545,12 @@ class TestAIMServiceFunctionChainingBase(test_aim_base.AIMBaseTestCase):
|
|||
0, len(self.aim_mgr.find(ctx, aim_sg.DeviceCluster)))
|
||||
self.assertEqual(
|
||||
0, len(self.aim_mgr.find(ctx, aim_sg.DeviceClusterInterface)))
|
||||
self.assertEqual(
|
||||
0, len(self.aim_mgr.find(ctx,
|
||||
aim_sg.ServiceRedirectMonitoringPolicy)))
|
||||
self.assertEqual(
|
||||
0, len(self.aim_mgr.find(ctx, aim_sg.ServiceRedirectHealthGroup)))
|
||||
|
||||
ppgs = [self.show_port_pair_group(x)['port_pair_group'] for x in
|
||||
pc['port_pair_groups']]
|
||||
for ppg in ppgs:
|
||||
|
@ -671,6 +713,58 @@ class TestPortPairGroup(TestAIMServiceFunctionChainingBase):
|
|||
ppg2['id'], port_pairs=[pp3['id'], pp1['id']],
|
||||
expected_res_status=500)
|
||||
|
||||
def test_healthcheck_group(self):
|
||||
# Correct creation
|
||||
net1 = self._make_network(self.fmt, 'net1', True)
|
||||
self._make_subnet(self.fmt, net1, '192.168.0.1', '192.168.0.0/24')
|
||||
net2 = self._make_network(self.fmt, 'net2', True)
|
||||
self._make_subnet(self.fmt, net2, '192.168.1.1', '192.168.1.0/24')
|
||||
|
||||
# Service 1
|
||||
p11 = self._make_port(self.fmt, net1['network']['id'])['port']
|
||||
self._bind_port_to_host(p11['id'], 'h1')
|
||||
p12 = self._make_port(self.fmt, net2['network']['id'])['port']
|
||||
self._bind_port_to_host(p12['id'], 'h1')
|
||||
pp1 = self.create_port_pair(ingress=p11['id'], egress=p12['id'],
|
||||
expected_res_status=201)['port_pair']
|
||||
ppg1 = self.create_port_pair_group(
|
||||
port_pairs=[pp1['id']], port_pair_group_parameters={
|
||||
'healthcheck_type': 'tcp', 'healthcheck_frequency': 60,
|
||||
'healthcheck_tcp_port': 8080},
|
||||
expected_res_status=201)['port_pair_group']
|
||||
self.assertEqual('tcp', ppg1['port_pair_group_parameters'][
|
||||
'healthcheck_type'])
|
||||
self.assertEqual(60, ppg1['port_pair_group_parameters'][
|
||||
'healthcheck_frequency'])
|
||||
self.assertEqual(8080, ppg1['port_pair_group_parameters'][
|
||||
'healthcheck_tcp_port'])
|
||||
self.delete_port_pair_group(ppg1['id'])
|
||||
self.create_port_pair_group(
|
||||
port_pairs=[pp1['id']], port_pair_group_parameters={
|
||||
'healthcheck_type': 'no', 'healthcheck_frequency': 60,
|
||||
'healthcheck_tcp_port': 8080},
|
||||
expected_res_status=400)
|
||||
self.create_port_pair_group(
|
||||
port_pairs=[pp1['id']], port_pair_group_parameters={
|
||||
'healthcheck_type': 'tcp', 'healthcheck_frequency': -1,
|
||||
'healthcheck_tcp_port': 8080},
|
||||
expected_res_status=400)
|
||||
self.create_port_pair_group(
|
||||
port_pairs=[pp1['id']], port_pair_group_parameters={
|
||||
'healthcheck_type': 'tcp', 'healthcheck_frequency': 60,
|
||||
'healthcheck_tcp_port': 80800},
|
||||
expected_res_status=400)
|
||||
ppg1 = self.create_port_pair_group(
|
||||
port_pairs=[pp1['id']], port_pair_group_parameters={
|
||||
'healthcheck_type': 'icmp'},
|
||||
expected_res_status=201)['port_pair_group']
|
||||
self.assertEqual('icmp', ppg1['port_pair_group_parameters'][
|
||||
'healthcheck_type'])
|
||||
self.assertTrue('check_frequency' not in ppg1[
|
||||
'port_pair_group_parameters'])
|
||||
self.assertTrue('tcp_port' not in ppg1[
|
||||
'port_pair_group_parameters'])
|
||||
|
||||
|
||||
class TestFlowClassifier(TestAIMServiceFunctionChainingBase):
|
||||
|
||||
|
@ -1235,6 +1329,18 @@ class TestPortChain(TestAIMServiceFunctionChainingBase):
|
|||
self.update_flow_classifier(fc['id'], name='newname')
|
||||
self._verify_pc_mapping(pc)
|
||||
|
||||
def test_pc_validation_hcheck(self):
|
||||
fc = self._create_simple_flowc(src_svi=self.src_svi,
|
||||
dst_svi=self.dst_svi)
|
||||
ppg = self._create_simple_ppg(pairs=2, check_type='icmp')
|
||||
ppg2 = self._create_simple_ppg(pairs=2, check_type='tcp',
|
||||
check_freq=31, check_port=90)
|
||||
pc = self.create_port_chain(port_pair_groups=[ppg['id'], ppg2['id']],
|
||||
flow_classifiers=[fc['id']],
|
||||
expected_res_status=201)['port_chain']
|
||||
self._verify_pc_mapping(pc)
|
||||
self._verify_pc_delete(pc)
|
||||
|
||||
|
||||
class TestPortChainSVI(TestPortChain):
|
||||
|
||||
|
|
Loading…
Reference in New Issue