/* * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.groupbasedpolicy.neutron.mapper.mapping.rule; import static com.google.common.base.Preconditions.checkNotNull; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nonnull; import org.opendaylight.controller.config.yang.config.neutron_mapper.impl.NeutronMapperModule; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition; import org.opendaylight.groupbasedpolicy.dto.EpgKeyDto; import org.opendaylight.groupbasedpolicy.neutron.mapper.EndpointRegistrator; import org.opendaylight.groupbasedpolicy.neutron.mapper.mapping.NeutronAware; import org.opendaylight.groupbasedpolicy.neutron.mapper.mapping.NeutronSecurityGroupAware; import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils; import org.opendaylight.groupbasedpolicy.neutron.mapper.util.NetworkUtils; import org.opendaylight.groupbasedpolicy.neutron.mapper.util.SecurityGroupUtils; import org.opendaylight.groupbasedpolicy.neutron.mapper.util.SecurityRuleUtils; import org.opendaylight.groupbasedpolicy.util.DataStoreHelper; import org.opendaylight.groupbasedpolicy.util.IidFactory; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.RegisterEndpointInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.RegisterEndpointInputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.register.endpoint.input.AddressEndpointRegBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionName; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClauseName; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContextId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContractId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Description; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ParameterName; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SelectorName; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.IpPrefixType; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.change.action.of.security.group.rules.input.action.ActionChoice; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.change.action.of.security.group.rules.input.action.action.choice.SfcActionCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValueBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.Contract; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.EndpointGroupBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.ExternalImplicitGroupBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.endpoint.group.ConsumerNamedSelector; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.endpoint.group.ConsumerNamedSelectorBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.endpoint.group.ProviderNamedSelector; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.endpoint.group.ProviderNamedSelectorBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstanceBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ClassifierInstance; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.secgroups.rev150712.security.groups.attributes.security.groups.SecurityGroup; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.secgroups.rev150712.security.groups.attributes.security.groups.SecurityGroupKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.secgroups.rev150712.security.rules.attributes.SecurityRules; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.secgroups.rev150712.security.rules.attributes.security.rules.SecurityRule; import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.secgroups.rev150712.security.rules.attributes.security.rules.SecurityRuleKey; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableList; import com.google.common.collect.Multiset; import com.google.common.collect.Sets; public class NeutronSecurityRuleAware implements NeutronAware<SecurityRule> { private static final Logger LOG = LoggerFactory.getLogger(NeutronSecurityRuleAware.class); public static final InstanceIdentifier<SecurityRule> SECURITY_RULE_WILDCARD_IID = InstanceIdentifier.builder(Neutron.class).child(SecurityRules.class).child(SecurityRule.class).build(); private static final String CONTRACT_PROVIDER = "Contract provider: "; private final DataBroker dataProvider; private final Multiset<InstanceIdentifier<ClassifierInstance>> createdClassifierInstances; private final Multiset<InstanceIdentifier<ActionInstance>> createdActionInstances; private final Map<SecurityRuleKey, SecurityRule> pendingCreatedRules; private final Map<SecurityGroupKey, SecurityGroup> pendingDeletedGroups; final static String PROVIDED_BY = "provided_by-"; final static String POSSIBLE_CONSUMER = "possible_consumer-"; private final EndpointRegistrator epRegistrator; public NeutronSecurityRuleAware(DataBroker dataProvider, EndpointRegistrator epRegistrator) { this(dataProvider, HashMultiset.<InstanceIdentifier<ClassifierInstance>>create(), HashMultiset.<InstanceIdentifier<ActionInstance>>create(), epRegistrator); } @VisibleForTesting NeutronSecurityRuleAware(DataBroker dataProvider, Multiset<InstanceIdentifier<ClassifierInstance>> classifierInstanceNames, Multiset<InstanceIdentifier<ActionInstance>> createdActionInstances, EndpointRegistrator epRegistrator) { this.dataProvider = checkNotNull(dataProvider); this.createdClassifierInstances = checkNotNull(classifierInstanceNames); this.createdActionInstances = checkNotNull(createdActionInstances); this.pendingCreatedRules = new HashMap<>(); this.pendingDeletedGroups = new HashMap<>(); this.epRegistrator = Preconditions.checkNotNull(epRegistrator); } @Override public void onCreated(SecurityRule secRule, Neutron neutron) { LOG.trace("created securityRule - {}", secRule); if (neutron.getSecurityGroups() == null || neutron.getSecurityGroups().getSecurityGroup() == null || !neutron.getSecurityGroups() .getSecurityGroup() .stream() .filter(sg -> sg.getKey().getUuid().equals(secRule.getSecurityGroupId())) .findFirst() .isPresent()) { pendingCreatedRules.put(secRule.getKey(), secRule); LOG.warn("Security group of security rule {} does not exist yet. The rule will be processed" + "when the missing security group is created.", secRule.getKey()); return; } ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); boolean isNeutronSecurityRuleAdded = addNeutronSecurityRule(secRule, neutron, rwTx); if (isNeutronSecurityRuleAdded) { DataStoreHelper.submitToDs(rwTx); } else { rwTx.cancel(); } } public void flushPendingSecurityRulesFor(@Nonnull SecurityGroupKey secGroupKey, Neutron neutron) { List<SecurityRule> rules = pendingCreatedRules.values() .stream() .filter(sr -> sr.getSecurityGroupId().equals(secGroupKey.getUuid())) .collect(Collectors.toList()); rules.forEach(sr -> { LOG.trace("Flushing pending security rule {}", sr); ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); boolean isNeutronSecurityRuleAdded = addNeutronSecurityRule(sr, neutron, rwTx); if (isNeutronSecurityRuleAdded) { DataStoreHelper.submitToDs(rwTx); } else { rwTx.cancel(); } pendingCreatedRules.remove(sr.getKey()); }); } public boolean addNeutronSecurityRule(SecurityRule secRule, Neutron neutron, ReadWriteTransaction rwTx) { return addNeutronSecurityRuleWithAction(secRule, neutron, MappingUtils.ALLOW_ACTION_CHOICE, rwTx); } public boolean addNeutronSecurityRuleWithAction(SecurityRule secRule, Neutron neutron, ActionChoice action, ReadWriteTransaction rwTx) { TenantId tenantId = new TenantId(secRule.getTenantId().getValue()); Uuid providerSecGroupId = secRule.getSecurityGroupId(); EndpointGroupId providerEpgId = new EndpointGroupId(providerSecGroupId.getValue()); Description contractDescription = createContractDescription(secRule, neutron); SingleRuleContract singleRuleContract = createSingleRuleContract(secRule, contractDescription, action, neutron); Contract contract = singleRuleContract.getContract(); rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.contractIid(tenantId, contract.getId()), contract, true); SelectorName providerSelector = getSelectorNameWithConsumer(secRule, neutron); writeProviderNamedSelectorToEpg(providerSelector, contract.getId(), new EpgKeyDto(providerEpgId, tenantId), rwTx); if (secRule.getRemoteGroupId() != null) { Uuid consumerSecGroupId = secRule.getRemoteGroupId(); designContractsBetweenProviderAndConsumer(tenantId, providerSecGroupId, consumerSecGroupId, neutron, rwTx); designContractsBetweenProviderAndConsumer(tenantId, consumerSecGroupId, providerSecGroupId, neutron, rwTx); } else { for (Uuid consumerSecGroupId : SecurityRuleUtils.findSecurityGroupsHavingSecurityRules(neutron)) { designContractsBetweenProviderAndConsumer(tenantId, providerSecGroupId, consumerSecGroupId, neutron, rwTx); designContractsBetweenProviderAndConsumer(tenantId, consumerSecGroupId, providerSecGroupId, neutron, rwTx); } } ClassifierInstance classifierInstance = singleRuleContract.getSingleClassifierRule().getClassifierInstance(); createClassifierInstanceIfNotExists(tenantId, classifierInstance, rwTx); createAllowActionInstanceIfNotExists(tenantId, rwTx); return true; } @VisibleForTesting static Description createContractDescription(SecurityRule secRule, Neutron neutron) { if (NeutronMapperModule.isDebugEnabled()) { Optional<SecurityGroup> providerSecGroup = SecurityGroupUtils.findSecurityGroup(secRule.getSecurityGroupId(), neutron.getSecurityGroups()); if (!providerSecGroup.isPresent()) { LOG.error("Neutron Security Group with UUID {} does not exist but it is in {}", secRule.getSecurityGroupId().getValue(), secRule); throw new IllegalStateException( "Neutron Security Group with UUID " + secRule.getSecurityGroupId().getValue() + " does not exist."); } return new Description(CONTRACT_PROVIDER + SecurityGroupUtils.getNameOrUuid(providerSecGroup.get())); } return new Description(CONTRACT_PROVIDER + secRule.getSecurityGroupId()); } @VisibleForTesting SingleRuleContract createSingleRuleContract(SecurityRule secRule, Description contractDescription, ActionChoice action, Neutron neutron) { if (secRule.getRemoteIpPrefix() != null) { if (neutron.getNetworks() != null && neutron.getNetworks().getNetwork() != null) { java.util.Optional<Network> publicNet = neutron.getNetworks() .getNetwork() .stream() .filter(net -> NetworkUtils.isRouterExternal(net)) .findAny(); if (publicNet.isPresent()) { WriteTransaction wTx = dataProvider.newWriteOnlyTransaction(); wTx.merge(LogicalDatastoreType.CONFIGURATION, IidFactory.externalImplicitGroupIid(new TenantId(secRule.getTenantId().getValue()), MappingUtils.EPG_EXTERNAL_ID), new ExternalImplicitGroupBuilder().setId(MappingUtils.EPG_EXTERNAL_ID).build(), true); wTx.merge(LogicalDatastoreType.CONFIGURATION, IidFactory.endpointGroupIid(new TenantId(secRule.getTenantId().getValue()), MappingUtils.EPG_EXTERNAL_ID), new EndpointGroupBuilder().setId(MappingUtils.EPG_EXTERNAL_ID).build(), true); DataStoreHelper.submitToDs(wTx); AddressEndpointRegBuilder addrEpbuilder = new AddressEndpointRegBuilder() .setAddress(String.valueOf(secRule.getRemoteIpPrefix().getValue())) .setAddressType(IpPrefixType.class) .setContextType(MappingUtils.L3_CONTEXT) .setContextId(new ContextId(publicNet.get().getUuid().getValue())) .setTenant(new TenantId(secRule.getTenantId().getValue())) .setTimestamp(System.currentTimeMillis()) .setEndpointGroup(ImmutableList.<EndpointGroupId>of(MappingUtils.EPG_EXTERNAL_ID)); RegisterEndpointInput regEp = new RegisterEndpointInputBuilder() .setAddressEndpointReg(ImmutableList.of(addrEpbuilder.build())).build(); boolean registered = epRegistrator.registerEndpoint(regEp); if (registered) { LOG.info("Registering endpoint representing remote-ip-prefix {}", addrEpbuilder.getKey()); } else { LOG.error("Failed to register endpoint {}", addrEpbuilder.getKey()); } } } return new SingleRuleContract(secRule, 0, contractDescription, action); } return new SingleRuleContract(secRule, 400, contractDescription, action); } @VisibleForTesting void designContractsBetweenProviderAndConsumer(TenantId tenantId, Uuid provSecGroupId, Uuid consSecGroupId, Neutron neutron, ReadWriteTransaction rwTx) { Set<SecurityRule> provSecRules = getProvidedSecRulesBetween(provSecGroupId, consSecGroupId, neutron); Set<SecurityRule> consSecRules = getProvidedSecRulesBetween(consSecGroupId, provSecGroupId, neutron); EndpointGroupId consEpgId = new EndpointGroupId(consSecGroupId.getValue()); for (SecurityRule provSecRule : provSecRules) { if (isProviderSecRuleSuitableForConsumerSecRules(provSecRule, consSecRules)) { SelectorName consumerSelector = getSelectorNameWithProvider(provSecRule, neutron); ContractId contractId = SecRuleEntityDecoder.getContractId(provSecRule); writeConsumerNamedSelectorToEpg(consumerSelector, contractId, new EpgKeyDto(consEpgId, tenantId), rwTx); } // TODO add case when port ranges overlap } } @VisibleForTesting Set<SecurityRule> getProvidedSecRulesBetween(Uuid provSecGroup, Uuid consSecGroup, Neutron neutron) { return Sets.union(SecurityRuleUtils.findSecurityRulesBySecGroupAndRemoteSecGroup(provSecGroup, consSecGroup, neutron), SecurityRuleUtils.findSecurityRulesBySecGroupAndRemoteSecGroup(provSecGroup, null, neutron)); } @VisibleForTesting static boolean isProviderSecRuleSuitableForConsumerSecRules(SecurityRule provSecRule, Set<SecurityRule> consSecRules) { Direction directionProvSecRule = SecRuleEntityDecoder.getDirection(provSecRule); for (SecurityRule consSecRule : consSecRules) { Direction directionConsSecRule = SecRuleEntityDecoder.getDirection(consSecRule); if (isDirectionOpposite(directionProvSecRule, directionConsSecRule) && isOneWithinTwo(provSecRule, consSecRule)) { return true; } } return false; } public boolean changeActionOfNeutronSecurityRule(SecurityRule secRule, ActionChoice action, Neutron neutron, ReadWriteTransaction rwTx) { addSfcChainActionInstance(action, new TenantId(secRule.getTenantId().getValue()), rwTx); LOG.trace("Changing to action {} for secuirity group rule {}", action, secRule); return addNeutronSecurityRuleWithAction(secRule, neutron, action, rwTx); } private void addSfcChainActionInstance(ActionChoice action, TenantId tenantId, ReadWriteTransaction rwTx) { if (action instanceof SfcActionCase) { String sfcChainName = ((SfcActionCase) action).getSfcChainName(); ActionName actionName = new ActionName(sfcChainName); ActionInstance sfcActionInstance = new ActionInstanceBuilder().setName(actionName) .setActionDefinitionId(ChainActionDefinition.ID) .setParameterValue( ImmutableList.of(new ParameterValueBuilder().setName( new ParameterName(ChainActionDefinition.SFC_CHAIN_NAME)) .setStringValue(sfcChainName) .build())) .build(); rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.actionInstanceIid(tenantId, actionName), sfcActionInstance, true); } } private void writeProviderNamedSelectorToEpg(SelectorName providerSelector, ContractId contractId, EpgKeyDto epgKey, WriteTransaction wTx) { ProviderNamedSelector providerNamedSelector = new ProviderNamedSelectorBuilder().setName(providerSelector) .setContract(ImmutableList.of(contractId)) .build(); wTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.providerNamedSelectorIid(epgKey.getTenantId(), epgKey.getEpgId(), providerNamedSelector.getName()), providerNamedSelector, true); } private void writeConsumerNamedSelectorToEpg(SelectorName consumerSelector, ContractId contractId, EpgKeyDto epgKey, WriteTransaction wTx) { ConsumerNamedSelector consumerNamedSelector = new ConsumerNamedSelectorBuilder().setName(consumerSelector) .setContract(ImmutableList.of(contractId)) .build(); wTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.consumerNamedSelectorIid(epgKey.getTenantId(), epgKey.getEpgId(), consumerNamedSelector.getName()), consumerNamedSelector, true); } @VisibleForTesting void createClassifierInstanceIfNotExists(TenantId tenantId, ClassifierInstance classifierInstance, WriteTransaction wTx) { InstanceIdentifier<ClassifierInstance> classifierInstanceIid = IidFactory.classifierInstanceIid(tenantId, classifierInstance.getName()); if (!createdClassifierInstances.contains(classifierInstanceIid)) { wTx.put(LogicalDatastoreType.CONFIGURATION, classifierInstanceIid, classifierInstance, true); } createdClassifierInstances.add(classifierInstanceIid); } @VisibleForTesting void createAllowActionInstanceIfNotExists(TenantId tenantId, ReadWriteTransaction rwTx) { InstanceIdentifier<ActionInstance> actionInstanceIid = IidFactory.actionInstanceIid(tenantId, MappingUtils.ACTION_ALLOW.getName()); if (!createdActionInstances.contains(actionInstanceIid)) { rwTx.put(LogicalDatastoreType.CONFIGURATION, actionInstanceIid, MappingUtils.ACTION_ALLOW, true); } createdActionInstances.add(actionInstanceIid); } @Override public void onUpdated(SecurityRule oldSecRule, SecurityRule newSecRule, Neutron oldNeutron, Neutron newNeutron) { LOG.warn("updated securityRule - Never should be called " + "- neutron API does not allow UPDATE on neutron security group rule. \nSecurity group rule: {}", newSecRule); } public void addPendingDeletedSecGroup(SecurityGroup secGroup) { LOG.trace("Caching pending deleted security group {}", secGroup.getKey()); pendingDeletedGroups.put(secGroup.getKey(), secGroup); } @Override public void onDeleted(SecurityRule deletedSecRule, Neutron oldNeutron, Neutron newNeutron) { LOG.trace("deleted securityRule - {}", deletedSecRule); ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); boolean isNeutronSecurityRuleDeleted = deleteNeutronSecurityRule(deletedSecRule, oldNeutron, rwTx); if (isNeutronSecurityRuleDeleted) { DataStoreHelper.submitToDs(rwTx); if (newNeutron == null || newNeutron.getSecurityRules() == null || newNeutron.getSecurityRules().getSecurityRule() == null || !newNeutron.getSecurityRules() .getSecurityRule() .stream() .filter(rule -> rule.getSecurityGroupId().equals(deletedSecRule.getSecurityGroupId())) .findAny() .isPresent()) { SecurityGroupKey secGroupKey = new SecurityGroupKey(deletedSecRule.getSecurityGroupId()); SecurityGroup pendingSg = pendingDeletedGroups.get(secGroupKey); if (pendingSg != null) { LOG.trace("Processing pending deleted security group {}", secGroupKey); NeutronSecurityGroupAware.deleteGbpEndpointGroup(dataProvider, new TenantId(deletedSecRule.getTenantId().getValue()), new EndpointGroupId(secGroupKey.getUuid().getValue())); pendingDeletedGroups.remove(secGroupKey); } } } else { rwTx.cancel(); } } public boolean deleteNeutronSecurityRule(SecurityRule secRule, Neutron neutron, ReadWriteTransaction rwTx) { TenantId tenantId = new TenantId(secRule.getTenantId().getValue()); Uuid providerSecGroupId = secRule.getSecurityGroupId(); EndpointGroupId providerEpgId = new EndpointGroupId(providerSecGroupId.getValue()); SelectorName providerSelector = getSelectorNameWithConsumer(secRule, neutron); deleteProviderNamedSelectorFromEpg(providerSelector, new EpgKeyDto(providerEpgId, tenantId), rwTx); if (secRule.getRemoteGroupId() != null) { Uuid consumerSecGroupId = secRule.getRemoteGroupId(); undesignContractsBetweenProviderAndConsumer(tenantId, providerSecGroupId, consumerSecGroupId, secRule, neutron, rwTx); undesignContractsBetweenProviderAndConsumer(tenantId, consumerSecGroupId, providerSecGroupId, secRule, neutron, rwTx); } else { for (Uuid consumerSecGroupId : SecurityRuleUtils.findSecurityGroupsHavingSecurityRules(neutron)) { undesignContractsBetweenProviderAndConsumer(tenantId, providerSecGroupId, consumerSecGroupId, secRule, neutron, rwTx); undesignContractsBetweenProviderAndConsumer(tenantId, consumerSecGroupId, providerSecGroupId, secRule, neutron, rwTx); } } ContractId contractId = SecRuleEntityDecoder.getContractId(secRule); rwTx.delete(LogicalDatastoreType.CONFIGURATION, IidFactory.contractIid(tenantId, contractId)); ClassifierInstance classifierInstance = SecRuleEntityDecoder.getClassifierInstance(secRule); deleteClassifierInstanceIfNotUsed(tenantId, classifierInstance, rwTx); return true; } @VisibleForTesting void undesignContractsBetweenProviderAndConsumer(TenantId tenantId, Uuid provSecGroupId, Uuid consSecGroupId, SecurityRule removedSecRule, Neutron neutron, ReadWriteTransaction rwTx) { Set<SecurityRule> provSecRules = getProvidedSecRulesBetween(provSecGroupId, consSecGroupId, neutron); Set<SecurityRule> consSecRules = getProvidedSecRulesBetween(consSecGroupId, provSecGroupId, neutron); EndpointGroupId consEpgId = new EndpointGroupId(consSecGroupId.getValue()); for (SecurityRule provSecRule : provSecRules) { if (isProvidersSecRuleSuitableForConsumersSecRulesAndGoodToRemove(provSecRule, consSecRules, removedSecRule)) { SelectorName consumerSelector = getSelectorNameWithProvider(provSecRule, neutron); deleteConsumerNamedSelector(consumerSelector, new EpgKeyDto(consEpgId, tenantId), rwTx); } // TODO add case when port ranges overlap } } @VisibleForTesting static boolean isProvidersSecRuleSuitableForConsumersSecRulesAndGoodToRemove(SecurityRule provSecRule, Set<SecurityRule> consSecRules, SecurityRule removedSecRule) { Direction directionProvSecRule = SecRuleEntityDecoder.getDirection(provSecRule); for (SecurityRule consSecRule : consSecRules) { if (isRuleIdEqual(removedSecRule, consSecRule) || isRuleIdEqual(removedSecRule, provSecRule)) { Direction directionConsSecRule = SecRuleEntityDecoder.getDirection(consSecRule); if (isDirectionOpposite(directionProvSecRule, directionConsSecRule) && isOneWithinTwo(provSecRule, consSecRule)) { return true; } } } return false; } @VisibleForTesting static boolean isRuleIdEqual(SecurityRule one, SecurityRule two) { checkNotNull(one); checkNotNull(two); return one.getSecurityGroupId().equals(two.getSecurityGroupId()); } private void deleteProviderNamedSelectorFromEpg(SelectorName providerSelector, EpgKeyDto providerEpgKey, ReadWriteTransaction rwTx) { InstanceIdentifier<ProviderNamedSelector> providerSelectorIid = IidFactory.providerNamedSelectorIid( providerEpgKey.getTenantId(), providerEpgKey.getEpgId(), providerSelector); DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, providerSelectorIid, rwTx); } private void deleteConsumerNamedSelector(SelectorName consumerSelector, EpgKeyDto consumerEpgKey, ReadWriteTransaction rwTx) { InstanceIdentifier<ConsumerNamedSelector> consumerSelectorIid = IidFactory.consumerNamedSelectorIid( consumerEpgKey.getTenantId(), consumerEpgKey.getEpgId(), consumerSelector); DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, consumerSelectorIid, rwTx); } private void deleteClassifierInstanceIfNotUsed(TenantId tenantId, ClassifierInstance classifierInstance, ReadWriteTransaction rwTx) { InstanceIdentifier<ClassifierInstance> classifierInstanceIid = IidFactory.classifierInstanceIid(tenantId, classifierInstance.getName()); createdClassifierInstances.remove(classifierInstanceIid); if (!createdClassifierInstances.contains(classifierInstanceIid)) { DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, classifierInstanceIid, rwTx); } } @VisibleForTesting void deleteAllowActionInstanceIfNotUsed(TenantId tenantId, ReadWriteTransaction rwTx) { InstanceIdentifier<ActionInstance> actionInstanceIid = IidFactory.actionInstanceIid(tenantId, MappingUtils.ACTION_ALLOW.getName()); createdActionInstances.remove(actionInstanceIid); if (!createdActionInstances.contains(actionInstanceIid)) { DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, actionInstanceIid, rwTx); } } private SelectorName getSelectorNameWithConsumer(SecurityRule secRule, Neutron neutron) { ClauseName clauseName = SecRuleNameDecoder.getClauseName(secRule); StringBuilder selectorNameBuilder = new StringBuilder().append(clauseName.getValue()); Uuid consumerSecGroupId = secRule.getRemoteGroupId(); if (consumerSecGroupId == null) { return new SelectorName(selectorNameBuilder.toString()); } // we cannot use name of security group in selector, because name can be changed // therefore name is used only in debug mode if (NeutronMapperModule.isDebugEnabled()) { Optional<SecurityGroup> potentialConsumerSecGroup = SecurityGroupUtils.findSecurityGroup(secRule.getRemoteGroupId(), neutron.getSecurityGroups()); if (!potentialConsumerSecGroup.isPresent()) { LOG.error("Neutron Security Group with UUID {} does not exist but it is in {}", consumerSecGroupId.getValue(), secRule); throw new IllegalStateException( "Neutron Security Group with UUID " + consumerSecGroupId.getValue() + " does not exist."); } selectorNameBuilder.append(MappingUtils.NAME_DOUBLE_DELIMETER) .append(POSSIBLE_CONSUMER) .append(SecurityGroupUtils.getNameOrUuid(potentialConsumerSecGroup.get())); return new SelectorName(selectorNameBuilder.toString()); } selectorNameBuilder.append(MappingUtils.NAME_DOUBLE_DELIMETER) .append(POSSIBLE_CONSUMER) .append(consumerSecGroupId.getValue()); return new SelectorName(selectorNameBuilder.toString()); } private SelectorName getSelectorNameWithProvider(SecurityRule secRule, Neutron neutron) { ClauseName clauseName = SecRuleNameDecoder.getClauseName(secRule); Uuid providerSecGroupId = secRule.getSecurityGroupId(); // we cannot use name of security group in selector, because name can be changed // therefore name is used only in debug mode if (NeutronMapperModule.isDebugEnabled()) { Optional<SecurityGroup> potentialProviderSecGroup = SecurityGroupUtils.findSecurityGroup(secRule.getSecurityGroupId(), neutron.getSecurityGroups()); if (!potentialProviderSecGroup.isPresent()) { LOG.error("Neutron Security Group with UUID {} does not exist but it is in {}", providerSecGroupId.getValue(), secRule); throw new IllegalStateException( "Neutron Security Group with UUID " + providerSecGroupId.getValue() + " does not exist."); } String selectorName = new StringBuilder().append(clauseName.getValue()) .append(MappingUtils.NAME_DOUBLE_DELIMETER) .append(PROVIDED_BY) .append(SecurityGroupUtils.getNameOrUuid(potentialProviderSecGroup.get())) .toString(); return new SelectorName(selectorName); } String selectorName = new StringBuilder().append(clauseName.getValue()) .append(MappingUtils.NAME_DOUBLE_DELIMETER) .append(PROVIDED_BY) .append(providerSecGroupId.getValue()) .toString(); return new SelectorName(selectorName); } @VisibleForTesting static boolean isDirectionOpposite(Direction one, Direction two) { return (one == Direction.In && two == Direction.Out) || (one == Direction.Out && two == Direction.In); } @VisibleForTesting static boolean isOneWithinTwo(SecurityRule one, SecurityRule two) { if (!isOneGroupIdWithinTwoRemoteGroupId(one, two) || !isOneGroupIdWithinTwoRemoteGroupId(two, one)) return false; if (!SecRuleEntityDecoder.isEtherTypeOfOneWithinTwo(one, two)) return false; if (!SecRuleEntityDecoder.isProtocolOfOneWithinTwo(one, two)) return false; if (!SecRuleEntityDecoder.isPortsOfOneWithinTwo(one, two)) return false; if (two.getRemoteIpPrefix() != null && one.getRemoteIpPrefix() == null) return false; return true; } @VisibleForTesting static boolean isOneGroupIdWithinTwoRemoteGroupId(SecurityRule one, SecurityRule two) { return (two.getRemoteGroupId() == null || two.getRemoteGroupId().equals( one.getSecurityGroupId())); } }