Changes based on fix of Bug 5137 - Neutron routes
[groupbasedpolicy.git] / neutron-ovsdb / src / main / java / org / opendaylight / groupbasedpolicy / neutron / ovsdb / NodeDataChangeListener.java
1 /*
2  * Copyright (c) 2015 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.neutron.ovsdb;
10
11 import static com.google.common.base.Preconditions.checkNotNull;
12 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.addOfOverlayExternalPort;
13 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper.getLongFromDpid;
14 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.NeutronOvsdbIidFactory.ovsdbNodeAugmentationIid;
15 import static org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.OvsdbHelper.getNodeFromBridgeRef;
16
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Map.Entry;
21
22 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
23 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
24 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
26 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.ExternalInterfaces;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ManagedNodeEntry;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchOtherConfigs;
37 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
38 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
39 import org.opendaylight.yangtools.concepts.ListenerRegistration;
40 import org.opendaylight.yangtools.yang.binding.DataObject;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 import com.google.common.base.Strings;
46
47 import static org.opendaylight.groupbasedpolicy.util.DataStoreHelper.removeIfExists;
48 import static org.opendaylight.groupbasedpolicy.util.DataStoreHelper.submitToDs;
49
50 public class NodeDataChangeListener implements DataChangeListener, AutoCloseable {
51
52     private static final Logger LOG = LoggerFactory.getLogger(NodeDataChangeListener.class);
53     private static final String NEUTRON_PROVIDER_MAPPINGS_KEY = "provider_mappings";
54     private static final String INVENTORY_PREFIX = "openflow:";
55     private final ListenerRegistration<DataChangeListener> registration;
56     private static DataBroker dataBroker;
57
58     public NodeDataChangeListener(DataBroker dataBroker) {
59         this.dataBroker = checkNotNull(dataBroker);
60         registration = dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, ovsdbNodeAugmentationIid(SouthboundConstants.OVSDB_TOPOLOGY_ID), this,
61                 DataChangeScope.ONE);
62         LOG.trace("NodeDataChangeListener started");
63     }
64
65     /*
66      * When vSwitch is deleted, we loose data in operational DS to determine Iid of
67      * corresponding ExternalInterfaces.
68      */
69     public static final Map<InstanceIdentifier<OvsdbNodeAugmentation>, InstanceIdentifier<ExternalInterfaces>> nodeIdByExtInterface = new HashMap<>();
70
71     @Override
72     @SuppressWarnings("unchecked")
73     public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
74
75         /*
76          * TerminationPoint notifications with OVSDB augmentations
77          * vSwitch ports. Iterate through the list of new ports.
78          */
79         for (Entry<InstanceIdentifier<?>, DataObject> entry : change.getCreatedData().entrySet()) {
80             if (entry.getValue() instanceof OvsdbNodeAugmentation) {
81                 OvsdbNodeAugmentation ovsdbNode = (OvsdbNodeAugmentation) entry.getValue();
82                 InstanceIdentifier<OvsdbNodeAugmentation> key = (InstanceIdentifier<OvsdbNodeAugmentation>) entry.getKey();
83                 InstanceIdentifier<ExternalInterfaces> extInterfacesIid = processNodeNotification(ovsdbNode);
84                 if (extInterfacesIid != null) {
85                     nodeIdByExtInterface.put(key, extInterfacesIid);
86                 }
87             }
88         }
89
90         /*
91          * Updates
92          */
93         for (Entry<InstanceIdentifier<?>, DataObject> entry : change.getUpdatedData().entrySet()) {
94             if (entry.getValue() instanceof OvsdbNodeAugmentation) {
95                 OvsdbNodeAugmentation ovsdbNode = (OvsdbNodeAugmentation) entry.getValue();
96                 if (Strings.isNullOrEmpty(getProviderMapping(ovsdbNode))) {
97                     removeExternalInterfaces((InstanceIdentifier<OvsdbNodeAugmentation>) entry.getKey());
98                 }
99             }
100         }
101
102         /*
103          * Deletions
104          */
105         for (InstanceIdentifier<?> iid : change.getRemovedPaths()) {
106             if (iid.getTargetType().equals(OvsdbNodeAugmentation.class)) {
107                 if (nodeIdByExtInterface.get(iid) != null) {
108                     removeExternalInterfaces((InstanceIdentifier<OvsdbNodeAugmentation>) iid);
109                 }
110             }
111         }
112     }
113
114     @Override
115     public void close() throws Exception {
116         registration.close();
117     }
118
119     public static InstanceIdentifier<ExternalInterfaces> processNodeNotification(OvsdbNodeAugmentation ovsdbNode) {
120         LOG.trace("Search for provider mapping on node {}", ovsdbNode);
121         String providerPortName = getProviderMapping(ovsdbNode);
122         if (providerPortName != null) {
123             LOG.trace("Found provider mapping, creating Inventory NodeId");
124             String nodeConnectorIdString = getInventoryNodeId(ovsdbNode, providerPortName);
125             if (nodeConnectorIdString != null) {
126                 LOG.trace("Adding OfOverlay External port for {}", nodeConnectorIdString);
127                 String[] elements = nodeConnectorIdString.split(":");
128                 String nodeIdString = elements[0] + ":" + elements[1];
129                 NodeConnectorId ncid = getNodeConnectorId(nodeConnectorIdString);
130                 return addOfOverlayExternalPort(new NodeId(nodeIdString), ncid, dataBroker);
131             }
132         }
133         return null;
134     }
135
136     private void removeExternalInterfaces(InstanceIdentifier<OvsdbNodeAugmentation> iidOvsdbNodeAug){
137         InstanceIdentifier<ExternalInterfaces> iidExtInterface = nodeIdByExtInterface.get(iidOvsdbNodeAug);
138         ReadWriteTransaction wTx = dataBroker.newReadWriteTransaction();
139         removeIfExists(LogicalDatastoreType.CONFIGURATION, iidExtInterface, wTx);
140         submitToDs(wTx);
141         nodeIdByExtInterface.remove(iidOvsdbNodeAug);
142     }
143
144     private static NodeConnectorId getNodeConnectorId(String nodeConnectorIdString) {
145         return new NodeConnectorId(nodeConnectorIdString);
146     }
147
148     public static String getProviderMapping(OvsdbNodeAugmentation ovsdbNode) {
149         if (ovsdbNode.getOpenvswitchOtherConfigs() != null) {
150             for (OpenvswitchOtherConfigs config : ovsdbNode.getOpenvswitchOtherConfigs()) {
151                 if (config.getOtherConfigKey() == null || config.getOtherConfigValue() == null) {
152                     continue;
153                 }
154                 if (config.getOtherConfigKey().equals(NEUTRON_PROVIDER_MAPPINGS_KEY)) {
155                     String otherConfig = config.getOtherConfigValue();
156                     if (otherConfig != null) {
157                         String[] elements = otherConfig.split(":");
158                         if (elements.length == 2) {
159                             return elements[1];
160                         }
161                     }
162                 }
163             }
164         }
165         return null;
166     }
167
168     /**
169      * Get the DPID and OpenFlow port of the bridge that owns the {@link TerminationPoint} in the
170      * provider mapping
171      *
172      * @return the DPID and OpenFlow port of the bridge that owns the {@link TerminationPoint} in
173      * the provider mapping
174      */
175     private static String getInventoryNodeId(OvsdbNodeAugmentation ovsdbNode, String externalPortName) {
176         List<ManagedNodeEntry> ovsdbNodes = ovsdbNode.getManagedNodeEntry();
177         if (ovsdbNodes == null) {
178             LOG.trace("No ManagedNodeEntry was found on {}", ovsdbNode);
179             return null;
180         }
181         for (ManagedNodeEntry managedNode : ovsdbNodes) {
182             if (managedNode.getBridgeRef() != null) {
183                 /*
184                  * Get the Node, then see if it has any TerminationPoint
185                  * augmentations. If it does, check each TerminationPoint
186                  * augmentation to see if it is the matching provider_mapping
187                  */
188                 Node node = getNodeFromBridgeRef(managedNode.getBridgeRef(), dataBroker);
189                 if (node == null) {
190                     LOG.error("Couldn't get Topology Node for {}", managedNode.getBridgeRef());
191                     return null;
192                 }
193                 OvsdbBridgeAugmentation ovsdbBridge = node.getAugmentation(OvsdbBridgeAugmentation.class);
194                 if (ovsdbBridge == null) {
195                     LOG.trace("OVSDB Node {} does not contain OvsdbBridgeAugmentation. {}", node.getKey(), node);
196                     continue;
197                 }
198                 for (TerminationPoint tp : node.getTerminationPoint()) {
199                     OvsdbTerminationPointAugmentation tpAug = tp.getAugmentation(OvsdbTerminationPointAugmentation.class);
200                     if ((tpAug == null) || (tpAug.getName() == null) || (tpAug.getOfport() == null)) {
201                         continue;
202                     }
203                     if (tpAug.getName().equals(externalPortName)) {
204                         return buildInventoryNcid(ovsdbBridge, tpAug);
205                     }
206                 }
207             }
208         }
209         return null;
210     }
211
212     private static String buildInventoryNcid(OvsdbBridgeAugmentation ovsdbBridge,
213             OvsdbTerminationPointAugmentation terminationPoint) {
214         Long macLong = getLongFromDpid(ovsdbBridge.getDatapathId().getValue());
215         return INVENTORY_PREFIX + String.valueOf(macLong) + ":" + String.valueOf(terminationPoint.getOfport());
216     }
217 }