[AIM] Add Policy Enforcement Pref to network extension

Add apic:policy_enforcement_pref attribute to the
cisco_apic network extension.

Change-Id: Ied38d60a6624791c7c0a9a92e247b04fe969402e
This commit is contained in:
Sayali Naval 2021-04-06 11:51:42 -07:00
parent f123939b8a
commit 3cb18aca75
9 changed files with 163 additions and 7 deletions

View File

@ -0,0 +1,37 @@
# 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.
"""add policy enforcement pref attribute
Revision ID: 872bf4ba86a6
Revises: 016a678fafd4
"""
# revision identifiers, used by Alembic.
revision = '872bf4ba86a6'
down_revision = '016a678fafd4'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('apic_aim_network_extensions',
sa.Column('policy_enforcement_pref',
sa.Enum('unenforced', 'enforced', ''),
server_default="unenforced",
nullable=False))
def downgrade():
pass

View File

@ -1 +1 @@
016a678fafd4
872bf4ba86a6

View File

@ -48,6 +48,7 @@ EXTRA_PROVIDED_CONTRACTS = 'apic:extra_provided_contracts'
EXTRA_CONSUMED_CONTRACTS = 'apic:extra_consumed_contracts'
EPG_CONTRACT_MASTERS = 'apic:epg_contract_masters'
ERSPAN_CONFIG = 'apic:erspan_config'
POLICY_ENFORCEMENT_PREF = 'apic:policy_enforcement_pref'
BD = 'BridgeDomain'
EPG = 'EndpointGroup'
@ -336,6 +337,11 @@ NET_ATTRIBUTES = {
},
}
},
POLICY_ENFORCEMENT_PREF: {
'allow_post': True, 'allow_put': True,
'is_visible': True, 'default': 'unenforced',
'validate': {'type:values': ['unenforced', 'enforced', '']},
},
}
EXT_NET_ATTRIBUTES = {

View File

@ -115,6 +115,11 @@ class InvalidNetworkForEpgContractMaster(exceptions.BadRequest):
"an external or SVI network.")
class InvalidNetworkForPolicyEnforcementPref(exceptions.BadRequest):
message = _("apic:policy_enforcement_pref cannot be 'enforced' for "
"SVI network.")
class InvalidNetworkForQos(exceptions.BadRequest):
message = _("Cannot specify qos policy for "
"an external or SVI network.")

View File

@ -66,6 +66,9 @@ class NetworkExtensionDb(model_base.BASEV2):
default='default_export',
nullable=False)
bgp_asn = sa.Column(sa.String(64), default='0', nullable=False)
policy_enforcement_pref = sa.Column(sa.Enum('unenforced', 'enforced', ''),
default='unenforced',
nullable=False)
network = orm.relationship(models_v2.Network,
backref=orm.backref(
@ -331,6 +334,8 @@ class ExtensionDbMixin(object):
net_res[cisco_apic.EPG_CONTRACT_MASTERS] = [
{'app_profile_name': m.app_profile_name,
'name': m.name} for m in db_masters]
net_res[cisco_apic.POLICY_ENFORCEMENT_PREF] = db_obj[
'policy_enforcement_pref']
if net_res.get(cisco_apic.EXTERNAL_NETWORK):
net_res[cisco_apic.EXTERNAL_CIDRS] = [c.cidr for c in db_cidrs]
return net_res
@ -376,6 +381,9 @@ class ExtensionDbMixin(object):
if cisco_apic.NESTED_DOMAIN_NODE_NETWORK_VLAN in res_dict:
db_obj['nested_domain_node_network_vlan'] = res_dict[
cisco_apic.NESTED_DOMAIN_NODE_NETWORK_VLAN]
if cisco_apic.POLICY_ENFORCEMENT_PREF in res_dict:
db_obj['policy_enforcement_pref'] = res_dict[
cisco_apic.POLICY_ENFORCEMENT_PREF]
session.add(db_obj)
if cisco_apic.EXTERNAL_CIDRS in res_dict:

View File

@ -158,6 +158,8 @@ class ApicExtensionDriver(api_plus.ExtensionDriver,
data.get(cisco_apic.EXTRA_CONSUMED_CONTRACTS),
cisco_apic.EPG_CONTRACT_MASTERS:
data.get(cisco_apic.EPG_CONTRACT_MASTERS),
cisco_apic.POLICY_ENFORCEMENT_PREF:
data.get(cisco_apic.POLICY_ENFORCEMENT_PREF, "unenforced"),
}
if cisco_apic.VLANS_LIST in (data.get(
cisco_apic.NESTED_DOMAIN_ALLOWED_VLANS) or {}):
@ -219,7 +221,8 @@ class ApicExtensionDriver(api_plus.ExtensionDriver,
cisco_apic.NESTED_DOMAIN_ALLOWED_VLANS,
cisco_apic.EXTRA_PROVIDED_CONTRACTS,
cisco_apic.EXTRA_CONSUMED_CONTRACTS,
cisco_apic.EPG_CONTRACT_MASTERS]
cisco_apic.EPG_CONTRACT_MASTERS,
cisco_apic.POLICY_ENFORCEMENT_PREF]
if not(set(update_attrs) & set(data.keys())):
return
@ -238,7 +241,8 @@ class ApicExtensionDriver(api_plus.ExtensionDriver,
cisco_apic.NESTED_DOMAIN_NODE_NETWORK_VLAN,
cisco_apic.EXTRA_PROVIDED_CONTRACTS,
cisco_apic.EXTRA_CONSUMED_CONTRACTS,
cisco_apic.EPG_CONTRACT_MASTERS]
cisco_apic.EPG_CONTRACT_MASTERS,
cisco_apic.POLICY_ENFORCEMENT_PREF]
for e_k in ext_keys:
if e_k in data:
res_dict.update({e_k: data[e_k]})

