Missed one listener migration to genius/tools
[netvirt.git] / coe / impl / src / main / java / org / opendaylight / netvirt / coe / listeners / PodListener.java
1 /*
2  * Copyright (c) 2017 - 2018 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.coe.listeners;
10
11 import com.google.common.util.concurrent.ListenableFuture;
12
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Collection;
16 import java.util.List;
17 import java.util.Objects;
18 import java.util.concurrent.Callable;
19 import javax.annotation.Nonnull;
20 import javax.annotation.PreDestroy;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
25 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
26 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
27 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
30 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
31 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
32 import org.opendaylight.netvirt.coe.utils.CoeUtils;
33 import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
34 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.coe.northbound.pod.rev170611.Coe;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.coe.northbound.pod.rev170611.coe.Pods;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.coe.northbound.pod.rev170611.pod_attributes.Interface;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
39 import org.opendaylight.yangtools.concepts.ListenerRegistration;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 @Singleton
45 public class PodListener implements DataTreeChangeListener<Pods> {
46
47     private static final Logger LOG = LoggerFactory.getLogger(PodListener.class);
48     private ListenerRegistration<PodListener> listenerRegistration;
49     private final JobCoordinator jobCoordinator;
50     private final ManagedNewTransactionRunner txRunner;
51
52     @Inject
53     public PodListener(final DataBroker dataBroker, JobCoordinator jobCoordinator) {
54         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
55         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
56         this.jobCoordinator = jobCoordinator;
57     }
58
59     protected InstanceIdentifier<Pods> getWildCardPath() {
60         return InstanceIdentifier.create(Coe.class).child(Pods.class);
61     }
62
63     public void registerListener(LogicalDatastoreType dsType, final DataBroker db) {
64         final DataTreeIdentifier<Pods> treeId = new DataTreeIdentifier<>(dsType, getWildCardPath());
65         listenerRegistration = db.registerDataTreeChangeListener(treeId, PodListener.this);
66     }
67
68     @PreDestroy
69     public void close() {
70         if (listenerRegistration != null) {
71             try {
72                 listenerRegistration.close();
73             } finally {
74                 listenerRegistration = null;
75             }
76         }
77     }
78
79     @Override
80     public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<Pods>> collection) {
81         collection.forEach(
82             podsDataTreeModification -> podsDataTreeModification.getRootNode().getModifiedChildren().stream()
83                     .filter(
84                         dataObjectModification -> dataObjectModification.getDataType().equals(Interface.class))
85                         .forEach(dataObjectModification -> onPodInterfacesChanged(
86                                 (DataObjectModification<Interface>) dataObjectModification,
87                                 podsDataTreeModification.getRootPath().getRootIdentifier(),
88                                 podsDataTreeModification.getRootNode()))
89         );
90     }
91
92     public void onPodInterfacesChanged(final DataObjectModification<Interface> dataObjectModification,
93                                        final InstanceIdentifier<Pods> rootIdentifier,
94                                        DataObjectModification<Pods> rootNode) {
95         Pods pods = rootNode.getDataAfter();
96         Pods podsBefore = rootNode.getDataBefore();
97         Interface podInterfaceBefore = dataObjectModification.getDataBefore();
98         Interface podInterfaceAfter = dataObjectModification.getDataAfter();
99         switch (dataObjectModification.getModificationType()) {
100             case DELETE:
101                 remove(podsBefore, podInterfaceBefore);
102                 break;
103             case SUBTREE_MODIFIED:
104                 update(rootIdentifier, pods, podsBefore, podInterfaceBefore, podInterfaceAfter);
105                 break;
106             case WRITE:
107                 if (podInterfaceBefore == null) {
108                     add(rootIdentifier, pods, podInterfaceAfter);
109                 } else {
110                     update(rootIdentifier, pods, podsBefore, podInterfaceBefore,
111                             podInterfaceAfter);
112                 }
113                 break;
114             default:
115                 LOG.error("Unhandled Modificiation Type{} for {}", dataObjectModification.getModificationType(),
116                         rootIdentifier);
117         }
118     }
119
120     private void add(InstanceIdentifier<Pods> instanceIdentifier, Pods pods, Interface podInterface) {
121         LOG.trace("Pod added {}",pods);
122         if (pods.getNetworkNS() == null || pods.getHostIpAddress() == null) {
123             LOG.warn("pod {} added with insufficient information to process", pods.getName());
124             return;
125         }
126         jobCoordinator.enqueueJob(pods.getName(), new PodConfigAddWorker(txRunner, instanceIdentifier,
127                 pods, podInterface));
128     }
129
130     private void update(InstanceIdentifier<Pods> instanceIdentifier, Pods podsAfter, Pods podsBefore,
131                         Interface podInterfaceBefore, Interface podInterfaceAfter) {
132         LOG.trace("Pod updated before :{}, after :{}",podsBefore, podsAfter);
133         if (!Objects.equals(podsAfter.getHostIpAddress(), podsBefore.getHostIpAddress())
134                 || !Objects.equals(podInterfaceBefore.getIpAddress(), podInterfaceAfter.getIpAddress())) {
135             //if (podsBefore.getNetworkNS() != null || podsBefore.getHostIpAddress() != null) {
136                 // Case where pod is moving from one namespace to another
137                 // issue a delete of all previous configuration, and add the new one.
138                 //jobCoordinator.enqueueJob(podsAfter.getName(), new PodConfigRemoveWorker(txRunner, podsBefore));
139             //}
140             jobCoordinator.enqueueJob(podsAfter.getName(), new PodConfigAddWorker(txRunner, instanceIdentifier,
141                     podsAfter, podInterfaceAfter));
142         }
143     }
144
145     private void remove(Pods pods, Interface podInterface) {
146         LOG.trace("Pod removed {}", pods);
147         if (pods.getNetworkNS() == null || pods.getHostIpAddress() == null) {
148             LOG.warn("pod {} deletion without a valid network id", podInterface.getUid().getValue());
149             return;
150         }
151
152         jobCoordinator.enqueueJob(pods.getName(), new PodConfigRemoveWorker(txRunner, pods));
153     }
154
155     private static class PodConfigAddWorker implements Callable<List<ListenableFuture<Void>>> {
156         InstanceIdentifier<Pods> podsInstanceIdentifier;
157         private final Pods pods;
158         private final Interface podInterface;
159         private final ManagedNewTransactionRunner txRunner;
160
161         PodConfigAddWorker(ManagedNewTransactionRunner txRunner, InstanceIdentifier<Pods> podsInstanceIdentifier,
162                            Pods pods, Interface podInterface) {
163             this.pods = pods;
164             this.podInterface = podInterface;
165             this.txRunner = txRunner;
166             this.podsInstanceIdentifier = podsInstanceIdentifier;
167         }
168
169         @Override
170         public List<ListenableFuture<Void>> call() {
171             LOG.trace("Adding Pod : {}", podInterface);
172             String interfaceName = CoeUtils.buildInterfaceName(pods.getNetworkNS(), pods.getName());
173             List<ListenableFuture<Void>> futures = new ArrayList<>();
174             futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(tx ->  {
175                 String nodeIp = String.valueOf(pods.getHostIpAddress().getValue());
176                 ElanInstance elanInstance = CoeUtils.createElanInstanceForTheFirstPodInTheNetwork(
177                         pods.getNetworkNS(), nodeIp, podInterface, tx);
178                 LOG.info("interface creation for pod {}", interfaceName);
179                 String portInterfaceName = CoeUtils.createOfPortInterface(interfaceName, tx);
180                 LOG.debug("Creating ELAN Interface for pod {}", interfaceName);
181                 CoeUtils.createElanInterface(portInterfaceName,
182                         elanInstance.getElanInstanceName(), tx);
183                 LOG.debug("Creating VPN instance for namespace {}", pods.getNetworkNS());
184                 List<String> rd = Arrays.asList("100:1");
185                 CoeUtils.createVpnInstance(pods.getNetworkNS(), rd, null, null,
186                         VpnInstance.Type.L3, 0, IpVersionChoice.IPV4, tx);
187             }));
188             if (podInterface.getIpAddress() != null) {
189                 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
190                     CoeUtils.createPodNameToPodUuidMap(interfaceName, podsInstanceIdentifier, tx);
191                 }));
192             }
193             return futures;
194         }
195     }
196
197     private static class PodConfigRemoveWorker implements Callable<List<ListenableFuture<Void>>> {
198         private final Pods pods;
199         private final ManagedNewTransactionRunner txRunner;
200
201
202         PodConfigRemoveWorker(ManagedNewTransactionRunner txRunner,
203                               Pods pods) {
204             this.pods = pods;
205             this.txRunner = txRunner;
206         }
207
208         @Override
209         public List<ListenableFuture<Void>> call() {
210             List<ListenableFuture<Void>> futures = new ArrayList<>();
211             String podInterfaceName = CoeUtils.buildInterfaceName(pods.getNetworkNS(), pods.getName());
212             futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
213                 LOG.trace("Deleting Pod : {}", podInterfaceName);
214                 LOG.debug("Deleting VPN Interface for pod {}", podInterfaceName);
215                 CoeUtils.deleteVpnInterface(podInterfaceName, tx);
216                 LOG.debug("Deleting ELAN Interface for pod {}", podInterfaceName);
217                 CoeUtils.deleteElanInterface(podInterfaceName, tx);
218                 LOG.info("interface deletion for pod {}", podInterfaceName);
219                 CoeUtils.deleteOfPortInterface(podInterfaceName, tx);
220                 // TODO delete elan-instance if this is the last pod in the host
221                 // TODO delete vpn-instance if this is the last pod in the network
222             }));
223             futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
224                 CoeUtils.deletePodNameToPodUuidMap(podInterfaceName, tx);
225             }));
226             return futures;
227         }
228     }
229 }