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