2 * Copyright (c) 2017 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 com.google.common.base.Preconditions;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import java.util.ArrayList;
13 import java.util.Collections;
14 import java.util.List;
15 import javax.annotation.PostConstruct;
16 import javax.inject.Inject;
17 import javax.inject.Singleton;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
22 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
23 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlanBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefs;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefsBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.SplitHorizon;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.SplitHorizonBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.NetworkTypeVlan;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.trunks.rev170118.trunk.attributes.SubPorts;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.trunks.rev170118.trunks.attributes.Trunks;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.trunks.rev170118.trunks.attributes.trunks.Trunk;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
44 public class NeutronTrunkChangeListener extends AsyncDataTreeChangeListenerBase<Trunk, NeutronTrunkChangeListener> {
45 private static final Logger LOG = LoggerFactory.getLogger(NeutronTrunkChangeListener.class);
47 private final DataBroker dataBroker;
48 private final IInterfaceManager ifMgr;
49 private final JobCoordinator jobCoordinator;
52 public NeutronTrunkChangeListener(final DataBroker dataBroker, final IInterfaceManager ifMgr,
53 final JobCoordinator jobCoordinator) {
54 this.dataBroker = dataBroker;
56 this.jobCoordinator = jobCoordinator;
62 LOG.info("{} init", getClass().getSimpleName());
63 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
67 protected InstanceIdentifier<Trunk> getWildCardPath() {
68 return InstanceIdentifier.create(Neutron.class).child(Trunks.class).child(Trunk.class);
72 protected NeutronTrunkChangeListener getDataTreeChangeListener() {
73 return NeutronTrunkChangeListener.this;
77 protected void add(InstanceIdentifier<Trunk> identifier, Trunk input) {
78 Preconditions.checkNotNull(input.getPortId());
79 LOG.trace("Adding Trunk : key: {}, value={}", identifier, input);
80 List<SubPorts> subPorts = input.getSubPorts();
81 if (subPorts != null) {
82 subPorts.forEach(subPort -> createSubPortInterface(input, subPort));
87 protected void remove(InstanceIdentifier<Trunk> identifier, Trunk input) {
88 Preconditions.checkNotNull(input.getPortId());
89 LOG.trace("Removing Trunk : key: {}, value={}", identifier, input);
90 List<SubPorts> subPorts = input.getSubPorts();
91 if (subPorts != null) {
92 subPorts.forEach(this::deleteSubPortInterface);
97 protected void update(InstanceIdentifier<Trunk> identifier, Trunk original, Trunk update) {
98 List<SubPorts> updatedSubPorts = update.getSubPorts();
99 if (updatedSubPorts == null) {
100 updatedSubPorts = Collections.emptyList();
102 List<SubPorts> originalSubPorts = original.getSubPorts();
103 if (originalSubPorts == null) {
104 originalSubPorts = Collections.emptyList();
106 List<SubPorts> added = new ArrayList<>(updatedSubPorts);
107 added.removeAll(originalSubPorts);
108 List<SubPorts> deleted = new ArrayList<>(originalSubPorts);
109 deleted.removeAll(updatedSubPorts);
111 LOG.trace("Updating Trunk : key: {}. subPortsAdded={}, subPortsDeleted={}", identifier, added, deleted);
112 deleted.forEach(this::deleteSubPortInterface);
113 added.forEach(subPort -> createSubPortInterface(update, subPort));
116 private void createSubPortInterface(Trunk trunk, SubPorts subPort) {
117 if (!NetworkTypeVlan.class.equals(subPort.getSegmentationType())) {
118 LOG.warn("SegmentationType other than VLAN not supported for Trunk:SubPorts");
121 String portName = subPort.getPortId().getValue();
122 String parentName = trunk.getPortId().getValue();
123 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(portName);
125 // Should we use parentName?
126 jobCoordinator.enqueueJob("PORT- " + portName, () -> {
127 Interface iface = ifMgr.getInterfaceInfoFromConfigDataStore(portName);
128 List<ListenableFuture<Void>> futures = new ArrayList<>();
131 * Trunk creation requires NeutronPort to be present, by this time interface
132 * should've been created. In controller restart use case Interface would already be present.
133 * Clustering consideration:
134 * This being same shard as NeutronPort, interface creation will be triggered on the same
135 * node as this one. Use of DSJC helps ensure the order.
137 LOG.warn("Interface not present for Trunk SubPort: {}", subPort);
140 InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
141 IfL2vlan ifL2vlan = new IfL2vlanBuilder().setL2vlanMode(IfL2vlan.L2vlanMode.TrunkMember)
142 .setVlanId(new VlanId(subPort.getSegmentationId().intValue())).build();
143 ParentRefs parentRefs = new ParentRefsBuilder().setParentInterface(parentName).build();
144 SplitHorizon splitHorizon = new SplitHorizonBuilder().setOverrideSplitHorizonProtection(true).build();
145 interfaceBuilder.setName(portName).setType(L2vlan.class).addAugmentation(IfL2vlan.class, ifL2vlan)
146 .addAugmentation(ParentRefs.class, parentRefs).addAugmentation(SplitHorizon.class, splitHorizon);
147 iface = interfaceBuilder.build();
149 * Interface is already created for parent NeutronPort. We're updating parent refs
150 * and VLAN Information
152 WriteTransaction txn = dataBroker.newWriteOnlyTransaction();
153 txn.merge(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier, iface);
154 LOG.trace("Creating trunk member interface {}", iface);
155 futures.add(txn.submit());
160 private void deleteSubPortInterface(SubPorts subPort) {
161 String portName = subPort.getPortId().getValue();
162 InstanceIdentifier<Interface> interfaceIdentifier =
163 NeutronvpnUtils.buildVlanInterfaceIdentifier(subPort.getPortId().getValue());
164 jobCoordinator.enqueueJob("PORT- " + portName, () -> {
165 Interface iface = ifMgr.getInterfaceInfoFromConfigDataStore(portName);
166 List<ListenableFuture<Void>> futures = new ArrayList<>();
168 LOG.warn("Interface not present for SubPort {}", subPort);
172 * We'll reset interface back to way it was? Can IFM handle parentRef delete?
174 InterfaceBuilder interfaceBuilder = new InterfaceBuilder(iface);
175 // Reset augmentations
176 interfaceBuilder.removeAugmentation(IfL2vlan.class).removeAugmentation(ParentRefs.class)
177 .removeAugmentation(SplitHorizon.class);
178 IfL2vlan ifL2vlan = new IfL2vlanBuilder().setL2vlanMode(IfL2vlan.L2vlanMode.Trunk).build();
179 interfaceBuilder.addAugmentation(IfL2vlan.class, ifL2vlan);
180 iface = interfaceBuilder.build();
182 * There is no means to do an update to remove elements from a node.
183 * Our solution is to get existing iface, remove parentRef and VlanId,
184 * and do a put to replace existing entry. This works out better as put
185 * has better performance than merge.
186 * Only drawback is any in-flight changes might be lost, but that is a corner case
187 * and this being subport delete path, don't expect any significant changes to
188 * corresponding Neutron Port. Deletion of NeutronPort should follow soon enough.
190 WriteTransaction txn = dataBroker.newWriteOnlyTransaction();
191 txn.put(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier, iface);
192 LOG.trace("Resetting trunk member interface {}", iface);
193 futures.add(txn.submit());