Bug 8228 - metadata service fix made cleaner
[groupbasedpolicy.git] / neutron-mapper / src / main / java / org / opendaylight / groupbasedpolicy / neutron / mapper / mapping / rule / NeutronSecurityRuleAware.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.groupbasedpolicy.neutron.mapper.mapping.rule;
10
11 import static com.google.common.base.Preconditions.checkNotNull;
12
13 import java.util.HashMap;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.stream.Collectors;
18
19 import javax.annotation.Nonnull;
20
21 import org.opendaylight.controller.config.yang.config.neutron_mapper.impl.NeutronMapperModule;
22 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
23 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
24 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition;
27 import org.opendaylight.groupbasedpolicy.dto.EpgKeyDto;
28 import org.opendaylight.groupbasedpolicy.neutron.mapper.EndpointRegistrator;
29 import org.opendaylight.groupbasedpolicy.neutron.mapper.mapping.NeutronAware;
30 import org.opendaylight.groupbasedpolicy.neutron.mapper.mapping.NeutronSecurityGroupAware;
31 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils;
32 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.NetworkUtils;
33 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.SecurityGroupUtils;
34 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.SecurityRuleUtils;
35 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
36 import org.opendaylight.groupbasedpolicy.util.IidFactory;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.RegisterEndpointInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.RegisterEndpointInputBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.register.endpoint.input.AddressEndpointRegBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionName;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClauseName;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContextId;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContractId;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Description;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ParameterName;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SelectorName;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.IpPrefixType;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.change.action.of.security.group.rules.input.action.ActionChoice;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.change.action.of.security.group.rules.input.action.action.choice.SfcActionCase;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValueBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.Contract;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.EndpointGroupBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.ExternalImplicitGroupBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.endpoint.group.ConsumerNamedSelector;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.endpoint.group.ConsumerNamedSelectorBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.endpoint.group.ProviderNamedSelector;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.endpoint.group.ProviderNamedSelectorBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstanceBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ClassifierInstance;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.secgroups.rev150712.security.groups.attributes.security.groups.SecurityGroup;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.secgroups.rev150712.security.groups.attributes.security.groups.SecurityGroupKey;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.secgroups.rev150712.security.rules.attributes.SecurityRules;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.secgroups.rev150712.security.rules.attributes.security.rules.SecurityRule;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.secgroups.rev150712.security.rules.attributes.security.rules.SecurityRuleKey;
72 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
75
76 import com.google.common.annotations.VisibleForTesting;
77 import com.google.common.base.Optional;
78 import com.google.common.base.Preconditions;
79 import com.google.common.collect.HashMultiset;
80 import com.google.common.collect.ImmutableList;
81 import com.google.common.collect.Multiset;
82 import com.google.common.collect.Sets;
83
84 public class NeutronSecurityRuleAware implements NeutronAware<SecurityRule> {
85
86     private static final Logger LOG = LoggerFactory.getLogger(NeutronSecurityRuleAware.class);
87     public static final InstanceIdentifier<SecurityRule> SECURITY_RULE_WILDCARD_IID =
88             InstanceIdentifier.builder(Neutron.class).child(SecurityRules.class).child(SecurityRule.class).build();
89     private static final String CONTRACT_PROVIDER = "Contract provider: ";
90     private final DataBroker dataProvider;
91     private final Multiset<InstanceIdentifier<ClassifierInstance>> createdClassifierInstances;
92     private final Multiset<InstanceIdentifier<ActionInstance>> createdActionInstances;
93     private final Map<SecurityRuleKey, SecurityRule> pendingCreatedRules;
94     private final Map<SecurityGroupKey, SecurityGroup> pendingDeletedGroups;
95     final static String PROVIDED_BY = "provided_by-";
96     final static String POSSIBLE_CONSUMER = "possible_consumer-";
97     private final EndpointRegistrator epRegistrator;
98
99     public NeutronSecurityRuleAware(DataBroker dataProvider, EndpointRegistrator epRegistrator) {
100         this(dataProvider, HashMultiset.<InstanceIdentifier<ClassifierInstance>>create(),
101                 HashMultiset.<InstanceIdentifier<ActionInstance>>create(), epRegistrator);
102     }
103
104     @VisibleForTesting
105     NeutronSecurityRuleAware(DataBroker dataProvider,
106             Multiset<InstanceIdentifier<ClassifierInstance>> classifierInstanceNames,
107             Multiset<InstanceIdentifier<ActionInstance>> createdActionInstances, EndpointRegistrator epRegistrator) {
108         this.dataProvider = checkNotNull(dataProvider);
109         this.createdClassifierInstances = checkNotNull(classifierInstanceNames);
110         this.createdActionInstances = checkNotNull(createdActionInstances);
111         this.pendingCreatedRules = new HashMap<>();
112         this.pendingDeletedGroups = new HashMap<>();
113         this.epRegistrator = Preconditions.checkNotNull(epRegistrator);
114     }
115
116     @Override
117     public void onCreated(SecurityRule secRule, Neutron neutron) {
118         LOG.trace("created securityRule - {}", secRule);
119         if (neutron.getSecurityGroups() == null || neutron.getSecurityGroups().getSecurityGroup() == null
120                 || !neutron.getSecurityGroups()
121                     .getSecurityGroup()
122                     .stream()
123                     .filter(sg -> sg.getKey().getUuid().equals(secRule.getSecurityGroupId()))
124                     .findFirst()
125                     .isPresent()) {
126             pendingCreatedRules.put(secRule.getKey(), secRule);
127             LOG.warn("Security group of security rule {} does not exist yet. The rule will be processed"
128                     + "when the missing security group is created.", secRule.getKey());
129             return;
130         }
131         ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
132         boolean isNeutronSecurityRuleAdded = addNeutronSecurityRule(secRule, neutron, rwTx);
133         if (isNeutronSecurityRuleAdded) {
134             DataStoreHelper.submitToDs(rwTx);
135         } else {
136             rwTx.cancel();
137         }
138     }
139
140     public void flushPendingSecurityRulesFor(@Nonnull SecurityGroupKey secGroupKey, Neutron neutron) {
141         List<SecurityRule> rules = pendingCreatedRules.values()
142             .stream()
143             .filter(sr -> sr.getSecurityGroupId().equals(secGroupKey.getUuid()))
144             .collect(Collectors.toList());
145         rules.forEach(sr -> {
146             LOG.trace("Flushing pending security rule {}", sr);
147             ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
148             boolean isNeutronSecurityRuleAdded = addNeutronSecurityRule(sr, neutron, rwTx);
149             if (isNeutronSecurityRuleAdded) {
150                 DataStoreHelper.submitToDs(rwTx);
151             } else {
152                 rwTx.cancel();
153             }
154             pendingCreatedRules.remove(sr.getKey());
155         });
156     }
157
158     public boolean addNeutronSecurityRule(SecurityRule secRule, Neutron neutron, ReadWriteTransaction rwTx) {
159         return addNeutronSecurityRuleWithAction(secRule, neutron, MappingUtils.ALLOW_ACTION_CHOICE, rwTx);
160     }
161
162     public boolean addNeutronSecurityRuleWithAction(SecurityRule secRule, Neutron neutron, ActionChoice action,
163             ReadWriteTransaction rwTx) {
164         TenantId tenantId = new TenantId(secRule.getTenantId().getValue());
165         Uuid providerSecGroupId = secRule.getSecurityGroupId();
166         EndpointGroupId providerEpgId = new EndpointGroupId(providerSecGroupId.getValue());
167
168         Description contractDescription = createContractDescription(secRule, neutron);
169         SingleRuleContract singleRuleContract = createSingleRuleContract(secRule, contractDescription, action, neutron);
170         Contract contract = singleRuleContract.getContract();
171         rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.contractIid(tenantId, contract.getId()), contract, true);
172         SelectorName providerSelector = getSelectorNameWithConsumer(secRule, neutron);
173         writeProviderNamedSelectorToEpg(providerSelector, contract.getId(), new EpgKeyDto(providerEpgId, tenantId), rwTx);
174
175         if (secRule.getRemoteGroupId() != null) {
176             Uuid consumerSecGroupId = secRule.getRemoteGroupId();
177             designContractsBetweenProviderAndConsumer(tenantId, providerSecGroupId, consumerSecGroupId, neutron, rwTx);
178             designContractsBetweenProviderAndConsumer(tenantId, consumerSecGroupId, providerSecGroupId, neutron, rwTx);
179         } else {
180             for (Uuid consumerSecGroupId : SecurityRuleUtils.findSecurityGroupsHavingSecurityRules(neutron)) {
181                 designContractsBetweenProviderAndConsumer(tenantId, providerSecGroupId, consumerSecGroupId, neutron, rwTx);
182                 designContractsBetweenProviderAndConsumer(tenantId, consumerSecGroupId, providerSecGroupId, neutron, rwTx);
183             }
184         }
185
186         ClassifierInstance classifierInstance = singleRuleContract.getSingleClassifierRule().getClassifierInstance();
187         createClassifierInstanceIfNotExists(tenantId, classifierInstance, rwTx);
188         createAllowActionInstanceIfNotExists(tenantId, rwTx);
189         return true;
190     }
191
192     @VisibleForTesting
193     static Description createContractDescription(SecurityRule secRule, Neutron neutron) {
194         if (NeutronMapperModule.isDebugEnabled()) {
195             Optional<SecurityGroup> providerSecGroup =
196                     SecurityGroupUtils.findSecurityGroup(secRule.getSecurityGroupId(), neutron.getSecurityGroups());
197             if (!providerSecGroup.isPresent()) {
198                 LOG.error("Neutron Security Group with UUID {} does not exist but it is in {}", secRule.getSecurityGroupId().getValue(),
199                         secRule);
200                 throw new IllegalStateException(
201                         "Neutron Security Group with UUID " + secRule.getSecurityGroupId().getValue() + " does not exist.");
202             }
203             return new Description(CONTRACT_PROVIDER + SecurityGroupUtils.getNameOrUuid(providerSecGroup.get()));
204         }
205
206         return new Description(CONTRACT_PROVIDER + secRule.getSecurityGroupId());
207     }
208
209     @VisibleForTesting
210     SingleRuleContract createSingleRuleContract(SecurityRule secRule, Description contractDescription,
211             ActionChoice action, Neutron neutron) {
212         if (secRule.getRemoteIpPrefix() != null) {
213             if (neutron.getNetworks() != null && neutron.getNetworks().getNetwork() != null) {
214                 java.util.Optional<Network> publicNet = neutron.getNetworks()
215                     .getNetwork()
216                     .stream()
217                     .filter(net -> NetworkUtils.isRouterExternal(net))
218                     .findAny();
219                 if (publicNet.isPresent()) {
220                     WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
221                     wTx.merge(LogicalDatastoreType.CONFIGURATION,
222                             IidFactory.externalImplicitGroupIid(new TenantId(secRule.getTenantId().getValue()),
223                                     MappingUtils.EPG_EXTERNAL_ID),
224                             new ExternalImplicitGroupBuilder().setId(MappingUtils.EPG_EXTERNAL_ID).build(), true);
225                     wTx.merge(LogicalDatastoreType.CONFIGURATION,
226                             IidFactory.endpointGroupIid(new TenantId(secRule.getTenantId().getValue()),
227                                     MappingUtils.EPG_EXTERNAL_ID),
228                             new EndpointGroupBuilder().setId(MappingUtils.EPG_EXTERNAL_ID).build(), true);
229                     DataStoreHelper.submitToDs(wTx);
230                     AddressEndpointRegBuilder addrEpbuilder = new AddressEndpointRegBuilder()
231                         .setAddress(String.valueOf(secRule.getRemoteIpPrefix().getValue()))
232                         .setAddressType(IpPrefixType.class)
233                         .setContextType(MappingUtils.L3_CONTEXT)
234                         .setContextId(new ContextId(publicNet.get().getUuid().getValue()))
235                         .setTenant(new TenantId(secRule.getTenantId().getValue()))
236                         .setTimestamp(System.currentTimeMillis())
237                         .setEndpointGroup(ImmutableList.<EndpointGroupId>of(MappingUtils.EPG_EXTERNAL_ID));
238                     RegisterEndpointInput regEp = new RegisterEndpointInputBuilder()
239                         .setAddressEndpointReg(ImmutableList.of(addrEpbuilder.build())).build();
240                     boolean registered = epRegistrator.registerEndpoint(regEp);
241                     if (registered) {
242                         LOG.info("Registering endpoint representing remote-ip-prefix {}", addrEpbuilder.getKey());
243                     } else {
244                         LOG.error("Failed to register endpoint {}", addrEpbuilder.getKey());
245                     }
246                 }
247             }
248             return new SingleRuleContract(secRule, 0, contractDescription, action);
249         }
250         return new SingleRuleContract(secRule, 400, contractDescription, action);
251     }
252
253     @VisibleForTesting
254     void designContractsBetweenProviderAndConsumer(TenantId tenantId, Uuid provSecGroupId, Uuid consSecGroupId,
255             Neutron neutron, ReadWriteTransaction rwTx) {
256         Set<SecurityRule> provSecRules = getProvidedSecRulesBetween(provSecGroupId, consSecGroupId, neutron);
257         Set<SecurityRule> consSecRules = getProvidedSecRulesBetween(consSecGroupId, provSecGroupId, neutron);
258         EndpointGroupId consEpgId = new EndpointGroupId(consSecGroupId.getValue());
259         for (SecurityRule provSecRule : provSecRules) {
260             if (isProviderSecRuleSuitableForConsumerSecRules(provSecRule, consSecRules)) {
261                 SelectorName consumerSelector = getSelectorNameWithProvider(provSecRule, neutron);
262                 ContractId contractId = SecRuleEntityDecoder.getContractId(provSecRule);
263                 writeConsumerNamedSelectorToEpg(consumerSelector, contractId, new EpgKeyDto(consEpgId, tenantId), rwTx);
264             }
265             // TODO add case when port ranges overlap
266         }
267     }
268
269     @VisibleForTesting
270     Set<SecurityRule> getProvidedSecRulesBetween(Uuid provSecGroup, Uuid consSecGroup, Neutron neutron) {
271         return Sets.union(SecurityRuleUtils.findSecurityRulesBySecGroupAndRemoteSecGroup(provSecGroup, consSecGroup, neutron),
272                 SecurityRuleUtils.findSecurityRulesBySecGroupAndRemoteSecGroup(provSecGroup, null, neutron));
273     }
274
275     @VisibleForTesting
276     static boolean isProviderSecRuleSuitableForConsumerSecRules(SecurityRule provSecRule,
277             Set<SecurityRule> consSecRules) {
278         Direction directionProvSecRule = SecRuleEntityDecoder.getDirection(provSecRule);
279         for (SecurityRule consSecRule : consSecRules) {
280             Direction directionConsSecRule = SecRuleEntityDecoder.getDirection(consSecRule);
281             if (isDirectionOpposite(directionProvSecRule, directionConsSecRule)
282                     && isOneWithinTwo(provSecRule, consSecRule)) {
283                 return true;
284             }
285         }
286         return false;
287     }
288
289     public boolean changeActionOfNeutronSecurityRule(SecurityRule secRule, ActionChoice action, Neutron neutron, ReadWriteTransaction rwTx) {
290         addSfcChainActionInstance(action, new TenantId(secRule.getTenantId().getValue()), rwTx);
291         LOG.trace("Changing to action {} for secuirity group rule {}", action, secRule);
292         return addNeutronSecurityRuleWithAction(secRule, neutron, action, rwTx);
293     }
294
295     private void addSfcChainActionInstance(ActionChoice action, TenantId tenantId, ReadWriteTransaction rwTx) {
296         if (action instanceof SfcActionCase) {
297             String sfcChainName = ((SfcActionCase) action).getSfcChainName();
298             ActionName actionName = new ActionName(sfcChainName);
299             ActionInstance sfcActionInstance = new ActionInstanceBuilder().setName(actionName)
300                 .setActionDefinitionId(ChainActionDefinition.ID)
301                 .setParameterValue(
302                         ImmutableList.of(new ParameterValueBuilder().setName(
303                                 new ParameterName(ChainActionDefinition.SFC_CHAIN_NAME))
304                             .setStringValue(sfcChainName)
305                             .build()))
306                 .build();
307             rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.actionInstanceIid(tenantId, actionName),
308                     sfcActionInstance, true);
309         }
310     }
311
312     private void writeProviderNamedSelectorToEpg(SelectorName providerSelector, ContractId contractId, EpgKeyDto epgKey,
313             WriteTransaction wTx) {
314         ProviderNamedSelector providerNamedSelector = new ProviderNamedSelectorBuilder().setName(providerSelector)
315             .setContract(ImmutableList.of(contractId))
316             .build();
317         wTx.put(LogicalDatastoreType.CONFIGURATION,
318                 IidFactory.providerNamedSelectorIid(epgKey.getTenantId(), epgKey.getEpgId(),
319                         providerNamedSelector.getName()), providerNamedSelector, true);
320     }
321
322     private void writeConsumerNamedSelectorToEpg(SelectorName consumerSelector, ContractId contractId, EpgKeyDto epgKey,
323             WriteTransaction wTx) {
324         ConsumerNamedSelector consumerNamedSelector = new ConsumerNamedSelectorBuilder().setName(consumerSelector)
325             .setContract(ImmutableList.of(contractId))
326             .build();
327         wTx.put(LogicalDatastoreType.CONFIGURATION,
328                 IidFactory.consumerNamedSelectorIid(epgKey.getTenantId(), epgKey.getEpgId(),
329                         consumerNamedSelector.getName()), consumerNamedSelector, true);
330     }
331
332     @VisibleForTesting
333     void createClassifierInstanceIfNotExists(TenantId tenantId, ClassifierInstance classifierInstance,
334             WriteTransaction wTx) {
335         InstanceIdentifier<ClassifierInstance> classifierInstanceIid = IidFactory.classifierInstanceIid(tenantId,
336                 classifierInstance.getName());
337         if (!createdClassifierInstances.contains(classifierInstanceIid)) {
338             wTx.put(LogicalDatastoreType.CONFIGURATION, classifierInstanceIid, classifierInstance, true);
339         }
340         createdClassifierInstances.add(classifierInstanceIid);
341     }
342
343     @VisibleForTesting
344     void createAllowActionInstanceIfNotExists(TenantId tenantId, ReadWriteTransaction rwTx) {
345         InstanceIdentifier<ActionInstance> actionInstanceIid = IidFactory.actionInstanceIid(tenantId,
346                 MappingUtils.ACTION_ALLOW.getName());
347         if (!createdActionInstances.contains(actionInstanceIid)) {
348             rwTx.put(LogicalDatastoreType.CONFIGURATION, actionInstanceIid, MappingUtils.ACTION_ALLOW, true);
349         }
350         createdActionInstances.add(actionInstanceIid);
351     }
352
353     @Override
354     public void onUpdated(SecurityRule oldSecRule, SecurityRule newSecRule, Neutron oldNeutron, Neutron newNeutron) {
355         LOG.warn("updated securityRule - Never should be called "
356                 + "- neutron API does not allow UPDATE on neutron security group rule. \nSecurity group rule: {}",
357                 newSecRule);
358     }
359
360     public void addPendingDeletedSecGroup(SecurityGroup secGroup) {
361         LOG.trace("Caching pending deleted security group {}", secGroup.getKey());
362         pendingDeletedGroups.put(secGroup.getKey(), secGroup);
363     }
364
365     @Override
366     public void onDeleted(SecurityRule deletedSecRule, Neutron oldNeutron, Neutron newNeutron) {
367         LOG.trace("deleted securityRule - {}", deletedSecRule);
368         ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
369         boolean isNeutronSecurityRuleDeleted = deleteNeutronSecurityRule(deletedSecRule, oldNeutron, rwTx);
370         if (isNeutronSecurityRuleDeleted) {
371             DataStoreHelper.submitToDs(rwTx);
372             if (newNeutron == null || newNeutron.getSecurityRules() == null
373                 || newNeutron.getSecurityRules().getSecurityRule() == null
374                 || !newNeutron.getSecurityRules()
375                         .getSecurityRule()
376                         .stream()
377                         .filter(rule -> rule.getSecurityGroupId().equals(deletedSecRule.getSecurityGroupId()))
378                         .findAny()
379                         .isPresent()) {
380                 SecurityGroupKey secGroupKey = new SecurityGroupKey(deletedSecRule.getSecurityGroupId());
381                 SecurityGroup pendingSg = pendingDeletedGroups.get(secGroupKey);
382                 if (pendingSg != null) {
383                     LOG.trace("Processing pending deleted security group {}", secGroupKey);
384                     NeutronSecurityGroupAware.deleteGbpEndpointGroup(dataProvider,
385                             new TenantId(deletedSecRule.getTenantId().getValue()),
386                             new EndpointGroupId(secGroupKey.getUuid().getValue()));
387                     pendingDeletedGroups.remove(secGroupKey);
388                 }
389             }
390         } else {
391             rwTx.cancel();
392         }
393     }
394
395     public boolean deleteNeutronSecurityRule(SecurityRule secRule, Neutron neutron, ReadWriteTransaction rwTx) {
396         TenantId tenantId = new TenantId(secRule.getTenantId().getValue());
397         Uuid providerSecGroupId = secRule.getSecurityGroupId();
398         EndpointGroupId providerEpgId = new EndpointGroupId(providerSecGroupId.getValue());
399
400         SelectorName providerSelector = getSelectorNameWithConsumer(secRule, neutron);
401         deleteProviderNamedSelectorFromEpg(providerSelector, new EpgKeyDto(providerEpgId, tenantId), rwTx);
402
403         if (secRule.getRemoteGroupId() != null) {
404             Uuid consumerSecGroupId = secRule.getRemoteGroupId();
405             undesignContractsBetweenProviderAndConsumer(tenantId, providerSecGroupId, consumerSecGroupId, secRule, neutron, rwTx);
406             undesignContractsBetweenProviderAndConsumer(tenantId, consumerSecGroupId, providerSecGroupId, secRule, neutron, rwTx);
407         } else {
408             for (Uuid consumerSecGroupId : SecurityRuleUtils.findSecurityGroupsHavingSecurityRules(neutron)) {
409                 undesignContractsBetweenProviderAndConsumer(tenantId, providerSecGroupId, consumerSecGroupId, secRule, neutron, rwTx);
410                 undesignContractsBetweenProviderAndConsumer(tenantId, consumerSecGroupId, providerSecGroupId, secRule, neutron, rwTx);
411             }
412         }
413
414         ContractId contractId = SecRuleEntityDecoder.getContractId(secRule);
415         rwTx.delete(LogicalDatastoreType.CONFIGURATION, IidFactory.contractIid(tenantId, contractId));
416
417         ClassifierInstance classifierInstance = SecRuleEntityDecoder.getClassifierInstance(secRule);
418         deleteClassifierInstanceIfNotUsed(tenantId, classifierInstance, rwTx);
419         return true;
420     }
421
422     @VisibleForTesting
423     void undesignContractsBetweenProviderAndConsumer(TenantId tenantId, Uuid provSecGroupId,
424             Uuid consSecGroupId, SecurityRule removedSecRule, Neutron neutron, ReadWriteTransaction rwTx) {
425         Set<SecurityRule> provSecRules = getProvidedSecRulesBetween(provSecGroupId, consSecGroupId, neutron);
426         Set<SecurityRule> consSecRules = getProvidedSecRulesBetween(consSecGroupId, provSecGroupId, neutron);
427         EndpointGroupId consEpgId = new EndpointGroupId(consSecGroupId.getValue());
428         for (SecurityRule provSecRule : provSecRules) {
429             if (isProvidersSecRuleSuitableForConsumersSecRulesAndGoodToRemove(provSecRule, consSecRules, removedSecRule)) {
430                 SelectorName consumerSelector = getSelectorNameWithProvider(provSecRule, neutron);
431                 deleteConsumerNamedSelector(consumerSelector, new EpgKeyDto(consEpgId, tenantId), rwTx);
432             }
433             // TODO add case when port ranges overlap
434         }
435     }
436
437     @VisibleForTesting
438     static boolean isProvidersSecRuleSuitableForConsumersSecRulesAndGoodToRemove(SecurityRule provSecRule,
439             Set<SecurityRule> consSecRules, SecurityRule removedSecRule) {
440         Direction directionProvSecRule = SecRuleEntityDecoder.getDirection(provSecRule);
441         for (SecurityRule consSecRule : consSecRules) {
442             if (isRuleIdEqual(removedSecRule, consSecRule) || isRuleIdEqual(removedSecRule, provSecRule)) {
443                 Direction directionConsSecRule = SecRuleEntityDecoder.getDirection(consSecRule);
444                 if (isDirectionOpposite(directionProvSecRule, directionConsSecRule)
445                         && isOneWithinTwo(provSecRule, consSecRule)) {
446                     return true;
447                 }
448             }
449         }
450         return false;
451     }
452
453     @VisibleForTesting
454     static boolean isRuleIdEqual(SecurityRule one, SecurityRule two) {
455         checkNotNull(one);
456         checkNotNull(two);
457         return one.getSecurityGroupId().equals(two.getSecurityGroupId());
458     }
459
460     private void deleteProviderNamedSelectorFromEpg(SelectorName providerSelector, EpgKeyDto providerEpgKey,
461             ReadWriteTransaction rwTx) {
462         InstanceIdentifier<ProviderNamedSelector> providerSelectorIid = IidFactory.providerNamedSelectorIid(
463                 providerEpgKey.getTenantId(), providerEpgKey.getEpgId(), providerSelector);
464         DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, providerSelectorIid, rwTx);
465     }
466
467     private void deleteConsumerNamedSelector(SelectorName consumerSelector, EpgKeyDto consumerEpgKey,
468             ReadWriteTransaction rwTx) {
469         InstanceIdentifier<ConsumerNamedSelector> consumerSelectorIid = IidFactory.consumerNamedSelectorIid(
470                 consumerEpgKey.getTenantId(), consumerEpgKey.getEpgId(), consumerSelector);
471         DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, consumerSelectorIid, rwTx);
472     }
473
474     private void deleteClassifierInstanceIfNotUsed(TenantId tenantId, ClassifierInstance classifierInstance,
475             ReadWriteTransaction rwTx) {
476         InstanceIdentifier<ClassifierInstance> classifierInstanceIid = IidFactory.classifierInstanceIid(tenantId,
477                 classifierInstance.getName());
478         createdClassifierInstances.remove(classifierInstanceIid);
479         if (!createdClassifierInstances.contains(classifierInstanceIid)) {
480             DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, classifierInstanceIid, rwTx);
481         }
482     }
483
484     @VisibleForTesting
485     void deleteAllowActionInstanceIfNotUsed(TenantId tenantId, ReadWriteTransaction rwTx) {
486         InstanceIdentifier<ActionInstance> actionInstanceIid = IidFactory.actionInstanceIid(tenantId,
487                 MappingUtils.ACTION_ALLOW.getName());
488         createdActionInstances.remove(actionInstanceIid);
489         if (!createdActionInstances.contains(actionInstanceIid)) {
490             DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, actionInstanceIid, rwTx);
491         }
492     }
493
494     private SelectorName getSelectorNameWithConsumer(SecurityRule secRule, Neutron neutron) {
495         ClauseName clauseName = SecRuleNameDecoder.getClauseName(secRule);
496         StringBuilder selectorNameBuilder = new StringBuilder().append(clauseName.getValue());
497         Uuid consumerSecGroupId = secRule.getRemoteGroupId();
498         if (consumerSecGroupId == null) {
499             return new SelectorName(selectorNameBuilder.toString());
500         }
501
502         // we cannot use name of security group in selector, because name can be changed
503         // therefore name is used only in debug mode
504         if (NeutronMapperModule.isDebugEnabled()) {
505             Optional<SecurityGroup> potentialConsumerSecGroup =
506                     SecurityGroupUtils.findSecurityGroup(secRule.getRemoteGroupId(), neutron.getSecurityGroups());
507             if (!potentialConsumerSecGroup.isPresent()) {
508                 LOG.error("Neutron Security Group with UUID {} does not exist but it is in {}",
509                         consumerSecGroupId.getValue(), secRule);
510                 throw new IllegalStateException(
511                         "Neutron Security Group with UUID " + consumerSecGroupId.getValue() + " does not exist.");
512             }
513
514             selectorNameBuilder.append(MappingUtils.NAME_DOUBLE_DELIMETER)
515                 .append(POSSIBLE_CONSUMER)
516                 .append(SecurityGroupUtils.getNameOrUuid(potentialConsumerSecGroup.get()));
517             return new SelectorName(selectorNameBuilder.toString());
518         }
519
520         selectorNameBuilder.append(MappingUtils.NAME_DOUBLE_DELIMETER)
521             .append(POSSIBLE_CONSUMER)
522             .append(consumerSecGroupId.getValue());
523         return new SelectorName(selectorNameBuilder.toString());
524     }
525
526     private SelectorName getSelectorNameWithProvider(SecurityRule secRule, Neutron neutron) {
527         ClauseName clauseName = SecRuleNameDecoder.getClauseName(secRule);
528         Uuid providerSecGroupId = secRule.getSecurityGroupId();
529
530         // we cannot use name of security group in selector, because name can be changed
531         // therefore name is used only in debug mode
532         if (NeutronMapperModule.isDebugEnabled()) {
533             Optional<SecurityGroup> potentialProviderSecGroup =
534                     SecurityGroupUtils.findSecurityGroup(secRule.getSecurityGroupId(), neutron.getSecurityGroups());
535             if (!potentialProviderSecGroup.isPresent()) {
536                 LOG.error("Neutron Security Group with UUID {} does not exist but it is in {}",
537                         providerSecGroupId.getValue(), secRule);
538                 throw new IllegalStateException(
539                         "Neutron Security Group with UUID " + providerSecGroupId.getValue() + " does not exist.");
540             }
541             String selectorName = new StringBuilder().append(clauseName.getValue())
542                 .append(MappingUtils.NAME_DOUBLE_DELIMETER)
543                 .append(PROVIDED_BY)
544                 .append(SecurityGroupUtils.getNameOrUuid(potentialProviderSecGroup.get()))
545                 .toString();
546             return new SelectorName(selectorName);
547         }
548
549         String selectorName = new StringBuilder().append(clauseName.getValue())
550             .append(MappingUtils.NAME_DOUBLE_DELIMETER)
551             .append(PROVIDED_BY)
552             .append(providerSecGroupId.getValue())
553             .toString();
554         return new SelectorName(selectorName);
555     }
556
557     @VisibleForTesting
558     static boolean isDirectionOpposite(Direction one, Direction two) {
559         return (one == Direction.In && two == Direction.Out) || (one == Direction.Out && two == Direction.In);
560     }
561
562     @VisibleForTesting
563     static boolean isOneWithinTwo(SecurityRule one, SecurityRule two) {
564         if (!isOneGroupIdWithinTwoRemoteGroupId(one, two) || !isOneGroupIdWithinTwoRemoteGroupId(two, one))
565             return false;
566         if (!SecRuleEntityDecoder.isEtherTypeOfOneWithinTwo(one, two))
567             return false;
568         if (!SecRuleEntityDecoder.isProtocolOfOneWithinTwo(one, two))
569             return false;
570         if (!SecRuleEntityDecoder.isPortsOfOneWithinTwo(one, two))
571             return false;
572         if (two.getRemoteIpPrefix() != null
573                 && one.getRemoteIpPrefix() == null)
574             return false;
575         return true;
576     }
577
578     @VisibleForTesting
579     static boolean isOneGroupIdWithinTwoRemoteGroupId(SecurityRule one, SecurityRule two) {
580         return (two.getRemoteGroupId() == null || two.getRemoteGroupId().equals(
581                 one.getSecurityGroupId()));
582     }
583
584 }