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