a0fb7bf8cd2d90cd82c76cbdd5b1898d610ab0f7
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / SubnetmapChangeListener.java
1 /*
2  * Copyright (c) 2016, 2017 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
9 package org.opendaylight.netvirt.vpnmanager;
10
11 import com.google.common.base.Optional;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.Objects;
15 import java.util.Set;
16 import java.util.concurrent.locks.ReentrantLock;
17 import javax.annotation.PostConstruct;
18 import javax.inject.Inject;
19 import javax.inject.Singleton;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
23 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
24 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
25 import org.opendaylight.genius.utils.JvmGlobalLocks;
26 import org.opendaylight.netvirt.vpnmanager.api.IVpnManager;
27 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTarget;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NetworkAttributes.NetworkType;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.Subnetmaps;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
36 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 @Singleton
41 public class SubnetmapChangeListener extends AsyncDataTreeChangeListenerBase<Subnetmap, SubnetmapChangeListener> {
42     private static final Logger LOG = LoggerFactory.getLogger(SubnetmapChangeListener.class);
43     private final DataBroker dataBroker;
44     private final VpnSubnetRouteHandler vpnSubnetRouteHandler;
45     private final VpnUtil vpnUtil;
46     private final IVpnManager vpnManager;
47
48     @Inject
49     public SubnetmapChangeListener(final DataBroker dataBroker, final VpnSubnetRouteHandler vpnSubnetRouteHandler,
50                                    VpnUtil vpnUtil, IVpnManager vpnManager) {
51         super(Subnetmap.class, SubnetmapChangeListener.class);
52         this.dataBroker = dataBroker;
53         this.vpnSubnetRouteHandler = vpnSubnetRouteHandler;
54         this.vpnUtil = vpnUtil;
55         this.vpnManager = vpnManager;
56     }
57
58     @PostConstruct
59     public void start() {
60         LOG.info("{} start", getClass().getSimpleName());
61         registerListener(dataBroker);
62     }
63
64     @Override
65     protected InstanceIdentifier<Subnetmap> getWildCardPath() {
66         return InstanceIdentifier.create(Subnetmaps.class).child(Subnetmap.class);
67     }
68
69     // TODO Clean up the exception handling
70     @SuppressWarnings("checkstyle:IllegalCatch")
71     private void registerListener(final DataBroker db) {
72         try {
73             registerListener(LogicalDatastoreType.CONFIGURATION, db);
74         } catch (final Exception e) {
75             LOG.error("VPNManager subnetMap config DataChange listener registration fail!", e);
76             throw new IllegalStateException("VPNManager subnetMap config DataChange listener registration failed.", e);
77         }
78     }
79
80     @Override
81     protected void add(InstanceIdentifier<Subnetmap> identifier, Subnetmap subnetmap) {
82         LOG.debug("SubnetmapChangeListener add subnetmap method - key: {}, value: {}", identifier, subnetmap);
83         Uuid subnetId = subnetmap.getId();
84         Network network = vpnUtil.getNeutronNetwork(subnetmap.getNetworkId());
85         if (network == null) {
86             LOG.error("SubnetMapChangeListener:add: network was not found for subnetId {}", subnetId.getValue());
87             return;
88         }
89         if (subnetmap.getVpnId() != null) {
90             if (NetworkType.VLAN.equals(subnetmap.getNetworkType())) {
91                 vpnUtil.addRouterPortToElanDpnListForVlaninAllDpn(subnetmap.getVpnId().getValue());
92             }
93         }
94         if (VpnUtil.getIsExternal(network)) {
95             LOG.debug("SubnetmapListener:add: provider subnetwork {} is handling in "
96                       + "ExternalSubnetVpnInstanceListener", subnetId.getValue());
97             return;
98         }
99         String elanInstanceName = subnetmap.getNetworkId().getValue();
100         long elanTag = getElanTag(elanInstanceName);
101         if (elanTag == 0L) {
102             LOG.error("SubnetMapChangeListener:add: unable to fetch elantag from ElanInstance {} for subnet {}",
103                       elanInstanceName, subnetId.getValue());
104             return;
105         }
106         Uuid vpnId = subnetmap.getVpnId();
107         if (vpnId != null) {
108             boolean isBgpVpn = !vpnId.equals(subnetmap.getRouterId());
109             LOG.info("SubnetMapChangeListener:add: subnetmap {} with elanTag {} to VPN {}", subnetmap, elanTag,
110                      vpnId);
111             vpnSubnetRouteHandler.onSubnetAddedToVpn(subnetmap, isBgpVpn, elanTag);
112             if (isBgpVpn && subnetmap.getRouterId() == null) {
113                 Set<VpnTarget> routeTargets = vpnManager.getRtListForVpn(vpnId.getValue());
114                 if (!routeTargets.isEmpty()) {
115                     // FIXME: separate this out somehow?
116                     final ReentrantLock lock = JvmGlobalLocks.getLockForString(subnetmap.getSubnetIp());
117                     lock.lock();
118                     try {
119                         vpnManager.updateRouteTargetsToSubnetAssociation(routeTargets, subnetmap.getSubnetIp(),
120                                 vpnId.getValue());
121                     } finally {
122                         lock.unlock();
123                     }
124                 }
125             }
126         }
127     }
128
129     @Override
130     protected void remove(InstanceIdentifier<Subnetmap> identifier, Subnetmap subnetmap) {
131         LOG.trace("SubnetmapListener:remove: subnetmap method - key: {}, value: {}", identifier, subnetmap);
132     }
133
134     @Override
135     // TODO Clean up the exception handling
136     @SuppressWarnings("checkstyle:IllegalCatch")
137     protected void update(InstanceIdentifier<Subnetmap> identifier, Subnetmap subnetmapOriginal, Subnetmap
138             subnetmapUpdate) {
139         LOG.debug("SubnetMapChangeListener update method - key {}, original {}, update {}", identifier,
140                   subnetmapOriginal, subnetmapUpdate);
141         Uuid subnetId = subnetmapUpdate.getId();
142         Network network = vpnUtil.getNeutronNetwork(subnetmapUpdate.getNetworkId());
143         if (network == null) {
144             LOG.error("SubnetMapChangeListener:update: network was not found for subnetId {}", subnetId.getValue());
145             return;
146         }
147         String elanInstanceName = subnetmapUpdate.getNetworkId().getValue();
148         long elanTag = getElanTag(elanInstanceName);
149         if (elanTag == 0L) {
150             LOG.error("SubnetMapChangeListener:update: unable to fetch elantag from ElanInstance {} for subnetId {}",
151                       elanInstanceName, subnetId);
152             return;
153         }
154         updateVlanDataEntry(subnetmapOriginal.getVpnId(), subnetmapUpdate.getVpnId(), subnetmapUpdate,
155                 subnetmapOriginal, elanInstanceName);
156         if (VpnUtil.getIsExternal(network)) {
157             LOG.debug("SubnetMapChangeListener:update: provider subnetwork {} is handling in "
158                       + "ExternalSubnetVpnInstanceListener", subnetId.getValue());
159             return;
160         }
161         // update on BGPVPN or InternalVPN change
162         Uuid vpnIdOld = subnetmapOriginal.getVpnId();
163         Uuid vpnIdNew = subnetmapUpdate.getVpnId();
164         if (!Objects.equals(vpnIdOld, vpnIdNew)) {
165             LOG.info("SubnetMapChangeListener:update: update subnetOpDataEntry for subnet {} imported in VPN",
166                      subnetmapUpdate.getId().getValue());
167             updateSubnetmapOpDataEntry(subnetmapOriginal.getVpnId(), subnetmapUpdate.getVpnId(), subnetmapUpdate,
168                                        subnetmapOriginal, elanTag);
169         }
170         // update on Internet VPN Id change
171         Uuid inetVpnIdOld = subnetmapOriginal.getInternetVpnId();
172         Uuid inetVpnIdNew = subnetmapUpdate.getInternetVpnId();
173         if (!Objects.equals(inetVpnIdOld, inetVpnIdNew)) {
174             LOG.info("SubnetMapChangeListener:update: update subnetOpDataEntry for subnet {} imported in InternetVPN",
175                      subnetmapUpdate.getId().getValue());
176             updateSubnetmapOpDataEntry(inetVpnIdOld, inetVpnIdNew, subnetmapUpdate, subnetmapOriginal, elanTag);
177         }
178         // update on PortList change
179         List<Uuid> oldPortList;
180         List<Uuid> newPortList;
181         newPortList = subnetmapUpdate.getPortList() != null ? subnetmapUpdate.getPortList() : new ArrayList<>();
182         oldPortList = subnetmapOriginal.getPortList() != null ? subnetmapOriginal.getPortList() : new ArrayList<>();
183         if (newPortList.size() == oldPortList.size()) {
184             return;
185         }
186         LOG.info("SubnetMapChangeListener:update: update port list for subnet {}", subnetmapUpdate.getId().getValue());
187         if (newPortList.size() > oldPortList.size()) {
188             for (Uuid portId : newPortList) {
189                 if (! oldPortList.contains(portId)) {
190                     vpnSubnetRouteHandler.onPortAddedToSubnet(subnetmapUpdate, portId);
191                     return;
192                 }
193             }
194         } else {
195             for (Uuid portId : oldPortList) {
196                 if (! newPortList.contains(portId)) {
197                     vpnSubnetRouteHandler.onPortRemovedFromSubnet(subnetmapUpdate, portId);
198                     return;
199                 }
200             }
201         }
202     }
203
204     private void updateSubnetmapOpDataEntry(Uuid vpnIdOld, Uuid vpnIdNew, Subnetmap subnetmapUpdate,
205                                     Subnetmap subnetmapOriginal, Long elanTag) {
206
207         // subnet added to VPN
208         if (vpnIdNew != null && vpnIdOld == null) {
209             if (vpnIdNew.equals(subnetmapUpdate.getRouterId())) {
210                 return;
211             }
212             vpnSubnetRouteHandler.onSubnetAddedToVpn(subnetmapUpdate, true, elanTag);
213         }
214         // subnet removed from VPN
215         if (vpnIdOld != null && vpnIdNew == null) {
216             if (vpnIdOld.equals(subnetmapOriginal.getRouterId())) {
217                 return;
218             }
219             vpnSubnetRouteHandler.onSubnetDeletedFromVpn(subnetmapOriginal, true);
220         }
221         // subnet updated in VPN
222         if (vpnIdOld != null && vpnIdNew != null && !vpnIdNew.equals(vpnIdOld)) {
223             vpnSubnetRouteHandler.onSubnetUpdatedInVpn(subnetmapUpdate, elanTag);
224         }
225     }
226
227     private void updateVlanDataEntry(Uuid vpnIdOld, Uuid vpnIdNew, Subnetmap subnetmapUpdate,
228             Subnetmap subnetmapOriginal, String elanInstanceName) {
229         if (vpnIdNew != null && vpnIdOld == null) {
230             if (elanInstanceName != null && NetworkType.VLAN.equals(subnetmapUpdate.getNetworkType())) {
231                 vpnUtil.addRouterPortToElanDpnListForVlaninAllDpn(vpnIdNew.getValue());
232             }
233         }
234         if (vpnIdOld != null && vpnIdNew == null) {
235             if (NetworkType.VLAN.equals(subnetmapOriginal.getNetworkType())) {
236                 vpnUtil.removeRouterPortFromElanDpnListForVlanInAllDpn(elanInstanceName, subnetmapOriginal
237                         .getRouterInterfacePortId().getValue(), vpnIdOld.getValue());
238             }
239         }
240     }
241
242     @Override
243     protected SubnetmapChangeListener getDataTreeChangeListener() {
244         return this;
245     }
246
247     protected long getElanTag(String elanInstanceName) {
248         InstanceIdentifier<ElanInstance> elanIdentifierId = InstanceIdentifier.builder(ElanInstances.class)
249                 .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName)).build();
250         long elanTag = 0L;
251         try {
252             Optional<ElanInstance> elanInstance = SingleTransactionDataBroker.syncReadOptional(dataBroker,
253                     LogicalDatastoreType.CONFIGURATION, elanIdentifierId);
254             if (elanInstance.isPresent()) {
255                 if (elanInstance.get().getElanTag() != null) {
256                     elanTag = elanInstance.get().getElanTag().toJava();
257                 } else {
258                     LOG.error("Notification failed because of failure in fetching elanTag for ElanInstance {}",
259                             elanInstanceName);
260                 }
261             } else {
262                 LOG.error("Notification failed because of failure in reading ELANInstance {}", elanInstanceName);
263             }
264         } catch (ReadFailedException e) {
265             LOG.error("Notification failed because of failure in fetching elanTag for ElanInstance {}",
266                 elanInstanceName, e);
267         }
268         return elanTag;
269     }
270 }