More transaction clean-up in ACL
[netvirt.git] / aclservice / impl / src / main / java / org / opendaylight / netvirt / aclservice / listeners / AclInterfaceListener.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. 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 package org.opendaylight.netvirt.aclservice.listeners;
9
10 import com.google.common.util.concurrent.Futures;
11 import java.util.ArrayList;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.concurrent.ExecutionException;
15 import javax.annotation.PostConstruct;
16 import javax.inject.Inject;
17 import javax.inject.Singleton;
18 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
22 import org.opendaylight.netvirt.aclservice.api.AclInterfaceCache;
23 import org.opendaylight.netvirt.aclservice.api.AclServiceManager;
24 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
25 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
26 import org.opendaylight.netvirt.aclservice.utils.AclClusterUtil;
27 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
28 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefs;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionEgress;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionIngress;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAcl;
36 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 @Singleton
41 public class AclInterfaceListener extends AsyncDataTreeChangeListenerBase<Interface, AclInterfaceListener>
42         implements ClusteredDataTreeChangeListener<Interface> {
43     private static final Logger LOG = LoggerFactory.getLogger(AclInterfaceListener.class);
44
45     private final AclServiceManager aclServiceManager;
46     private final AclClusterUtil aclClusterUtil;
47     private final DataBroker dataBroker;
48     private final AclDataUtil aclDataUtil;
49     private final AclInterfaceCache aclInterfaceCache;
50     private final AclServiceUtils aclServiceUtils;
51
52     @Inject
53     public AclInterfaceListener(AclServiceManager aclServiceManager, AclClusterUtil aclClusterUtil,
54             DataBroker dataBroker, AclDataUtil aclDataUtil, AclInterfaceCache aclInterfaceCache,
55             AclServiceUtils aclServicUtils) {
56         super(Interface.class, AclInterfaceListener.class);
57         this.aclServiceManager = aclServiceManager;
58         this.aclClusterUtil = aclClusterUtil;
59         this.dataBroker = dataBroker;
60         this.aclDataUtil = aclDataUtil;
61         this.aclInterfaceCache = aclInterfaceCache;
62         this.aclServiceUtils = aclServicUtils;
63     }
64
65     @Override
66     @PostConstruct
67     public void init() {
68         LOG.info("{} start", getClass().getSimpleName());
69         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
70     }
71
72     @Override
73     protected InstanceIdentifier<Interface> getWildCardPath() {
74         return InstanceIdentifier
75                 .create(Interfaces.class)
76                 .child(Interface.class);
77     }
78
79     @Override
80     protected void remove(InstanceIdentifier<Interface> key, Interface port) {
81         LOG.trace("Received AclInterface remove event, port={}", port);
82         String interfaceId = port.getName();
83         AclInterface aclInterface = aclInterfaceCache.remove(interfaceId);
84         if (AclServiceUtils.isOfInterest(aclInterface)) {
85             if (aclClusterUtil.isEntityOwner()) {
86                 LOG.debug("On remove event, notify ACL service manager to unbind ACL from interface: {}", port);
87                 aclServiceManager.notify(aclInterface, null, Action.UNBIND);
88                 AclServiceUtils.deleteSubnetIpPrefixes(dataBroker, interfaceId);
89             }
90         }
91     }
92
93     @Override
94     protected void update(InstanceIdentifier<Interface> key, Interface portBefore, Interface portAfter) {
95         if (portBefore.getAugmentation(ParentRefs.class) == null
96                 && portAfter.getAugmentation(ParentRefs.class) != null) {
97             LOG.trace("Ignoring event for update in ParentRefs for {} ", portAfter.getName());
98             return;
99         }
100         LOG.trace("Received AclInterface update event, portBefore={}, portAfter={}", portBefore, portAfter);
101         InterfaceAcl aclInPortAfter = portAfter.getAugmentation(InterfaceAcl.class);
102         InterfaceAcl aclInPortBefore = portBefore.getAugmentation(InterfaceAcl.class);
103
104         String interfaceId = portAfter.getName();
105         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
106             .Interface interfaceState = AclServiceUtils.getInterfaceStateFromOperDS(dataBroker, interfaceId);
107
108         AclInterface aclInterfaceBefore = aclInterfaceCache.get(interfaceId);
109         if (aclInterfaceBefore == null || isPortSecurityEnabledNow(aclInPortBefore, aclInPortAfter)) {
110             // Updating cache now as it might have not updated when
111             // port-security-enable=false
112             aclInterfaceBefore = addOrUpdateAclInterfaceCache(interfaceId, aclInPortBefore, true, interfaceState);
113         }
114         if (aclInPortAfter != null && aclInPortAfter.isPortSecurityEnabled()
115                 || aclInPortBefore != null && aclInPortBefore.isPortSecurityEnabled()) {
116             boolean isSgChanged =
117                     isSecurityGroupsChanged(aclInPortBefore.getSecurityGroups(), aclInPortAfter.getSecurityGroups());
118             AclInterface aclInterfaceAfter =
119                     addOrUpdateAclInterfaceCache(interfaceId, aclInPortAfter, isSgChanged, interfaceState);
120
121             if (aclClusterUtil.isEntityOwner()) {
122                 // Handle bind/unbind service irrespective of interface state (up/down)
123                 boolean isPortSecurityEnable = aclInterfaceAfter.isPortSecurityEnabled();
124                 boolean isPortSecurityEnableBefore = aclInterfaceBefore.isPortSecurityEnabled();
125                 // if port security enable is changed, bind/unbind ACL service
126                 if (isPortSecurityEnableBefore != isPortSecurityEnable) {
127                     LOG.debug("Notify bind/unbind ACL service for interface={}, isPortSecurityEnable={}", interfaceId,
128                             isPortSecurityEnable);
129                     if (isPortSecurityEnable) {
130                         aclServiceManager.notify(aclInterfaceAfter, null, Action.BIND);
131                     } else {
132                         aclServiceManager.notify(aclInterfaceAfter, null, Action.UNBIND);
133                     }
134                 }
135                 if (interfaceState != null && interfaceState.getOperStatus().equals(
136                         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
137                             .state.Interface.OperStatus.Up)) {
138                     LOG.debug("On update event, notify ACL service manager to update ACL for interface: {}",
139                             interfaceId);
140                     // handle add for AclPortsLookup before processing update
141                     try {
142                         Futures.allAsList(aclServiceUtils.addAclPortsLookupForInterfaceUpdate(aclInterfaceBefore,
143                                 aclInterfaceAfter)).get();
144                     } catch (InterruptedException | ExecutionException e) {
145                         LOG.error("Error adding ACL ports for interface update", e);
146                     }
147
148                     aclServiceManager.notify(aclInterfaceAfter, aclInterfaceBefore, AclServiceManager.Action.UPDATE);
149                     // handle delete for AclPortsLookup after processing update
150                     try {
151                         Futures.allAsList(aclServiceUtils.deleteAclPortsLookupForInterfaceUpdate(aclInterfaceBefore,
152                                 aclInterfaceAfter)).get();
153                     } catch (InterruptedException | ExecutionException e) {
154                         LOG.error("Error deleting ACL ports for interface update", e);
155                     }
156                 }
157             }
158             updateCacheWithAclChange(aclInterfaceBefore, aclInterfaceAfter);
159         }
160     }
161
162     private void updateCacheWithAclChange(AclInterface aclInterfaceBefore, AclInterface aclInterfaceAfter) {
163         List<Uuid> addedAcls = AclServiceUtils.getUpdatedAclList(aclInterfaceAfter.getSecurityGroups(),
164                 aclInterfaceBefore.getSecurityGroups());
165         List<Uuid> deletedAcls = AclServiceUtils.getUpdatedAclList(aclInterfaceBefore.getSecurityGroups(),
166                 aclInterfaceAfter.getSecurityGroups());
167         if (deletedAcls != null && !deletedAcls.isEmpty()) {
168             aclDataUtil.removeAclInterfaceMap(deletedAcls, aclInterfaceAfter);
169         }
170         if (addedAcls != null && !addedAcls.isEmpty()) {
171             aclDataUtil.addOrUpdateAclInterfaceMap(addedAcls, aclInterfaceAfter);
172         }
173     }
174
175     private boolean isPortSecurityEnabledNow(InterfaceAcl aclInPortBefore, InterfaceAcl aclInPortAfter) {
176         return aclInPortBefore != null && !aclInPortBefore.isPortSecurityEnabled() && aclInPortAfter != null
177                 && aclInPortAfter.isPortSecurityEnabled();
178     }
179
180     private boolean isSecurityGroupsChanged(List<Uuid> sgsBefore, List<Uuid> sgsAfter) {
181         if (sgsBefore == null && sgsAfter == null) {
182             return false;
183         }
184         if ((sgsBefore == null && sgsAfter != null) || (sgsBefore != null && sgsAfter == null)) {
185             return true;
186         }
187         if (sgsBefore != null && sgsAfter != null) {
188             return !(new HashSet<>(sgsBefore)).equals(new HashSet<>(sgsAfter));
189         }
190         return true;
191     }
192
193     private AclInterface addOrUpdateAclInterfaceCache(String interfaceId, InterfaceAcl aclInPort) {
194         return addOrUpdateAclInterfaceCache(interfaceId, aclInPort, true, null);
195     }
196
197     private AclInterface addOrUpdateAclInterfaceCache(String interfaceId, InterfaceAcl aclInPort, boolean isSgChanged,
198             org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
199                     .Interface interfaceState) {
200         AclInterface aclInterface = aclInterfaceCache.addOrUpdate(interfaceId, (prevAclInterface, builder) -> {
201             List<Uuid> sgs = new ArrayList<>();
202             if (aclInPort != null) {
203                 sgs = aclInPort.getSecurityGroups();
204                 builder.portSecurityEnabled(aclInPort.isPortSecurityEnabled()).securityGroups(sgs)
205                         .allowedAddressPairs(aclInPort.getAllowedAddressPairs());
206             }
207
208             if ((prevAclInterface == null || prevAclInterface.getLPortTag() == null) && interfaceState != null) {
209                 builder.dpId(AclServiceUtils.getDpIdFromIterfaceState(interfaceState))
210                         .lPortTag(interfaceState.getIfIndex()).isMarkedForDelete(false);
211             }
212
213             if (prevAclInterface == null) {
214                 builder.subnetIpPrefixes(AclServiceUtils.getSubnetIpPrefixes(dataBroker, interfaceId));
215             }
216             if (prevAclInterface == null || prevAclInterface.getElanId() == null) {
217                 builder.elanId(AclServiceUtils.getElanIdFromInterface(interfaceId, dataBroker));
218             }
219             if (prevAclInterface == null || isSgChanged) {
220                 builder.ingressRemoteAclTags(aclServiceUtils.getRemoteAclTags(sgs, DirectionIngress.class))
221                         .egressRemoteAclTags(aclServiceUtils.getRemoteAclTags(sgs, DirectionEgress.class));
222             }
223         });
224         // Clone and return the ACL interface object
225         return AclInterface.builder(aclInterface).build();
226     }
227
228     @Override
229     protected void add(InstanceIdentifier<Interface> key, Interface port) {
230         LOG.trace("Received AclInterface add event, port={}", port);
231         InterfaceAcl aclInPort = port.getAugmentation(InterfaceAcl.class);
232         if (aclInPort != null && aclInPort.isPortSecurityEnabled()) {
233             String interfaceId = port.getName();
234             AclInterface aclInterface = addOrUpdateAclInterfaceCache(interfaceId, aclInPort);
235
236             if (aclClusterUtil.isEntityOwner()) {
237                 LOG.debug("On add event, notify ACL service manager to bind ACL for interface: {}", port);
238                 aclServiceManager.notify(aclInterface, null, Action.BIND);
239             }
240         }
241     }
242
243     @Override
244     protected AclInterfaceListener getDataTreeChangeListener() {
245         return this;
246     }
247 }