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