MRI version bumpup for Aluminium
[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 java.util.ArrayList;
11 import java.util.List;
12 import javax.annotation.PreDestroy;
13 import javax.inject.Inject;
14 import javax.inject.Singleton;
15 import org.eclipse.jdt.annotation.Nullable;
16 import org.opendaylight.infrautils.utils.concurrent.Executors;
17 import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
18 import org.opendaylight.mdsal.binding.api.DataBroker;
19 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
20 import org.opendaylight.netvirt.aclservice.api.AclInterfaceCache;
21 import org.opendaylight.netvirt.aclservice.api.AclServiceManager;
22 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
23 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
24 import org.opendaylight.netvirt.aclservice.utils.AclClusterUtil;
25 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
26 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
27 import org.opendaylight.serviceutils.srm.RecoverableListener;
28 import org.opendaylight.serviceutils.srm.ServiceRecoveryRegistry;
29 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefs;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionEgress;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionIngress;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAcl;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.SubnetInfo;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 @Singleton
44 public class AclInterfaceListener extends AbstractAsyncDataTreeChangeListener<Interface>
45         implements ClusteredDataTreeChangeListener<Interface>, RecoverableListener {
46     private static final Logger LOG = LoggerFactory.getLogger(AclInterfaceListener.class);
47
48     private final AclServiceManager aclServiceManager;
49     private final AclClusterUtil aclClusterUtil;
50     private final DataBroker dataBroker;
51     private final AclDataUtil aclDataUtil;
52     private final AclInterfaceCache aclInterfaceCache;
53     private final AclServiceUtils aclServiceUtils;
54
55     @Inject
56     public AclInterfaceListener(AclServiceManager aclServiceManager, AclClusterUtil aclClusterUtil,
57             DataBroker dataBroker, AclDataUtil aclDataUtil, AclInterfaceCache aclInterfaceCache,
58             AclServiceUtils aclServicUtils, ServiceRecoveryRegistry serviceRecoveryRegistry) {
59         super(dataBroker, LogicalDatastoreType.CONFIGURATION,
60                 InstanceIdentifier.create(Interfaces.class).child(Interface.class),
61                 Executors.newListeningSingleThreadExecutor("AclEventListener", LOG));
62         this.aclServiceManager = aclServiceManager;
63         this.aclClusterUtil = aclClusterUtil;
64         this.dataBroker = dataBroker;
65         this.aclDataUtil = aclDataUtil;
66         this.aclInterfaceCache = aclInterfaceCache;
67         this.aclServiceUtils = aclServicUtils;
68         serviceRecoveryRegistry.addRecoverableListener(AclServiceUtils.getRecoverServiceRegistryKey(), this);
69     }
70
71     public void init() {
72         LOG.info("{} start", getClass().getSimpleName());
73     }
74
75     @Override
76     public void registerListener() {
77         super.register();
78     }
79
80     @Override
81     public void deregisterListener() {
82         super.close();
83     }
84
85     @Override
86     public void remove(InstanceIdentifier<Interface> key, Interface port) {
87         LOG.trace("Received AclInterface remove event, port={}", port);
88         String interfaceId = port.getName();
89         AclInterface aclInterface = aclInterfaceCache.remove(interfaceId);
90         if (AclServiceUtils.isOfInterest(aclInterface)) {
91             if (aclClusterUtil.isEntityOwner()) {
92                 LOG.debug("On remove event, notify ACL unbind/remove for interface: {}", interfaceId);
93                 aclServiceManager.notify(aclInterface, null, Action.UNBIND);
94                 if (aclInterface.getDpId() != null) {
95                     aclServiceManager.notify(aclInterface, null, Action.REMOVE);
96                 }
97             }
98         }
99     }
100
101     @Override
102     public void update(@Nullable InstanceIdentifier<Interface> key, Interface portBefore, Interface portAfter) {
103         if (portBefore.augmentation(ParentRefs.class) == null
104                 && portAfter.augmentation(ParentRefs.class) != null) {
105             LOG.trace("Ignoring event for update in ParentRefs for {} ", portAfter.getName());
106             return;
107         }
108         LOG.trace("Received AclInterface update event, portBefore={}, portAfter={}", portBefore, portAfter);
109         InterfaceAcl aclInPortAfter = portAfter.augmentation(InterfaceAcl.class);
110         InterfaceAcl aclInPortBefore = portBefore.augmentation(InterfaceAcl.class);
111
112         String interfaceId = portAfter.getName();
113         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
114             .Interface interfaceState = AclServiceUtils.getInterfaceStateFromOperDS(dataBroker, interfaceId);
115
116         AclInterface aclInterfaceBefore = aclInterfaceCache.get(interfaceId);
117         if (aclInterfaceBefore == null || isPortSecurityEnabledNow(aclInPortBefore, aclInPortAfter)) {
118             // Updating cache now as it might have not updated when
119             // port-security-enable=false
120             aclInterfaceBefore = addOrUpdateAclInterfaceCache(interfaceId, aclInPortBefore, true, interfaceState);
121         }
122         if (AclServiceUtils.isOfInterest(aclInPortAfter) || AclServiceUtils.isOfInterest(aclInPortBefore)) {
123             List<Uuid> sgsBefore = null;
124             if (aclInPortBefore != null) {
125                 sgsBefore = aclInPortBefore.getSecurityGroups();
126             }
127             boolean isSgChanged = isSecurityGroupsChanged(sgsBefore, aclInPortAfter.getSecurityGroups());
128             AclInterface aclInterfaceAfter =
129                     addOrUpdateAclInterfaceCache(interfaceId, aclInPortAfter, isSgChanged, interfaceState);
130             updateCacheWithAddedAcls(aclInterfaceBefore, aclInterfaceAfter);
131
132             if (aclClusterUtil.isEntityOwner()) {
133                 // Handle bind/unbind service irrespective of interface state (up/down)
134                 boolean isPortSecurityEnable = aclInterfaceAfter.isPortSecurityEnabled();
135                 boolean isPortSecurityEnableBefore = aclInterfaceBefore.isPortSecurityEnabled();
136                 // if port security enable is changed and is disabled, unbind ACL service
137                 if (isPortSecurityEnableBefore != isPortSecurityEnable && !isPortSecurityEnable) {
138                     LOG.debug("Notify unbind ACL service for interface={}, isPortSecurityEnable={}", interfaceId,
139                             isPortSecurityEnable);
140                     aclServiceManager.notify(aclInterfaceAfter, null, Action.UNBIND);
141                 }
142                 if (interfaceState != null && org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces
143                         .rev140508.interfaces.state.Interface.OperStatus.Up.equals(interfaceState.getOperStatus())) {
144                     // if port security enable is changed and is enabled, bind ACL service
145                     if (isPortSecurityEnableBefore != isPortSecurityEnable && isPortSecurityEnable) {
146                         LOG.debug("Notify bind ACL service for interface={}, isPortSecurityEnable={}", interfaceId,
147                                 isPortSecurityEnable);
148                         aclServiceManager.notify(aclInterfaceAfter, null, Action.BIND);
149                     }
150                     LOG.debug("On update event, notify ACL service manager to update ACL for interface: {}",
151                             interfaceId);
152                     // handle add for AclPortsLookup before processing update
153                     aclServiceUtils.addAclPortsLookupForInterfaceUpdate(aclInterfaceBefore, aclInterfaceAfter);
154
155                     aclServiceManager.notify(aclInterfaceAfter, aclInterfaceBefore, AclServiceManager.Action.UPDATE);
156                     // handle delete for AclPortsLookup after processing update
157                     aclServiceUtils.deleteAclPortsLookupForInterfaceUpdate(aclInterfaceBefore, aclInterfaceAfter);
158                 }
159             }
160             updateCacheWithAclChange(aclInterfaceBefore, aclInterfaceAfter);
161         }
162     }
163
164     private void updateCacheWithAclChange(AclInterface aclInterfaceBefore, AclInterface aclInterfaceAfter) {
165         List<Uuid> deletedAcls = AclServiceUtils.getUpdatedAclList(aclInterfaceBefore.getSecurityGroups(),
166                 aclInterfaceAfter.getSecurityGroups());
167         if (!deletedAcls.isEmpty()) {
168             aclDataUtil.removeAclInterfaceMap(deletedAcls, aclInterfaceAfter);
169         }
170         List<AllowedAddressPairs> addedAap = AclServiceUtils.getUpdatedAllowedAddressPairs(aclInterfaceAfter
171                 .getAllowedAddressPairs(), aclInterfaceBefore.getAllowedAddressPairs());
172         List<AllowedAddressPairs> deletedAap = AclServiceUtils.getUpdatedAllowedAddressPairs(aclInterfaceBefore
173                 .getAllowedAddressPairs(), aclInterfaceAfter.getAllowedAddressPairs());
174         if (!deletedAap.isEmpty() || !addedAap.isEmpty()) {
175             LOG.debug("Update cache with new AAP = {}", aclInterfaceAfter.getInterfaceId());
176             aclDataUtil.addOrUpdateAclInterfaceMap(aclInterfaceAfter.getSecurityGroups(), aclInterfaceAfter);
177         }
178     }
179
180     private void updateCacheWithAddedAcls(AclInterface aclInterfaceBefore, AclInterface aclInterfaceAfter) {
181         List<Uuid> addedAcls = AclServiceUtils.getUpdatedAclList(aclInterfaceAfter.getSecurityGroups(),
182                 aclInterfaceBefore.getSecurityGroups());
183         if (addedAcls != null && !addedAcls.isEmpty()) {
184             LOG.debug("Update cache by adding interface={}", aclInterfaceAfter.getInterfaceId());
185             aclDataUtil.addOrUpdateAclInterfaceMap(addedAcls, aclInterfaceAfter);
186         }
187     }
188
189     private boolean isPortSecurityEnabledNow(InterfaceAcl aclInPortBefore, InterfaceAcl aclInPortAfter) {
190         return aclInPortBefore != null && !aclInPortBefore.isPortSecurityEnabled() && aclInPortAfter != null
191                 && aclInPortAfter.isPortSecurityEnabled();
192     }
193
194     private boolean isSecurityGroupsChanged(@Nullable List<Uuid> sgsBefore, @Nullable List<Uuid> sgsAfter) {
195         if (sgsBefore == null && sgsAfter == null) {
196             return false;
197         }
198         if (sgsBefore == null || sgsAfter == null) {
199             return true;
200         }
201         return !(sgsBefore.containsAll(sgsAfter) && sgsAfter.containsAll(sgsBefore));
202     }
203
204     private AclInterface addOrUpdateAclInterfaceCache(String interfaceId, InterfaceAcl aclInPort) {
205         return addOrUpdateAclInterfaceCache(interfaceId, aclInPort, true, null);
206     }
207
208     private AclInterface addOrUpdateAclInterfaceCache(String interfaceId, InterfaceAcl aclInPort, boolean isSgChanged,
209             org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
210                 .@Nullable Interface interfaceState) {
211         AclInterface aclInterface = aclInterfaceCache.addOrUpdate(interfaceId, (prevAclInterface, builder) -> {
212             List<Uuid> sgs = new ArrayList<>();
213             if (aclInPort != null) {
214                 sgs = aclInPort.getSecurityGroups();
215                 builder.portSecurityEnabled(aclInPort.isPortSecurityEnabled())
216                         .interfaceType(aclInPort.getInterfaceType()).securityGroups(sgs)
217                         .allowedAddressPairs(new ArrayList<AllowedAddressPairs>(aclInPort
218                                 .getAllowedAddressPairs().values())).subnetInfo(new ArrayList<SubnetInfo>(aclInPort
219                         .getSubnetInfo().values()));
220             }
221
222             if ((prevAclInterface == null || prevAclInterface.getLPortTag() == null) && interfaceState != null) {
223                 builder.dpId(AclServiceUtils.getDpIdFromIterfaceState(interfaceState))
224                         .lPortTag(interfaceState.getIfIndex()).isMarkedForDelete(false);
225             }
226
227             if (prevAclInterface == null || prevAclInterface.getElanId() == null) {
228                 builder.elanId(AclServiceUtils.getElanIdFromInterface(interfaceId, dataBroker));
229             }
230             if (prevAclInterface == null || isSgChanged) {
231                 builder.ingressRemoteAclTags(aclServiceUtils.getRemoteAclTags(sgs, DirectionIngress.class))
232                         .egressRemoteAclTags(aclServiceUtils.getRemoteAclTags(sgs, DirectionEgress.class));
233             }
234         });
235         // Clone and return the ACL interface object
236         return AclInterface.builder(aclInterface).build();
237     }
238
239     @Override
240     public void add(InstanceIdentifier<Interface> key, Interface port) {
241         LOG.trace("Received AclInterface add event, port={}", port);
242         InterfaceAcl aclInPort = port.augmentation(InterfaceAcl.class);
243         if (AclServiceUtils.isOfInterest(aclInPort)) {
244             String interfaceId = port.getName();
245             AclInterface aclInterface = addOrUpdateAclInterfaceCache(interfaceId, aclInPort);
246
247             // if interface state event comes first followed by interface config event.
248             if (aclInterface.getDpId() != null && aclInterface.getElanId() != null && aclClusterUtil.isEntityOwner()) {
249                 LOG.debug("On add event, notify ACL bind/add for interface: {}", interfaceId);
250                 aclServiceManager.notify(aclInterface, null, Action.BIND);
251                 aclServiceManager.notify(aclInterface, null, Action.ADD);
252             }
253         }
254     }
255
256     @Override
257     @PreDestroy
258     public void close() {
259         super.close();
260         Executors.shutdownAndAwaitTermination(getExecutorService());
261     }
262 }