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
8 package org.opendaylight.netvirt.neutronvpn;
10 import static org.opendaylight.mdsal.binding.util.Datastore.CONFIGURATION;
12 import com.google.common.base.Preconditions;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.List;
18 import java.util.Objects;
19 import javax.annotation.PreDestroy;
20 import javax.inject.Inject;
21 import javax.inject.Singleton;
22 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
23 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
24 import org.opendaylight.infrautils.utils.concurrent.Executors;
25 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
26 import org.opendaylight.mdsal.binding.api.DataBroker;
27 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
28 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
29 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
30 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.L2vlan;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlanBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefs;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefsBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.SplitHorizon;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.SplitHorizonBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.port.id.subport.data.PortIdToSubportBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.port.id.subport.data.PortIdToSubportKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.NetworkTypeVlan;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.trunks.rev170118.trunk.attributes.SubPorts;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.trunks.rev170118.trunk.attributes.SubPortsKey;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.trunks.rev170118.trunks.attributes.Trunks;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.trunks.rev170118.trunks.attributes.trunks.Trunk;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
55 public class NeutronTrunkChangeListener extends AbstractAsyncDataTreeChangeListener<Trunk> {
56 private static final Logger LOG = LoggerFactory.getLogger(NeutronTrunkChangeListener.class);
58 private final DataBroker dataBroker;
59 private final ManagedNewTransactionRunner txRunner;
60 private final IInterfaceManager ifMgr;
61 private final JobCoordinator jobCoordinator;
64 public NeutronTrunkChangeListener(final DataBroker dataBroker, final IInterfaceManager ifMgr,
65 final JobCoordinator jobCoordinator) {
66 super(dataBroker, LogicalDatastoreType.CONFIGURATION,
67 InstanceIdentifier.create(Neutron.class).child(Trunks.class).child(Trunk.class),
68 Executors.newSingleThreadExecutor("NeutronTrunkChangeListener", LOG));
69 this.dataBroker = dataBroker;
70 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
72 this.jobCoordinator = jobCoordinator;
76 LOG.info("{} init", getClass().getSimpleName());
83 Executors.shutdownAndAwaitTermination(getExecutorService());
87 public void add(InstanceIdentifier<Trunk> identifier, Trunk input) {
88 Preconditions.checkNotNull(input.getPortId());
89 LOG.trace("Adding Trunk : key: {}, value={}", identifier, input);
90 Map<SubPortsKey, SubPorts> keySubPortsMap = input.getSubPorts();
91 if (keySubPortsMap != null) {
92 keySubPortsMap.values().forEach(subPort -> createSubPortInterface(input, subPort));
97 public void remove(InstanceIdentifier<Trunk> identifier, Trunk input) {
98 Preconditions.checkNotNull(input.getPortId());
99 LOG.trace("Removing Trunk : key: {}, value={}", identifier, input);
100 Map<SubPortsKey, SubPorts> keySubPortsMap = input.getSubPorts();
101 if (keySubPortsMap != null) {
102 keySubPortsMap.values().forEach(this::deleteSubPortInterface);
107 public void update(InstanceIdentifier<Trunk> identifier, Trunk original, Trunk update) {
108 if (Objects.equals(original, update)) {
111 List<SubPorts> updatedSubPorts = new ArrayList<>(update.nonnullSubPorts().values());
112 if (updatedSubPorts == null) {
113 updatedSubPorts = Collections.emptyList();
115 List<SubPorts> originalSubPorts = new ArrayList<>(original.nonnullSubPorts().values());
116 if (originalSubPorts == null) {
117 originalSubPorts = Collections.emptyList();
119 List<SubPorts> added = new ArrayList<>(updatedSubPorts);
120 added.removeAll(originalSubPorts);
121 List<SubPorts> deleted = new ArrayList<>(originalSubPorts);
122 deleted.removeAll(updatedSubPorts);
124 LOG.trace("Updating Trunk : key: {}. subPortsAdded={}, subPortsDeleted={}", identifier, added, deleted);
125 deleted.forEach(this::deleteSubPortInterface);
126 added.forEach(subPort -> createSubPortInterface(update, subPort));
129 private void createSubPortInterface(Trunk trunk, SubPorts subPort) {
130 if (!NetworkTypeVlan.class.equals(subPort.getSegmentationType())) {
131 LOG.warn("SegmentationType other than VLAN not supported for Trunk:SubPorts");
134 String portName = subPort.getPortId().getValue();
135 String parentName = trunk.getPortId().getValue();
136 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(portName);
138 // Should we use parentName?
139 jobCoordinator.enqueueJob("PORT- " + portName, () -> {
141 * Build Port-to-Subport details first, irrespective of port being available or not.
143 PortIdToSubportBuilder portIdToSubportBuilder = new PortIdToSubportBuilder();
144 Uuid subPortUuid = subPort.getPortId();
145 portIdToSubportBuilder.withKey(new PortIdToSubportKey(subPortUuid)).setPortId(subPortUuid)
146 .setTrunkPortId(trunk.getPortId()).setVlanId(subPort.getSegmentationId());
147 List<ListenableFuture<?>> futures = new ArrayList<>();
148 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
149 CONFIGURATION, tx -> {
150 tx.merge(NeutronvpnUtils.buildPortIdSubportMappingIdentifier(subPortUuid),
151 portIdToSubportBuilder.build());
152 LOG.trace("Creating PortIdSubportMapping for port{}", portName);
155 Interface iface = ifMgr.getInterfaceInfoFromConfigDataStore(portName);
158 * Trunk creation requires NeutronPort to be present, by this time interface
159 * should've been created. In controller restart use case Interface would already be present.
160 * Clustering consideration:
161 * This being same shard as NeutronPort, interface creation will be triggered on the same
162 * node as this one. Use of DSJC helps ensure the order.
164 LOG.warn("Interface not present for Trunk SubPort: {}", subPort);
167 InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
168 IfL2vlan ifL2vlan = new IfL2vlanBuilder().setL2vlanMode(IfL2vlan.L2vlanMode.TrunkMember)
169 .setVlanId(new VlanId(subPort.getSegmentationId().intValue())).build();
170 ParentRefs parentRefs = new ParentRefsBuilder().setParentInterface(parentName).build();
171 SplitHorizon splitHorizon = new SplitHorizonBuilder().setOverrideSplitHorizonProtection(true).build();
172 interfaceBuilder.setName(portName).setType(L2vlan.class).addAugmentation(ifL2vlan)
173 .addAugmentation(parentRefs).addAugmentation(splitHorizon);
174 Interface newIface = interfaceBuilder.build();
176 * Interface is already created for parent NeutronPort. We're updating parent refs
177 * and VLAN Information
179 ListenableFuture<?> future = txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
180 txn -> txn.merge(interfaceIdentifier, newIface));
181 LoggingFutures.addErrorLogging(future, LOG,
182 "createSubPortInterface: Failed for portName {}, parentName {}", portName, parentName);
188 private void deleteSubPortInterface(SubPorts subPort) {
189 String portName = subPort.getPortId().getValue();
190 InstanceIdentifier<Interface> interfaceIdentifier =
191 NeutronvpnUtils.buildVlanInterfaceIdentifier(subPort.getPortId().getValue());
192 jobCoordinator.enqueueJob("PORT- " + portName, () -> {
193 Interface iface = ifMgr.getInterfaceInfoFromConfigDataStore(portName);
194 List<ListenableFuture<?>> futures = new ArrayList<>();
196 LOG.warn("Interface not present for SubPort {}", subPort);
200 * We'll reset interface back to way it was? Can IFM handle parentRef delete?
202 InterfaceBuilder interfaceBuilder = new InterfaceBuilder(iface);
203 // Reset augmentations
204 interfaceBuilder.removeAugmentation(IfL2vlan.class).removeAugmentation(ParentRefs.class)
205 .removeAugmentation(SplitHorizon.class);
206 IfL2vlan ifL2vlan = new IfL2vlanBuilder().setL2vlanMode(IfL2vlan.L2vlanMode.Trunk).build();
207 interfaceBuilder.addAugmentation(ifL2vlan);
208 Interface newIface = interfaceBuilder.build();
210 * There is no means to do an update to remove elements from a node.
211 * Our solution is to get existing iface, remove parentRef and VlanId,
212 * and do a put to replace existing entry. This works out better as put
213 * has better performance than merge.
214 * Only drawback is any in-flight changes might be lost, but that is a corner case
215 * and this being subport delete path, don't expect any significant changes to
216 * corresponding Neutron Port. Deletion of NeutronPort should follow soon enough.
218 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
219 CONFIGURATION, tx -> {
220 tx.put(interfaceIdentifier, newIface);
221 LOG.trace("Resetting trunk member interface {}", newIface);
223 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
224 CONFIGURATION, tx -> {
225 tx.delete(NeutronvpnUtils.buildPortIdSubportMappingIdentifier(subPort.getPortId()));
226 LOG.trace("Deleting PortIdSubportMapping for portName {}", portName);