Interfacemgr: Handle port add/delete events
[vpnservice.git] / interfacemgr / interfacemgr-impl / src / main / java / org / opendaylight / vpnservice / interfacemgr / InterfaceManager.java
1 /*
2  * Copyright (c) 2015 Ericsson India Global Services Pvt Ltd. 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 package org.opendaylight.vpnservice.interfacemgr;
9
10 import java.util.Map;
11 import java.util.concurrent.ConcurrentHashMap;
12 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
13 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
14 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.IfL3tunnel;
15 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
16 import java.math.BigInteger;
17 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Counter32;
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Counter64;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state._interface.StatisticsBuilder;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state._interface.Statistics;
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.controller.md.sal.binding.api.DataBroker;
36 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
37 import org.opendaylight.yangtools.concepts.ListenerRegistration;
38 import org.opendaylight.yangtools.yang.binding.DataObject;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
41 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
42 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
43 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49 import com.google.common.base.Optional;
50
51 public class InterfaceManager extends AbstractDataChangeListener<Interface> implements AutoCloseable{
52     private static final Logger LOG = LoggerFactory.getLogger(InterfaceManager.class);
53     private ListenerRegistration<DataChangeListener> listenerRegistration;
54     private final DataBroker broker;
55     private final Map<NodeConnectorId, String> mapNcToInterfaceName = new ConcurrentHashMap<>();
56
57     private static final FutureCallback<Void> DEFAULT_CALLBACK =
58                     new FutureCallback<Void>() {
59                         public void onSuccess(Void result) {
60                             LOG.debug("Success in Datastore write operation");
61                         }
62
63                         public void onFailure(Throwable error) {
64                             LOG.error("Error in Datastore write operation", error);
65                         };
66                     };
67
68     public InterfaceManager(final DataBroker db) {
69         super(Interface.class);
70         broker = db;
71         registerListener(db);
72     }
73
74     @Override
75     public void close() throws Exception {
76         if (listenerRegistration != null) {
77             try {
78                 listenerRegistration.close();
79             } catch (final Exception e) {
80                 LOG.error("Error when cleaning up DataChangeListener.", e);
81             }
82             listenerRegistration = null;
83         }
84         LOG.info("Interface Manager Closed");
85     }
86
87     private void registerListener(final DataBroker db) {
88         try {
89             listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
90                     getWildCardPath(), InterfaceManager.this, DataChangeScope.SUBTREE);
91         } catch (final Exception e) {
92             LOG.error("InterfaceManager DataChange listener registration fail!", e);
93             throw new IllegalStateException("InterfaceManager registration Listener failed.", e);
94         }
95     }
96
97     @Override
98     protected void add(final InstanceIdentifier<Interface> identifier,
99             final Interface imgrInterface) {
100         LOG.trace("key: " + identifier + ", value=" + imgrInterface );
101         addInterface(identifier, imgrInterface);
102     }
103
104     private InstanceIdentifier<Interface> buildId(final InstanceIdentifier<Interface> identifier) {
105         //TODO Make this generic and move to AbstractDataChangeListener or Utils.
106         final InterfaceKey key = identifier.firstKeyOf(Interface.class, InterfaceKey.class);
107         return buildId(key.getName());
108     }
109
110     private InstanceIdentifier<Interface> buildId(String interfaceName) {
111         //TODO Make this generic and move to AbstractDataChangeListener or Utils.
112         InstanceIdentifierBuilder<Interface> idBuilder =
113                 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
114         InstanceIdentifier<Interface> id = idBuilder.build();
115         return id;
116     }
117
118     private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> buildStateInterfaceId(String interfaceName) {
119         //TODO Make this generic and move to AbstractDataChangeListener or Utils.
120         InstanceIdentifierBuilder<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> idBuilder =
121                 InstanceIdentifier.builder(InterfacesState.class)
122                 .child(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.class,
123                                 new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey(interfaceName));
124         InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> id = idBuilder.build();
125         return id;
126     }
127
128     private void addInterface(final InstanceIdentifier<Interface> identifier,
129                               final Interface imgrInterface) {
130         InstanceIdentifier<Interface> id = buildId(identifier);
131         Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
132         if(port.isPresent()) {
133             Interface interf = port.get();
134             NodeConnector nodeConn = getNodeConnectorFromDataStore(interf);
135             updateInterfaceState(identifier, interf, nodeConn);
136             if(nodeConn == null) {
137                 mapNcToInterfaceName.put(this.getNodeConnectorIdFromInterface(interf) , interf.getName());
138             } else {
139                 mapNcToInterfaceName.put(nodeConn.getId(), interf.getName());
140             }
141             /* TODO:
142              *  1. Get interface-id from id manager
143              *  2. Update interface-state with following:
144              *    admin-status = set to enable value
145              *    oper-status = Down [?]
146              *    if-index = interface-id
147              * FIXME:
148              *  1. Get operational data from node-connector-id?
149              *
150              */
151         }
152     }
153
154     private void updateInterfaceState(InstanceIdentifier<Interface> identifier,
155                     Interface interf, NodeConnector nodeConn) {
156         /* Update InterfaceState
157          * 1. Get interfaces-state Identifier
158          * 2. Add interface to interfaces-state/interface
159          * 3. Get interface-id from id manager
160          * 4. Update interface-state with following:
161          *    admin-status = set to enable value
162          *    oper-status = Down [?]
163          *    if-index = interface-id
164         */
165         InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> id =
166                         buildStateInterfaceId(interf.getName());
167         Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> stateIf =
168                         read(LogicalDatastoreType.OPERATIONAL, id);
169         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface stateIface;
170         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder ifaceBuilder =
171                         new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder();
172         if(!stateIf.isPresent()) {
173             // TODO: Get interface-id from IdManager
174             ifaceBuilder.setAdminStatus((interf.isEnabled()) ?  org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.AdminStatus.Up :
175                 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.AdminStatus.Down);
176             ifaceBuilder.setOperStatus(getOperStatus(nodeConn));
177
178             ifaceBuilder.setIfIndex(200).setName(interf.getName()).setType(interf.getType());
179             ifaceBuilder.setKey(getStateInterfaceKeyFromName(interf.getName()));
180             //ifaceBuilder.setStatistics(createStatistics(interf.getName(), nodeConn));
181             stateIface = ifaceBuilder.build();
182             LOG.trace("Adding stateIface {} and id {} to OPERATIONAL DS", stateIface, id);
183             asyncWrite(LogicalDatastoreType.OPERATIONAL, id, stateIface, DEFAULT_CALLBACK);
184         } else {
185             if(interf.isEnabled() != null) {
186                 ifaceBuilder.setAdminStatus((interf.isEnabled()) ?  org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.AdminStatus.Up :
187                     org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.AdminStatus.Down);
188             }
189             if(interf.getType() != null) {
190                 ifaceBuilder.setType(interf.getType());
191             }
192             ifaceBuilder.setOperStatus(getOperStatus(nodeConn));
193             stateIface = ifaceBuilder.build();
194             LOG.trace("updating OPERATIONAL data store with stateIface {} and id {}", stateIface, id);
195             asyncUpdate(LogicalDatastoreType.OPERATIONAL, id, stateIface, DEFAULT_CALLBACK);
196         }
197     }
198
199     /*
200     private void setAugmentations(
201                     org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder ifaceBuilder,
202                     InstanceIdentifier<Interface> identifier, Interface interf) {
203         // TODO Add code for all augmentations
204         InstanceIdentifier<IfL3tunnel> ifL3TunnelPath = identifier.augmentation(IfL3tunnel.class);
205         Optional<IfL3tunnel> l3Tunnel = read(LogicalDatastoreType.CONFIGURATION, ifL3TunnelPath);
206         String ifName = interf.getName();
207         if(l3Tunnel.isPresent()) {
208             l3Tunnel.get();
209         }
210     }
211     */
212
213     private OperStatus getOperStatus(NodeConnector nodeConn) {
214         LOG.trace("nodeConn is {}", nodeConn);
215         if(nodeConn == null) {
216             return OperStatus.Down;
217         }else {
218             return OperStatus.Up;
219         }
220     }
221
222     private Statistics createStatistics(String name, NodeConnector nodeConn) {
223         Counter64 init64 = new Counter64(new BigInteger("0000000000000000"));
224         Counter32 init32 = new Counter32((long) 0);
225         StatisticsBuilder statBuilder = new StatisticsBuilder();
226         statBuilder.setDiscontinuityTime(new DateAndTime("2015-04-04T00:00:00Z"))
227         .setInBroadcastPkts(init64).setInDiscards(init32).setInErrors(init32).setInMulticastPkts(init64)
228         .setInOctets(init64).setInUnicastPkts(init64).setInUnknownProtos(init32).setOutBroadcastPkts(init64)
229         .setOutDiscards(init32).setOutErrors(init32).setOutMulticastPkts(init64).setOutOctets(init64)
230         .setOutUnicastPkts(init64);
231         return statBuilder.build();
232     }
233
234     private org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey getStateInterfaceKeyFromName(
235                     String name) {
236         return new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey(name);
237     }
238
239     private NodeConnector getNodeConnectorFromDataStore(Interface interf) {
240         NodeConnectorId ncId = interf.getAugmentation(BaseIds.class).getOfPortId();
241         //TODO: Replace with MDSAL Util method
242         NodeId nodeId = new NodeId(ncId.getValue().substring(0,ncId.getValue().lastIndexOf(":")));
243         InstanceIdentifier<NodeConnector> ncIdentifier = InstanceIdentifier.builder(Nodes.class)
244                         .child(Node.class, new NodeKey(nodeId))
245                         .child(NodeConnector.class, new NodeConnectorKey(ncId)).build();
246
247         Optional<NodeConnector> nc = read(LogicalDatastoreType.OPERATIONAL, ncIdentifier);
248         if(nc.isPresent()) {
249             NodeConnector nodeConn = nc.get();
250             LOG.trace("nodeConnector: {}",nodeConn);
251             return nodeConn;
252         }
253         return null;
254     }
255
256     private NodeConnectorId getNodeConnectorIdFromInterface(Interface interf) {
257         return interf.getAugmentation(BaseIds.class).getOfPortId();
258     }
259
260     private void delInterface(final InstanceIdentifier<Interface> identifier,
261                               final Interface delInterface) {
262         InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> id =
263                         buildStateInterfaceId(delInterface.getName());
264         Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> stateIf =
265                         read(LogicalDatastoreType.OPERATIONAL, id);
266         if(stateIf.isPresent()) {
267             LOG.trace("deleting interfaces:state OPERATIONAL data store with id {}", id);
268             asyncRemove(LogicalDatastoreType.OPERATIONAL, id, DEFAULT_CALLBACK);
269             NodeConnectorId ncId = getNodeConnectorIdFromInterface(delInterface);
270             if(ncId != null) {
271                 mapNcToInterfaceName.remove(ncId);
272             }
273         }
274     }
275
276     private void updateInterface(final InstanceIdentifier<Interface> identifier,
277                               final Interface original, final Interface update) {
278         InstanceIdentifier<Interface> id = buildId(identifier);
279         Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
280         if(port.isPresent()) {
281             Interface interf = port.get();
282             NodeConnector nc = getNodeConnectorFromDataStore(update);
283             updateInterfaceState(identifier, update, nc);
284             /*
285              * Alternative is to get from interf and update map irrespective if NCID changed or not.
286              */
287             if(nc != null) {
288                 // Name doesn't change. Is it present in update?
289                 mapNcToInterfaceName.put(nc.getId(), original.getName());
290             }
291             //TODO: Update operational data
292         }
293     }
294
295     private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
296             InstanceIdentifier<T> path) {
297
298         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
299
300         Optional<T> result = Optional.absent();
301         try {
302             result = tx.read(datastoreType, path).get();
303         } catch (Exception e) {
304             throw new RuntimeException(e);
305         }
306
307         return result;
308     }
309
310     private InstanceIdentifier<Interface> getWildCardPath() {
311         return InstanceIdentifier.create(Interfaces.class).child(Interface.class);
312     }
313
314     @Override
315     protected void remove(InstanceIdentifier<Interface> identifier, Interface del) {
316         LOG.trace("remove - key: " + identifier + ", value=" + del );
317         delInterface(identifier, del);
318     }
319
320     @Override
321     protected void update(InstanceIdentifier<Interface> identifier, Interface original, Interface update) {
322         LOG.trace("update - key: " + identifier + ", original=" + original + ", update=" + update );
323         updateInterface(identifier, original, update);
324     }
325
326     private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
327                     InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
328     WriteTransaction tx = broker.newWriteOnlyTransaction();
329     tx.put(datastoreType, path, data, true);
330     Futures.addCallback(tx.submit(), callback);
331     }
332
333     private <T extends DataObject> void asyncUpdate(LogicalDatastoreType datastoreType,
334                     InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
335     WriteTransaction tx = broker.newWriteOnlyTransaction();
336     tx.merge(datastoreType, path, data, true);
337     Futures.addCallback(tx.submit(), callback);
338     }
339
340     private <T extends DataObject> void asyncRemove(LogicalDatastoreType datastoreType,
341                     InstanceIdentifier<T> path, FutureCallback<Void> callback) {
342     WriteTransaction tx = broker.newWriteOnlyTransaction();
343     tx.delete(datastoreType, path);
344     Futures.addCallback(tx.submit(), callback);
345     }
346
347     void processPortAdd(NodeConnector port) {
348         NodeConnectorId portId = port.getId();
349         FlowCapableNodeConnector ofPort = port.getAugmentation(FlowCapableNodeConnector.class);
350         LOG.debug("PortAdd: PortId { "+portId.getValue()+"} PortName {"+ofPort.getName()+"}");
351         String ifName = this.mapNcToInterfaceName.get(portId);
352         setInterfaceOperStatus(ifName, OperStatus.Up);
353     }
354
355     void processPortUpdate(NodeConnector oldPort, NodeConnector update) {
356         //TODO: Currently nothing to do here.
357         //LOG.trace("map: {}", this.mapNcToInterfaceName);
358     }
359
360     void processPortDelete(NodeConnector port) {
361         NodeConnectorId portId = port.getId();
362         FlowCapableNodeConnector ofPort = port.getAugmentation(FlowCapableNodeConnector.class);
363         LOG.debug("PortDelete: PortId { "+portId.getValue()+"} PortName {"+ofPort.getName()+"}");
364         String ifName = this.mapNcToInterfaceName.get(portId);
365         setInterfaceOperStatus(ifName, OperStatus.Down);
366     }
367
368     private void setInterfaceOperStatus(String ifName, OperStatus opStatus) {
369         if (ifName != null) {
370             InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> id =
371                             buildStateInterfaceId(ifName);
372             Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> stateIf =
373                             read(LogicalDatastoreType.OPERATIONAL, id);
374             org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface stateIface;
375             org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder ifaceBuilder =
376                             new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder();
377             if (stateIf.isPresent()) {
378                 stateIface = ifaceBuilder.setOperStatus(opStatus).build();
379                 LOG.trace("Setting OperStatus for {} to {} in OPERATIONAL DS", ifName, opStatus);
380                 asyncUpdate(LogicalDatastoreType.OPERATIONAL, id, stateIface, DEFAULT_CALLBACK);
381             }
382         }
383     }
384
385 }