c779ff69a1d008edc70e8c56befb86581398e2ff
[ovsdb.git] / southbound / southbound-impl / src / main / java / org / opendaylight / ovsdb / southbound / transactions / md / OvsdbQosUpdateCommand.java
1 /*
2  * Copyright © 2016, 2017 Intel Communications Systems, Inc. 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
9 package org.opendaylight.ovsdb.southbound.transactions.md;
10
11 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
12 import java.util.Collection;
13 import java.util.Map;
14 import java.util.Map.Entry;
15 import java.util.Optional;
16 import java.util.Set;
17 import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
18 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
19 import org.opendaylight.ovsdb.lib.message.TableUpdates;
20 import org.opendaylight.ovsdb.lib.notation.UUID;
21 import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
22 import org.opendaylight.ovsdb.lib.schema.typed.TyperUtils;
23 import org.opendaylight.ovsdb.schema.openvswitch.Qos;
24 import org.opendaylight.ovsdb.schema.openvswitch.Queue;
25 import org.opendaylight.ovsdb.southbound.InstanceIdentifierCodec;
26 import org.opendaylight.ovsdb.southbound.OvsdbConnectionInstance;
27 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
28 import org.opendaylight.ovsdb.southbound.SouthboundMapper;
29 import org.opendaylight.ovsdb.southbound.SouthboundUtil;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbQueueRef;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.QosEntries;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.QosEntriesBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.QosEntriesKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.Queues;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.QueuesKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.qos.entries.QosExternalIds;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.qos.entries.QosExternalIdsBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.qos.entries.QosExternalIdsKey;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.qos.entries.QosOtherConfig;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.qos.entries.QosOtherConfigBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.qos.entries.QosOtherConfigKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.qos.entries.QueueList;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.qos.entries.QueueListBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.qos.entries.QueueListKey;
48 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
49 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
50 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
51 import org.opendaylight.yangtools.yang.binding.util.BindingMap;
52 import org.opendaylight.yangtools.yang.common.Uint32;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 @SuppressFBWarnings(value = "MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR", justification = "Non-final for mocking")
57 public class OvsdbQosUpdateCommand extends AbstractTransactionCommand {
58     private static final Logger LOG = LoggerFactory.getLogger(OvsdbQosUpdateCommand.class);
59
60     private final InstanceIdentifierCodec instanceIdentifierCodec;
61
62     private final Map<UUID, Qos> updatedQosRows;
63     private final Map<UUID, Qos> oldQosRows;
64     private final Map<UUID, Queue> updatedQueueRows;
65
66     public OvsdbQosUpdateCommand(InstanceIdentifierCodec instanceIdentifierCodec, OvsdbConnectionInstance key,
67             TableUpdates updates, DatabaseSchema dbSchema) {
68         super(key, updates, dbSchema);
69         this.instanceIdentifierCodec = instanceIdentifierCodec;
70         updatedQosRows = TyperUtils.extractRowsUpdated(Qos.class,getUpdates(), getDbSchema());
71         oldQosRows = TyperUtils.extractRowsOld(Qos.class, getUpdates(), getDbSchema());
72         updatedQueueRows = TyperUtils.extractRowsUpdated(Queue.class, getUpdates(), getDbSchema());
73     }
74
75     @Override
76     public void execute(ReadWriteTransaction transaction) {
77         if (updatedQosRows != null && !updatedQosRows.isEmpty()) {
78             updateQos(transaction, updatedQosRows);
79         }
80     }
81
82     /**
83      * Update the QosEntries values after finding the related
84      * {@link org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch} list.
85      * <p>
86      * Qos and OpenVSwitch are independent tables in the Open_vSwitch schema
87      * but the OVSDB yang model includes the Qos fields in the
88      * OvsdbNode data. In some cases the OVSDB will send OpenVSwitch and Qos
89      * updates together and in other cases independently. This method here
90      * assumes the latter.
91      * </p>
92      *
93      * @param transaction the {@link ReadWriteTransaction}
94      * @param newUpdatedQosRows updated {@link Qos} rows
95
96      */
97     private void updateQos(ReadWriteTransaction transaction, Map<UUID, Qos> newUpdatedQosRows) {
98
99         final InstanceIdentifier<Node> nodeIId = getOvsdbConnectionInstance().getInstanceIdentifier();
100         final Optional<Node> ovsdbNode = SouthboundUtil.readNode(transaction, nodeIId);
101         if (ovsdbNode.isPresent()) {
102             for (Entry<UUID, Qos> entry : newUpdatedQosRows.entrySet()) {
103                 Qos qos = entry.getValue();
104                 QosEntriesBuilder qosEntryBuilder = new QosEntriesBuilder();
105                 qosEntryBuilder.setQosId(new Uri(getQosId(qos)));
106                 qosEntryBuilder.setQosUuid(new Uuid(entry.getKey().toString()));
107                 qosEntryBuilder.setQosType(
108                         SouthboundMapper.createQosType(qos.getTypeColumn().getData()));
109                 Qos oldQos = oldQosRows.get(entry.getKey());
110                 setOtherConfig(transaction, qosEntryBuilder, oldQos, qos, nodeIId);
111                 setExternalIds(transaction, qosEntryBuilder, oldQos, qos, nodeIId);
112                 setQueueList(transaction, qosEntryBuilder, oldQos, qos, nodeIId, ovsdbNode.get());
113
114                 QosEntries qosEntry = qosEntryBuilder.build();
115                 LOG.debug("Update Ovsdb Node {} with qos entries {}",ovsdbNode.get(), qosEntry);
116                 InstanceIdentifier<QosEntries> iid = nodeIId
117                         .augmentation(OvsdbNodeAugmentation.class)
118                         .child(QosEntries.class, qosEntry.key());
119                 transaction.merge(LogicalDatastoreType.OPERATIONAL,
120                         iid, qosEntry);
121             }
122         }
123     }
124
125     @SuppressWarnings("unchecked")
126     private String getQosId(Qos qos) {
127         if (qos.getExternalIdsColumn() != null
128                 && qos.getExternalIdsColumn().getData() != null) {
129             if (qos.getExternalIdsColumn().getData().containsKey(SouthboundConstants.IID_EXTERNAL_ID_KEY)) {
130                 InstanceIdentifier<QosEntries> qosIid =
131                         (InstanceIdentifier<QosEntries>) instanceIdentifierCodec.bindingDeserializerOrNull(
132                                 qos.getExternalIdsColumn().getData().get(SouthboundConstants.IID_EXTERNAL_ID_KEY));
133                 if (qosIid != null) {
134                     QosEntriesKey qosEntriesKey = qosIid.firstKeyOf(QosEntries.class);
135                     if (qosEntriesKey != null) {
136                         return qosEntriesKey.getQosId().getValue();
137                     }
138                 }
139             } else if (qos.getExternalIdsColumn().getData().containsKey(SouthboundConstants.QOS_ID_EXTERNAL_ID_KEY)) {
140                 return qos.getExternalIdsColumn().getData().get(SouthboundConstants.QOS_ID_EXTERNAL_ID_KEY);
141             }
142         }
143         return SouthboundConstants.QOS_URI_PREFIX + "://" + qos.getUuid().toString();
144     }
145
146     private Queue getQueue(UUID queueUuid) {
147         for (Entry<UUID, Queue> entry : updatedQueueRows.entrySet()) {
148             if (entry.getKey().equals(queueUuid)) {
149                 return entry.getValue();
150             }
151         }
152         return null;
153     }
154
155     @SuppressWarnings("unchecked")
156     private InstanceIdentifier<Queues> getQueueIid(UUID queueUuid, Node ovsdbNode) {
157         Queue queue = getQueue(queueUuid);
158         if (queue != null && queue.getExternalIdsColumn() != null
159                 && queue.getExternalIdsColumn().getData() != null
160                 && queue.getExternalIdsColumn().getData().containsKey(SouthboundConstants.IID_EXTERNAL_ID_KEY)) {
161             return (InstanceIdentifier<Queues>) instanceIdentifierCodec.bindingDeserializerOrNull(
162                     queue.getExternalIdsColumn().getData().get(SouthboundConstants.IID_EXTERNAL_ID_KEY));
163         } else {
164             OvsdbNodeAugmentation node = ovsdbNode.augmentation(OvsdbNodeAugmentation.class);
165             Map<QueuesKey, Queues> queues = node.getQueues();
166             if (queues != null) {
167                 final Uuid uuid = new Uuid(queueUuid.toString());
168                 for (Queues q : queues.values()) {
169                     if (uuid.equals(q.getQueueUuid())) {
170                         return SouthboundMapper.createInstanceIdentifier(ovsdbNode.getNodeId())
171                                 .augmentation(OvsdbNodeAugmentation.class)
172                                 .child(Queues.class, q.key());
173                     }
174                 }
175             }
176             LOG.debug("A Queue with UUID {} was not found in Ovsdb Node {}", queueUuid, node);
177             return SouthboundMapper.createInstanceIdentifier(ovsdbNode.getNodeId())
178                     .augmentation(OvsdbNodeAugmentation.class)
179                     .child(Queues.class, new QueuesKey(
180                             new Uri(SouthboundConstants.QUEUE_URI_PREFIX + "://" + queueUuid.toString())));
181         }
182     }
183
184     private static void setOtherConfig(ReadWriteTransaction transaction,
185             QosEntriesBuilder qosEntryBuilder, Qos oldQos, Qos qos,
186             InstanceIdentifier<Node> nodeIId) {
187         Map<String, String> oldOtherConfigs = null;
188         Map<String, String> otherConfigs = null;
189
190         if (qos.getOtherConfigColumn() != null) {
191             otherConfigs = qos.getOtherConfigColumn().getData();
192         }
193         if (oldQos != null && oldQos.getOtherConfigColumn() != null) {
194             oldOtherConfigs = oldQos.getOtherConfigColumn().getData();
195         }
196         if (oldOtherConfigs != null && !oldOtherConfigs.isEmpty()) {
197             removeOldConfigs(transaction, qosEntryBuilder, oldOtherConfigs, qos, nodeIId);
198         }
199         if (otherConfigs != null && !otherConfigs.isEmpty()) {
200             setNewOtherConfigs(qosEntryBuilder, otherConfigs);
201         }
202     }
203
204     private static void removeOldConfigs(ReadWriteTransaction transaction,
205             QosEntriesBuilder qosEntryBuilder, Map<String, String> oldOtherConfigs,
206             Qos qos, InstanceIdentifier<Node> nodeIId) {
207         InstanceIdentifier<QosEntries> qosIId = nodeIId
208                 .augmentation(OvsdbNodeAugmentation.class)
209                 .child(QosEntries.class, qosEntryBuilder.build().key());
210         Set<String> otherConfigKeys = oldOtherConfigs.keySet();
211         for (String otherConfigKey : otherConfigKeys) {
212             KeyedInstanceIdentifier<QosOtherConfig, QosOtherConfigKey> otherIId =
213                     qosIId
214                     .child(QosOtherConfig.class, new QosOtherConfigKey(otherConfigKey));
215             transaction.delete(LogicalDatastoreType.OPERATIONAL, otherIId);
216         }
217     }
218
219     private static void setNewOtherConfigs(QosEntriesBuilder qosEntryBuilder,
220             Map<String, String> otherConfig) {
221         var otherConfigList = BindingMap.<QosOtherConfigKey, QosOtherConfig>orderedBuilder();
222         for (Entry<String, String> entry : otherConfig.entrySet()) {
223             String otherConfigKey = entry.getKey();
224             String otherConfigValue = entry.getValue();
225             if (otherConfigKey != null && otherConfigValue != null) {
226                 otherConfigList.add(new QosOtherConfigBuilder().setOtherConfigKey(otherConfigKey)
227                         .setOtherConfigValue(otherConfigValue).build());
228             }
229         }
230         qosEntryBuilder.setQosOtherConfig(otherConfigList.build());
231     }
232
233     private static void setExternalIds(ReadWriteTransaction transaction,
234             QosEntriesBuilder qosEntryBuilder, Qos oldQos, Qos qos,
235             InstanceIdentifier<Node> nodeIId) {
236         Map<String, String> oldExternalIds = null;
237         Map<String, String> externalIds = null;
238
239         if (qos.getExternalIdsColumn() != null) {
240             externalIds = qos.getExternalIdsColumn().getData();
241         }
242         if (oldQos != null && oldQos.getExternalIdsColumn() != null) {
243             oldExternalIds = oldQos.getExternalIdsColumn().getData();
244         }
245         if (oldExternalIds != null && !oldExternalIds.isEmpty()) {
246             removeOldExternalIds(transaction, qosEntryBuilder, oldExternalIds, qos, nodeIId);
247         }
248         if (externalIds != null && !externalIds.isEmpty()) {
249             setNewExternalIds(qosEntryBuilder, externalIds);
250         }
251     }
252
253     private static void removeOldExternalIds(ReadWriteTransaction transaction,
254             QosEntriesBuilder qosEntryBuilder, Map<String, String> oldExternalIds,
255             Qos qos, InstanceIdentifier<Node> nodeIId) {
256         InstanceIdentifier<QosEntries> qosIId = nodeIId
257                 .augmentation(OvsdbNodeAugmentation.class)
258                 .child(QosEntries.class, qosEntryBuilder.build().key());
259         Set<String> externalIdsKeys = oldExternalIds.keySet();
260         for (String extIdKey : externalIdsKeys) {
261             KeyedInstanceIdentifier<QosExternalIds, QosExternalIdsKey> externalIId =
262                     qosIId
263                     .child(QosExternalIds.class, new QosExternalIdsKey(extIdKey));
264             transaction.delete(LogicalDatastoreType.OPERATIONAL, externalIId);
265         }
266     }
267
268     private static void setNewExternalIds(QosEntriesBuilder qosEntryBuilder,
269             Map<String, String> externalIds) {
270         var externalIdsList = BindingMap.<QosExternalIdsKey, QosExternalIds>orderedBuilder();
271         for (Entry<String, String> entry : externalIds.entrySet()) {
272             String extIdKey = entry.getKey();
273             String extIdValue = entry.getValue();
274             if (extIdKey != null && extIdValue != null) {
275                 externalIdsList.add(new QosExternalIdsBuilder().setQosExternalIdKey(extIdKey)
276                         .setQosExternalIdValue(extIdValue).build());
277             }
278         }
279         qosEntryBuilder.setQosExternalIds(externalIdsList.build());
280     }
281
282     private void setQueueList(ReadWriteTransaction transaction,
283             QosEntriesBuilder qosEntryBuilder, Qos oldQos, Qos qos,
284             InstanceIdentifier<Node> nodeIId, Node ovsdbNode) {
285         Map<Long,UUID> oldQueueList = null;
286         Map<Long,UUID> queueList = null;
287
288         if (qos.getQueuesColumn() != null) {
289             queueList = qos.getQueuesColumn().getData();
290         }
291         if (oldQos != null && oldQos.getQueuesColumn() != null) {
292             oldQueueList = oldQos.getQueuesColumn().getData();
293         }
294         if (oldQueueList != null && !oldQueueList.isEmpty()) {
295             removeOldQueues(transaction, qosEntryBuilder, oldQueueList, qos, nodeIId);
296         }
297         if (queueList != null && !queueList.isEmpty()) {
298             setNewQueues(qosEntryBuilder, queueList, ovsdbNode);
299         }
300     }
301
302     private static void removeOldQueues(ReadWriteTransaction transaction,
303             QosEntriesBuilder qosEntryBuilder, Map<Long, UUID> oldQueueList,
304             Qos qos, InstanceIdentifier<Node> nodeIId) {
305         InstanceIdentifier<QosEntries> qosIId = nodeIId
306                 .augmentation(OvsdbNodeAugmentation.class)
307                 .child(QosEntries.class, qosEntryBuilder.build().key());
308         Collection<Long> queueListKeys = oldQueueList.keySet();
309         for (Long queueListKey : queueListKeys) {
310             KeyedInstanceIdentifier<QueueList, QueueListKey> otherIId =
311                     qosIId.child(QueueList.class, new QueueListKey(Uint32.valueOf(queueListKey)));
312             transaction.delete(LogicalDatastoreType.OPERATIONAL, otherIId);
313         }
314     }
315
316     private void setNewQueues(QosEntriesBuilder qosEntryBuilder, Map<Long, UUID> queueList, Node ovsdbNode) {
317         Set<Entry<Long, UUID>> queueEntries = queueList.entrySet();
318         var newQueueList = BindingMap.<QueueListKey, QueueList>orderedBuilder();
319         for (Entry<Long, UUID> queueEntry : queueEntries) {
320             InstanceIdentifier<Queues> queueIid = getQueueIid(queueEntry.getValue(), ovsdbNode);
321             if (queueIid != null) {
322                 newQueueList.add(
323                     new QueueListBuilder()
324                     .setQueueNumber(Uint32.valueOf(queueEntry.getKey()))
325                     .setQueueRef(new OvsdbQueueRef(queueIid)).build());
326             }
327
328         }
329         qosEntryBuilder.setQueueList(newQueueList.build());
330     }
331 }