Merge "Move a Timer (scheduler) Submit Transaction from TxChain"
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / device / DeviceContextImpl.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 package org.opendaylight.openflowplugin.impl.device;
9
10 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
11
12 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder;
13 import org.opendaylight.openflowplugin.openflow.md.util.InventoryDataServiceUtil;
14 import org.opendaylight.openflowplugin.api.openflow.md.util.OpenflowVersion;
15 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
16 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.PortReason;
24 import com.google.common.annotations.VisibleForTesting;
25 import com.google.common.base.Preconditions;
26 import com.google.common.util.concurrent.SettableFuture;
27 import io.netty.util.HashedWheelTimer;
28 import io.netty.util.Timeout;
29 import java.math.BigInteger;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.TreeMap;
35 import java.util.concurrent.atomic.AtomicLong;
36 import javax.annotation.Nonnull;
37 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
38 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
39 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
40 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
41 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
42 import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
43 import org.opendaylight.openflowplugin.api.openflow.device.MessageTranslator;
44 import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
45 import org.opendaylight.openflowplugin.api.openflow.device.TranslatorLibrary;
46 import org.opendaylight.openflowplugin.api.openflow.device.Xid;
47 import org.opendaylight.openflowplugin.api.openflow.device.exception.DeviceDataException;
48 import org.opendaylight.openflowplugin.api.openflow.device.listener.OpenflowMessageListenerFacade;
49 import org.opendaylight.openflowplugin.api.openflow.flow.registry.DeviceFlowRegistry;
50 import org.opendaylight.openflowplugin.api.openflow.md.core.SwitchConnectionDistinguisher;
51 import org.opendaylight.openflowplugin.api.openflow.md.core.TranslatorKey;
52 import org.opendaylight.openflowplugin.impl.flow.registry.DeviceFlowRegistryImpl;
53 import org.opendaylight.openflowplugin.impl.translator.PacketReceivedTranslator;
54 import org.opendaylight.openflowplugin.openflow.md.core.session.SwitchConnectionCookieOFImpl;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.Error;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowRemoved;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PacketInMessage;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PortStatusMessage;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableFeatures;
64 import org.opendaylight.yangtools.yang.binding.ChildOf;
65 import org.opendaylight.yangtools.yang.binding.DataObject;
66 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
67 import org.opendaylight.yangtools.yang.common.RpcError;
68 import org.opendaylight.yangtools.yang.common.RpcResult;
69 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72
73 /**
74  *
75  */
76 public class DeviceContextImpl implements DeviceContext {
77
78     private static final Logger LOG = LoggerFactory.getLogger(DeviceContextImpl.class);
79
80     private final ConnectionContext primaryConnectionContext;
81     private final DeviceState deviceState;
82     private final DataBroker dataBroker;
83     private final XidGenerator xidGenerator;
84     private final HashedWheelTimer hashedWheelTimer;
85     private Map<Long, RequestContext> requests = new TreeMap<>();
86
87     private final Map<SwitchConnectionDistinguisher, ConnectionContext> auxiliaryConnectionContexts;
88     private final TransactionChainManager txChainManager;
89     private TranslatorLibrary translatorLibrary;
90     private OpenflowMessageListenerFacade openflowMessageListenerFacade;
91     private DeviceFlowRegistry deviceFlowRegistry;
92     private Timeout barrierTaskTimeout;
93     private NotificationProviderService notificationService;
94
95     @VisibleForTesting
96     DeviceContextImpl(@Nonnull final ConnectionContext primaryConnectionContext,
97                       @Nonnull final DeviceState deviceState, @Nonnull final DataBroker dataBroker,
98                       @Nonnull final HashedWheelTimer hashedWheelTimer) {
99         this.primaryConnectionContext = Preconditions.checkNotNull(primaryConnectionContext);
100         this.deviceState = Preconditions.checkNotNull(deviceState);
101         this.dataBroker = Preconditions.checkNotNull(dataBroker);
102         this.hashedWheelTimer = Preconditions.checkNotNull(hashedWheelTimer);
103         xidGenerator = new XidGenerator();
104         txChainManager = new TransactionChainManager(dataBroker, hashedWheelTimer, 500L);
105         auxiliaryConnectionContexts = new HashMap<>();
106         requests = new HashMap<>();
107         deviceFlowRegistry = new DeviceFlowRegistryImpl();
108     }
109
110     /**
111      * This method is called from {@link DeviceManagerImpl} only. So we could say "posthandshake process finish"
112      * and we are able to set a scheduler for an automatic transaction submitting by time (0,5sec).
113      */
114     void submitTransaction() {
115         txChainManager.submitTransaction();
116         txChainManager.enableCounter();
117     }
118
119     @Override
120     public <M extends ChildOf<DataObject>> void onMessage(final M message, final RequestContext requestContext) {
121         // TODO Auto-generated method stub
122
123     }
124
125     @Override
126     public void addAuxiliaryConenctionContext(final ConnectionContext connectionContext) {
127         final SwitchConnectionDistinguisher connectionDistinguisher = new SwitchConnectionCookieOFImpl(connectionContext.getFeatures().getAuxiliaryId());
128         auxiliaryConnectionContexts.put(connectionDistinguisher, connectionContext);
129     }
130
131     @Override
132     public void removeAuxiliaryConenctionContext(final ConnectionContext connectionContext) {
133         // TODO Auto-generated method stub
134     }
135
136     @Override
137     public DeviceState getDeviceState() {
138         return deviceState;
139     }
140
141     @Override
142     public ReadTransaction getReadTransaction() {
143         return dataBroker.newReadOnlyTransaction();
144     }
145
146     @Override
147     public <T extends DataObject> void writeToTransaction(final LogicalDatastoreType store,
148                                                           final InstanceIdentifier<T> path, final T data) {
149         txChainManager.writeToTransaction(store, path, data);
150     }
151
152     @Override
153     public <T extends DataObject> void addDeleteToTxChain(final LogicalDatastoreType store, final InstanceIdentifier<T> path) {
154         txChainManager.addDeleteOperationTotTxChain(store, path);
155     }
156
157     @Override
158     public TableFeatures getCapabilities() {
159         // TODO Auto-generated method stub
160         return null;
161     }
162
163     @Override
164     public ConnectionContext getPrimaryConnectionContext() {
165         return primaryConnectionContext;
166     }
167
168     @Override
169     public ConnectionContext getAuxiliaryConnectiobContexts(final BigInteger cookie) {
170         return auxiliaryConnectionContexts.get(new SwitchConnectionCookieOFImpl(cookie.longValue()));
171     }
172
173     @Override
174     public Xid getNextXid() {
175         return xidGenerator.generate();
176     }
177
178     @Override
179     public Map<Long, RequestContext> getRequests() {
180         return requests;
181     }
182
183     @Override
184     public void hookRequestCtx(final Xid xid, final RequestContext requestFutureContext) {
185         // TODO Auto-generated method stub
186         requests.put(xid.getValue(), requestFutureContext);
187     }
188
189     @Override
190     public void attachOpenflowMessageListener(final OpenflowMessageListenerFacade openflowMessageListenerFacade) {
191         this.openflowMessageListenerFacade = openflowMessageListenerFacade;
192         primaryConnectionContext.getConnectionAdapter().setMessageListener(openflowMessageListenerFacade);
193     }
194
195     @Override
196     public OpenflowMessageListenerFacade getOpenflowMessageListenerFacade() {
197         return openflowMessageListenerFacade;
198     }
199
200     @Override
201     public DeviceFlowRegistry getDeviceFlowRegistry() {
202         return deviceFlowRegistry;
203     }
204
205     @Override
206     public void processReply(final OfHeader ofHeader) {
207         final RequestContext requestContext = getRequests().get(ofHeader.getXid());
208         if (null != requestContext) {
209             final SettableFuture replyFuture = requestContext.getFuture();
210             getRequests().remove(ofHeader.getXid());
211             RpcResult<OfHeader> rpcResult;
212             if (ofHeader instanceof Error) {
213                 final Error error = (Error) ofHeader;
214                 final String message = "Operation on device failed";
215                 rpcResult = RpcResultBuilder
216                         .<OfHeader>failed()
217                         .withError(RpcError.ErrorType.APPLICATION, message, new DeviceDataException(message, error))
218                         .build();
219             } else {
220                 rpcResult = RpcResultBuilder
221                         .<OfHeader>success()
222                         .withResult(ofHeader)
223                         .build();
224             }
225
226             replyFuture.set(rpcResult);
227             try {
228                 requestContext.close();
229             } catch (final Exception e) {
230                 LOG.error("Closing RequestContext failed: ", e);
231             }
232         } else {
233             LOG.error("Can't find request context registered for xid : {}", ofHeader.getXid());
234         }
235     }
236
237     @Override
238     public void processReply(final Xid xid, final List<MultipartReply> ofHeaderList) {
239         final RequestContext requestContext = getRequests().get(xid.getValue());
240         if (null != requestContext) {
241             final SettableFuture replyFuture = requestContext.getFuture();
242             getRequests().remove(xid.getValue());
243             final RpcResult<List<MultipartReply>> rpcResult = RpcResultBuilder
244                     .<List<MultipartReply>>success()
245                     .withResult(ofHeaderList)
246                     .build();
247             replyFuture.set(rpcResult);
248             try {
249                 requestContext.close();
250             } catch (final Exception e) {
251                 LOG.error("Closing RequestContext failed: ", e);
252             }
253         } else {
254             LOG.error("Can't find request context registered for xid : {}", xid.getValue());
255         }
256     }
257
258     @Override
259     public void processException(final Xid xid, final DeviceDataException deviceDataException) {
260
261         LOG.trace("Processing exception for xid : {}", xid.getValue());
262
263         final RequestContext requestContext = getRequests().get(xid.getValue());
264
265         if (null != requestContext) {
266             final SettableFuture replyFuture = requestContext.getFuture();
267             getRequests().remove(xid.getValue());
268             final RpcResult<List<OfHeader>> rpcResult = RpcResultBuilder
269                     .<List<OfHeader>>failed()
270                     .withError(RpcError.ErrorType.APPLICATION, String.format("Message processing failed : %s", deviceDataException.getError()), deviceDataException)
271                     .build();
272             replyFuture.set(rpcResult);
273             try {
274                 requestContext.close();
275             } catch (final Exception e) {
276                 LOG.error("Closing RequestContext failed: ", e);
277             }
278         } else {
279             LOG.error("Can't find request context registered for xid : {}", xid.getValue());
280         }
281     }
282
283     @Override
284     public void processFlowRemovedMessage(final FlowRemoved flowRemoved) {
285         //TODO: will be defined later
286     }
287
288     @Override
289     public void processPortStatusMessage(final PortStatusMessage portStatus) {
290         final TranslatorKey translatorKey = new TranslatorKey(portStatus.getVersion(), PortStatusMessage.class.getName());
291         final MessageTranslator<PortStatusMessage, FlowCapableNodeConnector> messageTranslator = translatorLibrary.lookupTranslator(translatorKey);
292         FlowCapableNodeConnector flowCapableNodeConnector = messageTranslator.translate(portStatus, this, null);
293
294         final KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey> iiToNodeConnector =
295                 provideIIToNodeConnector(portStatus.getPortNo(), portStatus.getVersion());
296         if (portStatus.getReason().equals(PortReason.OFPPRADD) ) {
297             // because of ADD status node connector has to be created
298             createNodeConnectorInDS(iiToNodeConnector);
299         } else if (portStatus.getReason().equals(PortReason.OFPPRDELETE) ) {
300             //only put operation over datastore is available. therefore delete is
301             //inserting of empty FlowCapableNodeConnector
302             flowCapableNodeConnector = new FlowCapableNodeConnectorBuilder().build();
303         }
304
305         InstanceIdentifier<FlowCapableNodeConnector> iiToFlowCapableNodeConnector = iiToNodeConnector.augmentation(FlowCapableNodeConnector.class);
306         writeToTransaction(LogicalDatastoreType.OPERATIONAL, iiToFlowCapableNodeConnector, flowCapableNodeConnector);
307     }
308
309     private void createNodeConnectorInDS(final KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey> iiToNodeConnector) {
310         writeToTransaction(LogicalDatastoreType.OPERATIONAL, iiToNodeConnector, new NodeConnectorBuilder().setKey(iiToNodeConnector.getKey()).build());
311     }
312
313     private KeyedInstanceIdentifier<Node, NodeKey> provideIIToNodes() {
314         return InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(deviceState.getNodeId()));
315     }
316
317     private KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey> provideIIToNodeConnector(final Long portNo, final Short version) {
318         final KeyedInstanceIdentifier<Node, NodeKey> iiToNodes = provideIIToNodes();
319         final NodeConnectorId nodeConnectorId = InventoryDataServiceUtil.nodeConnectorIdfromDatapathPortNo(
320                 deviceState.getFeatures().getDatapathId(), portNo, OpenflowVersion.get(version));
321         final NodeConnectorKey nodeConnectorKey = new NodeConnectorKey(new NodeConnectorId(nodeConnectorId));
322         return iiToNodes.child(NodeConnector.class, nodeConnectorKey);
323
324     }
325
326     @Override
327     public void processPacketInMessage(final PacketInMessage packetInMessage) {
328         final TranslatorKey translatorKey = new TranslatorKey(packetInMessage.getVersion(), PacketReceivedTranslator.class.getName());
329         final MessageTranslator<PacketInMessage, PacketReceived> messageTranslator = translatorLibrary.lookupTranslator(translatorKey);
330         final PacketReceived packetReceived = messageTranslator.translate(packetInMessage, this, null);
331         notificationService.publish(packetReceived);
332     }
333
334     @Override
335     public TranslatorLibrary oook() {
336         return translatorLibrary;
337     }
338
339     @Override
340     public void setTranslatorLibrary(final TranslatorLibrary translatorLibrary) {
341         this.translatorLibrary = translatorLibrary;
342     }
343
344     @Override
345     public HashedWheelTimer getTimer() {
346         return hashedWheelTimer;
347     }
348
349
350     private class XidGenerator {
351
352         private final AtomicLong xid = new AtomicLong(0);
353
354         public Xid generate() {
355             return new Xid(xid.incrementAndGet());
356         }
357     }
358
359     @Override
360     public RequestContext extractNextOutstandingMessage(final long barrierXid) {
361         RequestContext nextMessage = null;
362         final Iterator<Long> keyIterator = requests.keySet().iterator();
363         if (keyIterator.hasNext()) {
364             final Long oldestXid = keyIterator.next();
365             if (oldestXid < barrierXid) {
366                 nextMessage = requests.remove(oldestXid);
367             }
368         }
369         return nextMessage;
370     }
371
372     @Override
373     public void setCurrentBarrierTimeout(Timeout timeout) {
374         barrierTaskTimeout = timeout;
375     }
376
377     @Override
378     public Timeout getBarrierTaskTimeout() {
379         return barrierTaskTimeout;
380     }
381
382     @Override
383     public void setNotificationService(final NotificationProviderService notificationServiceParam) {
384         notificationService = notificationServiceParam;
385     }
386 }