2 * Copyright (c) 2015 Red Hat, Inc. 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.ovsdb.openstack.netvirt.impl;
13 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
14 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
15 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
16 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.ovsdb.openstack.netvirt.MdsalHelper;
19 import org.opendaylight.ovsdb.openstack.netvirt.api.Action;
20 import org.opendaylight.ovsdb.openstack.netvirt.api.OvsdbInventoryListener;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
24 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
25 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
26 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
27 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
28 import org.opendaylight.yangtools.concepts.ListenerRegistration;
29 import org.opendaylight.yangtools.yang.binding.DataObject;
30 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
35 * MDSAL dataChangeListener for the OVSDB Southbound
37 * @author Sam Hague (shague@redhat.com)
39 public class OvsdbDataChangeListener implements DataChangeListener, AutoCloseable {
40 private static final Logger LOG = LoggerFactory.getLogger(OvsdbDataChangeListener.class);
41 private DataBroker dataBroker = null;
42 private ListenerRegistration<DataChangeListener> registration;
44 public OvsdbDataChangeListener (DataBroker dataBroker) {
45 LOG.info(">>>>> Registering OvsdbNodeDataChangeListener: dataBroker= {}", dataBroker);
46 this.dataBroker = dataBroker;
47 InstanceIdentifier<Node> path = InstanceIdentifier
48 .create(NetworkTopology.class)
49 .child(Topology.class, new TopologyKey(MdsalHelper.OVSDB_TOPOLOGY_ID))
51 registration = dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, path, this,
52 DataChangeScope.SUBTREE);
53 LOG.info("netvirt OvsdbDataChangeListener: registration= {}", registration);
57 public void close () throws Exception {
62 public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) {
63 LOG.debug(">>>>> onDataChanged: {}", changes);
64 //TODO SB_MIGRATION: off load this process to execution service, blocking md-sal notification thread
65 // has performance impact on overall controller performance. With new notification broker
66 //it might create weird issues.
67 processOvsdbConnections(changes);
68 processOvsdbConnectionAttributeUpdates(changes);
69 processBridgeCreation(changes);
70 processBridgeUpdate(changes);
71 processPortCreation(changes);
72 processPortUpdate(changes);
73 processPortDeletion(changes);
74 processBridgeDeletion(changes);
75 processOvsdbDisconnect(changes);
78 private void processOvsdbConnections(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) {
79 for (Map.Entry<InstanceIdentifier<?>, DataObject> created : changes.getCreatedData().entrySet()) {
80 if (created.getValue() instanceof OvsdbNodeAugmentation) {
81 Node ovsdbNode = getNode(changes.getCreatedData(), created);
82 LOG.info("Processing ovsdb connections : {}, ovsdbNode: {}", created, ovsdbNode);
83 ovsdbUpdate(ovsdbNode, created.getValue(), OvsdbInventoryListener.OvsdbType.NODE, Action.ADD);
88 private void processOvsdbDisconnect(
89 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) {
91 for(InstanceIdentifier<?> removedOvsdbNode : changes.getRemovedPaths()) {
92 if(removedOvsdbNode.getTargetType().equals(OvsdbNodeAugmentation.class)){
93 //Get top node to get details of all the bridge/termination point augmentation
94 // in case we want to do any cleanup task while processing node disconnection
95 Node parentNode = getNode(changes.getOriginalData(), removedOvsdbNode);
96 if(parentNode == null){
97 //Throwing this warning in case behavior of southbound plugin changes.
98 LOG.warn("OvsdbNode's {} parent node details are not present in original data,"
99 + " it should not happen", parentNode);
102 //Fetch data of removed connection info from original data
103 @SuppressWarnings("unchecked")
104 OvsdbNodeAugmentation removedOvsdbNodeAugmentationData = getDataChanges(changes.getOriginalData(),
105 (InstanceIdentifier<OvsdbNodeAugmentation>) removedOvsdbNode);
107 LOG.debug("Process ovsdb node delete : {} ", removedOvsdbNode);
108 ////Assuming Openvswitch type represent the ovsdb node connection and not OvsdbType.NODE
110 ovsdbUpdate(parentNode, removedOvsdbNodeAugmentationData,
111 OvsdbInventoryListener.OvsdbType.NODE, Action.DELETE);
116 private void processOvsdbConnectionAttributeUpdates(
117 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) {
119 for(Map.Entry<InstanceIdentifier<?>, DataObject> updatedOvsdbNode : changes.getUpdatedData().entrySet()){
120 if(updatedOvsdbNode.getKey().getTargetType().equals(OvsdbNodeAugmentation.class)){
121 LOG.info("Processing Ovsdb Node attributes update : {}",updatedOvsdbNode);
122 /* XXX (NOTE): Till now we don't really need the old ovsdb connection attributes data before update.
123 * I am passing the updated data of both Node and resource augmentation data (connection attributes).
124 * If in future we need old OvsdbNodeAugmentation attributes data, we will extract it from
125 * original data and pass it as a resourceAugmentationData.
127 Node parentNode = getNode(changes.getUpdatedData(),updatedOvsdbNode);
128 if(parentNode == null){
129 // Logging this warning, to catch any change in southbound plugin's behavior.
130 LOG.warn("Parent Node for OvsdbNodeAugmentation is not found. On OvsdbNodeAugmentation update "
131 + "data store must provide the parent node update. This condition should not occur "
132 + "with the existing models defined in southbound plugin." );
135 LOG.debug("Process ovsdb connection {} related update on Node : {}",
136 updatedOvsdbNode.getValue(), parentNode);
138 ovsdbUpdate(parentNode, updatedOvsdbNode.getValue(),
139 OvsdbInventoryListener.OvsdbType.NODE, Action.UPDATE);
144 private void processPortCreation(
145 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) {
146 for(Map.Entry<InstanceIdentifier<?>, DataObject> newPort : changes.getCreatedData().entrySet()){
147 if(newPort.getKey().getTargetType().equals(OvsdbTerminationPointAugmentation.class)){
148 LOG.info("Processing creation of new port : {}",newPort);
149 //If user created termination point only, Node will get updated
150 Node tpParentNode = getNode(changes.getUpdatedData(), newPort);
151 if(tpParentNode == null){
152 //If user created port with the bridge itself, Node will be in created data
153 tpParentNode = getNode(changes.getCreatedData(),newPort);
155 if(tpParentNode == null){
156 // Logging this warning, to make sure we didn't change anything
157 // in southbound plugin that changes this behavior.
158 LOG.warn("Parent Node for port is not found. Port creation must create or "
159 + "update the Node. This condition should not occure" );
163 LOG.debug("Process new port {} creation on Node : {}", newPort.getValue(),tpParentNode);
164 ovsdbUpdate(tpParentNode, newPort.getValue(),OvsdbInventoryListener.OvsdbType.PORT, Action.ADD);
169 private void processPortDeletion(
170 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) {
172 for(InstanceIdentifier<?> removedPort : changes.getRemovedPaths()) {
173 if(removedPort.getTargetType().equals(OvsdbTerminationPointAugmentation.class)){
174 Node tpParentNode = getNode(changes.getOriginalData(), removedPort);
175 if(tpParentNode == null){
176 //Throwing this warning in case behavior of southbound plugin changes.
177 LOG.warn("Port's {} parent node details are not present in original data, "
178 + "it should not happen", removedPort);
181 //Fetch data of removed port from original data
182 @SuppressWarnings("unchecked")
183 OvsdbTerminationPointAugmentation removedTPAugmentationData = getDataChanges(changes.getOriginalData(),
184 (InstanceIdentifier<OvsdbTerminationPointAugmentation>)removedPort);
186 LOG.debug("Process port {} deletion on Node : {}", removedPort,tpParentNode);
187 ovsdbUpdate(tpParentNode, removedTPAugmentationData,
188 OvsdbInventoryListener.OvsdbType.PORT, Action.DELETE);
194 private void processPortUpdate(
195 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) {
197 for(Map.Entry<InstanceIdentifier<?>, DataObject> updatedPort : changes.getUpdatedData().entrySet()){
198 if(updatedPort.getKey().getTargetType().equals(OvsdbTerminationPointAugmentation.class)){
199 LOG.info("Processing port update : {}",updatedPort);
200 /* XXX (NOTE): Till now we don't really need the old termination point data before update.
201 * I am passing the updated data of both Node and resource augmentation data (termination-point).
202 * If in future we need old TerminationPointAugmentation data, we will extract it from
203 * original data and pass it as a resourceAugmentationData.
205 Node tpParentNode = getNode(changes.getUpdatedData(),updatedPort);
206 if(tpParentNode == null){
207 // Logging this warning, to catch any change in southbound plugin's behavior.
208 LOG.warn("Parent Node for port is not found. On Port/Interface update data store"
209 + " must provide the parent node update. This condition should not occure "
210 + "with the existing models define in southbound plugin." );
214 LOG.debug("Process port's {} update on Node : {}", updatedPort.getValue(),tpParentNode);
215 ovsdbUpdate(tpParentNode, updatedPort.getValue(),
216 OvsdbInventoryListener.OvsdbType.PORT, Action.UPDATE);
221 private void processBridgeCreation(
222 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) {
224 for(Map.Entry<InstanceIdentifier<?>, DataObject> newBridge : changes.getCreatedData().entrySet()){
225 if(newBridge.getKey().getTargetType().equals(OvsdbBridgeAugmentation.class)){
226 LOG.info("Processing creation of new bridge : {}",newBridge);
227 //Bridge augmentation happens directly on the Node so Node details should also exist in created data.
228 Node bridgeParentNode = getNode(changes.getCreatedData(),newBridge);
229 if(bridgeParentNode == null){
230 // Logging this warning, to catch any change in southbound plugin behavior
231 LOG.warn("Parent Node for bridge is not found. Bridge creation must provide the Node "
232 + "details in create Data Changes. This condition should not occure" );
235 LOG.debug("Process new bridge {} creation on Node : {}", newBridge.getValue(),bridgeParentNode);
236 ovsdbUpdate(bridgeParentNode, newBridge.getValue(),
237 OvsdbInventoryListener.OvsdbType.BRIDGE, Action.ADD);
242 private void processBridgeUpdate(
243 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) {
245 for (Map.Entry<InstanceIdentifier<?>, DataObject> updatedBridge : changes.getUpdatedData().entrySet()) {
246 if(updatedBridge.getKey().getTargetType().equals(OvsdbBridgeAugmentation.class)){
247 LOG.info("Processing update on a bridge : {}",updatedBridge);
248 /* XXX (NOTE): Till now we don't really need the old bridge data before update.
249 * I am passing the updated data of both Node and resource augmentation data.
250 * If in future we need old bridgeAugmentationData, we will extract it from
251 * original data and pass it as a resourceAugmentationData.
254 Node bridgeParentNode = getNode(changes.getUpdatedData(), updatedBridge);
255 if(bridgeParentNode == null){
256 // Logging this warning, to catch any change in southbound plugin behavior
257 LOG.warn("Parent Node for bridge is not found. Bridge update must provide the Node "
258 + "details in updated Data Changes. This condition should not occure" );
261 LOG.debug("Process bridge {} update on Node : {}", updatedBridge.getValue(),bridgeParentNode);
262 ovsdbUpdate(bridgeParentNode, updatedBridge.getValue(),
263 OvsdbInventoryListener.OvsdbType.BRIDGE, Action.UPDATE);
268 private void processBridgeDeletion(
269 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) {
271 for(InstanceIdentifier<?> removedBridge : changes.getRemovedPaths()) {
272 if(removedBridge.getTargetType().equals(OvsdbBridgeAugmentation.class)){
273 Node bridgeParentNode = getNode(changes.getOriginalData(), removedBridge);
274 if(bridgeParentNode == null){
275 //Throwing this warning to catch the behavior change of southbound plugin.
276 LOG.warn("Bridge's {} parent node details are not present in original data"
277 + ", it should not happen", removedBridge);
280 //Fetch data of removed bridge from original data
281 @SuppressWarnings("unchecked")
282 OvsdbBridgeAugmentation removedBridgeAugmentationData = getDataChanges(changes.getOriginalData(),
283 (InstanceIdentifier<OvsdbBridgeAugmentation>) removedBridge);
285 LOG.debug("Process bridge {} deletion on Node : {}", removedBridge,bridgeParentNode);
286 ovsdbUpdate(bridgeParentNode, removedBridgeAugmentationData,
287 OvsdbInventoryListener.OvsdbType.BRIDGE, Action.DELETE);
292 private Node getNode(Map<InstanceIdentifier<?>, DataObject> changes,
293 Map.Entry<InstanceIdentifier<?>, DataObject> change) {
294 InstanceIdentifier<Node> nodeInstanceIdentifier = change.getKey().firstIdentifierOf(Node.class);
295 return (Node)changes.get(nodeInstanceIdentifier);
298 private Node getNode(Map<InstanceIdentifier<?>, DataObject> changes,InstanceIdentifier<?> path) {
299 InstanceIdentifier<Node> nodeInstanceIdentifier = path.firstIdentifierOf(Node.class);
300 return (Node)changes.get(nodeInstanceIdentifier);
303 private <T extends DataObject> T getDataChanges(
304 Map<InstanceIdentifier<?>, DataObject> changes,InstanceIdentifier<T> path){
306 for(Map.Entry<InstanceIdentifier<?>,DataObject> change : changes.entrySet()){
307 if(change.getKey().getTargetType().equals(path.getTargetType())){
308 @SuppressWarnings("unchecked")
309 T dataObject = (T) change.getValue();
316 private void ovsdbUpdate(Node node, DataObject resourceAugmentationDataChanges,
317 OvsdbInventoryListener.OvsdbType ovsdbType, Action action) {
319 Set<OvsdbInventoryListener> mdsalConsumerListeners = OvsdbInventoryServiceImpl.getMdsalConsumerListeners();
320 for (OvsdbInventoryListener mdsalConsumerListener : mdsalConsumerListeners) {
321 mdsalConsumerListener.ovsdbUpdate(node, resourceAugmentationDataChanges, ovsdbType, action);