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