View File

@ -792,6 +792,10 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
if (current[cisco_apic.EPG_CONTRACT_MASTERS] and (is_ext or is_svi)):
raise exceptions.InvalidNetworkForEpgContractMaster()
if current[cisco_apic.POLICY_ENFORCEMENT_PREF] == 'enforced' and \
is_svi:
raise exceptions.InvalidNetworkForPolicyEnforcementPref()
if (current.get(qos_consts.QOS_POLICY_ID) and (is_ext or is_svi)):
raise exceptions.InvalidNetworkForQos()
@ -928,6 +932,8 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
cisco_apic.EXTRA_CONSUMED_CONTRACTS]
epg.epg_contract_masters = current[
cisco_apic.EPG_CONTRACT_MASTERS]
epg.policy_enforcement_pref = current[
cisco_apic.POLICY_ENFORCEMENT_PREF]
epg.qos_name = current.get(qos_consts.QOS_POLICY_ID, None)
self.aim.create(aim_ctx, epg)
@ -1016,6 +1022,17 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
self.aim.update(
aim_ctx, epg, epg_contract_masters=curr_masters)
# Update Policy Enforcement Pref if changed.
curr_masters = current[cisco_apic.POLICY_ENFORCEMENT_PREF]
orig_masters = original[cisco_apic.POLICY_ENFORCEMENT_PREF]
if curr_masters != orig_masters:
if curr_masters == 'enforced' and is_svi:
raise exceptions.InvalidNetworkForPolicyEnforcementPref()
epg = self.aim.get(aim_ctx, self._get_network_epg(mapping))
self.aim.update(
aim_ctx, epg, policy_enforcement_pref=curr_masters)
if is_ext:
_, ext_net, ns = self._get_aim_nat_strategy(current)
if ext_net:
@ -6940,6 +6957,8 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
for key in ('app_profile_name', 'name')}
for x in
net_db.aim_extension_epg_contract_masters]
policy_enforcement_pref = (
net_db.aim_extension_mapping.policy_enforcement_pref)
# REVISIT: Refactor to share code.
dname = aim_utils.sanitize_display_name(net_db.name)
@ -6956,7 +6975,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
epg.display_name = dname
epg.bd_name = bd.name
epg.policy_enforcement_pref = 'unenforced'
epg.policy_enforcement_pref = policy_enforcement_pref
epg.provided_contract_names = provided_contract_names
epg.consumed_contract_names = consumed_contract_names
epg.openstack_vmm_domain_names = []

View File

