Bump odlparent to 5.0.0
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / device / initialization / OF13DeviceInitializer.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.initialization;
9
10 import com.google.common.base.Function;
11 import com.google.common.base.Preconditions;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.Optional;
19 import java.util.concurrent.Future;
20 import java.util.stream.Collectors;
21 import javax.annotation.Nonnull;
22 import javax.annotation.Nullable;
23 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
24 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
25 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
26 import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
27 import org.opendaylight.openflowplugin.impl.common.MultipartReplyTranslatorUtil;
28 import org.opendaylight.openflowplugin.impl.datastore.MultipartWriterProvider;
29 import org.opendaylight.openflowplugin.impl.services.multilayer.MultiLayerMultipartCollectorService;
30 import org.opendaylight.openflowplugin.impl.services.singlelayer.SingleLayerMultipartCollectorService;
31 import org.opendaylight.openflowplugin.impl.util.DeviceInitializationUtil;
32 import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
33 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.ConvertorExecutor;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterFeatures;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.Capabilities;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
38 import org.opendaylight.yangtools.yang.common.RpcResult;
39 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 public class OF13DeviceInitializer extends AbstractDeviceInitializer {
44
45     private static final Logger LOG = LoggerFactory.getLogger(OF13DeviceInitializer.class);
46
47     @Override
48     protected Future<Void> initializeNodeInformation(@Nonnull final DeviceContext deviceContext,
49                                                      final boolean switchFeaturesMandatory,
50                                                      final boolean skipTableFeatures,
51                                                      @Nullable final MultipartWriterProvider multipartWriterProvider,
52                                                      @Nullable final ConvertorExecutor convertorExecutor) {
53         final ConnectionContext connectionContext =
54                 Preconditions.checkNotNull(deviceContext.getPrimaryConnectionContext());
55         final DeviceState deviceState = Preconditions.checkNotNull(deviceContext.getDeviceState());
56         final DeviceInfo deviceInfo = Preconditions.checkNotNull(deviceContext.getDeviceInfo());
57         final Capabilities capabilities = connectionContext.getFeatures().getCapabilities();
58         LOG.debug("Setting capabilities for device {}", deviceInfo);
59         DeviceStateUtil.setDeviceStateBasedOnV13Capabilities(deviceState, capabilities);
60
61         // First process description reply, write data to DS and write consequent data if successful
62         return Futures.transformAsync(
63             requestMultipart(MultipartType.OFPMPDESC, deviceContext),
64             input -> {
65                 translateAndWriteResult(
66                     MultipartType.OFPMPDESC,
67                     input.getResult(),
68                     deviceContext,
69                     multipartWriterProvider,
70                     convertorExecutor);
71
72                 final List<ListenableFuture<RpcResult<List<OfHeader>>>> futures = new ArrayList<>();
73                 futures.add(requestAndProcessMultipart(MultipartType.OFPMPMETERFEATURES, deviceContext,
74                         skipTableFeatures, multipartWriterProvider, convertorExecutor));
75                 futures.add(requestAndProcessMultipart(MultipartType.OFPMPGROUPFEATURES, deviceContext,
76                         skipTableFeatures, multipartWriterProvider, convertorExecutor));
77                 futures.add(requestAndProcessMultipart(MultipartType.OFPMPTABLEFEATURES, deviceContext,
78                         skipTableFeatures, multipartWriterProvider, convertorExecutor));
79                 futures.add(requestAndProcessMultipart(MultipartType.OFPMPPORTDESC, deviceContext, skipTableFeatures,
80                         multipartWriterProvider, convertorExecutor));
81
82                 return Futures.transform(
83                     switchFeaturesMandatory ? Futures.allAsList(futures) : Futures.successfulAsList(futures),
84                     new Function<List<RpcResult<List<OfHeader>>>, Void>() {
85                         @Override
86                         public Void apply(final List<RpcResult<List<OfHeader>>> input) {
87                             LOG.info("Static node {} successfully finished collecting",
88                                     deviceContext.getDeviceInfo());
89                             return null;
90                         }
91                     }, MoreExecutors.directExecutor());
92             }, MoreExecutors.directExecutor());
93
94     }
95
96     /**
97      * Request multipart of specified type and then run some processing on it.
98      *
99      * @param type multipart type
100      * @param deviceContext device context
101      * @param skipTableFeatures skip collecting of table features
102      * @param multipartWriterProvider multipart writer provider
103      * @param convertorExecutor convertor executor
104      * @return list of multipart messages unified to parent interface
105      */
106     private static ListenableFuture<RpcResult<List<OfHeader>>> requestAndProcessMultipart(final MultipartType type,
107                                                                   final DeviceContext deviceContext,
108                                                                   final boolean skipTableFeatures,
109                                                                   final MultipartWriterProvider multipartWriterProvider,
110                                                                   @Nullable final ConvertorExecutor convertorExecutor) {
111         final ListenableFuture<RpcResult<List<OfHeader>>> rpcResultListenableFuture =
112             MultipartType.OFPMPTABLEFEATURES.equals(type) && skipTableFeatures
113                 ? RpcResultBuilder.<List<OfHeader>>success().buildFuture()
114                 : requestMultipart(type, deviceContext);
115
116         createCallback(type, rpcResultListenableFuture, deviceContext, multipartWriterProvider, convertorExecutor);
117         return rpcResultListenableFuture;
118     }
119
120     /**
121      * Inject callback ti future for specified multipart type. This callback will translate and write
122      * result of multipart messages.
123      *
124      * @param type multipart type
125      * @param future multipart collection future
126      * @param deviceContext device context
127      * @param multipartWriterProvider multipart writer provider
128      * @param convertorExecutor convertor executor
129      */
130     private static void createCallback(final MultipartType type,
131                                        final ListenableFuture<RpcResult<List<OfHeader>>> future,
132                                        final DeviceContext deviceContext,
133                                        @Nullable final MultipartWriterProvider multipartWriterProvider,
134                                        @Nullable final ConvertorExecutor convertorExecutor) {
135         Futures.addCallback(future, new FutureCallback<RpcResult<List<OfHeader>>>() {
136             @Override
137             public void onSuccess(final RpcResult<List<OfHeader>> result) {
138                 if (result.getResult() != null) {
139                     LOG.info("Static node {} info: {} collected", deviceContext.getDeviceInfo(), type);
140                     translateAndWriteResult(
141                         type,
142                         result.getResult(),
143                         deviceContext,
144                         multipartWriterProvider,
145                         convertorExecutor);
146                 } else {
147                     result.getErrors().forEach(rpcError -> {
148                         LOG.warn("Failed to retrieve static node {} info: {}", type, rpcError.getMessage());
149
150                         if (LOG.isTraceEnabled() && rpcError.getCause() != null) {
151                             LOG.trace("Detailed error:", rpcError.getCause());
152                         }
153                     });
154
155                     // If table features are disabled or returned nothing, at least make empty tables
156                     if (MultipartType.OFPMPTABLEFEATURES.equals(type)) {
157                         DeviceInitializationUtil.makeEmptyTables(
158                             deviceContext,
159                             deviceContext.getDeviceInfo(),
160                             deviceContext.getPrimaryConnectionContext().getFeatures().getTables());
161                     }
162                 }
163             }
164
165             @Override
166             public void onFailure(final Throwable throwable) {
167                 LOG.warn("Request of type {} for static info of node {} failed.",
168                         type, deviceContext.getDeviceInfo());
169             }
170         }, MoreExecutors.directExecutor());
171     }
172
173     /**
174      * Translate and write multipart messages from OpenflowJava.
175      *
176      * @param type multipart type
177      * @param result multipart messages
178      * @param deviceContext device context
179      * @param multipartWriterProvider multipart writer provider
180      * @param convertorExecutor convertor executor
181      */
182     @SuppressWarnings("checkstyle:IllegalCatch")
183     private static void translateAndWriteResult(final MultipartType type,
184                                                 final List<OfHeader> result,
185                                                 final DeviceContext deviceContext,
186                                                 @Nullable final MultipartWriterProvider multipartWriterProvider,
187                                                 @Nullable final ConvertorExecutor convertorExecutor) {
188         if (result != null) {
189             try {
190                 result.forEach(reply -> {
191                     // First, translate collected data to proper openflowplugin representation
192                     MultipartReplyTranslatorUtil
193                         .translate(
194                             reply,
195                             deviceContext.getDeviceInfo(),
196                             convertorExecutor,
197                             deviceContext.oook())
198                         .ifPresent(translatedReply -> {
199                             // If we collected meter features, check if we have support for meters
200                             // and pass this information to device context
201                             if (MultipartType.OFPMPMETERFEATURES.equals(type)
202                                     && translatedReply instanceof MeterFeatures) {
203                                 final MeterFeatures meterFeatures = (MeterFeatures) translatedReply;
204
205                                 if (meterFeatures.getMaxMeter().getValue() > 0) {
206                                     deviceContext.getDeviceState().setMeterAvailable(true);
207                                 }
208                             }
209
210                             // Now. try to write translated collected features
211                             Optional.ofNullable(multipartWriterProvider)
212                                 .flatMap(provider -> provider.lookup(type))
213                                 .ifPresent(writer -> writer.write(translatedReply, false));
214                         });
215                 });
216             } catch (final Exception e) {
217                 LOG.warn("Failed to write node {} to DS ", deviceContext.getDeviceInfo(), e);
218             }
219         } else {
220             LOG.warn("Failed to write node {} to DS because we failed to gather device info.",
221                 deviceContext.getDeviceInfo());
222         }
223     }
224
225     /**
226      * Send request to device and unify different possible reply types from OpenflowJava to common parent interface.
227      *
228      * @param multipartType multipart type
229      * @param deviceContext device context
230      * @return unified replies
231      */
232     private static ListenableFuture<RpcResult<List<OfHeader>>> requestMultipart(final MultipartType multipartType,
233                                                                                 final DeviceContext deviceContext) {
234         if (deviceContext.canUseSingleLayerSerialization()) {
235             final SingleLayerMultipartCollectorService service =
236                     new SingleLayerMultipartCollectorService(deviceContext, deviceContext);
237
238             return Futures.transform(service.handleServiceCall(multipartType),
239                 input -> {
240                     if (input.getResult() == null && input.isSuccessful()) {
241                         return RpcResultBuilder.<List<OfHeader>>success(null).build();
242                     }
243
244                     return input.isSuccessful()
245                             ? RpcResultBuilder.success(input
246                                     .getResult()
247                                     .stream()
248                                     .map(OfHeader.class::cast)
249                                     .collect(Collectors.toList()))
250                                     .build()
251                             : RpcResultBuilder.<List<OfHeader>>failed()
252                                     .withRpcErrors(input.getErrors())
253                                     .build();
254                 }, MoreExecutors.directExecutor());
255         }
256
257         final MultiLayerMultipartCollectorService service =
258             new MultiLayerMultipartCollectorService(deviceContext, deviceContext);
259
260         return Futures.transform(service.handleServiceCall(multipartType), input -> {
261             if (input.getResult() == null && input.isSuccessful()) {
262                 return RpcResultBuilder.<List<OfHeader>>success(null).build();
263             }
264
265             return input.isSuccessful() ? RpcResultBuilder
266                     .success(input.getResult().stream().map(OfHeader.class::cast).collect(Collectors.toList())).build()
267                     : RpcResultBuilder.<List<OfHeader>>failed().withRpcErrors(input.getErrors()).build();
268         }, MoreExecutors.directExecutor());
269     }
270 }