Creating default FIB router flows for external subnets.
[netvirt.git] / vpnservice / neutronvpn / neutronvpn-impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronSubnetChangeListener.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 package org.opendaylight.netvirt.neutronvpn;
9
10 import com.google.common.base.Optional;
11 import com.google.common.util.concurrent.ListenableFuture;
12
13 import java.util.ArrayList;
14 import java.util.List;
15
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
20 import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
21 import org.opendaylight.genius.mdsalutil.MDSALUtil;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.networkmaps.NetworkMap;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.networkmaps.NetworkMapBuilder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.networkmaps.NetworkMapKey;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.Subnets;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
30 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 public class NeutronSubnetChangeListener extends AsyncDataTreeChangeListenerBase<Subnet, NeutronSubnetChangeListener>
35         implements AutoCloseable {
36     private static final Logger LOG = LoggerFactory.getLogger(NeutronSubnetChangeListener.class);
37     private final DataBroker dataBroker;
38     private final NeutronvpnManager nvpnManager;
39     private final NeutronvpnNatManager nvpnNatManager;
40     private final NeutronExternalSubnetHandler externalSubnetHandler;
41
42     public NeutronSubnetChangeListener(final DataBroker dataBroker, final NeutronvpnManager neutronvpnManager,
43             final NeutronvpnNatManager neutronvpnNatMgr, final NeutronExternalSubnetHandler externalSubnetHandler) {
44         super(Subnet.class, NeutronSubnetChangeListener.class);
45         this.dataBroker = dataBroker;
46         this.nvpnManager = neutronvpnManager;
47         this.nvpnNatManager = neutronvpnNatMgr;
48         this.externalSubnetHandler = externalSubnetHandler;
49     }
50
51     public void start() {
52         LOG.info("{} start", getClass().getSimpleName());
53         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
54     }
55
56     @Override
57     protected InstanceIdentifier<Subnet> getWildCardPath() {
58         return InstanceIdentifier.create(Neutron.class).child(Subnets.class).child(Subnet.class);
59     }
60
61     @Override
62     protected NeutronSubnetChangeListener getDataTreeChangeListener() {
63         return NeutronSubnetChangeListener.this;
64     }
65
66
67     @Override
68     protected void add(InstanceIdentifier<Subnet> identifier, Subnet input) {
69         LOG.trace("Adding Subnet : key: {}, value={}", identifier, input);
70         Uuid networkId = input.getNetworkId();
71         Uuid subnetId = input.getUuid();
72         Network network = NeutronvpnUtils.getNeutronNetwork(dataBroker, networkId);
73         if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
74             //FIXME: This should be removed when support for VLAN and GRE network types is added
75             LOG.error("neutron vpn doesn't support vlan/gre network provider type for the port {} "
76                 + "which is part of network {}."
77                 + " Skipping the processing of Subnet add DCN", input.getName(), network);
78             return;
79         }
80
81         NeutronvpnUtils.addToSubnetCache(input);
82         final DataStoreJobCoordinator subnetDataStoreCoordinator = DataStoreJobCoordinator.getInstance();
83         subnetDataStoreCoordinator.enqueueJob("SUBNET- " + subnetId.getValue(), () -> {
84             WriteTransaction writeConfigTxn = dataBroker.newWriteOnlyTransaction();
85             handleNeutronSubnetCreated(input.getUuid(), String.valueOf(input.getCidr().getValue()), networkId,
86                     input.getTenantId());
87             externalSubnetHandler.handleExternalSubnetAdded(network, subnetId, null, writeConfigTxn);
88
89             List<ListenableFuture<Void>> futures = new ArrayList<>();
90             futures.add(writeConfigTxn.submit());
91             return futures;
92         });
93     }
94
95     @Override
96     protected void remove(InstanceIdentifier<Subnet> identifier, Subnet input) {
97         LOG.trace("Removing subnet : key: {}, value={}", identifier, input);
98         Uuid networkId = input.getNetworkId();
99         Uuid subnetId = input.getUuid();
100         Network network = NeutronvpnUtils.getNeutronNetwork(dataBroker, networkId);
101         if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
102             //FIXME: This should be removed when support for GRE network types is added
103             LOG.error("neutron vpn doesn't support gre network provider type for the port {} "
104                 + "which is part of network {}."
105                 + " Skipping the processing of Subnet remove DCN", input.getName(), network);
106             return;
107         }
108
109         NeutronvpnUtils.removeFromSubnetCache(input);
110         final DataStoreJobCoordinator subnetDataStoreCoordinator = DataStoreJobCoordinator.getInstance();
111         subnetDataStoreCoordinator.enqueueJob("SUBNET- " + subnetId.getValue(), () -> {
112             WriteTransaction writeConfigTxn = dataBroker.newWriteOnlyTransaction();
113             handleNeutronSubnetDeleted(input.getUuid(), networkId, null);
114             externalSubnetHandler.handleExternalSubnetRemoved(network, subnetId, writeConfigTxn);
115
116             List<ListenableFuture<Void>> futures = new ArrayList<>();
117             futures.add(writeConfigTxn.submit());
118             return futures;
119         });
120     }
121
122     @Override
123     protected void update(InstanceIdentifier<Subnet> identifier, Subnet original, Subnet update) {
124         LOG.trace("Updating Subnet : key: {}, original value={}, update value={}", identifier, original, update);
125         Uuid networkId = update.getNetworkId();
126         Uuid subnetId = update.getUuid();
127         Network network = NeutronvpnUtils.getNeutronNetwork(dataBroker, networkId);
128         if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
129             LOG.error("neutron vpn doesn't support vlan/gre network provider type for the port {} "
130                 + "which is part of network {}."
131                 + " Skipping the processing of Subnet update DCN", update.getName(), network);
132             return;
133         }
134
135         final DataStoreJobCoordinator portDataStoreCoordinator = DataStoreJobCoordinator.getInstance();
136         portDataStoreCoordinator.enqueueJob("SUBNET- " + subnetId.getValue(), () -> {
137             WriteTransaction writeConfigTxn = dataBroker.newWriteOnlyTransaction();
138             handleNeutronSubnetUpdated(subnetId, network, update.getTenantId());
139             externalSubnetHandler.handleExternalSubnetUpdated(network, subnetId, null, writeConfigTxn);
140             NeutronvpnUtils.addToSubnetCache(update);
141
142             List<ListenableFuture<Void>> futures = new ArrayList<>();
143             futures.add(writeConfigTxn.submit());
144             return futures;
145         });
146     }
147
148     private void handleNeutronSubnetCreated(Uuid subnetId, String subnetIp, Uuid networkId, Uuid tenantId) {
149         nvpnManager.updateSubnetNode(subnetId, subnetIp, tenantId, networkId, null/*routerID*/, null/*vpnID*/);
150         if (networkId != null) {
151             createSubnetToNetworkMapping(subnetId, networkId);
152         }
153     }
154
155     private void handleNeutronSubnetDeleted(Uuid subnetId, Uuid networkId, Uuid tenantId) {
156         Uuid vpnId = NeutronvpnUtils.getVpnForNetwork(dataBroker, networkId);
157         if (vpnId != null) {
158             nvpnManager.removeSubnetFromVpn(vpnId, subnetId);
159         }
160         if (networkId != null) {
161             deleteSubnetToNetworkMapping(subnetId, networkId);
162         }
163         nvpnManager.deleteSubnetMapNode(subnetId);
164     }
165
166     private void handleNeutronSubnetUpdated(Uuid subnetId, Network network, Uuid tenantId) {
167         Uuid oldNetworkId = NeutronvpnUtils.getSubnetmap(dataBroker, subnetId).getNetworkId();
168         Uuid networkId = network.getUuid();
169         if (oldNetworkId != null && !oldNetworkId.equals(networkId)) {
170             deleteSubnetToNetworkMapping(subnetId, oldNetworkId);
171         }
172         if (networkId != null && !networkId.equals(oldNetworkId)) {
173             createSubnetToNetworkMapping(subnetId, networkId);
174         }
175
176         nvpnManager.updateSubnetNode(subnetId, null, tenantId, networkId, null/*routerID*/, null/*vpnID*/);
177     }
178
179     // TODO Clean up the exception handling
180     @SuppressWarnings("checkstyle:IllegalCatch")
181     private void createSubnetToNetworkMapping(Uuid subnetId, Uuid networkId) {
182         try {
183             InstanceIdentifier networkMapIdentifier = NeutronvpnUtils.buildNetworkMapIdentifier(networkId);
184             Optional<NetworkMap> optionalNetworkMap =
185                 NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, networkMapIdentifier);
186             NetworkMapBuilder nwMapBuilder = null;
187             if (optionalNetworkMap.isPresent()) {
188                 nwMapBuilder = new NetworkMapBuilder(optionalNetworkMap.get());
189             } else {
190                 nwMapBuilder = new NetworkMapBuilder().setKey(new NetworkMapKey(networkId)).setNetworkId(networkId);
191                 LOG.debug("Adding a new network node in NetworkMaps DS for network {}", networkId.getValue());
192             }
193             List<Uuid> subnetIdList = nwMapBuilder.getSubnetIdList();
194             if (subnetIdList == null) {
195                 subnetIdList = new ArrayList<>();
196             }
197             subnetIdList.add(subnetId);
198             nwMapBuilder.setSubnetIdList(subnetIdList);
199             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
200                 networkMapIdentifier, nwMapBuilder.build());
201             LOG.debug("Created subnet-network mapping for subnet {} network {}", subnetId.getValue(),
202                     networkId.getValue());
203         } catch (Exception e) {
204             LOG.error("Create subnet-network mapping failed for subnet {} network {}", subnetId.getValue(),
205                     networkId.getValue());
206         }
207     }
208
209     // TODO Clean up the exception handling
210     @SuppressWarnings("checkstyle:IllegalCatch")
211     private void deleteSubnetToNetworkMapping(Uuid subnetId, Uuid networkId) {
212         try {
213             InstanceIdentifier networkMapIdentifier = NeutronvpnUtils.buildNetworkMapIdentifier(networkId);
214             Optional<NetworkMap> optionalNetworkMap =
215                 NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, networkMapIdentifier);
216             if (optionalNetworkMap.isPresent()) {
217                 NetworkMapBuilder nwMapBuilder = new NetworkMapBuilder(optionalNetworkMap.get());
218                 List<Uuid> subnetIdList = nwMapBuilder.getSubnetIdList();
219                 if (subnetIdList.remove(subnetId)) {
220                     if (subnetIdList.size() == 0) {
221                         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, networkMapIdentifier);
222                         LOG.debug("Deleted network node in NetworkMaps DS for network {}", subnetId.getValue(),
223                                 networkId.getValue());
224                     } else {
225                         nwMapBuilder.setSubnetIdList(subnetIdList);
226                         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, networkMapIdentifier,
227                                 nwMapBuilder.build());
228                         LOG.debug("Deleted subnet-network mapping for subnet {} network {}", subnetId.getValue(),
229                                 networkId.getValue());
230                     }
231                 } else {
232                     LOG.error("Subnet {} is not mapped to network {}", subnetId.getValue(), networkId.getValue());
233                 }
234             } else {
235                 LOG.error("network {} not present for subnet {} ", networkId, subnetId);
236             }
237         } catch (Exception e) {
238             LOG.error("Delete subnet-network mapping failed for subnet {} network {}", subnetId.getValue(),
239                     networkId.getValue());
240         }
241     }
242 }
243