Convert DataChangeListeners to DataTreeChangeListeners
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / node / FlowCapableNodeConnectorListener.java
1 /*
2  * Copyright (c) 2014 Cisco 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.groupbasedpolicy.renderer.ofoverlay.node;
10
11 import static com.google.common.base.Preconditions.checkNotNull;
12
13 import com.google.common.base.Objects;
14 import com.google.common.base.Optional;
15 import com.google.common.util.concurrent.Futures;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.Map;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
22 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
23 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
24 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
25 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
26 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
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.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Name;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.Endpoints;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContextBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayL3Context;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayL3ContextBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
45 import org.opendaylight.yangtools.concepts.ListenerRegistration;
46 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 public class FlowCapableNodeConnectorListener implements DataTreeChangeListener<FlowCapableNodeConnector>,
51         AutoCloseable {
52
53     private static final Logger LOG = LoggerFactory.getLogger(FlowCapableNodeConnectorListener.class);
54
55     private final static InstanceIdentifier<FlowCapableNodeConnector> fcNodeConnectorIid = InstanceIdentifier.builder(
56             Nodes.class)
57         .child(Node.class)
58         .child(NodeConnector.class)
59         .augmentation(FlowCapableNodeConnector.class)
60         .build();
61     private final static InstanceIdentifier<Endpoints> endpointsIid = InstanceIdentifier.builder(Endpoints.class)
62         .build();
63     private final DataBroker dataProvider;
64     private final SwitchManager switchManager;
65     private final ListenerRegistration<?> listenerRegistration;
66
67     public FlowCapableNodeConnectorListener(DataBroker dataProvider, SwitchManager switchManager) {
68         this.dataProvider = checkNotNull(dataProvider);
69         this.switchManager = checkNotNull(switchManager);
70         listenerRegistration = dataProvider.registerDataTreeChangeListener(new DataTreeIdentifier<>(
71                 LogicalDatastoreType.OPERATIONAL, fcNodeConnectorIid), this);
72     }
73
74     @Override
75     public void onDataTreeChanged(Collection<DataTreeModification<FlowCapableNodeConnector>> changes) {
76         ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
77
78         //endpoint and endpoint L3 maps
79         Map<Name, Endpoint> epWithOfOverlayAugByPortName = readEpsWithOfOverlayAugByPortName(rwTx);
80         Map<Name, EndpointL3> l3EpWithOfOverlayAugByPortName = readL3EpsWithOfOverlayAugByPortName(rwTx);
81
82         boolean isDataPutToTx = false;
83         for (DataTreeModification<FlowCapableNodeConnector> change: changes) {
84             DataObjectModification<FlowCapableNodeConnector> rootNode = change.getRootNode();
85             InstanceIdentifier<NodeConnector> ncIid = change.getRootPath().getRootIdentifier()
86                     .firstIdentifierOf(NodeConnector.class);
87             final FlowCapableNodeConnector originalFcnc = rootNode.getDataBefore();
88             boolean updated;
89             boolean l3Updated;
90             switch (rootNode.getModificationType()) {
91                 case SUBTREE_MODIFIED:
92                 case WRITE:
93                     FlowCapableNodeConnector fcnc = rootNode.getDataAfter();
94                     LOG.trace(
95                             "FlowCapableNodeConnector updated: NodeId: {} NodeConnectorId: {} FlowCapableNodeConnector: {}",
96                             ncIid.firstKeyOf(Node.class, NodeKey.class).getId().getValue(),
97                             ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue(), fcnc);
98                     switchManager.updateSwitchNodeConnectorConfig(ncIid, fcnc);
99                     Name portName = getPortName(fcnc);
100                     updated = updateEpWithNodeConnectorInfo(epWithOfOverlayAugByPortName.get(portName), ncIid, rwTx);
101                     l3Updated = updateL3EpWithNodeConnectorInfo(l3EpWithOfOverlayAugByPortName.get(portName), ncIid, rwTx);
102                     if (updated || l3Updated) {
103                         isDataPutToTx = true;
104                     }
105
106                     if (originalFcnc != null) {
107                         Name portNameFromOriginalFcnc = getPortName(originalFcnc);
108                         // port name already existed and then was changed
109                         if (portNameFromOriginalFcnc != null && !Objects.equal(portNameFromOriginalFcnc, portName)) {
110                             updated = updateEpWithNodeConnectorInfo(epWithOfOverlayAugByPortName
111                                     .get(portNameFromOriginalFcnc), null, rwTx);
112                             l3Updated = updateL3EpWithNodeConnectorInfo(l3EpWithOfOverlayAugByPortName
113                                     .get(portNameFromOriginalFcnc), null, rwTx);
114                             if (updated || l3Updated) {
115                                 isDataPutToTx = true;
116                             }
117                         }
118                     }
119                     break;
120                 case DELETE:
121                     LOG.trace("FlowCapableNodeConnector removed: NodeId: {} NodeConnectorId: {}",
122                             ncIid.firstKeyOf(Node.class, NodeKey.class).getId().getValue(),
123                             ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue());
124                     switchManager.updateSwitchNodeConnectorConfig(ncIid, null);
125                     Name portNameFromOriginalFcnc = getPortName(originalFcnc);
126                     updated = updateEpWithNodeConnectorInfo(epWithOfOverlayAugByPortName.get(portNameFromOriginalFcnc), null, rwTx);
127                     l3Updated = updateL3EpWithNodeConnectorInfo(l3EpWithOfOverlayAugByPortName.get(portNameFromOriginalFcnc), null, rwTx);
128                     if (updated || l3Updated) {
129                         isDataPutToTx = true;
130                     }
131                     break;
132                 default:
133                     break;
134             }
135         }
136
137         if (isDataPutToTx) {
138             rwTx.submit();
139         } else {
140             rwTx.cancel();
141         }
142     }
143
144     //read endpoints from listener entry
145     private Map<Name, Endpoint> readEpsWithOfOverlayAugByPortName(ReadTransaction rTx) {
146         Optional<Endpoints> potentialEps = Futures.getUnchecked(rTx.read(LogicalDatastoreType.OPERATIONAL, endpointsIid));
147         if (!potentialEps.isPresent() || potentialEps.get().getEndpoint() == null) {
148             return Collections.emptyMap();
149         }
150         Map<Name, Endpoint> epsByPortName = new HashMap<>();
151         for (Endpoint ep : potentialEps.get().getEndpoint()) {
152             OfOverlayContext ofOverlayEp = ep.getAugmentation(OfOverlayContext.class);
153             if (ofOverlayEp != null && ofOverlayEp.getPortName() != null) {
154                 epsByPortName.put(ofOverlayEp.getPortName(), ep);
155             }
156         }
157         return epsByPortName;
158     }
159
160     //read l3 endpoint from listener entry
161     private Map<Name, EndpointL3> readL3EpsWithOfOverlayAugByPortName(ReadTransaction rTx) {
162         Optional<Endpoints> potentialEps = Futures.getUnchecked(rTx.read(LogicalDatastoreType.OPERATIONAL, endpointsIid));
163         if (!potentialEps.isPresent() || potentialEps.get().getEndpoint() == null) {
164             return Collections.emptyMap();
165         }
166         Map<Name, EndpointL3> epsByPortName = new HashMap<>();
167         for (EndpointL3 epL3 : potentialEps.get().getEndpointL3()) {
168             OfOverlayL3Context ofOverlayL3Ep = epL3.getAugmentation(OfOverlayL3Context.class);
169             if (ofOverlayL3Ep != null && ofOverlayL3Ep.getPortName() != null) {
170                 epsByPortName.put(ofOverlayL3Ep.getPortName(), epL3);
171             }
172         }
173         return epsByPortName;
174     }
175
176     private Name getPortName(FlowCapableNodeConnector fcnc) {
177         if (fcnc == null || fcnc.getName() == null) {
178             return null;
179         }
180         return new Name(fcnc.getName());
181     }
182
183     /**
184      * @return {@code true} if data (Endpoint) was put to the transaction; {@code false} otherwise
185      */
186     private boolean updateEpWithNodeConnectorInfo(Endpoint epWithOfOverlayAug, InstanceIdentifier<NodeConnector> ncIid,
187             WriteTransaction tx) {
188         if (epWithOfOverlayAug == null) {
189             return false;
190         }
191         OfOverlayContext oldOfOverlayAug = epWithOfOverlayAug.getAugmentation(OfOverlayContext.class);
192         OfOverlayContextBuilder newOfOverlayAug = new OfOverlayContextBuilder(oldOfOverlayAug);
193         if (ncIid == null && oldOfOverlayAug.getNodeConnectorId() == null) {
194             return false;
195         }
196         if (ncIid != null) {
197             NodeConnectorId ncId = ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
198             if (ncId.equals(oldOfOverlayAug.getNodeConnectorId())) {
199                 return false;
200             }
201             NodeId nodeId = ncIid.firstKeyOf(Node.class, NodeKey.class).getId();
202             newOfOverlayAug.setNodeId(nodeId);
203             newOfOverlayAug.setNodeConnectorId(ncId);
204         } else {
205             //when nodeId is null, remove info about that node from endpoint
206             newOfOverlayAug.setNodeId(null);
207             newOfOverlayAug.setNodeConnectorId(null);
208         }
209         InstanceIdentifier<OfOverlayContext> epOfOverlayAugIid = InstanceIdentifier.builder(Endpoints.class)
210             .child(Endpoint.class, epWithOfOverlayAug.getKey())
211             .augmentation(OfOverlayContext.class)
212             .build();
213         tx.put(LogicalDatastoreType.OPERATIONAL, epOfOverlayAugIid, newOfOverlayAug.build());
214         return true;
215     }
216
217     /**
218      * @return {@code true} if data (EndpointL3) was put to the transaction; {@code false} otherwise
219      */
220     private boolean updateL3EpWithNodeConnectorInfo(EndpointL3 epWithOfOverlayL3Aug,
221             InstanceIdentifier<NodeConnector> ncIid, WriteTransaction tx) {
222         if (epWithOfOverlayL3Aug == null) {
223             return false;
224         }
225         OfOverlayL3Context oldOfOverlayL3Aug = epWithOfOverlayL3Aug.getAugmentation(OfOverlayL3Context.class);
226         OfOverlayL3ContextBuilder newOfOverlayL3Aug = new OfOverlayL3ContextBuilder(oldOfOverlayL3Aug);
227         if (ncIid == null && oldOfOverlayL3Aug.getNodeConnectorId() == null) {
228             return false;
229         }
230         if (ncIid != null) {
231             NodeConnectorId ncId = ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
232             if (ncId.equals(oldOfOverlayL3Aug.getNodeConnectorId())) {
233                 return false;
234             }
235             NodeId nodeId = ncIid.firstKeyOf(Node.class, NodeKey.class).getId();
236             newOfOverlayL3Aug.setNodeId(nodeId);
237             newOfOverlayL3Aug.setNodeConnectorId(ncId);
238         } else {
239             // remove node info
240             newOfOverlayL3Aug.setNodeId(null);
241             newOfOverlayL3Aug.setNodeConnectorId(null);
242         }
243         InstanceIdentifier<OfOverlayL3Context> epOfOverlayAugIid = InstanceIdentifier.builder(Endpoints.class)
244                 .child(EndpointL3.class, epWithOfOverlayL3Aug.getKey())
245                 .augmentation(OfOverlayL3Context.class)
246                 .build();
247         tx.put(LogicalDatastoreType.OPERATIONAL, epOfOverlayAugIid, newOfOverlayL3Aug.build());
248         return true;
249     }
250
251     @Override
252     public void close() throws Exception {
253         if (listenerRegistration != null) {
254             listenerRegistration.close();
255         }
256     }
257 }