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