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