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