NETVIRT-1630 migrate to md-sal APIs
[netvirt.git] / neutronvpn / impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronNetworkChangeListener.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 java.util.Collections;
11 import java.util.List;
12 import java.util.Objects;
13 import java.util.Optional;
14 import java.util.concurrent.ExecutionException;
15 import java.util.stream.Collectors;
16 import javax.annotation.PreDestroy;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.opendaylight.genius.mdsalutil.MDSALUtil;
22 import org.opendaylight.infrautils.utils.concurrent.Executors;
23 import org.opendaylight.mdsal.binding.api.DataBroker;
24 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
25 import org.opendaylight.netvirt.elanmanager.api.IElanService;
26 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
27 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeBase;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeFlat;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeVlan;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeVxlan;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.elan.instance.ElanSegments;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.elan.instance.ElanSegmentsBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.NetworkTypeBase;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.NetworkTypeFlat;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.NetworkTypeVlan;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.NetworkTypeVxlan;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.Networks;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.provider.ext.rev150712.NetworkProviderExtension;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.provider.ext.rev150712.neutron.networks.network.Segments;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
48 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
49 import org.opendaylight.yangtools.yang.common.Uint32;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 @Singleton
54 public class NeutronNetworkChangeListener extends AbstractAsyncDataTreeChangeListener<Network> {
55     private static final Logger LOG = LoggerFactory.getLogger(NeutronNetworkChangeListener.class);
56     private final DataBroker dataBroker;
57     private final NeutronvpnManager nvpnManager;
58     private final NeutronvpnNatManager nvpnNatManager;
59     private final IElanService elanService;
60     private final NeutronvpnUtils neutronvpnUtils;
61
62     @Inject
63     public NeutronNetworkChangeListener(final DataBroker dataBroker, final NeutronvpnManager neutronvpnManager,
64                                         final NeutronvpnNatManager neutronvpnNatManager,
65                                         final IElanService elanService, final NeutronvpnUtils neutronvpnUtils) {
66         super(dataBroker, LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Neutron.class)
67                 .child(Networks.class).child(Network.class), Executors.newSingleThreadExecutor(
68                         "NeutronNetworkChangeListener", LOG));
69         this.dataBroker = dataBroker;
70         nvpnManager = neutronvpnManager;
71         nvpnNatManager = neutronvpnNatManager;
72         this.elanService = elanService;
73         this.neutronvpnUtils = neutronvpnUtils;
74     }
75
76     public void init() {
77         LOG.info("{} init", getClass().getSimpleName());
78     }
79
80     @Override
81     @PreDestroy
82     public void close() {
83         super.close();
84         Executors.shutdownAndAwaitTermination(getExecutorService());
85     }
86
87     @Override
88     public void add(InstanceIdentifier<Network> identifier, Network input) {
89         LOG.trace("Adding Network : key: {}, value={}", identifier, input);
90         String networkId = input.getUuid().getValue();
91         if (!NeutronvpnUtils.isNetworkTypeSupported(input)) {
92             LOG.error("Neutronvpn doesn't support the provider type for given network {}", networkId);
93             return;
94         }
95         Class<? extends NetworkTypeBase> networkType = input.augmentation(NetworkProviderExtension.class)
96                 .getNetworkType();
97         if (NeutronvpnUtils.isVlanOrVxlanNetwork(networkType)
98                 && NeutronUtils.getSegmentationIdFromNeutronNetwork(input, networkType) == null) {
99             LOG.error("Segmentation ID is null for configured provider network {} of type {}. Abandoning any further "
100                     + "processing for the network", input.getUuid().getValue(), networkType);
101             return;
102         }
103
104         neutronvpnUtils.addToNetworkCache(input);
105         // Create ELAN instance for this network
106         ElanInstance elanInstance = createElanInstance(input);
107
108         if (NeutronvpnUtils.getIsExternal(input)) {
109             // Create ELAN interface and IETF interfaces for the physical network
110             elanService.createExternalElanNetwork(elanInstance);
111             ProviderTypes providerNwType = NeutronvpnUtils.getProviderNetworkType(input);
112             if (providerNwType == null) {
113                 LOG.error("Unable to get Network Provider Type for network {}", networkId);
114                 return;
115             }
116             LOG.trace("External Network Provider Type for network {} is {}", networkId, providerNwType.getName());
117             nvpnNatManager.addExternalNetwork(input);
118             if (NeutronvpnUtils.isFlatOrVlanNetwork(input)) {
119                 nvpnManager.createL3InternalVpn(input.getUuid(), null, null, null, null, null, null, null);
120                 nvpnManager.createExternalVpnInterfaces(input.getUuid());
121             }
122         }
123     }
124
125     @Override
126     public void remove(InstanceIdentifier<Network> identifier, Network input) {
127         LOG.trace("Removing Network : key: {}, value={}", identifier, input);
128         if (NeutronvpnUtils.getIsExternal(input)) {
129             if (NeutronvpnUtils.isFlatOrVlanNetwork(input)) {
130                 nvpnManager.removeExternalVpnInterfaces(input.getUuid());
131                 nvpnManager.removeVpn(input.getUuid());
132             }
133             nvpnNatManager.removeExternalNetwork(input);
134         }
135         //Delete ELAN instance for this network
136         String elanInstanceName = input.getUuid().getValue();
137         ElanInstance elanInstance = elanService.getElanInstance(elanInstanceName);
138         if (elanInstance != null) {
139             elanService.deleteExternalElanNetwork(elanInstance);
140             deleteElanInstance(elanInstanceName);
141         }
142         neutronvpnUtils.removeFromNetworkCache(input);
143     }
144
145     @Override
146     public void update(InstanceIdentifier<Network> identifier, Network original, Network update) {
147         LOG.trace("Updating Network : key: {}, original value={}, update value={}", identifier, original, update);
148         neutronvpnUtils.addToNetworkCache(update);
149         String elanInstanceName = original.getUuid().getValue();
150         Class<? extends SegmentTypeBase> origSegmentType = NeutronvpnUtils.getSegmentTypeFromNeutronNetwork(original);
151         String origSegmentationId = NeutronvpnUtils.getSegmentationIdFromNeutronNetwork(original);
152         String origPhysicalNetwork = NeutronvpnUtils.getPhysicalNetworkName(original);
153         Class<? extends SegmentTypeBase> updateSegmentType = NeutronvpnUtils.getSegmentTypeFromNeutronNetwork(update);
154         String updateSegmentationId = NeutronvpnUtils.getSegmentationIdFromNeutronNetwork(update);
155         String updatePhysicalNetwork = NeutronvpnUtils.getPhysicalNetworkName(update);
156         Boolean origExternal = NeutronvpnUtils.getIsExternal(original);
157         Boolean updateExternal = NeutronvpnUtils.getIsExternal(update);
158         Boolean origIsFlatOrVlanNetwork = NeutronvpnUtils.isFlatOrVlanNetwork(original);
159         Boolean updateIsFlatOrVlanNetwork = NeutronvpnUtils.isFlatOrVlanNetwork(update);
160
161         if (!Objects.equals(origSegmentType, updateSegmentType)
162                 || !Objects.equals(origSegmentationId, updateSegmentationId)
163                 || !Objects.equals(origPhysicalNetwork, updatePhysicalNetwork)
164                 || !Objects.equals(origExternal, updateExternal)) {
165             if (origExternal && origIsFlatOrVlanNetwork && (!updateExternal || !updateIsFlatOrVlanNetwork)) {
166                 nvpnManager.removeExternalVpnInterfaces(original.getUuid());
167                 nvpnManager.removeVpn(original.getUuid());
168                 nvpnNatManager.removeExternalNetwork(original);
169             }
170
171             ElanInstance elanInstance = elanService.getElanInstance(elanInstanceName);
172             if (elanInstance != null) {
173                 elanService.deleteExternalElanNetwork(elanInstance);
174                 elanInstance = updateElanInstance(elanInstanceName, updateSegmentType, updateSegmentationId,
175                         updatePhysicalNetwork, update);
176                 if (updateExternal) {
177                     elanService.updateExternalElanNetwork(elanInstance);
178                 }
179             }
180
181             if (updateExternal && updateIsFlatOrVlanNetwork && !origExternal) {
182                 nvpnNatManager.addExternalNetwork(update);
183                 nvpnManager.createL3InternalVpn(update.getUuid(), null, null, null, null, null, null, null);
184                 nvpnManager.createExternalVpnInterfaces(update.getUuid());
185             }
186         }
187     }
188
189     @NonNull
190     private List<ElanSegments> buildSegments(Network input) {
191         NetworkProviderExtension providerExtension = input.augmentation(NetworkProviderExtension.class);
192         if (providerExtension == null || providerExtension.getSegments() == null) {
193             return Collections.emptyList();
194         }
195         return providerExtension.getSegments().stream()
196                 .map(segment -> new ElanSegmentsBuilder()
197                         .setSegmentationIndex(segment.getSegmentationIndex())
198                         .setSegmentationId(getSegmentationId(input, segment))
199                         .setSegmentType(elanSegmentTypeFromNetworkType(segment.getNetworkType()))
200                         .build())
201                 .collect(Collectors.toList());
202     }
203
204     private Long getSegmentationId(Network network, Segments segment) {
205         try {
206             if (segment.getSegmentationId() != null) {
207                 return Long.valueOf(segment.getSegmentationId());
208             }
209         } catch (NumberFormatException error) {
210             LOG.error("Failed to get the segment id for network {}", network);
211         }
212         return 0L;
213     }
214
215     @Nullable
216     private Class<? extends SegmentTypeBase> elanSegmentTypeFromNetworkType(
217             @Nullable Class<? extends NetworkTypeBase> networkType) {
218         if (networkType == null) {
219             return null;
220         }
221         if (networkType.isAssignableFrom(NetworkTypeVxlan.class)) {
222             return SegmentTypeVxlan.class;
223         } else if (networkType.isAssignableFrom(NetworkTypeVlan.class)) {
224             return SegmentTypeVlan.class;
225         } else if (networkType.isAssignableFrom(NetworkTypeFlat.class)) {
226             return SegmentTypeFlat.class;
227         }
228         return null;
229     }
230
231     private ElanInstance createElanInstance(Network input) {
232         String elanInstanceName = input.getUuid().getValue();
233         InstanceIdentifier<ElanInstance> id = createElanInstanceIdentifier(elanInstanceName);
234         Optional<ElanInstance> existingElanInstance = null;
235         try {
236             existingElanInstance = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
237                     id);
238             if (existingElanInstance.isPresent()) {
239                 return existingElanInstance.get();
240             }
241         } catch (ExecutionException | InterruptedException e) {
242             LOG.error("createElanInstance: failed to read elanInstance {} due to exception ", elanInstanceName, e);
243         }
244         Class<? extends SegmentTypeBase> segmentType = NeutronvpnUtils.getSegmentTypeFromNeutronNetwork(input);
245         String segmentationId = NeutronvpnUtils.getSegmentationIdFromNeutronNetwork(input);
246         String physicalNetworkName = NeutronvpnUtils.getPhysicalNetworkName(input);
247         Uint32 elanTag = elanService.retrieveNewElanTag(elanInstanceName);
248         ElanInstance elanInstance = createElanInstanceBuilder(elanInstanceName, segmentType, segmentationId,
249                 physicalNetworkName, input).setElanTag(elanTag).build();
250         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, elanInstance);
251         LOG.debug("ELANInstance {} created with elan tag {} and segmentation ID {}", elanInstanceName, elanTag,
252                 segmentationId);
253         return elanInstance;
254     }
255
256     private ElanInstanceBuilder createElanInstanceBuilder(String elanInstanceName, Class<? extends SegmentTypeBase>
257             segmentType, String segmentationId, String physicalNetworkName, Network network) {
258         Boolean isExternal = NeutronvpnUtils.getIsExternal(network);
259         List<ElanSegments> segments = buildSegments(network);
260         ElanInstanceBuilder elanInstanceBuilder = new ElanInstanceBuilder().setElanInstanceName(elanInstanceName);
261         if (segmentType != null) {
262             elanInstanceBuilder.setSegmentType(segmentType);
263             if (segmentationId != null) {
264                 elanInstanceBuilder.setSegmentationId(Long.valueOf(segmentationId));
265             }
266             if (physicalNetworkName != null) {
267                 elanInstanceBuilder.setPhysicalNetworkName(physicalNetworkName);
268             }
269         }
270
271         elanInstanceBuilder.setElanSegments(segments);
272         elanInstanceBuilder.setExternal(isExternal);
273         elanInstanceBuilder.withKey(new ElanInstanceKey(elanInstanceName));
274         return elanInstanceBuilder;
275     }
276
277     private void deleteElanInstance(String elanInstanceName) {
278         InstanceIdentifier<ElanInstance> id = createElanInstanceIdentifier(elanInstanceName);
279         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
280         LOG.debug("ELANInstance {} deleted", elanInstanceName);
281     }
282
283     private ElanInstance updateElanInstance(String elanInstanceName, Class<? extends SegmentTypeBase> segmentType,
284                                             String segmentationId, String physicalNetworkName, Network network) {
285
286         ElanInstance elanInstance = createElanInstanceBuilder(elanInstanceName, segmentType, segmentationId,
287                 physicalNetworkName, network).build();
288         InstanceIdentifier<ElanInstance> id = createElanInstanceIdentifier(elanInstanceName);
289         MDSALUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, id, elanInstance);
290         return elanInstance;
291     }
292
293     private InstanceIdentifier<ElanInstance> createElanInstanceIdentifier(String elanInstanceName) {
294         InstanceIdentifier<ElanInstance> id = InstanceIdentifier.builder(ElanInstances.class)
295                 .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName)).build();
296         return id;
297     }
298 }