2 * Copyright (c) 2017 - 2018 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
9 package org.opendaylight.netvirt.coe.listeners;
11 import com.google.common.util.concurrent.ListenableFuture;
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;
45 public class PodListener implements DataTreeChangeListener<Pods> {
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;
53 public PodListener(final DataBroker dataBroker, JobCoordinator jobCoordinator) {
54 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
55 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
56 this.jobCoordinator = jobCoordinator;
59 protected InstanceIdentifier<Pods> getWildCardPath() {
60 return InstanceIdentifier.create(Coe.class).child(Pods.class);
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);
70 if (listenerRegistration != null) {
72 listenerRegistration.close();
74 listenerRegistration = null;
80 public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<Pods>> collection) {
82 podsDataTreeModification -> podsDataTreeModification.getRootNode().getModifiedChildren().stream()
84 dataObjectModification -> dataObjectModification.getDataType().equals(Interface.class))
85 .forEach(dataObjectModification -> onPodInterfacesChanged(
86 (DataObjectModification<Interface>) dataObjectModification,
87 podsDataTreeModification.getRootPath().getRootIdentifier(),
88 podsDataTreeModification.getRootNode()))
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()) {
101 remove(podsBefore, podInterfaceBefore);
103 case SUBTREE_MODIFIED:
104 update(rootIdentifier, pods, podsBefore, podInterfaceBefore, podInterfaceAfter);
107 if (podInterfaceBefore == null) {
108 add(rootIdentifier, pods, podInterfaceAfter);
110 update(rootIdentifier, pods, podsBefore, podInterfaceBefore,
115 LOG.error("Unhandled Modificiation Type{} for {}", dataObjectModification.getModificationType(),
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());
126 jobCoordinator.enqueueJob(pods.getName(), new PodConfigAddWorker(txRunner, instanceIdentifier,
127 pods, podInterface));
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));
140 jobCoordinator.enqueueJob(podsAfter.getName(), new PodConfigAddWorker(txRunner, instanceIdentifier,
141 podsAfter, podInterfaceAfter));
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());
152 jobCoordinator.enqueueJob(pods.getName(), new PodConfigRemoveWorker(txRunner, pods));
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;
161 PodConfigAddWorker(ManagedNewTransactionRunner txRunner, InstanceIdentifier<Pods> podsInstanceIdentifier,
162 Pods pods, Interface podInterface) {
164 this.podInterface = podInterface;
165 this.txRunner = txRunner;
166 this.podsInstanceIdentifier = podsInstanceIdentifier;
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);
188 if (podInterface.getIpAddress() != null) {
189 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
190 CoeUtils.createPodNameToPodUuidMap(interfaceName, podsInstanceIdentifier, tx);
197 private static class PodConfigRemoveWorker implements Callable<List<ListenableFuture<Void>>> {
198 private final Pods pods;
199 private final ManagedNewTransactionRunner txRunner;
202 PodConfigRemoveWorker(ManagedNewTransactionRunner txRunner,
205 this.txRunner = txRunner;
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
223 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
224 CoeUtils.deletePodNameToPodUuidMap(podInterfaceName, tx);