Use managed transactions in neutronvpn-impl
[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 java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.List;
14 import javax.annotation.PostConstruct;
15 import javax.inject.Inject;
16 import javax.inject.Singleton;
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
20 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
21 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
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 ManagedNewTransactionRunner txRunner;
49     private final IInterfaceManager ifMgr;
50     private final JobCoordinator jobCoordinator;
51
52     @Inject
53     public NeutronTrunkChangeListener(final DataBroker dataBroker, final IInterfaceManager ifMgr,
54             final JobCoordinator jobCoordinator) {
55         this.dataBroker = dataBroker;
56         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
57         this.ifMgr = ifMgr;
58         this.jobCoordinator = jobCoordinator;
59     }
60
61     @Override
62     @PostConstruct
63     public void init() {
64         LOG.info("{} init", getClass().getSimpleName());
65         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
66     }
67
68     @Override
69     protected InstanceIdentifier<Trunk> getWildCardPath() {
70         return InstanceIdentifier.create(Neutron.class).child(Trunks.class).child(Trunk.class);
71     }
72
73     @Override
74     protected NeutronTrunkChangeListener getDataTreeChangeListener() {
75         return NeutronTrunkChangeListener.this;
76     }
77
78     @Override
79     protected void add(InstanceIdentifier<Trunk> identifier, Trunk input) {
80         Preconditions.checkNotNull(input.getPortId());
81         LOG.trace("Adding Trunk : key: {}, value={}", identifier, input);
82         List<SubPorts> subPorts = input.getSubPorts();
83         if (subPorts != null) {
84             subPorts.forEach(subPort -> createSubPortInterface(input, subPort));
85         }
86     }
87
88     @Override
89     protected void remove(InstanceIdentifier<Trunk> identifier, Trunk input) {
90         Preconditions.checkNotNull(input.getPortId());
91         LOG.trace("Removing Trunk : key: {}, value={}", identifier, input);
92         List<SubPorts> subPorts = input.getSubPorts();
93         if (subPorts != null) {
94             subPorts.forEach(this::deleteSubPortInterface);
95         }
96     }
97
98     @Override
99     protected void update(InstanceIdentifier<Trunk> identifier, Trunk original, Trunk update) {
100         List<SubPorts> updatedSubPorts = update.getSubPorts();
101         if (updatedSubPorts == null) {
102             updatedSubPorts = Collections.emptyList();
103         }
104         List<SubPorts> originalSubPorts = original.getSubPorts();
105         if (originalSubPorts == null) {
106             originalSubPorts = Collections.emptyList();
107         }
108         List<SubPorts> added = new ArrayList<>(updatedSubPorts);
109         added.removeAll(originalSubPorts);
110         List<SubPorts> deleted = new ArrayList<>(originalSubPorts);
111         deleted.removeAll(updatedSubPorts);
112
113         LOG.trace("Updating Trunk : key: {}. subPortsAdded={}, subPortsDeleted={}", identifier, added, deleted);
114         deleted.forEach(this::deleteSubPortInterface);
115         added.forEach(subPort -> createSubPortInterface(update, subPort));
116     }
117
118     private void createSubPortInterface(Trunk trunk, SubPorts subPort) {
119         if (!NetworkTypeVlan.class.equals(subPort.getSegmentationType())) {
120             LOG.warn("SegmentationType other than VLAN not supported for Trunk:SubPorts");
121             return;
122         }
123         String portName = subPort.getPortId().getValue();
124         String parentName = trunk.getPortId().getValue();
125         InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(portName);
126
127         // Should we use parentName?
128         jobCoordinator.enqueueJob("PORT- " + portName, () -> {
129             Interface iface = ifMgr.getInterfaceInfoFromConfigDataStore(portName);
130             if (iface == null) {
131                 /*
132                  * Trunk creation requires NeutronPort to be present, by this time interface
133                  * should've been created. In controller restart use case Interface would already be present.
134                  * Clustering consideration:
135                  *      This being same shard as NeutronPort, interface creation will be triggered on the same
136                  *      node as this one. Use of DSJC helps ensure the order.
137                  */
138                 LOG.warn("Interface not present for Trunk SubPort: {}", subPort);
139                 return Collections.emptyList();
140             }
141             InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
142             IfL2vlan ifL2vlan = new IfL2vlanBuilder().setL2vlanMode(IfL2vlan.L2vlanMode.TrunkMember)
143                 .setVlanId(new VlanId(subPort.getSegmentationId().intValue())).build();
144             ParentRefs parentRefs = new ParentRefsBuilder().setParentInterface(parentName).build();
145             SplitHorizon splitHorizon = new SplitHorizonBuilder().setOverrideSplitHorizonProtection(true).build();
146             interfaceBuilder.setName(portName).setType(L2vlan.class).addAugmentation(IfL2vlan.class, ifL2vlan)
147                 .addAugmentation(ParentRefs.class, parentRefs).addAugmentation(SplitHorizon.class, splitHorizon);
148             Interface newIface = interfaceBuilder.build();
149             /*
150              * Interface is already created for parent NeutronPort. We're updating parent refs
151              * and VLAN Information
152              */
153             return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
154                 tx.merge(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier, newIface);
155                 LOG.trace("Creating trunk member interface {}", newIface);
156             }));
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             if (iface == null) {
167                 LOG.warn("Interface not present for SubPort {}", subPort);
168                 return Collections.emptyList();
169             }
170             /*
171              * We'll reset interface back to way it was? Can IFM handle parentRef delete?
172              */
173             InterfaceBuilder interfaceBuilder = new InterfaceBuilder(iface);
174             // Reset augmentations
175             interfaceBuilder.removeAugmentation(IfL2vlan.class).removeAugmentation(ParentRefs.class)
176                 .removeAugmentation(SplitHorizon.class);
177             IfL2vlan ifL2vlan = new IfL2vlanBuilder().setL2vlanMode(IfL2vlan.L2vlanMode.Trunk).build();
178             interfaceBuilder.addAugmentation(IfL2vlan.class, ifL2vlan);
179             Interface newIface = interfaceBuilder.build();
180             /*
181              * There is no means to do an update to remove elements from a node.
182              * Our solution is to get existing iface, remove parentRef and VlanId,
183              * and do a put to replace existing entry. This works out better as put
184              * has better performance than merge.
185              * Only drawback is any in-flight changes might be lost, but that is a corner case
186              * and this being subport delete path, don't expect any significant changes to
187              * corresponding Neutron Port. Deletion of NeutronPort should follow soon enough.
188              */
189             return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
190                 tx.put(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier, newIface);
191                 LOG.trace("Resetting trunk member interface {}", newIface);
192             }));
193         });
194
195     }
196 }