2 * Copyright (c) 2015 Ericsson India Global Services Pvt Ltd. 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.vpnservice.interfacemgr;
10 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.IfL2vlan;
12 import java.util.ArrayList;
13 import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
14 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
15 import java.util.List;
16 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.IfL3tunnel;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.L3tunnel;
19 import java.util.concurrent.ConcurrentHashMap;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
22 import com.google.common.util.concurrent.Futures;
23 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
25 import com.google.common.util.concurrent.FutureCallback;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.BaseIds;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
34 import org.opendaylight.vpnservice.AbstractDataChangeListener;
35 import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
36 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
37 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
38 import org.opendaylight.yangtools.concepts.ListenerRegistration;
39 import org.opendaylight.yangtools.yang.binding.DataObject;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
42 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
43 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
44 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50 import com.google.common.base.Optional;
52 public class InterfaceManager extends AbstractDataChangeListener<Interface> implements AutoCloseable {
53 private static final Logger LOG = LoggerFactory.getLogger(InterfaceManager.class);
54 private ListenerRegistration<DataChangeListener> listenerRegistration;
55 private final DataBroker broker;
56 private final Map<NodeConnectorId, String> mapNcToInterfaceName = new ConcurrentHashMap<>();
57 private final Map<NodeId, String> dbDpnEndpoints = new ConcurrentHashMap<>();
59 private static final FutureCallback<Void> DEFAULT_CALLBACK =
60 new FutureCallback<Void>() {
61 public void onSuccess(Void result) {
62 LOG.debug("Success in Datastore write operation");
65 public void onFailure(Throwable error) {
66 LOG.error("Error in Datastore write operation", error);
70 public InterfaceManager(final DataBroker db) {
71 super(Interface.class);
77 public void close() throws Exception {
78 if (listenerRegistration != null) {
80 listenerRegistration.close();
81 } catch (final Exception e) {
82 LOG.error("Error when cleaning up DataChangeListener.", e);
84 listenerRegistration = null;
86 LOG.info("Interface Manager Closed");
89 private void registerListener(final DataBroker db) {
91 listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
92 getWildCardPath(), InterfaceManager.this, DataChangeScope.SUBTREE);
93 } catch (final Exception e) {
94 LOG.error("InterfaceManager DataChange listener registration fail!", e);
95 throw new IllegalStateException("InterfaceManager registration Listener failed.", e);
100 protected void add(final InstanceIdentifier<Interface> identifier,
101 final Interface imgrInterface) {
102 LOG.trace("Adding interface key: " + identifier + ", value=" + imgrInterface );
103 addInterface(identifier, imgrInterface);
106 private InstanceIdentifier<Interface> buildId(final InstanceIdentifier<Interface> identifier) {
107 //TODO Make this generic and move to AbstractDataChangeListener or Utils.
108 final InterfaceKey key = identifier.firstKeyOf(Interface.class, InterfaceKey.class);
109 return buildId(key.getName());
112 private InstanceIdentifier<Interface> buildId(String interfaceName) {
113 //TODO Make this generic and move to AbstractDataChangeListener or Utils.
114 InstanceIdentifierBuilder<Interface> idBuilder =
115 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
116 InstanceIdentifier<Interface> id = idBuilder.build();
120 private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> buildStateInterfaceId(String interfaceName) {
121 //TODO Make this generic and move to AbstractDataChangeListener or Utils.
122 InstanceIdentifierBuilder<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> idBuilder =
123 InstanceIdentifier.builder(InterfacesState.class)
124 .child(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.class,
125 new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey(interfaceName));
126 InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> id = idBuilder.build();
130 private void addInterface(final InstanceIdentifier<Interface> identifier,
131 final Interface interf) {
132 NodeConnector nodeConn = getNodeConnectorFromDataStore(interf);
133 NodeConnectorId ncId = null;
134 updateInterfaceState(identifier, interf, nodeConn);
135 if (nodeConn == null) {
136 ncId = getNodeConnectorIdFromInterface(interf);
138 ncId = nodeConn.getId();
140 mapNcToInterfaceName.put(ncId, interf.getName());
141 if(interf.getType().getClass().isInstance(L3tunnel.class)) {
142 NodeId nodeId = getNodeIdFromNodeConnectorId(ncId);
143 IfL3tunnel l3Tunnel = interf.getAugmentation(IfL3tunnel.class);
144 this.dbDpnEndpoints.put(nodeId, l3Tunnel.getLocalIp().getIpv4Address().getValue());
148 private void updateInterfaceState(InstanceIdentifier<Interface> identifier,
149 Interface interf, NodeConnector nodeConn) {
150 /* Update InterfaceState
151 * 1. Get interfaces-state Identifier
152 * 2. Add interface to interfaces-state/interface
153 * 3. Get interface-id from id manager
154 * 4. Update interface-state with following:
155 * admin-status = set to enable value
156 * oper-status = Down [?]
157 * if-index = interface-id
159 InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> id =
160 buildStateInterfaceId(interf.getName());
161 Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> stateIf =
162 read(LogicalDatastoreType.OPERATIONAL, id);
163 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface stateIface;
164 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder ifaceBuilder =
165 new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder();
166 if(!stateIf.isPresent()) {
167 // TODO: Get interface-id from IdManager
168 ifaceBuilder.setAdminStatus((interf.isEnabled()) ? org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.AdminStatus.Up :
169 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.AdminStatus.Down);
170 ifaceBuilder.setOperStatus(getOperStatus(nodeConn));
172 ifaceBuilder.setIfIndex(200).setName(interf.getName()).setType(interf.getType());
173 ifaceBuilder.setKey(getStateInterfaceKeyFromName(interf.getName()));
174 //ifaceBuilder.setStatistics(createStatistics(interf.getName(), nodeConn));
175 stateIface = ifaceBuilder.build();
176 LOG.trace("Adding stateIface {} and id {} to OPERATIONAL DS", stateIface, id);
177 asyncWrite(LogicalDatastoreType.OPERATIONAL, id, stateIface, DEFAULT_CALLBACK);
179 if(interf.isEnabled() != null) {
180 ifaceBuilder.setAdminStatus((interf.isEnabled()) ? org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.AdminStatus.Up :
181 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.AdminStatus.Down);
183 if(interf.getType() != null) {
184 ifaceBuilder.setType(interf.getType());
186 ifaceBuilder.setOperStatus(getOperStatus(nodeConn));
187 stateIface = ifaceBuilder.build();
188 LOG.trace("updating OPERATIONAL data store with stateIface {} and id {}", stateIface, id);
189 asyncUpdate(LogicalDatastoreType.OPERATIONAL, id, stateIface, DEFAULT_CALLBACK);
194 private void setAugmentations(
195 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder ifaceBuilder,
196 InstanceIdentifier<Interface> identifier, Interface interf) {
197 // TODO Add code for all augmentations
198 InstanceIdentifier<IfL3tunnel> ifL3TunnelPath = identifier.augmentation(IfL3tunnel.class);
199 Optional<IfL3tunnel> l3Tunnel = read(LogicalDatastoreType.CONFIGURATION, ifL3TunnelPath);
200 String ifName = interf.getName();
201 if(l3Tunnel.isPresent()) {
207 private OperStatus getOperStatus(NodeConnector nodeConn) {
208 LOG.trace("nodeConn is {}", nodeConn);
209 if(nodeConn == null) {
210 return OperStatus.Down;
212 return OperStatus.Up;
216 private org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey getStateInterfaceKeyFromName(
218 return new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey(name);
221 private NodeConnector getNodeConnectorFromDataStore(Interface interf) {
222 NodeConnectorId ncId = interf.getAugmentation(BaseIds.class).getOfPortId();
223 //TODO: Replace with MDSAL Util method
224 NodeId nodeId = getNodeIdFromNodeConnectorId(ncId);
225 InstanceIdentifier<NodeConnector> ncIdentifier = InstanceIdentifier.builder(Nodes.class)
226 .child(Node.class, new NodeKey(nodeId))
227 .child(NodeConnector.class, new NodeConnectorKey(ncId)).build();
229 Optional<NodeConnector> nc = read(LogicalDatastoreType.OPERATIONAL, ncIdentifier);
231 NodeConnector nodeConn = nc.get();
232 LOG.trace("nodeConnector: {}",nodeConn);
238 private NodeConnectorId getNodeConnectorIdFromInterface(Interface interf) {
239 return interf.getAugmentation(BaseIds.class).getOfPortId();
242 private void delInterface(final InstanceIdentifier<Interface> identifier,
243 final Interface delInterface) {
244 InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> id =
245 buildStateInterfaceId(delInterface.getName());
246 Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> stateIf =
247 read(LogicalDatastoreType.OPERATIONAL, id);
248 if(stateIf.isPresent()) {
249 LOG.trace("deleting interfaces:state OPERATIONAL data store with id {}", id);
250 asyncRemove(LogicalDatastoreType.OPERATIONAL, id, DEFAULT_CALLBACK);
251 NodeConnectorId ncId = getNodeConnectorIdFromInterface(delInterface);
253 mapNcToInterfaceName.remove(ncId);
254 if(delInterface.getType().getClass().isInstance(L3tunnel.class)) {
255 Node node = getNodeFromDataStore(delInterface);
256 if(node.getNodeConnector().isEmpty()) {
257 this.dbDpnEndpoints.remove(node.getId());
264 private Node getNodeFromDataStore(Interface interf) {
265 NodeConnectorId ncId = interf.getAugmentation(BaseIds.class).getOfPortId();
266 //TODO: Replace with MDSAL Util method
267 NodeId nodeId = getNodeIdFromNodeConnectorId(ncId);
268 InstanceIdentifier<Node> ncIdentifier = InstanceIdentifier.builder(Nodes.class)
269 .child(Node.class, new NodeKey(nodeId)).build();
271 Optional<Node> dpn = read(LogicalDatastoreType.OPERATIONAL, ncIdentifier);
272 if(dpn.isPresent()) {
273 Node node = dpn.get();
274 LOG.trace("node: {}",node);
280 private void updateInterface(final InstanceIdentifier<Interface> identifier,
281 final Interface original, final Interface update) {
282 InstanceIdentifier<Interface> id = buildId(identifier);
283 Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
284 if(port.isPresent()) {
285 Interface interf = port.get();
286 NodeConnector nc = getNodeConnectorFromDataStore(update);
287 updateInterfaceState(identifier, update, nc);
289 * Alternative is to get from interf and update map irrespective if NCID changed or not.
292 // Name doesn't change. Is it present in update?
293 mapNcToInterfaceName.put(nc.getId(), original.getName());
294 if(interf.getType().getClass().isInstance(L3tunnel.class)) {
295 NodeId nodeId = getNodeIdFromNodeConnectorId(nc.getId());
296 IfL3tunnel l3Tunnel = interf.getAugmentation(IfL3tunnel.class);
297 this.dbDpnEndpoints.put(nodeId, l3Tunnel.getLocalIp().getIpv4Address().getValue());
303 private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
304 InstanceIdentifier<T> path) {
306 ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
308 Optional<T> result = Optional.absent();
310 result = tx.read(datastoreType, path).get();
311 } catch (Exception e) {
312 throw new RuntimeException(e);
318 private InstanceIdentifier<Interface> getWildCardPath() {
319 return InstanceIdentifier.create(Interfaces.class).child(Interface.class);
323 protected void remove(InstanceIdentifier<Interface> identifier, Interface del) {
324 LOG.trace("remove - key: " + identifier + ", value=" + del );
325 delInterface(identifier, del);
329 protected void update(InstanceIdentifier<Interface> identifier, Interface original, Interface update) {
330 LOG.trace("update - key: " + identifier + ", original=" + original + ", update=" + update );
331 updateInterface(identifier, original, update);
334 private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
335 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
336 WriteTransaction tx = broker.newWriteOnlyTransaction();
337 tx.put(datastoreType, path, data, true);
338 Futures.addCallback(tx.submit(), callback);
341 private <T extends DataObject> void asyncUpdate(LogicalDatastoreType datastoreType,
342 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
343 WriteTransaction tx = broker.newWriteOnlyTransaction();
344 tx.merge(datastoreType, path, data, true);
345 Futures.addCallback(tx.submit(), callback);
348 private <T extends DataObject> void asyncRemove(LogicalDatastoreType datastoreType,
349 InstanceIdentifier<T> path, FutureCallback<Void> callback) {
350 WriteTransaction tx = broker.newWriteOnlyTransaction();
351 tx.delete(datastoreType, path);
352 Futures.addCallback(tx.submit(), callback);
355 void processPortAdd(NodeConnector port) {
356 NodeConnectorId portId = port.getId();
357 FlowCapableNodeConnector ofPort = port.getAugmentation(FlowCapableNodeConnector.class);
358 LOG.debug("PortAdd: PortId { "+portId.getValue()+"} PortName {"+ofPort.getName()+"}");
359 String ifName = this.mapNcToInterfaceName.get(portId);
360 setInterfaceOperStatus(ifName, OperStatus.Up);
363 void processPortUpdate(NodeConnector oldPort, NodeConnector update) {
364 //TODO: Currently nothing to do here.
365 LOG.trace("ifMap: {}, dpnMap: {}", mapNcToInterfaceName, dbDpnEndpoints);
368 void processPortDelete(NodeConnector port) {
369 NodeConnectorId portId = port.getId();
370 FlowCapableNodeConnector ofPort = port.getAugmentation(FlowCapableNodeConnector.class);
371 LOG.debug("PortDelete: PortId { "+portId.getValue()+"} PortName {"+ofPort.getName()+"}");
372 String ifName = this.mapNcToInterfaceName.get(portId);
373 setInterfaceOperStatus(ifName, OperStatus.Down);
376 private void setInterfaceOperStatus(String ifName, OperStatus opStatus) {
377 if (ifName != null) {
378 InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> id =
379 buildStateInterfaceId(ifName);
380 Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> stateIf =
381 read(LogicalDatastoreType.OPERATIONAL, id);
382 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface stateIface;
383 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder ifaceBuilder =
384 new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder();
385 if (stateIf.isPresent()) {
386 stateIface = ifaceBuilder.setOperStatus(opStatus).build();
387 LOG.trace("Setting OperStatus for {} to {} in OPERATIONAL DS", ifName, opStatus);
388 asyncUpdate(LogicalDatastoreType.OPERATIONAL, id, stateIface, DEFAULT_CALLBACK);
393 private Interface getInterfaceByIfName(String ifName) {
394 InstanceIdentifier<Interface> id = buildId(ifName);
395 Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
396 if(port.isPresent()) {
402 Long getPortForInterface(String ifName) {
403 Interface iface = getInterfaceByIfName(ifName);
404 return getPortNumForInterface(iface);
407 long getDpnForInterface(String ifName) {
408 Interface iface = getInterfaceByIfName(ifName);
410 NodeConnector port = getNodeConnectorFromDataStore(iface);
411 //TODO: This should be an MDSAL Util method
412 return Long.parseLong(getDpnFromNodeConnectorId(port.getId()));
413 } catch (NullPointerException e) {
414 LOG.error("OFPort for Interface {} not found", ifName);
419 String getEndpointIpForDpn(long dpnId) {
420 //TODO: This should be MDSAL Util function
421 NodeId dpnNodeId = buildDpnNodeId(dpnId);
422 return dbDpnEndpoints.get(dpnNodeId);
425 List<MatchInfo> getInterfaceIngressRule(String ifName){
426 Interface iface = getInterfaceByIfName(ifName);
427 List<MatchInfo> matches = new ArrayList<MatchInfo>();
428 Class<? extends Class> ifType = iface.getType().getClass();
429 long dpn = this.getDpnForInterface(ifName);
430 long portNo = this.getPortNumForInterface(iface).longValue();
431 matches.add(new MatchInfo(MatchFieldType.in_port, new long[] {dpn, portNo}));
432 if(ifType.isInstance(L2vlan.class)) {
433 IfL2vlan vlanIface = iface.getAugmentation(IfL2vlan.class);
434 matches.add(new MatchInfo(MatchFieldType.vlan_vid,
435 new long[] {vlanIface.getVlanId().longValue()}));
437 } else if (ifType.isInstance(L3tunnel.class)) {
438 //TODO: Handle different tunnel types
444 private String getDpnFromNodeConnectorId(NodeConnectorId portId) {
446 * NodeConnectorId is of form 'openflow:dpnid:portnum'
448 String[] split = portId.getValue().split(":");
452 private NodeId buildDpnNodeId(long dpnId) {
453 // TODO Auto-generated method stub
454 return new NodeId("openflow:" + dpnId);
457 private NodeId getNodeIdFromNodeConnectorId(NodeConnectorId ncId) {
458 return new NodeId(ncId.getValue().substring(0,ncId.getValue().lastIndexOf(":")));
461 private Long getPortNumForInterface(Interface iface) {
463 NodeConnector port = getNodeConnectorFromDataStore(iface);
464 FlowCapableNodeConnector ofPort = port.getAugmentation(FlowCapableNodeConnector.class);
465 return ofPort.getPortNumber().getUint32();
466 } catch (Exception e) {
467 LOG.error("OFPort for Interface {} not found", iface.getName());