Remove redundant names in paths
[netvirt.git] / neutronvpn / impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronTrunkChangeListener.java
1 /*
2  * Copyright (c) 2017 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 package org.opendaylight.netvirt.neutronvpn;
9
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;
42
43 @Singleton
44 public class NeutronTrunkChangeListener extends AsyncDataTreeChangeListenerBase<Trunk, NeutronTrunkChangeListener> {
45     private static final Logger LOG = LoggerFactory.getLogger(NeutronTrunkChangeListener.class);
46
47     private final DataBroker dataBroker;
48     private final IInterfaceManager ifMgr;
49     private final JobCoordinator jobCoordinator;
50
51     @Inject
52     public NeutronTrunkChangeListener(final DataBroker dataBroker, final IInterfaceManager ifMgr,
53             final JobCoordinator jobCoordinator) {
54         this.dataBroker = dataBroker;
55         this.ifMgr = ifMgr;
56         this.jobCoordinator = jobCoordinator;
57     }
58
59     @Override
60     @PostConstruct
61     public void init() {
62         LOG.info("{} init", getClass().getSimpleName());
63         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
64     }
65
66     @Override
67     protected InstanceIdentifier<Trunk> getWildCardPath() {
68         return InstanceIdentifier.create(Neutron.class).child(Trunks.class).child(Trunk.class);
69     }
70
71     @Override
72     protected NeutronTrunkChangeListener getDataTreeChangeListener() {
73         return NeutronTrunkChangeListener.this;
74     }
75
76     @Override
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));
83         }
84     }
85
86     @Override
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);
93         }
94     }
95
96     @Override
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();
101         }
102         List<SubPorts> originalSubPorts = original.getSubPorts();
103         if (originalSubPorts == null) {
104             originalSubPorts = Collections.emptyList();
105         }
106         List<SubPorts> added = new ArrayList<>(updatedSubPorts);
107         added.removeAll(originalSubPorts);
108         List<SubPorts> deleted = new ArrayList<>(originalSubPorts);
109         deleted.removeAll(updatedSubPorts);
110
111         LOG.trace("Updating Trunk : key: {}. subPortsAdded={}, subPortsDeleted={}", identifier, added, deleted);
112         deleted.forEach(this::deleteSubPortInterface);
113         added.forEach(subPort -> createSubPortInterface(update, subPort));
114     }
115
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");
119             return;
120         }
121         String portName = subPort.getPortId().getValue();
122         String parentName = trunk.getPortId().getValue();
123         InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(portName);
124
125         // Should we use parentName?
126         jobCoordinator.enqueueJob("PORT- " + portName, () -> {
127             Interface iface = ifMgr.getInterfaceInfoFromConfigDataStore(portName);
128             List<ListenableFuture<Void>> futures = new ArrayList<>();
129             if (iface == null) {
130                 /*
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.
136                  */
137                 LOG.warn("Interface not present for Trunk SubPort: {}", subPort);
138                 return futures;
139             }
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();
148             /*
149              * Interface is already created for parent NeutronPort. We're updating parent refs
150              * and VLAN Information
151              */
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());
156             return futures;
157         });
158     }
159
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<>();
167             if (iface == null) {
168                 LOG.warn("Interface not present for SubPort {}", subPort);
169                 return futures;
170             }
171             /*
172              * We'll reset interface back to way it was? Can IFM handle parentRef delete?
173              */
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();
181             /*
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.
189              */
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());
194             return futures;
195         });
196
197     }
198 }