@ -1020,6 +1020,7 @@ class TestAimMapping(ApicAimTestCase):
consumed_contract_names = set(
router_anames + net['apic:extra_consumed_contracts'])
epg_contract_masters = net['apic:epg_contract_masters']
policy_enforcement_pref = net['apic:policy_enforcement_pref']
if routers:
if vrf:
@ -1098,6 +1099,8 @@ class TestAimMapping(ApicAimTestCase):
aim_epg.consumed_contract_names)
self.assertItemsEqual(epg_contract_masters,
aim_epg.epg_contract_masters)
self.assertItemsEqual(policy_enforcement_pref,
aim_epg.policy_enforcement_pref)
# REVISIT(rkukura): Check openstack_vmm_domain_names and
# physical_domain_names?
self._check_dn_is_resource(dns, 'EndpointGroup', aim_epg)
@ -1402,7 +1405,8 @@ class TestAimMapping(ApicAimTestCase):
'apic:epg_contract_masters': [{'app_profile_name': 'ap1',
'name': 'epg1'},
{'app_profile_name': 'ap2',
'name': 'epg2'}]}
'name': 'epg2'}],
'apic:policy_enforcement_pref': 'enforced'}
net = self._make_network(
self.fmt, 'net1', True, arg_list=tuple(list(kwargs.keys())),
**kwargs)['network']
@ -1421,7 +1425,8 @@ class TestAimMapping(ApicAimTestCase):
'apic:epg_contract_masters': [{'app_profile_name': 'ap1',
'name': 'epg2'},
{'app_profile_name': 'ap3',
'name': 'epg2'}]}}
'name': 'epg2'}],
'apic:policy_enforcement_pref': 'enforced'}}
net = self._update('networks', net_id, data)['network']
self._check_network(net)
@ -1610,11 +1615,39 @@ class TestAimMapping(ApicAimTestCase):
'InvalidNetworkForEpgContractMaster',
result['NeutronError']['type'])
def _test_invalid_policy_enforcement_pref(self, kwargs):
# Verify creating network with Policy Enforcement Pref fails.
kwargs['apic:policy_enforcement_pref'] = 'enforced'
resp = self._create_network(
self.fmt, 'net', True, arg_list=tuple(list(kwargs.keys())),
**kwargs)
result = self.deserialize(self.fmt, resp)
self.assertEqual(
'InvalidNetworkForPolicyEnforcementPref',
result['NeutronError']['type'])
del kwargs['apic:policy_enforcement_pref']
# Create network with default Policy Enforcement Pref
net_id = self._make_network(
self.fmt, 'net', True,
arg_list=tuple(list(kwargs.keys())), **kwargs)['network']['id']
# Verify setting Policy Enforcement Pref on network fails.
result = self._update(
'networks', net_id,
{'network': {'apic:policy_enforcement_pref': 'enforced'}},
webob.exc.HTTPBadRequest.code)
self.assertEqual(
'InvalidNetworkForPolicyEnforcementPref',
result['NeutronError']['type'])
def test_external_network_exceptions(self):
self._test_invalid_network_exceptions({'router:external': True})
def test_svi_network_exceptions(self):
self._test_invalid_network_exceptions({'apic:svi': True})
self._test_invalid_policy_enforcement_pref({'apic:svi': True})
def test_security_group_lifecycle(self):
# Test create
@ -6546,6 +6579,49 @@ class TestExtensionAttributes(ApicAimTestCase):
network_id=net_id).all())
self.assertEqual([], db_masters)
def test_network_with_policy_enforcement_pref_lifecycle(self):
session = db_api.get_reader_session()
extn = extn_db.ExtensionDbMixin()
# Create network with default Policy Enforcement Pref
net = self._make_network(
self.fmt, 'net1', True)['network']
net_id = net['id']
self.assertItemsEqual('unenforced',
net['apic:policy_enforcement_pref'])
# Test show.
net = self._show('networks', net_id)['network']
self.assertItemsEqual('unenforced',
net['apic:policy_enforcement_pref'])
# Test list.
net = self._list(
'networks', query_params=('id=%s' % net_id))['networks'][0]
self.assertItemsEqual('unenforced',
net['apic:policy_enforcement_pref'])
# Test update with Policy Enforcement Pref
net = self._update(
'networks', net_id,
{'network':
{'apic:policy_enforcement_pref': 'enforced'}})['network']
self.assertItemsEqual('enforced',
net['apic:policy_enforcement_pref'])
# Test show after update.
net = self._show('networks', net_id)['network']
self.assertItemsEqual('enforced',
net['apic:policy_enforcement_pref'])
# Test delete.
self._delete('networks', net_id)
self.assertFalse(extn.get_network_extn_db(session, net_id))
db_masters = (session.query(
extn_db.NetworkExtEpgContractMasterDb).filter_by(
network_id=net_id).all())
self.assertEqual([], db_masters)
def test_external_network_lifecycle(self):
session = db_api.get_reader_session()
extn = extn_db.ExtensionDbMixin()

View File

@ -473,7 +473,8 @@ class TestNeutronMapping(AimValidationTestCase):
'apic:epg_contract_masters': [{'app_profile_name': 'ap1',
'name': 'ec3'},
{'app_profile_name': 'ap2',
'name': 'ec4'}]}
'name': 'ec4'}],
'apic:policy_enforcement_pref': 'unenforced'}
if preexisting_bd:
kwargs.update(
{'apic:distinguished_names':