group-based-policy/gbpservice/neutron/tests/unit/services/sfc/test_aim_sfc_driver.py

1436 lines
69 KiB
Python

# 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 aim.api import infra as aim_infra
from aim.api import resource as aim_res
from aim.api import service_graph as aim_sg
import mock
from networking_sfc.extensions import flowclassifier as flowc_ext
from networking_sfc.extensions import sfc as sfc_ext
from networking_sfc.services.flowclassifier.common import config as flc_cfg
from networking_sfc.services.flowclassifier import driver_manager as fc_driverm
from networking_sfc.services.sfc.common import config as sfc_cfg
from networking_sfc.services.sfc import driver_manager as sfc_driverm
from neutron.callbacks import exceptions as c_exc
from neutron.db import api as db_api
from neutron.db.models import l3 as l3_db
from neutron_lib import context
from neutron_lib.plugins import directory
from oslo_log import log as logging
from gbpservice.neutron.services.grouppolicy import config
from gbpservice.neutron.tests.unit.db.grouppolicy import test_group_policy_db
from gbpservice.neutron.tests.unit.services.grouppolicy import (
test_aim_mapping_driver as test_aim_base)
LOG = logging.getLogger(__name__)
class Rollback(Exception):
pass
class TestAIMServiceFunctionChainingBase(test_aim_base.AIMBaseTestCase):
def setUp(self, *args, **kwargs):
sfc_cfg.cfg.CONF.set_override('drivers', ['aim'], group='sfc')
flc_cfg.cfg.CONF.set_override('drivers', ['aim'],
group='flowclassifier')
config.cfg.CONF.set_override(
'network_vlan_ranges', ['physnet1:100:200'], group='ml2_type_vlan')
ml2_options = {'mechanism_drivers': ['apic_aim', 'openvswitch'],
'extension_drivers': ['apic_aim', 'port_security',
'dns'],
'type_drivers': ['opflex', 'local', 'vlan'],
'tenant_network_types': ['vlan']}
# NOTE(ivar): the SFC and FLC driver managers load the driver names in
# the default parameters of their INIT functions. In Python, default
# params are evaluated only once when the module is loaded hence
# causing issues in the tests if those modules ever get loaded before
# the aim override happens. We need to reload the modules at this point
# to fix the issue.
reload(fc_driverm)
reload(sfc_driverm)
super(TestAIMServiceFunctionChainingBase, self).setUp(
*args, ml2_options=ml2_options, trunk_plugin='trunk', **kwargs)
self.agent_conf = test_group_policy_db.AGENT_CONF
self._sfc_driver = None
self._flowc_driver = None
self._sfc_plugin = None
self._flowc_plugin = None
self._aim_mech_driver = None
self.hlink1 = aim_infra.HostLink(host_name='h1', interface_name='eth0',
path='topology/pod-1/paths-101/pathep-[eth1/1]')
self.hlink2 = aim_infra.HostLink(host_name='h2', interface_name='eth0',
path='topology/pod-1/paths-102/pathep-[eth1/1]')
self.path_by_host = {'h1': 'topology/pod-1/paths-101/pathep-[eth1/1]',
'h2': 'topology/pod-1/paths-102/pathep-[eth1/1]'}
self.physdom = aim_res.PhysicalDomain(name='sfc-phys', monitored=True)
self.aim_mgr.create(self._aim_context, self.hlink1)
self.aim_mgr.create(self._aim_context, self.hlink2)
self.aim_mgr.create(self._aim_context, self.physdom)
self.aim_mgr.create(self._aim_context,
aim_infra.HostDomainMappingV2(
host_name='h1', domain_name=self.physdom.name,
domain_type='PhysDom'))
self.aim_mgr.create(self._aim_context,
aim_infra.HostDomainMappingV2(
host_name='h2', domain_name=self.physdom.name,
domain_type='PhysDom'))
self._plugin = directory.get_plugin()
self._plugin.remove_networks_from_down_agents = mock.Mock()
self._plugin.is_agent_down = mock.Mock(return_value=False)
self._ctx = context.get_admin_context()
def tearDown(self):
LOG.warning("SFCDs used in this test: %s",
self.sfc_plugin.driver_manager.drivers.keys())
LOG.warning("FLCDs used in this test: %s",
self.flowc_plugin.driver_manager.drivers.keys())
# Always reset configuration to dummy driver. Any
# test which requires to configure a different
# policy driver would have done so in it's setup
# (and should have ideally reset it too).
config.cfg.CONF.set_override('drivers', ['dummy'], group='sfc')
config.cfg.CONF.set_override('drivers', ['dummy'],
group='flowclassifier')
super(TestAIMServiceFunctionChainingBase, self).tearDown()
@property
def sfc_plugin(self):
if not self._sfc_plugin:
self._sfc_plugin = directory.get_plugin(sfc_ext.SFC_EXT)
return self._sfc_plugin
@property
def flowc_plugin(self):
if not self._flowc_plugin:
self._flowc_plugin = directory.get_plugin(
flowc_ext.FLOW_CLASSIFIER_EXT)
return self._flowc_plugin
@property
def sfc_driver(self):
# aim_mapping policy driver reference
if not self._sfc_driver:
self._sfc_driver = (
self.sfc_plugin.driver_manager.drivers['aim'].obj)
return self._sfc_driver
@property
def flowc_driver(self):
# aim_mapping policy driver reference
if not self._flowc_driver:
self._flowc_driver = (
self.flowc_plugin.driver_manager.drivers['aim'].obj)
return self._flowc_driver
@property
def aim_mech(self):
if not self._aim_mech_driver:
self._aim_mech_driver = (
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,
check_type=None, check_freq=None, check_port=None):
nets = []
# Pairs go in 2 networks
if not leftn_id or not rightn_id:
for i in range(2):
net = self._make_network(self.fmt, 'net1', True)
self._make_subnet(self.fmt, net, '192.168.%s.1' % i,
'192.168.%s.0/24' % i)
nets.append(net['network']['id'])
else:
nets = [leftn_id, rightn_id]
port_pairs = []
for i in range(pairs):
p1 = self._make_port(self.fmt, nets[0])['port']
self._bind_port_to_host(p1['id'], 'h%s' % ((i % 2) + 1))
self._plugin.update_port_status(self._ctx, p1['id'], 'ACTIVE')
p2 = self._make_port(self.fmt, nets[1])['port']
self._bind_port_to_host(p2['id'], 'h%s' % ((i % 2) + 1))
self._plugin.update_port_status(self._ctx, p2['id'], 'ACTIVE')
pp = self.create_port_pair(ingress=p1['id'], egress=p2['id'],
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, **kwargs)['port_pair_group']
def _create_simple_flowc(self, src_svi=False, dst_svi=False):
kwargs = {}
def get_svi_kwargs():
return {'apic:svi': True}
# Need to make sure all the SVI networks are in the same common
# unrouted vrf as other service chain resources do.
if src_svi or dst_svi:
router = self._make_router(
self.fmt, self._tenant_id, 'router1')['router']
vrf_name = 'openstack_UnroutedVRF'
vrf = aim_res.VRF(tenant_name='common', name=vrf_name,
monitored=True)
as1 = self._make_address_scope_for_vrf(
vrf.dn, name='as1')['address_scope']
pool = self._make_subnetpool(
self.fmt, ['192.168.0.0/8'], name='sp',
address_scope_id=as1['id'], tenant_id=as1['tenant_id'],
default_prefixlen=24)['subnetpool']
if src_svi:
# We need to create the L3Out and the External network
kwargs = get_svi_kwargs()
net1 = self._make_network(self.fmt, 'net1', True,
arg_list=self.extension_attributes,
**kwargs)
if src_svi:
subnet1 = self._make_subnet(
self.fmt, net1, '192.168.0.1', '192.168.0.0/24',
subnetpool_id=pool['id'])['subnet']
self.l3_plugin.add_router_interface(
context.get_admin_context(), router['id'],
{'subnet_id': subnet1['id']})
else:
self._make_subnet(self.fmt, net1, '192.168.0.1', '192.168.0.0/24')
kwargs = {}
if dst_svi:
kwargs = get_svi_kwargs()
net2 = self._make_network(self.fmt, 'net2', True,
arg_list=self.extension_attributes,
**kwargs)
if dst_svi:
subnet2 = self._make_subnet(
self.fmt, net2, '192.168.1.1', '192.168.1.0/24',
subnetpool_id=pool['id'])['subnet']
self.l3_plugin.add_router_interface(
context.get_admin_context(), router['id'],
{'subnet_id': subnet2['id']})
else:
self._make_subnet(self.fmt, net2, '192.168.1.1', '192.168.1.0/24')
return self.create_flow_classifier(
l7_parameters={
'logical_source_network': net1['network']['id'],
'logical_destination_network': net2['network']['id']},
source_ip_prefix='192.168.0.0/24',
destination_ip_prefix='192.168.1.0/24',
expected_res_status=201)['flow_classifier']
def _create_simple_port_chain(self, flowcs=1, ppgs=2, flowcs_args=None,
ppg_args=None):
flowc_ids = []
ppg_args = ppg_args or []
flowcs_args = flowcs_args or []
for i in range(flowcs):
try:
flowc_ids.append(
self._create_simple_flowc(**flowcs_args[i])['id'])
except IndexError:
flowc_ids.append(self._create_simple_flowc()['id'])
ppg_ids = []
for i in range(ppgs):
try:
ppg_ids.append(self._create_simple_ppg(**ppg_args[i])['id'])
except IndexError:
ppg_ids.append(self._create_simple_ppg()['id'])
return self.create_port_chain(port_pair_groups=ppg_ids,
flow_classifiers=flowc_ids,
expected_res_status=201)['port_chain']
def _verify_ppg_mapping(self, ppg, tenant):
apic_tn = tenant
# Verify expected AIM model
ctx = self._aim_context
# 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
iprt = self._show_port(pp['ingress'])
eprt = self._show_port(pp['egress'])
for p in [iprt, eprt]:
pp_dciin = self.aim_mgr.find(
ctx, aim_sg.ConcreteDeviceInterface,
name='prt_' + p['id'])
pp_dciin = pp_dciin[0]
self.assertEqual(
self.path_by_host.get(p['binding:host_id'], ''),
pp_dciin.path)
iepg = self.aim_mech._get_epg_by_network_id(self._ctx.session,
iprt['network_id'])
eepg = self.aim_mech._get_epg_by_network_id(self._ctx.session,
eprt['network_id'])
self.assertFalse(self.aim_mgr.get(ctx, iepg).sync)
self.assertFalse(self.aim_mgr.get(ctx, eepg).sync)
# 1 PPG means 1 service, which has 2 DeviceClusterInterfaces
# comprehensive of all the above ConcreteDeviceInterfaces
idci = self.aim_mgr.get(ctx, aim_sg.DeviceClusterInterface(
tenant_name=dc.tenant_name, device_cluster_name=dc.name,
name='ingress'))
edci = self.aim_mgr.get(ctx, aim_sg.DeviceClusterInterface(
tenant_name=dc.tenant_name, device_cluster_name=dc.name,
name='egress'))
self.assertIsNotNone(idci)
self.assertIsNotNone(edci)
# Ingress CDIs
ingr_cdis = [
aim_sg.ConcreteDeviceInterface(
tenant_name=apic_tn, device_cluster_name=dc.name,
device_name='pp_' + pp['id'], name='prt_' + pp['ingress'])
for pp in pps]
self.assertEqual({ingr.dn for ingr in ingr_cdis},
set(idci.concrete_interfaces))
# Egress CDIs
egr_cdis = [
aim_sg.ConcreteDeviceInterface(
tenant_name=apic_tn, device_cluster_name=dc.name,
device_name='pp_' + pp['id'], name='prt_' + pp['egress'])
for pp in pps]
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']))
erp = self.aim_mgr.get(ctx, aim_sg.ServiceRedirectPolicy(
tenant_name=dc.tenant_name, name='egr_ppg_' + ppg['id']))
self.assertIsNotNone(irp)
self.assertIsNotNone(erp)
# 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
flowcs = [self.show_flow_classifier(x)['flow_classifier'] for x in
pc['flow_classifiers']]
flowc_tenants = set([self._show_network(
flowc['l7_parameters']['logical_destination_network'])['tenant_id']
for flowc in flowcs])
ppgs = [self.show_port_pair_group(x)['port_pair_group'] for x in
pc['port_pair_groups']]
if not multiple:
self.assertEqual(
len(flowc_tenants) * len(ppgs),
len(self.aim_mgr.find(ctx, aim_sg.DeviceClusterContext)))
self.assertEqual(
len(flowc_tenants) * len(ppgs) * 2,
len(self.aim_mgr.find(ctx,
aim_sg.DeviceClusterInterfaceContext)))
self.assertEqual(
len(flowc_tenants),
len(self.aim_mgr.find(ctx, aim_sg.ServiceGraph)))
for flowc in flowcs:
src_net = self._show_network(
flowc['l7_parameters']['logical_source_network'])
dst_net = self._show_network(
flowc['l7_parameters']['logical_destination_network'])
apic_tn = 'prj_' + dst_net['tenant_id']
device_clusters = []
sg = self.aim_mgr.get(ctx, aim_sg.ServiceGraph(
tenant_name=apic_tn, name='ptc_' + pc['id']))
self.assertIsNotNone(sg)
src_cidr = flowc['source_ip_prefix']
dst_cird = flowc['destination_ip_prefix']
def get_net_group(net, cidr):
if net['apic:svi']:
# TODO(ivar): this will not work, there's no L3Outside
# DN extension for external networks.
ext = aim_res.ExternalNetwork.from_dn(
net['apic:distinguished_names']['ExternalNetwork'])
name_prefix = cidr.replace('/', '_')
if cidr in ['0.0.0.0/0', '::/0']:
# use default external EPG
name_prefix = 'default'
ext_net = self.aim_mgr.get(
ctx, aim_res.ExternalNetwork(
tenant_name=ext.tenant_name,
l3out_name=ext.l3out_name,
name=name_prefix + '_' + 'net_' + net['id']))
return ext_net
else:
epg = self.aim_mgr.get(
ctx, aim_res.EndpointGroup.from_dn(
net['apic:distinguished_names']['EndpointGroup']))
return epg
provider = get_net_group(dst_net, dst_cird)
# Verify Flow Classifier mapping
contract = self.aim_mgr.get(
ctx, aim_res.Contract(
tenant_name=apic_tn,
name=self.sfc_driver._generate_contract_name(provider.name,
sg.name)))
self.assertIsNotNone(contract)
subject = self.aim_mgr.get(
ctx, aim_res.ContractSubject(
tenant_name=apic_tn,
contract_name=contract.name,
name='ptc_' + pc['id']))
self.assertIsNotNone(subject)
self.assertEqual(['openstack_AnyFilter'], subject.bi_filters)
for net, pref, cidr in [(src_net, 'src_', src_cidr),
(dst_net, 'dst_', dst_cird)]:
group = get_net_group(net, cidr)
if net['apic:svi']:
ext_net = group
subnets = [cidr]
if cidr in ['0.0.0.0/0', '::/0']:
# use default external EPG
subnets = ['128.0.0.0/1', '0.0.0.0/1', '8000::/1',
'::/1']
for sub in subnets:
ext_sub = self.aim_mgr.get(ctx, aim_res.ExternalSubnet(
tenant_name=ext_net.tenant_name,
l3out_name=ext_net.l3out_name,
external_network_name=ext_net.name, cidr=sub))
self.assertIsNotNone(ext_sub)
self.assertIsNotNone(ext_net)
self.assertTrue(
contract.name in (ext_net.consumed_contract_names if
pref == 'src_' else
ext_net.provided_contract_names),
"%s not in ext net %s" % (contract.name,
ext_net.__dict__))
else:
epg = group
self.assertTrue(
contract.name in (epg.consumed_contract_names if
pref == 'src_' else
epg.provided_contract_names))
for ppg in ppgs:
self._verify_ppg_mapping(ppg, apic_tn)
device_cluster = self.aim_mgr.get(
ctx, aim_sg.DeviceCluster(tenant_name=apic_tn,
name='ppg_' + ppg['id']))
device_clusters.append(device_cluster)
dcc = self.aim_mgr.get(
ctx, aim_sg.DeviceClusterContext(
tenant_name=sg.tenant_name,
contract_name="any",
service_graph_name=sg.name,
node_name=device_cluster.name))
self.assertIsNotNone(dcc)
self.assertEqual(device_cluster.name, dcc.device_cluster_name)
self.assertEqual(apic_tn, dcc.device_cluster_tenant_name)
# Get ingress/egress BD
pp = self.show_port_pair(ppg['port_pairs'][0])['port_pair']
ingress_net = self._get_port_network(pp['ingress'])
egress_net = self._get_port_network(pp['egress'])
ingress_bd = ingress_net[
'apic:distinguished_names']['BridgeDomain']
egress_bd = egress_net[
'apic:distinguished_names']['BridgeDomain']
dci = aim_sg.DeviceClusterInterface(
tenant_name=device_cluster.tenant_name,
device_cluster_name=device_cluster.name, name='ingress')
dcic = aim_sg.DeviceClusterInterfaceContext(
tenant_name=apic_tn, contract_name="any",
service_graph_name=sg.name, node_name=device_cluster.name,
connector_name='consumer')
dcic = self.aim_mgr.get(ctx, dcic)
self.assertIsNotNone(dcic)
self.assertEqual(ingress_bd, dcic.bridge_domain_dn)
self.assertEqual(dci.dn, dcic.device_cluster_interface_dn)
self.assertNotEqual('', dcic.service_redirect_policy_dn)
dci = aim_sg.DeviceClusterInterface(
tenant_name=device_cluster.tenant_name,
device_cluster_name=device_cluster.name, name='egress')
dcic = aim_sg.DeviceClusterInterfaceContext(
tenant_name=apic_tn, contract_name="any",
service_graph_name=sg.name, node_name=device_cluster.name,
connector_name='provider')
dcic = self.aim_mgr.get(ctx, dcic)
self.assertIsNotNone(dcic)
self.assertEqual(egress_bd, dcic.bridge_domain_dn)
self.assertEqual(dci.dn, dcic.device_cluster_interface_dn)
self.assertNotEqual('', dcic.service_redirect_policy_dn)
self.assertEqual(
sorted({'name': x.name, 'device_cluster_name': x.name,
'device_cluster_tenant_name': x.tenant_name}
for x in device_clusters),
sorted(sg.linear_chain_nodes))
def _verify_pc_delete(self, pc):
ctx = self._aim_context
self.delete_port_chain(pc['id'])
# PC and Flowc unmapped
routers_count = ctx.db_session.query(l3_db.Router).count()
self.assertEqual(routers_count,
len(self.aim_mgr.find(ctx, aim_res.Contract)))
self.assertEqual(routers_count,
len(self.aim_mgr.find(ctx, aim_res.ContractSubject)))
self.assertEqual(
[], self.aim_mgr.find(ctx, aim_sg.DeviceClusterContext))
self.assertEqual(
[], self.aim_mgr.find(ctx, aim_sg.DeviceClusterInterfaceContext))
self.assertEqual([], self.aim_mgr.find(ctx, aim_sg.ServiceGraph))
# PPGs unmapped
self.assertEqual(
0, len(self.aim_mgr.find(ctx, aim_sg.ServiceRedirectPolicy)))
self.assertEqual(
0, len(self.aim_mgr.find(ctx, aim_sg.ConcreteDeviceInterface)))
self.assertEqual(
0, len(self.aim_mgr.find(ctx, aim_sg.ConcreteDevice)))
self.assertEqual(
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:
pps = [self.show_port_pair(x)['port_pair'] for x in
ppg['port_pairs']]
for pp in pps:
iprt = self._show_port(pp['ingress'])
eprt = self._show_port(pp['egress'])
iepg = self.aim_mech._get_epg_by_network_id(self._ctx.session,
iprt['network_id'])
eepg = self.aim_mech._get_epg_by_network_id(self._ctx.session,
eprt['network_id'])
self.assertTrue(self.aim_mgr.get(ctx, iepg).sync)
self.assertTrue(self.aim_mgr.get(ctx, eepg).sync)
def _delete_network(self, network_id):
req = self.new_delete_request('networks', network_id)
return req.get_response(self.api)
class TestPortPair(TestAIMServiceFunctionChainingBase):
def test_port_pair_validation(self):
net1 = self._make_network(self.fmt, 'net1', True)
self._make_subnet(self.fmt, net1, '192.168.0.1', '192.168.0.0/24')
p1 = self._make_port(self.fmt, net1['network']['id'])['port']
net2 = self._make_network(self.fmt, 'net2', True)
self._make_subnet(self.fmt, net1, '192.168.1.1', '192.168.1.0/24')
p2 = self._make_port(self.fmt, net2['network']['id'])['port']
self._bind_port_to_host(p1['id'], 'h1')
self._bind_port_to_host(p2['id'], 'h2')
self.create_port_pair(ingress=p1['id'], egress=p2['id'],
expected_res_status=201)
# Same network ports
p3 = self._make_port(self.fmt, net2['network']['id'])['port']
p4 = self._make_port(self.fmt, net2['network']['id'])['port']
self._bind_port_to_host(p3['id'], 'h1')
self._bind_port_to_host(p4['id'], 'h2')
self.create_port_pair(ingress=p3['id'], egress=p4['id'],
expected_res_status=500)
# Also unbound ports can be used
p5 = self._make_port(self.fmt, net1['network']['id'])['port']
self.create_port_pair(ingress=p3['id'], egress=p5['id'],
expected_res_status=400)
# Ports with no domain
def test_port_pair_validation_no_domain(self):
self.aim_mgr.delete(self._aim_context, self.physdom)
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, net1, '192.168.1.1', '192.168.1.0/24')
p1 = self._make_port(self.fmt, net1['network']['id'])['port']
p2 = self._make_port(self.fmt, net2['network']['id'])['port']
self._bind_port_to_host(p1['id'], 'h1')
# H3 has no domain specified
self._bind_port_to_host(p2['id'], 'h3')
self.create_port_pair(ingress=p1['id'], egress=p2['id'],
expected_res_status=500)
# Both ports no domain
p3 = self._make_port(self.fmt, net1['network']['id'])['port']
self._bind_port_to_host(p3['id'], 'h4')
self.create_port_pair(ingress=p3['id'], egress=p2['id'],
expected_res_status=500)
# Add domain, but different than H1
pd = self.aim_mgr.create(
self._aim_context, aim_infra.HostDomainMappingV2(
host_name='h3', domain_name='diff-name',
domain_type='PhysDom'))
self.create_port_pair(ingress=p1['id'], egress=p2['id'],
expected_res_status=500)
# Multi domain per host
self.aim_mgr.create(self._aim_context, aim_infra.HostDomainMappingV2(
host_name='h3', domain_name=self.physdom.name,
domain_type='PhysDom'))
self.create_port_pair(ingress=p1['id'], egress=p2['id'],
expected_res_status=500)
# Delete extra domain
self.aim_mgr.delete(self._aim_context, pd)
self.create_port_pair(ingress=p1['id'], egress=p2['id'],
expected_res_status=201)
def test_port_pair_validation_trunk(self):
net1 = self._make_network(self.fmt, 'net1', True)
self._make_subnet(self.fmt, net1, '192.168.0.1', '192.168.0.0/24')
snet1 = self._make_network(self.fmt, 'net1', True)
self._make_subnet(self.fmt, net1, '192.167.0.1', '192.167.0.0/24')
net2 = self._make_network(self.fmt, 'net2', True)
self._make_subnet(self.fmt, net1, '192.168.1.1', '192.168.1.0/24')
snet2 = self._make_network(self.fmt, 'net1', True)
self._make_subnet(self.fmt, net1, '192.167.1.1', '192.167.1.0/24')
p1 = self._make_port(self.fmt, net1['network']['id'])['port']
p2 = self._make_port(self.fmt, net2['network']['id'])['port']
sp1 = self._make_port(self.fmt, snet1['network']['id'])['port']
sp2 = self._make_port(self.fmt, snet2['network']['id'])['port']
trunk1 = self._create_resource('trunk', port_id=p1['id'])
trunk2 = self._create_resource('trunk', port_id=p2['id'])
self._bind_port_to_host(p1['id'], 'h1')
self._bind_port_to_host(p2['id'], 'h2')
self._bind_subport(self._ctx, trunk1, sp1)
self._bind_subport(self._ctx, trunk2, sp2)
self.driver._trunk_plugin.add_subports(
self._ctx, trunk1['trunk']['id'],
{'sub_ports': [{'port_id': sp1['id'],
'segmentation_type': 'vlan',
'segmentation_id': 100}]})
self.driver._trunk_plugin.add_subports(
self._ctx, trunk2['trunk']['id'],
{'sub_ports': [{'port_id': sp2['id'],
'segmentation_type': 'vlan',
'segmentation_id': 100}]})
self.create_port_pair(ingress=sp1['id'], egress=sp2['id'],
expected_res_status=201)
class TestPortPairGroup(TestAIMServiceFunctionChainingBase):
def test_ppg_validation(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']
# Service 2
p21 = self._make_port(self.fmt, net1['network']['id'])['port']
self._bind_port_to_host(p21['id'], 'h2')
p22 = self._make_port(self.fmt, net2['network']['id'])['port']
self._bind_port_to_host(p22['id'], 'h2')
pp2 = self.create_port_pair(ingress=p21['id'], egress=p22['id'],
expected_res_status=201)['port_pair']
# This goes through
ppg1 = self.create_port_pair_group(
port_pairs=[pp1['id'], pp2['id']],
expected_res_status=201)['port_pair_group']
# Use invalid pairs
net3 = self._make_network(self.fmt, 'net1', True)
self._make_subnet(self.fmt, net3, '192.168.0.1', '192.168.0.0/24')
p31 = self._make_port(self.fmt, net3['network']['id'])['port']
self._bind_port_to_host(p31['id'], 'h1')
pp3 = self.create_port_pair(ingress=p21['id'], egress=p31['id'],
expected_res_status=201)['port_pair']
self.delete_port_pair_group(ppg1['id'])
self.create_port_pair_group(port_pairs=[pp1['id'], pp3['id']],
expected_res_status=500)
# Works with only one PP
ppg2 = self.create_port_pair_group(
port_pairs=[pp3['id']],
expected_res_status=201)['port_pair_group']
# But update fails
self.update_port_pair_group(
ppg2['id'], port_pairs=[pp3['id'], pp1['id']],
expected_res_status=500)
def test_ppg_update(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']
# This goes through
ppg1 = self.create_port_pair_group(
port_pairs=[pp1['id']],
expected_res_status=201)['port_pair_group']
# Same ID update works
self.update_port_pair_group(ppg1['id'], port_pairs=[pp1['id']],
expected_res_status=200)
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):
def test_fc_validation(self):
# Correct classifier
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, net1, '192.168.1.1', '192.168.1.0/24')
fc = self.create_flow_classifier(
l7_parameters={
'logical_source_network': net1['network']['id'],
'logical_destination_network': net2['network']['id']},
source_ip_prefix='192.168.0.0/24',
destination_ip_prefix='192.168.1.0/24',
expected_res_status=201)['flow_classifier']
self.delete_flow_classifier(fc['id'], expected_res_status=204)
# Wrong FCs
self.create_flow_classifier(
l7_parameters={
'logical_source_network': net1['network']['id'],
'logical_destination_network': net2['network']['id']},
source_ip_prefix='192.168.0.0/24', expected_res_status=400)
self.create_flow_classifier(
l7_parameters={
'logical_source_network': net1['network']['id'],
'logical_destination_network': net2['network']['id']},
destination_ip_prefix='192.168.1.0/24',
expected_res_status=400)
self.create_flow_classifier(
l7_parameters={'logical_source_network': net1['network']['id']},
source_ip_prefix='192.168.0.0/24',
destination_ip_prefix='192.168.1.0/24', expected_res_status=400)
self.create_flow_classifier(
l7_parameters={
'logical_destination_network': net2['network']['id']},
source_ip_prefix='192.168.0.0/24',
destination_ip_prefix='192.168.1.0/24', expected_res_status=400)
self.create_flow_classifier(
l7_parameters={
'logical_source_network': net1['network']['id'],
'logical_destination_network': net1['network']['id']},
source_ip_prefix='192.168.0.0/24', expected_res_status=400)
self.create_flow_classifier(
l7_parameters={
'logical_source_network': net1['network']['id'],
'logical_destination_network': ''},
source_ip_prefix='192.168.0.0/24', expected_res_status=400)
self.create_flow_classifier(
l7_parameters={
'logical_source_network': net1['network']['id'],
'logical_destination_network': net2['network']['id'],
}, source_ip_prefix='192.168.0.0/24',
destination_ip_prefix='192.168.0.0/24', expected_res_status=400)
self._delete_network(net2['network']['id'])
self.create_flow_classifier(
l7_parameters={
'logical_source_network': net1['network']['id'],
'logical_destination_network': net2['network']['id']},
source_ip_prefix='192.168.0.0/24',
destination_ip_prefix='192.168.1.0/24', expected_res_status=404)
net_svi = self._make_network(self.fmt, 'net_svi', True,
arg_list=self.extension_attributes,
**{'apic:svi': True})
self._make_subnet(self.fmt, net_svi, '192.168.0.1', '192.168.0.0/24')
fc = self.create_flow_classifier(
l7_parameters={
'logical_source_network': net_svi['network']['id'],
'logical_destination_network': net_svi['network']['id']},
source_ip_prefix='192.168.0.0/24',
destination_ip_prefix='192.168.1.0/24',
expected_res_status=201)['flow_classifier']
# Same subnets, different networks.
net3 = self._make_network(self.fmt, 'net3', True)
self._make_subnet(self.fmt, net1, '192.168.2.1', '192.168.2.0/24')
net4 = self._make_network(self.fmt, 'net4', True)
self._make_subnet(self.fmt, net1, '192.168.3.1', '192.168.3.0/24')
self.create_flow_classifier(
l7_parameters={
'logical_source_network': net3['network']['id'],
'logical_destination_network': net4['network']['id']},
source_ip_prefix='192.168.0.0/24',
destination_ip_prefix='192.168.1.0/24',
expected_res_status=201)
self.delete_flow_classifier(fc['id'], expected_res_status=204)
class TestPortChain(TestAIMServiceFunctionChainingBase):
def setUp(self, *args, **kwargs):
super(TestPortChain, self).setUp()
self.src_svi = False
self.dst_svi = False
def _get_port_network(self, port_id):
port = self._show_port(port_id)
return self._show_network(port['network_id'])
def test_pc_validation(self):
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
ppg = self._create_simple_ppg(pairs=2)
ppg2 = self._create_simple_ppg(pairs=2)
pc = self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc['id']],
expected_res_status=201)['port_chain']
# Same classifier is not allowed.
self.create_port_chain(port_pair_groups=[ppg2['id']],
flow_classifiers=[fc['id']],
expected_res_status=409)
self.update_port_chain(
pc['id'], port_pair_groups=[ppg['id'], ppg2['id']],
expected_res_status=200)
def test_pc_validation_network_conflict(self):
nets = []
for i in range(3):
net = self._make_network(self.fmt, 'net1', True)
self._make_subnet(self.fmt, net, '192.168.%s.1' % i,
'192.168.%s.0/24' % i)
nets.append(net['network']['id'])
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
ppg = self._create_simple_ppg(pairs=2, leftn_id=nets[0],
rightn_id=nets[1])
ppg2 = self._create_simple_ppg(pairs=2, leftn_id=nets[0],
rightn_id=nets[1])
# Conflict with only one network
ppg3 = self._create_simple_ppg(pairs=2, leftn_id=nets[0],
rightn_id=nets[2])
self.create_port_chain(port_pair_groups=[ppg['id'], ppg2['id']],
flow_classifiers=[fc['id']],
expected_res_status=400)
self.create_port_chain(port_pair_groups=[ppg['id'], ppg3['id']],
flow_classifiers=[fc['id']],
expected_res_status=400)
def test_pc_mapping(self):
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
ppg = self._create_simple_ppg(pairs=2)
pc = self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc['id']],
expected_res_status=201)['port_chain']
self._verify_pc_mapping(pc)
self._verify_pc_delete(pc)
def test_pc_mapping_two_flowcs(self):
pc = self._create_simple_port_chain(
flowcs=2, ppgs=3, ppg_args=[{'pairs': 1}, {'pairs': 2},
{'pairs': 3}])
self._verify_pc_mapping(pc)
self._verify_pc_delete(pc)
def test_pc_mapping_no_flowcs(self):
pc = self._create_simple_port_chain(
flowcs=0, ppgs=3, ppg_args=[{'pairs': 1}, {'pairs': 2},
{'pairs': 3}])
self._verify_pc_mapping(pc)
self._verify_pc_delete(pc)
def test_ppg_update(self):
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
ppg = self._create_simple_ppg(pairs=2)
pc = self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc['id']],
expected_res_status=201)['port_chain']
pps = ppg['port_pairs']
# remove one pp
ppg = self.update_port_pair_group(
ppg['id'], port_pairs=[pps[0]])['port_pair_group']
self._verify_pc_mapping(pc)
# Replace pp
ppg = self.update_port_pair_group(
ppg['id'], port_pairs=[pps[1]])['port_pair_group']
self._verify_pc_mapping(pc)
# Add pp
ppg = self.update_port_pair_group(
ppg['id'], port_pairs=pps)['port_pair_group']
self._verify_pc_mapping(pc)
def test_flowc_update(self):
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
ppg = self._create_simple_ppg(pairs=1)
self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc['id']],
expected_res_status=201)
res = self._delete_network(
fc['l7_parameters']['logical_source_network'])
self.assertTrue(res.status_int >= 400)
res = self._delete_network(
fc['l7_parameters']['logical_destination_network'])
self.assertTrue(res.status_int >= 400)
def test_vrf_update(self):
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
ppg = self._create_simple_ppg(pairs=2)
self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc['id']],
expected_res_status=201)
self.aim_mgr.create(
self._aim_context, aim_res.EndpointGroup(
tenant_name='new', app_profile_name='new', name='new'))
try:
with db_api.context_manager.writer.using(self._ctx):
net_db = self._plugin._get_network(
self._ctx, fc['l7_parameters']['logical_source_network'])
self.assertRaises(c_exc.CallbackFailure,
self.aim_mech._set_network_vrf_and_notify,
self._ctx, net_db.aim_mapping,
aim_res.VRF(tenant_name='new', name='new'))
# For rollback to happen, the outermost transaction needs to
# fail.
raise Rollback()
except Rollback:
pass
try:
with db_api.context_manager.writer.using(self._ctx):
net_db = self._plugin._get_network(
self._ctx,
fc['l7_parameters']['logical_destination_network'])
self.assertRaises(c_exc.CallbackFailure,
self.aim_mech._set_network_vrf_and_notify,
self._ctx, net_db.aim_mapping,
aim_res.VRF(tenant_name='new', name='new'))
# For rollback to happen, the outermost transaction needs to
# fail.
raise Rollback()
except Rollback:
pass
# Also changing EPG affects PC if tenant changes
try:
with db_api.context_manager.writer.using(self._ctx):
net_db = self._plugin._get_network(
self._ctx,
fc['l7_parameters']['logical_destination_network'])
self.assertRaises(c_exc.CallbackFailure,
self.aim_mech._set_network_epg_and_notify,
self._ctx, net_db.aim_mapping,
aim_res.EndpointGroup(tenant_name='new',
app_profile_name='new',
name='new'))
# For rollback to happen, the outermost transaction needs to
# fail.
raise Rollback()
except Rollback:
pass
# But it doesn't if tenant stays the same
if not self.dst_svi:
net_db = self._plugin._get_network(
self._ctx,
fc['l7_parameters']['logical_destination_network'])
self.aim_mgr.create(
self._aim_context, aim_res.EndpointGroup(
tenant_name=net_db.aim_mapping.epg_tenant_name,
app_profile_name='new', name='new'))
self.aim_mech._set_network_epg_and_notify(
self._ctx, net_db.aim_mapping, aim_res.EndpointGroup(
tenant_name=net_db.aim_mapping.epg_tenant_name,
app_profile_name='new', name='new'))
pp = self.show_port_pair(ppg['port_pairs'][0])['port_pair']
net = self._get_port_network(pp['ingress'])
with db_api.context_manager.writer.using(self._ctx):
# Modifying EPG in service nets has no effect
net_db = self._plugin._get_network(self._ctx, net['id'])
self.aim_mech._set_network_epg_and_notify(
self._ctx, net_db.aim_mapping,
aim_res.EndpointGroup(tenant_name='new',
app_profile_name='new',
name='new'))
with db_api.context_manager.writer.using(self._ctx):
# But it fails when VRF is changed
net_db = self._plugin._get_network(self._ctx, net['id'])
self.assertRaises(c_exc.CallbackFailure,
self.aim_mech._set_network_vrf_and_notify,
self._ctx, net_db.aim_mapping,
aim_res.VRF(tenant_name='new', name='new'))
def test_pc_mapping_no_host_mapping(self):
ctx = self._aim_context
self.aim_mgr.delete_all(ctx, aim_infra.HostDomainMappingV2)
# Since one physdom exists, everything works just fine
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
ppg = self._create_simple_ppg(pairs=2)
pc = self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc['id']],
expected_res_status=201)['port_chain']
self._verify_pc_mapping(pc)
self._verify_pc_delete(pc)
# If I also delete the physdom, everything fails
self.aim_mgr.delete(ctx, self.physdom)
pc = self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc['id']],
expected_res_status=400)
def test_pc_mapping_same_provider_diff_consumer(self):
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
# New classifier with only one change in subnet
fc2 = self.create_flow_classifier(
l7_parameters={
'logical_source_network': fc[
'l7_parameters']['logical_source_network'],
'logical_destination_network': fc[
'l7_parameters']['logical_destination_network']},
source_ip_prefix='192.168.3.0/24',
destination_ip_prefix=fc['destination_ip_prefix'],
expected_res_status=201)['flow_classifier']
ppg = self._create_simple_ppg(pairs=2)
pc = self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc['id']],
expected_res_status=201)['port_chain']
pc = self.update_port_chain(pc['id'],
flow_classifiers=[fc['id'], fc2['id']],
expected_res_status=200)['port_chain']
self._verify_pc_mapping(pc)
self._verify_pc_delete(pc)
def test_pc_mapping_default_sub_dst(self):
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
# New classifier with only one change in subnet
fc2 = self.create_flow_classifier(
l7_parameters={
'logical_source_network': fc[
'l7_parameters']['logical_source_network'],
'logical_destination_network': fc[
'l7_parameters']['logical_destination_network']},
source_ip_prefix=fc['source_ip_prefix'],
destination_ip_prefix='0.0.0.0/0',
expected_res_status=201)['flow_classifier']
ppg = self._create_simple_ppg(pairs=2)
pc = self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc2['id']],
expected_res_status=201)['port_chain']
self._verify_pc_mapping(pc)
self._verify_pc_delete(pc)
def test_pc_mapping_default_sub_src(self):
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
# New classifier with only one change in subnet
fc2 = self.create_flow_classifier(
l7_parameters={
'logical_source_network': fc[
'l7_parameters']['logical_source_network'],
'logical_destination_network': fc[
'l7_parameters']['logical_destination_network']},
source_ip_prefix='0.0.0.0/0',
destination_ip_prefix=fc['destination_ip_prefix'],
expected_res_status=201)['flow_classifier']
ppg = self._create_simple_ppg(pairs=2)
pc = self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc2['id']],
expected_res_status=201)['port_chain']
self._verify_pc_mapping(pc)
self._verify_pc_delete(pc)
def test_port_pair_device_migration(self):
def verify_port_in_host(port, host):
dci = self.aim_mgr.find(
self._aim_context, aim_sg.ConcreteDeviceInterface,
name='prt_' + port['id'])[0]
self.assertEqual(self.path_by_host[host], dci.path)
ppg = self._create_simple_ppg(pairs=1)
pp = self.show_port_pair(ppg['port_pairs'][0])['port_pair']
iprt = self._show_port(pp['ingress'])
eprt = self._show_port(pp['egress'])
# Ports are initially bound to H1
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
pc = self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc['id']],
expected_res_status=201)['port_chain']
# Rebind completely first port, then second
iprt = self._unbind_port(iprt['id'])['port']
verify_port_in_host(iprt, 'h1')
self._plugin.update_port_status(self._ctx, iprt['id'], 'BUILD')
verify_port_in_host(iprt, 'h1')
self._bind_port_to_host(iprt['id'], 'h2')
verify_port_in_host(iprt, 'h1')
self._plugin.update_port_status(self._ctx, iprt['id'], 'ACTIVE')
verify_port_in_host(iprt, 'h2')
eprt = self._unbind_port(eprt['id'])['port']
verify_port_in_host(eprt, 'h1')
self._plugin.update_port_status(self._ctx, eprt['id'], 'BUILD')
verify_port_in_host(eprt, 'h1')
self._bind_port_to_host(eprt['id'], 'h2')
verify_port_in_host(eprt, 'h1')
self._plugin.update_port_status(self._ctx, iprt['id'], 'BUILD')
self._plugin.update_port_status(self._ctx, eprt['id'], 'ACTIVE')
# Other is not active
verify_port_in_host(eprt, 'h1')
self._plugin.update_port_status(self._ctx, iprt['id'], 'ACTIVE')
verify_port_in_host(eprt, 'h2')
self._verify_pc_mapping(pc)
# Enable once fixed on the SVI side.
def _test_pc_mapping_default_sub_ipv6(self):
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
# New classifier with only one change in subnet
fc2 = self.create_flow_classifier(
l7_parameters={
'logical_source_network': fc[
'l7_parameters']['logical_source_network'],
'logical_destination_network': fc[
'l7_parameters']['logical_destination_network']},
source_ip_prefix='::/0', destination_ip_prefix='::/0',
expected_res_status=201, ethertype='IPv6')['flow_classifier']
ppg = self._create_simple_ppg(pairs=2)
pc = self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc2['id']],
expected_res_status=201)['port_chain']
self._verify_pc_mapping(pc)
self._verify_pc_delete(pc)
def test_pc_max_ppg_validation(self):
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
ppg1 = self._create_simple_ppg(pairs=1)
ppg2 = self._create_simple_ppg(pairs=1)
ppg3 = self._create_simple_ppg(pairs=1)
ppg4 = self._create_simple_ppg(pairs=1)
self.create_port_chain(port_pair_groups=[ppg1['id'], ppg2['id'],
ppg3['id'], ppg4['id']],
flow_classifiers=[fc['id']],
expected_res_status=400)
pc = self.create_port_chain(port_pair_groups=[ppg1['id'],
ppg2['id'],
ppg3['id']],
flow_classifiers=[fc['id']],
expected_res_status=201)['port_chain']
self.update_port_chain(pc['id'],
port_pair_groups=[ppg1['id'], ppg2['id'],
ppg3['id'], ppg4['id']],
expected_res_status=500)
def test_pc_move_fc(self):
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
fcs = [fc]
for i in range(3):
fcs.append(self.create_flow_classifier(
l7_parameters={
'logical_source_network': fc[
'l7_parameters']['logical_source_network'],
'logical_destination_network': fc[
'l7_parameters']['logical_destination_network']},
source_ip_prefix='192.198.%s.0/24' % (i + 3),
destination_ip_prefix=fc['destination_ip_prefix'],
expected_res_status=201)['flow_classifier'])
# We have four FCs
ppg1 = self._create_simple_ppg(pairs=1)
ppg2 = self._create_simple_ppg(pairs=1)
pc1 = self.create_port_chain(port_pair_groups=[ppg1['id']],
flow_classifiers=[fcs[0]['id'],
fcs[1]['id']],
expected_res_status=201)['port_chain']
pc2 = self.create_port_chain(port_pair_groups=[ppg2['id']],
flow_classifiers=[fcs[2]['id'],
fcs[3]['id']],
expected_res_status=201)['port_chain']
self._verify_pc_mapping(pc1, multiple=True)
self._verify_pc_mapping(pc2, multiple=True)
# Remove FC 2
pc1 = self.update_port_chain(pc1['id'],
flow_classifiers=[fcs[0]['id']],
expected_res_status=200)['port_chain']
self._verify_pc_mapping(pc1, multiple=True)
self._verify_pc_mapping(pc2, multiple=True)
if self.dst_svi:
dst_net_id = fc['l7_parameters']['logical_destination_network']
ext_net = self.aim_mgr.find(
self._aim_context, aim_res.ExternalNetwork,
name=fc['destination_ip_prefix'].replace(
'/', '_') + '_' + 'net_' + dst_net_id)[0]
self.assertEqual(2, len(ext_net.provided_contract_names))
self.delete_port_chain(pc1['id'])
ext_net = self.aim_mgr.get(self._aim_context, ext_net)
self.assertEqual(1, len(ext_net.provided_contract_names))
self.delete_port_chain(pc2['id'])
self.assertIsNone(self.aim_mgr.get(self._aim_context, ext_net))
def test_port_link_update(self):
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')
p1 = self._make_port(self.fmt, net1['network']['id'])['port']
self._bind_port_to_host(p1['id'], 'h1')
p2 = self._make_port(self.fmt, net2['network']['id'])['port']
self._bind_port_to_host(p2['id'], 'h2')
pp = self.create_port_pair(ingress=p1['id'], egress=p2['id'],
expected_res_status=201)['port_pair']
ppg = self.create_port_pair_group(
port_pairs=[pp['id']], expected_res_status=201)['port_pair_group']
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
pc = self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc['id']],
expected_res_status=201)['port_chain']
self.path_by_host['h2'] = 'topology/pod-1/paths-103/pathep-[eth3/1]'
self.aim_mech.update_link(
self._context, 'h2', 'eth0', 'aa:bb', '103',
'3', '1',
port_description='topology/pod-1/paths-103/pathep-[eth3/1]')
self._verify_pc_mapping(pc)
# Test Removal
self.path_by_host.pop('h2')
self.aim_mech.delete_link(self._context, 'h2', 'eth0', 'aa:bb', '103',
'3', '1')
self._verify_pc_mapping(pc)
def test_port_no_host_link(self):
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')
p1 = self._make_port(self.fmt, net1['network']['id'])['port']
self._bind_port_to_host(p1['id'], 'h-nolink')
p2 = self._make_port(self.fmt, net2['network']['id'])['port']
self._bind_port_to_host(p2['id'], 'h-nolink')
pp = self.create_port_pair(ingress=p1['id'], egress=p2['id'],
expected_res_status=201)['port_pair']
ppg = self.create_port_pair_group(
port_pairs=[pp['id']], expected_res_status=201)['port_pair_group']
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
pc = self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc['id']],
expected_res_status=201)['port_chain']
self._verify_pc_mapping(pc)
def test_delete_no_contract(self):
pc = self._create_simple_port_chain()
epgs = self.aim_mgr.find(self._aim_context, aim_res.EndpointGroup)
extn = self.aim_mgr.find(self._aim_context, aim_res.ExternalNetwork)
for res in epgs + extn:
self.aim_mgr.update(self._aim_context, res,
consumed_contract_names=[])
self.aim_mgr.update(self._aim_context, res,
provided_contract_names=[])
self._verify_pc_delete(pc)
def test_pc_update_flowc(self):
fc = self._create_simple_flowc(src_svi=self.src_svi,
dst_svi=self.dst_svi)
ppg = self._create_simple_ppg(pairs=2)
pc = self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc['id']],
expected_res_status=201)['port_chain']
self.update_flow_classifier(
fc['id'], name='new_name',
expected_res_status=200)
self._verify_pc_mapping(pc)
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):
def setUp(self, *args, **kwargs):
super(TestPortChainSVI, self).setUp()
self.src_svi = True
self.dst_svi = True
def test_pc_flowc_same_network(self):
# Need to make sure the SVI network is in the same common
# unrouted vrf as other service chain resources do.
router = self._make_router(
self.fmt, self._tenant_id, 'router1')['router']
vrf_name = 'openstack_UnroutedVRF'
vrf = aim_res.VRF(tenant_name='common', name=vrf_name,
monitored=True)
as1 = self._make_address_scope_for_vrf(
vrf.dn, name='as1')['address_scope']
pool = self._make_subnetpool(
self.fmt, ['192.168.0.0/8'], name='sp',
address_scope_id=as1['id'], tenant_id=as1['tenant_id'],
default_prefixlen=24)['subnetpool']
net_svi = self._make_network(self.fmt, 'net_svi', True,
arg_list=self.extension_attributes,
**{'apic:svi': True})
subnet1 = self._make_subnet(
self.fmt, net_svi, '192.168.0.1', '192.168.0.0/24',
subnetpool_id=pool['id'])['subnet']
self.l3_plugin.add_router_interface(
context.get_admin_context(), router['id'],
{'subnet_id': subnet1['id']})
fc = self.create_flow_classifier(
l7_parameters={
'logical_source_network': net_svi['network']['id'],
'logical_destination_network': net_svi['network']['id']},
source_ip_prefix='192.168.0.0/24',
destination_ip_prefix='192.168.1.0/24',
expected_res_status=201)['flow_classifier']
ppg = self._create_simple_ppg(pairs=2)
pc = self.create_port_chain(port_pair_groups=[ppg['id']],
flow_classifiers=[fc['id']],
expected_res_status=201)['port_chain']
self._verify_pc_mapping(pc)
self._verify_pc_delete(pc)