Fixup Augmentable and Identifiable methods changing
[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.genius.srm.RecoverableListener;
23 import org.opendaylight.genius.srm.ServiceRecoveryRegistry;
24 import org.opendaylight.netvirt.aclservice.api.AclInterfaceCache;
25 import org.opendaylight.netvirt.aclservice.api.AclServiceManager;
26 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
27 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
28 import org.opendaylight.netvirt.aclservice.utils.AclClusterUtil;
29 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
30 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefs;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionEgress;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionIngress;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAcl;
38 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 @Singleton
43 public class AclInterfaceListener extends AsyncDataTreeChangeListenerBase<Interface, AclInterfaceListener>
44         implements ClusteredDataTreeChangeListener<Interface>, RecoverableListener {
45     private static final Logger LOG = LoggerFactory.getLogger(AclInterfaceListener.class);
46
47     private final AclServiceManager aclServiceManager;
48     private final AclClusterUtil aclClusterUtil;
49     private final DataBroker dataBroker;
50     private final AclDataUtil aclDataUtil;
51     private final AclInterfaceCache aclInterfaceCache;
52     private final AclServiceUtils aclServiceUtils;
53
54     @Inject
55     public AclInterfaceListener(AclServiceManager aclServiceManager, AclClusterUtil aclClusterUtil,
56             DataBroker dataBroker, AclDataUtil aclDataUtil, AclInterfaceCache aclInterfaceCache,
57             AclServiceUtils aclServicUtils, ServiceRecoveryRegistry serviceRecoveryRegistry) {
58         super(Interface.class, AclInterfaceListener.class);
59         this.aclServiceManager = aclServiceManager;
60         this.aclClusterUtil = aclClusterUtil;
61         this.dataBroker = dataBroker;
62         this.aclDataUtil = aclDataUtil;
63         this.aclInterfaceCache = aclInterfaceCache;
64         this.aclServiceUtils = aclServicUtils;
65         serviceRecoveryRegistry.addRecoverableListener(AclServiceUtils.getRecoverServiceRegistryKey(), this);
66     }
67
68     @Override
69     @PostConstruct
70     public void init() {
71         LOG.info("{} start", getClass().getSimpleName());
72         registerListener();
73     }
74
75     @Override
76     public void registerListener() {
77         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
78     }
79
80     @Override
81     protected InstanceIdentifier<Interface> getWildCardPath() {
82         return InstanceIdentifier.create(Interfaces.class).child(Interface.class);
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                 aclServiceUtils.deleteSubnetInfo(interfaceId);
98             }
99         }
100     }
101
102     @Override
103     public void update(InstanceIdentifier<Interface> key, Interface portBefore, Interface portAfter) {
104         if (portBefore.augmentation(ParentRefs.class) == null
105                 && portAfter.augmentation(ParentRefs.class) != null) {
106             LOG.trace("Ignoring event for update in ParentRefs for {} ", portAfter.getName());
107             return;
108         }
109         LOG.trace("Received AclInterface update event, portBefore={}, portAfter={}", portBefore, portAfter);
110         InterfaceAcl aclInPortAfter = portAfter.augmentation(InterfaceAcl.class);
111         InterfaceAcl aclInPortBefore = portBefore.augmentation(InterfaceAcl.class);
112
113         String interfaceId = portAfter.getName();
114         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
115             .Interface interfaceState = AclServiceUtils.getInterfaceStateFromOperDS(dataBroker, interfaceId);
116
117         AclInterface aclInterfaceBefore = aclInterfaceCache.get(interfaceId);
118         if (aclInterfaceBefore == null || isPortSecurityEnabledNow(aclInPortBefore, aclInPortAfter)) {
119             // Updating cache now as it might have not updated when
120             // port-security-enable=false
121             aclInterfaceBefore = addOrUpdateAclInterfaceCache(interfaceId, aclInPortBefore, true, interfaceState);
122         }
123         if (aclInPortAfter != null && aclInPortAfter.isPortSecurityEnabled()
124                 || aclInPortBefore != null && aclInPortBefore.isPortSecurityEnabled()) {
125             List<Uuid> sgsBefore = null;
126             if (aclInPortBefore != null) {
127                 sgsBefore = aclInPortBefore.getSecurityGroups();
128             }
129             boolean isSgChanged = isSecurityGroupsChanged(sgsBefore, aclInPortAfter.getSecurityGroups());
130             AclInterface aclInterfaceAfter =
131                     addOrUpdateAclInterfaceCache(interfaceId, aclInPortAfter, isSgChanged, interfaceState);
132
133             if (aclClusterUtil.isEntityOwner()) {
134                 // Handle bind/unbind service irrespective of interface state (up/down)
135                 boolean isPortSecurityEnable = aclInterfaceAfter.isPortSecurityEnabled();
136                 boolean isPortSecurityEnableBefore = aclInterfaceBefore.isPortSecurityEnabled();
137                 // if port security enable is changed and is disabled, unbind ACL service
138                 if (isPortSecurityEnableBefore != isPortSecurityEnable && !isPortSecurityEnable) {
139                     LOG.debug("Notify unbind ACL service for interface={}, isPortSecurityEnable={}", interfaceId,
140                             isPortSecurityEnable);
141                     aclServiceManager.notify(aclInterfaceAfter, null, Action.UNBIND);
142                 }
143                 if (interfaceState != null && interfaceState.getOperStatus().equals(
144                         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
145                             .state.Interface.OperStatus.Up)) {
146                     // if port security enable is changed and is enabled, bind ACL service
147                     if (isPortSecurityEnableBefore != isPortSecurityEnable && isPortSecurityEnable) {
148                         LOG.debug("Notify bind ACL service for interface={}, isPortSecurityEnable={}", interfaceId,
149                                 isPortSecurityEnable);
150                         aclServiceManager.notify(aclInterfaceAfter, null, Action.BIND);
151                     }
152                     LOG.debug("On update event, notify ACL service manager to update ACL for interface: {}",
153                             interfaceId);
154                     // handle add for AclPortsLookup before processing update
155                     try {
156                         Futures.allAsList(aclServiceUtils.addAclPortsLookupForInterfaceUpdate(aclInterfaceBefore,
157                                 aclInterfaceAfter)).get();
158                     } catch (InterruptedException | ExecutionException e) {
159                         LOG.error("Error adding ACL ports for interface update", e);
160                     }
161
162                     aclServiceManager.notify(aclInterfaceAfter, aclInterfaceBefore, AclServiceManager.Action.UPDATE);
163                     // handle delete for AclPortsLookup after processing update
164                     try {
165                         Futures.allAsList(aclServiceUtils.deleteAclPortsLookupForInterfaceUpdate(aclInterfaceBefore,
166                                 aclInterfaceAfter)).get();
167                     } catch (InterruptedException | ExecutionException e) {
168                         LOG.error("Error deleting ACL ports for interface update", e);
169                     }
170                 }
171             }
172             updateCacheWithAclChange(aclInterfaceBefore, aclInterfaceAfter);
173         }
174     }
175
176     private void updateCacheWithAclChange(AclInterface aclInterfaceBefore, AclInterface aclInterfaceAfter) {
177         List<Uuid> addedAcls = AclServiceUtils.getUpdatedAclList(aclInterfaceAfter.getSecurityGroups(),
178                 aclInterfaceBefore.getSecurityGroups());
179         List<Uuid> deletedAcls = AclServiceUtils.getUpdatedAclList(aclInterfaceBefore.getSecurityGroups(),
180                 aclInterfaceAfter.getSecurityGroups());
181         if (deletedAcls != null && !deletedAcls.isEmpty()) {
182             aclDataUtil.removeAclInterfaceMap(deletedAcls, aclInterfaceAfter);
183         }
184         if (addedAcls != null && !addedAcls.isEmpty()) {
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(List<Uuid> sgsBefore, List<Uuid> sgsAfter) {
195         if (sgsBefore == null && sgsAfter == null) {
196             return false;
197         }
198         if ((sgsBefore == null && sgsAfter != null) || (sgsBefore != null && sgsAfter == null)) {
199             return true;
200         }
201         if (sgsBefore != null && sgsAfter != null) {
202             return !(new HashSet<>(sgsBefore)).equals(new HashSet<>(sgsAfter));
203         }
204         return true;
205     }
206
207     private AclInterface addOrUpdateAclInterfaceCache(String interfaceId, InterfaceAcl aclInPort) {
208         return addOrUpdateAclInterfaceCache(interfaceId, aclInPort, true, null);
209     }
210
211     private AclInterface addOrUpdateAclInterfaceCache(String interfaceId, InterfaceAcl aclInPort, boolean isSgChanged,
212             org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
213                     .Interface interfaceState) {
214         AclInterface aclInterface = aclInterfaceCache.addOrUpdate(interfaceId, (prevAclInterface, builder) -> {
215             List<Uuid> sgs = new ArrayList<>();
216             if (aclInPort != null) {
217                 sgs = aclInPort.getSecurityGroups();
218                 builder.portSecurityEnabled(aclInPort.isPortSecurityEnabled()).securityGroups(sgs)
219                         .allowedAddressPairs(aclInPort.getAllowedAddressPairs());
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             builder.subnetInfo(aclServiceUtils.getSubnetInfo(interfaceId));
228             if (prevAclInterface == null || prevAclInterface.getElanId() == null) {
229                 builder.elanId(AclServiceUtils.getElanIdFromInterface(interfaceId, dataBroker));
230             }
231             if (prevAclInterface == null || isSgChanged) {
232                 builder.ingressRemoteAclTags(aclServiceUtils.getRemoteAclTags(sgs, DirectionIngress.class))
233                         .egressRemoteAclTags(aclServiceUtils.getRemoteAclTags(sgs, DirectionEgress.class));
234             }
235         });
236         // Clone and return the ACL interface object
237         return AclInterface.builder(aclInterface).build();
238     }
239
240     @Override
241     public void add(InstanceIdentifier<Interface> key, Interface port) {
242         LOG.trace("Received AclInterface add event, port={}", port);
243         InterfaceAcl aclInPort = port.augmentation(InterfaceAcl.class);
244         if (aclInPort != null && aclInPort.isPortSecurityEnabled()) {
245             String interfaceId = port.getName();
246             AclInterface aclInterface = addOrUpdateAclInterfaceCache(interfaceId, aclInPort);
247
248             // if interface state event comes first followed by interface config event.
249             if (aclInterface.getDpId() != null && aclInterface.getElanId() != null && aclClusterUtil.isEntityOwner()) {
250                 LOG.debug("On add event, notify ACL bind/add for interface: {}", interfaceId);
251                 aclServiceManager.notify(aclInterface, null, Action.BIND);
252                 aclServiceManager.notify(aclInterface, null, Action.ADD);
253             }
254         }
255     }
256
257     @Override
258     protected AclInterfaceListener getDataTreeChangeListener() {
259         return this;
260     }
261 }