Use managed transactions in qosservice-impl
[netvirt.git] / qosservice / impl / src / main / java / org / opendaylight / netvirt / qosservice / QosNeutronUtils.java
1 /*
2  * Copyright (c) 2017 Intel Corporation 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.qosservice;
9
10 import com.google.common.base.Optional;
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.concurrent.ConcurrentMap;
19 import java.util.concurrent.CopyOnWriteArraySet;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.Future;
22 import javax.annotation.Nonnull;
23 import javax.annotation.Nullable;
24 import javax.inject.Inject;
25 import javax.inject.Singleton;
26 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
27 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
30 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
31 import org.opendaylight.genius.mdsalutil.ActionInfo;
32 import org.opendaylight.genius.mdsalutil.FlowEntity;
33 import org.opendaylight.genius.mdsalutil.InstructionInfo;
34 import org.opendaylight.genius.mdsalutil.MDSALUtil;
35 import org.opendaylight.genius.mdsalutil.MatchInfo;
36 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
37 import org.opendaylight.genius.mdsalutil.NwConstants;
38 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
39 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldDscp;
40 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
41 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
42 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
43 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
44 import org.opendaylight.genius.utils.ServiceIndex;
45 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
46 import org.opendaylight.netvirt.neutronvpn.interfaces.INeutronVpnManager;
47 import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.BridgeInterfaceInfo;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.BridgeRefInfo;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge._interface.info.BridgeEntry;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge._interface.info.BridgeEntryKey;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntry;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntryKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceInput;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceInputBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceOutput;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetPortFromInterfaceInput;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetPortFromInterfaceInputBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetPortFromInterfaceOutput;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceTypeFlowBased;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflow;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflowBuilder;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NetworkMaps;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.Subnetmaps;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.networkmaps.NetworkMap;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.networkmaps.NetworkMapKey;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.qos.ext.rev160613.QosNetworkExtension;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.qos.ext.rev160613.QosPortExtension;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.qos.rev160613.qos.attributes.qos.policies.QosPolicy;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.qos.rev160613.qos.attributes.qos.policies.qos.policy.BandwidthLimitRules;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.qos.rev160613.qos.attributes.qos.policies.qos.policy.BandwidthLimitRulesBuilder;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.qos.rev160613.qos.attributes.qos.policies.qos.policy.DscpmarkingRules;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeRef;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentationBuilder;
95 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
96 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
97 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
98 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
99 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
100 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointBuilder;
101 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
102 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
103 import org.opendaylight.yangtools.yang.common.RpcResult;
104 import org.slf4j.Logger;
105 import org.slf4j.LoggerFactory;
106
107 @Singleton
108 public class QosNeutronUtils {
109     private static final Logger LOG = LoggerFactory.getLogger(QosNeutronUtils.class);
110
111     private final ConcurrentMap<Uuid, QosPolicy> qosPolicyMap = new ConcurrentHashMap<>();
112     private final ConcurrentMap<Uuid, ConcurrentMap<Uuid, Port>> qosPortsMap = new ConcurrentHashMap<>();
113     private final ConcurrentMap<Uuid, ConcurrentMap<Uuid, Network>> qosNetworksMap = new ConcurrentHashMap<>();
114     private final CopyOnWriteArraySet<Uuid> qosServiceConfiguredPorts = new CopyOnWriteArraySet<>();
115
116     private final QosEosHandler qosEosHandler;
117     private final INeutronVpnManager neutronVpnManager;
118     private final OdlInterfaceRpcService odlInterfaceRpcService;
119     private final DataBroker dataBroker;
120     private final ManagedNewTransactionRunner txRunner;
121     private final IMdsalApiManager mdsalUtils;
122     private final JobCoordinator jobCoordinator;
123
124     @Inject
125     public QosNeutronUtils(final QosEosHandler qosEosHandler, final INeutronVpnManager neutronVpnManager,
126             final OdlInterfaceRpcService odlInterfaceRpcService, final DataBroker dataBroker,
127             final IMdsalApiManager mdsalUtils, final JobCoordinator jobCoordinator) {
128         this.qosEosHandler = qosEosHandler;
129         this.neutronVpnManager = neutronVpnManager;
130         this.odlInterfaceRpcService = odlInterfaceRpcService;
131         this.dataBroker = dataBroker;
132         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
133         this.mdsalUtils = mdsalUtils;
134         this.jobCoordinator = jobCoordinator;
135     }
136
137     public void addToQosPolicyCache(QosPolicy qosPolicy) {
138         qosPolicyMap.put(qosPolicy.getUuid(),qosPolicy);
139     }
140
141     public void removeFromQosPolicyCache(QosPolicy qosPolicy) {
142         qosPolicyMap.remove(qosPolicy.getUuid());
143     }
144
145     public Map<Uuid, QosPolicy> getQosPolicyMap() {
146         return qosPolicyMap;
147     }
148
149     public Collection<Port> getQosPorts(Uuid qosUuid) {
150         final ConcurrentMap<Uuid, Port> portMap = qosPortsMap.get(qosUuid);
151         return portMap != null ? portMap.values() : Collections.emptyList();
152     }
153
154     public void addToQosPortsCache(Uuid qosUuid, Port port) {
155         qosPortsMap.computeIfAbsent(qosUuid, key -> new ConcurrentHashMap<>()).putIfAbsent(port.getUuid(), port);
156     }
157
158     public void removeFromQosPortsCache(Uuid qosUuid, Port port) {
159         if (qosPortsMap.containsKey(qosUuid) && qosPortsMap.get(qosUuid).containsKey(port.getUuid())) {
160             qosPortsMap.get(qosUuid).remove(port.getUuid(), port);
161         }
162     }
163
164     public void addToQosNetworksCache(Uuid qosUuid, Network network) {
165         qosNetworksMap.computeIfAbsent(qosUuid, key -> new ConcurrentHashMap<>()).putIfAbsent(network.getUuid(),
166                 network);
167     }
168
169     public void removeFromQosNetworksCache(Uuid qosUuid, Network network) {
170         if (qosNetworksMap.containsKey(qosUuid) && qosNetworksMap.get(qosUuid).containsKey(network.getUuid())) {
171             qosNetworksMap.get(qosUuid).remove(network.getUuid(), network);
172         }
173     }
174
175     @Nonnull
176     public Collection<Network> getQosNetworks(Uuid qosUuid) {
177         final ConcurrentMap<Uuid, Network> networkMap = qosNetworksMap.get(qosUuid);
178         return networkMap != null ? networkMap.values() : Collections.emptyList();
179     }
180
181     @Nonnull
182     public List<Uuid> getSubnetIdsFromNetworkId(Uuid networkId) {
183         InstanceIdentifier<NetworkMap> networkMapId = InstanceIdentifier.builder(NetworkMaps.class)
184                 .child(NetworkMap.class, new NetworkMapKey(networkId)).build();
185         Optional<NetworkMap> optionalNetworkMap = MDSALUtil.read(LogicalDatastoreType.CONFIGURATION,
186                 networkMapId, dataBroker);
187         return optionalNetworkMap.isPresent() ? optionalNetworkMap.get().getSubnetIdList() : Collections.emptyList();
188     }
189
190     @Nonnull
191     protected List<Uuid> getPortIdsFromSubnetId(Uuid subnetId) {
192         InstanceIdentifier<Subnetmap> subnetMapId = InstanceIdentifier
193                 .builder(Subnetmaps.class)
194                 .child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
195         Optional<Subnetmap> optionalSubnetmap = MDSALUtil.read(LogicalDatastoreType.CONFIGURATION,
196                 subnetMapId,dataBroker);
197         return optionalSubnetmap.isPresent() ? optionalSubnetmap.get().getPortList() : Collections.emptyList();
198     }
199
200     public void handleNeutronPortQosAdd(Port port, Uuid qosUuid) {
201         LOG.trace("Handling Port add and QoS associated: port: {} qos: {}", port.getUuid(), qosUuid);
202
203         QosPolicy qosPolicy = qosPolicyMap.get(qosUuid);
204
205         jobCoordinator.enqueueJob("QosPort-" + port.getUuid().getValue(),
206             () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
207                 // handle Bandwidth Limit Rules update
208                 if (qosPolicy != null && qosPolicy.getBandwidthLimitRules() != null
209                         && !qosPolicy.getBandwidthLimitRules().isEmpty()) {
210                     setPortBandwidthLimits(port, qosPolicy.getBandwidthLimitRules().get(0), tx);
211                 }
212                 // handle DSCP Mark Rules update
213                 if (qosPolicy != null && qosPolicy.getDscpmarkingRules() != null
214                         && !qosPolicy.getDscpmarkingRules().isEmpty()) {
215                     setPortDscpMarking(port, qosPolicy.getDscpmarkingRules().get(0));
216                 }
217             })));
218     }
219
220     public void handleNeutronPortQosUpdate(Port port, Uuid qosUuidNew, Uuid qosUuidOld) {
221         LOG.trace("Handling Port QoS update: port: {} qosservice: {}", port.getUuid(), qosUuidNew);
222
223         QosPolicy qosPolicyNew = qosPolicyMap.get(qosUuidNew);
224         QosPolicy qosPolicyOld = qosPolicyMap.get(qosUuidOld);
225
226         jobCoordinator.enqueueJob("QosPort-" + port.getUuid().getValue(),
227             () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
228                 // handle Bandwidth Limit Rules update
229                 if (qosPolicyNew != null && qosPolicyNew.getBandwidthLimitRules() != null
230                         && !qosPolicyNew.getBandwidthLimitRules().isEmpty()) {
231                     setPortBandwidthLimits(port, qosPolicyNew.getBandwidthLimitRules().get(0), tx);
232                 } else {
233                     if (qosPolicyOld != null && qosPolicyOld.getBandwidthLimitRules() != null
234                             && !qosPolicyOld.getBandwidthLimitRules().isEmpty()) {
235                         BandwidthLimitRulesBuilder bwLimitBuilder = new BandwidthLimitRulesBuilder();
236                         setPortBandwidthLimits(port, bwLimitBuilder
237                                 .setMaxBurstKbps(BigInteger.ZERO)
238                                 .setMaxKbps(BigInteger.ZERO).build(), tx);
239                     }
240                 }
241                 //handle DSCP Mark Rules update
242                 if (qosPolicyNew != null && qosPolicyNew.getDscpmarkingRules() != null
243                         && !qosPolicyNew.getDscpmarkingRules().isEmpty()) {
244                     setPortDscpMarking(port, qosPolicyNew.getDscpmarkingRules().get(0));
245                 } else {
246                     if (qosPolicyOld != null && qosPolicyOld.getDscpmarkingRules() != null
247                             && !qosPolicyOld.getDscpmarkingRules().isEmpty()) {
248                         unsetPortDscpMark(port);
249                     }
250                 }
251             })));
252     }
253
254     public void handleNeutronPortQosRemove(Port port, Uuid qosUuid) {
255         LOG.trace("Handling Port QoS removal: port: {} qosservice: {}", port.getUuid(), qosUuid);
256
257         // check for network qosservice to apply
258         Network network =  neutronVpnManager.getNeutronNetwork(port.getNetworkId());
259         if (network != null && network.getAugmentation(QosNetworkExtension.class) != null) {
260             Uuid networkQosUuid = network.getAugmentation(QosNetworkExtension.class).getQosPolicyId();
261             if (networkQosUuid != null) {
262                 handleNeutronPortQosUpdate(port, networkQosUuid, qosUuid);
263             }
264         } else {
265             QosPolicy qosPolicy = qosPolicyMap.get(qosUuid);
266
267             jobCoordinator.enqueueJob("QosPort-" + port.getUuid().getValue(),
268                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
269                     // handle Bandwidth Limit Rules removal
270                     if (qosPolicy != null && qosPolicy.getBandwidthLimitRules() != null
271                             && !qosPolicy.getBandwidthLimitRules().isEmpty()) {
272                         BandwidthLimitRulesBuilder bwLimitBuilder = new BandwidthLimitRulesBuilder();
273                         setPortBandwidthLimits(port, bwLimitBuilder
274                                 .setMaxBurstKbps(BigInteger.ZERO)
275                                 .setMaxKbps(BigInteger.ZERO).build(), tx);
276                     }
277                     // handle DSCP MArk Rules removal
278                     if (qosPolicy != null && qosPolicy.getDscpmarkingRules() != null
279                             && !qosPolicy.getDscpmarkingRules().isEmpty()) {
280                         unsetPortDscpMark(port);
281                     }
282                 })));
283         }
284     }
285
286     public void handleNeutronPortRemove(Port port, Uuid qosUuid) {
287         LOG.trace("Handling Port removal and Qos associated: port: {} qos: {}", port.getUuid(), qosUuid);
288         QosPolicy qosPolicy = qosPolicyMap.get(qosUuid);
289
290         jobCoordinator.enqueueJob("QosPort-" + port.getUuid().getValue(), () -> {
291             //check if any DSCP rule in the policy
292             if (qosPolicy != null && qosPolicy.getDscpmarkingRules() != null
293                     && !qosPolicy.getDscpmarkingRules().isEmpty()) {
294                 unsetPortDscpMark(port);
295             }
296             return Collections.emptyList();
297         });
298     }
299
300     public void handleNeutronPortRemove(Port port, Uuid qosUuid, Interface intrf) {
301         LOG.trace("Handling Port removal and Qos associated: port: {} qos: {}", port.getUuid(), qosUuid);
302         QosPolicy qosPolicy = qosPolicyMap.get(qosUuid);
303
304         jobCoordinator.enqueueJob("QosPort-" + port.getUuid().getValue(), () -> {
305             if (qosPolicy != null && qosPolicy.getDscpmarkingRules() != null
306                     && !qosPolicy.getDscpmarkingRules().isEmpty()) {
307                 unsetPortDscpMark(port, intrf);
308             }
309             return Collections.emptyList();
310         });
311     }
312
313
314     public void handleNeutronNetworkQosUpdate(Network network, Uuid qosUuid) {
315         LOG.trace("Handling Network QoS update: net: {} qosservice: {}", network.getUuid(), qosUuid);
316         QosPolicy qosPolicy = qosPolicyMap.get(qosUuid);
317         if (qosPolicy == null || (qosPolicy.getBandwidthLimitRules() == null
318                 || qosPolicy.getBandwidthLimitRules().isEmpty())
319                 && (qosPolicy.getDscpmarkingRules() == null
320                 || qosPolicy.getDscpmarkingRules().isEmpty())) {
321             return;
322         }
323         List<Uuid> subnetIds = getSubnetIdsFromNetworkId(network.getUuid());
324         for (Uuid subnetId : subnetIds) {
325             List<Uuid> portIds = getPortIdsFromSubnetId(subnetId);
326             for (Uuid portId : portIds) {
327                 Port port = neutronVpnManager.getNeutronPort(portId);
328                 if (port != null && (port.getAugmentation(QosPortExtension.class) == null
329                         || port.getAugmentation(QosPortExtension.class).getQosPolicyId() == null)) {
330                     jobCoordinator.enqueueJob("QosPort-" + portId.getValue(),
331                         () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
332                             if (qosPolicy.getBandwidthLimitRules() != null
333                                     && !qosPolicy.getBandwidthLimitRules().isEmpty()) {
334                                 setPortBandwidthLimits(port, qosPolicy.getBandwidthLimitRules().get(0), tx);
335                             }
336                             if (qosPolicy.getDscpmarkingRules() != null
337                                     && !qosPolicy.getDscpmarkingRules().isEmpty()) {
338                                 setPortDscpMarking(port, qosPolicy.getDscpmarkingRules().get(0));
339                             }
340                         })));
341                 }
342             }
343         }
344     }
345
346     public void handleNeutronNetworkQosRemove(Network network, Uuid qosUuid) {
347         LOG.trace("Handling Network QoS removal: net: {} qosservice: {}", network.getUuid(), qosUuid);
348         QosPolicy qosPolicy = qosPolicyMap.get(qosUuid);
349
350         List<Uuid> subnetIds = getSubnetIdsFromNetworkId(network.getUuid());
351         for (Uuid subnetId : subnetIds) {
352             List<Uuid> portIds = getPortIdsFromSubnetId(subnetId);
353             for (Uuid portId : portIds) {
354                 Port port = neutronVpnManager.getNeutronPort(portId);
355                 if (port != null && (port.getAugmentation(QosPortExtension.class) == null
356                         || port.getAugmentation(QosPortExtension.class).getQosPolicyId() == null)) {
357                     jobCoordinator.enqueueJob("QosPort-" + portId.getValue(),
358                         () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
359                             if (qosPolicy != null && qosPolicy.getBandwidthLimitRules() != null
360                                     && !qosPolicy.getBandwidthLimitRules().isEmpty()) {
361                                 BandwidthLimitRulesBuilder bwLimitBuilder = new BandwidthLimitRulesBuilder();
362                                 setPortBandwidthLimits(port, bwLimitBuilder
363                                         .setMaxBurstKbps(BigInteger.ZERO)
364                                         .setMaxKbps(BigInteger.ZERO).build(), tx);
365                             }
366                             if (qosPolicy != null && qosPolicy.getDscpmarkingRules() != null
367                                     && !qosPolicy.getDscpmarkingRules().isEmpty()) {
368                                 unsetPortDscpMark(port);
369                             }
370                         })));
371                 }
372             }
373         }
374     }
375
376     public void handleNeutronNetworkQosBwRuleRemove(Network network, BandwidthLimitRules zeroBwLimitRule) {
377         LOG.trace("Handling Qos Bandwidth Rule Remove, net: {}", network.getUuid());
378
379         List<Uuid> subnetIds = getSubnetIdsFromNetworkId(network.getUuid());
380
381         for (Uuid subnetId: subnetIds) {
382             List<Uuid> portIds = getPortIdsFromSubnetId(subnetId);
383             for (Uuid portId : portIds) {
384                 Port port = neutronVpnManager.getNeutronPort(portId);
385                 if (port != null && (port.getAugmentation(QosPortExtension.class) == null
386                         || port.getAugmentation(QosPortExtension.class).getQosPolicyId() == null)) {
387                     jobCoordinator.enqueueJob("QosPort-" + portId.getValue(), () -> Collections.singletonList(
388                             txRunner.callWithNewWriteOnlyTransactionAndSubmit(
389                                 tx -> setPortBandwidthLimits(port, zeroBwLimitRule, tx))));
390                 }
391             }
392         }
393     }
394
395     public void handleNeutronNetworkQosDscpRuleRemove(Network network) {
396         LOG.trace("Handling Qos Dscp Rule Remove, net: {}", network.getUuid());
397
398         List<Uuid> subnetIds = getSubnetIdsFromNetworkId(network.getUuid());
399
400         for (Uuid subnetId: subnetIds) {
401             List<Uuid> portIds = getPortIdsFromSubnetId(subnetId);
402             for (Uuid portId : portIds) {
403                 Port port = neutronVpnManager.getNeutronPort(portId);
404                 if (port != null && (port.getAugmentation(QosPortExtension.class) == null
405                         || port.getAugmentation(QosPortExtension.class).getQosPolicyId() == null)) {
406                     jobCoordinator.enqueueJob("QosPort-" + portId.getValue(), () -> {
407                         unsetPortDscpMark(port);
408                         return Collections.emptyList();
409                     });
410                 }
411             }
412         }
413     }
414
415     // TODO Clean up the exception handling
416     @SuppressWarnings("checkstyle:IllegalCatch")
417     public void setPortBandwidthLimits(Port port, BandwidthLimitRules bwLimit, WriteTransaction writeConfigTxn) {
418         if (!qosEosHandler.isQosClusterOwner()) {
419             LOG.trace("Not Qos Cluster Owner. Ignoring setting bandwidth limits");
420             return;
421         }
422         LOG.trace("Setting bandwidth limits {} on Port {}", port, bwLimit);
423
424         BigInteger dpId = getDpnForInterface(port.getUuid().getValue());
425         if (dpId.equals(BigInteger.ZERO)) {
426             LOG.info("DPN ID for interface {} not found", port.getUuid().getValue());
427             return;
428         }
429
430         OvsdbBridgeRef bridgeRefEntry = getBridgeRefEntryFromOperDS(dpId);
431         Optional<Node> bridgeNode = MDSALUtil.read(LogicalDatastoreType.OPERATIONAL,
432                 bridgeRefEntry.getValue().firstIdentifierOf(Node.class), dataBroker);
433
434
435         TerminationPoint tp = SouthboundUtils.getTerminationPointByExternalId(bridgeNode.get(),
436                 port.getUuid().getValue());
437         OvsdbTerminationPointAugmentation ovsdbTp = tp.getAugmentation(OvsdbTerminationPointAugmentation.class);
438
439         OvsdbTerminationPointAugmentationBuilder tpAugmentationBuilder = new OvsdbTerminationPointAugmentationBuilder();
440         tpAugmentationBuilder.setName(ovsdbTp.getName());
441         tpAugmentationBuilder.setIngressPolicingRate(bwLimit.getMaxKbps().longValue());
442         tpAugmentationBuilder.setIngressPolicingBurst(bwLimit.getMaxBurstKbps().longValue());
443
444         TerminationPointBuilder tpBuilder = new TerminationPointBuilder();
445         tpBuilder.setKey(tp.getKey());
446         tpBuilder.addAugmentation(OvsdbTerminationPointAugmentation.class, tpAugmentationBuilder.build());
447         try {
448             if (writeConfigTxn != null) {
449                 writeConfigTxn.put(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier
450                         .create(NetworkTopology.class)
451                         .child(Topology.class, new TopologyKey(SouthboundUtils.OVSDB_TOPOLOGY_ID))
452                         .child(Node.class, bridgeNode.get().getKey())
453                         .child(TerminationPoint.class, new TerminationPointKey(tp.getKey())), tpBuilder.build());
454             } else {
455                 MDSALUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, InstanceIdentifier
456                         .create(NetworkTopology.class)
457                         .child(Topology.class, new TopologyKey(SouthboundUtils.OVSDB_TOPOLOGY_ID))
458                         .child(Node.class, bridgeNode.get().getKey())
459                         .child(TerminationPoint.class, new TerminationPointKey(tp.getKey())), tpBuilder.build());
460             }
461         } catch (Exception e) {
462             LOG.error("Failure while setting BwLimitRule {} to port {}", bwLimit, port, e);
463         }
464
465     }
466
467     public void setPortDscpMarking(Port port, DscpmarkingRules dscpMark) {
468         if (!qosEosHandler.isQosClusterOwner()) {
469             LOG.trace("Not Qos Cluster Owner. Ignoring setting DSCP marking");
470             return;
471         }
472         LOG.trace("Setting DSCP value {} on Port {}", port, dscpMark);
473
474         BigInteger dpnId = getDpnForInterface(port.getUuid().getValue());
475         String ifName = port.getUuid().getValue();
476         IpAddress ipAddress = port.getFixedIps().get(0).getIpAddress();
477         Short dscpValue = dscpMark.getDscpMark();
478
479         if (dpnId.equals(BigInteger.ZERO)) {
480             LOG.info("DPN ID for interface {} not found", port.getUuid().getValue());
481             return;
482         }
483
484         //1. OF rules
485         addFlow(dpnId, dscpValue, ifName, ipAddress, getInterfaceStateFromOperDS(ifName));
486         if (qosServiceConfiguredPorts.add(port.getUuid())) {
487             // bind qos service to interface
488             bindservice(ifName);
489         }
490     }
491
492     public void unsetPortDscpMark(Port port) {
493         if (!qosEosHandler.isQosClusterOwner()) {
494             LOG.trace("Not Qos Cluster Owner. Ignoring unsetting DSCP marking");
495             return;
496         }
497         LOG.trace("Removing dscp marking rule from Port {}", port);
498
499         BigInteger dpnId = getDpnForInterface(port.getUuid().getValue());
500         String ifName = port.getUuid().getValue();
501
502         if (dpnId.equals(BigInteger.ZERO)) {
503             LOG.info("DPN ID for port {} not found", port);
504             return;
505         }
506
507         //unbind service from interface
508         unbindservice(ifName);
509         // 1. OF
510         removeFlow(dpnId, ifName, getInterfaceStateFromOperDS(ifName));
511         qosServiceConfiguredPorts.remove(port.getUuid());
512     }
513
514     public void unsetPortDscpMark(Port port, Interface intrf) {
515         LOG.trace("Removing dscp marking rule from Port {}", port);
516
517         BigInteger dpnId = getDpIdFromInterface(intrf);
518         String ifName = port.getUuid().getValue();
519
520         if (dpnId.equals(BigInteger.ZERO)) {
521             LOG.error("Unable to retrieve DPN Id for interface {}", ifName);
522             return;
523         }
524         unbindservice(ifName);
525         removeFlow(dpnId, ifName, intrf);
526     }
527
528     private static BigInteger getDpIdFromInterface(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf
529                                                            .interfaces.rev140508.interfaces.state.Interface ifState) {
530         String lowerLayerIf = ifState.getLowerLayerIf().get(0);
531         NodeConnectorId nodeConnectorId = new NodeConnectorId(lowerLayerIf);
532         return BigInteger.valueOf(MDSALUtil.getDpnIdFromPortName(nodeConnectorId));
533     }
534
535     public BigInteger getDpnForInterface(String ifName) {
536         BigInteger nodeId = BigInteger.ZERO;
537         try {
538             GetDpidFromInterfaceInput
539                     dpIdInput = new GetDpidFromInterfaceInputBuilder().setIntfName(ifName).build();
540             Future<RpcResult<GetDpidFromInterfaceOutput>>
541                     dpIdOutput = odlInterfaceRpcService.getDpidFromInterface(dpIdInput);
542             RpcResult<GetDpidFromInterfaceOutput> dpIdResult = dpIdOutput.get();
543             if (dpIdResult.isSuccessful()) {
544                 nodeId = dpIdResult.getResult().getDpid();
545             } else {
546                 LOG.error("Could not retrieve DPN Id for interface {}", ifName);
547             }
548         } catch (NullPointerException | InterruptedException | ExecutionException e) {
549             LOG.error("Exception when getting dpn for interface {}", ifName,  e);
550         }
551         return nodeId;
552     }
553
554     @Nullable
555     private BridgeEntry getBridgeEntryFromConfigDS(BigInteger dpnId) {
556         BridgeEntryKey bridgeEntryKey = new BridgeEntryKey(dpnId);
557         InstanceIdentifier<BridgeEntry> bridgeEntryInstanceIdentifier = getBridgeEntryIdentifier(bridgeEntryKey);
558         LOG.debug("Trying to retrieve bridge entry from config for Id: {}", bridgeEntryInstanceIdentifier);
559         return getBridgeEntryFromConfigDS(bridgeEntryInstanceIdentifier);
560     }
561
562     @Nullable
563     private BridgeEntry getBridgeEntryFromConfigDS(InstanceIdentifier<BridgeEntry> bridgeEntryInstanceIdentifier) {
564         return MDSALUtil.read(LogicalDatastoreType.CONFIGURATION, bridgeEntryInstanceIdentifier, dataBroker).orNull();
565     }
566
567     @Nullable
568     private BridgeRefEntry getBridgeRefEntryFromOperDS(InstanceIdentifier<BridgeRefEntry> dpnBridgeEntryIid) {
569         return MDSALUtil.read(LogicalDatastoreType.OPERATIONAL, dpnBridgeEntryIid, dataBroker).orNull();
570     }
571
572     @Nullable
573     private OvsdbBridgeRef getBridgeRefEntryFromOperDS(BigInteger dpId) {
574         BridgeRefEntryKey bridgeRefEntryKey = new BridgeRefEntryKey(dpId);
575         InstanceIdentifier<BridgeRefEntry> bridgeRefEntryIid = getBridgeRefEntryIdentifier(bridgeRefEntryKey);
576         BridgeRefEntry bridgeRefEntry = getBridgeRefEntryFromOperDS(bridgeRefEntryIid);
577         if (bridgeRefEntry == null) {
578             // bridge ref entry will be null if the bridge is disconnected from controller.
579             // In that case, fetch bridge reference from bridge interface entry config DS
580             BridgeEntry bridgeEntry = getBridgeEntryFromConfigDS(dpId);
581             if (bridgeEntry == null) {
582                 return null;
583             }
584             return  bridgeEntry.getBridgeReference();
585         }
586         return bridgeRefEntry.getBridgeReference();
587     }
588
589     @Nonnull
590     private static InstanceIdentifier<BridgeRefEntry> getBridgeRefEntryIdentifier(BridgeRefEntryKey bridgeRefEntryKey) {
591         return InstanceIdentifier.builder(BridgeRefInfo.class).child(BridgeRefEntry.class, bridgeRefEntryKey).build();
592     }
593
594     @Nonnull
595     private static InstanceIdentifier<BridgeEntry> getBridgeEntryIdentifier(BridgeEntryKey bridgeEntryKey) {
596         return InstanceIdentifier.builder(BridgeInterfaceInfo.class).child(BridgeEntry.class, bridgeEntryKey).build();
597     }
598
599     public void removeStaleFlowEntry(Interface intrf) {
600         List<MatchInfo> matches = new ArrayList<>();
601
602         BigInteger dpnId = getDpIdFromInterface(intrf);
603
604         Integer ifIndex = intrf.getIfIndex();
605         matches.add(new MatchMetadata(MetaDataUtil.getLportTagMetaData(ifIndex), MetaDataUtil.METADATA_MASK_LPORT_TAG));
606         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.QOS_DSCP_TABLE,
607                 getQosFlowId(NwConstants.QOS_DSCP_TABLE, dpnId, ifIndex),
608                 QosConstants.QOS_DEFAULT_FLOW_PRIORITY, "QoSRemoveFlow", 0, 0, NwConstants.COOKIE_QOS_TABLE,
609                 matches, null);
610         mdsalUtils.removeFlow(flowEntity);
611     }
612
613     private void addFlow(BigInteger dpnId, Short dscpValue, String ifName, IpAddress ipAddress, Interface ifState) {
614         if (ifState == null) {
615             LOG.trace("Could not find the ifState for interface {}", ifName);
616             return;
617         }
618         Integer ifIndex = ifState.getIfIndex();
619
620         List<MatchInfo> matches = new ArrayList<>();
621         if (ipAddress.getIpv4Address() != null) {
622             matches.add(new MatchEthernetType(NwConstants.ETHTYPE_IPV4));
623         } else {
624             matches.add(new MatchEthernetType(NwConstants.ETHTYPE_IPV6));
625         }
626         matches.add(new MatchMetadata(MetaDataUtil.getLportTagMetaData(ifIndex), MetaDataUtil.METADATA_MASK_LPORT_TAG));
627
628         List<ActionInfo> actionsInfos = new ArrayList<>();
629         actionsInfos.add(new ActionSetFieldDscp(dscpValue));
630         actionsInfos.add(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE));
631
632         List<InstructionInfo> instructions = Collections.singletonList(new InstructionApplyActions(actionsInfos));
633         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.QOS_DSCP_TABLE,
634                 getQosFlowId(NwConstants.QOS_DSCP_TABLE, dpnId, ifIndex),
635                 QosConstants.QOS_DEFAULT_FLOW_PRIORITY, "QoSConfigFlow", 0, 0, NwConstants.COOKIE_QOS_TABLE,
636                 matches, instructions);
637         mdsalUtils.installFlow(flowEntity);
638     }
639
640     private void removeFlow(BigInteger dpnId, String ifName, Interface ifState) {
641         if (ifState == null) {
642             LOG.trace("Could not find the ifState for interface {}", ifName);
643             return;
644         }
645         Integer ifIndex = ifState.getIfIndex();
646
647         mdsalUtils.removeFlow(dpnId, NwConstants.QOS_DSCP_TABLE,
648                 new FlowId(getQosFlowId(NwConstants.QOS_DSCP_TABLE, dpnId, ifIndex)));
649     }
650
651     @Nullable
652     public org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
653             .ietf.interfaces.rev140508.interfaces.state.Interface getInterfaceStateFromOperDS(
654             String interfaceName) {
655         return MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
656                 createInterfaceStateInstanceIdentifier(interfaceName)).orNull();
657     }
658
659     @Nonnull
660     public static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
661             .ietf.interfaces.rev140508.interfaces.state.Interface> createInterfaceStateInstanceIdentifier(
662             String interfaceName) {
663         return InstanceIdentifier
664                 .builder(InterfacesState.class)
665                 .child(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
666                                 .ietf.interfaces.rev140508.interfaces.state.Interface.class,
667                         new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
668                                 .ietf.interfaces.rev140508.interfaces.state.InterfaceKey(
669                                 interfaceName))
670                 .build();
671     }
672
673     public void bindservice(String ifName) {
674         int priority = QosConstants.QOS_DEFAULT_FLOW_PRIORITY;
675         int instructionKey = 0;
676         List<Instruction> instructions = new ArrayList<>();
677         instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.QOS_DSCP_TABLE, ++instructionKey));
678         short qosServiceIndex = ServiceIndex.getIndex(NwConstants.QOS_SERVICE_NAME, NwConstants.QOS_SERVICE_INDEX);
679
680         BoundServices serviceInfo = getBoundServices(
681                 String.format("%s.%s", "qos", ifName), qosServiceIndex,
682                 priority, NwConstants.COOKIE_QOS_TABLE, instructions);
683         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
684                 buildServiceId(ifName, qosServiceIndex),
685                 serviceInfo);
686     }
687
688     public void unbindservice(String ifName) {
689         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, buildServiceId(ifName,
690                 ServiceIndex.getIndex(NwConstants.QOS_SERVICE_NAME, NwConstants.QOS_SERVICE_INDEX)));
691     }
692
693     private static InstanceIdentifier<BoundServices> buildServiceId(String interfaceName, short qosServiceIndex) {
694         return InstanceIdentifier.builder(ServiceBindings.class)
695                 .child(ServicesInfo.class, new ServicesInfoKey(interfaceName, ServiceModeIngress.class))
696                 .child(BoundServices.class, new BoundServicesKey(qosServiceIndex)).build();
697     }
698
699     private static BoundServices getBoundServices(String serviceName, short qosServiceIndex, int priority,
700                                                   BigInteger cookieQosTable, List<Instruction> instructions) {
701         StypeOpenflowBuilder augBuilder = new StypeOpenflowBuilder().setFlowCookie(cookieQosTable)
702                 .setFlowPriority(priority).setInstruction(instructions);
703         return new BoundServicesBuilder().setKey(new BoundServicesKey(qosServiceIndex)).setServiceName(serviceName)
704                 .setServicePriority(qosServiceIndex).setServiceType(ServiceTypeFlowBased.class)
705                 .addAugmentation(StypeOpenflow.class, augBuilder.build()).build();
706     }
707
708     @Nonnull
709     public static String getQosFlowId(short tableId, BigInteger dpId, int lportTag) {
710         return String.valueOf(tableId) + dpId + lportTag;
711     }
712
713     public String getPortNumberForInterface(String ifName) {
714         GetPortFromInterfaceInput portNumberInput = new GetPortFromInterfaceInputBuilder().setIntfName(ifName).build();
715         Future<RpcResult<GetPortFromInterfaceOutput>> portNumberOutput =
716                 odlInterfaceRpcService.getPortFromInterface(portNumberInput);
717         try {
718             RpcResult<GetPortFromInterfaceOutput> portResult = portNumberOutput.get();
719             if (portResult.isSuccessful()) {
720                 return portResult.getResult().getPortno().toString();
721             }
722         } catch (NullPointerException | InterruptedException | ExecutionException e) {
723             LOG.warn("Exception when getting port for interface {}", e);
724         }
725         return null;
726     }
727
728     public boolean portHasQosPolicy(Port port) {
729         LOG.trace("checking qos policy for port: {}", port.getUuid());
730
731         boolean isQosPolicy = port.getAugmentation(QosPortExtension.class) != null
732                 && port.getAugmentation(QosPortExtension.class).getQosPolicyId() != null;
733
734         LOG.trace("portHasQosPolicy for  port: {} return value {}", port.getUuid(), isQosPolicy);
735         return isQosPolicy;
736     }
737
738
739
740     public boolean hasBandwidthLimitRule(Port port) {
741         Uuid qosUuid = null;
742         boolean bwLimitRule = false;
743
744         LOG.trace("checking bandwidth limit rule for  port: {}", port.getUuid());
745
746         if (port.getAugmentation(QosPortExtension.class) != null) {
747             qosUuid = port.getAugmentation(QosPortExtension.class).getQosPolicyId();
748         } else {
749             Network network = neutronVpnManager.getNeutronNetwork(port.getNetworkId());
750
751             if (network.getAugmentation(QosNetworkExtension.class) != null) {
752                 qosUuid = network.getAugmentation(QosNetworkExtension.class).getQosPolicyId();
753             }
754         }
755
756         if (qosUuid != null) {
757             QosPolicy qosPolicy = qosPolicyMap.get(qosUuid);
758             if (qosPolicy != null && qosPolicy.getBandwidthLimitRules() != null
759                     && !qosPolicy.getBandwidthLimitRules().isEmpty()) {
760                 bwLimitRule = true;
761             }
762         }
763
764         LOG.trace("Bandwidth limit rule for  port: {} return value {}", port.getUuid(), bwLimitRule);
765         return bwLimitRule;
766     }
767
768     public boolean hasBandwidthLimitRule(Network network) {
769         boolean bwLimitRule = false;
770
771         LOG.trace("checking bandwidth limit rule for  network: {}", network.getUuid());
772
773         if (network.getAugmentation(QosNetworkExtension.class) != null) {
774             Uuid qosUuid = network.getAugmentation(QosNetworkExtension.class).getQosPolicyId();
775
776             if (qosUuid != null) {
777                 QosPolicy qosPolicy = qosPolicyMap.get(qosUuid);
778                 if (qosPolicy != null && qosPolicy.getBandwidthLimitRules() != null
779                         && !qosPolicy.getBandwidthLimitRules().isEmpty()) {
780                     bwLimitRule = true;
781                 }
782             }
783         }
784
785         LOG.trace("Bandwidth limit rule for  network: {} return value {}", network.getUuid(), bwLimitRule);
786         return bwLimitRule;
787     }
788
789     @Nullable
790     public QosPolicy getQosPolicy(Port port) {
791         Uuid qosUuid = null;
792         QosPolicy qosPolicy = null;
793
794         if (port.getAugmentation(QosPortExtension.class) != null) {
795             qosUuid = port.getAugmentation(QosPortExtension.class).getQosPolicyId();
796         } else {
797             Network network = neutronVpnManager.getNeutronNetwork(port.getNetworkId());
798
799             if (network.getAugmentation(QosNetworkExtension.class) != null) {
800                 qosUuid = network.getAugmentation(QosNetworkExtension.class).getQosPolicyId();
801             }
802         }
803
804         if (qosUuid != null) {
805             qosPolicy = qosPolicyMap.get(qosUuid);
806         }
807
808         return qosPolicy;
809     }
810
811 }